Auto merge of #118457 - eholk:genfn, r=compiler-errors
Add support for `gen fn` This builds on #116447 to add support for `gen fn` functions. For the most part we follow the same approach as desugaring `async fn`, but replacing `Future` with `Iterator` and `async {}` with `gen {}` for the body. The version implemented here uses the return type of a `gen fn` as the yield type. For example: ```rust gen fn count_to_three() -> i32 { yield 1; yield 2; yield 3; } ``` In the future, I think we should experiment with a syntax like `gen fn count_to_three() yield i32 { ... }`, but that can go in another PR. cc `@oli-obk` `@compiler-errors`
This commit is contained in:
commit
56278a6e28
36 changed files with 417 additions and 234 deletions
|
@ -1311,7 +1311,7 @@ pub struct Closure {
|
||||||
pub binder: ClosureBinder,
|
pub binder: ClosureBinder,
|
||||||
pub capture_clause: CaptureBy,
|
pub capture_clause: CaptureBy,
|
||||||
pub constness: Const,
|
pub constness: Const,
|
||||||
pub asyncness: Async,
|
pub coro_kind: Option<CoroutineKind>,
|
||||||
pub movability: Movability,
|
pub movability: Movability,
|
||||||
pub fn_decl: P<FnDecl>,
|
pub fn_decl: P<FnDecl>,
|
||||||
pub body: P<Expr>,
|
pub body: P<Expr>,
|
||||||
|
@ -2406,28 +2406,34 @@ pub enum Unsafe {
|
||||||
No,
|
No,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Describes what kind of coroutine markers, if any, a function has.
|
||||||
|
///
|
||||||
|
/// Coroutine markers are things that cause the function to generate a coroutine, such as `async`,
|
||||||
|
/// which makes the function return `impl Future`, or `gen`, which makes the function return `impl
|
||||||
|
/// Iterator`.
|
||||||
#[derive(Copy, Clone, Encodable, Decodable, Debug)]
|
#[derive(Copy, Clone, Encodable, Decodable, Debug)]
|
||||||
pub enum Async {
|
pub enum CoroutineKind {
|
||||||
Yes { span: Span, closure_id: NodeId, return_impl_trait_id: NodeId },
|
/// `async`, which evaluates to `impl Future`
|
||||||
No,
|
Async { span: Span, closure_id: NodeId, return_impl_trait_id: NodeId },
|
||||||
|
/// `gen`, which evaluates to `impl Iterator`
|
||||||
|
Gen { span: Span, closure_id: NodeId, return_impl_trait_id: NodeId },
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone, Encodable, Decodable, Debug)]
|
impl CoroutineKind {
|
||||||
pub enum Gen {
|
|
||||||
Yes { span: Span, closure_id: NodeId, return_impl_trait_id: NodeId },
|
|
||||||
No,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Async {
|
|
||||||
pub fn is_async(self) -> bool {
|
pub fn is_async(self) -> bool {
|
||||||
matches!(self, Async::Yes { .. })
|
matches!(self, CoroutineKind::Async { .. })
|
||||||
}
|
}
|
||||||
|
|
||||||
/// In this case this is an `async` return, the `NodeId` for the generated `impl Trait` item.
|
pub fn is_gen(self) -> bool {
|
||||||
pub fn opt_return_id(self) -> Option<(NodeId, Span)> {
|
matches!(self, CoroutineKind::Gen { .. })
|
||||||
|
}
|
||||||
|
|
||||||
|
/// In this case this is an `async` or `gen` return, the `NodeId` for the generated `impl Trait`
|
||||||
|
/// item.
|
||||||
|
pub fn return_id(self) -> (NodeId, Span) {
|
||||||
match self {
|
match self {
|
||||||
Async::Yes { return_impl_trait_id, span, .. } => Some((return_impl_trait_id, span)),
|
CoroutineKind::Async { return_impl_trait_id, span, .. }
|
||||||
Async::No => None,
|
| CoroutineKind::Gen { return_impl_trait_id, span, .. } => (return_impl_trait_id, span),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2831,8 +2837,8 @@ impl Extern {
|
||||||
pub struct FnHeader {
|
pub struct FnHeader {
|
||||||
/// The `unsafe` keyword, if any
|
/// The `unsafe` keyword, if any
|
||||||
pub unsafety: Unsafe,
|
pub unsafety: Unsafe,
|
||||||
/// The `async` keyword, if any
|
/// Whether this is `async`, `gen`, or nothing.
|
||||||
pub asyncness: Async,
|
pub coro_kind: Option<CoroutineKind>,
|
||||||
/// The `const` keyword, if any
|
/// The `const` keyword, if any
|
||||||
pub constness: Const,
|
pub constness: Const,
|
||||||
/// The `extern` keyword and corresponding ABI string, if any
|
/// The `extern` keyword and corresponding ABI string, if any
|
||||||
|
@ -2842,9 +2848,9 @@ pub struct FnHeader {
|
||||||
impl FnHeader {
|
impl FnHeader {
|
||||||
/// Does this function header have any qualifiers or is it empty?
|
/// Does this function header have any qualifiers or is it empty?
|
||||||
pub fn has_qualifiers(&self) -> bool {
|
pub fn has_qualifiers(&self) -> bool {
|
||||||
let Self { unsafety, asyncness, constness, ext } = self;
|
let Self { unsafety, coro_kind, constness, ext } = self;
|
||||||
matches!(unsafety, Unsafe::Yes(_))
|
matches!(unsafety, Unsafe::Yes(_))
|
||||||
|| asyncness.is_async()
|
|| coro_kind.is_some()
|
||||||
|| matches!(constness, Const::Yes(_))
|
|| matches!(constness, Const::Yes(_))
|
||||||
|| !matches!(ext, Extern::None)
|
|| !matches!(ext, Extern::None)
|
||||||
}
|
}
|
||||||
|
@ -2852,12 +2858,7 @@ impl FnHeader {
|
||||||
|
|
||||||
impl Default for FnHeader {
|
impl Default for FnHeader {
|
||||||
fn default() -> FnHeader {
|
fn default() -> FnHeader {
|
||||||
FnHeader {
|
FnHeader { unsafety: Unsafe::No, coro_kind: None, constness: Const::No, ext: Extern::None }
|
||||||
unsafety: Unsafe::No,
|
|
||||||
asyncness: Async::No,
|
|
||||||
constness: Const::No,
|
|
||||||
ext: Extern::None,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3177,7 +3178,7 @@ mod size_asserts {
|
||||||
static_assert_size!(Block, 32);
|
static_assert_size!(Block, 32);
|
||||||
static_assert_size!(Expr, 72);
|
static_assert_size!(Expr, 72);
|
||||||
static_assert_size!(ExprKind, 40);
|
static_assert_size!(ExprKind, 40);
|
||||||
static_assert_size!(Fn, 152);
|
static_assert_size!(Fn, 160);
|
||||||
static_assert_size!(ForeignItem, 96);
|
static_assert_size!(ForeignItem, 96);
|
||||||
static_assert_size!(ForeignItemKind, 24);
|
static_assert_size!(ForeignItemKind, 24);
|
||||||
static_assert_size!(GenericArg, 24);
|
static_assert_size!(GenericArg, 24);
|
||||||
|
|
|
@ -121,8 +121,8 @@ pub trait MutVisitor: Sized {
|
||||||
noop_visit_fn_decl(d, self);
|
noop_visit_fn_decl(d, self);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn visit_asyncness(&mut self, a: &mut Async) {
|
fn visit_coro_kind(&mut self, a: &mut CoroutineKind) {
|
||||||
noop_visit_asyncness(a, self);
|
noop_visit_coro_kind(a, self);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn visit_closure_binder(&mut self, b: &mut ClosureBinder) {
|
fn visit_closure_binder(&mut self, b: &mut ClosureBinder) {
|
||||||
|
@ -871,13 +871,14 @@ pub fn noop_visit_closure_binder<T: MutVisitor>(binder: &mut ClosureBinder, vis:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn noop_visit_asyncness<T: MutVisitor>(asyncness: &mut Async, vis: &mut T) {
|
pub fn noop_visit_coro_kind<T: MutVisitor>(coro_kind: &mut CoroutineKind, vis: &mut T) {
|
||||||
match asyncness {
|
match coro_kind {
|
||||||
Async::Yes { span: _, closure_id, return_impl_trait_id } => {
|
CoroutineKind::Async { span, closure_id, return_impl_trait_id }
|
||||||
|
| CoroutineKind::Gen { span, closure_id, return_impl_trait_id } => {
|
||||||
|
vis.visit_span(span);
|
||||||
vis.visit_id(closure_id);
|
vis.visit_id(closure_id);
|
||||||
vis.visit_id(return_impl_trait_id);
|
vis.visit_id(return_impl_trait_id);
|
||||||
}
|
}
|
||||||
Async::No => {}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1170,9 +1171,9 @@ fn visit_const_item<T: MutVisitor>(
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn noop_visit_fn_header<T: MutVisitor>(header: &mut FnHeader, vis: &mut T) {
|
pub fn noop_visit_fn_header<T: MutVisitor>(header: &mut FnHeader, vis: &mut T) {
|
||||||
let FnHeader { unsafety, asyncness, constness, ext: _ } = header;
|
let FnHeader { unsafety, coro_kind, constness, ext: _ } = header;
|
||||||
visit_constness(constness, vis);
|
visit_constness(constness, vis);
|
||||||
vis.visit_asyncness(asyncness);
|
coro_kind.as_mut().map(|coro_kind| vis.visit_coro_kind(coro_kind));
|
||||||
visit_unsafety(unsafety, vis);
|
visit_unsafety(unsafety, vis);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1406,7 +1407,7 @@ pub fn noop_visit_expr<T: MutVisitor>(
|
||||||
binder,
|
binder,
|
||||||
capture_clause,
|
capture_clause,
|
||||||
constness,
|
constness,
|
||||||
asyncness,
|
coro_kind,
|
||||||
movability: _,
|
movability: _,
|
||||||
fn_decl,
|
fn_decl,
|
||||||
body,
|
body,
|
||||||
|
@ -1415,7 +1416,7 @@ pub fn noop_visit_expr<T: MutVisitor>(
|
||||||
}) => {
|
}) => {
|
||||||
vis.visit_closure_binder(binder);
|
vis.visit_closure_binder(binder);
|
||||||
visit_constness(constness, vis);
|
visit_constness(constness, vis);
|
||||||
vis.visit_asyncness(asyncness);
|
coro_kind.as_mut().map(|coro_kind| vis.visit_coro_kind(coro_kind));
|
||||||
vis.visit_capture_by(capture_clause);
|
vis.visit_capture_by(capture_clause);
|
||||||
vis.visit_fn_decl(fn_decl);
|
vis.visit_fn_decl(fn_decl);
|
||||||
vis.visit_expr(body);
|
vis.visit_expr(body);
|
||||||
|
|
|
@ -861,7 +861,7 @@ pub fn walk_expr<'a, V: Visitor<'a>>(visitor: &mut V, expression: &'a Expr) {
|
||||||
ExprKind::Closure(box Closure {
|
ExprKind::Closure(box Closure {
|
||||||
binder,
|
binder,
|
||||||
capture_clause,
|
capture_clause,
|
||||||
asyncness: _,
|
coro_kind: _,
|
||||||
constness: _,
|
constness: _,
|
||||||
movability: _,
|
movability: _,
|
||||||
fn_decl,
|
fn_decl,
|
||||||
|
|
|
@ -195,15 +195,17 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
||||||
binder,
|
binder,
|
||||||
capture_clause,
|
capture_clause,
|
||||||
constness,
|
constness,
|
||||||
asyncness,
|
coro_kind,
|
||||||
movability,
|
movability,
|
||||||
fn_decl,
|
fn_decl,
|
||||||
body,
|
body,
|
||||||
fn_decl_span,
|
fn_decl_span,
|
||||||
fn_arg_span,
|
fn_arg_span,
|
||||||
}) => {
|
}) => match coro_kind {
|
||||||
if let Async::Yes { closure_id, .. } = asyncness {
|
Some(
|
||||||
self.lower_expr_async_closure(
|
CoroutineKind::Async { closure_id, .. }
|
||||||
|
| CoroutineKind::Gen { closure_id, .. },
|
||||||
|
) => self.lower_expr_async_closure(
|
||||||
binder,
|
binder,
|
||||||
*capture_clause,
|
*capture_clause,
|
||||||
e.id,
|
e.id,
|
||||||
|
@ -213,9 +215,8 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
||||||
body,
|
body,
|
||||||
*fn_decl_span,
|
*fn_decl_span,
|
||||||
*fn_arg_span,
|
*fn_arg_span,
|
||||||
)
|
),
|
||||||
} else {
|
None => self.lower_expr_closure(
|
||||||
self.lower_expr_closure(
|
|
||||||
binder,
|
binder,
|
||||||
*capture_clause,
|
*capture_clause,
|
||||||
e.id,
|
e.id,
|
||||||
|
@ -225,9 +226,8 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
||||||
body,
|
body,
|
||||||
*fn_decl_span,
|
*fn_decl_span,
|
||||||
*fn_arg_span,
|
*fn_arg_span,
|
||||||
)
|
),
|
||||||
}
|
},
|
||||||
}
|
|
||||||
ExprKind::Block(blk, opt_label) => {
|
ExprKind::Block(blk, opt_label) => {
|
||||||
let opt_label = self.lower_label(*opt_label);
|
let opt_label = self.lower_label(*opt_label);
|
||||||
hir::ExprKind::Block(self.lower_block(blk, opt_label.is_some()), opt_label)
|
hir::ExprKind::Block(self.lower_block(blk, opt_label.is_some()), opt_label)
|
||||||
|
|
|
@ -206,15 +206,19 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
||||||
// `impl Future<Output = T>` here because lower_body
|
// `impl Future<Output = T>` here because lower_body
|
||||||
// only cares about the input argument patterns in the function
|
// only cares about the input argument patterns in the function
|
||||||
// declaration (decl), not the return types.
|
// declaration (decl), not the return types.
|
||||||
let asyncness = header.asyncness;
|
let coro_kind = header.coro_kind;
|
||||||
let body_id =
|
let body_id = this.lower_maybe_coroutine_body(
|
||||||
this.lower_maybe_async_body(span, hir_id, decl, asyncness, body.as_deref());
|
span,
|
||||||
|
hir_id,
|
||||||
|
decl,
|
||||||
|
coro_kind,
|
||||||
|
body.as_deref(),
|
||||||
|
);
|
||||||
|
|
||||||
let itctx = ImplTraitContext::Universal;
|
let itctx = ImplTraitContext::Universal;
|
||||||
let (generics, decl) =
|
let (generics, decl) =
|
||||||
this.lower_generics(generics, header.constness, id, &itctx, |this| {
|
this.lower_generics(generics, header.constness, id, &itctx, |this| {
|
||||||
let ret_id = asyncness.opt_return_id();
|
this.lower_fn_decl(decl, id, *fn_sig_span, FnDeclKind::Fn, coro_kind)
|
||||||
this.lower_fn_decl(decl, id, *fn_sig_span, FnDeclKind::Fn, ret_id)
|
|
||||||
});
|
});
|
||||||
let sig = hir::FnSig {
|
let sig = hir::FnSig {
|
||||||
decl,
|
decl,
|
||||||
|
@ -725,27 +729,30 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
||||||
(generics, kind, expr.is_some())
|
(generics, kind, expr.is_some())
|
||||||
}
|
}
|
||||||
AssocItemKind::Fn(box Fn { sig, generics, body: None, .. }) => {
|
AssocItemKind::Fn(box Fn { sig, generics, body: None, .. }) => {
|
||||||
let asyncness = sig.header.asyncness;
|
|
||||||
let names = self.lower_fn_params_to_names(&sig.decl);
|
let names = self.lower_fn_params_to_names(&sig.decl);
|
||||||
let (generics, sig) = self.lower_method_sig(
|
let (generics, sig) = self.lower_method_sig(
|
||||||
generics,
|
generics,
|
||||||
sig,
|
sig,
|
||||||
i.id,
|
i.id,
|
||||||
FnDeclKind::Trait,
|
FnDeclKind::Trait,
|
||||||
asyncness.opt_return_id(),
|
sig.header.coro_kind,
|
||||||
);
|
);
|
||||||
(generics, hir::TraitItemKind::Fn(sig, hir::TraitFn::Required(names)), false)
|
(generics, hir::TraitItemKind::Fn(sig, hir::TraitFn::Required(names)), false)
|
||||||
}
|
}
|
||||||
AssocItemKind::Fn(box Fn { sig, generics, body: Some(body), .. }) => {
|
AssocItemKind::Fn(box Fn { sig, generics, body: Some(body), .. }) => {
|
||||||
let asyncness = sig.header.asyncness;
|
let body_id = self.lower_maybe_coroutine_body(
|
||||||
let body_id =
|
i.span,
|
||||||
self.lower_maybe_async_body(i.span, hir_id, &sig.decl, asyncness, Some(body));
|
hir_id,
|
||||||
|
&sig.decl,
|
||||||
|
sig.header.coro_kind,
|
||||||
|
Some(body),
|
||||||
|
);
|
||||||
let (generics, sig) = self.lower_method_sig(
|
let (generics, sig) = self.lower_method_sig(
|
||||||
generics,
|
generics,
|
||||||
sig,
|
sig,
|
||||||
i.id,
|
i.id,
|
||||||
FnDeclKind::Trait,
|
FnDeclKind::Trait,
|
||||||
asyncness.opt_return_id(),
|
sig.header.coro_kind,
|
||||||
);
|
);
|
||||||
(generics, hir::TraitItemKind::Fn(sig, hir::TraitFn::Provided(body_id)), true)
|
(generics, hir::TraitItemKind::Fn(sig, hir::TraitFn::Provided(body_id)), true)
|
||||||
}
|
}
|
||||||
|
@ -834,12 +841,11 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
AssocItemKind::Fn(box Fn { sig, generics, body, .. }) => {
|
AssocItemKind::Fn(box Fn { sig, generics, body, .. }) => {
|
||||||
let asyncness = sig.header.asyncness;
|
let body_id = self.lower_maybe_coroutine_body(
|
||||||
let body_id = self.lower_maybe_async_body(
|
|
||||||
i.span,
|
i.span,
|
||||||
hir_id,
|
hir_id,
|
||||||
&sig.decl,
|
&sig.decl,
|
||||||
asyncness,
|
sig.header.coro_kind,
|
||||||
body.as_deref(),
|
body.as_deref(),
|
||||||
);
|
);
|
||||||
let (generics, sig) = self.lower_method_sig(
|
let (generics, sig) = self.lower_method_sig(
|
||||||
|
@ -847,7 +853,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
||||||
sig,
|
sig,
|
||||||
i.id,
|
i.id,
|
||||||
if self.is_in_trait_impl { FnDeclKind::Impl } else { FnDeclKind::Inherent },
|
if self.is_in_trait_impl { FnDeclKind::Impl } else { FnDeclKind::Inherent },
|
||||||
asyncness.opt_return_id(),
|
sig.header.coro_kind,
|
||||||
);
|
);
|
||||||
|
|
||||||
(generics, hir::ImplItemKind::Fn(sig, body_id))
|
(generics, hir::ImplItemKind::Fn(sig, body_id))
|
||||||
|
@ -1011,17 +1017,23 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn lower_maybe_async_body(
|
/// Takes what may be the body of an `async fn` or a `gen fn` and wraps it in an `async {}` or
|
||||||
|
/// `gen {}` block as appropriate.
|
||||||
|
fn lower_maybe_coroutine_body(
|
||||||
&mut self,
|
&mut self,
|
||||||
span: Span,
|
span: Span,
|
||||||
fn_id: hir::HirId,
|
fn_id: hir::HirId,
|
||||||
decl: &FnDecl,
|
decl: &FnDecl,
|
||||||
asyncness: Async,
|
coro_kind: Option<CoroutineKind>,
|
||||||
body: Option<&Block>,
|
body: Option<&Block>,
|
||||||
) -> hir::BodyId {
|
) -> hir::BodyId {
|
||||||
let (closure_id, body) = match (asyncness, body) {
|
let (Some(coro_kind), Some(body)) = (coro_kind, body) else {
|
||||||
(Async::Yes { closure_id, .. }, Some(body)) => (closure_id, body),
|
return self.lower_fn_body_block(span, decl, body);
|
||||||
_ => return self.lower_fn_body_block(span, decl, body),
|
};
|
||||||
|
let closure_id = match coro_kind {
|
||||||
|
CoroutineKind::Async { closure_id, .. } | CoroutineKind::Gen { closure_id, .. } => {
|
||||||
|
closure_id
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
self.lower_body(|this| {
|
self.lower_body(|this| {
|
||||||
|
@ -1163,21 +1175,14 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
||||||
parameters.push(new_parameter);
|
parameters.push(new_parameter);
|
||||||
}
|
}
|
||||||
|
|
||||||
let async_expr = this.make_async_expr(
|
let mkbody = |this: &mut LoweringContext<'_, 'hir>| {
|
||||||
CaptureBy::Value { move_kw: rustc_span::DUMMY_SP },
|
|
||||||
closure_id,
|
|
||||||
None,
|
|
||||||
body.span,
|
|
||||||
hir::CoroutineSource::Fn,
|
|
||||||
|this| {
|
|
||||||
// Create a block from the user's function body:
|
// Create a block from the user's function body:
|
||||||
let user_body = this.lower_block_expr(body);
|
let user_body = this.lower_block_expr(body);
|
||||||
|
|
||||||
// Transform into `drop-temps { <user-body> }`, an expression:
|
// Transform into `drop-temps { <user-body> }`, an expression:
|
||||||
let desugared_span =
|
let desugared_span =
|
||||||
this.mark_span_with_reason(DesugaringKind::Async, user_body.span, None);
|
this.mark_span_with_reason(DesugaringKind::Async, user_body.span, None);
|
||||||
let user_body =
|
let user_body = this.expr_drop_temps(desugared_span, this.arena.alloc(user_body));
|
||||||
this.expr_drop_temps(desugared_span, this.arena.alloc(user_body));
|
|
||||||
|
|
||||||
// As noted above, create the final block like
|
// As noted above, create the final block like
|
||||||
//
|
//
|
||||||
|
@ -1195,12 +1200,29 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
||||||
);
|
);
|
||||||
|
|
||||||
this.expr_block(body)
|
this.expr_block(body)
|
||||||
},
|
};
|
||||||
);
|
let coroutine_expr = match coro_kind {
|
||||||
|
CoroutineKind::Async { .. } => this.make_async_expr(
|
||||||
|
CaptureBy::Value { move_kw: rustc_span::DUMMY_SP },
|
||||||
|
closure_id,
|
||||||
|
None,
|
||||||
|
body.span,
|
||||||
|
hir::CoroutineSource::Fn,
|
||||||
|
mkbody,
|
||||||
|
),
|
||||||
|
CoroutineKind::Gen { .. } => this.make_gen_expr(
|
||||||
|
CaptureBy::Value { move_kw: rustc_span::DUMMY_SP },
|
||||||
|
closure_id,
|
||||||
|
None,
|
||||||
|
body.span,
|
||||||
|
hir::CoroutineSource::Fn,
|
||||||
|
mkbody,
|
||||||
|
),
|
||||||
|
};
|
||||||
|
|
||||||
let hir_id = this.lower_node_id(closure_id);
|
let hir_id = this.lower_node_id(closure_id);
|
||||||
this.maybe_forward_track_caller(body.span, fn_id, hir_id);
|
this.maybe_forward_track_caller(body.span, fn_id, hir_id);
|
||||||
let expr = hir::Expr { hir_id, kind: async_expr, span: this.lower_span(body.span) };
|
let expr = hir::Expr { hir_id, kind: coroutine_expr, span: this.lower_span(body.span) };
|
||||||
|
|
||||||
(this.arena.alloc_from_iter(parameters), expr)
|
(this.arena.alloc_from_iter(parameters), expr)
|
||||||
})
|
})
|
||||||
|
@ -1212,21 +1234,26 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
||||||
sig: &FnSig,
|
sig: &FnSig,
|
||||||
id: NodeId,
|
id: NodeId,
|
||||||
kind: FnDeclKind,
|
kind: FnDeclKind,
|
||||||
is_async: Option<(NodeId, Span)>,
|
coro_kind: Option<CoroutineKind>,
|
||||||
) -> (&'hir hir::Generics<'hir>, hir::FnSig<'hir>) {
|
) -> (&'hir hir::Generics<'hir>, hir::FnSig<'hir>) {
|
||||||
let header = self.lower_fn_header(sig.header);
|
let header = self.lower_fn_header(sig.header);
|
||||||
let itctx = ImplTraitContext::Universal;
|
let itctx = ImplTraitContext::Universal;
|
||||||
let (generics, decl) =
|
let (generics, decl) =
|
||||||
self.lower_generics(generics, sig.header.constness, id, &itctx, |this| {
|
self.lower_generics(generics, sig.header.constness, id, &itctx, |this| {
|
||||||
this.lower_fn_decl(&sig.decl, id, sig.span, kind, is_async)
|
this.lower_fn_decl(&sig.decl, id, sig.span, kind, coro_kind)
|
||||||
});
|
});
|
||||||
(generics, hir::FnSig { header, decl, span: self.lower_span(sig.span) })
|
(generics, hir::FnSig { header, decl, span: self.lower_span(sig.span) })
|
||||||
}
|
}
|
||||||
|
|
||||||
fn lower_fn_header(&mut self, h: FnHeader) -> hir::FnHeader {
|
fn lower_fn_header(&mut self, h: FnHeader) -> hir::FnHeader {
|
||||||
|
let asyncness = if let Some(CoroutineKind::Async { span, .. }) = h.coro_kind {
|
||||||
|
hir::IsAsync::Async(span)
|
||||||
|
} else {
|
||||||
|
hir::IsAsync::NotAsync
|
||||||
|
};
|
||||||
hir::FnHeader {
|
hir::FnHeader {
|
||||||
unsafety: self.lower_unsafety(h.unsafety),
|
unsafety: self.lower_unsafety(h.unsafety),
|
||||||
asyncness: self.lower_asyncness(h.asyncness),
|
asyncness: asyncness,
|
||||||
constness: self.lower_constness(h.constness),
|
constness: self.lower_constness(h.constness),
|
||||||
abi: self.lower_extern(h.ext),
|
abi: self.lower_extern(h.ext),
|
||||||
}
|
}
|
||||||
|
@ -1268,13 +1295,6 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
fn lower_asyncness(&mut self, a: Async) -> hir::IsAsync {
|
|
||||||
match a {
|
|
||||||
Async::Yes { span, .. } => hir::IsAsync::Async(span),
|
|
||||||
Async::No => hir::IsAsync::NotAsync,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(super) fn lower_constness(&mut self, c: Const) -> hir::Constness {
|
pub(super) fn lower_constness(&mut self, c: Const) -> hir::Constness {
|
||||||
match c {
|
match c {
|
||||||
Const::Yes(_) => hir::Constness::Const,
|
Const::Yes(_) => hir::Constness::Const,
|
||||||
|
|
|
@ -1778,13 +1778,15 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Lowers a function declaration.
|
/// Lowers a function declaration.
|
||||||
//
|
///
|
||||||
// `decl`: the unlowered (AST) function declaration.
|
/// `decl`: the unlowered (AST) function declaration.
|
||||||
// `fn_node_id`: `impl Trait` arguments are lowered into generic parameters on the given `NodeId`.
|
///
|
||||||
// `make_ret_async`: if `Some`, converts `-> T` into `-> impl Future<Output = T>` in the
|
/// `fn_node_id`: `impl Trait` arguments are lowered into generic parameters on the given
|
||||||
// return type. This is used for `async fn` declarations. The `NodeId` is the ID of the
|
/// `NodeId`.
|
||||||
// return type `impl Trait` item, and the `Span` points to the `async` keyword.
|
///
|
||||||
|
/// `transform_return_type`: if `Some`, applies some conversion to the return type, such as is
|
||||||
|
/// needed for `async fn` and `gen fn`. See [`CoroutineKind`] for more details.
|
||||||
#[instrument(level = "debug", skip(self))]
|
#[instrument(level = "debug", skip(self))]
|
||||||
fn lower_fn_decl(
|
fn lower_fn_decl(
|
||||||
&mut self,
|
&mut self,
|
||||||
|
@ -1792,7 +1794,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
||||||
fn_node_id: NodeId,
|
fn_node_id: NodeId,
|
||||||
fn_span: Span,
|
fn_span: Span,
|
||||||
kind: FnDeclKind,
|
kind: FnDeclKind,
|
||||||
make_ret_async: Option<(NodeId, Span)>,
|
coro: Option<CoroutineKind>,
|
||||||
) -> &'hir hir::FnDecl<'hir> {
|
) -> &'hir hir::FnDecl<'hir> {
|
||||||
let c_variadic = decl.c_variadic();
|
let c_variadic = decl.c_variadic();
|
||||||
|
|
||||||
|
@ -1821,11 +1823,12 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
||||||
self.lower_ty_direct(¶m.ty, &itctx)
|
self.lower_ty_direct(¶m.ty, &itctx)
|
||||||
}));
|
}));
|
||||||
|
|
||||||
let output = if let Some((ret_id, _span)) = make_ret_async {
|
let output = match coro {
|
||||||
|
Some(coro) => {
|
||||||
let fn_def_id = self.local_def_id(fn_node_id);
|
let fn_def_id = self.local_def_id(fn_node_id);
|
||||||
self.lower_async_fn_ret_ty(&decl.output, fn_def_id, ret_id, kind, fn_span)
|
self.lower_coroutine_fn_ret_ty(&decl.output, fn_def_id, coro, kind, fn_span)
|
||||||
} else {
|
}
|
||||||
match &decl.output {
|
None => match &decl.output {
|
||||||
FnRetTy::Ty(ty) => {
|
FnRetTy::Ty(ty) => {
|
||||||
let context = if kind.return_impl_trait_allowed() {
|
let context = if kind.return_impl_trait_allowed() {
|
||||||
let fn_def_id = self.local_def_id(fn_node_id);
|
let fn_def_id = self.local_def_id(fn_node_id);
|
||||||
|
@ -1849,7 +1852,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
||||||
hir::FnRetTy::Return(self.lower_ty(ty, &context))
|
hir::FnRetTy::Return(self.lower_ty(ty, &context))
|
||||||
}
|
}
|
||||||
FnRetTy::Default(span) => hir::FnRetTy::DefaultReturn(self.lower_span(*span)),
|
FnRetTy::Default(span) => hir::FnRetTy::DefaultReturn(self.lower_span(*span)),
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
self.arena.alloc(hir::FnDecl {
|
self.arena.alloc(hir::FnDecl {
|
||||||
|
@ -1888,17 +1891,22 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
||||||
// `fn_node_id`: `NodeId` of the parent function (used to create child impl trait definition)
|
// `fn_node_id`: `NodeId` of the parent function (used to create child impl trait definition)
|
||||||
// `opaque_ty_node_id`: `NodeId` of the opaque `impl Trait` type that should be created
|
// `opaque_ty_node_id`: `NodeId` of the opaque `impl Trait` type that should be created
|
||||||
#[instrument(level = "debug", skip(self))]
|
#[instrument(level = "debug", skip(self))]
|
||||||
fn lower_async_fn_ret_ty(
|
fn lower_coroutine_fn_ret_ty(
|
||||||
&mut self,
|
&mut self,
|
||||||
output: &FnRetTy,
|
output: &FnRetTy,
|
||||||
fn_def_id: LocalDefId,
|
fn_def_id: LocalDefId,
|
||||||
opaque_ty_node_id: NodeId,
|
coro: CoroutineKind,
|
||||||
fn_kind: FnDeclKind,
|
fn_kind: FnDeclKind,
|
||||||
fn_span: Span,
|
fn_span: Span,
|
||||||
) -> hir::FnRetTy<'hir> {
|
) -> hir::FnRetTy<'hir> {
|
||||||
let span = self.lower_span(fn_span);
|
let span = self.lower_span(fn_span);
|
||||||
let opaque_ty_span = self.mark_span_with_reason(DesugaringKind::Async, span, None);
|
let opaque_ty_span = self.mark_span_with_reason(DesugaringKind::Async, span, None);
|
||||||
|
|
||||||
|
let opaque_ty_node_id = match coro {
|
||||||
|
CoroutineKind::Async { return_impl_trait_id, .. }
|
||||||
|
| CoroutineKind::Gen { return_impl_trait_id, .. } => return_impl_trait_id,
|
||||||
|
};
|
||||||
|
|
||||||
let captured_lifetimes: Vec<_> = self
|
let captured_lifetimes: Vec<_> = self
|
||||||
.resolver
|
.resolver
|
||||||
.take_extra_lifetime_params(opaque_ty_node_id)
|
.take_extra_lifetime_params(opaque_ty_node_id)
|
||||||
|
@ -1914,15 +1922,16 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
||||||
span,
|
span,
|
||||||
opaque_ty_span,
|
opaque_ty_span,
|
||||||
|this| {
|
|this| {
|
||||||
let future_bound = this.lower_async_fn_output_type_to_future_bound(
|
let bound = this.lower_coroutine_fn_output_type_to_bound(
|
||||||
output,
|
output,
|
||||||
|
coro,
|
||||||
span,
|
span,
|
||||||
ImplTraitContext::ReturnPositionOpaqueTy {
|
ImplTraitContext::ReturnPositionOpaqueTy {
|
||||||
origin: hir::OpaqueTyOrigin::FnReturn(fn_def_id),
|
origin: hir::OpaqueTyOrigin::FnReturn(fn_def_id),
|
||||||
fn_kind,
|
fn_kind,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
arena_vec![this; future_bound]
|
arena_vec![this; bound]
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -1931,9 +1940,10 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Transforms `-> T` into `Future<Output = T>`.
|
/// Transforms `-> T` into `Future<Output = T>`.
|
||||||
fn lower_async_fn_output_type_to_future_bound(
|
fn lower_coroutine_fn_output_type_to_bound(
|
||||||
&mut self,
|
&mut self,
|
||||||
output: &FnRetTy,
|
output: &FnRetTy,
|
||||||
|
coro: CoroutineKind,
|
||||||
span: Span,
|
span: Span,
|
||||||
nested_impl_trait_context: ImplTraitContext,
|
nested_impl_trait_context: ImplTraitContext,
|
||||||
) -> hir::GenericBound<'hir> {
|
) -> hir::GenericBound<'hir> {
|
||||||
|
@ -1948,17 +1958,21 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
||||||
FnRetTy::Default(ret_ty_span) => self.arena.alloc(self.ty_tup(*ret_ty_span, &[])),
|
FnRetTy::Default(ret_ty_span) => self.arena.alloc(self.ty_tup(*ret_ty_span, &[])),
|
||||||
};
|
};
|
||||||
|
|
||||||
// "<Output = T>"
|
// "<$assoc_ty_name = T>"
|
||||||
|
let (assoc_ty_name, trait_lang_item) = match coro {
|
||||||
|
CoroutineKind::Async { .. } => (hir::FN_OUTPUT_NAME, hir::LangItem::Future),
|
||||||
|
CoroutineKind::Gen { .. } => (hir::ITERATOR_ITEM_NAME, hir::LangItem::Iterator),
|
||||||
|
};
|
||||||
|
|
||||||
let future_args = self.arena.alloc(hir::GenericArgs {
|
let future_args = self.arena.alloc(hir::GenericArgs {
|
||||||
args: &[],
|
args: &[],
|
||||||
bindings: arena_vec![self; self.output_ty_binding(span, output_ty)],
|
bindings: arena_vec![self; self.assoc_ty_binding(assoc_ty_name, span, output_ty)],
|
||||||
parenthesized: hir::GenericArgsParentheses::No,
|
parenthesized: hir::GenericArgsParentheses::No,
|
||||||
span_ext: DUMMY_SP,
|
span_ext: DUMMY_SP,
|
||||||
});
|
});
|
||||||
|
|
||||||
hir::GenericBound::LangItemTrait(
|
hir::GenericBound::LangItemTrait(
|
||||||
// ::std::future::Future<future_params>
|
trait_lang_item,
|
||||||
hir::LangItem::Future,
|
|
||||||
self.lower_span(span),
|
self.lower_span(span),
|
||||||
self.next_id(),
|
self.next_id(),
|
||||||
future_args,
|
future_args,
|
||||||
|
|
|
@ -389,7 +389,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
||||||
FnRetTy::Default(_) => self.arena.alloc(self.ty_tup(*span, &[])),
|
FnRetTy::Default(_) => self.arena.alloc(self.ty_tup(*span, &[])),
|
||||||
};
|
};
|
||||||
let args = smallvec![GenericArg::Type(self.arena.alloc(self.ty_tup(*inputs_span, inputs)))];
|
let args = smallvec![GenericArg::Type(self.arena.alloc(self.ty_tup(*inputs_span, inputs)))];
|
||||||
let binding = self.output_ty_binding(output_ty.span, output_ty);
|
let binding = self.assoc_ty_binding(hir::FN_OUTPUT_NAME, output_ty.span, output_ty);
|
||||||
(
|
(
|
||||||
GenericArgsCtor {
|
GenericArgsCtor {
|
||||||
args,
|
args,
|
||||||
|
@ -401,13 +401,14 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// An associated type binding `Output = $ty`.
|
/// An associated type binding `$assoc_ty_name = $ty`.
|
||||||
pub(crate) fn output_ty_binding(
|
pub(crate) fn assoc_ty_binding(
|
||||||
&mut self,
|
&mut self,
|
||||||
|
assoc_ty_name: rustc_span::Symbol,
|
||||||
span: Span,
|
span: Span,
|
||||||
ty: &'hir hir::Ty<'hir>,
|
ty: &'hir hir::Ty<'hir>,
|
||||||
) -> hir::TypeBinding<'hir> {
|
) -> hir::TypeBinding<'hir> {
|
||||||
let ident = Ident::with_dummy_span(hir::FN_OUTPUT_NAME);
|
let ident = Ident::with_dummy_span(assoc_ty_name);
|
||||||
let kind = hir::TypeBindingKind::Equality { term: ty.into() };
|
let kind = hir::TypeBindingKind::Equality { term: ty.into() };
|
||||||
let args = arena_vec![self;];
|
let args = arena_vec![self;];
|
||||||
let bindings = arena_vec![self;];
|
let bindings = arena_vec![self;];
|
||||||
|
|
|
@ -1268,13 +1268,18 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
|
||||||
|
|
||||||
self.check_c_variadic_type(fk);
|
self.check_c_variadic_type(fk);
|
||||||
|
|
||||||
// Functions cannot both be `const async`
|
// Functions cannot both be `const async` or `const gen`
|
||||||
if let Some(&FnHeader {
|
if let Some(&FnHeader {
|
||||||
constness: Const::Yes(cspan),
|
constness: Const::Yes(cspan),
|
||||||
asyncness: Async::Yes { span: aspan, .. },
|
coro_kind:
|
||||||
|
Some(
|
||||||
|
CoroutineKind::Async { span: aspan, .. }
|
||||||
|
| CoroutineKind::Gen { span: aspan, .. },
|
||||||
|
),
|
||||||
..
|
..
|
||||||
}) = fk.header()
|
}) = fk.header()
|
||||||
{
|
{
|
||||||
|
// FIXME(gen_blocks): Report a different error for `const gen`
|
||||||
self.err_handler().emit_err(errors::ConstAndAsync {
|
self.err_handler().emit_err(errors::ConstAndAsync {
|
||||||
spans: vec![cspan, aspan],
|
spans: vec![cspan, aspan],
|
||||||
cspan,
|
cspan,
|
||||||
|
|
|
@ -1490,11 +1490,16 @@ impl<'a> State<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn print_asyncness(&mut self, asyncness: ast::Async) {
|
fn print_coro_kind(&mut self, coro_kind: ast::CoroutineKind) {
|
||||||
if asyncness.is_async() {
|
match coro_kind {
|
||||||
|
ast::CoroutineKind::Gen { .. } => {
|
||||||
|
self.word_nbsp("gen");
|
||||||
|
}
|
||||||
|
ast::CoroutineKind::Async { .. } => {
|
||||||
self.word_nbsp("async");
|
self.word_nbsp("async");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn print_type_bounds(&mut self, bounds: &[ast::GenericBound]) {
|
pub fn print_type_bounds(&mut self, bounds: &[ast::GenericBound]) {
|
||||||
let mut first = true;
|
let mut first = true;
|
||||||
|
@ -1685,7 +1690,7 @@ impl<'a> State<'a> {
|
||||||
|
|
||||||
fn print_fn_header_info(&mut self, header: ast::FnHeader) {
|
fn print_fn_header_info(&mut self, header: ast::FnHeader) {
|
||||||
self.print_constness(header.constness);
|
self.print_constness(header.constness);
|
||||||
self.print_asyncness(header.asyncness);
|
header.coro_kind.map(|coro_kind| self.print_coro_kind(coro_kind));
|
||||||
self.print_unsafety(header.unsafety);
|
self.print_unsafety(header.unsafety);
|
||||||
|
|
||||||
match header.ext {
|
match header.ext {
|
||||||
|
|
|
@ -413,7 +413,7 @@ impl<'a> State<'a> {
|
||||||
binder,
|
binder,
|
||||||
capture_clause,
|
capture_clause,
|
||||||
constness,
|
constness,
|
||||||
asyncness,
|
coro_kind,
|
||||||
movability,
|
movability,
|
||||||
fn_decl,
|
fn_decl,
|
||||||
body,
|
body,
|
||||||
|
@ -423,7 +423,7 @@ impl<'a> State<'a> {
|
||||||
self.print_closure_binder(binder);
|
self.print_closure_binder(binder);
|
||||||
self.print_constness(*constness);
|
self.print_constness(*constness);
|
||||||
self.print_movability(*movability);
|
self.print_movability(*movability);
|
||||||
self.print_asyncness(*asyncness);
|
coro_kind.map(|coro_kind| self.print_coro_kind(coro_kind));
|
||||||
self.print_capture_clause(*capture_clause);
|
self.print_capture_clause(*capture_clause);
|
||||||
|
|
||||||
self.print_fn_params_and_ret(fn_decl, true);
|
self.print_fn_params_and_ret(fn_decl, true);
|
||||||
|
|
|
@ -541,10 +541,14 @@ fn check_test_signature(
|
||||||
return Err(sd.emit_err(errors::TestBadFn { span: i.span, cause: span, kind: "unsafe" }));
|
return Err(sd.emit_err(errors::TestBadFn { span: i.span, cause: span, kind: "unsafe" }));
|
||||||
}
|
}
|
||||||
|
|
||||||
if let ast::Async::Yes { span, .. } = f.sig.header.asyncness {
|
if let Some(ast::CoroutineKind::Async { span, .. }) = f.sig.header.coro_kind {
|
||||||
return Err(sd.emit_err(errors::TestBadFn { span: i.span, cause: span, kind: "async" }));
|
return Err(sd.emit_err(errors::TestBadFn { span: i.span, cause: span, kind: "async" }));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if let Some(ast::CoroutineKind::Gen { span, .. }) = f.sig.header.coro_kind {
|
||||||
|
return Err(sd.emit_err(errors::TestBadFn { span: i.span, cause: span, kind: "gen" }));
|
||||||
|
}
|
||||||
|
|
||||||
// If the termination trait is active, the compiler will check that the output
|
// If the termination trait is active, the compiler will check that the output
|
||||||
// type implements the `Termination` trait as `libtest` enforces that.
|
// type implements the `Termination` trait as `libtest` enforces that.
|
||||||
let has_output = match &f.sig.decl.output {
|
let has_output = match &f.sig.decl.output {
|
||||||
|
|
|
@ -547,7 +547,7 @@ impl<'a> ExtCtxt<'a> {
|
||||||
binder: ast::ClosureBinder::NotPresent,
|
binder: ast::ClosureBinder::NotPresent,
|
||||||
capture_clause: ast::CaptureBy::Ref,
|
capture_clause: ast::CaptureBy::Ref,
|
||||||
constness: ast::Const::No,
|
constness: ast::Const::No,
|
||||||
asyncness: ast::Async::No,
|
coro_kind: None,
|
||||||
movability: ast::Movability::Movable,
|
movability: ast::Movability::Movable,
|
||||||
fn_decl,
|
fn_decl,
|
||||||
body,
|
body,
|
||||||
|
|
|
@ -2255,6 +2255,8 @@ pub enum ImplItemKind<'hir> {
|
||||||
|
|
||||||
/// The name of the associated type for `Fn` return types.
|
/// The name of the associated type for `Fn` return types.
|
||||||
pub const FN_OUTPUT_NAME: Symbol = sym::Output;
|
pub const FN_OUTPUT_NAME: Symbol = sym::Output;
|
||||||
|
/// The name of the associated type for `Iterator` item types.
|
||||||
|
pub const ITERATOR_ITEM_NAME: Symbol = sym::Item;
|
||||||
|
|
||||||
/// Bind a type to an associated type (i.e., `A = Foo`).
|
/// Bind a type to an associated type (i.e., `A = Foo`).
|
||||||
///
|
///
|
||||||
|
|
|
@ -651,9 +651,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
Some(hir::CoroutineKind::Gen(hir::CoroutineSource::Fn)) => {
|
// For a `gen {}` block created as a `gen fn` body, we need the return type to be
|
||||||
todo!("gen closures do not exist yet")
|
// ().
|
||||||
}
|
Some(hir::CoroutineKind::Gen(hir::CoroutineSource::Fn)) => self.tcx.types.unit,
|
||||||
|
|
||||||
_ => astconv.ty_infer(None, decl.output.span()),
|
_ => astconv.ty_infer(None, decl.output.span()),
|
||||||
},
|
},
|
||||||
|
|
|
@ -162,7 +162,11 @@ impl<'a, T: EarlyLintPass> ast_visit::Visitor<'a> for EarlyContextAndPass<'a, T>
|
||||||
// Explicitly check for lints associated with 'closure_id', since
|
// Explicitly check for lints associated with 'closure_id', since
|
||||||
// it does not have a corresponding AST node
|
// it does not have a corresponding AST node
|
||||||
if let ast_visit::FnKind::Fn(_, _, sig, _, _, _) = fk {
|
if let ast_visit::FnKind::Fn(_, _, sig, _, _, _) = fk {
|
||||||
if let ast::Async::Yes { closure_id, .. } = sig.header.asyncness {
|
if let Some(
|
||||||
|
ast::CoroutineKind::Async { closure_id, .. }
|
||||||
|
| ast::CoroutineKind::Gen { closure_id, .. },
|
||||||
|
) = sig.header.coro_kind
|
||||||
|
{
|
||||||
self.check_id(closure_id);
|
self.check_id(closure_id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -223,7 +227,11 @@ impl<'a, T: EarlyLintPass> ast_visit::Visitor<'a> for EarlyContextAndPass<'a, T>
|
||||||
// it does not have a corresponding AST node
|
// it does not have a corresponding AST node
|
||||||
match e.kind {
|
match e.kind {
|
||||||
ast::ExprKind::Closure(box ast::Closure {
|
ast::ExprKind::Closure(box ast::Closure {
|
||||||
asyncness: ast::Async::Yes { closure_id, .. },
|
coro_kind:
|
||||||
|
Some(
|
||||||
|
ast::CoroutineKind::Async { closure_id, .. }
|
||||||
|
| ast::CoroutineKind::Gen { closure_id, .. },
|
||||||
|
),
|
||||||
..
|
..
|
||||||
}) => self.check_id(closure_id),
|
}) => self.check_id(closure_id),
|
||||||
_ => {}
|
_ => {}
|
||||||
|
|
|
@ -23,6 +23,8 @@ parse_async_block_in_2015 = `async` blocks are only allowed in Rust 2018 or late
|
||||||
parse_async_fn_in_2015 = `async fn` is not permitted in Rust 2015
|
parse_async_fn_in_2015 = `async fn` is not permitted in Rust 2015
|
||||||
.label = to use `async fn`, switch to Rust 2018 or later
|
.label = to use `async fn`, switch to Rust 2018 or later
|
||||||
|
|
||||||
|
parse_async_gen_fn = `async gen` functions are not supported
|
||||||
|
|
||||||
parse_async_move_block_in_2015 = `async move` blocks are only allowed in Rust 2018 or later
|
parse_async_move_block_in_2015 = `async move` blocks are only allowed in Rust 2018 or later
|
||||||
|
|
||||||
parse_async_move_order_incorrect = the order of `move` and `async` is incorrect
|
parse_async_move_order_incorrect = the order of `move` and `async` is incorrect
|
||||||
|
|
|
@ -562,6 +562,13 @@ pub(crate) struct GenFn {
|
||||||
pub span: Span,
|
pub span: Span,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Diagnostic)]
|
||||||
|
#[diag(parse_async_gen_fn)]
|
||||||
|
pub(crate) struct AsyncGenFn {
|
||||||
|
#[primary_span]
|
||||||
|
pub span: Span,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Diagnostic)]
|
#[derive(Diagnostic)]
|
||||||
#[diag(parse_comma_after_base_struct)]
|
#[diag(parse_comma_after_base_struct)]
|
||||||
#[note]
|
#[note]
|
||||||
|
|
|
@ -10,7 +10,7 @@ use super::{
|
||||||
use crate::errors;
|
use crate::errors;
|
||||||
use crate::maybe_recover_from_interpolated_ty_qpath;
|
use crate::maybe_recover_from_interpolated_ty_qpath;
|
||||||
use ast::mut_visit::{noop_visit_expr, MutVisitor};
|
use ast::mut_visit::{noop_visit_expr, MutVisitor};
|
||||||
use ast::{GenBlockKind, Pat, Path, PathSegment};
|
use ast::{CoroutineKind, GenBlockKind, Pat, Path, PathSegment};
|
||||||
use core::mem;
|
use core::mem;
|
||||||
use rustc_ast::ptr::P;
|
use rustc_ast::ptr::P;
|
||||||
use rustc_ast::token::{self, Delimiter, Token, TokenKind};
|
use rustc_ast::token::{self, Delimiter, Token, TokenKind};
|
||||||
|
@ -21,7 +21,7 @@ use rustc_ast::util::parser::{prec_let_scrutinee_needs_par, AssocOp, Fixity};
|
||||||
use rustc_ast::visit::Visitor;
|
use rustc_ast::visit::Visitor;
|
||||||
use rustc_ast::{self as ast, AttrStyle, AttrVec, CaptureBy, ExprField, UnOp, DUMMY_NODE_ID};
|
use rustc_ast::{self as ast, AttrStyle, AttrVec, CaptureBy, ExprField, UnOp, DUMMY_NODE_ID};
|
||||||
use rustc_ast::{AnonConst, BinOp, BinOpKind, FnDecl, FnRetTy, MacCall, Param, Ty, TyKind};
|
use rustc_ast::{AnonConst, BinOp, BinOpKind, FnDecl, FnRetTy, MacCall, Param, Ty, TyKind};
|
||||||
use rustc_ast::{Arm, Async, BlockCheckMode, Expr, ExprKind, Label, Movability, RangeLimits};
|
use rustc_ast::{Arm, BlockCheckMode, Expr, ExprKind, Label, Movability, RangeLimits};
|
||||||
use rustc_ast::{ClosureBinder, MetaItemLit, StmtKind};
|
use rustc_ast::{ClosureBinder, MetaItemLit, StmtKind};
|
||||||
use rustc_ast_pretty::pprust;
|
use rustc_ast_pretty::pprust;
|
||||||
use rustc_data_structures::stack::ensure_sufficient_stack;
|
use rustc_data_structures::stack::ensure_sufficient_stack;
|
||||||
|
@ -2237,7 +2237,7 @@ impl<'a> Parser<'a> {
|
||||||
let asyncness = if self.token.uninterpolated_span().at_least_rust_2018() {
|
let asyncness = if self.token.uninterpolated_span().at_least_rust_2018() {
|
||||||
self.parse_asyncness(Case::Sensitive)
|
self.parse_asyncness(Case::Sensitive)
|
||||||
} else {
|
} else {
|
||||||
Async::No
|
None
|
||||||
};
|
};
|
||||||
|
|
||||||
let capture_clause = self.parse_capture_clause()?;
|
let capture_clause = self.parse_capture_clause()?;
|
||||||
|
@ -2261,7 +2261,7 @@ impl<'a> Parser<'a> {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Async::Yes { span, .. } = asyncness {
|
if let Some(CoroutineKind::Async { span, .. }) = asyncness {
|
||||||
// Feature-gate `async ||` closures.
|
// Feature-gate `async ||` closures.
|
||||||
self.sess.gated_spans.gate(sym::async_closure, span);
|
self.sess.gated_spans.gate(sym::async_closure, span);
|
||||||
}
|
}
|
||||||
|
@ -2284,7 +2284,7 @@ impl<'a> Parser<'a> {
|
||||||
binder,
|
binder,
|
||||||
capture_clause,
|
capture_clause,
|
||||||
constness,
|
constness,
|
||||||
asyncness,
|
coro_kind: asyncness,
|
||||||
movability,
|
movability,
|
||||||
fn_decl,
|
fn_decl,
|
||||||
body,
|
body,
|
||||||
|
|
|
@ -11,8 +11,8 @@ use rustc_ast::tokenstream::{DelimSpan, TokenStream, TokenTree};
|
||||||
use rustc_ast::util::case::Case;
|
use rustc_ast::util::case::Case;
|
||||||
use rustc_ast::MacCall;
|
use rustc_ast::MacCall;
|
||||||
use rustc_ast::{self as ast, AttrVec, Attribute, DUMMY_NODE_ID};
|
use rustc_ast::{self as ast, AttrVec, Attribute, DUMMY_NODE_ID};
|
||||||
use rustc_ast::{Async, Const, Defaultness, IsAuto, Mutability, Unsafe, UseTree, UseTreeKind};
|
|
||||||
use rustc_ast::{BindingAnnotation, Block, FnDecl, FnSig, Param, SelfKind};
|
use rustc_ast::{BindingAnnotation, Block, FnDecl, FnSig, Param, SelfKind};
|
||||||
|
use rustc_ast::{Const, Defaultness, IsAuto, Mutability, Unsafe, UseTree, UseTreeKind};
|
||||||
use rustc_ast::{EnumDef, FieldDef, Generics, TraitRef, Ty, TyKind, Variant, VariantData};
|
use rustc_ast::{EnumDef, FieldDef, Generics, TraitRef, Ty, TyKind, Variant, VariantData};
|
||||||
use rustc_ast::{FnHeader, ForeignItem, Path, PathSegment, Visibility, VisibilityKind};
|
use rustc_ast::{FnHeader, ForeignItem, Path, PathSegment, Visibility, VisibilityKind};
|
||||||
use rustc_ast_pretty::pprust;
|
use rustc_ast_pretty::pprust;
|
||||||
|
@ -2401,7 +2401,7 @@ impl<'a> Parser<'a> {
|
||||||
let ext_start_sp = self.token.span;
|
let ext_start_sp = self.token.span;
|
||||||
let ext = self.parse_extern(case);
|
let ext = self.parse_extern(case);
|
||||||
|
|
||||||
if let Async::Yes { span, .. } = asyncness {
|
if let Some(CoroutineKind::Async { span, .. }) = asyncness {
|
||||||
if span.is_rust_2015() {
|
if span.is_rust_2015() {
|
||||||
self.sess.emit_err(errors::AsyncFnIn2015 {
|
self.sess.emit_err(errors::AsyncFnIn2015 {
|
||||||
span,
|
span,
|
||||||
|
@ -2410,8 +2410,16 @@ impl<'a> Parser<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Gen::Yes { span, .. } = genness {
|
if let Some(CoroutineKind::Gen { span, .. }) = genness {
|
||||||
self.sess.emit_err(errors::GenFn { span });
|
self.sess.gated_spans.gate(sym::gen_blocks, span);
|
||||||
|
}
|
||||||
|
|
||||||
|
if let (
|
||||||
|
Some(CoroutineKind::Async { span: async_span, .. }),
|
||||||
|
Some(CoroutineKind::Gen { span: gen_span, .. }),
|
||||||
|
) = (asyncness, genness)
|
||||||
|
{
|
||||||
|
self.sess.emit_err(errors::AsyncGenFn { span: async_span.to(gen_span) });
|
||||||
}
|
}
|
||||||
|
|
||||||
if !self.eat_keyword_case(kw::Fn, case) {
|
if !self.eat_keyword_case(kw::Fn, case) {
|
||||||
|
@ -2444,13 +2452,18 @@ impl<'a> Parser<'a> {
|
||||||
}
|
}
|
||||||
} else if self.check_keyword(kw::Async) {
|
} else if self.check_keyword(kw::Async) {
|
||||||
match asyncness {
|
match asyncness {
|
||||||
Async::Yes { span, .. } => Some(WrongKw::Duplicated(span)),
|
Some(CoroutineKind::Async { span, .. }) => {
|
||||||
Async::No => {
|
Some(WrongKw::Duplicated(span))
|
||||||
recover_asyncness = Async::Yes {
|
}
|
||||||
|
Some(CoroutineKind::Gen { .. }) => {
|
||||||
|
panic!("not sure how to recover here")
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
recover_asyncness = Some(CoroutineKind::Async {
|
||||||
span: self.token.span,
|
span: self.token.span,
|
||||||
closure_id: DUMMY_NODE_ID,
|
closure_id: DUMMY_NODE_ID,
|
||||||
return_impl_trait_id: DUMMY_NODE_ID,
|
return_impl_trait_id: DUMMY_NODE_ID,
|
||||||
};
|
});
|
||||||
Some(WrongKw::Misplaced(unsafe_start_sp))
|
Some(WrongKw::Misplaced(unsafe_start_sp))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2531,6 +2544,8 @@ impl<'a> Parser<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FIXME(gen_blocks): add keyword recovery logic for genness
|
||||||
|
|
||||||
if wrong_kw.is_some()
|
if wrong_kw.is_some()
|
||||||
&& self.may_recover()
|
&& self.may_recover()
|
||||||
&& self.look_ahead(1, |tok| tok.is_keyword_case(kw::Fn, case))
|
&& self.look_ahead(1, |tok| tok.is_keyword_case(kw::Fn, case))
|
||||||
|
@ -2542,7 +2557,7 @@ impl<'a> Parser<'a> {
|
||||||
return Ok(FnHeader {
|
return Ok(FnHeader {
|
||||||
constness: recover_constness,
|
constness: recover_constness,
|
||||||
unsafety: recover_unsafety,
|
unsafety: recover_unsafety,
|
||||||
asyncness: recover_asyncness,
|
coro_kind: recover_asyncness,
|
||||||
ext,
|
ext,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -2552,7 +2567,13 @@ impl<'a> Parser<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(FnHeader { constness, unsafety, asyncness, ext })
|
let coro_kind = match asyncness {
|
||||||
|
Some(CoroutineKind::Async { .. }) => asyncness,
|
||||||
|
Some(CoroutineKind::Gen { .. }) => unreachable!("asycness cannot be Gen"),
|
||||||
|
None => genness,
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(FnHeader { constness, unsafety, coro_kind, ext })
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parses the parameter list and result type of a function declaration.
|
/// Parses the parameter list and result type of a function declaration.
|
||||||
|
|
|
@ -11,7 +11,6 @@ mod stmt;
|
||||||
mod ty;
|
mod ty;
|
||||||
|
|
||||||
use crate::lexer::UnmatchedDelim;
|
use crate::lexer::UnmatchedDelim;
|
||||||
use ast::Gen;
|
|
||||||
pub use attr_wrapper::AttrWrapper;
|
pub use attr_wrapper::AttrWrapper;
|
||||||
pub use diagnostics::AttemptLocalParseRecovery;
|
pub use diagnostics::AttemptLocalParseRecovery;
|
||||||
pub(crate) use expr::ForbiddenLetReason;
|
pub(crate) use expr::ForbiddenLetReason;
|
||||||
|
@ -25,9 +24,10 @@ use rustc_ast::tokenstream::{AttributesData, DelimSpan, Spacing};
|
||||||
use rustc_ast::tokenstream::{TokenStream, TokenTree, TokenTreeCursor};
|
use rustc_ast::tokenstream::{TokenStream, TokenTree, TokenTreeCursor};
|
||||||
use rustc_ast::util::case::Case;
|
use rustc_ast::util::case::Case;
|
||||||
use rustc_ast::AttrId;
|
use rustc_ast::AttrId;
|
||||||
|
use rustc_ast::CoroutineKind;
|
||||||
use rustc_ast::DUMMY_NODE_ID;
|
use rustc_ast::DUMMY_NODE_ID;
|
||||||
use rustc_ast::{self as ast, AnonConst, Const, DelimArgs, Extern};
|
use rustc_ast::{self as ast, AnonConst, Const, DelimArgs, Extern};
|
||||||
use rustc_ast::{Async, AttrArgs, AttrArgsEq, Expr, ExprKind, Mutability, StrLit};
|
use rustc_ast::{AttrArgs, AttrArgsEq, Expr, ExprKind, Mutability, StrLit};
|
||||||
use rustc_ast::{HasAttrs, HasTokens, Unsafe, Visibility, VisibilityKind};
|
use rustc_ast::{HasAttrs, HasTokens, Unsafe, Visibility, VisibilityKind};
|
||||||
use rustc_ast_pretty::pprust;
|
use rustc_ast_pretty::pprust;
|
||||||
use rustc_data_structures::fx::FxHashMap;
|
use rustc_data_structures::fx::FxHashMap;
|
||||||
|
@ -1125,22 +1125,30 @@ impl<'a> Parser<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parses asyncness: `async` or nothing.
|
/// Parses asyncness: `async` or nothing.
|
||||||
fn parse_asyncness(&mut self, case: Case) -> Async {
|
fn parse_asyncness(&mut self, case: Case) -> Option<CoroutineKind> {
|
||||||
if self.eat_keyword_case(kw::Async, case) {
|
if self.eat_keyword_case(kw::Async, case) {
|
||||||
let span = self.prev_token.uninterpolated_span();
|
let span = self.prev_token.uninterpolated_span();
|
||||||
Async::Yes { span, closure_id: DUMMY_NODE_ID, return_impl_trait_id: DUMMY_NODE_ID }
|
Some(CoroutineKind::Async {
|
||||||
|
span,
|
||||||
|
closure_id: DUMMY_NODE_ID,
|
||||||
|
return_impl_trait_id: DUMMY_NODE_ID,
|
||||||
|
})
|
||||||
} else {
|
} else {
|
||||||
Async::No
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parses genness: `gen` or nothing.
|
/// Parses genness: `gen` or nothing.
|
||||||
fn parse_genness(&mut self, case: Case) -> Gen {
|
fn parse_genness(&mut self, case: Case) -> Option<CoroutineKind> {
|
||||||
if self.token.span.at_least_rust_2024() && self.eat_keyword_case(kw::Gen, case) {
|
if self.token.span.at_least_rust_2024() && self.eat_keyword_case(kw::Gen, case) {
|
||||||
let span = self.prev_token.uninterpolated_span();
|
let span = self.prev_token.uninterpolated_span();
|
||||||
Gen::Yes { span, closure_id: DUMMY_NODE_ID, return_impl_trait_id: DUMMY_NODE_ID }
|
Some(CoroutineKind::Gen {
|
||||||
|
span,
|
||||||
|
closure_id: DUMMY_NODE_ID,
|
||||||
|
return_impl_trait_id: DUMMY_NODE_ID,
|
||||||
|
})
|
||||||
} else {
|
} else {
|
||||||
Gen::No
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -596,7 +596,7 @@ impl<'a> Parser<'a> {
|
||||||
tokens: None,
|
tokens: None,
|
||||||
};
|
};
|
||||||
let span_start = self.token.span;
|
let span_start = self.token.span;
|
||||||
let ast::FnHeader { ext, unsafety, constness, asyncness } =
|
let ast::FnHeader { ext, unsafety, constness, coro_kind } =
|
||||||
self.parse_fn_front_matter(&inherited_vis, Case::Sensitive)?;
|
self.parse_fn_front_matter(&inherited_vis, Case::Sensitive)?;
|
||||||
if self.may_recover() && self.token.kind == TokenKind::Lt {
|
if self.may_recover() && self.token.kind == TokenKind::Lt {
|
||||||
self.recover_fn_ptr_with_generics(lo, &mut params, param_insertion_point)?;
|
self.recover_fn_ptr_with_generics(lo, &mut params, param_insertion_point)?;
|
||||||
|
@ -609,9 +609,10 @@ impl<'a> Parser<'a> {
|
||||||
// cover it.
|
// cover it.
|
||||||
self.sess.emit_err(FnPointerCannotBeConst { span: whole_span, qualifier: span });
|
self.sess.emit_err(FnPointerCannotBeConst { span: whole_span, qualifier: span });
|
||||||
}
|
}
|
||||||
if let ast::Async::Yes { span, .. } = asyncness {
|
if let Some(ast::CoroutineKind::Async { span, .. }) = coro_kind {
|
||||||
self.sess.emit_err(FnPointerCannotBeAsync { span: whole_span, qualifier: span });
|
self.sess.emit_err(FnPointerCannotBeAsync { span: whole_span, qualifier: span });
|
||||||
}
|
}
|
||||||
|
// FIXME(gen_blocks): emit a similar error for `gen fn()`
|
||||||
let decl_span = span_start.to(self.token.span);
|
let decl_span = span_start.to(self.token.span);
|
||||||
Ok(TyKind::BareFn(P(BareFnTy { ext, unsafety, generic_params: params, decl, decl_span })))
|
Ok(TyKind::BareFn(P(BareFnTy { ext, unsafety, generic_params: params, decl, decl_span })))
|
||||||
}
|
}
|
||||||
|
|
|
@ -156,7 +156,10 @@ impl<'a, 'b, 'tcx> visit::Visitor<'a> for DefCollector<'a, 'b, 'tcx> {
|
||||||
|
|
||||||
fn visit_fn(&mut self, fn_kind: FnKind<'a>, span: Span, _: NodeId) {
|
fn visit_fn(&mut self, fn_kind: FnKind<'a>, span: Span, _: NodeId) {
|
||||||
if let FnKind::Fn(_, _, sig, _, generics, body) = fn_kind {
|
if let FnKind::Fn(_, _, sig, _, generics, body) = fn_kind {
|
||||||
if let Async::Yes { closure_id, .. } = sig.header.asyncness {
|
if let Some(
|
||||||
|
CoroutineKind::Async { closure_id, .. } | CoroutineKind::Gen { closure_id, .. },
|
||||||
|
) = sig.header.coro_kind
|
||||||
|
{
|
||||||
self.visit_generics(generics);
|
self.visit_generics(generics);
|
||||||
|
|
||||||
// For async functions, we need to create their inner defs inside of a
|
// For async functions, we need to create their inner defs inside of a
|
||||||
|
@ -281,11 +284,12 @@ impl<'a, 'b, 'tcx> visit::Visitor<'a> for DefCollector<'a, 'b, 'tcx> {
|
||||||
// Async closures desugar to closures inside of closures, so
|
// Async closures desugar to closures inside of closures, so
|
||||||
// we must create two defs.
|
// we must create two defs.
|
||||||
let closure_def = self.create_def(expr.id, kw::Empty, DefKind::Closure, expr.span);
|
let closure_def = self.create_def(expr.id, kw::Empty, DefKind::Closure, expr.span);
|
||||||
match closure.asyncness {
|
match closure.coro_kind {
|
||||||
Async::Yes { closure_id, .. } => {
|
Some(
|
||||||
self.create_def(closure_id, kw::Empty, DefKind::Closure, expr.span)
|
CoroutineKind::Async { closure_id, .. }
|
||||||
}
|
| CoroutineKind::Gen { closure_id, .. },
|
||||||
Async::No => closure_def,
|
) => self.create_def(closure_id, kw::Empty, DefKind::Closure, expr.span),
|
||||||
|
None => closure_def,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ExprKind::Gen(_, _, _) => {
|
ExprKind::Gen(_, _, _) => {
|
||||||
|
|
|
@ -916,8 +916,10 @@ impl<'a: 'ast, 'ast, 'tcx> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast,
|
||||||
&sig.decl.output,
|
&sig.decl.output,
|
||||||
);
|
);
|
||||||
|
|
||||||
if let Some((async_node_id, _)) = sig.header.asyncness.opt_return_id() {
|
if let Some((coro_node_id, _)) =
|
||||||
this.record_lifetime_params_for_impl_trait(async_node_id);
|
sig.header.coro_kind.map(|coro_kind| coro_kind.return_id())
|
||||||
|
{
|
||||||
|
this.record_lifetime_params_for_impl_trait(coro_node_id);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
@ -940,12 +942,13 @@ impl<'a: 'ast, 'ast, 'tcx> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast,
|
||||||
this.visit_generics(generics);
|
this.visit_generics(generics);
|
||||||
|
|
||||||
let declaration = &sig.decl;
|
let declaration = &sig.decl;
|
||||||
let async_node_id = sig.header.asyncness.opt_return_id();
|
let coro_node_id =
|
||||||
|
sig.header.coro_kind.map(|coro_kind| coro_kind.return_id());
|
||||||
|
|
||||||
this.with_lifetime_rib(
|
this.with_lifetime_rib(
|
||||||
LifetimeRibKind::AnonymousCreateParameter {
|
LifetimeRibKind::AnonymousCreateParameter {
|
||||||
binder: fn_id,
|
binder: fn_id,
|
||||||
report_in_path: async_node_id.is_some(),
|
report_in_path: coro_node_id.is_some(),
|
||||||
},
|
},
|
||||||
|this| {
|
|this| {
|
||||||
this.resolve_fn_signature(
|
this.resolve_fn_signature(
|
||||||
|
@ -958,7 +961,7 @@ impl<'a: 'ast, 'ast, 'tcx> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast,
|
||||||
&declaration.output,
|
&declaration.output,
|
||||||
);
|
);
|
||||||
|
|
||||||
if let Some((async_node_id, _)) = async_node_id {
|
if let Some((async_node_id, _)) = coro_node_id {
|
||||||
this.record_lifetime_params_for_impl_trait(async_node_id);
|
this.record_lifetime_params_for_impl_trait(async_node_id);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -4288,8 +4291,10 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> {
|
||||||
// `async |x| ...` gets desugared to `|x| async {...}`, so we need to
|
// `async |x| ...` gets desugared to `|x| async {...}`, so we need to
|
||||||
// resolve the arguments within the proper scopes so that usages of them inside the
|
// resolve the arguments within the proper scopes so that usages of them inside the
|
||||||
// closure are detected as upvars rather than normal closure arg usages.
|
// closure are detected as upvars rather than normal closure arg usages.
|
||||||
|
//
|
||||||
|
// Similarly, `gen |x| ...` gets desugared to `|x| gen {...}`, so we handle that too.
|
||||||
ExprKind::Closure(box ast::Closure {
|
ExprKind::Closure(box ast::Closure {
|
||||||
asyncness: Async::Yes { .. },
|
coro_kind: Some(_),
|
||||||
ref fn_decl,
|
ref fn_decl,
|
||||||
ref body,
|
ref body,
|
||||||
..
|
..
|
||||||
|
|
|
@ -3,7 +3,7 @@ use std::{io, thread};
|
||||||
|
|
||||||
use crate::doc::{NEEDLESS_DOCTEST_MAIN, TEST_ATTR_IN_DOCTEST};
|
use crate::doc::{NEEDLESS_DOCTEST_MAIN, TEST_ATTR_IN_DOCTEST};
|
||||||
use clippy_utils::diagnostics::span_lint;
|
use clippy_utils::diagnostics::span_lint;
|
||||||
use rustc_ast::{Async, Fn, FnRetTy, Item, ItemKind};
|
use rustc_ast::{CoroutineKind, Fn, FnRetTy, Item, ItemKind};
|
||||||
use rustc_data_structures::sync::Lrc;
|
use rustc_data_structures::sync::Lrc;
|
||||||
use rustc_errors::emitter::EmitterWriter;
|
use rustc_errors::emitter::EmitterWriter;
|
||||||
use rustc_errors::Handler;
|
use rustc_errors::Handler;
|
||||||
|
@ -69,7 +69,7 @@ pub fn check(
|
||||||
if !ignore {
|
if !ignore {
|
||||||
get_test_spans(&item, &mut test_attr_spans);
|
get_test_spans(&item, &mut test_attr_spans);
|
||||||
}
|
}
|
||||||
let is_async = matches!(sig.header.asyncness, Async::Yes { .. });
|
let is_async = matches!(sig.header.coro_kind, Some(CoroutineKind::Async { .. }));
|
||||||
let returns_nothing = match &sig.decl.output {
|
let returns_nothing = match &sig.decl.output {
|
||||||
FnRetTy::Default(..) => true,
|
FnRetTy::Default(..) => true,
|
||||||
FnRetTy::Ty(ty) if ty.kind.is_unit() => true,
|
FnRetTy::Ty(ty) if ty.kind.is_unit() => true,
|
||||||
|
|
|
@ -188,7 +188,7 @@ pub fn eq_expr(l: &Expr, r: &Expr) -> bool {
|
||||||
Closure(box ast::Closure {
|
Closure(box ast::Closure {
|
||||||
binder: lb,
|
binder: lb,
|
||||||
capture_clause: lc,
|
capture_clause: lc,
|
||||||
asyncness: la,
|
coro_kind: la,
|
||||||
movability: lm,
|
movability: lm,
|
||||||
fn_decl: lf,
|
fn_decl: lf,
|
||||||
body: le,
|
body: le,
|
||||||
|
@ -197,7 +197,7 @@ pub fn eq_expr(l: &Expr, r: &Expr) -> bool {
|
||||||
Closure(box ast::Closure {
|
Closure(box ast::Closure {
|
||||||
binder: rb,
|
binder: rb,
|
||||||
capture_clause: rc,
|
capture_clause: rc,
|
||||||
asyncness: ra,
|
coro_kind: ra,
|
||||||
movability: rm,
|
movability: rm,
|
||||||
fn_decl: rf,
|
fn_decl: rf,
|
||||||
body: re,
|
body: re,
|
||||||
|
@ -206,7 +206,7 @@ pub fn eq_expr(l: &Expr, r: &Expr) -> bool {
|
||||||
) => {
|
) => {
|
||||||
eq_closure_binder(lb, rb)
|
eq_closure_binder(lb, rb)
|
||||||
&& lc == rc
|
&& lc == rc
|
||||||
&& la.is_async() == ra.is_async()
|
&& la.map_or(false, |la| la.is_async()) == ra.map_or(false, |ra| ra.is_async())
|
||||||
&& lm == rm
|
&& lm == rm
|
||||||
&& eq_fn_decl(lf, rf)
|
&& eq_fn_decl(lf, rf)
|
||||||
&& eq_expr(le, re)
|
&& eq_expr(le, re)
|
||||||
|
@ -563,9 +563,18 @@ pub fn eq_fn_sig(l: &FnSig, r: &FnSig) -> bool {
|
||||||
eq_fn_decl(&l.decl, &r.decl) && eq_fn_header(&l.header, &r.header)
|
eq_fn_decl(&l.decl, &r.decl) && eq_fn_header(&l.header, &r.header)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn eq_opt_coro_kind(l: Option<CoroutineKind>, r: Option<CoroutineKind>) -> bool {
|
||||||
|
match (l, r) {
|
||||||
|
(Some(CoroutineKind::Async { .. }), Some(CoroutineKind::Async { .. }))
|
||||||
|
| (Some(CoroutineKind::Gen { .. }), Some(CoroutineKind::Gen { .. })) => true,
|
||||||
|
(None, None) => true,
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn eq_fn_header(l: &FnHeader, r: &FnHeader) -> bool {
|
pub fn eq_fn_header(l: &FnHeader, r: &FnHeader) -> bool {
|
||||||
matches!(l.unsafety, Unsafe::No) == matches!(r.unsafety, Unsafe::No)
|
matches!(l.unsafety, Unsafe::No) == matches!(r.unsafety, Unsafe::No)
|
||||||
&& l.asyncness.is_async() == r.asyncness.is_async()
|
&& eq_opt_coro_kind(l.coro_kind, r.coro_kind)
|
||||||
&& matches!(l.constness, Const::No) == matches!(r.constness, Const::No)
|
&& matches!(l.constness, Const::No) == matches!(r.constness, Const::No)
|
||||||
&& eq_ext(&l.ext, &r.ext)
|
&& eq_ext(&l.ext, &r.ext)
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,7 +29,7 @@ pub(crate) fn rewrite_closure(
|
||||||
binder: &ast::ClosureBinder,
|
binder: &ast::ClosureBinder,
|
||||||
constness: ast::Const,
|
constness: ast::Const,
|
||||||
capture: ast::CaptureBy,
|
capture: ast::CaptureBy,
|
||||||
is_async: &ast::Async,
|
coro_kind: &Option<ast::CoroutineKind>,
|
||||||
movability: ast::Movability,
|
movability: ast::Movability,
|
||||||
fn_decl: &ast::FnDecl,
|
fn_decl: &ast::FnDecl,
|
||||||
body: &ast::Expr,
|
body: &ast::Expr,
|
||||||
|
@ -40,7 +40,7 @@ pub(crate) fn rewrite_closure(
|
||||||
debug!("rewrite_closure {:?}", body);
|
debug!("rewrite_closure {:?}", body);
|
||||||
|
|
||||||
let (prefix, extra_offset) = rewrite_closure_fn_decl(
|
let (prefix, extra_offset) = rewrite_closure_fn_decl(
|
||||||
binder, constness, capture, is_async, movability, fn_decl, body, span, context, shape,
|
binder, constness, capture, coro_kind, movability, fn_decl, body, span, context, shape,
|
||||||
)?;
|
)?;
|
||||||
// 1 = space between `|...|` and body.
|
// 1 = space between `|...|` and body.
|
||||||
let body_shape = shape.offset_left(extra_offset)?;
|
let body_shape = shape.offset_left(extra_offset)?;
|
||||||
|
@ -233,7 +233,7 @@ fn rewrite_closure_fn_decl(
|
||||||
binder: &ast::ClosureBinder,
|
binder: &ast::ClosureBinder,
|
||||||
constness: ast::Const,
|
constness: ast::Const,
|
||||||
capture: ast::CaptureBy,
|
capture: ast::CaptureBy,
|
||||||
asyncness: &ast::Async,
|
coro_kind: &Option<ast::CoroutineKind>,
|
||||||
movability: ast::Movability,
|
movability: ast::Movability,
|
||||||
fn_decl: &ast::FnDecl,
|
fn_decl: &ast::FnDecl,
|
||||||
body: &ast::Expr,
|
body: &ast::Expr,
|
||||||
|
@ -263,7 +263,11 @@ fn rewrite_closure_fn_decl(
|
||||||
} else {
|
} else {
|
||||||
""
|
""
|
||||||
};
|
};
|
||||||
let is_async = if asyncness.is_async() { "async " } else { "" };
|
let coro = match coro_kind {
|
||||||
|
Some(ast::CoroutineKind::Async { .. }) => "async ",
|
||||||
|
Some(ast::CoroutineKind::Gen { .. }) => "gen ",
|
||||||
|
None => "",
|
||||||
|
};
|
||||||
let mover = if matches!(capture, ast::CaptureBy::Value { .. }) {
|
let mover = if matches!(capture, ast::CaptureBy::Value { .. }) {
|
||||||
"move "
|
"move "
|
||||||
} else {
|
} else {
|
||||||
|
@ -272,7 +276,7 @@ fn rewrite_closure_fn_decl(
|
||||||
// 4 = "|| {".len(), which is overconservative when the closure consists of
|
// 4 = "|| {".len(), which is overconservative when the closure consists of
|
||||||
// a single expression.
|
// a single expression.
|
||||||
let nested_shape = shape
|
let nested_shape = shape
|
||||||
.shrink_left(binder.len() + const_.len() + immovable.len() + is_async.len() + mover.len())?
|
.shrink_left(binder.len() + const_.len() + immovable.len() + coro.len() + mover.len())?
|
||||||
.sub_width(4)?;
|
.sub_width(4)?;
|
||||||
|
|
||||||
// 1 = |
|
// 1 = |
|
||||||
|
@ -310,7 +314,7 @@ fn rewrite_closure_fn_decl(
|
||||||
.tactic(tactic)
|
.tactic(tactic)
|
||||||
.preserve_newline(true);
|
.preserve_newline(true);
|
||||||
let list_str = write_list(&item_vec, &fmt)?;
|
let list_str = write_list(&item_vec, &fmt)?;
|
||||||
let mut prefix = format!("{binder}{const_}{immovable}{is_async}{mover}|{list_str}|");
|
let mut prefix = format!("{binder}{const_}{immovable}{coro}{mover}|{list_str}|");
|
||||||
|
|
||||||
if !ret_str.is_empty() {
|
if !ret_str.is_empty() {
|
||||||
if prefix.contains('\n') {
|
if prefix.contains('\n') {
|
||||||
|
@ -339,7 +343,7 @@ pub(crate) fn rewrite_last_closure(
|
||||||
ref binder,
|
ref binder,
|
||||||
constness,
|
constness,
|
||||||
capture_clause,
|
capture_clause,
|
||||||
ref asyncness,
|
ref coro_kind,
|
||||||
movability,
|
movability,
|
||||||
ref fn_decl,
|
ref fn_decl,
|
||||||
ref body,
|
ref body,
|
||||||
|
@ -360,7 +364,7 @@ pub(crate) fn rewrite_last_closure(
|
||||||
binder,
|
binder,
|
||||||
constness,
|
constness,
|
||||||
capture_clause,
|
capture_clause,
|
||||||
asyncness,
|
coro_kind,
|
||||||
movability,
|
movability,
|
||||||
fn_decl,
|
fn_decl,
|
||||||
body,
|
body,
|
||||||
|
|
|
@ -212,7 +212,7 @@ pub(crate) fn format_expr(
|
||||||
&cl.binder,
|
&cl.binder,
|
||||||
cl.constness,
|
cl.constness,
|
||||||
cl.capture_clause,
|
cl.capture_clause,
|
||||||
&cl.asyncness,
|
&cl.coro_kind,
|
||||||
cl.movability,
|
cl.movability,
|
||||||
&cl.fn_decl,
|
&cl.fn_decl,
|
||||||
&cl.body,
|
&cl.body,
|
||||||
|
|
|
@ -287,7 +287,7 @@ pub(crate) struct FnSig<'a> {
|
||||||
decl: &'a ast::FnDecl,
|
decl: &'a ast::FnDecl,
|
||||||
generics: &'a ast::Generics,
|
generics: &'a ast::Generics,
|
||||||
ext: ast::Extern,
|
ext: ast::Extern,
|
||||||
is_async: Cow<'a, ast::Async>,
|
coro_kind: Cow<'a, Option<ast::CoroutineKind>>,
|
||||||
constness: ast::Const,
|
constness: ast::Const,
|
||||||
defaultness: ast::Defaultness,
|
defaultness: ast::Defaultness,
|
||||||
unsafety: ast::Unsafe,
|
unsafety: ast::Unsafe,
|
||||||
|
@ -302,7 +302,7 @@ impl<'a> FnSig<'a> {
|
||||||
) -> FnSig<'a> {
|
) -> FnSig<'a> {
|
||||||
FnSig {
|
FnSig {
|
||||||
unsafety: method_sig.header.unsafety,
|
unsafety: method_sig.header.unsafety,
|
||||||
is_async: Cow::Borrowed(&method_sig.header.asyncness),
|
coro_kind: Cow::Borrowed(&method_sig.header.coro_kind),
|
||||||
constness: method_sig.header.constness,
|
constness: method_sig.header.constness,
|
||||||
defaultness: ast::Defaultness::Final,
|
defaultness: ast::Defaultness::Final,
|
||||||
ext: method_sig.header.ext,
|
ext: method_sig.header.ext,
|
||||||
|
@ -328,7 +328,7 @@ impl<'a> FnSig<'a> {
|
||||||
generics,
|
generics,
|
||||||
ext: fn_sig.header.ext,
|
ext: fn_sig.header.ext,
|
||||||
constness: fn_sig.header.constness,
|
constness: fn_sig.header.constness,
|
||||||
is_async: Cow::Borrowed(&fn_sig.header.asyncness),
|
coro_kind: Cow::Borrowed(&fn_sig.header.coro_kind),
|
||||||
defaultness,
|
defaultness,
|
||||||
unsafety: fn_sig.header.unsafety,
|
unsafety: fn_sig.header.unsafety,
|
||||||
visibility: vis,
|
visibility: vis,
|
||||||
|
@ -343,7 +343,8 @@ impl<'a> FnSig<'a> {
|
||||||
result.push_str(&*format_visibility(context, self.visibility));
|
result.push_str(&*format_visibility(context, self.visibility));
|
||||||
result.push_str(format_defaultness(self.defaultness));
|
result.push_str(format_defaultness(self.defaultness));
|
||||||
result.push_str(format_constness(self.constness));
|
result.push_str(format_constness(self.constness));
|
||||||
result.push_str(format_async(&self.is_async));
|
self.coro_kind
|
||||||
|
.map(|coro_kind| result.push_str(format_coro(&coro_kind)));
|
||||||
result.push_str(format_unsafety(self.unsafety));
|
result.push_str(format_unsafety(self.unsafety));
|
||||||
result.push_str(&format_extern(
|
result.push_str(&format_extern(
|
||||||
self.ext,
|
self.ext,
|
||||||
|
|
|
@ -75,10 +75,10 @@ pub(crate) fn format_visibility(
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub(crate) fn format_async(is_async: &ast::Async) -> &'static str {
|
pub(crate) fn format_coro(coro_kind: &ast::CoroutineKind) -> &'static str {
|
||||||
match is_async {
|
match coro_kind {
|
||||||
ast::Async::Yes { .. } => "async ",
|
ast::CoroutineKind::Async { .. } => "async ",
|
||||||
ast::Async::No => "",
|
ast::CoroutineKind::Gen { .. } => "gen ",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -132,7 +132,7 @@ fn iter_exprs(depth: usize, f: &mut dyn FnMut(P<Expr>)) {
|
||||||
binder: ClosureBinder::NotPresent,
|
binder: ClosureBinder::NotPresent,
|
||||||
capture_clause: CaptureBy::Value { move_kw: DUMMY_SP },
|
capture_clause: CaptureBy::Value { move_kw: DUMMY_SP },
|
||||||
constness: Const::No,
|
constness: Const::No,
|
||||||
asyncness: Async::No,
|
coro_kind: None,
|
||||||
movability: Movability::Movable,
|
movability: Movability::Movable,
|
||||||
fn_decl: decl.clone(),
|
fn_decl: decl.clone(),
|
||||||
body: e,
|
body: e,
|
||||||
|
|
11
tests/ui/coroutine/async_gen_fn.rs
Normal file
11
tests/ui/coroutine/async_gen_fn.rs
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
// edition: 2024
|
||||||
|
// compile-flags: -Zunstable-options
|
||||||
|
#![feature(gen_blocks)]
|
||||||
|
|
||||||
|
// async generators are not yet supported, so this test makes sure they make some kind of reasonable
|
||||||
|
// error.
|
||||||
|
|
||||||
|
async gen fn foo() {}
|
||||||
|
//~^ `async gen` functions are not supported
|
||||||
|
|
||||||
|
fn main() {}
|
8
tests/ui/coroutine/async_gen_fn.stderr
Normal file
8
tests/ui/coroutine/async_gen_fn.stderr
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
error: `async gen` functions are not supported
|
||||||
|
--> $DIR/async_gen_fn.rs:8:1
|
||||||
|
|
|
||||||
|
LL | async gen fn foo() {}
|
||||||
|
| ^^^^^^^^^
|
||||||
|
|
||||||
|
error: aborting due to 1 previous error
|
||||||
|
|
|
@ -1,10 +1,12 @@
|
||||||
error: `gen` functions are not yet implemented
|
error[E0658]: gen blocks are experimental
|
||||||
--> $DIR/gen_fn.rs:4:1
|
--> $DIR/gen_fn.rs:4:1
|
||||||
|
|
|
|
||||||
LL | gen fn foo() {}
|
LL | gen fn foo() {}
|
||||||
| ^^^
|
| ^^^
|
||||||
|
|
|
|
||||||
= help: for now you can use `gen {}` blocks and return `impl Iterator` instead
|
= 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: aborting due to 1 previous error
|
error: aborting due to 1 previous error
|
||||||
|
|
||||||
|
For more information about this error, try `rustc --explain E0658`.
|
||||||
|
|
|
@ -3,6 +3,6 @@
|
||||||
|
|
||||||
gen fn foo() {}
|
gen fn foo() {}
|
||||||
//[none]~^ ERROR: expected one of `#`, `async`, `const`, `default`, `extern`, `fn`, `pub`, `unsafe`, or `use`, found `gen`
|
//[none]~^ ERROR: expected one of `#`, `async`, `const`, `default`, `extern`, `fn`, `pub`, `unsafe`, or `use`, found `gen`
|
||||||
//[e2024]~^^ ERROR: `gen` functions are not yet implemented
|
//[e2024]~^^ ERROR: gen blocks are experimental
|
||||||
|
|
||||||
fn main() {}
|
fn main() {}
|
||||||
|
|
20
tests/ui/coroutine/gen_fn_iter.rs
Normal file
20
tests/ui/coroutine/gen_fn_iter.rs
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
// edition: 2024
|
||||||
|
// compile-flags: -Zunstable-options
|
||||||
|
// run-pass
|
||||||
|
#![feature(gen_blocks)]
|
||||||
|
|
||||||
|
// make sure that a ridiculously simple gen fn works as an iterator.
|
||||||
|
|
||||||
|
gen fn foo() -> i32 {
|
||||||
|
yield 1;
|
||||||
|
yield 2;
|
||||||
|
yield 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let mut iter = foo();
|
||||||
|
assert_eq!(iter.next(), Some(1));
|
||||||
|
assert_eq!(iter.next(), Some(2));
|
||||||
|
assert_eq!(iter.next(), Some(3));
|
||||||
|
assert_eq!(iter.next(), None);
|
||||||
|
}
|
19
tests/ui/coroutine/gen_fn_lifetime_capture.rs
Normal file
19
tests/ui/coroutine/gen_fn_lifetime_capture.rs
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
// edition: 2024
|
||||||
|
// compile-flags: -Zunstable-options
|
||||||
|
// check-pass
|
||||||
|
#![feature(gen_blocks)]
|
||||||
|
|
||||||
|
// make sure gen fn captures lifetimes in its signature
|
||||||
|
|
||||||
|
gen fn foo<'a, 'b>(x: &'a i32, y: &'b i32, z: &'b i32) -> &'b i32 {
|
||||||
|
yield y;
|
||||||
|
yield z;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let z = 3;
|
||||||
|
let mut iter = foo(&1, &2, &z);
|
||||||
|
assert_eq!(iter.next(), Some(&2));
|
||||||
|
assert_eq!(iter.next(), Some(&3));
|
||||||
|
assert_eq!(iter.next(), None);
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue