Skip to content

Commit b32acf6

Browse files
committed
Error on using yield without also using #[coroutine] on the closure
And suggest adding the `#[coroutine]` to the closure
1 parent 5abb286 commit b32acf6

File tree

267 files changed

+1230
-830
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

267 files changed

+1230
-830
lines changed

compiler/rustc_ast_lowering/messages.ftl

+3
Original file line numberDiff line numberDiff line change
@@ -163,3 +163,6 @@ ast_lowering_underscore_expr_lhs_assign =
163163
.label = `_` not allowed here
164164
165165
ast_lowering_use_angle_brackets = use angle brackets instead
166+
ast_lowering_yield_in_closure =
167+
`yield` can only be used in `#[coroutine]` closures, or `gen` blocks
168+
.suggestion = use `#[coroutine]` to make this closure a coroutine

compiler/rustc_ast_lowering/src/errors.rs

+9
Original file line numberDiff line numberDiff line change
@@ -421,3 +421,12 @@ pub(crate) struct NoPreciseCapturesOnApit {
421421
#[primary_span]
422422
pub span: Span,
423423
}
424+
425+
#[derive(Diagnostic)]
426+
#[diag(ast_lowering_yield_in_closure)]
427+
pub(crate) struct YieldInClosure {
428+
#[primary_span]
429+
pub span: Span,
430+
#[suggestion(code = "#[coroutine] ", applicability = "maybe-incorrect", style = "verbose")]
431+
pub suggestion: Option<Span>,
432+
}

compiler/rustc_ast_lowering/src/expr.rs

+5
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ use super::errors::{
88
};
99
use super::ResolverAstLoweringExt;
1010
use super::{ImplTraitContext, LoweringContext, ParamMode, ParenthesizedGenericArgs};
11+
use crate::errors::YieldInClosure;
1112
use crate::{FnDeclKind, ImplTraitPosition};
1213
use rustc_ast::ptr::P as AstP;
1314
use rustc_ast::*;
@@ -980,6 +981,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
980981
None
981982
};
982983
let body_id = this.lower_fn_body(decl, |this| {
984+
this.coroutine_kind = coroutine_kind;
983985
let e = this.lower_expr_mut(body);
984986
coroutine_kind = this.coroutine_kind;
985987
e
@@ -1578,7 +1580,10 @@ impl<'hir> LoweringContext<'_, 'hir> {
15781580
)
15791581
.emit();
15801582
}
1583+
let suggestion = self.current_item.map(|s| s.shrink_to_lo());
1584+
self.dcx().emit_err(YieldInClosure { span, suggestion });
15811585
self.coroutine_kind = Some(hir::CoroutineKind::Coroutine(Movability::Movable));
1586+
15821587
false
15831588
}
15841589
};

