mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-25 08:13:41 +00:00
Auto merge of #116447 - oli-obk:gen_fn, r=compiler-errors
Implement `gen` blocks in the 2024 edition Coroutines tracking issue https://github.com/rust-lang/rust/issues/43122 `gen` block tracking issue https://github.com/rust-lang/rust/issues/117078 This PR implements `gen` blocks that implement `Iterator`. Most of the logic with `async` blocks is shared, and thus I renamed various types that were referring to `async` specifically. An example usage of `gen` blocks is ```rust fn foo() -> impl Iterator<Item = i32> { gen { yield 42; for i in 5..18 { if i.is_even() { continue } yield i * 2; } } } ``` The limitations (to be resolved) of the implementation are listed in the tracking issue
This commit is contained in:
commit
2cad938a81
@ -1235,7 +1235,7 @@ impl Expr {
|
||||
ExprKind::Closure(..) => ExprPrecedence::Closure,
|
||||
ExprKind::Block(..) => ExprPrecedence::Block,
|
||||
ExprKind::TryBlock(..) => ExprPrecedence::TryBlock,
|
||||
ExprKind::Async(..) => ExprPrecedence::Async,
|
||||
ExprKind::Gen(..) => ExprPrecedence::Gen,
|
||||
ExprKind::Await(..) => ExprPrecedence::Await,
|
||||
ExprKind::Assign(..) => ExprPrecedence::Assign,
|
||||
ExprKind::AssignOp(..) => ExprPrecedence::AssignOp,
|
||||
@ -1405,11 +1405,9 @@ pub enum ExprKind {
|
||||
Closure(Box<Closure>),
|
||||
/// A block (`'label: { ... }`).
|
||||
Block(P<Block>, Option<Label>),
|
||||
/// An async block (`async move { ... }`).
|
||||
///
|
||||
/// The async block used to have a `NodeId`, which was removed in favor of
|
||||
/// using the parent `NodeId` of the parent `Expr`.
|
||||
Async(CaptureBy, P<Block>),
|
||||
/// An `async` block (`async move { ... }`),
|
||||
/// or a `gen` block (`gen move { ... }`)
|
||||
Gen(CaptureBy, P<Block>, GenBlockKind),
|
||||
/// An await expression (`my_future.await`). Span is of await keyword.
|
||||
Await(P<Expr>, Span),
|
||||
|
||||
@ -1499,6 +1497,28 @@ pub enum ExprKind {
|
||||
Err,
|
||||
}
|
||||
|
||||
/// Used to differentiate between `async {}` blocks and `gen {}` blocks.
|
||||
#[derive(Clone, Encodable, Decodable, Debug, PartialEq, Eq)]
|
||||
pub enum GenBlockKind {
|
||||
Async,
|
||||
Gen,
|
||||
}
|
||||
|
||||
impl fmt::Display for GenBlockKind {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
self.modifier().fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
impl GenBlockKind {
|
||||
pub fn modifier(&self) -> &'static str {
|
||||
match self {
|
||||
GenBlockKind::Async => "async",
|
||||
GenBlockKind::Gen => "gen",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// The explicit `Self` type in a "qualified path". The actual
|
||||
/// path, including the trait and the associated item, is stored
|
||||
/// separately. `position` represents the index of the associated
|
||||
@ -2363,6 +2383,12 @@ pub enum Async {
|
||||
No,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Encodable, Decodable, Debug)]
|
||||
pub enum Gen {
|
||||
Yes { span: Span, closure_id: NodeId, return_impl_trait_id: NodeId },
|
||||
No,
|
||||
}
|
||||
|
||||
impl Async {
|
||||
pub fn is_async(self) -> bool {
|
||||
matches!(self, Async::Yes { .. })
|
||||
|
@ -1418,7 +1418,7 @@ pub fn noop_visit_expr<T: MutVisitor>(
|
||||
vis.visit_block(blk);
|
||||
visit_opt(label, |label| vis.visit_label(label));
|
||||
}
|
||||
ExprKind::Async(_capture_by, body) => {
|
||||
ExprKind::Gen(_capture_by, body, _) => {
|
||||
vis.visit_block(body);
|
||||
}
|
||||
ExprKind::Await(expr, await_kw_span) => {
|
||||
|
@ -197,6 +197,7 @@ pub fn ident_can_begin_expr(name: Symbol, span: Span, is_raw: bool) -> bool {
|
||||
kw::Continue,
|
||||
kw::False,
|
||||
kw::For,
|
||||
kw::Gen,
|
||||
kw::If,
|
||||
kw::Let,
|
||||
kw::Loop,
|
||||
|
@ -46,7 +46,7 @@ pub fn expr_trailing_brace(mut expr: &ast::Expr) -> Option<&ast::Expr> {
|
||||
Closure(closure) => {
|
||||
expr = &closure.body;
|
||||
}
|
||||
Async(..) | Block(..) | ForLoop(..) | If(..) | Loop(..) | Match(..) | Struct(..)
|
||||
Gen(..) | Block(..) | ForLoop(..) | If(..) | Loop(..) | Match(..) | Struct(..)
|
||||
| TryBlock(..) | While(..) => break Some(expr),
|
||||
_ => break None,
|
||||
}
|
||||
|
@ -285,7 +285,7 @@ pub enum ExprPrecedence {
|
||||
Block,
|
||||
TryBlock,
|
||||
Struct,
|
||||
Async,
|
||||
Gen,
|
||||
Await,
|
||||
Err,
|
||||
}
|
||||
@ -351,7 +351,7 @@ impl ExprPrecedence {
|
||||
| ExprPrecedence::ConstBlock
|
||||
| ExprPrecedence::Block
|
||||
| ExprPrecedence::TryBlock
|
||||
| ExprPrecedence::Async
|
||||
| ExprPrecedence::Gen
|
||||
| ExprPrecedence::Struct
|
||||
| ExprPrecedence::Err => PREC_PAREN,
|
||||
}
|
||||
|
@ -872,7 +872,7 @@ pub fn walk_expr<'a, V: Visitor<'a>>(visitor: &mut V, expression: &'a Expr) {
|
||||
walk_list!(visitor, visit_label, opt_label);
|
||||
visitor.visit_block(block);
|
||||
}
|
||||
ExprKind::Async(_, body) => {
|
||||
ExprKind::Gen(_, body, _) => {
|
||||
visitor.visit_block(body);
|
||||
}
|
||||
ExprKind::Await(expr, _) => visitor.visit_expr(expr),
|
||||
|
@ -183,7 +183,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
||||
self.arena.alloc_from_iter(arms.iter().map(|x| self.lower_arm(x))),
|
||||
hir::MatchSource::Normal,
|
||||
),
|
||||
ExprKind::Async(capture_clause, block) => self.make_async_expr(
|
||||
ExprKind::Gen(capture_clause, block, GenBlockKind::Async) => self.make_async_expr(
|
||||
*capture_clause,
|
||||
e.id,
|
||||
None,
|
||||
@ -317,6 +317,14 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
||||
rest,
|
||||
)
|
||||
}
|
||||
ExprKind::Gen(capture_clause, block, GenBlockKind::Gen) => self.make_gen_expr(
|
||||
*capture_clause,
|
||||
e.id,
|
||||
None,
|
||||
e.span,
|
||||
hir::CoroutineSource::Block,
|
||||
|this| this.with_new_scopes(|this| this.lower_block_expr(block)),
|
||||
),
|
||||
ExprKind::Yield(opt_expr) => self.lower_expr_yield(e.span, opt_expr.as_deref()),
|
||||
ExprKind::Err => hir::ExprKind::Err(
|
||||
self.tcx.sess.delay_span_bug(e.span, "lowered ExprKind::Err"),
|
||||
@ -661,6 +669,57 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
||||
}))
|
||||
}
|
||||
|
||||
/// Lower a `gen` construct to a generator that implements `Iterator`.
|
||||
///
|
||||
/// This results in:
|
||||
///
|
||||
/// ```text
|
||||
/// static move? |()| -> () {
|
||||
/// <body>
|
||||
/// }
|
||||
/// ```
|
||||
pub(super) fn make_gen_expr(
|
||||
&mut self,
|
||||
capture_clause: CaptureBy,
|
||||
closure_node_id: NodeId,
|
||||
_yield_ty: Option<hir::FnRetTy<'hir>>,
|
||||
span: Span,
|
||||
gen_kind: hir::CoroutineSource,
|
||||
body: impl FnOnce(&mut Self) -> hir::Expr<'hir>,
|
||||
) -> hir::ExprKind<'hir> {
|
||||
let output = hir::FnRetTy::DefaultReturn(self.lower_span(span));
|
||||
|
||||
// The closure/generator `FnDecl` takes a single (resume) argument of type `input_ty`.
|
||||
let fn_decl = self.arena.alloc(hir::FnDecl {
|
||||
inputs: &[],
|
||||
output,
|
||||
c_variadic: false,
|
||||
implicit_self: hir::ImplicitSelfKind::None,
|
||||
lifetime_elision_allowed: false,
|
||||
});
|
||||
|
||||
let body = self.lower_body(move |this| {
|
||||
this.coroutine_kind = Some(hir::CoroutineKind::Gen(gen_kind));
|
||||
|
||||
let res = body(this);
|
||||
(&[], res)
|
||||
});
|
||||
|
||||
// `static |()| -> () { body }`:
|
||||
hir::ExprKind::Closure(self.arena.alloc(hir::Closure {
|
||||
def_id: self.local_def_id(closure_node_id),
|
||||
binder: hir::ClosureBinder::Default,
|
||||
capture_clause,
|
||||
bound_generic_params: &[],
|
||||
fn_decl,
|
||||
body,
|
||||
fn_decl_span: self.lower_span(span),
|
||||
fn_arg_span: None,
|
||||
movability: Some(Movability::Movable),
|
||||
constness: hir::Constness::NotConst,
|
||||
}))
|
||||
}
|
||||
|
||||
/// Forwards a possible `#[track_caller]` annotation from `outer_hir_id` to
|
||||
/// `inner_hir_id` in case the `async_fn_track_caller` feature is enabled.
|
||||
pub(super) fn maybe_forward_track_caller(
|
||||
@ -712,7 +771,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
||||
let full_span = expr.span.to(await_kw_span);
|
||||
match self.coroutine_kind {
|
||||
Some(hir::CoroutineKind::Async(_)) => {}
|
||||
Some(hir::CoroutineKind::Coroutine) | None => {
|
||||
Some(hir::CoroutineKind::Coroutine) | Some(hir::CoroutineKind::Gen(_)) | None => {
|
||||
self.tcx.sess.emit_err(AwaitOnlyInAsyncFnAndBlocks {
|
||||
await_kw_span,
|
||||
item_span: self.current_item,
|
||||
@ -936,8 +995,8 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
||||
}
|
||||
Some(movability)
|
||||
}
|
||||
Some(hir::CoroutineKind::Async(_)) => {
|
||||
panic!("non-`async` closure body turned `async` during lowering");
|
||||
Some(hir::CoroutineKind::Gen(_)) | Some(hir::CoroutineKind::Async(_)) => {
|
||||
panic!("non-`async`/`gen` closure body turned `async`/`gen` during lowering");
|
||||
}
|
||||
None => {
|
||||
if movability == Movability::Static {
|
||||
@ -1445,11 +1504,22 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
||||
|
||||
fn lower_expr_yield(&mut self, span: Span, opt_expr: Option<&Expr>) -> hir::ExprKind<'hir> {
|
||||
match self.coroutine_kind {
|
||||
Some(hir::CoroutineKind::Coroutine) => {}
|
||||
Some(hir::CoroutineKind::Gen(_)) => {}
|
||||
Some(hir::CoroutineKind::Async(_)) => {
|
||||
self.tcx.sess.emit_err(AsyncCoroutinesNotSupported { span });
|
||||
}
|
||||
None => self.coroutine_kind = Some(hir::CoroutineKind::Coroutine),
|
||||
Some(hir::CoroutineKind::Coroutine) | None => {
|
||||
if !self.tcx.features().coroutines {
|
||||
rustc_session::parse::feature_err(
|
||||
&self.tcx.sess.parse_sess,
|
||||
sym::coroutines,
|
||||
span,
|
||||
"yield syntax is experimental",
|
||||
)
|
||||
.emit();
|
||||
}
|
||||
self.coroutine_kind = Some(hir::CoroutineKind::Coroutine)
|
||||
}
|
||||
}
|
||||
|
||||
let expr =
|
||||
|
@ -554,7 +554,12 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session, features: &Features) {
|
||||
"consider removing `for<...>`"
|
||||
);
|
||||
gate_all!(more_qualified_paths, "usage of qualified paths in this context is experimental");
|
||||
gate_all!(coroutines, "yield syntax is experimental");
|
||||
for &span in spans.get(&sym::yield_expr).iter().copied().flatten() {
|
||||
if !span.at_least_rust_2024() {
|
||||
gate_feature_post!(&visitor, coroutines, span, "yield syntax is experimental");
|
||||
}
|
||||
}
|
||||
gate_all!(gen_blocks, "gen blocks are experimental");
|
||||
gate_all!(raw_ref_op, "raw address of syntax is experimental");
|
||||
gate_all!(const_trait_impl, "const trait impls are experimental");
|
||||
gate_all!(
|
||||
|
@ -445,8 +445,8 @@ impl<'a> State<'a> {
|
||||
self.ibox(0);
|
||||
self.print_block_with_attrs(blk, attrs);
|
||||
}
|
||||
ast::ExprKind::Async(capture_clause, blk) => {
|
||||
self.word_nbsp("async");
|
||||
ast::ExprKind::Gen(capture_clause, blk, kind) => {
|
||||
self.word_nbsp(kind.modifier());
|
||||
self.print_capture_clause(*capture_clause);
|
||||
// cbox/ibox in analogy to the `ExprKind::Block` arm above
|
||||
self.cbox(0);
|
||||
|
@ -373,11 +373,12 @@ impl<'cx, 'tcx> crate::MirBorrowckCtxt<'cx, 'tcx> {
|
||||
span: Span,
|
||||
yield_span: Span,
|
||||
) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
|
||||
let coroutine_kind = self.body.coroutine.as_ref().unwrap().coroutine_kind;
|
||||
let mut err = struct_span_err!(
|
||||
self,
|
||||
span,
|
||||
E0626,
|
||||
"borrow may still be in use when coroutine yields",
|
||||
"borrow may still be in use when {coroutine_kind:#} yields",
|
||||
);
|
||||
err.span_label(yield_span, "possible yield occurs here");
|
||||
err
|
||||
|
@ -2491,11 +2491,17 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
|
||||
|
||||
let (sugg_span, suggestion) = match tcx.sess.source_map().span_to_snippet(args_span) {
|
||||
Ok(string) => {
|
||||
if string.starts_with("async ") {
|
||||
let pos = args_span.lo() + BytePos(6);
|
||||
(args_span.with_lo(pos).with_hi(pos), "move ")
|
||||
} else if string.starts_with("async|") {
|
||||
let pos = args_span.lo() + BytePos(5);
|
||||
let coro_prefix = if string.starts_with("async") {
|
||||
// `async` is 5 chars long. Not using `.len()` to avoid the cast from `usize` to `u32`
|
||||
Some(5)
|
||||
} else if string.starts_with("gen") {
|
||||
// `gen` is 3 chars long
|
||||
Some(3)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
if let Some(n) = coro_prefix {
|
||||
let pos = args_span.lo() + BytePos(n);
|
||||
(args_span.with_lo(pos).with_hi(pos), " move")
|
||||
} else {
|
||||
(args_span.shrink_to_lo(), "move ")
|
||||
@ -2505,6 +2511,11 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
|
||||
};
|
||||
let kind = match use_span.coroutine_kind() {
|
||||
Some(coroutine_kind) => match coroutine_kind {
|
||||
CoroutineKind::Gen(kind) => match kind {
|
||||
CoroutineSource::Block => "gen block",
|
||||
CoroutineSource::Closure => "gen closure",
|
||||
_ => bug!("gen block/closure expected, but gen function found."),
|
||||
},
|
||||
CoroutineKind::Async(async_kind) => match async_kind {
|
||||
CoroutineSource::Block => "async block",
|
||||
CoroutineSource::Closure => "async closure",
|
||||
|
@ -698,6 +698,20 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> {
|
||||
" of async function"
|
||||
}
|
||||
},
|
||||
Some(hir::CoroutineKind::Gen(gen)) => match gen {
|
||||
hir::CoroutineSource::Block => " of gen block",
|
||||
hir::CoroutineSource::Closure => " of gen closure",
|
||||
hir::CoroutineSource::Fn => {
|
||||
let parent_item =
|
||||
hir.get_by_def_id(hir.get_parent_item(mir_hir_id).def_id);
|
||||
let output = &parent_item
|
||||
.fn_decl()
|
||||
.expect("coroutine lowered from gen fn should be in fn")
|
||||
.output;
|
||||
span = output.span();
|
||||
" of gen function"
|
||||
}
|
||||
},
|
||||
Some(hir::CoroutineKind::Coroutine) => " of coroutine",
|
||||
None => " of closure",
|
||||
};
|
||||
|
@ -294,7 +294,7 @@ impl<'cx, 'a> Context<'cx, 'a> {
|
||||
// sync with the `rfc-2011-nicer-assert-messages/all-expr-kinds.rs` test.
|
||||
ExprKind::Assign(_, _, _)
|
||||
| ExprKind::AssignOp(_, _, _)
|
||||
| ExprKind::Async(_, _)
|
||||
| ExprKind::Gen(_, _, _)
|
||||
| ExprKind::Await(_, _)
|
||||
| ExprKind::Block(_, _)
|
||||
| ExprKind::Break(_, _)
|
||||
|
@ -560,6 +560,9 @@ pub fn push_item_name(tcx: TyCtxt<'_>, def_id: DefId, qualified: bool, output: &
|
||||
|
||||
fn coroutine_kind_label(coroutine_kind: Option<CoroutineKind>) -> &'static str {
|
||||
match coroutine_kind {
|
||||
Some(CoroutineKind::Gen(CoroutineSource::Block)) => "gen_block",
|
||||
Some(CoroutineKind::Gen(CoroutineSource::Closure)) => "gen_closure",
|
||||
Some(CoroutineKind::Gen(CoroutineSource::Fn)) => "gen_fn",
|
||||
Some(CoroutineKind::Async(CoroutineSource::Block)) => "async_block",
|
||||
Some(CoroutineKind::Async(CoroutineSource::Closure)) => "async_closure",
|
||||
Some(CoroutineKind::Async(CoroutineSource::Fn)) => "async_fn",
|
||||
|
@ -456,6 +456,8 @@ declare_features! (
|
||||
(unstable, ffi_returns_twice, "1.34.0", Some(58314), None),
|
||||
/// Allows using `#[repr(align(...))]` on function items
|
||||
(unstable, fn_align, "1.53.0", Some(82232), None),
|
||||
/// Allows defining gen blocks and `gen fn`.
|
||||
(unstable, gen_blocks, "CURRENT_RUSTC_VERSION", Some(117078), None),
|
||||
/// Infer generic args for both consts and types.
|
||||
(unstable, generic_arg_infer, "1.55.0", Some(85077), None),
|
||||
/// An extension to the `generic_associated_types` feature, allowing incomplete features.
|
||||
|
@ -1522,6 +1522,9 @@ pub enum CoroutineKind {
|
||||
/// An explicit `async` block or the body of an async function.
|
||||
Async(CoroutineSource),
|
||||
|
||||
/// An explicit `gen` block or the body of a `gen` function.
|
||||
Gen(CoroutineSource),
|
||||
|
||||
/// A coroutine literal created via a `yield` inside a closure.
|
||||
Coroutine,
|
||||
}
|
||||
@ -1538,6 +1541,14 @@ impl fmt::Display for CoroutineKind {
|
||||
k.fmt(f)
|
||||
}
|
||||
CoroutineKind::Coroutine => f.write_str("coroutine"),
|
||||
CoroutineKind::Gen(k) => {
|
||||
if f.alternate() {
|
||||
f.write_str("`gen` ")?;
|
||||
} else {
|
||||
f.write_str("gen ")?
|
||||
}
|
||||
k.fmt(f)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -2251,6 +2262,7 @@ impl From<CoroutineKind> for YieldSource {
|
||||
// Guess based on the kind of the current coroutine.
|
||||
CoroutineKind::Coroutine => Self::Yield,
|
||||
CoroutineKind::Async(_) => Self::Await { expr: None },
|
||||
CoroutineKind::Gen(_) => Self::Yield,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -58,15 +58,16 @@ pub(super) fn check_fn<'a, 'tcx>(
|
||||
if let Some(kind) = body.coroutine_kind
|
||||
&& can_be_coroutine.is_some()
|
||||
{
|
||||
let yield_ty = if kind == hir::CoroutineKind::Coroutine {
|
||||
let yield_ty = fcx.next_ty_var(TypeVariableOrigin {
|
||||
kind: TypeVariableOriginKind::TypeInference,
|
||||
span,
|
||||
});
|
||||
fcx.require_type_is_sized(yield_ty, span, traits::SizedYieldType);
|
||||
yield_ty
|
||||
} else {
|
||||
Ty::new_unit(tcx)
|
||||
let yield_ty = match kind {
|
||||
hir::CoroutineKind::Gen(..) | hir::CoroutineKind::Coroutine => {
|
||||
let yield_ty = fcx.next_ty_var(TypeVariableOrigin {
|
||||
kind: TypeVariableOriginKind::TypeInference,
|
||||
span,
|
||||
});
|
||||
fcx.require_type_is_sized(yield_ty, span, traits::SizedYieldType);
|
||||
yield_ty
|
||||
}
|
||||
hir::CoroutineKind::Async(..) => Ty::new_unit(tcx),
|
||||
};
|
||||
|
||||
// Resume type defaults to `()` if the coroutine has no argument.
|
||||
|
@ -652,6 +652,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
},
|
||||
)
|
||||
}
|
||||
Some(hir::CoroutineKind::Gen(hir::CoroutineSource::Fn)) => {
|
||||
todo!("gen closures do not exist yet")
|
||||
}
|
||||
|
||||
_ => astconv.ty_infer(None, decl.output.span()),
|
||||
},
|
||||
|
@ -148,8 +148,15 @@ impl<O> AssertKind<O> {
|
||||
RemainderByZero(_) => "attempt to calculate the remainder with a divisor of zero",
|
||||
ResumedAfterReturn(CoroutineKind::Coroutine) => "coroutine resumed after completion",
|
||||
ResumedAfterReturn(CoroutineKind::Async(_)) => "`async fn` resumed after completion",
|
||||
ResumedAfterReturn(CoroutineKind::Gen(_)) => {
|
||||
"`gen fn` should just keep returning `None` after completion"
|
||||
}
|
||||
ResumedAfterPanic(CoroutineKind::Coroutine) => "coroutine resumed after panicking",
|
||||
ResumedAfterPanic(CoroutineKind::Async(_)) => "`async fn` resumed after panicking",
|
||||
ResumedAfterPanic(CoroutineKind::Gen(_)) => {
|
||||
"`gen fn` should just keep returning `None` after panicking"
|
||||
}
|
||||
|
||||
BoundsCheck { .. } | MisalignedPointerDereference { .. } => {
|
||||
bug!("Unexpected AssertKind")
|
||||
}
|
||||
@ -236,10 +243,15 @@ impl<O> AssertKind<O> {
|
||||
DivisionByZero(_) => middle_assert_divide_by_zero,
|
||||
RemainderByZero(_) => middle_assert_remainder_by_zero,
|
||||
ResumedAfterReturn(CoroutineKind::Async(_)) => middle_assert_async_resume_after_return,
|
||||
ResumedAfterReturn(CoroutineKind::Gen(_)) => {
|
||||
bug!("gen blocks can be resumed after they return and will keep returning `None`")
|
||||
}
|
||||
ResumedAfterReturn(CoroutineKind::Coroutine) => {
|
||||
middle_assert_coroutine_resume_after_return
|
||||
}
|
||||
ResumedAfterPanic(CoroutineKind::Async(_)) => middle_assert_async_resume_after_panic,
|
||||
// FIXME(gen_blocks): custom error message for `gen` blocks
|
||||
ResumedAfterPanic(CoroutineKind::Gen(_)) => middle_assert_async_resume_after_panic,
|
||||
ResumedAfterPanic(CoroutineKind::Coroutine) => {
|
||||
middle_assert_coroutine_resume_after_panic
|
||||
}
|
||||
|
@ -144,6 +144,10 @@ pub enum SelectionCandidate<'tcx> {
|
||||
/// generated for an async construct.
|
||||
FutureCandidate,
|
||||
|
||||
/// Implementation of an `Iterator` trait by one of the generator types
|
||||
/// generated for a gen construct.
|
||||
IteratorCandidate,
|
||||
|
||||
/// Implementation of a `Fn`-family trait by one of the anonymous
|
||||
/// types generated for a fn pointer type (e.g., `fn(int) -> int`)
|
||||
FnPointerCandidate {
|
||||
|
@ -782,6 +782,17 @@ impl<'tcx> TyCtxt<'tcx> {
|
||||
matches!(self.coroutine_kind(def_id), Some(hir::CoroutineKind::Async(_)))
|
||||
}
|
||||
|
||||
/// Returns `true` if the node pointed to by `def_id` is a general coroutine that implements `Coroutine`.
|
||||
/// This means it is neither an `async` or `gen` construct.
|
||||
pub fn is_general_coroutine(self, def_id: DefId) -> bool {
|
||||
matches!(self.coroutine_kind(def_id), Some(hir::CoroutineKind::Coroutine))
|
||||
}
|
||||
|
||||
/// Returns `true` if the node pointed to by `def_id` is a coroutine for a gen construct.
|
||||
pub fn coroutine_is_gen(self, def_id: DefId) -> bool {
|
||||
matches!(self.coroutine_kind(def_id), Some(hir::CoroutineKind::Gen(_)))
|
||||
}
|
||||
|
||||
pub fn stability(self) -> &'tcx stability::Index {
|
||||
self.stability_index(())
|
||||
}
|
||||
|
@ -749,6 +749,7 @@ impl<'tcx> TyCtxt<'tcx> {
|
||||
DefKind::Coroutine => match self.coroutine_kind(def_id).unwrap() {
|
||||
rustc_hir::CoroutineKind::Async(..) => "async closure",
|
||||
rustc_hir::CoroutineKind::Coroutine => "coroutine",
|
||||
rustc_hir::CoroutineKind::Gen(..) => "gen closure",
|
||||
},
|
||||
_ => def_kind.descr(def_id),
|
||||
}
|
||||
@ -766,6 +767,7 @@ impl<'tcx> TyCtxt<'tcx> {
|
||||
DefKind::Coroutine => match self.coroutine_kind(def_id).unwrap() {
|
||||
rustc_hir::CoroutineKind::Async(..) => "an",
|
||||
rustc_hir::CoroutineKind::Coroutine => "a",
|
||||
rustc_hir::CoroutineKind::Gen(..) => "a",
|
||||
},
|
||||
_ => def_kind.article(),
|
||||
}
|
||||
|
@ -224,7 +224,7 @@ struct SuspensionPoint<'tcx> {
|
||||
|
||||
struct TransformVisitor<'tcx> {
|
||||
tcx: TyCtxt<'tcx>,
|
||||
is_async_kind: bool,
|
||||
coroutine_kind: hir::CoroutineKind,
|
||||
state_adt_ref: AdtDef<'tcx>,
|
||||
state_args: GenericArgsRef<'tcx>,
|
||||
|
||||
@ -249,6 +249,47 @@ struct TransformVisitor<'tcx> {
|
||||
}
|
||||
|
||||
impl<'tcx> TransformVisitor<'tcx> {
|
||||
fn insert_none_ret_block(&self, body: &mut Body<'tcx>) -> BasicBlock {
|
||||
let block = BasicBlock::new(body.basic_blocks.len());
|
||||
|
||||
let source_info = SourceInfo::outermost(body.span);
|
||||
|
||||
let (kind, idx) = self.coroutine_state_adt_and_variant_idx(true);
|
||||
assert_eq!(self.state_adt_ref.variant(idx).fields.len(), 0);
|
||||
let statements = vec![Statement {
|
||||
kind: StatementKind::Assign(Box::new((
|
||||
Place::return_place(),
|
||||
Rvalue::Aggregate(Box::new(kind), IndexVec::new()),
|
||||
))),
|
||||
source_info,
|
||||
}];
|
||||
|
||||
body.basic_blocks_mut().push(BasicBlockData {
|
||||
statements,
|
||||
terminator: Some(Terminator { source_info, kind: TerminatorKind::Return }),
|
||||
is_cleanup: false,
|
||||
});
|
||||
|
||||
block
|
||||
}
|
||||
|
||||
fn coroutine_state_adt_and_variant_idx(
|
||||
&self,
|
||||
is_return: bool,
|
||||
) -> (AggregateKind<'tcx>, VariantIdx) {
|
||||
let idx = VariantIdx::new(match (is_return, self.coroutine_kind) {
|
||||
(true, hir::CoroutineKind::Coroutine) => 1, // CoroutineState::Complete
|
||||
(false, hir::CoroutineKind::Coroutine) => 0, // CoroutineState::Yielded
|
||||
(true, hir::CoroutineKind::Async(_)) => 0, // Poll::Ready
|
||||
(false, hir::CoroutineKind::Async(_)) => 1, // Poll::Pending
|
||||
(true, hir::CoroutineKind::Gen(_)) => 0, // Option::None
|
||||
(false, hir::CoroutineKind::Gen(_)) => 1, // Option::Some
|
||||
});
|
||||
|
||||
let kind = AggregateKind::Adt(self.state_adt_ref.did(), idx, self.state_args, None, None);
|
||||
(kind, idx)
|
||||
}
|
||||
|
||||
// Make a `CoroutineState` or `Poll` variant assignment.
|
||||
//
|
||||
// `core::ops::CoroutineState` only has single element tuple variants,
|
||||
@ -261,31 +302,44 @@ impl<'tcx> TransformVisitor<'tcx> {
|
||||
is_return: bool,
|
||||
statements: &mut Vec<Statement<'tcx>>,
|
||||
) {
|
||||
let idx = VariantIdx::new(match (is_return, self.is_async_kind) {
|
||||
(true, false) => 1, // CoroutineState::Complete
|
||||
(false, false) => 0, // CoroutineState::Yielded
|
||||
(true, true) => 0, // Poll::Ready
|
||||
(false, true) => 1, // Poll::Pending
|
||||
});
|
||||
let (kind, idx) = self.coroutine_state_adt_and_variant_idx(is_return);
|
||||
|
||||
let kind = AggregateKind::Adt(self.state_adt_ref.did(), idx, self.state_args, None, None);
|
||||
match self.coroutine_kind {
|
||||
// `Poll::Pending`
|
||||
CoroutineKind::Async(_) => {
|
||||
if !is_return {
|
||||
assert_eq!(self.state_adt_ref.variant(idx).fields.len(), 0);
|
||||
|
||||
// `Poll::Pending`
|
||||
if self.is_async_kind && idx == VariantIdx::new(1) {
|
||||
assert_eq!(self.state_adt_ref.variant(idx).fields.len(), 0);
|
||||
// FIXME(swatinem): assert that `val` is indeed unit?
|
||||
statements.push(Statement {
|
||||
kind: StatementKind::Assign(Box::new((
|
||||
Place::return_place(),
|
||||
Rvalue::Aggregate(Box::new(kind), IndexVec::new()),
|
||||
))),
|
||||
source_info,
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
// `Option::None`
|
||||
CoroutineKind::Gen(_) => {
|
||||
if is_return {
|
||||
assert_eq!(self.state_adt_ref.variant(idx).fields.len(), 0);
|
||||
|
||||
// FIXME(swatinem): assert that `val` is indeed unit?
|
||||
statements.push(Statement {
|
||||
kind: StatementKind::Assign(Box::new((
|
||||
Place::return_place(),
|
||||
Rvalue::Aggregate(Box::new(kind), IndexVec::new()),
|
||||
))),
|
||||
source_info,
|
||||
});
|
||||
return;
|
||||
statements.push(Statement {
|
||||
kind: StatementKind::Assign(Box::new((
|
||||
Place::return_place(),
|
||||
Rvalue::Aggregate(Box::new(kind), IndexVec::new()),
|
||||
))),
|
||||
source_info,
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
CoroutineKind::Coroutine => {}
|
||||
}
|
||||
|
||||
// else: `Poll::Ready(x)`, `CoroutineState::Yielded(x)` or `CoroutineState::Complete(x)`
|
||||
// else: `Poll::Ready(x)`, `CoroutineState::Yielded(x)`, `CoroutineState::Complete(x)`, or `Option::Some(x)`
|
||||
assert_eq!(self.state_adt_ref.variant(idx).fields.len(), 1);
|
||||
|
||||
statements.push(Statement {
|
||||
@ -1263,10 +1317,13 @@ fn create_coroutine_resume_function<'tcx>(
|
||||
}
|
||||
|
||||
if can_return {
|
||||
cases.insert(
|
||||
1,
|
||||
(RETURNED, insert_panic_block(tcx, body, ResumedAfterReturn(coroutine_kind))),
|
||||
);
|
||||
let block = match coroutine_kind {
|
||||
CoroutineKind::Async(_) | CoroutineKind::Coroutine => {
|
||||
insert_panic_block(tcx, body, ResumedAfterReturn(coroutine_kind))
|
||||
}
|
||||
CoroutineKind::Gen(_) => transform.insert_none_ret_block(body),
|
||||
};
|
||||
cases.insert(1, (RETURNED, block));
|
||||
}
|
||||
|
||||
insert_switch(body, cases, &transform, TerminatorKind::Unreachable);
|
||||
@ -1439,18 +1496,28 @@ impl<'tcx> MirPass<'tcx> for StateTransform {
|
||||
};
|
||||
|
||||
let is_async_kind = matches!(body.coroutine_kind(), Some(CoroutineKind::Async(_)));
|
||||
let (state_adt_ref, state_args) = if is_async_kind {
|
||||
// Compute Poll<return_ty>
|
||||
let poll_did = tcx.require_lang_item(LangItem::Poll, None);
|
||||
let poll_adt_ref = tcx.adt_def(poll_did);
|
||||
let poll_args = tcx.mk_args(&[body.return_ty().into()]);
|
||||
(poll_adt_ref, poll_args)
|
||||
} else {
|
||||
// Compute CoroutineState<yield_ty, return_ty>
|
||||
let state_did = tcx.require_lang_item(LangItem::CoroutineState, None);
|
||||
let state_adt_ref = tcx.adt_def(state_did);
|
||||
let state_args = tcx.mk_args(&[yield_ty.into(), body.return_ty().into()]);
|
||||
(state_adt_ref, state_args)
|
||||
let (state_adt_ref, state_args) = match body.coroutine_kind().unwrap() {
|
||||
CoroutineKind::Async(_) => {
|
||||
// Compute Poll<return_ty>
|
||||
let poll_did = tcx.require_lang_item(LangItem::Poll, None);
|
||||
let poll_adt_ref = tcx.adt_def(poll_did);
|
||||
let poll_args = tcx.mk_args(&[body.return_ty().into()]);
|
||||
(poll_adt_ref, poll_args)
|
||||
}
|
||||
CoroutineKind::Gen(_) => {
|
||||
// Compute Option<yield_ty>
|
||||
let option_did = tcx.require_lang_item(LangItem::Option, None);
|
||||
let option_adt_ref = tcx.adt_def(option_did);
|
||||
let option_args = tcx.mk_args(&[body.yield_ty().unwrap().into()]);
|
||||
(option_adt_ref, option_args)
|
||||
}
|
||||
CoroutineKind::Coroutine => {
|
||||
// Compute CoroutineState<yield_ty, return_ty>
|
||||
let state_did = tcx.require_lang_item(LangItem::CoroutineState, None);
|
||||
let state_adt_ref = tcx.adt_def(state_did);
|
||||
let state_args = tcx.mk_args(&[yield_ty.into(), body.return_ty().into()]);
|
||||
(state_adt_ref, state_args)
|
||||
}
|
||||
};
|
||||
let ret_ty = Ty::new_adt(tcx, state_adt_ref, state_args);
|
||||
|
||||
@ -1518,7 +1585,7 @@ impl<'tcx> MirPass<'tcx> for StateTransform {
|
||||
// or Poll::Ready(x) and Poll::Pending respectively depending on `is_async_kind`.
|
||||
let mut transform = TransformVisitor {
|
||||
tcx,
|
||||
is_async_kind,
|
||||
coroutine_kind: body.coroutine_kind().unwrap(),
|
||||
state_adt_ref,
|
||||
state_args,
|
||||
remap,
|
||||
|
@ -278,6 +278,9 @@ parse_found_expr_would_be_stmt = expected expression, found `{$token}`
|
||||
parse_function_body_equals_expr = function body cannot be `= expression;`
|
||||
.suggestion = surround the expression with `{"{"}` and `{"}"}` instead of `=` and `;`
|
||||
|
||||
parse_gen_block = `gen` blocks are not yet implemented
|
||||
.help = only the keyword is reserved for now
|
||||
|
||||
parse_generic_args_in_pat_require_turbofish_syntax = generic args in patterns require the turbofish syntax
|
||||
|
||||
parse_generic_parameters_without_angle_brackets = generic parameters without surrounding angle brackets
|
||||
|
@ -520,6 +520,14 @@ pub(crate) struct CatchAfterTry {
|
||||
pub span: Span,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(parse_gen_block)]
|
||||
#[help]
|
||||
pub(crate) struct GenBlock {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(parse_comma_after_base_struct)]
|
||||
#[note]
|
||||
|
@ -9,7 +9,7 @@ use super::{
|
||||
use crate::errors;
|
||||
use crate::maybe_recover_from_interpolated_ty_qpath;
|
||||
use ast::mut_visit::{noop_visit_expr, MutVisitor};
|
||||
use ast::{Path, PathSegment};
|
||||
use ast::{GenBlockKind, Path, PathSegment};
|
||||
use core::mem;
|
||||
use rustc_ast::ptr::P;
|
||||
use rustc_ast::token::{self, Delimiter, Token, TokenKind};
|
||||
@ -1441,14 +1441,20 @@ impl<'a> Parser<'a> {
|
||||
} else if this.token.uninterpolated_span().at_least_rust_2018() {
|
||||
// `Span:.at_least_rust_2018()` is somewhat expensive; don't get it repeatedly.
|
||||
if this.check_keyword(kw::Async) {
|
||||
if this.is_async_block() {
|
||||
if this.is_gen_block(kw::Async) {
|
||||
// Check for `async {` and `async move {`.
|
||||
this.parse_async_block()
|
||||
this.parse_gen_block()
|
||||
} else {
|
||||
this.parse_expr_closure()
|
||||
}
|
||||
} else if this.eat_keyword(kw::Await) {
|
||||
this.recover_incorrect_await_syntax(lo, this.prev_token.span)
|
||||
} else if this.token.uninterpolated_span().at_least_rust_2024() {
|
||||
if this.is_gen_block(kw::Gen) {
|
||||
this.parse_gen_block()
|
||||
} else {
|
||||
this.parse_expr_lit()
|
||||
}
|
||||
} else {
|
||||
this.parse_expr_lit()
|
||||
}
|
||||
@ -1848,7 +1854,7 @@ impl<'a> Parser<'a> {
|
||||
let lo = self.prev_token.span;
|
||||
let kind = ExprKind::Yield(self.parse_expr_opt()?);
|
||||
let span = lo.to(self.prev_token.span);
|
||||
self.sess.gated_spans.gate(sym::coroutines, span);
|
||||
self.sess.gated_spans.gate(sym::yield_expr, span);
|
||||
let expr = self.mk_expr(span, kind);
|
||||
self.maybe_recover_from_bad_qpath(expr)
|
||||
}
|
||||
@ -3059,18 +3065,24 @@ impl<'a> Parser<'a> {
|
||||
&& self.token.uninterpolated_span().at_least_rust_2018()
|
||||
}
|
||||
|
||||
/// Parses an `async move? {...}` expression.
|
||||
fn parse_async_block(&mut self) -> PResult<'a, P<Expr>> {
|
||||
/// Parses an `async move? {...}` or `gen move? {...}` expression.
|
||||
fn parse_gen_block(&mut self) -> PResult<'a, P<Expr>> {
|
||||
let lo = self.token.span;
|
||||
self.expect_keyword(kw::Async)?;
|
||||
let kind = if self.eat_keyword(kw::Async) {
|
||||
GenBlockKind::Async
|
||||
} else {
|
||||
assert!(self.eat_keyword(kw::Gen));
|
||||
self.sess.gated_spans.gate(sym::gen_blocks, lo.to(self.token.span));
|
||||
GenBlockKind::Gen
|
||||
};
|
||||
let capture_clause = self.parse_capture_clause()?;
|
||||
let (attrs, body) = self.parse_inner_attrs_and_block()?;
|
||||
let kind = ExprKind::Async(capture_clause, body);
|
||||
let kind = ExprKind::Gen(capture_clause, body, kind);
|
||||
Ok(self.mk_expr_with_attrs(lo.to(self.prev_token.span), kind, attrs))
|
||||
}
|
||||
|
||||
fn is_async_block(&self) -> bool {
|
||||
self.token.is_keyword(kw::Async)
|
||||
fn is_gen_block(&self, kw: Symbol) -> bool {
|
||||
self.token.is_keyword(kw)
|
||||
&& ((
|
||||
// `async move {`
|
||||
self.is_keyword_ahead(1, &[kw::Move])
|
||||
@ -3596,7 +3608,7 @@ impl MutVisitor for CondChecker<'_> {
|
||||
| ExprKind::Match(_, _)
|
||||
| ExprKind::Closure(_)
|
||||
| ExprKind::Block(_, _)
|
||||
| ExprKind::Async(_, _)
|
||||
| ExprKind::Gen(_, _, _)
|
||||
| ExprKind::TryBlock(_)
|
||||
| ExprKind::Underscore
|
||||
| ExprKind::Path(_, _)
|
||||
|
@ -2297,9 +2297,9 @@ impl<'a> Parser<'a> {
|
||||
// `pub` is added in case users got confused with the ordering like `async pub fn`,
|
||||
// only if it wasn't preceded by `default` as `default pub` is invalid.
|
||||
let quals: &[Symbol] = if check_pub {
|
||||
&[kw::Pub, kw::Const, kw::Async, kw::Unsafe, kw::Extern]
|
||||
&[kw::Pub, kw::Gen, kw::Const, kw::Async, kw::Unsafe, kw::Extern]
|
||||
} else {
|
||||
&[kw::Const, kw::Async, kw::Unsafe, kw::Extern]
|
||||
&[kw::Gen, kw::Const, kw::Async, kw::Unsafe, kw::Extern]
|
||||
};
|
||||
self.check_keyword_case(kw::Fn, case) // Definitely an `fn`.
|
||||
// `$qual fn` or `$qual $qual`:
|
||||
@ -2353,6 +2353,9 @@ impl<'a> Parser<'a> {
|
||||
let async_start_sp = self.token.span;
|
||||
let asyncness = self.parse_asyncness(case);
|
||||
|
||||
let _gen_start_sp = self.token.span;
|
||||
let genness = self.parse_genness(case);
|
||||
|
||||
let unsafe_start_sp = self.token.span;
|
||||
let unsafety = self.parse_unsafety(case);
|
||||
|
||||
@ -2368,6 +2371,10 @@ impl<'a> Parser<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
if let Gen::Yes { span, .. } = genness {
|
||||
self.sess.emit_err(errors::GenBlock { span });
|
||||
}
|
||||
|
||||
if !self.eat_keyword_case(kw::Fn, case) {
|
||||
// It is possible for `expect_one_of` to recover given the contents of
|
||||
// `self.expected_tokens`, therefore, do not use `self.unexpected()` which doesn't
|
||||
|
@ -11,6 +11,7 @@ mod stmt;
|
||||
mod ty;
|
||||
|
||||
use crate::lexer::UnmatchedDelim;
|
||||
use ast::Gen;
|
||||
pub use attr_wrapper::AttrWrapper;
|
||||
pub use diagnostics::AttemptLocalParseRecovery;
|
||||
pub(crate) use expr::ForbiddenLetReason;
|
||||
@ -1128,6 +1129,16 @@ impl<'a> Parser<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Parses genness: `gen` or nothing.
|
||||
fn parse_genness(&mut self, case: Case) -> Gen {
|
||||
if self.token.span.at_least_rust_2024() && self.eat_keyword_case(kw::Gen, case) {
|
||||
let span = self.prev_token.uninterpolated_span();
|
||||
Gen::Yes { span, closure_id: DUMMY_NODE_ID, return_impl_trait_id: DUMMY_NODE_ID }
|
||||
} else {
|
||||
Gen::No
|
||||
}
|
||||
}
|
||||
|
||||
/// Parses unsafety: `unsafe` or nothing.
|
||||
fn parse_unsafety(&mut self, case: Case) -> Unsafe {
|
||||
if self.eat_keyword_case(kw::Unsafe, case) {
|
||||
|
@ -567,10 +567,10 @@ impl<'v> ast_visit::Visitor<'v> for StatCollector<'v> {
|
||||
(self, e, e.kind, Id::None, ast, Expr, ExprKind),
|
||||
[
|
||||
Array, ConstBlock, Call, MethodCall, Tup, Binary, Unary, Lit, Cast, Type, Let,
|
||||
If, While, ForLoop, Loop, Match, Closure, Block, Async, Await, TryBlock, Assign,
|
||||
If, While, ForLoop, Loop, Match, Closure, Block, Await, TryBlock, Assign,
|
||||
AssignOp, Field, Index, Range, Underscore, Path, AddrOf, Break, Continue, Ret,
|
||||
InlineAsm, FormatArgs, OffsetOf, MacCall, Struct, Repeat, Paren, Try, Yield, Yeet,
|
||||
Become, IncludedBytes, Err
|
||||
Become, IncludedBytes, Gen, Err
|
||||
]
|
||||
);
|
||||
ast_visit::walk_expr(self, e)
|
||||
|
@ -260,7 +260,7 @@ impl<'a, 'b, 'tcx> visit::Visitor<'a> for DefCollector<'a, 'b, 'tcx> {
|
||||
Async::No => closure_def,
|
||||
}
|
||||
}
|
||||
ExprKind::Async(_, _) => self.create_def(expr.id, DefPathData::ClosureExpr, expr.span),
|
||||
ExprKind::Gen(_, _, _) => self.create_def(expr.id, DefPathData::ClosureExpr, expr.span),
|
||||
_ => self.parent_def,
|
||||
};
|
||||
|
||||
|
@ -1083,7 +1083,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
|
||||
for rib in ribs {
|
||||
match rib.kind {
|
||||
RibKind::Normal
|
||||
| RibKind::ClosureOrAsync
|
||||
| RibKind::FnOrCoroutine
|
||||
| RibKind::Module(..)
|
||||
| RibKind::MacroDefinition(..)
|
||||
| RibKind::ForwardGenericParamBan => {
|
||||
@ -1156,7 +1156,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
|
||||
for rib in ribs {
|
||||
let has_generic_params: HasGenericParams = match rib.kind {
|
||||
RibKind::Normal
|
||||
| RibKind::ClosureOrAsync
|
||||
| RibKind::FnOrCoroutine
|
||||
| RibKind::Module(..)
|
||||
| RibKind::MacroDefinition(..)
|
||||
| RibKind::InlineAsmSym
|
||||
@ -1240,7 +1240,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
|
||||
for rib in ribs {
|
||||
let has_generic_params = match rib.kind {
|
||||
RibKind::Normal
|
||||
| RibKind::ClosureOrAsync
|
||||
| RibKind::FnOrCoroutine
|
||||
| RibKind::Module(..)
|
||||
| RibKind::MacroDefinition(..)
|
||||
| RibKind::InlineAsmSym
|
||||
|
@ -177,8 +177,8 @@ pub(crate) enum RibKind<'a> {
|
||||
/// upvars).
|
||||
AssocItem,
|
||||
|
||||
/// We passed through a closure. Disallow labels.
|
||||
ClosureOrAsync,
|
||||
/// We passed through a function, closure or coroutine signature. Disallow labels.
|
||||
FnOrCoroutine,
|
||||
|
||||
/// We passed through an item scope. Disallow upvars.
|
||||
Item(HasGenericParams),
|
||||
@ -215,7 +215,7 @@ impl RibKind<'_> {
|
||||
pub(crate) fn contains_params(&self) -> bool {
|
||||
match self {
|
||||
RibKind::Normal
|
||||
| RibKind::ClosureOrAsync
|
||||
| RibKind::FnOrCoroutine
|
||||
| RibKind::ConstantItem(..)
|
||||
| RibKind::Module(_)
|
||||
| RibKind::MacroDefinition(_)
|
||||
@ -231,7 +231,7 @@ impl RibKind<'_> {
|
||||
RibKind::Normal | RibKind::MacroDefinition(..) => false,
|
||||
|
||||
RibKind::AssocItem
|
||||
| RibKind::ClosureOrAsync
|
||||
| RibKind::FnOrCoroutine
|
||||
| RibKind::Item(..)
|
||||
| RibKind::ConstantItem(..)
|
||||
| RibKind::Module(..)
|
||||
@ -924,9 +924,9 @@ impl<'a: 'ast, 'ast, 'tcx> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast,
|
||||
debug!("(resolving function) entering function");
|
||||
|
||||
// Create a value rib for the function.
|
||||
self.with_rib(ValueNS, RibKind::ClosureOrAsync, |this| {
|
||||
self.with_rib(ValueNS, RibKind::FnOrCoroutine, |this| {
|
||||
// Create a label rib for the function.
|
||||
this.with_label_rib(RibKind::ClosureOrAsync, |this| {
|
||||
this.with_label_rib(RibKind::FnOrCoroutine, |this| {
|
||||
match fn_kind {
|
||||
FnKind::Fn(_, _, sig, _, generics, body) => {
|
||||
this.visit_generics(generics);
|
||||
@ -4287,7 +4287,7 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> {
|
||||
..
|
||||
}) => {
|
||||
self.with_rib(ValueNS, RibKind::Normal, |this| {
|
||||
this.with_label_rib(RibKind::ClosureOrAsync, |this| {
|
||||
this.with_label_rib(RibKind::FnOrCoroutine, |this| {
|
||||
// Resolve arguments:
|
||||
this.resolve_params(&fn_decl.inputs);
|
||||
// No need to resolve return type --
|
||||
@ -4304,7 +4304,7 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> {
|
||||
})
|
||||
});
|
||||
}
|
||||
// For closures, ClosureOrAsyncRibKind is added in visit_fn
|
||||
// For closures, RibKind::FnOrCoroutine is added in visit_fn
|
||||
ExprKind::Closure(box ast::Closure {
|
||||
binder: ClosureBinder::For { ref generic_params, span },
|
||||
..
|
||||
@ -4321,8 +4321,8 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> {
|
||||
);
|
||||
}
|
||||
ExprKind::Closure(..) => visit::walk_expr(self, expr),
|
||||
ExprKind::Async(..) => {
|
||||
self.with_label_rib(RibKind::ClosureOrAsync, |this| visit::walk_expr(this, expr));
|
||||
ExprKind::Gen(..) => {
|
||||
self.with_label_rib(RibKind::FnOrCoroutine, |this| visit::walk_expr(this, expr));
|
||||
}
|
||||
ExprKind::Repeat(ref elem, ref ct) => {
|
||||
self.visit_expr(elem);
|
||||
|
@ -879,18 +879,28 @@ impl<'tcx> Stable<'tcx> for mir::AggregateKind<'tcx> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> Stable<'tcx> for rustc_hir::CoroutineSource {
|
||||
type T = stable_mir::mir::CoroutineSource;
|
||||
fn stable(&self, _: &mut Tables<'tcx>) -> Self::T {
|
||||
use rustc_hir::CoroutineSource;
|
||||
match self {
|
||||
CoroutineSource::Block => stable_mir::mir::CoroutineSource::Block,
|
||||
CoroutineSource::Closure => stable_mir::mir::CoroutineSource::Closure,
|
||||
CoroutineSource::Fn => stable_mir::mir::CoroutineSource::Fn,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> Stable<'tcx> for rustc_hir::CoroutineKind {
|
||||
type T = stable_mir::mir::CoroutineKind;
|
||||
fn stable(&self, _: &mut Tables<'tcx>) -> Self::T {
|
||||
use rustc_hir::{CoroutineKind, CoroutineSource};
|
||||
fn stable(&self, tables: &mut Tables<'tcx>) -> Self::T {
|
||||
use rustc_hir::CoroutineKind;
|
||||
match self {
|
||||
CoroutineKind::Async(async_gen) => {
|
||||
let async_gen = match async_gen {
|
||||
CoroutineSource::Block => stable_mir::mir::CoroutineSource::Block,
|
||||
CoroutineSource::Closure => stable_mir::mir::CoroutineSource::Closure,
|
||||
CoroutineSource::Fn => stable_mir::mir::CoroutineSource::Fn,
|
||||
};
|
||||
stable_mir::mir::CoroutineKind::Async(async_gen)
|
||||
CoroutineKind::Async(source) => {
|
||||
stable_mir::mir::CoroutineKind::Async(source.stable(tables))
|
||||
}
|
||||
CoroutineKind::Gen(source) => {
|
||||
stable_mir::mir::CoroutineKind::Gen(source.stable(tables))
|
||||
}
|
||||
CoroutineKind::Coroutine => stable_mir::mir::CoroutineKind::Coroutine,
|
||||
}
|
||||
|
@ -98,6 +98,7 @@ symbols! {
|
||||
Builtin: "builtin",
|
||||
Catch: "catch",
|
||||
Default: "default",
|
||||
Gen: "gen",
|
||||
MacroRules: "macro_rules",
|
||||
Raw: "raw",
|
||||
Union: "union",
|
||||
@ -225,6 +226,7 @@ symbols! {
|
||||
IpAddr,
|
||||
IrTyKind,
|
||||
Is,
|
||||
Item,
|
||||
ItemContext,
|
||||
IterEmpty,
|
||||
IterOnce,
|
||||
@ -818,6 +820,7 @@ symbols! {
|
||||
future_trait,
|
||||
gdb_script_file,
|
||||
ge,
|
||||
gen_blocks,
|
||||
gen_future,
|
||||
gen_kill,
|
||||
generator_clone,
|
||||
@ -1779,6 +1782,7 @@ symbols! {
|
||||
xmm_reg,
|
||||
yeet_desugar_details,
|
||||
yeet_expr,
|
||||
yield_expr,
|
||||
ymm_reg,
|
||||
zmm_reg,
|
||||
}
|
||||
@ -2189,8 +2193,9 @@ impl Symbol {
|
||||
self >= kw::Abstract && self <= kw::Yield
|
||||
}
|
||||
|
||||
fn is_unused_keyword_conditional(self, edition: impl FnOnce() -> Edition) -> bool {
|
||||
self == kw::Try && edition() >= Edition::Edition2018
|
||||
fn is_unused_keyword_conditional(self, edition: impl Copy + FnOnce() -> Edition) -> bool {
|
||||
self == kw::Try && edition().at_least_rust_2018()
|
||||
|| self == kw::Gen && edition().at_least_rust_2024()
|
||||
}
|
||||
|
||||
pub fn is_reserved(self, edition: impl Copy + FnOnce() -> Edition) -> bool {
|
||||
|
@ -201,7 +201,15 @@ pub(super) trait GoalKind<'tcx>:
|
||||
goal: Goal<'tcx, Self>,
|
||||
) -> QueryResult<'tcx>;
|
||||
|
||||
/// A coroutine (that doesn't come from an `async` desugaring) is known to
|
||||
/// A coroutine (that comes from a `gen` desugaring) is known to implement
|
||||
/// `Iterator<Item = O>`, where `O` is given by the generator's yield type
|
||||
/// that was computed during type-checking.
|
||||
fn consider_builtin_iterator_candidate(
|
||||
ecx: &mut EvalCtxt<'_, 'tcx>,
|
||||
goal: Goal<'tcx, Self>,
|
||||
) -> QueryResult<'tcx>;
|
||||
|
||||
/// A coroutine (that doesn't come from an `async` or `gen` desugaring) is known to
|
||||
/// implement `Coroutine<R, Yield = Y, Return = O>`, given the resume, yield,
|
||||
/// and return types of the coroutine computed during type-checking.
|
||||
fn consider_builtin_coroutine_candidate(
|
||||
@ -554,6 +562,8 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
|
||||
G::consider_builtin_pointee_candidate(self, goal)
|
||||
} else if lang_items.future_trait() == Some(trait_def_id) {
|
||||
G::consider_builtin_future_candidate(self, goal)
|
||||
} else if lang_items.iterator_trait() == Some(trait_def_id) {
|
||||
G::consider_builtin_iterator_candidate(self, goal)
|
||||
} else if lang_items.gen_trait() == Some(trait_def_id) {
|
||||
G::consider_builtin_coroutine_candidate(self, goal)
|
||||
} else if lang_items.discriminant_kind_trait() == Some(trait_def_id) {
|
||||
|
@ -489,6 +489,37 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> {
|
||||
)
|
||||
}
|
||||
|
||||
fn consider_builtin_iterator_candidate(
|
||||
ecx: &mut EvalCtxt<'_, 'tcx>,
|
||||
goal: Goal<'tcx, Self>,
|
||||
) -> QueryResult<'tcx> {
|
||||
let self_ty = goal.predicate.self_ty();
|
||||
let ty::Coroutine(def_id, args, _) = *self_ty.kind() else {
|
||||
return Err(NoSolution);
|
||||
};
|
||||
|
||||
// Coroutines are not Iterators unless they come from `gen` desugaring
|
||||
let tcx = ecx.tcx();
|
||||
if !tcx.coroutine_is_gen(def_id) {
|
||||
return Err(NoSolution);
|
||||
}
|
||||
|
||||
let term = args.as_coroutine().yield_ty().into();
|
||||
|
||||
Self::consider_implied_clause(
|
||||
ecx,
|
||||
goal,
|
||||
ty::ProjectionPredicate {
|
||||
projection_ty: ty::AliasTy::new(ecx.tcx(), goal.predicate.def_id(), [self_ty]),
|
||||
term,
|
||||
}
|
||||
.to_predicate(tcx),
|
||||
// Technically, we need to check that the iterator type is Sized,
|
||||
// but that's already proven by the generator being WF.
|
||||
[],
|
||||
)
|
||||
}
|
||||
|
||||
fn consider_builtin_coroutine_candidate(
|
||||
ecx: &mut EvalCtxt<'_, 'tcx>,
|
||||
goal: Goal<'tcx, Self>,
|
||||
@ -500,7 +531,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> {
|
||||
|
||||
// `async`-desugared coroutines do not implement the coroutine trait
|
||||
let tcx = ecx.tcx();
|
||||
if tcx.coroutine_is_async(def_id) {
|
||||
if !tcx.is_general_coroutine(def_id) {
|
||||
return Err(NoSolution);
|
||||
}
|
||||
|
||||
@ -527,7 +558,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> {
|
||||
term,
|
||||
}
|
||||
.to_predicate(tcx),
|
||||
// Technically, we need to check that the future type is Sized,
|
||||
// Technically, we need to check that the coroutine type is Sized,
|
||||
// but that's already proven by the coroutine being WF.
|
||||
[],
|
||||
)
|
||||
|
@ -350,6 +350,30 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
|
||||
ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
|
||||
}
|
||||
|
||||
fn consider_builtin_iterator_candidate(
|
||||
ecx: &mut EvalCtxt<'_, 'tcx>,
|
||||
goal: Goal<'tcx, Self>,
|
||||
) -> QueryResult<'tcx> {
|
||||
if goal.predicate.polarity != ty::ImplPolarity::Positive {
|
||||
return Err(NoSolution);
|
||||
}
|
||||
|
||||
let ty::Coroutine(def_id, _, _) = *goal.predicate.self_ty().kind() else {
|
||||
return Err(NoSolution);
|
||||
};
|
||||
|
||||
// Coroutines are not iterators unless they come from `gen` desugaring
|
||||
let tcx = ecx.tcx();
|
||||
if !tcx.coroutine_is_gen(def_id) {
|
||||
return Err(NoSolution);
|
||||
}
|
||||
|
||||
// Gen coroutines unconditionally implement `Iterator`
|
||||
// Technically, we need to check that the iterator output type is Sized,
|
||||
// but that's already proven by the coroutines being WF.
|
||||
ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
|
||||
}
|
||||
|
||||
fn consider_builtin_coroutine_candidate(
|
||||
ecx: &mut EvalCtxt<'_, 'tcx>,
|
||||
goal: Goal<'tcx, Self>,
|
||||
@ -365,7 +389,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
|
||||
|
||||
// `async`-desugared coroutines do not implement the coroutine trait
|
||||
let tcx = ecx.tcx();
|
||||
if tcx.coroutine_is_async(def_id) {
|
||||
if !tcx.is_general_coroutine(def_id) {
|
||||
return Err(NoSolution);
|
||||
}
|
||||
|
||||
|
@ -2425,6 +2425,21 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
|
||||
CoroutineKind::Async(CoroutineSource::Closure) => {
|
||||
format!("future created by async closure is not {trait_name}")
|
||||
}
|
||||
CoroutineKind::Gen(CoroutineSource::Fn) => self
|
||||
.tcx
|
||||
.parent(coroutine_did)
|
||||
.as_local()
|
||||
.map(|parent_did| hir.local_def_id_to_hir_id(parent_did))
|
||||
.and_then(|parent_hir_id| hir.opt_name(parent_hir_id))
|
||||
.map(|name| {
|
||||
format!("iterator returned by `{name}` is not {trait_name}")
|
||||
})?,
|
||||
CoroutineKind::Gen(CoroutineSource::Block) => {
|
||||
format!("iterator created by gen block is not {trait_name}")
|
||||
}
|
||||
CoroutineKind::Gen(CoroutineSource::Closure) => {
|
||||
format!("iterator created by gen closure is not {trait_name}")
|
||||
}
|
||||
})
|
||||
})
|
||||
.unwrap_or_else(|| format!("{future_or_coroutine} is not {trait_name}"));
|
||||
@ -2931,7 +2946,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
|
||||
}
|
||||
ObligationCauseCode::SizedCoroutineInterior(coroutine_def_id) => {
|
||||
let what = match self.tcx.coroutine_kind(coroutine_def_id) {
|
||||
None | Some(hir::CoroutineKind::Coroutine) => "yield",
|
||||
None | Some(hir::CoroutineKind::Coroutine) | Some(hir::CoroutineKind::Gen(_)) => "yield",
|
||||
Some(hir::CoroutineKind::Async(..)) => "await",
|
||||
};
|
||||
err.note(format!(
|
||||
|
@ -1653,6 +1653,9 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
|
||||
hir::CoroutineKind::Async(hir::CoroutineSource::Block) => "an async block",
|
||||
hir::CoroutineKind::Async(hir::CoroutineSource::Fn) => "an async function",
|
||||
hir::CoroutineKind::Async(hir::CoroutineSource::Closure) => "an async closure",
|
||||
hir::CoroutineKind::Gen(hir::CoroutineSource::Block) => "a gen block",
|
||||
hir::CoroutineKind::Gen(hir::CoroutineSource::Fn) => "a gen function",
|
||||
hir::CoroutineKind::Gen(hir::CoroutineSource::Closure) => "a gen closure",
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -1798,7 +1798,7 @@ fn assemble_candidates_from_impls<'cx, 'tcx>(
|
||||
let self_ty = selcx.infcx.shallow_resolve(obligation.predicate.self_ty());
|
||||
|
||||
let lang_items = selcx.tcx().lang_items();
|
||||
if [lang_items.gen_trait(), lang_items.future_trait()].contains(&Some(trait_ref.def_id))
|
||||
if [lang_items.gen_trait(), lang_items.future_trait(), lang_items.iterator_trait()].contains(&Some(trait_ref.def_id))
|
||||
|| selcx.tcx().fn_trait_kind_from_def_id(trait_ref.def_id).is_some()
|
||||
{
|
||||
true
|
||||
@ -2015,6 +2015,8 @@ fn confirm_select_candidate<'cx, 'tcx>(
|
||||
confirm_coroutine_candidate(selcx, obligation, data)
|
||||
} else if lang_items.future_trait() == Some(trait_def_id) {
|
||||
confirm_future_candidate(selcx, obligation, data)
|
||||
} else if lang_items.iterator_trait() == Some(trait_def_id) {
|
||||
confirm_iterator_candidate(selcx, obligation, data)
|
||||
} else if selcx.tcx().fn_trait_kind_from_def_id(trait_def_id).is_some() {
|
||||
if obligation.predicate.self_ty().is_closure() {
|
||||
confirm_closure_candidate(selcx, obligation, data)
|
||||
@ -2135,6 +2137,50 @@ fn confirm_future_candidate<'cx, 'tcx>(
|
||||
.with_addl_obligations(obligations)
|
||||
}
|
||||
|
||||
fn confirm_iterator_candidate<'cx, 'tcx>(
|
||||
selcx: &mut SelectionContext<'cx, 'tcx>,
|
||||
obligation: &ProjectionTyObligation<'tcx>,
|
||||
nested: Vec<PredicateObligation<'tcx>>,
|
||||
) -> Progress<'tcx> {
|
||||
let ty::Coroutine(_, args, _) =
|
||||
selcx.infcx.shallow_resolve(obligation.predicate.self_ty()).kind()
|
||||
else {
|
||||
unreachable!()
|
||||
};
|
||||
let gen_sig = args.as_coroutine().poly_sig();
|
||||
let Normalized { value: gen_sig, obligations } = normalize_with_depth(
|
||||
selcx,
|
||||
obligation.param_env,
|
||||
obligation.cause.clone(),
|
||||
obligation.recursion_depth + 1,
|
||||
gen_sig,
|
||||
);
|
||||
|
||||
debug!(?obligation, ?gen_sig, ?obligations, "confirm_future_candidate");
|
||||
|
||||
let tcx = selcx.tcx();
|
||||
let iter_def_id = tcx.require_lang_item(LangItem::Iterator, None);
|
||||
|
||||
let predicate = super::util::iterator_trait_ref_and_outputs(
|
||||
tcx,
|
||||
iter_def_id,
|
||||
obligation.predicate.self_ty(),
|
||||
gen_sig,
|
||||
)
|
||||
.map_bound(|(trait_ref, yield_ty)| {
|
||||
debug_assert_eq!(tcx.associated_item(obligation.predicate.def_id).name, sym::Item);
|
||||
|
||||
ty::ProjectionPredicate {
|
||||
projection_ty: ty::AliasTy::new(tcx, obligation.predicate.def_id, trait_ref.args),
|
||||
term: yield_ty.into(),
|
||||
}
|
||||
});
|
||||
|
||||
confirm_param_env_candidate(selcx, obligation, predicate, false)
|
||||
.with_addl_obligations(nested)
|
||||
.with_addl_obligations(obligations)
|
||||
}
|
||||
|
||||
fn confirm_builtin_candidate<'cx, 'tcx>(
|
||||
selcx: &mut SelectionContext<'cx, 'tcx>,
|
||||
obligation: &ProjectionTyObligation<'tcx>,
|
||||
|
@ -113,6 +113,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
|
||||
self.assemble_coroutine_candidates(obligation, &mut candidates);
|
||||
} else if lang_items.future_trait() == Some(def_id) {
|
||||
self.assemble_future_candidates(obligation, &mut candidates);
|
||||
} else if lang_items.iterator_trait() == Some(def_id) {
|
||||
self.assemble_iterator_candidates(obligation, &mut candidates);
|
||||
}
|
||||
|
||||
self.assemble_closure_candidates(obligation, &mut candidates);
|
||||
@ -210,9 +212,9 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
|
||||
// type/region parameters.
|
||||
let self_ty = obligation.self_ty().skip_binder();
|
||||
match self_ty.kind() {
|
||||
// async constructs get lowered to a special kind of coroutine that
|
||||
// `async`/`gen` constructs get lowered to a special kind of coroutine that
|
||||
// should *not* `impl Coroutine`.
|
||||
ty::Coroutine(did, ..) if !self.tcx().coroutine_is_async(*did) => {
|
||||
ty::Coroutine(did, ..) if self.tcx().is_general_coroutine(*did) => {
|
||||
debug!(?self_ty, ?obligation, "assemble_coroutine_candidates",);
|
||||
|
||||
candidates.vec.push(CoroutineCandidate);
|
||||
@ -242,6 +244,23 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
|
||||
}
|
||||
}
|
||||
|
||||
fn assemble_iterator_candidates(
|
||||
&mut self,
|
||||
obligation: &PolyTraitObligation<'tcx>,
|
||||
candidates: &mut SelectionCandidateSet<'tcx>,
|
||||
) {
|
||||
let self_ty = obligation.self_ty().skip_binder();
|
||||
if let ty::Coroutine(did, ..) = self_ty.kind() {
|
||||
// gen constructs get lowered to a special kind of coroutine that
|
||||
// should directly `impl Iterator`.
|
||||
if self.tcx().coroutine_is_gen(*did) {
|
||||
debug!(?self_ty, ?obligation, "assemble_iterator_candidates",);
|
||||
|
||||
candidates.vec.push(IteratorCandidate);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Checks for the artificial impl that the compiler will create for an obligation like `X :
|
||||
/// FnMut<..>` where `X` is a closure type.
|
||||
///
|
||||
|
@ -93,6 +93,11 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
|
||||
ImplSource::Builtin(BuiltinImplSource::Misc, vtable_future)
|
||||
}
|
||||
|
||||
IteratorCandidate => {
|
||||
let vtable_iterator = self.confirm_iterator_candidate(obligation)?;
|
||||
ImplSource::Builtin(BuiltinImplSource::Misc, vtable_iterator)
|
||||
}
|
||||
|
||||
FnPointerCandidate { is_const } => {
|
||||
let data = self.confirm_fn_pointer_candidate(obligation, is_const)?;
|
||||
ImplSource::Builtin(BuiltinImplSource::Misc, data)
|
||||
@ -780,6 +785,36 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
|
||||
Ok(nested)
|
||||
}
|
||||
|
||||
fn confirm_iterator_candidate(
|
||||
&mut self,
|
||||
obligation: &PolyTraitObligation<'tcx>,
|
||||
) -> Result<Vec<PredicateObligation<'tcx>>, SelectionError<'tcx>> {
|
||||
// Okay to skip binder because the args on coroutine types never
|
||||
// touch bound regions, they just capture the in-scope
|
||||
// type/region parameters.
|
||||
let self_ty = self.infcx.shallow_resolve(obligation.self_ty().skip_binder());
|
||||
let ty::Coroutine(coroutine_def_id, args, _) = *self_ty.kind() else {
|
||||
bug!("closure candidate for non-closure {:?}", obligation);
|
||||
};
|
||||
|
||||
debug!(?obligation, ?coroutine_def_id, ?args, "confirm_iterator_candidate");
|
||||
|
||||
let gen_sig = args.as_coroutine().poly_sig();
|
||||
|
||||
let trait_ref = super::util::iterator_trait_ref_and_outputs(
|
||||
self.tcx(),
|
||||
obligation.predicate.def_id(),
|
||||
obligation.predicate.no_bound_vars().expect("iterator has no bound vars").self_ty(),
|
||||
gen_sig,
|
||||
)
|
||||
.map_bound(|(trait_ref, ..)| trait_ref);
|
||||
|
||||
let nested = self.confirm_poly_trait_refs(obligation, trait_ref)?;
|
||||
debug!(?trait_ref, ?nested, "iterator candidate obligations");
|
||||
|
||||
Ok(nested)
|
||||
}
|
||||
|
||||
#[instrument(skip(self), level = "debug")]
|
||||
fn confirm_closure_candidate(
|
||||
&mut self,
|
||||
|
@ -1888,6 +1888,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
|
||||
| ClosureCandidate { .. }
|
||||
| CoroutineCandidate
|
||||
| FutureCandidate
|
||||
| IteratorCandidate
|
||||
| FnPointerCandidate { .. }
|
||||
| BuiltinObjectCandidate
|
||||
| BuiltinUnsizeCandidate
|
||||
@ -1916,6 +1917,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
|
||||
| ClosureCandidate { .. }
|
||||
| CoroutineCandidate
|
||||
| FutureCandidate
|
||||
| IteratorCandidate
|
||||
| FnPointerCandidate { .. }
|
||||
| BuiltinObjectCandidate
|
||||
| BuiltinUnsizeCandidate
|
||||
@ -1950,6 +1952,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
|
||||
| ClosureCandidate { .. }
|
||||
| CoroutineCandidate
|
||||
| FutureCandidate
|
||||
| IteratorCandidate
|
||||
| FnPointerCandidate { .. }
|
||||
| BuiltinObjectCandidate
|
||||
| BuiltinUnsizeCandidate
|
||||
@ -1964,6 +1967,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
|
||||
| ClosureCandidate { .. }
|
||||
| CoroutineCandidate
|
||||
| FutureCandidate
|
||||
| IteratorCandidate
|
||||
| FnPointerCandidate { .. }
|
||||
| BuiltinObjectCandidate
|
||||
| BuiltinUnsizeCandidate
|
||||
@ -2070,6 +2074,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
|
||||
| ClosureCandidate { .. }
|
||||
| CoroutineCandidate
|
||||
| FutureCandidate
|
||||
| IteratorCandidate
|
||||
| FnPointerCandidate { .. }
|
||||
| BuiltinObjectCandidate
|
||||
| BuiltinUnsizeCandidate
|
||||
@ -2080,6 +2085,7 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
|
||||
| ClosureCandidate { .. }
|
||||
| CoroutineCandidate
|
||||
| FutureCandidate
|
||||
| IteratorCandidate
|
||||
| FnPointerCandidate { .. }
|
||||
| BuiltinObjectCandidate
|
||||
| BuiltinUnsizeCandidate
|
||||
|
@ -297,6 +297,17 @@ pub fn future_trait_ref_and_outputs<'tcx>(
|
||||
sig.map_bound(|sig| (trait_ref, sig.return_ty))
|
||||
}
|
||||
|
||||
pub fn iterator_trait_ref_and_outputs<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
iterator_def_id: DefId,
|
||||
self_ty: Ty<'tcx>,
|
||||
sig: ty::PolyGenSig<'tcx>,
|
||||
) -> ty::Binder<'tcx, (ty::TraitRef<'tcx>, Ty<'tcx>)> {
|
||||
assert!(!self_ty.has_escaping_bound_vars());
|
||||
let trait_ref = ty::TraitRef::new(tcx, iterator_def_id, [self_ty]);
|
||||
sig.map_bound(|sig| (trait_ref, sig.yield_ty))
|
||||
}
|
||||
|
||||
pub fn impl_item_is_final(tcx: TyCtxt<'_>, assoc_item: &ty::AssocItem) -> bool {
|
||||
assoc_item.defaultness(tcx).is_final()
|
||||
&& tcx.defaultness(assoc_item.container_id(tcx)).is_final()
|
||||
|
@ -258,6 +258,19 @@ fn resolve_associated_item<'tcx>(
|
||||
debug_assert!(tcx.defaultness(trait_item_id).has_value());
|
||||
Some(Instance::new(trait_item_id, rcvr_args))
|
||||
}
|
||||
} else if Some(trait_ref.def_id) == lang_items.iterator_trait() {
|
||||
let ty::Coroutine(coroutine_def_id, args, _) = *rcvr_args.type_at(0).kind() else {
|
||||
bug!()
|
||||
};
|
||||
if Some(trait_item_id) == tcx.lang_items().next_fn() {
|
||||
// `Iterator::next` is generated by the compiler.
|
||||
Some(Instance { def: ty::InstanceDef::Item(coroutine_def_id), args })
|
||||
} else {
|
||||
// All other methods are default methods of the `Iterator` trait.
|
||||
// (this assumes that `ImplSource::Builtin` is only used for methods on `Iterator`)
|
||||
debug_assert!(tcx.defaultness(trait_item_id).has_value());
|
||||
Some(Instance::new(trait_item_id, rcvr_args))
|
||||
}
|
||||
} else if Some(trait_ref.def_id) == lang_items.gen_trait() {
|
||||
let ty::Coroutine(coroutine_def_id, args, _) = *rcvr_args.type_at(0).kind() else {
|
||||
bug!()
|
||||
|
@ -187,6 +187,7 @@ pub enum UnOp {
|
||||
pub enum CoroutineKind {
|
||||
Async(CoroutineSource),
|
||||
Coroutine,
|
||||
Gen(CoroutineSource),
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
|
@ -578,7 +578,7 @@ fn ident_difference_expr_with_base_location(
|
||||
| (Assign(_, _, _), Assign(_, _, _))
|
||||
| (TryBlock(_), TryBlock(_))
|
||||
| (Await(_, _), Await(_, _))
|
||||
| (Async(_, _), Async(_, _))
|
||||
| (Gen(_, _, _), Gen(_, _, _))
|
||||
| (Block(_, _), Block(_, _))
|
||||
| (Closure(_), Closure(_))
|
||||
| (Match(_, _), Match(_, _))
|
||||
|
@ -211,7 +211,7 @@ pub fn eq_expr(l: &Expr, r: &Expr) -> bool {
|
||||
&& eq_fn_decl(lf, rf)
|
||||
&& eq_expr(le, re)
|
||||
},
|
||||
(Async(lc, lb), Async(rc, rb)) => lc == rc && eq_block(lb, rb),
|
||||
(Gen(lc, lb, lk), Gen(rc, rb, rk)) => lc == rc && eq_block(lb, rb) && lk == rk,
|
||||
(Range(lf, lt, ll), Range(rf, rt, rl)) => ll == rl && eq_expr_opt(lf, rf) && eq_expr_opt(lt, rt),
|
||||
(AddrOf(lbk, lm, le), AddrOf(rbk, rm, re)) => lbk == rbk && lm == rm && eq_expr(le, re),
|
||||
(Path(lq, lp), Path(rq, rp)) => both(lq, rq, eq_qself) && eq_path(lp, rp),
|
||||
|
@ -190,7 +190,7 @@ impl<'a> Sugg<'a> {
|
||||
(snip, false) => Sugg::MaybeParen(snip),
|
||||
(snip, true) => Sugg::NonParen(snip),
|
||||
},
|
||||
ast::ExprKind::Async(..)
|
||||
ast::ExprKind::Gen(..)
|
||||
| ast::ExprKind::Block(..)
|
||||
| ast::ExprKind::Break(..)
|
||||
| ast::ExprKind::Call(..)
|
||||
|
@ -188,7 +188,7 @@ fn rewrite_closure_expr(
|
||||
fn allow_multi_line(expr: &ast::Expr) -> bool {
|
||||
match expr.kind {
|
||||
ast::ExprKind::Match(..)
|
||||
| ast::ExprKind::Async(..)
|
||||
| ast::ExprKind::Gen(..)
|
||||
| ast::ExprKind::Block(..)
|
||||
| ast::ExprKind::TryBlock(..)
|
||||
| ast::ExprKind::Loop(..)
|
||||
|
@ -367,7 +367,7 @@ pub(crate) fn format_expr(
|
||||
))
|
||||
}
|
||||
}
|
||||
ast::ExprKind::Async(capture_by, ref block) => {
|
||||
ast::ExprKind::Gen(capture_by, ref block, ref kind) => {
|
||||
let mover = if capture_by == ast::CaptureBy::Value {
|
||||
"move "
|
||||
} else {
|
||||
@ -375,7 +375,7 @@ pub(crate) fn format_expr(
|
||||
};
|
||||
if let rw @ Some(_) = rewrite_single_line_block(
|
||||
context,
|
||||
format!("async {mover}").as_str(),
|
||||
format!("{kind} {mover}").as_str(),
|
||||
block,
|
||||
Some(&expr.attrs),
|
||||
None,
|
||||
@ -386,7 +386,7 @@ pub(crate) fn format_expr(
|
||||
// 6 = `async `
|
||||
let budget = shape.width.saturating_sub(6);
|
||||
Some(format!(
|
||||
"async {mover}{}",
|
||||
"{kind} {mover}{}",
|
||||
rewrite_block(
|
||||
block,
|
||||
Some(&expr.attrs),
|
||||
@ -1371,7 +1371,7 @@ pub(crate) fn can_be_overflowed_expr(
|
||||
}
|
||||
|
||||
// Handle always block-like expressions
|
||||
ast::ExprKind::Async(..) | ast::ExprKind::Block(..) | ast::ExprKind::Closure(..) => true,
|
||||
ast::ExprKind::Gen(..) | ast::ExprKind::Block(..) | ast::ExprKind::Closure(..) => true,
|
||||
|
||||
// Handle `[]` and `{}`-like expressions
|
||||
ast::ExprKind::Array(..) | ast::ExprKind::Struct(..) => {
|
||||
|
@ -473,7 +473,7 @@ pub(crate) fn is_block_expr(context: &RewriteContext<'_>, expr: &ast::Expr, repr
|
||||
| ast::ExprKind::If(..)
|
||||
| ast::ExprKind::Block(..)
|
||||
| ast::ExprKind::ConstBlock(..)
|
||||
| ast::ExprKind::Async(..)
|
||||
| ast::ExprKind::Gen(..)
|
||||
| ast::ExprKind::Loop(..)
|
||||
| ast::ExprKind::ForLoop(..)
|
||||
| ast::ExprKind::TryBlock(..)
|
||||
|
19
tests/ui/coroutine/gen_block.e2024.stderr
Normal file
19
tests/ui/coroutine/gen_block.e2024.stderr
Normal file
@ -0,0 +1,19 @@
|
||||
error[E0658]: yield syntax is experimental
|
||||
--> $DIR/gen_block.rs:15:16
|
||||
|
|
||||
LL | let _ = || yield true;
|
||||
| ^^^^^^^^^^
|
||||
|
|
||||
= note: see issue #43122 <https://github.com/rust-lang/rust/issues/43122> for more information
|
||||
= help: add `#![feature(coroutines)]` to the crate attributes to enable
|
||||
|
||||
error[E0282]: type annotations needed
|
||||
--> $DIR/gen_block.rs:6:17
|
||||
|
|
||||
LL | let x = gen {};
|
||||
| ^^ cannot infer type
|
||||
|
||||
error: aborting due to 2 previous errors
|
||||
|
||||
Some errors have detailed explanations: E0282, E0658.
|
||||
For more information about an error, try `rustc --explain E0282`.
|
49
tests/ui/coroutine/gen_block.none.stderr
Normal file
49
tests/ui/coroutine/gen_block.none.stderr
Normal file
@ -0,0 +1,49 @@
|
||||
error: expected identifier, found reserved keyword `yield`
|
||||
--> $DIR/gen_block.rs:9:19
|
||||
|
|
||||
LL | let y = gen { yield 42 };
|
||||
| --- ^^^^^ expected identifier, found reserved keyword
|
||||
| |
|
||||
| while parsing this struct
|
||||
|
||||
error[E0422]: cannot find struct, variant or union type `gen` in this scope
|
||||
--> $DIR/gen_block.rs:6:13
|
||||
|
|
||||
LL | let x = gen {};
|
||||
| ^^^ not found in this scope
|
||||
|
||||
error[E0422]: cannot find struct, variant or union type `gen` in this scope
|
||||
--> $DIR/gen_block.rs:9:13
|
||||
|
|
||||
LL | let y = gen { yield 42 };
|
||||
| ^^^ not found in this scope
|
||||
|
||||
error[E0422]: cannot find struct, variant or union type `gen` in this scope
|
||||
--> $DIR/gen_block.rs:12:5
|
||||
|
|
||||
LL | gen {};
|
||||
| ^^^ not found in this scope
|
||||
|
||||
error[E0658]: yield syntax is experimental
|
||||
--> $DIR/gen_block.rs:15:16
|
||||
|
|
||||
LL | let _ = || yield true;
|
||||
| ^^^^^^^^^^
|
||||
|
|
||||
= note: see issue #43122 <https://github.com/rust-lang/rust/issues/43122> for more information
|
||||
= help: add `#![feature(coroutines)]` to the crate attributes to enable
|
||||
|
||||
error[E0658]: yield syntax is experimental
|
||||
--> $DIR/gen_block.rs:15:16
|
||||
|
|
||||
LL | let _ = || yield true;
|
||||
| ^^^^^^^^^^
|
||||
|
|
||||
= note: see issue #43122 <https://github.com/rust-lang/rust/issues/43122> for more information
|
||||
= help: add `#![feature(coroutines)]` to the crate attributes to enable
|
||||
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
|
||||
|
||||
error: aborting due to 6 previous errors
|
||||
|
||||
Some errors have detailed explanations: E0422, E0658.
|
||||
For more information about an error, try `rustc --explain E0422`.
|
17
tests/ui/coroutine/gen_block.rs
Normal file
17
tests/ui/coroutine/gen_block.rs
Normal file
@ -0,0 +1,17 @@
|
||||
// revisions: e2024 none
|
||||
//[e2024] compile-flags: --edition 2024 -Zunstable-options
|
||||
#![cfg_attr(e2024, feature(gen_blocks))]
|
||||
|
||||
fn main() {
|
||||
let x = gen {};
|
||||
//[none]~^ ERROR: cannot find
|
||||
//[e2024]~^^ ERROR: type annotations needed
|
||||
let y = gen { yield 42 };
|
||||
//[none]~^ ERROR: found reserved keyword `yield`
|
||||
//[none]~| ERROR: cannot find
|
||||
gen {};
|
||||
//[none]~^ ERROR: cannot find
|
||||
|
||||
let _ = || yield true; //[none]~ ERROR yield syntax is experimental
|
||||
//~^ ERROR yield syntax is experimental
|
||||
}
|
18
tests/ui/coroutine/gen_block_is_coro.rs
Normal file
18
tests/ui/coroutine/gen_block_is_coro.rs
Normal file
@ -0,0 +1,18 @@
|
||||
//compile-flags: --edition 2024 -Zunstable-options
|
||||
#![feature(coroutines, coroutine_trait, gen_blocks)]
|
||||
|
||||
use std::ops::Coroutine;
|
||||
|
||||
fn foo() -> impl Coroutine<Yield = u32, Return = ()> { //~ ERROR: Coroutine` is not satisfied
|
||||
gen { yield 42 }
|
||||
}
|
||||
|
||||
fn bar() -> impl Coroutine<Yield = i64, Return = ()> { //~ ERROR: Coroutine` is not satisfied
|
||||
gen { yield 42 }
|
||||
}
|
||||
|
||||
fn baz() -> impl Coroutine<Yield = i32, Return = ()> { //~ ERROR: Coroutine` is not satisfied
|
||||
gen { yield 42 }
|
||||
}
|
||||
|
||||
fn main() {}
|
21
tests/ui/coroutine/gen_block_is_coro.stderr
Normal file
21
tests/ui/coroutine/gen_block_is_coro.stderr
Normal file
@ -0,0 +1,21 @@
|
||||
error[E0277]: the trait bound `{gen block@$DIR/gen_block_is_coro.rs:7:5: 7:21}: Coroutine` is not satisfied
|
||||
--> $DIR/gen_block_is_coro.rs:6:13
|
||||
|
|
||||
LL | fn foo() -> impl Coroutine<Yield = u32, Return = ()> {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Coroutine` is not implemented for `{gen block@$DIR/gen_block_is_coro.rs:7:5: 7:21}`
|
||||
|
||||
error[E0277]: the trait bound `{gen block@$DIR/gen_block_is_coro.rs:11:5: 11:21}: Coroutine` is not satisfied
|
||||
--> $DIR/gen_block_is_coro.rs:10:13
|
||||
|
|
||||
LL | fn bar() -> impl Coroutine<Yield = i64, Return = ()> {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Coroutine` is not implemented for `{gen block@$DIR/gen_block_is_coro.rs:11:5: 11:21}`
|
||||
|
||||
error[E0277]: the trait bound `{gen block@$DIR/gen_block_is_coro.rs:15:5: 15:21}: Coroutine` is not satisfied
|
||||
--> $DIR/gen_block_is_coro.rs:14:13
|
||||
|
|
||||
LL | fn baz() -> impl Coroutine<Yield = i32, Return = ()> {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Coroutine` is not implemented for `{gen block@$DIR/gen_block_is_coro.rs:15:5: 15:21}`
|
||||
|
||||
error: aborting due to 3 previous errors
|
||||
|
||||
For more information about this error, try `rustc --explain E0277`.
|
19
tests/ui/coroutine/gen_block_is_iter.rs
Normal file
19
tests/ui/coroutine/gen_block_is_iter.rs
Normal file
@ -0,0 +1,19 @@
|
||||
// revisions: next old
|
||||
//compile-flags: --edition 2024 -Zunstable-options
|
||||
//[next] compile-flags: -Ztrait-solver=next
|
||||
// check-pass
|
||||
#![feature(gen_blocks)]
|
||||
|
||||
fn foo() -> impl Iterator<Item = u32> {
|
||||
gen { yield 42 }
|
||||
}
|
||||
|
||||
fn bar() -> impl Iterator<Item = i64> {
|
||||
gen { yield 42 }
|
||||
}
|
||||
|
||||
fn baz() -> impl Iterator<Item = i32> {
|
||||
gen { yield 42 }
|
||||
}
|
||||
|
||||
fn main() {}
|
8
tests/ui/coroutine/gen_block_is_no_future.rs
Normal file
8
tests/ui/coroutine/gen_block_is_no_future.rs
Normal file
@ -0,0 +1,8 @@
|
||||
//compile-flags: --edition 2024 -Zunstable-options
|
||||
#![feature(gen_blocks)]
|
||||
|
||||
fn foo() -> impl std::future::Future { //~ ERROR is not a future
|
||||
gen { yield 42 }
|
||||
}
|
||||
|
||||
fn main() {}
|
12
tests/ui/coroutine/gen_block_is_no_future.stderr
Normal file
12
tests/ui/coroutine/gen_block_is_no_future.stderr
Normal file
@ -0,0 +1,12 @@
|
||||
error[E0277]: `{gen block@$DIR/gen_block_is_no_future.rs:5:5: 5:21}` is not a future
|
||||
--> $DIR/gen_block_is_no_future.rs:4:13
|
||||
|
|
||||
LL | fn foo() -> impl std::future::Future {
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^ `{gen block@$DIR/gen_block_is_no_future.rs:5:5: 5:21}` is not a future
|
||||
|
|
||||
= help: the trait `Future` is not implemented for `{gen block@$DIR/gen_block_is_no_future.rs:5:5: 5:21}`
|
||||
= note: {gen block@$DIR/gen_block_is_no_future.rs:5:5: 5:21} must be a future or must implement `IntoFuture` to be awaited
|
||||
|
||||
error: aborting due to previous error
|
||||
|
||||
For more information about this error, try `rustc --explain E0277`.
|
35
tests/ui/coroutine/gen_block_iterate.rs
Normal file
35
tests/ui/coroutine/gen_block_iterate.rs
Normal file
@ -0,0 +1,35 @@
|
||||
// revisions: next old
|
||||
//compile-flags: --edition 2024 -Zunstable-options
|
||||
//[next] compile-flags: -Ztrait-solver=next
|
||||
// run-pass
|
||||
#![feature(gen_blocks)]
|
||||
|
||||
fn foo() -> impl Iterator<Item = u32> {
|
||||
gen { yield 42; for x in 3..6 { yield x } }
|
||||
}
|
||||
|
||||
fn moved() -> impl Iterator<Item = u32> {
|
||||
let mut x = "foo".to_string();
|
||||
gen move {
|
||||
yield 42;
|
||||
if x == "foo" { return }
|
||||
x.clear();
|
||||
for x in 3..6 { yield x }
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let mut iter = foo();
|
||||
assert_eq!(iter.next(), Some(42));
|
||||
assert_eq!(iter.next(), Some(3));
|
||||
assert_eq!(iter.next(), Some(4));
|
||||
assert_eq!(iter.next(), Some(5));
|
||||
assert_eq!(iter.next(), None);
|
||||
// `gen` blocks are fused
|
||||
assert_eq!(iter.next(), None);
|
||||
|
||||
let mut iter = moved();
|
||||
assert_eq!(iter.next(), Some(42));
|
||||
assert_eq!(iter.next(), None);
|
||||
|
||||
}
|
17
tests/ui/coroutine/gen_block_move.fixed
Normal file
17
tests/ui/coroutine/gen_block_move.fixed
Normal file
@ -0,0 +1,17 @@
|
||||
// compile-flags: --edition 2024 -Zunstable-options
|
||||
// run-rustfix
|
||||
#![feature(gen_blocks)]
|
||||
|
||||
fn moved() -> impl Iterator<Item = u32> {
|
||||
let mut x = "foo".to_string();
|
||||
gen move { //~ ERROR: gen block may outlive the current function
|
||||
yield 42;
|
||||
if x == "foo" { return }
|
||||
x.clear();
|
||||
for x in 3..6 { yield x }
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
for _ in moved() {}
|
||||
}
|
17
tests/ui/coroutine/gen_block_move.rs
Normal file
17
tests/ui/coroutine/gen_block_move.rs
Normal file
@ -0,0 +1,17 @@
|
||||
// compile-flags: --edition 2024 -Zunstable-options
|
||||
// run-rustfix
|
||||
#![feature(gen_blocks)]
|
||||
|
||||
fn moved() -> impl Iterator<Item = u32> {
|
||||
let mut x = "foo".to_string();
|
||||
gen { //~ ERROR: gen block may outlive the current function
|
||||
yield 42;
|
||||
if x == "foo" { return }
|
||||
x.clear();
|
||||
for x in 3..6 { yield x }
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
for _ in moved() {}
|
||||
}
|
30
tests/ui/coroutine/gen_block_move.stderr
Normal file
30
tests/ui/coroutine/gen_block_move.stderr
Normal file
@ -0,0 +1,30 @@
|
||||
error[E0373]: gen block may outlive the current function, but it borrows `x`, which is owned by the current function
|
||||
--> $DIR/gen_block_move.rs:7:5
|
||||
|
|
||||
LL | / gen {
|
||||
LL | | yield 42;
|
||||
LL | | if x == "foo" { return }
|
||||
LL | | x.clear();
|
||||
| | - `x` is borrowed here
|
||||
LL | | for x in 3..6 { yield x }
|
||||
LL | | }
|
||||
| |_____^ may outlive borrowed value `x`
|
||||
|
|
||||
note: gen block is returned here
|
||||
--> $DIR/gen_block_move.rs:7:5
|
||||
|
|
||||
LL | / gen {
|
||||
LL | | yield 42;
|
||||
LL | | if x == "foo" { return }
|
||||
LL | | x.clear();
|
||||
LL | | for x in 3..6 { yield x }
|
||||
LL | | }
|
||||
| |_____^
|
||||
help: to force the gen block to take ownership of `x` (and any other referenced variables), use the `move` keyword
|
||||
|
|
||||
LL | gen move {
|
||||
| ++++
|
||||
|
||||
error: aborting due to previous error
|
||||
|
||||
For more information about this error, try `rustc --explain E0373`.
|
10
tests/ui/coroutine/gen_fn.e2024.stderr
Normal file
10
tests/ui/coroutine/gen_fn.e2024.stderr
Normal file
@ -0,0 +1,10 @@
|
||||
error: `gen` blocks are not yet implemented
|
||||
--> $DIR/gen_fn.rs:4:1
|
||||
|
|
||||
LL | gen fn foo() {}
|
||||
| ^^^
|
||||
|
|
||||
= help: only the keyword is reserved for now
|
||||
|
||||
error: aborting due to previous error
|
||||
|
8
tests/ui/coroutine/gen_fn.none.stderr
Normal file
8
tests/ui/coroutine/gen_fn.none.stderr
Normal file
@ -0,0 +1,8 @@
|
||||
error: expected one of `#`, `async`, `const`, `default`, `extern`, `fn`, `pub`, `unsafe`, or `use`, found `gen`
|
||||
--> $DIR/gen_fn.rs:4:1
|
||||
|
|
||||
LL | gen fn foo() {}
|
||||
| ^^^ expected one of 9 possible tokens
|
||||
|
||||
error: aborting due to previous error
|
||||
|
8
tests/ui/coroutine/gen_fn.rs
Normal file
8
tests/ui/coroutine/gen_fn.rs
Normal file
@ -0,0 +1,8 @@
|
||||
// revisions: e2024 none
|
||||
//[e2024] compile-flags: --edition 2024 -Zunstable-options
|
||||
|
||||
gen fn foo() {}
|
||||
//[none]~^ ERROR: expected one of `#`, `async`, `const`, `default`, `extern`, `fn`, `pub`, `unsafe`, or `use`, found `gen`
|
||||
//[e2024]~^^ ERROR: `gen` blocks are not yet implemented
|
||||
|
||||
fn main() {}
|
17
tests/ui/coroutine/self_referential_gen_block.rs
Normal file
17
tests/ui/coroutine/self_referential_gen_block.rs
Normal file
@ -0,0 +1,17 @@
|
||||
// compile-flags: --edition 2024 -Zunstable-options
|
||||
#![feature(gen_blocks)]
|
||||
//! This test checks that we don't allow self-referential generators
|
||||
|
||||
fn main() {
|
||||
let mut x = {
|
||||
let mut x = gen {
|
||||
let y = 42;
|
||||
let z = &y; //~ ERROR: borrow may still be in use when `gen` block yields
|
||||
yield 43;
|
||||
panic!("{z}");
|
||||
};
|
||||
x.next();
|
||||
Box::new(x)
|
||||
};
|
||||
x.next();
|
||||
}
|
11
tests/ui/coroutine/self_referential_gen_block.stderr
Normal file
11
tests/ui/coroutine/self_referential_gen_block.stderr
Normal file
@ -0,0 +1,11 @@
|
||||
error[E0626]: borrow may still be in use when `gen` block yields
|
||||
--> $DIR/self_referential_gen_block.rs:9:21
|
||||
|
|
||||
LL | let z = &y;
|
||||
| ^^
|
||||
LL | yield 43;
|
||||
| -------- possible yield occurs here
|
||||
|
||||
error: aborting due to previous error
|
||||
|
||||
For more information about this error, try `rustc --explain E0626`.
|
@ -1,5 +1,5 @@
|
||||
error[E0658]: yield syntax is experimental
|
||||
--> $DIR/feature-gate-coroutines.rs:2:5
|
||||
--> $DIR/feature-gate-coroutines.rs:5:5
|
||||
|
|
||||
LL | yield true;
|
||||
| ^^^^^^^^^^
|
||||
@ -8,30 +8,21 @@ LL | yield true;
|
||||
= help: add `#![feature(coroutines)]` to the crate attributes to enable
|
||||
|
||||
error[E0658]: yield syntax is experimental
|
||||
--> $DIR/feature-gate-coroutines.rs:8:5
|
||||
--> $DIR/feature-gate-coroutines.rs:9:16
|
||||
|
|
||||
LL | yield;
|
||||
| ^^^^^
|
||||
|
|
||||
= note: see issue #43122 <https://github.com/rust-lang/rust/issues/43122> for more information
|
||||
= help: add `#![feature(coroutines)]` to the crate attributes to enable
|
||||
|
||||
error[E0658]: yield syntax is experimental
|
||||
--> $DIR/feature-gate-coroutines.rs:9:5
|
||||
|
|
||||
LL | yield 0;
|
||||
| ^^^^^^^
|
||||
LL | let _ = || yield true;
|
||||
| ^^^^^^^^^^
|
||||
|
|
||||
= note: see issue #43122 <https://github.com/rust-lang/rust/issues/43122> for more information
|
||||
= help: add `#![feature(coroutines)]` to the crate attributes to enable
|
||||
|
||||
error[E0627]: yield expression outside of coroutine literal
|
||||
--> $DIR/feature-gate-coroutines.rs:2:5
|
||||
--> $DIR/feature-gate-coroutines.rs:5:5
|
||||
|
|
||||
LL | yield true;
|
||||
| ^^^^^^^^^^
|
||||
|
||||
error: aborting due to 4 previous errors
|
||||
error: aborting due to 3 previous errors
|
||||
|
||||
Some errors have detailed explanations: E0627, E0658.
|
||||
For more information about an error, try `rustc --explain E0627`.
|
66
tests/ui/feature-gates/feature-gate-coroutines.none.stderr
Normal file
66
tests/ui/feature-gates/feature-gate-coroutines.none.stderr
Normal file
@ -0,0 +1,66 @@
|
||||
error[E0658]: yield syntax is experimental
|
||||
--> $DIR/feature-gate-coroutines.rs:5:5
|
||||
|
|
||||
LL | yield true;
|
||||
| ^^^^^^^^^^
|
||||
|
|
||||
= note: see issue #43122 <https://github.com/rust-lang/rust/issues/43122> for more information
|
||||
= help: add `#![feature(coroutines)]` to the crate attributes to enable
|
||||
|
||||
error[E0658]: yield syntax is experimental
|
||||
--> $DIR/feature-gate-coroutines.rs:9:16
|
||||
|
|
||||
LL | let _ = || yield true;
|
||||
| ^^^^^^^^^^
|
||||
|
|
||||
= note: see issue #43122 <https://github.com/rust-lang/rust/issues/43122> for more information
|
||||
= help: add `#![feature(coroutines)]` to the crate attributes to enable
|
||||
|
||||
error[E0658]: yield syntax is experimental
|
||||
--> $DIR/feature-gate-coroutines.rs:16:5
|
||||
|
|
||||
LL | yield;
|
||||
| ^^^^^
|
||||
|
|
||||
= note: see issue #43122 <https://github.com/rust-lang/rust/issues/43122> for more information
|
||||
= help: add `#![feature(coroutines)]` to the crate attributes to enable
|
||||
|
||||
error[E0658]: yield syntax is experimental
|
||||
--> $DIR/feature-gate-coroutines.rs:17:5
|
||||
|
|
||||
LL | yield 0;
|
||||
| ^^^^^^^
|
||||
|
|
||||
= note: see issue #43122 <https://github.com/rust-lang/rust/issues/43122> for more information
|
||||
= help: add `#![feature(coroutines)]` to the crate attributes to enable
|
||||
|
||||
error[E0658]: yield syntax is experimental
|
||||
--> $DIR/feature-gate-coroutines.rs:5:5
|
||||
|
|
||||
LL | yield true;
|
||||
| ^^^^^^^^^^
|
||||
|
|
||||
= note: see issue #43122 <https://github.com/rust-lang/rust/issues/43122> for more information
|
||||
= help: add `#![feature(coroutines)]` to the crate attributes to enable
|
||||
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
|
||||
|
||||
error[E0658]: yield syntax is experimental
|
||||
--> $DIR/feature-gate-coroutines.rs:9:16
|
||||
|
|
||||
LL | let _ = || yield true;
|
||||
| ^^^^^^^^^^
|
||||
|
|
||||
= note: see issue #43122 <https://github.com/rust-lang/rust/issues/43122> for more information
|
||||
= help: add `#![feature(coroutines)]` to the crate attributes to enable
|
||||
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
|
||||
|
||||
error[E0627]: yield expression outside of coroutine literal
|
||||
--> $DIR/feature-gate-coroutines.rs:5:5
|
||||
|
|
||||
LL | yield true;
|
||||
| ^^^^^^^^^^
|
||||
|
||||
error: aborting due to 7 previous errors
|
||||
|
||||
Some errors have detailed explanations: E0627, E0658.
|
||||
For more information about an error, try `rustc --explain E0627`.
|
@ -1,10 +1,18 @@
|
||||
// revisions: e2024 none
|
||||
//[e2024] compile-flags: --edition 2024 -Zunstable-options
|
||||
|
||||
fn main() {
|
||||
yield true; //~ ERROR yield syntax is experimental
|
||||
//~^ ERROR yield expression outside of coroutine literal
|
||||
//[none]~^^ ERROR yield syntax is experimental
|
||||
|
||||
let _ = || yield true; //~ ERROR yield syntax is experimental
|
||||
//[none]~^ ERROR yield syntax is experimental
|
||||
}
|
||||
|
||||
#[cfg(FALSE)]
|
||||
fn foo() {
|
||||
yield; //~ ERROR yield syntax is experimental
|
||||
yield 0; //~ ERROR yield syntax is experimental
|
||||
// Ok in 2024 edition
|
||||
yield; //[none]~ ERROR yield syntax is experimental
|
||||
yield 0; //[none]~ ERROR yield syntax is experimental
|
||||
}
|
||||
|
28
tests/ui/feature-gates/feature-gate-gen_blocks.e2024.stderr
Normal file
28
tests/ui/feature-gates/feature-gate-gen_blocks.e2024.stderr
Normal file
@ -0,0 +1,28 @@
|
||||
error[E0658]: gen blocks are experimental
|
||||
--> $DIR/feature-gate-gen_blocks.rs:5:5
|
||||
|
|
||||
LL | gen {};
|
||||
| ^^^^^
|
||||
|
|
||||
= note: see issue #117078 <https://github.com/rust-lang/rust/issues/117078> for more information
|
||||
= help: add `#![feature(gen_blocks)]` to the crate attributes to enable
|
||||
|
||||
error[E0658]: gen blocks are experimental
|
||||
--> $DIR/feature-gate-gen_blocks.rs:13:5
|
||||
|
|
||||
LL | gen {};
|
||||
| ^^^^^
|
||||
|
|
||||
= note: see issue #117078 <https://github.com/rust-lang/rust/issues/117078> for more information
|
||||
= help: add `#![feature(gen_blocks)]` to the crate attributes to enable
|
||||
|
||||
error[E0282]: type annotations needed
|
||||
--> $DIR/feature-gate-gen_blocks.rs:5:9
|
||||
|
|
||||
LL | gen {};
|
||||
| ^^ cannot infer type
|
||||
|
||||
error: aborting due to 3 previous errors
|
||||
|
||||
Some errors have detailed explanations: E0282, E0658.
|
||||
For more information about an error, try `rustc --explain E0282`.
|
@ -0,0 +1,9 @@
|
||||
error[E0422]: cannot find struct, variant or union type `gen` in this scope
|
||||
--> $DIR/feature-gate-gen_blocks.rs:5:5
|
||||
|
|
||||
LL | gen {};
|
||||
| ^^^ not found in this scope
|
||||
|
||||
error: aborting due to previous error
|
||||
|
||||
For more information about this error, try `rustc --explain E0422`.
|
15
tests/ui/feature-gates/feature-gate-gen_blocks.rs
Normal file
15
tests/ui/feature-gates/feature-gate-gen_blocks.rs
Normal file
@ -0,0 +1,15 @@
|
||||
// revisions: e2024 none
|
||||
//[e2024] compile-flags: --edition 2024 -Zunstable-options
|
||||
|
||||
fn main() {
|
||||
gen {};
|
||||
//[none]~^ ERROR: cannot find struct, variant or union type `gen`
|
||||
//[e2024]~^^ ERROR: gen blocks are experimental
|
||||
//[e2024]~| ERROR: type annotations needed
|
||||
}
|
||||
|
||||
#[cfg(FALSE)]
|
||||
fn foo() {
|
||||
gen {};
|
||||
//[e2024]~^ ERROR: gen blocks are experimental
|
||||
}
|
Loading…
Reference in New Issue
Block a user