diff --git a/compiler/rustc_borrowck/src/type_check/input_output.rs b/compiler/rustc_borrowck/src/type_check/input_output.rs
index ace9c5ae71d..46fd0ce2b39 100644
--- a/compiler/rustc_borrowck/src/type_check/input_output.rs
+++ b/compiler/rustc_borrowck/src/type_check/input_output.rs
@@ -47,8 +47,9 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
user_provided_sig,
);
- // FIXME(async_closures): We must apply the same transformation to our
- // signature here as we do during closure checking.
+ // FIXME(async_closures): It's kind of wacky that we must apply this
+ // transformation here, since we do the same thing in HIR typeck.
+ // Maybe we could just fix up the canonicalized signature during HIR typeck?
if let DefiningTy::CoroutineClosure(_, args) =
self.borrowck_context.universal_regions.defining_ty
{
diff --git a/compiler/rustc_borrowck/src/universal_regions.rs b/compiler/rustc_borrowck/src/universal_regions.rs
index 9c266d123b3..d696e624823 100644
--- a/compiler/rustc_borrowck/src/universal_regions.rs
+++ b/compiler/rustc_borrowck/src/universal_regions.rs
@@ -98,7 +98,10 @@ pub enum DefiningTy<'tcx> {
Coroutine(DefId, GenericArgsRef<'tcx>),
/// The MIR is a special kind of closure that returns coroutines.
- /// TODO: describe how to make the sig...
+ ///
+ /// See the documentation on `CoroutineClosureSignature` for details
+ /// on how to construct the callable signature of the coroutine from
+ /// its args.
CoroutineClosure(DefId, GenericArgsRef<'tcx>),
/// The MIR is a fn item with the given `DefId` and args. The signature
diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs
index d30c7a4fb38..23fb7eba656 100644
--- a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs
+++ b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs
@@ -335,7 +335,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
continue;
}
- // For this check, we do *not* want to treat async coroutine closures (async blocks)
+ // For this check, we do *not* want to treat async coroutine-closures (async blocks)
// as proper closures. Doing so would regress type inference when feeding
// the return value of an argument-position async block to an argument-position
// closure wrapped in a block.
diff --git a/compiler/rustc_middle/src/mir/mod.rs b/compiler/rustc_middle/src/mir/mod.rs
index d88e9261e5a..3d6c28088ad 100644
--- a/compiler/rustc_middle/src/mir/mod.rs
+++ b/compiler/rustc_middle/src/mir/mod.rs
@@ -262,8 +262,16 @@ pub struct CoroutineInfo<'tcx> {
/// Coroutine drop glue. This field is populated after the state transform pass.
pub coroutine_drop: Option
>,
- /// The body of the coroutine, modified to take its upvars by move.
- /// TODO:
+ /// The body of the coroutine, modified to take its upvars by move rather than by ref.
+ ///
+ /// This is used by coroutine-closures, which must return a different flavor of coroutine
+ /// when called using `AsyncFnOnce::call_once`. It is produced by the `ByMoveBody` which
+ /// is run right after building the initial MIR, and will only be populated for coroutines
+ /// which come out of the async closure desugaring.
+ ///
+ /// This body should be processed in lockstep with the containing body -- any optimization
+ /// passes, etc, should be applied to this body as well. This is done automatically if
+ /// using `run_passes`.
pub by_move_body: Option>,
/// The layout of a coroutine. This field is populated after the state transform pass.
diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs
index f9ab32b16f5..938fba0ed09 100644
--- a/compiler/rustc_middle/src/query/mod.rs
+++ b/compiler/rustc_middle/src/query/mod.rs
@@ -756,7 +756,7 @@ rustc_queries! {
}
query coroutine_for_closure(def_id: DefId) -> DefId {
- desc { |_tcx| "TODO" }
+ desc { |_tcx| "Given a coroutine-closure def id, return the def id of the coroutine returned by it" }
separate_provide_extern
}
diff --git a/compiler/rustc_middle/src/traits/select.rs b/compiler/rustc_middle/src/traits/select.rs
index 4e11575cf98..28eba133c76 100644
--- a/compiler/rustc_middle/src/traits/select.rs
+++ b/compiler/rustc_middle/src/traits/select.rs
@@ -138,7 +138,9 @@ pub enum SelectionCandidate<'tcx> {
/// generated for an `async ||` expression.
AsyncClosureCandidate,
- // TODO:
+ /// Implementation of the the `AsyncFnKindHelper` helper trait, which
+ /// is used internally to delay computation for async closures until after
+ /// upvar analysis is performed in HIR typeck.
AsyncFnKindHelperCandidate,
/// Implementation of a `Coroutine` trait by one of the anonymous types
diff --git a/compiler/rustc_middle/src/ty/instance.rs b/compiler/rustc_middle/src/ty/instance.rs
index 44bf3c32b48..2c80dd02145 100644
--- a/compiler/rustc_middle/src/ty/instance.rs
+++ b/compiler/rustc_middle/src/ty/instance.rs
@@ -101,7 +101,10 @@ pub enum InstanceDef<'tcx> {
target_kind: ty::ClosureKind,
},
- /// TODO:
+ /// `<[coroutine] as Future>::poll`, but for coroutines produced when `AsyncFnOnce`
+ /// is called on a coroutine-closure whose closure kind is not `FnOnce`. This
+ /// will select the body that is produced by the `ByMoveBody` transform, and thus
+ /// take and use all of its upvars by-move rather than by-ref.
CoroutineByMoveShim { coroutine_def_id: DefId },
/// Compiler-generated accessor for thread locals which returns a reference to the thread local
diff --git a/compiler/rustc_middle/src/ty/print/pretty.rs b/compiler/rustc_middle/src/ty/print/pretty.rs
index be6b887ba7d..c0bfd2380ad 100644
--- a/compiler/rustc_middle/src/ty/print/pretty.rs
+++ b/compiler/rustc_middle/src/ty/print/pretty.rs
@@ -877,7 +877,7 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write {
ty::CoroutineClosure(did, args) => {
p!(write("{{"));
if !self.should_print_verbose() {
- p!(write("coroutine closure"));
+ p!(write("coroutine-closure"));
// FIXME(eddyb) should use `def_span`.
if let Some(did) = did.as_local() {
if self.tcx().sess.opts.unstable_opts.span_free_formats {
diff --git a/compiler/rustc_middle/src/ty/sty.rs b/compiler/rustc_middle/src/ty/sty.rs
index 8918a3735d6..3bbebaddbdd 100644
--- a/compiler/rustc_middle/src/ty/sty.rs
+++ b/compiler/rustc_middle/src/ty/sty.rs
@@ -276,11 +276,31 @@ pub struct CoroutineClosureArgs<'tcx> {
}
pub struct CoroutineClosureArgsParts<'tcx> {
+ /// This is the args of the typeck root.
pub parent_args: &'tcx [GenericArg<'tcx>],
+ /// Represents the maximum calling capability of the closure.
pub closure_kind_ty: Ty<'tcx>,
+ /// Represents all of the relevant parts of the coroutine returned by this
+ /// coroutine-closure. This signature parts type will have the general
+ /// shape of `fn(tupled_inputs, resume_ty) -> (return_ty, yield_ty)`, where
+ /// `resume_ty`, `return_ty`, and `yield_ty` are the respective types for the
+ /// coroutine returned by the coroutine-closure.
+ ///
+ /// Use `coroutine_closure_sig` to break up this type rather than using it
+ /// yourself.
pub signature_parts_ty: Ty<'tcx>,
+ /// The upvars captured by the closure. Remains an inference variable
+ /// until the upvar analysis, which happens late in HIR typeck.
pub tupled_upvars_ty: Ty<'tcx>,
+ /// a function pointer that has the shape `for<'env> fn() -> (&'env T, ...)`.
+ /// This allows us to represent the binder of the self-captures of the closure.
+ ///
+ /// For example, if the coroutine returned by the closure borrows `String`
+ /// from the closure's upvars, this will be `for<'env> fn() -> (&'env String,)`,
+ /// while the `tupled_upvars_ty`, representing the by-move version of the same
+ /// captures, will be `(String,)`.
pub coroutine_captures_by_ref_ty: Ty<'tcx>,
+ /// Witness type returned by the generator produced by this coroutine-closure.
pub coroutine_witness_ty: Ty<'tcx>,
}
@@ -496,15 +516,27 @@ pub struct CoroutineArgs<'tcx> {
pub struct CoroutineArgsParts<'tcx> {
/// This is the args of the typeck root.
pub parent_args: &'tcx [GenericArg<'tcx>],
- // TODO: why
+
+ /// The coroutines returned by a coroutine-closure's `AsyncFnOnce`/`AsyncFnMut`
+ /// implementations must be distinguished since the former takes the closure's
+ /// upvars by move, and the latter takes the closure's upvars by ref.
+ ///
+ /// This field distinguishes these fields so that codegen can select the right
+ /// body for the coroutine. This has the same type representation as the closure
+ /// kind: `i8`/`i16`/`i32`.
+ ///
+ /// For regular coroutines, this field will always just be `()`.
pub kind_ty: Ty<'tcx>,
+
pub resume_ty: Ty<'tcx>,
pub yield_ty: Ty<'tcx>,
pub return_ty: Ty<'tcx>,
+
/// The interior type of the coroutine.
/// Represents all types that are stored in locals
/// in the coroutine's body.
pub witness: Ty<'tcx>,
+
/// The upvars captured by the closure. Remains an inference variable
/// until the upvar analysis, which happens late in HIR typeck.
pub tupled_upvars_ty: Ty<'tcx>,
@@ -556,7 +588,7 @@ impl<'tcx> CoroutineArgs<'tcx> {
self.split().parent_args
}
- // TODO:
+ // Returns the kind of the coroutine. See docs on the `kind_ty` field.
pub fn kind_ty(self) -> Ty<'tcx> {
self.split().kind_ty
}
diff --git a/compiler/rustc_smir/src/rustc_smir/convert/mir.rs b/compiler/rustc_smir/src/rustc_smir/convert/mir.rs
index 65ff85a9669..41a4edfc03b 100644
--- a/compiler/rustc_smir/src/rustc_smir/convert/mir.rs
+++ b/compiler/rustc_smir/src/rustc_smir/convert/mir.rs
@@ -539,7 +539,7 @@ impl<'tcx> Stable<'tcx> for mir::AggregateKind<'tcx> {
)
}
mir::AggregateKind::CoroutineClosure(..) => {
- todo!("FIXME(async_closure): Lower these to SMIR")
+ todo!("FIXME(async_closures): Lower these to SMIR")
}
}
}
diff --git a/compiler/rustc_smir/src/rustc_smir/convert/ty.rs b/compiler/rustc_smir/src/rustc_smir/convert/ty.rs
index 3c1858e920b..066348dcb67 100644
--- a/compiler/rustc_smir/src/rustc_smir/convert/ty.rs
+++ b/compiler/rustc_smir/src/rustc_smir/convert/ty.rs
@@ -383,7 +383,7 @@ impl<'tcx> Stable<'tcx> for ty::TyKind<'tcx> {
tables.closure_def(*def_id),
generic_args.stable(tables),
)),
- ty::CoroutineClosure(..) => todo!("/* TODO */"),
+ ty::CoroutineClosure(..) => todo!("FIXME(async_closures): Lower these to SMIR"),
ty::Coroutine(def_id, generic_args) => TyKind::RigidTy(RigidTy::Coroutine(
tables.coroutine_def(*def_id),
generic_args.stable(tables),
diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs
index 587eb1c7cc5..4203cb29db6 100644
--- a/compiler/rustc_span/src/symbol.rs
+++ b/compiler/rustc_span/src/symbol.rs
@@ -321,6 +321,7 @@ symbols! {
TyCtxt,
TyKind,
Unknown,
+ Upvars,
Vec,
VecDeque,
Wrapper,
diff --git a/compiler/rustc_trait_selection/src/solve/assembly/mod.rs b/compiler/rustc_trait_selection/src/solve/assembly/mod.rs
index 8451fbcc434..7052fd776b0 100644
--- a/compiler/rustc_trait_selection/src/solve/assembly/mod.rs
+++ b/compiler/rustc_trait_selection/src/solve/assembly/mod.rs
@@ -190,7 +190,9 @@ pub(super) trait GoalKind<'tcx>:
kind: ty::ClosureKind,
) -> QueryResult<'tcx>;
- /// TODO:
+ /// Compute the built-in logic of the `AsyncFnKindHelper` helper trait, which
+ /// is used internally to delay computation for async closures until after
+ /// upvar analysis is performed in HIR typeck.
fn consider_builtin_async_fn_kind_helper_candidate(
ecx: &mut EvalCtxt<'_, 'tcx>,
goal: Goal<'tcx, Self>,
diff --git a/compiler/rustc_trait_selection/src/solve/assembly/structural_traits.rs b/compiler/rustc_trait_selection/src/solve/assembly/structural_traits.rs
index 0699026117d..dbf3e4876a9 100644
--- a/compiler/rustc_trait_selection/src/solve/assembly/structural_traits.rs
+++ b/compiler/rustc_trait_selection/src/solve/assembly/structural_traits.rs
@@ -8,6 +8,7 @@ use rustc_middle::traits::solve::Goal;
use rustc_middle::ty::{
self, ToPredicate, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeSuperFoldable, TypeVisitableExt,
};
+use rustc_span::sym;
use crate::solve::EvalCtxt;
@@ -274,7 +275,7 @@ pub(in crate::solve) fn extract_tupled_inputs_and_output_from_callable<'tcx>(
Ok(Some(closure_args.sig().map_bound(|sig| (sig.inputs()[0], sig.output()))))
}
- // Coroutine closures don't implement `Fn` traits the normal way.
+ // Coroutine-closures don't implement `Fn` traits the normal way.
ty::CoroutineClosure(..) => Err(NoSolution),
ty::Bool
@@ -341,11 +342,11 @@ pub(in crate::solve) fn extract_tupled_inputs_and_output_from_async_callable<'tc
vec![],
))
} else {
- let helper_trait_def_id = tcx.require_lang_item(LangItem::AsyncFnKindHelper, None);
- // FIXME(async_closures): Make this into a lang item.
+ let async_fn_kind_trait_def_id =
+ tcx.require_lang_item(LangItem::AsyncFnKindHelper, None);
let upvars_projection_def_id = tcx
- .associated_items(helper_trait_def_id)
- .in_definition_order()
+ .associated_items(async_fn_kind_trait_def_id)
+ .filter_by_name_unhygienic(sym::Upvars)
.next()
.unwrap()
.def_id;
@@ -375,7 +376,7 @@ pub(in crate::solve) fn extract_tupled_inputs_and_output_from_async_callable<'tc
vec![
ty::TraitRef::new(
tcx,
- helper_trait_def_id,
+ async_fn_kind_trait_def_id,
[kind_ty, Ty::from_closure_kind(tcx, goal_kind)],
)
.to_predicate(tcx),
diff --git a/compiler/rustc_trait_selection/src/traits/project.rs b/compiler/rustc_trait_selection/src/traits/project.rs
index db1e89ae72f..a8d6b9812be 100644
--- a/compiler/rustc_trait_selection/src/traits/project.rs
+++ b/compiler/rustc_trait_selection/src/traits/project.rs
@@ -2461,12 +2461,13 @@ fn confirm_async_closure_candidate<'cx, 'tcx>(
let goal_kind =
tcx.async_fn_trait_kind_from_def_id(obligation.predicate.trait_def_id(tcx)).unwrap();
- let helper_trait_def_id = tcx.require_lang_item(LangItem::AsyncFnKindHelper, None);
+ let async_fn_kind_helper_trait_def_id =
+ tcx.require_lang_item(LangItem::AsyncFnKindHelper, None);
nested.push(obligation.with(
tcx,
ty::TraitRef::new(
tcx,
- helper_trait_def_id,
+ async_fn_kind_helper_trait_def_id,
[kind_ty, Ty::from_closure_kind(tcx, goal_kind)],
),
));
@@ -2476,9 +2477,12 @@ fn confirm_async_closure_candidate<'cx, 'tcx>(
ty::ClosureKind::FnOnce => tcx.lifetimes.re_static,
};
- // FIXME(async_closures): Make this into a lang item.
- let upvars_projection_def_id =
- tcx.associated_items(helper_trait_def_id).in_definition_order().next().unwrap().def_id;
+ let upvars_projection_def_id = tcx
+ .associated_items(async_fn_kind_helper_trait_def_id)
+ .filter_by_name_unhygienic(sym::Upvars)
+ .next()
+ .unwrap()
+ .def_id;
// FIXME(async_closures): Confirmation is kind of a mess here. Ideally,
// we'd short-circuit when we know that the goal_kind >= closure_kind, and not
diff --git a/compiler/rustc_type_ir/src/ty_kind.rs b/compiler/rustc_type_ir/src/ty_kind.rs
index 5941fce8825..a4fe572067b 100644
--- a/compiler/rustc_type_ir/src/ty_kind.rs
+++ b/compiler/rustc_type_ir/src/ty_kind.rs
@@ -202,7 +202,11 @@ pub enum TyKind {
/// `ClosureArgs` for more details.
Closure(I::DefId, I::GenericArgs),
- /// TODO
+ /// The anonymous type of a closure. Used to represent the type of `async |a| a`.
+ ///
+ /// Coroutine-closure args contain both the - potentially substituted - generic
+ /// parameters of its parent and some synthetic parameters. See the documentation
+ /// for `CoroutineClosureArgs` for more details.
CoroutineClosure(I::DefId, I::GenericArgs),
/// The anonymous type of a coroutine. Used to represent the type of
diff --git a/library/core/src/ops/async_function.rs b/library/core/src/ops/async_function.rs
index b11d5643990..efbe9d164c3 100644
--- a/library/core/src/ops/async_function.rs
+++ b/library/core/src/ops/async_function.rs
@@ -108,9 +108,26 @@ mod impls {
}
mod internal_implementation_detail {
- // TODO: needs a detailed explanation
+ /// A helper trait that is used to enforce that the `ClosureKind` of a goal
+ /// is within the capabilities of a `CoroutineClosure`, and which allows us
+ /// to delay the projection of the tupled upvar types until after upvar
+ /// analysis is complete.
+ ///
+ /// The `Self` type is expected to be the `kind_ty` of the coroutine-closure,
+ /// and thus either `?0` or `i8`/`i16`/`i32` (see docs for `ClosureKind`
+ /// for an explanation of that). The `GoalKind` is also the same type, but
+ /// representing the kind of the trait that the closure is being called with.
#[cfg_attr(not(bootstrap), lang = "async_fn_kind_helper")]
trait AsyncFnKindHelper {
- type Assoc<'closure_env, Inputs, Upvars, BorrowedUpvarsAsFnPtr>;
+ // Projects a set of closure inputs (arguments), a region, and a set of upvars
+ // (by move and by ref) to the upvars that we expect the coroutine to have
+ // according to the `GoalKind` parameter above.
+ //
+ // The `Upvars` parameter should be the upvars of the parent coroutine-closure,
+ // and the `BorrowedUpvarsAsFnPtr` will be a function pointer that has the shape
+ // `for<'env> fn() -> (&'env T, ...)`. This allows us to represent the binder
+ // of the closure's self-capture, and these upvar types will be instantiated with
+ // the `'closure_env` region provided to the associated type.
+ type Upvars<'closure_env, Inputs, Upvars, BorrowedUpvarsAsFnPtr>;
}
}
diff --git a/tests/ui/async-await/async-borrowck-escaping-closure-error.rs b/tests/ui/async-await/async-borrowck-escaping-closure-error.rs
index c02bac2d7dd..2a3e382e118 100644
--- a/tests/ui/async-await/async-borrowck-escaping-closure-error.rs
+++ b/tests/ui/async-await/async-borrowck-escaping-closure-error.rs
@@ -4,7 +4,8 @@
fn foo() -> Box> {
let x = 0u32;
Box::new((async || x)())
- //~^ ERROR closure may outlive the current function, but it borrows `x`, which is owned by the current function
+ //~^ ERROR cannot return value referencing local variable `x`
+ //~| ERROR cannot return value referencing temporary value
}
fn main() {
diff --git a/tests/ui/async-await/async-borrowck-escaping-closure-error.stderr b/tests/ui/async-await/async-borrowck-escaping-closure-error.stderr
index 87851e1ae5b..be67c78221a 100644
--- a/tests/ui/async-await/async-borrowck-escaping-closure-error.stderr
+++ b/tests/ui/async-await/async-borrowck-escaping-closure-error.stderr
@@ -1,21 +1,21 @@
-error[E0373]: closure may outlive the current function, but it borrows `x`, which is owned by the current function
- --> $DIR/async-borrowck-escaping-closure-error.rs:6:15
- |
-LL | Box::new((async || x)())
- | ^^^^^^^^ - `x` is borrowed here
- | |
- | may outlive borrowed value `x`
- |
-note: closure is returned here
+error[E0515]: cannot return value referencing local variable `x`
--> $DIR/async-borrowck-escaping-closure-error.rs:6:5
|
LL | Box::new((async || x)())
- | ^^^^^^^^^^^^^^^^^^^^^^^^
-help: to force the closure to take ownership of `x` (and any other referenced variables), use the `move` keyword
+ | ^^^^^^^^^------------^^^
+ | | |
+ | | `x` is borrowed here
+ | returns a value referencing data owned by the current function
+
+error[E0515]: cannot return value referencing temporary value
+ --> $DIR/async-borrowck-escaping-closure-error.rs:6:5
|
-LL | Box::new((async move || x)())
- | ++++
+LL | Box::new((async || x)())
+ | ^^^^^^^^^------------^^^
+ | | |
+ | | temporary value created here
+ | returns a value referencing data owned by the current function
-error: aborting due to 1 previous error
+error: aborting due to 2 previous errors
-For more information about this error, try `rustc --explain E0373`.
+For more information about this error, try `rustc --explain E0515`.
diff --git a/tests/ui/async-await/async-closures/higher-ranked.rs b/tests/ui/async-await/async-closures/higher-ranked.rs
index f0bdcf691ae..5bbcc7041a8 100644
--- a/tests/ui/async-await/async-closures/higher-ranked.rs
+++ b/tests/ui/async-await/async-closures/higher-ranked.rs
@@ -1,12 +1,10 @@
// edition:2021
+// check-pass
#![feature(async_closure)]
fn main() {
let x = async move |x: &str| {
- //~^ ERROR lifetime may not live long enough
- // This error is proof that the `&str` type is higher-ranked.
- // This won't work until async closures are fully impl'd.
println!("{x}");
};
}
diff --git a/tests/ui/async-await/async-closures/higher-ranked.stderr b/tests/ui/async-await/async-closures/higher-ranked.stderr
deleted file mode 100644
index fb02a15b079..00000000000
--- a/tests/ui/async-await/async-closures/higher-ranked.stderr
+++ /dev/null
@@ -1,17 +0,0 @@
-error: lifetime may not live long enough
- --> $DIR/higher-ranked.rs:6:34
- |
-LL | let x = async move |x: &str| {
- | ____________________________-___-_^
- | | | |
- | | | return type of closure `{async closure body@$DIR/higher-ranked.rs:6:34: 11:6}` contains a lifetime `'2`
- | | let's call the lifetime of this reference `'1`
-LL | |
-LL | | // This error is proof that the `&str` type is higher-ranked.
-LL | | // This won't work until async closures are fully impl'd.
-LL | | println!("{x}");
-LL | | };
- | |_____^ returning this value requires that `'1` must outlive `'2`
-
-error: aborting due to 1 previous error
-
diff --git a/tests/ui/async-await/issue-74072-lifetime-name-annotations.rs b/tests/ui/async-await/issue-74072-lifetime-name-annotations.rs
index 2d453e7891e..904d28fb0a7 100644
--- a/tests/ui/async-await/issue-74072-lifetime-name-annotations.rs
+++ b/tests/ui/async-await/issue-74072-lifetime-name-annotations.rs
@@ -12,7 +12,8 @@ pub async fn async_fn(x: &mut i32) -> &i32 {
pub fn async_closure(x: &mut i32) -> impl Future