compiler/rustc_ast_lowering/src/item.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -203,7 +203,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
203203
body,
204204
..
205205
}) => {
206-
self.with_new_scopes(ident.span, |this| {
206+
self.with_new_scopes(*fn_sig_span, |this| {
207207
// Note: we don't need to change the return type from `T` to
208208
// `impl Future<Output = T>` here because lower_body
209209
// only cares about the input argument patterns in the function

compiler/rustc_codegen_cranelift/example/polymorphize_coroutine.rs

+3-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
#![feature(coroutines, coroutine_trait)]
1+
#![feature(coroutines, coroutine_trait, stmt_expr_attributes)]
22

33
use std::ops::Coroutine;
44
use std::pin::Pin;
@@ -8,7 +8,8 @@ fn main() {
88
}
99

1010
fn run_coroutine<T>() {
11-
let mut coroutine = || {
11+
let mut coroutine = #[coroutine]
12+
|| {
1213
yield;
1314
return;
1415
};

compiler/rustc_codegen_cranelift/example/std_example.rs

+7-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
#![feature(
22
core_intrinsics,
33
coroutines,
4+
stmt_expr_attributes,
45
coroutine_trait,
56
is_sorted,
67
repr_simd,
@@ -122,9 +123,12 @@ fn main() {
122123
test_simd();
123124
}
124125

125-
Box::pin(move |mut _task_context| {
126-
yield ();
127-
})
126+
Box::pin(
127+
#[coroutine]
128+
move |mut _task_context| {
129+
yield ();
130+
},
131+
)
128132
.as_mut()
129133
.resume(0);
130134

compiler/rustc_codegen_gcc/example/std_example.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
#![allow(internal_features)]
2-
#![feature(core_intrinsics, coroutines, coroutine_trait, is_sorted)]
2+
#![feature(core_intrinsics, coroutines, coroutine_trait, is_sorted, stmt_expr_attributes)]
33

44
#[cfg(feature="master")]
55
#[cfg(target_arch="x86_64")]
@@ -103,7 +103,7 @@ fn main() {
103103
test_simd();
104104
}
105105

106-
Box::pin(move |mut _task_context| {
106+
Box::pin(#[coroutine] move |mut _task_context| {
107107
yield ();
108108
}).as_mut().resume(0);
109109

compiler/rustc_metadata/src/rmeta/decoder.rs

+23-19
Original file line numberDiff line numberDiff line change
@@ -1260,30 +1260,34 @@ impl<'a, 'tcx> CrateMetadataRef<'a> {
12601260
id: DefIndex,
12611261
sess: &'a Session,
12621262
) -> impl Iterator<Item = ModChild> + 'a {
1263-
iter::from_coroutine(move || {
1264-
if let Some(data) = &self.root.proc_macro_data {
1265-
// If we are loading as a proc macro, we want to return
1266-
// the view of this crate as a proc macro crate.
1267-
if id == CRATE_DEF_INDEX {
1268-
for child_index in data.macros.decode(self) {
1263+
iter::from_coroutine(
1264+
#[cfg_attr(not(bootstrap), coroutine)]
1265+
move || {
1266+
if let Some(data) = &self.root.proc_macro_data {
1267+
// If we are loading as a proc macro, we want to return
1268+
// the view of this crate as a proc macro crate.
1269+
if id == CRATE_DEF_INDEX {
1270+
for child_index in data.macros.decode(self) {
1271+
yield self.get_mod_child(child_index, sess);
1272+
}
1273+
}
1274+
} else {
1275+
// Iterate over all children.
1276+
let non_reexports =
1277+
self.root.tables.module_children_non_reexports.get(self, id);
1278+
for child_index in non_reexports.unwrap().decode(self) {
12691279
yield self.get_mod_child(child_index, sess);
12701280
}
1271-
}
1272-
} else {
1273-
// Iterate over all children.
1274-
let non_reexports = self.root.tables.module_children_non_reexports.get(self, id);
1275-
for child_index in non_reexports.unwrap().decode(self) {
1276-
yield self.get_mod_child(child_index, sess);
1277-
}
12781281

1279-
let reexports = self.root.tables.module_children_reexports.get(self, id);
1280-
if !reexports.is_default() {
1281-
for reexport in reexports.decode((self, sess)) {
1282-
yield reexport;
1282+
let reexports = self.root.tables.module_children_reexports.get(self, id);
1283+
if !reexports.is_default() {
1284+
for reexport in reexports.decode((self, sess)) {
1285+
yield reexport;
1286+
}
12831287
}
12841288
}
1285-
}
1286-
})
1289+
},
1290+
)
12871291
}
12881292

12891293
fn is_ctfe_mir_available(self, id: DefIndex) -> bool {

compiler/rustc_middle/src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
#![feature(const_type_name)]
3636
#![feature(discriminant_kind)]
3737
#![feature(coroutines)]
38+
#![feature(stmt_expr_attributes)]
3839
#![feature(generic_nonzero)]
3940
#![feature(if_let_guard)]
4041
#![feature(inline_const)]

compiler/rustc_middle/src/ty/closure.rs

+43-39
Original file line numberDiff line numberDiff line change
@@ -422,49 +422,53 @@ pub fn analyze_coroutine_closure_captures<'a, 'tcx: 'a, T>(
422422
child_captures: impl IntoIterator<Item = &'a CapturedPlace<'tcx>>,
423423
mut for_each: impl FnMut((usize, &'a CapturedPlace<'tcx>), (usize, &'a CapturedPlace<'tcx>)) -> T,
424424
) -> impl Iterator<Item = T> + Captures<'a> + Captures<'tcx> {
425-
std::iter::from_coroutine(move || {
426-
let mut child_captures = child_captures.into_iter().enumerate().peekable();
427-
428-
// One parent capture may correspond to several child captures if we end up
429-
// refining the set of captures via edition-2021 precise captures. We want to
430-
// match up any number of child captures with one parent capture, so we keep
431-
// peeking off this `Peekable` until the child doesn't match anymore.
432-
for (parent_field_idx, parent_capture) in parent_captures.into_iter().enumerate() {
433-
// Make sure we use every field at least once, b/c why are we capturing something
434-
// if it's not used in the inner coroutine.
435-
let mut field_used_at_least_once = false;
436-
437-
// A parent matches a child if they share the same prefix of projections.
438-
// The child may have more, if it is capturing sub-fields out of
439-
// something that is captured by-move in the parent closure.
440-
while child_captures.peek().map_or(false, |(_, child_capture)| {
441-
child_prefix_matches_parent_projections(parent_capture, child_capture)
442-
}) {
443-
let (child_field_idx, child_capture) = child_captures.next().unwrap();
444-
// This analysis only makes sense if the parent capture is a
445-
// prefix of the child capture.
446-
assert!(
447-
child_capture.place.projections.len() >= parent_capture.place.projections.len(),
448-
"parent capture ({parent_capture:#?}) expected to be prefix of \
425+
std::iter::from_coroutine(
426+
#[cfg_attr(not(bootstrap), coroutine)]
427+
move || {
428+
let mut child_captures = child_captures.into_iter().enumerate().peekable();
429+
430+
// One parent capture may correspond to several child captures if we end up
431+
// refining the set of captures via edition-2021 precise captures. We want to
432+
// match up any number of child captures with one parent capture, so we keep
433+
// peeking off this `Peekable` until the child doesn't match anymore.
434+
for (parent_field_idx, parent_capture) in parent_captures.into_iter().enumerate() {
435+
// Make sure we use every field at least once, b/c why are we capturing something
436+
// if it's not used in the inner coroutine.
437+
let mut field_used_at_least_once = false;
438+
439+
// A parent matches a child if they share the same prefix of projections.
440+
// The child may have more, if it is capturing sub-fields out of
441+
// something that is captured by-move in the parent closure.
442+
while child_captures.peek().map_or(false, |(_, child_capture)| {
443+
child_prefix_matches_parent_projections(parent_capture, child_capture)
444+
}) {
445+
let (child_field_idx, child_capture) = child_captures.next().unwrap();
446+
// This analysis only makes sense if the parent capture is a
447+
// prefix of the child capture.
448+
assert!(
449+
child_capture.place.projections.len()
450+
>= parent_capture.place.projections.len(),
451+
"parent capture ({parent_capture:#?}) expected to be prefix of \
449452
child capture ({child_capture:#?})"
450-
);
453+
);
451454

452-
yield for_each(
453-
(parent_field_idx, parent_capture),
454-
(child_field_idx, child_capture),
455-
);
455+
yield for_each(
456+
(parent_field_idx, parent_capture),
457+
(child_field_idx, child_capture),
458+
);
456459

457-
field_used_at_least_once = true;
458-
}
460+
field_used_at_least_once = true;
461+
}
459462

460-
// Make sure the field was used at least once.
461-
assert!(
462-
field_used_at_least_once,
463-
"we captured {parent_capture:#?} but it was not used in the child coroutine?"
464-
);
465-
}
466-
assert_eq!(child_captures.next(), None, "leftover child captures?");
467-
})
463+
// Make sure the field was used at least once.
464+
assert!(
465+
field_used_at_least_once,
466+
"we captured {parent_capture:#?} but it was not used in the child coroutine?"
467+
);
468+
}
469+
assert_eq!(child_captures.next(), None, "leftover child captures?");
470+
},
471+
)
468472
}
469473

470474
fn child_prefix_matches_parent_projections(

compiler/rustc_middle/src/ty/context.rs

+16-13
Original file line numberDiff line numberDiff line change
@@ -1262,20 +1262,23 @@ impl<'tcx> TyCtxt<'tcx> {
12621262
self.dep_graph.read_index(DepNodeIndex::FOREVER_RED_NODE);
12631263

12641264
let definitions = &self.untracked.definitions;
1265-
std::iter::from_coroutine(|| {
1266-
let mut i = 0;
1267-
1268-
// Recompute the number of definitions each time, because our caller may be creating
1269-
// new ones.
1270-
while i < { definitions.read().num_definitions() } {
1271-
let local_def_index = rustc_span::def_id::DefIndex::from_usize(i);
1272-
yield LocalDefId { local_def_index };
1273-
i += 1;
1274-
}
1265+
std::iter::from_coroutine(
1266+
#[cfg_attr(not(bootstrap), coroutine)]
1267+
|| {
1268+
let mut i = 0;
1269+
1270+
// Recompute the number of definitions each time, because our caller may be creating
1271+
// new ones.
1272+
while i < { definitions.read().num_definitions() } {
1273+
let local_def_index = rustc_span::def_id::DefIndex::from_usize(i);
1274+
yield LocalDefId { local_def_index };
1275+
i += 1;
1276+
}
12751277

1276-
// Freeze definitions once we finish iterating on them, to prevent adding new ones.
1277-
definitions.freeze();
1278-
})
1278+
// Freeze definitions once we finish iterating on them, to prevent adding new ones.
1279+
definitions.freeze();
1280+
},
1281+
)
12791282
}
12801283

12811284
pub fn def_path_table(self) -> &'tcx rustc_hir::definitions::DefPathTable {
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
// Regression test for #5238 / https://github.com/rust-lang/rust/pull/69562
22

3-
#![feature(coroutines, coroutine_trait)]
3+
#![feature(coroutines, coroutine_trait, stmt_expr_attributes)]
44

55
fn main() {
6-
let _ = || {
6+
let _ = #[coroutine] || {
77
yield;
88
};
99
}

src/tools/clippy/tests/ui/large_futures.fixed

-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
#![feature(coroutines)]
21
#![warn(clippy::large_futures)]
32
#![allow(clippy::never_loop)]
43
#![allow(clippy::future_not_send)]

src/tools/clippy/tests/ui/large_futures.rs

-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
#![feature(coroutines)]
21
#![warn(clippy::large_futures)]
32
#![allow(clippy::never_loop)]
43
#![allow(clippy::future_not_send)]

src/tools/clippy/tests/ui/large_futures.stderr

+8-8
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
error: large future with a size of 16385 bytes
2-
--> tests/ui/large_futures.rs:11:9
2+
--> tests/ui/large_futures.rs:10:9
33
|
44
LL | big_fut([0u8; 1024 * 16]).await;
55
| ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider `Box::pin` on it: `Box::pin(big_fut([0u8; 1024 * 16]))`
@@ -8,37 +8,37 @@ LL | big_fut([0u8; 1024 * 16]).await;
88
= help: to override `-D warnings` add `#[allow(clippy::large_futures)]`
99

1010
error: large future with a size of 16386 bytes
11-
--> tests/ui/large_futures.rs:15:5
11+
--> tests/ui/large_futures.rs:14:5
1212
|
1313
LL | f.await
1414
| ^ help: consider `Box::pin` on it: `Box::pin(f)`
1515

1616
error: large future with a size of 16387 bytes
17-
--> tests/ui/large_futures.rs:20:9
17+
--> tests/ui/large_futures.rs:19:9
1818
|
1919
LL | wait().await;
2020
| ^^^^^^ help: consider `Box::pin` on it: `Box::pin(wait())`
2121

2222
error: large future with a size of 16387 bytes
23-
--> tests/ui/large_futures.rs:25:13
23+
--> tests/ui/large_futures.rs:24:13
2424
|
2525
LL | wait().await;
2626
| ^^^^^^ help: consider `Box::pin` on it: `Box::pin(wait())`
2727

2828
error: large future with a size of 65540 bytes
29-
--> tests/ui/large_futures.rs:33:5
29+
--> tests/ui/large_futures.rs:32:5
3030
|
3131
LL | foo().await;
3232
| ^^^^^ help: consider `Box::pin` on it: `Box::pin(foo())`
3333

3434
error: large future with a size of 49159 bytes
35-
--> tests/ui/large_futures.rs:35:5
35+
--> tests/ui/large_futures.rs:34:5
3636
|
3737
LL | calls_fut(fut).await;
3838
| ^^^^^^^^^^^^^^ help: consider `Box::pin` on it: `Box::pin(calls_fut(fut))`
3939

4040
error: large future with a size of 65540 bytes
41-
--> tests/ui/large_futures.rs:48:5
41+
--> tests/ui/large_futures.rs:47:5
4242
|
4343
LL | / async {
4444
LL | |
@@ -59,7 +59,7 @@ LL + })
5959
|
6060

6161
error: large future with a size of 65540 bytes
62-
--> tests/ui/large_futures.rs:60:13
62+
--> tests/ui/large_futures.rs:59:13
6363
|
6464
LL | / async {
6565
LL | | let x = [0i32; 1024 * 16];

0 commit comments

Comments
 (0)