Auto merge of #120361 - compiler-errors:async-closures, r=oli-obk

Rework support for async closures; allow them to return futures that borrow from the closure's captures

This PR implements a new lowering for async closures via `TyKind::CoroutineClosure` which handles the curious relationship between the closure and the coroutine that it returns.

I wrote up a bunch in [this hackmd](https://hackmd.io/`@compiler-errors/S1HvqQxca)` which will be copied to the dev guide after this PR lands, and hopefully left sufficient comments in the source code explaining why this change is as large as it is.

This also necessitates that they begin implementing the `AsyncFn`-family of traits, rather than the `Fn`-family of traits -- if you need `Fn` implementations, you should probably use the non-sugar `|| async {}` syntax instead.

Notably this PR does not yet implement `async Fn()` syntax sugar for bounds, but I expect to add those soon (**edit:** #120392). For now, users must use `AsyncFn()` traits directly, which necessitates adding the `async_fn_traits` feature gate as well. I will add this as a follow-up very soon.

r? oli-obk

This is based on top of #120322, but that PR is minimal.
This commit is contained in:
bors 2024-02-06 15:04:01 +00:00
commit 4a2fe4491e
180 changed files with 3644 additions and 367 deletions

View file

@ -123,9 +123,6 @@ ast_lowering_never_pattern_with_guard =
a guard on a never pattern will never be run
.suggestion = remove this guard
ast_lowering_not_supported_for_lifetime_binder_async_closure =
`for<...>` binders on `async` closures are not currently supported
ast_lowering_previously_used_here = previously used here
ast_lowering_register1 = register `{$reg1_name}`

View file

@ -326,13 +326,6 @@ pub struct MisplacedRelaxTraitBound {
pub span: Span,
}
#[derive(Diagnostic, Clone, Copy)]
#[diag(ast_lowering_not_supported_for_lifetime_binder_async_closure)]
pub struct NotSupportedForLifetimeBinderAsyncClosure {
#[primary_span]
pub span: Span,
}
#[derive(Diagnostic)]
#[diag(ast_lowering_match_arm_with_no_body)]
pub struct MatchArmWithNoBody {

View file

@ -1,9 +1,10 @@
use std::assert_matches::assert_matches;
use super::errors::{
AsyncCoroutinesNotSupported, AwaitOnlyInAsyncFnAndBlocks, BaseExpressionDoubleDot,
ClosureCannotBeStatic, CoroutineTooManyParameters,
FunctionalRecordUpdateDestructuringAssignment, InclusiveRangeWithNoEnd, MatchArmWithNoBody,
NeverPatternWithBody, NeverPatternWithGuard, NotSupportedForLifetimeBinderAsyncClosure,
UnderscoreExprLhsAssign,
NeverPatternWithBody, NeverPatternWithGuard, UnderscoreExprLhsAssign,
};
use super::ResolverAstLoweringExt;
use super::{ImplTraitContext, LoweringContext, ParamMode, ParenthesizedGenericArgs};
@ -1028,30 +1029,27 @@ fn lower_expr_coroutine_closure(
fn_decl_span: Span,
fn_arg_span: Span,
) -> hir::ExprKind<'hir> {
if let &ClosureBinder::For { span, .. } = binder {
self.dcx().emit_err(NotSupportedForLifetimeBinderAsyncClosure { span });
}
let (binder_clause, generic_params) = self.lower_closure_binder(binder);
assert_matches!(
coroutine_kind,
CoroutineKind::Async { .. },
"only async closures are supported currently"
);
let body = self.with_new_scopes(fn_decl_span, |this| {
let inner_decl =
FnDecl { inputs: decl.inputs.clone(), output: FnRetTy::Default(fn_decl_span) };
// Transform `async |x: u8| -> X { ... }` into
// `|x: u8| || -> X { ... }`.
let body_id = this.lower_body(|this| {
let async_ret_ty = if let FnRetTy::Ty(ty) = &decl.output {
let itctx = ImplTraitContext::Disallowed(ImplTraitPosition::AsyncBlock);
Some(hir::FnRetTy::Return(this.lower_ty(ty, &itctx)))
} else {
None
};
let (parameters, expr) = this.lower_coroutine_body_with_moved_arguments(
decl,
&inner_decl,
|this| this.with_new_scopes(fn_decl_span, |this| this.lower_expr_mut(body)),
body.span,
coroutine_kind,
hir::CoroutineSource::Closure,
async_ret_ty,
);
let hir_id = this.lower_node_id(coroutine_kind.closure_id());
@ -1062,15 +1060,12 @@ fn lower_expr_coroutine_closure(
body_id
});
let outer_decl =
FnDecl { inputs: decl.inputs.clone(), output: FnRetTy::Default(fn_decl_span) };
let bound_generic_params = self.lower_lifetime_binder(closure_id, generic_params);
// We need to lower the declaration outside the new scope, because we
// have to conserve the state of being inside a loop condition for the
// closure argument types.
let fn_decl =
self.lower_fn_decl(&outer_decl, closure_id, fn_decl_span, FnDeclKind::Closure, None);
self.lower_fn_decl(&decl, closure_id, fn_decl_span, FnDeclKind::Closure, None);
let c = self.arena.alloc(hir::Closure {
def_id: self.local_def_id(closure_id),
@ -1081,7 +1076,10 @@ fn lower_expr_coroutine_closure(
body,
fn_decl_span: self.lower_span(fn_decl_span),
fn_arg_span: Some(self.lower_span(fn_arg_span)),
kind: hir::ClosureKind::Closure,
// Lower this as a `CoroutineClosure`. That will ensure that HIR typeck
// knows that a `FnDecl` output type like `-> &str` actually means
// "coroutine that returns &str", rather than directly returning a `&str`.
kind: hir::ClosureKind::CoroutineClosure(hir::CoroutineDesugaring::Async),
constness: hir::Constness::NotConst,
});
hir::ExprKind::Closure(c)

View file

@ -1091,7 +1091,6 @@ fn lower_maybe_coroutine_body(
body.span,
coroutine_kind,
hir::CoroutineSource::Fn,
None,
);
// FIXME(async_fn_track_caller): Can this be moved above?
@ -1113,7 +1112,6 @@ pub fn lower_coroutine_body_with_moved_arguments(
body_span: Span,
coroutine_kind: CoroutineKind,
coroutine_source: hir::CoroutineSource,
return_type_hint: Option<hir::FnRetTy<'hir>>,
) -> (&'hir [hir::Param<'hir>], hir::Expr<'hir>) {
let mut parameters: Vec<hir::Param<'_>> = Vec::new();
let mut statements: Vec<hir::Stmt<'_>> = Vec::new();
@ -1283,12 +1281,13 @@ pub fn lower_coroutine_body_with_moved_arguments(
};
let closure_id = coroutine_kind.closure_id();
let coroutine_expr = self.make_desugared_coroutine_expr(
// FIXME(async_closures): This should only move locals,
// and not upvars. Capturing closure upvars by ref doesn't
// work right now anyways, so whatever.
CaptureBy::Value { move_kw: rustc_span::DUMMY_SP },
// The default capture mode here is by-ref. Later on during upvar analysis,
// we will force the captured arguments to by-move, but for async closures,
// we want to make sure that we avoid unnecessarily moving captures, or else
// all async closures would default to `FnOnce` as their calling mode.
CaptureBy::Ref,
closure_id,
return_type_hint,
None,
body_span,
desugaring_kind,
coroutine_source,

View file

@ -33,6 +33,7 @@
#![allow(internal_features)]
#![feature(rustdoc_internals)]
#![doc(rust_logo)]
#![feature(assert_matches)]
#![feature(box_patterns)]
#![feature(let_chains)]
#![deny(rustc::untranslatable_diagnostic)]
@ -298,7 +299,6 @@ enum ImplTraitPosition {
Path,
Variable,
Trait,
AsyncBlock,
Bound,
Generic,
ExternFnParam,
@ -325,7 +325,6 @@ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
ImplTraitPosition::Path => "paths",
ImplTraitPosition::Variable => "the type of variable bindings",
ImplTraitPosition::Trait => "traits",
ImplTraitPosition::AsyncBlock => "async blocks",
ImplTraitPosition::Bound => "bounds",
ImplTraitPosition::Generic => "generics",
ImplTraitPosition::ExternFnParam => "`extern fn` parameters",

View file

@ -858,7 +858,9 @@ pub(crate) fn report_move_out_while_borrowed(
use crate::session_diagnostics::CaptureVarCause::*;
match kind {
hir::ClosureKind::Coroutine(_) => MoveUseInCoroutine { var_span },
hir::ClosureKind::Closure => MoveUseInClosure { var_span },
hir::ClosureKind::Closure | hir::ClosureKind::CoroutineClosure(_) => {
MoveUseInClosure { var_span }
}
}
});
@ -905,7 +907,7 @@ pub(crate) fn report_use_while_mutably_borrowed(
hir::ClosureKind::Coroutine(_) => {
BorrowUsePlaceCoroutine { place: desc_place, var_span, is_single_var: true }
}
hir::ClosureKind::Closure => {
hir::ClosureKind::Closure | hir::ClosureKind::CoroutineClosure(_) => {
BorrowUsePlaceClosure { place: desc_place, var_span, is_single_var: true }
}
}
@ -1056,7 +1058,8 @@ pub(crate) fn report_conflicting_borrow(
var_span,
is_single_var: true,
},
hir::ClosureKind::Closure => BorrowUsePlaceClosure {
hir::ClosureKind::Closure
| hir::ClosureKind::CoroutineClosure(_) => BorrowUsePlaceClosure {
place: desc_place,
var_span,
is_single_var: true,
@ -1140,7 +1143,7 @@ pub(crate) fn report_conflicting_borrow(
var_span,
is_single_var: false,
},
hir::ClosureKind::Closure => {
hir::ClosureKind::Closure | hir::ClosureKind::CoroutineClosure(_) => {
BorrowUsePlaceClosure { place: desc_place, var_span, is_single_var: false }
}
}
@ -1158,7 +1161,7 @@ pub(crate) fn report_conflicting_borrow(
hir::ClosureKind::Coroutine(_) => {
FirstBorrowUsePlaceCoroutine { place: borrow_place_desc, var_span }
}
hir::ClosureKind::Closure => {
hir::ClosureKind::Closure | hir::ClosureKind::CoroutineClosure(_) => {
FirstBorrowUsePlaceClosure { place: borrow_place_desc, var_span }
}
}
@ -1175,7 +1178,7 @@ pub(crate) fn report_conflicting_borrow(
hir::ClosureKind::Coroutine(_) => {
SecondBorrowUsePlaceCoroutine { place: desc_place, var_span }
}
hir::ClosureKind::Closure => {
hir::ClosureKind::Closure | hir::ClosureKind::CoroutineClosure(_) => {
SecondBorrowUsePlaceClosure { place: desc_place, var_span }
}
}
@ -2942,7 +2945,9 @@ pub(crate) fn report_illegal_mutation_of_borrowed(
use crate::session_diagnostics::CaptureVarCause::*;
match kind {
hir::ClosureKind::Coroutine(_) => BorrowUseInCoroutine { var_span },
hir::ClosureKind::Closure => BorrowUseInClosure { var_span },
hir::ClosureKind::Closure | hir::ClosureKind::CoroutineClosure(_) => {
BorrowUseInClosure { var_span }
}
}
});
@ -2958,7 +2963,9 @@ pub(crate) fn report_illegal_mutation_of_borrowed(
use crate::session_diagnostics::CaptureVarCause::*;
match kind {
hir::ClosureKind::Coroutine(_) => BorrowUseInCoroutine { var_span },
hir::ClosureKind::Closure => BorrowUseInClosure { var_span },
hir::ClosureKind::Closure | hir::ClosureKind::CoroutineClosure(_) => {
BorrowUseInClosure { var_span }
}
}
});

View file

@ -614,7 +614,7 @@ pub(super) fn var_path_only_subdiag(
PartialAssignment => AssignPartInCoroutine { path_span },
});
}
hir::ClosureKind::Closure => {
hir::ClosureKind::Closure | hir::ClosureKind::CoroutineClosure(_) => {
err.subdiagnostic(match action {
Borrow => BorrowInClosure { path_span },
MatchOn | Use => UseInClosure { path_span },
@ -1253,7 +1253,7 @@ fn explain_captures(
hir::ClosureKind::Coroutine(_) => {
CaptureVarCause::PartialMoveUseInCoroutine { var_span, is_partial }
}
hir::ClosureKind::Closure => {
hir::ClosureKind::Closure | hir::ClosureKind::CoroutineClosure(_) => {
CaptureVarCause::PartialMoveUseInClosure { var_span, is_partial }
}
})

View file

@ -1472,7 +1472,7 @@ fn suggest_ampmut<'tcx>(
}
fn is_closure_or_coroutine(ty: Ty<'_>) -> bool {
ty.is_closure() || ty.is_coroutine()
ty.is_closure() || ty.is_coroutine() || ty.is_coroutine_closure()
}
/// Given a field that needs to be mutable, returns a span where the " mut " could go.

View file

@ -324,9 +324,13 @@ fn give_name_from_error_region(&self, fr: RegionVid) -> Option<RegionName> {
ty::BoundRegionKind::BrEnv => {
let def_ty = self.regioncx.universal_regions().defining_ty;
let DefiningTy::Closure(_, args) = def_ty else {
// Can't have BrEnv in functions, constants or coroutines.
bug!("BrEnv outside of closure.");
let closure_kind = match def_ty {
DefiningTy::Closure(_, args) => args.as_closure().kind(),
DefiningTy::CoroutineClosure(_, args) => args.as_coroutine_closure().kind(),
_ => {
// Can't have BrEnv in functions, constants or coroutines.
bug!("BrEnv outside of closure.");
}
};
let hir::ExprKind::Closure(&hir::Closure { fn_decl_span, .. }) =
tcx.hir().expect_expr(self.mir_hir_id()).kind
@ -334,21 +338,18 @@ fn give_name_from_error_region(&self, fr: RegionVid) -> Option<RegionName> {
bug!("Closure is not defined by a closure expr");
};
let region_name = self.synthesize_region_name();
let closure_kind_ty = args.as_closure().kind_ty();
let note = match closure_kind_ty.to_opt_closure_kind() {
Some(ty::ClosureKind::Fn) => {
let note = match closure_kind {
ty::ClosureKind::Fn => {
"closure implements `Fn`, so references to captured variables \
can't escape the closure"
}
Some(ty::ClosureKind::FnMut) => {
ty::ClosureKind::FnMut => {
"closure implements `FnMut`, so references to captured variables \
can't escape the closure"
}
Some(ty::ClosureKind::FnOnce) => {
ty::ClosureKind::FnOnce => {
bug!("BrEnv in a `FnOnce` closure");
}
None => bug!("Closure kind not inferred in borrow check"),
};
Some(RegionName {
@ -692,7 +693,10 @@ fn give_name_if_anonymous_region_appears_in_output(&self, fr: RegionVid) -> Opti
hir::ClosureKind::Coroutine(hir::CoroutineKind::Desugared(
hir::CoroutineDesugaring::Async,
hir::CoroutineSource::Closure,
)) => " of async closure",
))
| hir::ClosureKind::CoroutineClosure(hir::CoroutineDesugaring::Async) => {
" of async closure"
}
hir::ClosureKind::Coroutine(hir::CoroutineKind::Desugared(
hir::CoroutineDesugaring::Async,
@ -719,7 +723,10 @@ fn give_name_if_anonymous_region_appears_in_output(&self, fr: RegionVid) -> Opti
hir::ClosureKind::Coroutine(hir::CoroutineKind::Desugared(
hir::CoroutineDesugaring::Gen,
hir::CoroutineSource::Closure,
)) => " of gen closure",
))
| hir::ClosureKind::CoroutineClosure(hir::CoroutineDesugaring::Gen) => {
" of gen closure"
}
hir::ClosureKind::Coroutine(hir::CoroutineKind::Desugared(
hir::CoroutineDesugaring::Gen,
@ -743,7 +750,10 @@ fn give_name_if_anonymous_region_appears_in_output(&self, fr: RegionVid) -> Opti
hir::ClosureKind::Coroutine(hir::CoroutineKind::Desugared(
hir::CoroutineDesugaring::AsyncGen,
hir::CoroutineSource::Closure,
)) => " of async gen closure",
))
| hir::ClosureKind::CoroutineClosure(hir::CoroutineDesugaring::AsyncGen) => {
" of async gen closure"
}
hir::ClosureKind::Coroutine(hir::CoroutineKind::Desugared(
hir::CoroutineDesugaring::AsyncGen,

View file

@ -3,6 +3,7 @@
#![allow(internal_features)]
#![feature(rustdoc_internals)]
#![doc(rust_logo)]
#![feature(assert_matches)]
#![feature(associated_type_bounds)]
#![feature(box_patterns)]
#![feature(let_chains)]
@ -1303,7 +1304,9 @@ fn consume_rvalue(
// moved into the closure and subsequently used by the closure,
// in order to populate our used_mut set.
match **aggregate_kind {
AggregateKind::Closure(def_id, _) | AggregateKind::Coroutine(def_id, _) => {
AggregateKind::Closure(def_id, _)
| AggregateKind::CoroutineClosure(def_id, _)
| AggregateKind::Coroutine(def_id, _) => {
let def_id = def_id.expect_local();
let BorrowCheckResult { used_mut_upvars, .. } =
self.infcx.tcx.mir_borrowck(def_id);
@ -1609,6 +1612,7 @@ fn check_movable_place(&mut self, location: Location, place: Place<'tcx>) {
| ty::FnPtr(_)
| ty::Dynamic(_, _, _)
| ty::Closure(_, _)
| ty::CoroutineClosure(_, _)
| ty::Coroutine(_, _)
| ty::CoroutineWitness(..)
| ty::Never
@ -1633,7 +1637,10 @@ fn check_movable_place(&mut self, location: Location, place: Place<'tcx>) {
return;
}
}
ty::Closure(_, _) | ty::Coroutine(_, _) | ty::Tuple(_) => (),
ty::Closure(..)
| ty::CoroutineClosure(..)
| ty::Coroutine(_, _)
| ty::Tuple(_) => (),
ty::Bool
| ty::Char
| ty::Int(_)

View file

@ -164,7 +164,7 @@ pub(crate) fn is_upvar_field_projection<'tcx>(
match place_ref.last_projection() {
Some((place_base, ProjectionElem::Field(field, _ty))) => {
let base_ty = place_base.ty(body, tcx).ty;
if (base_ty.is_closure() || base_ty.is_coroutine())
if (base_ty.is_closure() || base_ty.is_coroutine() || base_ty.is_coroutine_closure())
&& (!by_ref || upvars[field.index()].is_by_ref())
{
Some(field)

View file

@ -7,13 +7,18 @@
//! `RETURN_PLACE` the MIR arguments) are always fully normalized (and
//! contain revealed `impl Trait` values).
use std::assert_matches::assert_matches;
use itertools::Itertools;
use rustc_infer::infer::BoundRegionConversionTime;
use rustc_hir as hir;
use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
use rustc_infer::infer::{BoundRegionConversionTime, RegionVariableOrigin};
use rustc_middle::mir::*;
use rustc_middle::ty::{self, Ty};
use rustc_span::Span;
use crate::universal_regions::UniversalRegions;
use crate::renumber::RegionCtxt;
use crate::universal_regions::{DefiningTy, UniversalRegions};
use super::{Locations, TypeChecker};
@ -23,9 +28,11 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
#[instrument(skip(self, body), level = "debug")]
pub(super) fn check_signature_annotation(&mut self, body: &Body<'tcx>) {
let mir_def_id = body.source.def_id().expect_local();
if !self.tcx().is_closure_or_coroutine(mir_def_id.to_def_id()) {
return;
}
let user_provided_poly_sig = self.tcx().closure_user_provided_sig(mir_def_id);
// Instantiate the canonicalized variables from user-provided signature
@ -34,12 +41,75 @@ pub(super) fn check_signature_annotation(&mut self, body: &Body<'tcx>) {
// so that they represent the view from "inside" the closure.
let user_provided_sig = self
.instantiate_canonical_with_fresh_inference_vars(body.span, &user_provided_poly_sig);
let user_provided_sig = self.infcx.instantiate_binder_with_fresh_vars(
let mut user_provided_sig = self.infcx.instantiate_binder_with_fresh_vars(
body.span,
BoundRegionConversionTime::FnCall,
user_provided_sig,
);
// 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
{
assert_matches!(
self.tcx().coroutine_kind(self.tcx().coroutine_for_closure(mir_def_id)),
Some(hir::CoroutineKind::Desugared(
hir::CoroutineDesugaring::Async,
hir::CoroutineSource::Closure
)),
"this needs to be modified if we're lowering non-async closures"
);
// Make sure to use the args from `DefiningTy` so the right NLL region vids are prepopulated
// into the type.
let args = args.as_coroutine_closure();
let tupled_upvars_ty = ty::CoroutineClosureSignature::tupled_upvars_by_closure_kind(
self.tcx(),
args.kind(),
Ty::new_tup(self.tcx(), user_provided_sig.inputs()),
args.tupled_upvars_ty(),
args.coroutine_captures_by_ref_ty(),
self.infcx.next_region_var(RegionVariableOrigin::MiscVariable(body.span), || {
RegionCtxt::Unknown
}),
);
let next_ty_var = || {
self.infcx.next_ty_var(TypeVariableOrigin {
span: body.span,
kind: TypeVariableOriginKind::MiscVariable,
})
};
let output_ty = Ty::new_coroutine(
self.tcx(),
self.tcx().coroutine_for_closure(mir_def_id),
ty::CoroutineArgs::new(
self.tcx(),
ty::CoroutineArgsParts {
parent_args: args.parent_args(),
kind_ty: Ty::from_closure_kind(self.tcx(), args.kind()),
return_ty: user_provided_sig.output(),
tupled_upvars_ty,
// For async closures, none of these can be annotated, so just fill
// them with fresh ty vars.
resume_ty: next_ty_var(),
yield_ty: next_ty_var(),
witness: next_ty_var(),
},
)
.args,
);
user_provided_sig = self.tcx().mk_fn_sig(
user_provided_sig.inputs().iter().copied(),
output_ty,
user_provided_sig.c_variadic,
user_provided_sig.unsafety,
user_provided_sig.abi,
);
}
let is_coroutine_with_implicit_resume_ty = self.tcx().is_coroutine(mir_def_id.to_def_id())
&& user_provided_sig.inputs().is_empty();

View file

@ -808,6 +808,14 @@ fn field_ty(
}),
};
}
ty::CoroutineClosure(_, args) => {
return match args.as_coroutine_closure().upvar_tys().get(field.index()) {
Some(&ty) => Ok(ty),
None => Err(FieldAccessError::OutOfRange {
field_count: args.as_coroutine_closure().upvar_tys().len(),
}),
};
}
ty::Coroutine(_, args) => {
// Only prefix fields (upvars and current state) are
// accessible without a variant index.
@ -1875,6 +1883,14 @@ fn aggregate_field_ty(
}),
}
}
AggregateKind::CoroutineClosure(_, args) => {
match args.as_coroutine_closure().upvar_tys().get(field_index.as_usize()) {
Some(ty) => Ok(*ty),
None => Err(FieldAccessError::OutOfRange {
field_count: args.as_coroutine_closure().upvar_tys().len(),
}),
}
}
AggregateKind::Array(ty) => Ok(ty),
AggregateKind::Tuple => {
unreachable!("This should have been covered in check_rvalues");
@ -2478,6 +2494,7 @@ fn rvalue_user_ty(&self, rvalue: &Rvalue<'tcx>) -> Option<UserTypeAnnotationInde
AggregateKind::Tuple => None,
AggregateKind::Closure(_, _) => None,
AggregateKind::Coroutine(_, _) => None,
AggregateKind::CoroutineClosure(_, _) => None,
},
}
}
@ -2705,7 +2722,9 @@ fn prove_aggregate_predicates(
// desugaring. A closure gets desugared to a struct, and
// these extra requirements are basically like where
// clauses on the struct.
AggregateKind::Closure(def_id, args) | AggregateKind::Coroutine(def_id, args) => (
AggregateKind::Closure(def_id, args)
| AggregateKind::CoroutineClosure(def_id, args)
| AggregateKind::Coroutine(def_id, args) => (
def_id,
self.prove_closure_bounds(
tcx,
@ -2754,10 +2773,15 @@ fn prove_closure_bounds(
let typeck_root_args = ty::GenericArgs::identity_for_item(tcx, typeck_root_def_id);
let parent_args = match tcx.def_kind(def_id) {
DefKind::Closure if tcx.is_coroutine(def_id.to_def_id()) => {
args.as_coroutine().parent_args()
// We don't want to dispatch on 3 different kind of closures here, so take
// advantage of the fact that the `parent_args` is the same length as the
// `typeck_root_args`.
DefKind::Closure => {
// FIXME(async_closures): It may be useful to add a debug assert here
// to actually call `type_of` and check the `parent_args` are the same
// length as the `typeck_root_args`.
&args[..typeck_root_args.len()]
}
DefKind::Closure => args.as_closure().parent_args(),
DefKind::InlineConst => args.as_inline_const().parent_args(),
other => bug!("unexpected item {:?}", other),
};

View file

@ -97,6 +97,13 @@ pub enum DefiningTy<'tcx> {
/// `ClosureArgs::coroutine_return_ty`.
Coroutine(DefId, GenericArgsRef<'tcx>),
/// The MIR is a special kind of closure that returns coroutines.
///
/// 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
/// of the function can be bound then with the `fn_sig` query.
FnDef(DefId, GenericArgsRef<'tcx>),
@ -119,6 +126,7 @@ impl<'tcx> DefiningTy<'tcx> {
pub fn upvar_tys(self) -> &'tcx ty::List<Ty<'tcx>> {
match self {
DefiningTy::Closure(_, args) => args.as_closure().upvar_tys(),
DefiningTy::CoroutineClosure(_, args) => args.as_coroutine_closure().upvar_tys(),
DefiningTy::Coroutine(_, args) => args.as_coroutine().upvar_tys(),
DefiningTy::FnDef(..) | DefiningTy::Const(..) | DefiningTy::InlineConst(..) => {
ty::List::empty()
@ -131,7 +139,9 @@ pub fn upvar_tys(self) -> &'tcx ty::List<Ty<'tcx>> {
/// user's code.
pub fn implicit_inputs(self) -> usize {
match self {
DefiningTy::Closure(..) | DefiningTy::Coroutine(..) => 1,
DefiningTy::Closure(..)
| DefiningTy::CoroutineClosure(..)
| DefiningTy::Coroutine(..) => 1,
DefiningTy::FnDef(..) | DefiningTy::Const(..) | DefiningTy::InlineConst(..) => 0,
}
}
@ -147,6 +157,7 @@ pub fn is_const(&self) -> bool {
pub fn def_id(&self) -> DefId {
match *self {
DefiningTy::Closure(def_id, ..)
| DefiningTy::CoroutineClosure(def_id, ..)
| DefiningTy::Coroutine(def_id, ..)
| DefiningTy::FnDef(def_id, ..)
| DefiningTy::Const(def_id, ..)
@ -355,6 +366,9 @@ pub(crate) fn annotate(&self, tcx: TyCtxt<'tcx>, err: &mut Diagnostic) {
err.note(format!("late-bound region is {:?}", self.to_region_vid(r)));
});
}
DefiningTy::CoroutineClosure(..) => {
todo!()
}
DefiningTy::Coroutine(def_id, args) => {
let v = with_no_trimmed_paths!(
args[tcx.generics_of(def_id).parent_count..]
@ -568,6 +582,9 @@ fn defining_ty(&self) -> DefiningTy<'tcx> {
match *defining_ty.kind() {
ty::Closure(def_id, args) => DefiningTy::Closure(def_id, args),
ty::Coroutine(def_id, args) => DefiningTy::Coroutine(def_id, args),
ty::CoroutineClosure(def_id, args) => {
DefiningTy::CoroutineClosure(def_id, args)
}
ty::FnDef(def_id, args) => DefiningTy::FnDef(def_id, args),
_ => span_bug!(
tcx.def_span(self.mir_def),
@ -623,6 +640,7 @@ fn compute_indices(
let identity_args = GenericArgs::identity_for_item(tcx, typeck_root_def_id);
let fr_args = match defining_ty {
DefiningTy::Closure(_, args)
| DefiningTy::CoroutineClosure(_, args)
| DefiningTy::Coroutine(_, args)
| DefiningTy::InlineConst(_, args) => {
// In the case of closures, we rely on the fact that
@ -702,6 +720,55 @@ fn compute_inputs_and_output(
ty::Binder::dummy(inputs_and_output)
}
// Construct the signature of the CoroutineClosure for the purposes of borrowck.
// This is pretty straightforward -- we:
// 1. first grab the `coroutine_closure_sig`,
// 2. compute the self type (`&`/`&mut`/no borrow),
// 3. flatten the tupled_input_tys,
// 4. construct the correct generator type to return with
// `CoroutineClosureSignature::to_coroutine_given_kind_and_upvars`.
// Then we wrap it all up into a list of inputs and output.
DefiningTy::CoroutineClosure(def_id, args) => {
assert_eq!(self.mir_def.to_def_id(), def_id);
let closure_sig = args.as_coroutine_closure().coroutine_closure_sig();
let bound_vars = tcx.mk_bound_variable_kinds_from_iter(
closure_sig
.bound_vars()
.iter()
.chain(iter::once(ty::BoundVariableKind::Region(ty::BrEnv))),
);
let br = ty::BoundRegion {
var: ty::BoundVar::from_usize(bound_vars.len() - 1),
kind: ty::BrEnv,
};
let env_region = ty::Region::new_bound(tcx, ty::INNERMOST, br);
let closure_kind = args.as_coroutine_closure().kind();
let closure_ty = tcx.closure_env_ty(
Ty::new_coroutine_closure(tcx, def_id, args),
closure_kind,
env_region,
);
let inputs = closure_sig.skip_binder().tupled_inputs_ty.tuple_fields();
let output = closure_sig.skip_binder().to_coroutine_given_kind_and_upvars(
tcx,
args.as_coroutine_closure().parent_args(),
tcx.coroutine_for_closure(def_id),
closure_kind,
env_region,
args.as_coroutine_closure().tupled_upvars_ty(),
args.as_coroutine_closure().coroutine_captures_by_ref_ty(),
);
ty::Binder::bind_with_vars(
tcx.mk_type_list_from_iter(
iter::once(closure_ty).chain(inputs).chain(iter::once(output)),
),
bound_vars,
)
}
DefiningTy::FnDef(def_id, _) => {
let sig = tcx.fn_sig(def_id).instantiate_identity();
let sig = indices.fold_to_region_vids(tcx, sig);

View file

@ -87,7 +87,7 @@ fn uncached_gcc_type<'gcc, 'tcx>(cx: &CodegenCx<'gcc, 'tcx>, layout: TyAndLayout
// FIXME(eddyb) producing readable type names for trait objects can result
// in problematically distinct types due to HRTB and subtyping (see #47638).
// ty::Dynamic(..) |
ty::Adt(..) | ty::Closure(..) | ty::Foreign(..) | ty::Coroutine(..) | ty::Str
ty::Adt(..) | ty::Closure(..) | ty::CoroutineClosure(..) | ty::Foreign(..) | ty::Coroutine(..) | ty::Str
if !cx.sess().fewer_names() =>
{
let mut name = with_no_trimmed_paths!(layout.ty.to_string());

View file

@ -33,7 +33,7 @@ fn uncached_llvm_type<'a, 'tcx>(
// FIXME(eddyb) producing readable type names for trait objects can result
// in problematically distinct types due to HRTB and subtyping (see #47638).
// ty::Dynamic(..) |
ty::Adt(..) | ty::Closure(..) | ty::Foreign(..) | ty::Coroutine(..) | ty::Str
ty::Adt(..) | ty::Closure(..) | ty::CoroutineClosure(..) | ty::Foreign(..) | ty::Coroutine(..) | ty::Str
// For performance reasons we use names only when emitting LLVM IR.
if !cx.sess().fewer_names() =>
{

View file

@ -398,7 +398,9 @@ fn push_debuginfo_type_name<'tcx>(
// processing
visited.remove(&t);
}
ty::Closure(def_id, args) | ty::Coroutine(def_id, args, ..) => {
ty::Closure(def_id, args)
| ty::CoroutineClosure(def_id, args)
| ty::Coroutine(def_id, args, ..) => {
// Name will be "{closure_env#0}<T1, T2, ...>", "{coroutine_env#0}<T1, T2, ...>", or
// "{async_fn_env#0}<T1, T2, ...>", etc.
// In the case of cpp-like debuginfo, the name additionally gets wrapped inside of
@ -768,6 +770,8 @@ fn push_closure_or_coroutine_name<'tcx>(
// Truncate the args to the length of the above generics. This will cut off
// anything closure- or coroutine-specific.
// FIXME(async_closures): This is probably not going to be correct w.r.t.
// multiple coroutine flavors. Maybe truncate to (parent + 1)?
let args = args.truncate_to(tcx, generics);
push_generic_params_internal(tcx, args, enclosing_fn_def_id, output, visited);
}

View file

@ -172,6 +172,7 @@ pub(crate) fn const_to_valtree_inner<'tcx>(
| ty::Infer(_)
// FIXME(oli-obk): we can probably encode closures just like structs
| ty::Closure(..)
| ty::CoroutineClosure(..)
| ty::Coroutine(..)
| ty::CoroutineWitness(..) => Err(ValTreeCreationError::NonSupportedType),
}
@ -301,6 +302,7 @@ pub fn valtree_to_const_value<'tcx>(
| ty::Placeholder(..)
| ty::Infer(_)
| ty::Closure(..)
| ty::CoroutineClosure(..)
| ty::Coroutine(..)
| ty::CoroutineWitness(..)
| ty::FnPtr(_)

View file

@ -1007,6 +1007,7 @@ fn is_very_trivially_sized(ty: Ty<'_>) -> bool {
| ty::CoroutineWitness(..)
| ty::Array(..)
| ty::Closure(..)
| ty::CoroutineClosure(..)
| ty::Never
| ty::Error(_) => true,

View file

@ -85,6 +85,7 @@ pub(crate) fn eval_nullary_intrinsic<'tcx>(
| ty::FnPtr(_)
| ty::Dynamic(_, _, _)
| ty::Closure(_, _)
| ty::CoroutineClosure(_, _)
| ty::Coroutine(_, _)
| ty::CoroutineWitness(..)
| ty::Never

View file

@ -545,6 +545,8 @@ pub(crate) fn eval_fn_call(
ty::InstanceDef::VTableShim(..)
| ty::InstanceDef::ReifyShim(..)
| ty::InstanceDef::ClosureOnceShim { .. }
| ty::InstanceDef::ConstructCoroutineInClosureShim { .. }
| ty::InstanceDef::CoroutineKindShim { .. }
| ty::InstanceDef::FnPtrShim(..)
| ty::InstanceDef::DropGlue(..)
| ty::InstanceDef::CloneShim(..)

View file

@ -644,6 +644,7 @@ fn try_visit_primitive(
| ty::Str
| ty::Dynamic(..)
| ty::Closure(..)
| ty::CoroutineClosure(..)
| ty::Coroutine(..) => Ok(false),
// Some types only occur during typechecking, they have no layout.
// We should not see them here and we could not check them anyway.

View file

@ -58,6 +58,7 @@ fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
let body_abi = match body_ty.kind() {
ty::FnDef(..) => body_ty.fn_sig(tcx).abi(),
ty::Closure(..) => Abi::RustCall,
ty::CoroutineClosure(..) => Abi::RustCall,
ty::Coroutine(..) => Abi::Rust,
_ => {
span_bug!(body.span, "unexpected body ty: {:?} phase {:?}", body_ty, mir_phase)
@ -665,6 +666,14 @@ fn visit_projection_elem(
};
check_equal(self, location, f_ty);
}
ty::CoroutineClosure(_, args) => {
let args = args.as_coroutine_closure();
let Some(&f_ty) = args.upvar_tys().get(f.as_usize()) else {
fail_out_of_bounds(self, location);
return;
};
check_equal(self, location, f_ty);
}
&ty::Coroutine(def_id, args) => {
let f_ty = if let Some(var) = parent_ty.variant_index {
let gen_body = if def_id == self.body.source.def_id() {
@ -861,6 +870,20 @@ macro_rules! check_kinds {
}
}
}
AggregateKind::CoroutineClosure(_, args) => {
let upvars = args.as_coroutine_closure().upvar_tys();
if upvars.len() != fields.len() {
self.fail(
location,
"coroutine-closure has the wrong number of initialized fields",
);
}
for (src, dest) in std::iter::zip(fields, upvars) {
if !self.mir_assign_valid_types(src.ty(self.body, self.tcx), dest) {
self.fail(location, "coroutine-closure field has the wrong type");
}
}
}
},
Rvalue::Ref(_, BorrowKind::Fake, _) => {
if self.mir_phase >= MirPhase::Runtime(RuntimePhase::Initial) {

View file

@ -51,6 +51,7 @@ fn print_type(&mut self, ty: Ty<'tcx>) -> Result<(), PrintError> {
| ty::FnDef(def_id, args)
| ty::Alias(ty::Projection | ty::Opaque, ty::AliasTy { def_id, args, .. })
| ty::Closure(def_id, args)
| ty::CoroutineClosure(def_id, args)
| ty::Coroutine(def_id, args) => self.print_def_path(def_id, args),
ty::Foreign(def_id) => self.print_def_path(def_id, &[]),

View file

@ -952,6 +952,11 @@ pub enum ClosureKind {
/// usage (e.g. `let x = || { yield (); }`) or from a desugared expression
/// (e.g. `async` and `gen` blocks).
Coroutine(CoroutineKind),
/// This is a coroutine-closure, which is a special sugared closure that
/// returns one of the sugared coroutine (`async`/`gen`/`async gen`). It
/// additionally allows capturing the coroutine's upvars by ref, and therefore
/// needs to be specially treated during analysis and borrowck.
CoroutineClosure(CoroutineDesugaring),
}
/// A block of statements `{ .. }`, which may have a label (in this case the
@ -3698,6 +3703,7 @@ pub fn fn_kind(self) -> Option<FnKind<'hir>> {
expect_generic_param, &'hir GenericParam<'hir>, Node::GenericParam(n), n;
expect_crate, &'hir Mod<'hir>, Node::Crate(n), n;
expect_infer, &'hir InferArg, Node::Infer(n), n;
expect_closure, &'hir Closure<'hir>, Node::Expr(Expr { kind: ExprKind::Closure(n), .. }), n;
}
}

View file

@ -209,6 +209,7 @@ pub fn extract(attrs: &[ast::Attribute]) -> Option<(Symbol, Span)> {
AsyncFn, sym::async_fn, async_fn_trait, Target::Trait, GenericRequirement::Exact(1);
AsyncFnMut, sym::async_fn_mut, async_fn_mut_trait, Target::Trait, GenericRequirement::Exact(1);
AsyncFnOnce, sym::async_fn_once, async_fn_once_trait, Target::Trait, GenericRequirement::Exact(1);
AsyncFnKindHelper, sym::async_fn_kind_helper,async_fn_kind_helper, Target::Trait, GenericRequirement::Exact(1);
FnOnceOutput, sym::fn_once_output, fn_once_output, Target::AssocTy, GenericRequirement::None;

View file

@ -171,6 +171,7 @@ fn check_item(&mut self, id: hir::ItemId) -> Result<(), ErrorGuaranteed> {
}
ty::FnDef(..)
| ty::Closure(..)
| ty::CoroutineClosure(..)
| ty::Coroutine(..)
| ty::CoroutineWitness(..)
| ty::Bound(..)

View file

@ -244,6 +244,7 @@ enum NonlocalImpl {
| ty::Tuple(..) => (LocalImpl::Allow, NonlocalImpl::DisallowOther),
ty::Closure(..)
| ty::CoroutineClosure(..)
| ty::Coroutine(..)
| ty::CoroutineWitness(..)
| ty::Bound(..)

View file

@ -81,6 +81,7 @@ pub fn provide(providers: &mut Providers) {
impl_trait_ref,
impl_polarity,
coroutine_kind,
coroutine_for_closure,
collect_mod_item_types,
is_type_alias_impl_trait,
..*providers
@ -1531,6 +1532,29 @@ fn coroutine_kind(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Option<hir::CoroutineK
}
}
fn coroutine_for_closure(tcx: TyCtxt<'_>, def_id: LocalDefId) -> DefId {
let &rustc_hir::Closure { kind: hir::ClosureKind::CoroutineClosure(_), body, .. } =
tcx.hir_node_by_def_id(def_id).expect_closure()
else {
bug!()
};
let &hir::Expr {
kind:
hir::ExprKind::Closure(&rustc_hir::Closure {
def_id,
kind: hir::ClosureKind::Coroutine(_),
..
}),
..
} = tcx.hir().body(body).value
else {
bug!()
};
def_id.to_def_id()
}
fn is_type_alias_impl_trait<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> bool {
match tcx.hir_node_by_def_id(def_id) {
Node::Item(hir::Item { kind: hir::ItemKind::OpaqueTy(opaque), .. }) => {

View file

@ -349,6 +349,13 @@ enum Defaults {
ClosureKind::Coroutine(_) => {
&["<resume_ty>", "<yield_ty>", "<return_ty>", "<witness>", "<upvars>"][..]
}
ClosureKind::CoroutineClosure(_) => &[
"<closure_kind>",
"<closure_signature_parts>",
"<upvars>",
"<bound_captures_by_ref>",
"<witness>",
][..],
};
params.extend(dummy_args.iter().map(|&arg| ty::GenericParamDef {

View file

@ -235,8 +235,8 @@ fn add_constraints_from_ty(
// leaf type -- noop
}
ty::FnDef(..) | ty::Coroutine(..) | ty::Closure(..) => {
bug!("Unexpected closure type in variance computation");
ty::FnDef(..) | ty::Coroutine(..) | ty::Closure(..) | ty::CoroutineClosure(..) => {
bug!("Unexpected coroutine/closure type in variance computation");
}
ty::Ref(region, ty, mutbl) => {

View file

@ -141,33 +141,77 @@ fn try_overloaded_call_step(
return Some(CallStep::Builtin(adjusted_ty));
}
ty::Closure(def_id, args) => {
// Check whether this is a call to a closure where we
// haven't yet decided on whether the closure is fn vs
// fnmut vs fnonce. If so, we have to defer further processing.
ty::Closure(def_id, args) if self.closure_kind(adjusted_ty).is_none() => {
let def_id = def_id.expect_local();
let closure_sig = args.as_closure().sig();
let closure_sig = self.instantiate_binder_with_fresh_vars(
call_expr.span,
infer::FnCall,
closure_sig,
);
let adjustments = self.adjust_steps(autoderef);
self.record_deferred_call_resolution(
def_id,
DeferredCallResolution {
call_expr,
callee_expr,
closure_ty: adjusted_ty,
adjustments,
fn_sig: closure_sig,
},
);
return Some(CallStep::DeferredClosure(def_id, closure_sig));
}
// Check whether this is a call to a closure where we
// haven't yet decided on whether the closure is fn vs
// fnmut vs fnonce. If so, we have to defer further processing.
if self.closure_kind(args).is_none() {
let closure_sig = args.as_closure().sig();
let closure_sig = self.instantiate_binder_with_fresh_vars(
call_expr.span,
infer::FnCall,
closure_sig,
);
let adjustments = self.adjust_steps(autoderef);
self.record_deferred_call_resolution(
def_id,
DeferredCallResolution {
call_expr,
callee_expr,
adjusted_ty,
adjustments,
fn_sig: closure_sig,
closure_args: args,
},
);
return Some(CallStep::DeferredClosure(def_id, closure_sig));
}
// When calling a `CoroutineClosure` that is local to the body, we will
// not know what its `closure_kind` is yet. Instead, just fill in the
// signature with an infer var for the `tupled_upvars_ty` of the coroutine,
// and record a deferred call resolution which will constrain that var
// as part of `AsyncFn*` trait confirmation.
ty::CoroutineClosure(def_id, args) if self.closure_kind(adjusted_ty).is_none() => {
let def_id = def_id.expect_local();
let closure_args = args.as_coroutine_closure();
let coroutine_closure_sig = self.instantiate_binder_with_fresh_vars(
call_expr.span,
infer::FnCall,
closure_args.coroutine_closure_sig(),
);
let tupled_upvars_ty = self.next_ty_var(TypeVariableOrigin {
kind: TypeVariableOriginKind::TypeInference,
span: callee_expr.span,
});
let call_sig = self.tcx.mk_fn_sig(
[coroutine_closure_sig.tupled_inputs_ty],
coroutine_closure_sig.to_coroutine(
self.tcx,
closure_args.parent_args(),
// Inherit the kind ty of the closure, since we're calling this
// coroutine with the most relaxed `AsyncFn*` trait that we can.
// We don't necessarily need to do this here, but it saves us
// computing one more infer var that will get constrained later.
closure_args.kind_ty(),
self.tcx.coroutine_for_closure(def_id),
tupled_upvars_ty,
),
coroutine_closure_sig.c_variadic,
coroutine_closure_sig.unsafety,
coroutine_closure_sig.abi,
);
let adjustments = self.adjust_steps(autoderef);
self.record_deferred_call_resolution(
def_id,
DeferredCallResolution {
call_expr,
callee_expr,
closure_ty: adjusted_ty,
adjustments,
fn_sig: call_sig,
},
);
return Some(CallStep::DeferredClosure(def_id, call_sig));
}
// Hack: we know that there are traits implementing Fn for &F
@ -886,10 +930,9 @@ fn confirm_overloaded_call(
pub struct DeferredCallResolution<'tcx> {
call_expr: &'tcx hir::Expr<'tcx>,
callee_expr: &'tcx hir::Expr<'tcx>,
adjusted_ty: Ty<'tcx>,
closure_ty: Ty<'tcx>,
adjustments: Vec<Adjustment<'tcx>>,
fn_sig: ty::FnSig<'tcx>,
closure_args: GenericArgsRef<'tcx>,
}
impl<'a, 'tcx> DeferredCallResolution<'tcx> {
@ -898,10 +941,10 @@ pub fn resolve(self, fcx: &FnCtxt<'a, 'tcx>) {
// we should not be invoked until the closure kind has been
// determined by upvar inference
assert!(fcx.closure_kind(self.closure_args).is_some());
assert!(fcx.closure_kind(self.closure_ty).is_some());
// We may now know enough to figure out fn vs fnmut etc.
match fcx.try_overloaded_call_traits(self.call_expr, self.adjusted_ty, None) {
match fcx.try_overloaded_call_traits(self.call_expr, self.closure_ty, None) {
Some((autoref, method_callee)) => {
// One problem is that when we get here, we are going
// to have a newly instantiated function signature
@ -937,7 +980,7 @@ pub fn resolve(self, fcx: &FnCtxt<'a, 'tcx>) {
span_bug!(
self.call_expr.span,
"Expected to find a suitable `Fn`/`FnMut`/`FnOnce` implementation for `{}`",
self.adjusted_ty
self.closure_ty
)
}
}

View file

@ -133,6 +133,7 @@ fn pointer_kind(
| ty::FnDef(..)
| ty::FnPtr(..)
| ty::Closure(..)
| ty::CoroutineClosure(..)
| ty::Coroutine(..)
| ty::Adt(..)
| ty::Never

View file

@ -63,7 +63,7 @@ pub fn check_expr_closure(
None => (None, None),
};
let ClosureSignatures { bound_sig, liberated_sig } =
let ClosureSignatures { bound_sig, mut liberated_sig } =
self.sig_of_closure(expr_def_id, closure.fn_decl, closure.kind, expected_sig);
debug!(?bound_sig, ?liberated_sig);
@ -125,7 +125,7 @@ pub fn check_expr_closure(
hir::CoroutineKind::Desugared(hir::CoroutineDesugaring::Gen, _)
| hir::CoroutineKind::Coroutine(_) => {
let yield_ty = self.next_ty_var(TypeVariableOrigin {
kind: TypeVariableOriginKind::TypeInference,
kind: TypeVariableOriginKind::ClosureSynthetic,
span: expr_span,
});
self.require_type_is_sized(yield_ty, expr_span, traits::SizedYieldType);
@ -137,7 +137,7 @@ pub fn check_expr_closure(
// not a problem.
hir::CoroutineKind::Desugared(hir::CoroutineDesugaring::AsyncGen, _) => {
let yield_ty = self.next_ty_var(TypeVariableOrigin {
kind: TypeVariableOriginKind::TypeInference,
kind: TypeVariableOriginKind::ClosureSynthetic,
span: expr_span,
});
self.require_type_is_sized(yield_ty, expr_span, traits::SizedYieldType);
@ -166,8 +166,8 @@ pub fn check_expr_closure(
let resume_ty = liberated_sig.inputs().get(0).copied().unwrap_or(tcx.types.unit);
let interior = self.next_ty_var(TypeVariableOrigin {
kind: TypeVariableOriginKind::MiscVariable,
span: body.value.span,
kind: TypeVariableOriginKind::ClosureSynthetic,
span: expr_span,
});
self.deferred_coroutine_interiors.borrow_mut().push((
expr_def_id,
@ -175,10 +175,24 @@ pub fn check_expr_closure(
interior,
));
// Coroutines that come from coroutine closures have not yet determined
// their kind ty, so make a fresh infer var which will be constrained
// later during upvar analysis. Regular coroutines always have the kind
// ty of `().`
let kind_ty = match kind {
hir::CoroutineKind::Desugared(_, hir::CoroutineSource::Closure) => self
.next_ty_var(TypeVariableOrigin {
kind: TypeVariableOriginKind::ClosureSynthetic,
span: expr_span,
}),
_ => tcx.types.unit,
};
let coroutine_args = ty::CoroutineArgs::new(
tcx,
ty::CoroutineArgsParts {
parent_args,
kind_ty,
resume_ty,
yield_ty,
return_ty: liberated_sig.output(),
@ -192,6 +206,94 @@ pub fn check_expr_closure(
Some(CoroutineTypes { resume_ty, yield_ty }),
)
}
hir::ClosureKind::CoroutineClosure(kind) => {
// async closures always return the type ascribed after the `->` (if present),
// and yield `()`.
let (bound_return_ty, bound_yield_ty) = match kind {
hir::CoroutineDesugaring::Async => {
(bound_sig.skip_binder().output(), tcx.types.unit)
}
hir::CoroutineDesugaring::Gen | hir::CoroutineDesugaring::AsyncGen => {
todo!("`gen` and `async gen` closures not supported yet")
}
};
// Compute all of the variables that will be used to populate the coroutine.
let resume_ty = self.next_ty_var(TypeVariableOrigin {
kind: TypeVariableOriginKind::ClosureSynthetic,
span: expr_span,
});
let interior = self.next_ty_var(TypeVariableOrigin {
kind: TypeVariableOriginKind::ClosureSynthetic,
span: expr_span,
});
let closure_kind_ty = self.next_ty_var(TypeVariableOrigin {
// FIXME(eddyb) distinguish closure kind inference variables from the rest.
kind: TypeVariableOriginKind::ClosureSynthetic,
span: expr_span,
});
let coroutine_captures_by_ref_ty = self.next_ty_var(TypeVariableOrigin {
kind: TypeVariableOriginKind::ClosureSynthetic,
span: expr_span,
});
let closure_args = ty::CoroutineClosureArgs::new(
tcx,
ty::CoroutineClosureArgsParts {
parent_args,
closure_kind_ty,
signature_parts_ty: Ty::new_fn_ptr(
tcx,
bound_sig.map_bound(|sig| {
tcx.mk_fn_sig(
[
resume_ty,
Ty::new_tup_from_iter(tcx, sig.inputs().iter().copied()),
],
Ty::new_tup(tcx, &[bound_yield_ty, bound_return_ty]),
sig.c_variadic,
sig.unsafety,
sig.abi,
)
}),
),
tupled_upvars_ty,
coroutine_captures_by_ref_ty,
coroutine_witness_ty: interior,
},
);
let coroutine_upvars_ty = self.next_ty_var(TypeVariableOrigin {
kind: TypeVariableOriginKind::ClosureSynthetic,
span: expr_span,
});
// We need to turn the liberated signature that we got from HIR, which
// looks something like `|Args...| -> T`, into a signature that is suitable
// for type checking the inner body of the closure, which always returns a
// coroutine. To do so, we use the `CoroutineClosureSignature` to compute
// the coroutine type, filling in the tupled_upvars_ty and kind_ty with infer
// vars which will get constrained during upvar analysis.
let coroutine_output_ty = tcx.liberate_late_bound_regions(
expr_def_id.to_def_id(),
closure_args.coroutine_closure_sig().map_bound(|sig| {
sig.to_coroutine(
tcx,
parent_args,
closure_kind_ty,
tcx.coroutine_for_closure(expr_def_id),
coroutine_upvars_ty,
)
}),
);
liberated_sig = tcx.mk_fn_sig(
liberated_sig.inputs().iter().copied(),
coroutine_output_ty,
liberated_sig.c_variadic,
liberated_sig.unsafety,
liberated_sig.abi,
);
(Ty::new_coroutine_closure(tcx, expr_def_id.to_def_id(), closure_args.args), None)
}
};
check_fn(
@ -690,7 +792,10 @@ fn supplied_sig_of_closure(
_,
))
| hir::ClosureKind::Coroutine(hir::CoroutineKind::Coroutine(_))
| hir::ClosureKind::Closure => astconv.ty_infer(None, decl.output.span()),
| hir::ClosureKind::Closure
| hir::ClosureKind::CoroutineClosure(_) => {
astconv.ty_infer(None, decl.output.span())
}
},
};

View file

@ -57,6 +57,7 @@ fn is_fn_ty(&self, ty: Ty<'tcx>, span: Span) -> bool {
match ty.kind() {
// Not all of these (e.g., unsafe fns) implement `FnOnce`,
// so we look for these beforehand.
// FIXME(async_closures): These don't impl `FnOnce` by default.
ty::Closure(..) | ty::FnDef(..) | ty::FnPtr(_) => true,
// If it's not a simple function, look for things which implement `FnOnce`.
_ => {

View file

@ -170,9 +170,14 @@ fn analyze_closure(
) {
// Extract the type of the closure.
let ty = self.node_ty(closure_hir_id);
let (closure_def_id, args) = match *ty.kind() {
ty::Closure(def_id, args) => (def_id, UpvarArgs::Closure(args)),
ty::Coroutine(def_id, args) => (def_id, UpvarArgs::Coroutine(args)),
let (closure_def_id, args, infer_kind) = match *ty.kind() {
ty::Closure(def_id, args) => {
(def_id, UpvarArgs::Closure(args), self.closure_kind(ty).is_none())
}
ty::CoroutineClosure(def_id, args) => {
(def_id, UpvarArgs::CoroutineClosure(args), self.closure_kind(ty).is_none())
}
ty::Coroutine(def_id, args) => (def_id, UpvarArgs::Coroutine(args), false),
ty::Error(_) => {
// #51714: skip analysis when we have already encountered type errors
return;
@ -188,18 +193,63 @@ fn analyze_closure(
};
let closure_def_id = closure_def_id.expect_local();
let infer_kind = if let UpvarArgs::Closure(closure_args) = args {
self.closure_kind(closure_args).is_none().then_some(closure_args)
} else {
None
};
assert_eq!(self.tcx.hir().body_owner_def_id(body.id()), closure_def_id);
let mut delegate = InferBorrowKind {
closure_def_id,
capture_information: Default::default(),
fake_reads: Default::default(),
};
// As noted in `lower_coroutine_body_with_moved_arguments`, we default the capture mode
// to `ByRef` for the `async {}` block internal to async fns/closure. This means
// that we would *not* be moving all of the parameters into the async block by default.
//
// We force all of these arguments to be captured by move before we do expr use analysis.
//
// FIXME(async_closures): This could be cleaned up. It's a bit janky that we're just
// moving all of the `LocalSource::AsyncFn` locals here.
if let Some(hir::CoroutineKind::Desugared(
_,
hir::CoroutineSource::Fn | hir::CoroutineSource::Closure,
)) = self.tcx.coroutine_kind(closure_def_id)
{
let hir::ExprKind::Block(block, _) = body.value.kind else {
bug!();
};
for stmt in block.stmts {
let hir::StmtKind::Local(hir::Local {
init: Some(init),
source: hir::LocalSource::AsyncFn,
pat,
..
}) = stmt.kind
else {
bug!();
};
let hir::PatKind::Binding(hir::BindingAnnotation(hir::ByRef::No, _), _, _, _) =
pat.kind
else {
// Complex pattern, skip the non-upvar local.
continue;
};
let hir::ExprKind::Path(hir::QPath::Resolved(_, path)) = init.kind else {
bug!();
};
let hir::def::Res::Local(local_id) = path.res else {
bug!();
};
let place = self.place_for_root_variable(closure_def_id, local_id);
delegate.capture_information.push((
place,
ty::CaptureInfo {
capture_kind_expr_id: Some(init.hir_id),
path_expr_id: Some(init.hir_id),
capture_kind: UpvarCapture::ByValue,
},
));
}
}
euv::ExprUseVisitor::new(
&mut delegate,
&self.infcx,
@ -257,10 +307,14 @@ fn analyze_closure(
let before_feature_tys = self.final_upvar_tys(closure_def_id);
if let Some(closure_args) = infer_kind {
if infer_kind {
// Unify the (as yet unbound) type variable in the closure
// args with the kind we inferred.
let closure_kind_ty = closure_args.as_closure().kind_ty();
let closure_kind_ty = match args {
UpvarArgs::Closure(args) => args.as_closure().kind_ty(),
UpvarArgs::CoroutineClosure(args) => args.as_coroutine_closure().kind_ty(),
UpvarArgs::Coroutine(_) => unreachable!("coroutines don't have an inferred kind"),
};
self.demand_eqtype(
span,
Ty::from_closure_kind(self.tcx, closure_kind),
@ -282,6 +336,84 @@ fn analyze_closure(
}
}
// For coroutine-closures, we additionally must compute the
// `coroutine_captures_by_ref_ty` type, which is used to generate the by-ref
// version of the coroutine-closure's output coroutine.
if let UpvarArgs::CoroutineClosure(args) = args {
let closure_env_region: ty::Region<'_> = ty::Region::new_bound(
self.tcx,
ty::INNERMOST,
ty::BoundRegion {
var: ty::BoundVar::from_usize(0),
kind: ty::BoundRegionKind::BrEnv,
},
);
let tupled_upvars_ty_for_borrow = Ty::new_tup_from_iter(
self.tcx,
self.typeck_results
.borrow()
.closure_min_captures_flattened(
self.tcx.coroutine_for_closure(closure_def_id).expect_local(),
)
// Skip the captures that are just moving the closure's args
// into the coroutine. These are always by move, and we append
// those later in the `CoroutineClosureSignature` helper functions.
.skip(
args.as_coroutine_closure()
.coroutine_closure_sig()
.skip_binder()
.tupled_inputs_ty
.tuple_fields()
.len(),
)
.map(|captured_place| {
let upvar_ty = captured_place.place.ty();
let capture = captured_place.info.capture_kind;
// Not all upvars are captured by ref, so use
// `apply_capture_kind_on_capture_ty` to ensure that we
// compute the right captured type.
apply_capture_kind_on_capture_ty(
self.tcx,
upvar_ty,
capture,
Some(closure_env_region),
)
}),
);
let coroutine_captures_by_ref_ty = Ty::new_fn_ptr(
self.tcx,
ty::Binder::bind_with_vars(
self.tcx.mk_fn_sig(
[],
tupled_upvars_ty_for_borrow,
false,
hir::Unsafety::Normal,
rustc_target::spec::abi::Abi::Rust,
),
self.tcx.mk_bound_variable_kinds(&[ty::BoundVariableKind::Region(
ty::BoundRegionKind::BrEnv,
)]),
),
);
self.demand_eqtype(
span,
args.as_coroutine_closure().coroutine_captures_by_ref_ty(),
coroutine_captures_by_ref_ty,
);
// Additionally, we can now constrain the coroutine's kind type.
let ty::Coroutine(_, coroutine_args) =
*self.typeck_results.borrow().expr_ty(body.value).kind()
else {
bug!();
};
self.demand_eqtype(
span,
coroutine_args.as_coroutine().kind_ty(),
Ty::from_closure_kind(self.tcx, closure_kind),
);
}
self.log_closure_min_capture_info(closure_def_id, span);
// Now that we've analyzed the closure, we know how each
@ -551,7 +683,7 @@ fn compute_min_captures(
};
// Go through each entry in the current list of min_captures
// - if ancestor is found, update it's capture kind to account for current place's
// - if ancestor is found, update its capture kind to account for current place's
// capture information.
//
// - if descendant is found, remove it from the list, and update the current place's

View file

@ -414,6 +414,7 @@ fn fold_ty(&mut self, mut t: Ty<'tcx>) -> Ty<'tcx> {
}
ty::Closure(..)
| ty::CoroutineClosure(..)
| ty::Coroutine(..)
| ty::CoroutineWitness(..)
| ty::Bool

View file

@ -2839,7 +2839,11 @@ fn as_failure_code(&self, terr: TypeError<'tcx>) -> FailureCode {
// say, also take a look at the error code, maybe we can
// tailor to that.
_ => match terr {
TypeError::CyclicTy(ty) if ty.is_closure() || ty.is_coroutine() => Error0644,
TypeError::CyclicTy(ty)
if ty.is_closure() || ty.is_coroutine() || ty.is_coroutine_closure() =>
{
Error0644
}
TypeError::IntrinsicCast => Error0308,
_ => Error0308,
},
@ -2886,7 +2890,9 @@ fn as_failure_code_diag(
// say, also take a look at the error code, maybe we can
// tailor to that.
_ => match terr {
TypeError::CyclicTy(ty) if ty.is_closure() || ty.is_coroutine() => {
TypeError::CyclicTy(ty)
if ty.is_closure() || ty.is_coroutine() || ty.is_coroutine_closure() =>
{
ObligationCauseFailureCode::ClosureSelfref { span }
}
TypeError::IntrinsicCast => {

View file

@ -883,7 +883,10 @@ fn generic_arg_contains_target(&self, arg: GenericArg<'tcx>) -> bool {
GenericArgKind::Type(ty) => {
if matches!(
ty.kind(),
ty::Alias(ty::Opaque, ..) | ty::Closure(..) | ty::Coroutine(..)
ty::Alias(ty::Opaque, ..)
| ty::Closure(..)
| ty::CoroutineClosure(..)
| ty::Coroutine(..)
) {
// Opaque types can't be named by the user right now.
//

View file

@ -228,7 +228,10 @@ fn foo(&self, x: T) -> T { x }
#traits-as-parameters",
);
}
(ty::Param(p), ty::Closure(..) | ty::Coroutine(..)) => {
(
ty::Param(p),
ty::Closure(..) | ty::CoroutineClosure(..) | ty::Coroutine(..),
) => {
let generics = tcx.generics_of(body_owner_def_id);
if let Some(param) = generics.opt_type_param(p, tcx) {
let p_span = tcx.def_span(param.def_id);
@ -497,7 +500,7 @@ fn foo(&self, x: T) -> T { x }
}
CyclicTy(ty) => {
// Watch out for various cases of cyclic types and try to explain.
if ty.is_closure() || ty.is_coroutine() {
if ty.is_closure() || ty.is_coroutine() || ty.is_coroutine_closure() {
diag.note(
"closures cannot capture themselves or take themselves as argument;\n\
this error may be the result of a recent compiler bug-fix,\n\

View file

@ -1538,9 +1538,13 @@ pub fn verify_generic_bound(
/// Obtains the latest type of the given closure; this may be a
/// closure in the current function, in which case its
/// `ClosureKind` may not yet be known.
pub fn closure_kind(&self, closure_args: GenericArgsRef<'tcx>) -> Option<ty::ClosureKind> {
let closure_kind_ty = closure_args.as_closure().kind_ty();
let closure_kind_ty = self.shallow_resolve(closure_kind_ty);
pub fn closure_kind(&self, closure_ty: Ty<'tcx>) -> Option<ty::ClosureKind> {
let unresolved_kind_ty = match *closure_ty.kind() {
ty::Closure(_, args) => args.as_closure().kind_ty(),
ty::CoroutineClosure(_, args) => args.as_coroutine_closure().kind_ty(),
_ => bug!("unexpected type {closure_ty}"),
};
let closure_kind_ty = self.shallow_resolve(unresolved_kind_ty);
closure_kind_ty.to_opt_closure_kind()
}

View file

@ -456,6 +456,17 @@ fn visit_ty(&mut self, ty: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
args.as_closure().sig_as_fn_ptr_ty().visit_with(self);
}
ty::CoroutineClosure(_, args) => {
// Skip lifetime parameters of the enclosing item(s)
for upvar in args.as_coroutine_closure().upvar_tys() {
upvar.visit_with(self);
}
// FIXME(async_closures): Is this the right signature to visit here?
args.as_coroutine_closure().signature_parts_ty().visit_with(self);
}
ty::Coroutine(_, args) => {
// Skip lifetime parameters of the enclosing item(s)
// Also skip the witness type, because that has no free regions.

View file

@ -103,6 +103,11 @@ fn compute_components<'tcx>(
compute_components(tcx, tupled_ty, out, visited);
}
ty::CoroutineClosure(_, args) => {
let tupled_ty = args.as_coroutine_closure().tupled_upvars_ty();
compute_components(tcx, tupled_ty, out, visited);
}
ty::Coroutine(_, args) => {
// Same as the closure case
let tupled_ty = args.as_coroutine().tupled_upvars_ty();

View file

@ -1435,6 +1435,7 @@ fn check_type_for_ffi(&self, cache: &mut FxHashSet<Ty<'tcx>>, ty: Ty<'tcx>) -> F
| ty::Bound(..)
| ty::Error(_)
| ty::Closure(..)
| ty::CoroutineClosure(..)
| ty::Coroutine(..)
| ty::CoroutineWitness(..)
| ty::Placeholder(..)

View file

@ -355,7 +355,7 @@ fn is_ty_must_use<'tcx>(
Some(len) => is_ty_must_use(cx, ty, expr, span)
.map(|inner| MustUsePath::Array(Box::new(inner), len)),
},
ty::Closure(..) => Some(MustUsePath::Closure(span)),
ty::Closure(..) | ty::CoroutineClosure(..) => Some(MustUsePath::Closure(span)),
ty::Coroutine(def_id, ..) => {
// async fn should be treated as "implementor of `Future`"
let must_use = if cx.tcx.coroutine_is_async(def_id) {

View file

@ -23,7 +23,7 @@ pub fn require_lang_item(self, lang_item: LangItem, span: Option<Span>) -> DefId
})
}
/// Given a [`DefId`] of a [`Fn`], [`FnMut`] or [`FnOnce`] traits,
/// Given a [`DefId`] of one of the [`Fn`], [`FnMut`] or [`FnOnce`] traits,
/// returns a corresponding [`ty::ClosureKind`].
/// For any other [`DefId`] return `None`.
pub fn fn_trait_kind_from_def_id(self, id: DefId) -> Option<ty::ClosureKind> {
@ -36,6 +36,19 @@ pub fn fn_trait_kind_from_def_id(self, id: DefId) -> Option<ty::ClosureKind> {
}
}
/// Given a [`DefId`] of one of the `AsyncFn`, `AsyncFnMut` or `AsyncFnOnce` traits,
/// returns a corresponding [`ty::ClosureKind`].
/// For any other [`DefId`] return `None`.
pub fn async_fn_trait_kind_from_def_id(self, id: DefId) -> Option<ty::ClosureKind> {
let items = self.lang_items();
match Some(id) {
x if x == items.async_fn_trait() => Some(ty::ClosureKind::Fn),
x if x == items.async_fn_mut_trait() => Some(ty::ClosureKind::FnMut),
x if x == items.async_fn_once_trait() => Some(ty::ClosureKind::FnOnce),
_ => None,
}
}
/// Given a [`ty::ClosureKind`], get the [`DefId`] of its corresponding `Fn`-family
/// trait, if it is defined.
pub fn fn_trait_kind_to_def_id(self, kind: ty::ClosureKind) -> Option<DefId> {

View file

@ -262,6 +262,25 @@ pub struct CoroutineInfo<'tcx> {
/// Coroutine drop glue. This field is populated after the state transform pass.
pub coroutine_drop: Option<Body<'tcx>>,
/// 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` pass 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<Body<'tcx>>,
/// The body of the coroutine, modified to take its upvars by mutable ref rather than by
/// immutable ref.
///
/// FIXME(async_closures): This is literally the same body as the parent body. Find a better
/// way to represent the by-mut signature (or cap the closure-kind of the coroutine).
pub by_mut_body: Option<Body<'tcx>>,
/// The layout of a coroutine. This field is populated after the state transform pass.
pub coroutine_layout: Option<CoroutineLayout<'tcx>>,
@ -281,6 +300,8 @@ pub fn initial(
coroutine_kind,
yield_ty: Some(yield_ty),
resume_ty: Some(resume_ty),
by_move_body: None,
by_mut_body: None,
coroutine_drop: None,
coroutine_layout: None,
}
@ -591,6 +612,14 @@ pub fn coroutine_drop(&self) -> Option<&Body<'tcx>> {
self.coroutine.as_ref().and_then(|coroutine| coroutine.coroutine_drop.as_ref())
}
pub fn coroutine_by_move_body(&self) -> Option<&Body<'tcx>> {
self.coroutine.as_ref()?.by_move_body.as_ref()
}
pub fn coroutine_by_mut_body(&self) -> Option<&Body<'tcx>> {
self.coroutine.as_ref()?.by_mut_body.as_ref()
}
#[inline]
pub fn coroutine_kind(&self) -> Option<CoroutineKind> {
self.coroutine.as_ref().map(|coroutine| coroutine.coroutine_kind)

View file

@ -402,6 +402,8 @@ fn item_sort_key<'tcx>(tcx: TyCtxt<'tcx>, item: MonoItem<'tcx>) -> ItemSortKey<'
| InstanceDef::FnPtrShim(..)
| InstanceDef::Virtual(..)
| InstanceDef::ClosureOnceShim { .. }
| InstanceDef::ConstructCoroutineInClosureShim { .. }
| InstanceDef::CoroutineKindShim { .. }
| InstanceDef::DropGlue(..)
| InstanceDef::CloneShim(..)
| InstanceDef::ThreadLocalShim(..)

View file

@ -990,7 +990,8 @@ fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {
})
}
AggregateKind::Closure(def_id, args) => ty::tls::with(|tcx| {
AggregateKind::Closure(def_id, args)
| AggregateKind::CoroutineClosure(def_id, args) => ty::tls::with(|tcx| {
let name = if tcx.sess.opts.unstable_opts.span_free_formats {
let args = tcx.lift(args).unwrap();
format!("{{closure@{}}}", tcx.def_path_str_with_args(def_id, args),)

View file

@ -1350,6 +1350,7 @@ pub enum AggregateKind<'tcx> {
Closure(DefId, GenericArgsRef<'tcx>),
Coroutine(DefId, GenericArgsRef<'tcx>),
CoroutineClosure(DefId, GenericArgsRef<'tcx>),
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, TyEncodable, TyDecodable, Hash, HashStable)]

View file

@ -202,6 +202,9 @@ pub fn ty<D: ?Sized>(&self, local_decls: &D, tcx: TyCtxt<'tcx>) -> Ty<'tcx>
AggregateKind::Adt(did, _, args, _, _) => tcx.type_of(did).instantiate(tcx, args),
AggregateKind::Closure(did, args) => Ty::new_closure(tcx, did, args),
AggregateKind::Coroutine(did, args) => Ty::new_coroutine(tcx, did, args),
AggregateKind::CoroutineClosure(did, args) => {
Ty::new_coroutine_closure(tcx, did, args)
}
},
Rvalue::ShallowInitBox(_, ty) => Ty::new_box(tcx, ty),
Rvalue::CopyForDeref(ref place) => place.ty(local_decls, tcx).ty,

View file

@ -345,6 +345,8 @@ fn super_source_scope_data(
ty::InstanceDef::Virtual(_def_id, _) |
ty::InstanceDef::ThreadLocalShim(_def_id) |
ty::InstanceDef::ClosureOnceShim { call_once: _def_id, track_caller: _ } |
ty::InstanceDef::ConstructCoroutineInClosureShim { coroutine_closure_def_id: _def_id, target_kind: _ } |
ty::InstanceDef::CoroutineKindShim { coroutine_def_id: _def_id, target_kind: _ } |
ty::InstanceDef::DropGlue(_def_id, None) => {}
ty::InstanceDef::FnPtrShim(_def_id, ty) |
@ -739,6 +741,12 @@ fn super_rvalue(&mut self,
) => {
self.visit_args(coroutine_args, location);
}
AggregateKind::CoroutineClosure(
_,
coroutine_closure_args,
) => {
self.visit_args(coroutine_closure_args, location);
}
}
for operand in operands {

View file

@ -755,6 +755,11 @@
separate_provide_extern
}
query coroutine_for_closure(def_id: DefId) -> DefId {
desc { |_tcx| "Given a coroutine-closure def id, return the def id of the coroutine returned by it" }
separate_provide_extern
}
/// Gets a map with the variance of every item; use `variances_of` instead.
query crate_variances(_: ()) -> &'tcx ty::CrateVariancesMap<'tcx> {
arena_cache

View file

@ -134,6 +134,15 @@ pub enum SelectionCandidate<'tcx> {
is_const: bool,
},
/// Implementation of an `AsyncFn`-family trait by one of the anonymous types
/// generated for an `async ||` expression.
AsyncClosureCandidate,
/// 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
/// generated for a coroutine.
CoroutineCandidate,

View file

@ -1544,6 +1544,7 @@ fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
CoroutineWitness,
Dynamic,
Closure,
CoroutineClosure,
Tuple,
Bound,
Param,

View file

@ -299,7 +299,7 @@ pub fn prefix_string(self, tcx: TyCtxt<'_>) -> Cow<'static, str> {
},
ty::FnPtr(_) => "fn pointer".into(),
ty::Dynamic(..) => "trait object".into(),
ty::Closure(..) => "closure".into(),
ty::Closure(..) | ty::CoroutineClosure(..) => "closure".into(),
ty::Coroutine(def_id, ..) => {
format!("{:#}", tcx.coroutine_kind(def_id).unwrap()).into()
}

View file

@ -128,7 +128,9 @@ pub fn simplify_type<'tcx>(
_ => Some(SimplifiedType::MarkerTraitObject),
},
ty::Ref(_, _, mutbl) => Some(SimplifiedType::Ref(mutbl)),
ty::FnDef(def_id, _) | ty::Closure(def_id, _) => Some(SimplifiedType::Closure(def_id)),
ty::FnDef(def_id, _) | ty::Closure(def_id, _) | ty::CoroutineClosure(def_id, _) => {
Some(SimplifiedType::Closure(def_id))
}
ty::Coroutine(def_id, _) => Some(SimplifiedType::Coroutine(def_id)),
ty::CoroutineWitness(def_id, _) => Some(SimplifiedType::CoroutineWitness(def_id)),
ty::Never => Some(SimplifiedType::Never),
@ -236,6 +238,7 @@ pub fn types_may_unify<'tcx>(self, obligation_ty: Ty<'tcx>, impl_ty: Ty<'tcx>) -
| ty::Foreign(..) => debug_assert!(impl_ty.is_known_rigid()),
ty::FnDef(..)
| ty::Closure(..)
| ty::CoroutineClosure(..)
| ty::Coroutine(..)
| ty::CoroutineWitness(..)
| ty::Placeholder(..)
@ -312,7 +315,7 @@ pub fn types_may_unify<'tcx>(self, obligation_ty: Ty<'tcx>, impl_ty: Ty<'tcx>) -
},
// Impls cannot contain these types as these cannot be named directly.
ty::FnDef(..) | ty::Closure(..) | ty::Coroutine(..) => false,
ty::FnDef(..) | ty::Closure(..) | ty::CoroutineClosure(..) | ty::Coroutine(..) => false,
// Placeholder types don't unify with anything on their own
ty::Placeholder(..) | ty::Bound(..) => false,

View file

@ -136,6 +136,22 @@ fn add_kind(&mut self, kind: &ty::TyKind<'_>) {
self.add_ty(args.tupled_upvars_ty());
}
&ty::CoroutineClosure(_, args) => {
let args = args.as_coroutine_closure();
let should_remove_further_specializable =
!self.flags.contains(TypeFlags::STILL_FURTHER_SPECIALIZABLE);
self.add_args(args.parent_args());
if should_remove_further_specializable {
self.flags -= TypeFlags::STILL_FURTHER_SPECIALIZABLE;
}
self.add_ty(args.kind_ty());
self.add_ty(args.signature_parts_ty());
self.add_ty(args.tupled_upvars_ty());
self.add_ty(args.coroutine_captures_by_ref_ty());
self.add_ty(args.coroutine_witness_ty());
}
&ty::Bound(debruijn, _) => {
self.add_bound_var(debruijn);
self.add_flags(TypeFlags::HAS_TY_BOUND);

View file

@ -2,7 +2,7 @@
use crate::ty::codec::{TyDecoder, TyEncoder};
use crate::ty::fold::{FallibleTypeFolder, TypeFoldable, TypeFolder, TypeSuperFoldable};
use crate::ty::sty::{ClosureArgs, CoroutineArgs, InlineConstArgs};
use crate::ty::sty::{ClosureArgs, CoroutineArgs, CoroutineClosureArgs, InlineConstArgs};
use crate::ty::visit::{TypeVisitable, TypeVisitableExt, TypeVisitor};
use crate::ty::{self, Lift, List, ParamConst, Ty, TyCtxt};
@ -288,6 +288,14 @@ pub fn as_closure(&'tcx self) -> ClosureArgs<'tcx> {
ClosureArgs { args: self }
}
/// Interpret these generic args as the args of a coroutine-closure type.
/// Coroutine-closure args have a particular structure controlled by the
/// compiler that encodes information like the signature and closure kind;
/// see `ty::CoroutineClosureArgs` struct for more comments.
pub fn as_coroutine_closure(&'tcx self) -> CoroutineClosureArgs<'tcx> {
CoroutineClosureArgs { args: self }
}
/// Interpret these generic args as the args of a coroutine type.
/// Coroutine args have a particular structure controlled by the
/// compiler that encodes information like the signature and coroutine kind;

View file

@ -82,11 +82,33 @@ pub enum InstanceDef<'tcx> {
/// details on that).
Virtual(DefId, usize),
/// `<[FnMut closure] as FnOnce>::call_once`.
/// `<[FnMut/Fn closure] as FnOnce>::call_once`.
///
/// The `DefId` is the ID of the `call_once` method in `FnOnce`.
///
/// This generates a body that will just borrow the (owned) self type,
/// and dispatch to the `FnMut::call_mut` instance for the closure.
ClosureOnceShim { call_once: DefId, track_caller: bool },
/// `<[FnMut/Fn coroutine-closure] as FnOnce>::call_once` or
/// `<[Fn coroutine-closure] as FnMut>::call_mut`.
///
/// The body generated here differs significantly from the `ClosureOnceShim`,
/// since we need to generate a distinct coroutine type that will move the
/// closure's upvars *out* of the closure.
ConstructCoroutineInClosureShim {
coroutine_closure_def_id: DefId,
target_kind: ty::ClosureKind,
},
/// `<[coroutine] as Future>::poll`, but for coroutines produced when `AsyncFnOnce`
/// is called on a coroutine-closure whose closure kind greater than `FnOnce`, or
/// similarly for `AsyncFnMut`.
///
/// 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.
CoroutineKindShim { coroutine_def_id: DefId, target_kind: ty::ClosureKind },
/// Compiler-generated accessor for thread locals which returns a reference to the thread local
/// the `DefId` defines. This is used to export thread locals from dylibs on platforms lacking
/// native support.
@ -168,6 +190,11 @@ pub fn def_id(self) -> DefId {
| InstanceDef::Intrinsic(def_id)
| InstanceDef::ThreadLocalShim(def_id)
| InstanceDef::ClosureOnceShim { call_once: def_id, track_caller: _ }
| ty::InstanceDef::ConstructCoroutineInClosureShim {
coroutine_closure_def_id: def_id,
target_kind: _,
}
| ty::InstanceDef::CoroutineKindShim { coroutine_def_id: def_id, target_kind: _ }
| InstanceDef::DropGlue(def_id, _)
| InstanceDef::CloneShim(def_id, _)
| InstanceDef::FnPtrAddrShim(def_id, _) => def_id,
@ -187,6 +214,8 @@ pub fn def_id_if_not_guaranteed_local_codegen(self) -> Option<DefId> {
| InstanceDef::Virtual(..)
| InstanceDef::Intrinsic(..)
| InstanceDef::ClosureOnceShim { .. }
| ty::InstanceDef::ConstructCoroutineInClosureShim { .. }
| ty::InstanceDef::CoroutineKindShim { .. }
| InstanceDef::DropGlue(..)
| InstanceDef::CloneShim(..)
| InstanceDef::FnPtrAddrShim(..) => None,
@ -282,6 +311,8 @@ pub fn has_polymorphic_mir_body(&self) -> bool {
| InstanceDef::FnPtrShim(..)
| InstanceDef::DropGlue(_, Some(_)) => false,
InstanceDef::ClosureOnceShim { .. }
| InstanceDef::ConstructCoroutineInClosureShim { .. }
| InstanceDef::CoroutineKindShim { .. }
| InstanceDef::DropGlue(..)
| InstanceDef::Item(_)
| InstanceDef::Intrinsic(..)
@ -319,6 +350,8 @@ fn fmt_instance(
InstanceDef::Virtual(_, num) => write!(f, " - virtual#{num}"),
InstanceDef::FnPtrShim(_, ty) => write!(f, " - shim({ty})"),
InstanceDef::ClosureOnceShim { .. } => write!(f, " - shim"),
InstanceDef::ConstructCoroutineInClosureShim { .. } => write!(f, " - shim"),
InstanceDef::CoroutineKindShim { .. } => write!(f, " - shim"),
InstanceDef::DropGlue(_, None) => write!(f, " - shim(None)"),
InstanceDef::DropGlue(_, Some(ty)) => write!(f, " - shim(Some({ty}))"),
InstanceDef::CloneShim(_, ty) => write!(f, " - shim({ty})"),
@ -610,7 +643,24 @@ pub fn try_resolve_item_for_coroutine(
};
if tcx.lang_items().get(coroutine_callable_item) == Some(trait_item_id) {
Some(Instance { def: ty::InstanceDef::Item(coroutine_def_id), args: args })
let ty::Coroutine(_, id_args) = *tcx.type_of(coroutine_def_id).skip_binder().kind()
else {
bug!()
};
// If the closure's kind ty disagrees with the identity closure's kind ty,
// then this must be a coroutine generated by one of the `ConstructCoroutineInClosureShim`s.
if args.as_coroutine().kind_ty() == id_args.as_coroutine().kind_ty() {
Some(Instance { def: ty::InstanceDef::Item(coroutine_def_id), args })
} else {
Some(Instance {
def: ty::InstanceDef::CoroutineKindShim {
coroutine_def_id,
target_kind: args.as_coroutine().kind_ty().to_opt_closure_kind().unwrap(),
},
args,
})
}
} else {
// All other methods should be defaulted methods of the built-in trait.
// This is important for `Iterator`'s combinators, but also useful for

View file

@ -906,6 +906,12 @@ fn field_ty_or_layout<'tcx>(
i,
),
ty::CoroutineClosure(_, args) => field_ty_or_layout(
TyAndLayout { ty: args.as_coroutine_closure().tupled_upvars_ty(), ..this },
cx,
i,
),
ty::Coroutine(def_id, args) => match this.variants {
Variants::Single { index } => TyMaybeWithLayout::Ty(
args.as_coroutine()

View file

@ -105,9 +105,10 @@
pub use self::rvalue_scopes::RvalueScopes;
pub use self::sty::{
AliasTy, Article, Binder, BoundTy, BoundTyKind, BoundVariableKind, CanonicalPolyFnSig,
ClosureArgs, ClosureArgsParts, CoroutineArgs, CoroutineArgsParts, FnSig, GenSig,
InlineConstArgs, InlineConstArgsParts, ParamConst, ParamTy, PolyFnSig, TyKind, TypeAndMut,
UpvarArgs, VarianceDiagInfo,
ClosureArgs, ClosureArgsParts, CoroutineArgs, CoroutineArgsParts, CoroutineClosureArgs,
CoroutineClosureArgsParts, CoroutineClosureSignature, FnSig, GenSig, InlineConstArgs,
InlineConstArgsParts, ParamConst, ParamTy, PolyFnSig, TyKind, TypeAndMut, UpvarArgs,
VarianceDiagInfo,
};
pub use self::trait_def::TraitDef;
pub use self::typeck_results::{
@ -1679,6 +1680,8 @@ pub fn instance_mir(self, instance: ty::InstanceDef<'tcx>) -> &'tcx Body<'tcx> {
| ty::InstanceDef::FnPtrShim(..)
| ty::InstanceDef::Virtual(..)
| ty::InstanceDef::ClosureOnceShim { .. }
| ty::InstanceDef::ConstructCoroutineInClosureShim { .. }
| ty::InstanceDef::CoroutineKindShim { .. }
| ty::InstanceDef::DropGlue(..)
| ty::InstanceDef::CloneShim(..)
| ty::InstanceDef::ThreadLocalShim(..)

View file

@ -3,6 +3,7 @@
use rustc_data_structures::fx::FxHashSet;
use rustc_data_structures::sso::SsoHashSet;
use rustc_hir as hir;
use rustc_hir::def_id::{CrateNum, DefId, LocalDefId};
use rustc_hir::definitions::{DefPathData, DisambiguatedDefPathData};
@ -130,8 +131,24 @@ fn default_print_def_path(
parent_args = &args[..generics.parent_count.min(args.len())];
match key.disambiguated_data.data {
// Closures' own generics are only captures, don't print them.
DefPathData::Closure => {}
DefPathData::Closure => {
// FIXME(async_closures): This is somewhat ugly.
// We need to additionally print the `kind` field of a closure if
// it is desugared from a coroutine-closure.
if let Some(hir::CoroutineKind::Desugared(
_,
hir::CoroutineSource::Closure,
)) = self.tcx().coroutine_kind(def_id)
&& args.len() >= parent_args.len() + 1
{
return self.path_generic_args(
|cx| cx.print_def_path(def_id, parent_args),
&args[..parent_args.len() + 1][..1],
);
} else {
// Closures' own generics are only captures, don't print them.
}
}
// This covers both `DefKind::AnonConst` and `DefKind::InlineConst`.
// Anon consts doesn't have their own generics, and inline consts' own
// generics are their inferred types, so don't print them.
@ -259,6 +276,7 @@ fn characteristic_def_id_of_type_cached<'a>(
ty::FnDef(def_id, _)
| ty::Closure(def_id, _)
| ty::CoroutineClosure(def_id, _)
| ty::Coroutine(def_id, _)
| ty::CoroutineWitness(def_id, _)
| ty::Foreign(def_id) => Some(def_id),

View file

@ -874,6 +874,48 @@ fn pretty_print_type(&mut self, ty: Ty<'tcx>) -> Result<(), PrintError> {
}
p!("}}");
}
ty::CoroutineClosure(did, args) => {
p!(write("{{"));
if !self.should_print_verbose() {
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 {
p!("@", print_def_path(did.to_def_id(), args));
} else {
let span = self.tcx().def_span(did);
let preference = if with_forced_trimmed_paths() {
FileNameDisplayPreference::Short
} else {
FileNameDisplayPreference::Remapped
};
p!(write(
"@{}",
// This may end up in stderr diagnostics but it may also be emitted
// into MIR. Hence we use the remapped path if available
self.tcx().sess.source_map().span_to_string(span, preference)
));
}
} else {
p!(write("@"), print_def_path(did, args));
}
} else {
p!(print_def_path(did, args));
p!(
" closure_kind_ty=",
print(args.as_coroutine_closure().kind_ty()),
" signature_parts_ty=",
print(args.as_coroutine_closure().signature_parts_ty()),
" upvar_tys=",
print(args.as_coroutine_closure().tupled_upvars_ty()),
" coroutine_captures_by_ref_ty=",
print(args.as_coroutine_closure().coroutine_captures_by_ref_ty()),
" coroutine_witness_ty=",
print(args.as_coroutine_closure().coroutine_witness_ty())
);
}
p!("}}");
}
ty::Array(ty, sz) => p!("[", print(ty), "; ", print(sz), "]"),
ty::Slice(ty) => p!("[", print(ty), "]"),
}

View file

@ -481,6 +481,13 @@ pub fn structurally_relate_tys<'tcx, R: TypeRelation<'tcx>>(
Ok(Ty::new_closure(tcx, a_id, args))
}
(&ty::CoroutineClosure(a_id, a_args), &ty::CoroutineClosure(b_id, b_args))
if a_id == b_id =>
{
let args = relate_args_invariantly(relation, a_args, b_args)?;
Ok(Ty::new_coroutine_closure(tcx, a_id, args))
}
(&ty::RawPtr(a_mt), &ty::RawPtr(b_mt)) => {
let mt = relate_type_and_mut(relation, a_mt, b_mt, a)?;
Ok(Ty::new_ptr(tcx, mt))

View file

@ -584,6 +584,9 @@ fn try_super_fold_with<F: FallibleTypeFolder<TyCtxt<'tcx>>>(
ty::CoroutineWitness(did, args.try_fold_with(folder)?)
}
ty::Closure(did, args) => ty::Closure(did, args.try_fold_with(folder)?),
ty::CoroutineClosure(did, args) => {
ty::CoroutineClosure(did, args.try_fold_with(folder)?)
}
ty::Alias(kind, data) => ty::Alias(kind, data.try_fold_with(folder)?),
ty::Bool
@ -632,6 +635,7 @@ fn super_visit_with<V: TypeVisitor<TyCtxt<'tcx>>>(
ty::Coroutine(_did, ref args) => args.visit_with(visitor),
ty::CoroutineWitness(_did, ref args) => args.visit_with(visitor),
ty::Closure(_did, ref args) => args.visit_with(visitor),
ty::CoroutineClosure(_did, ref args) => args.visit_with(visitor),
ty::Alias(_, ref data) => data.visit_with(visitor),
ty::Bool

View file

@ -36,6 +36,7 @@
use rustc_type_ir::TyKind::*;
use rustc_type_ir::TypeAndMut as IrTypeAndMut;
use super::fold::FnMutDelegate;
use super::GenericParamDefKind;
// Re-export and re-parameterize some `I = TyCtxt<'tcx>` types here
@ -269,6 +270,275 @@ pub fn print_as_impl_trait(self) -> ty::print::PrintClosureAsImpl<'tcx> {
}
}
#[derive(Copy, Clone, PartialEq, Eq, Debug, TypeFoldable, TypeVisitable, Lift)]
pub struct CoroutineClosureArgs<'tcx> {
pub args: GenericArgsRef<'tcx>,
}
/// See docs for explanation of how each argument is used.
///
/// See [`CoroutineClosureSignature`] for how these arguments are put together
/// to make a callable [`FnSig`] suitable for typeck and borrowck.
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>,
}
impl<'tcx> CoroutineClosureArgs<'tcx> {
pub fn new(
tcx: TyCtxt<'tcx>,
parts: CoroutineClosureArgsParts<'tcx>,
) -> CoroutineClosureArgs<'tcx> {
CoroutineClosureArgs {
args: tcx.mk_args_from_iter(parts.parent_args.iter().copied().chain([
parts.closure_kind_ty.into(),
parts.signature_parts_ty.into(),
parts.tupled_upvars_ty.into(),
parts.coroutine_captures_by_ref_ty.into(),
parts.coroutine_witness_ty.into(),
])),
}
}
fn split(self) -> CoroutineClosureArgsParts<'tcx> {
match self.args[..] {
[
ref parent_args @ ..,
closure_kind_ty,
signature_parts_ty,
tupled_upvars_ty,
coroutine_captures_by_ref_ty,
coroutine_witness_ty,
] => CoroutineClosureArgsParts {
parent_args,
closure_kind_ty: closure_kind_ty.expect_ty(),
signature_parts_ty: signature_parts_ty.expect_ty(),
tupled_upvars_ty: tupled_upvars_ty.expect_ty(),
coroutine_captures_by_ref_ty: coroutine_captures_by_ref_ty.expect_ty(),
coroutine_witness_ty: coroutine_witness_ty.expect_ty(),
},
_ => bug!("closure args missing synthetics"),
}
}
pub fn parent_args(self) -> &'tcx [GenericArg<'tcx>] {
self.split().parent_args
}
#[inline]
pub fn upvar_tys(self) -> &'tcx List<Ty<'tcx>> {
match self.tupled_upvars_ty().kind() {
TyKind::Error(_) => ty::List::empty(),
TyKind::Tuple(..) => self.tupled_upvars_ty().tuple_fields(),
TyKind::Infer(_) => bug!("upvar_tys called before capture types are inferred"),
ty => bug!("Unexpected representation of upvar types tuple {:?}", ty),
}
}
#[inline]
pub fn tupled_upvars_ty(self) -> Ty<'tcx> {
self.split().tupled_upvars_ty
}
pub fn kind_ty(self) -> Ty<'tcx> {
self.split().closure_kind_ty
}
pub fn kind(self) -> ty::ClosureKind {
self.kind_ty().to_opt_closure_kind().unwrap()
}
pub fn signature_parts_ty(self) -> Ty<'tcx> {
self.split().signature_parts_ty
}
pub fn coroutine_closure_sig(self) -> ty::Binder<'tcx, CoroutineClosureSignature<'tcx>> {
let interior = self.coroutine_witness_ty();
let ty::FnPtr(sig) = self.signature_parts_ty().kind() else { bug!() };
sig.map_bound(|sig| {
let [resume_ty, tupled_inputs_ty] = *sig.inputs() else {
bug!();
};
let [yield_ty, return_ty] = **sig.output().tuple_fields() else { bug!() };
CoroutineClosureSignature {
interior,
tupled_inputs_ty,
resume_ty,
yield_ty,
return_ty,
c_variadic: sig.c_variadic,
unsafety: sig.unsafety,
abi: sig.abi,
}
})
}
pub fn coroutine_captures_by_ref_ty(self) -> Ty<'tcx> {
self.split().coroutine_captures_by_ref_ty
}
pub fn coroutine_witness_ty(self) -> Ty<'tcx> {
self.split().coroutine_witness_ty
}
}
#[derive(Copy, Clone, PartialEq, Eq, Debug, TypeFoldable, TypeVisitable)]
pub struct CoroutineClosureSignature<'tcx> {
pub interior: Ty<'tcx>,
pub tupled_inputs_ty: Ty<'tcx>,
pub resume_ty: Ty<'tcx>,
pub yield_ty: Ty<'tcx>,
pub return_ty: Ty<'tcx>,
// Like the `fn_sig_as_fn_ptr_ty` of a regular closure, these types
// never actually differ. But we save them rather than recreating them
// from scratch just for good measure.
/// Always false
pub c_variadic: bool,
/// Always [`hir::Unsafety::Normal`]
pub unsafety: hir::Unsafety,
/// Always [`abi::Abi::RustCall`]
pub abi: abi::Abi,
}
impl<'tcx> CoroutineClosureSignature<'tcx> {
/// Construct a coroutine from the closure signature. Since a coroutine signature
/// is agnostic to the type of generator that is returned (by-ref/by-move),
/// the caller must specify what "flavor" of generator that they'd like to
/// create. Additionally, they must manually compute the upvars of the closure.
///
/// This helper is not really meant to be used directly except for early on
/// during typeck, when we want to put inference vars into the kind and upvars tys.
/// When the kind and upvars are known, use the other helper functions.
pub fn to_coroutine(
self,
tcx: TyCtxt<'tcx>,
parent_args: &'tcx [GenericArg<'tcx>],
coroutine_kind_ty: Ty<'tcx>,
coroutine_def_id: DefId,
tupled_upvars_ty: Ty<'tcx>,
) -> Ty<'tcx> {
let coroutine_args = ty::CoroutineArgs::new(
tcx,
ty::CoroutineArgsParts {
parent_args,
kind_ty: coroutine_kind_ty,
resume_ty: self.resume_ty,
yield_ty: self.yield_ty,
return_ty: self.return_ty,
witness: self.interior,
tupled_upvars_ty,
},
);
Ty::new_coroutine(tcx, coroutine_def_id, coroutine_args.args)
}
/// Given known upvars and a [`ClosureKind`](ty::ClosureKind), compute the coroutine
/// returned by that corresponding async fn trait.
///
/// This function expects the upvars to have been computed already, and doesn't check
/// that the `ClosureKind` is actually supported by the coroutine-closure.
pub fn to_coroutine_given_kind_and_upvars(
self,
tcx: TyCtxt<'tcx>,
parent_args: &'tcx [GenericArg<'tcx>],
coroutine_def_id: DefId,
goal_kind: ty::ClosureKind,
env_region: ty::Region<'tcx>,
closure_tupled_upvars_ty: Ty<'tcx>,
coroutine_captures_by_ref_ty: Ty<'tcx>,
) -> Ty<'tcx> {
let tupled_upvars_ty = Self::tupled_upvars_by_closure_kind(
tcx,
goal_kind,
self.tupled_inputs_ty,
closure_tupled_upvars_ty,
coroutine_captures_by_ref_ty,
env_region,
);
self.to_coroutine(
tcx,
parent_args,
Ty::from_closure_kind(tcx, goal_kind),
coroutine_def_id,
tupled_upvars_ty,
)
}
/// Compute the tupled upvars that a coroutine-closure's output coroutine
/// would return for the given `ClosureKind`.
///
/// When `ClosureKind` is `FnMut`/`Fn`, then this will use the "captures by ref"
/// to return a set of upvars which are borrowed with the given `env_region`.
///
/// This ensures that the `AsyncFn::call` will return a coroutine whose upvars'
/// lifetimes are related to the lifetime of the borrow on the closure made for
/// the call. This allows borrowck to enforce the self-borrows correctly.
pub fn tupled_upvars_by_closure_kind(
tcx: TyCtxt<'tcx>,
kind: ty::ClosureKind,
tupled_inputs_ty: Ty<'tcx>,
closure_tupled_upvars_ty: Ty<'tcx>,
coroutine_captures_by_ref_ty: Ty<'tcx>,
env_region: ty::Region<'tcx>,
) -> Ty<'tcx> {
match kind {
ty::ClosureKind::Fn | ty::ClosureKind::FnMut => {
let ty::FnPtr(sig) = *coroutine_captures_by_ref_ty.kind() else {
bug!();
};
let coroutine_captures_by_ref_ty = tcx.replace_escaping_bound_vars_uncached(
sig.output().skip_binder(),
FnMutDelegate {
consts: &mut |c, t| ty::Const::new_bound(tcx, ty::INNERMOST, c, t),
types: &mut |t| Ty::new_bound(tcx, ty::INNERMOST, t),
regions: &mut |_| env_region,
},
);
Ty::new_tup_from_iter(
tcx,
tupled_inputs_ty
.tuple_fields()
.iter()
.chain(coroutine_captures_by_ref_ty.tuple_fields()),
)
}
ty::ClosureKind::FnOnce => Ty::new_tup_from_iter(
tcx,
tupled_inputs_ty
.tuple_fields()
.iter()
.chain(closure_tupled_upvars_ty.tuple_fields()),
),
}
}
}
/// Similar to `ClosureArgs`; see the above documentation for more.
#[derive(Copy, Clone, PartialEq, Eq, Debug, TypeFoldable, TypeVisitable)]
pub struct CoroutineArgs<'tcx> {
@ -278,13 +548,27 @@ pub struct CoroutineArgs<'tcx> {
pub struct CoroutineArgsParts<'tcx> {
/// This is the args of the typeck root.
pub parent_args: &'tcx [GenericArg<'tcx>],
/// 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>,
@ -296,6 +580,7 @@ impl<'tcx> CoroutineArgs<'tcx> {
pub fn new(tcx: TyCtxt<'tcx>, parts: CoroutineArgsParts<'tcx>) -> CoroutineArgs<'tcx> {
CoroutineArgs {
args: tcx.mk_args_from_iter(parts.parent_args.iter().copied().chain([
parts.kind_ty.into(),
parts.resume_ty.into(),
parts.yield_ty.into(),
parts.return_ty.into(),
@ -309,16 +594,23 @@ pub fn new(tcx: TyCtxt<'tcx>, parts: CoroutineArgsParts<'tcx>) -> CoroutineArgs<
/// The ordering assumed here must match that used by `CoroutineArgs::new` above.
fn split(self) -> CoroutineArgsParts<'tcx> {
match self.args[..] {
[ref parent_args @ .., resume_ty, yield_ty, return_ty, witness, tupled_upvars_ty] => {
CoroutineArgsParts {
parent_args,
resume_ty: resume_ty.expect_ty(),
yield_ty: yield_ty.expect_ty(),
return_ty: return_ty.expect_ty(),
witness: witness.expect_ty(),
tupled_upvars_ty: tupled_upvars_ty.expect_ty(),
}
}
[
ref parent_args @ ..,
kind_ty,
resume_ty,
yield_ty,
return_ty,
witness,
tupled_upvars_ty,
] => CoroutineArgsParts {
parent_args,
kind_ty: kind_ty.expect_ty(),
resume_ty: resume_ty.expect_ty(),
yield_ty: yield_ty.expect_ty(),
return_ty: return_ty.expect_ty(),
witness: witness.expect_ty(),
tupled_upvars_ty: tupled_upvars_ty.expect_ty(),
},
_ => bug!("coroutine args missing synthetics"),
}
}
@ -328,6 +620,11 @@ pub fn parent_args(self) -> &'tcx [GenericArg<'tcx>] {
self.split().parent_args
}
// Returns the kind of the coroutine. See docs on the `kind_ty` field.
pub fn kind_ty(self) -> Ty<'tcx> {
self.split().kind_ty
}
/// This describes the types that can be contained in a coroutine.
/// It will be a type variable initially and unified in the last stages of typeck of a body.
/// It contains a tuple of all the types that could end up on a coroutine frame.
@ -479,6 +776,7 @@ pub fn prefix_tys(self) -> &'tcx List<Ty<'tcx>> {
pub enum UpvarArgs<'tcx> {
Closure(GenericArgsRef<'tcx>),
Coroutine(GenericArgsRef<'tcx>),
CoroutineClosure(GenericArgsRef<'tcx>),
}
impl<'tcx> UpvarArgs<'tcx> {
@ -490,6 +788,7 @@ pub fn upvar_tys(self) -> &'tcx List<Ty<'tcx>> {
let tupled_tys = match self {
UpvarArgs::Closure(args) => args.as_closure().tupled_upvars_ty(),
UpvarArgs::Coroutine(args) => args.as_coroutine().tupled_upvars_ty(),
UpvarArgs::CoroutineClosure(args) => args.as_coroutine_closure().tupled_upvars_ty(),
};
match tupled_tys.kind() {
@ -505,6 +804,7 @@ pub fn tupled_upvars_ty(self) -> Ty<'tcx> {
match self {
UpvarArgs::Closure(args) => args.as_closure().tupled_upvars_ty(),
UpvarArgs::Coroutine(args) => args.as_coroutine().tupled_upvars_ty(),
UpvarArgs::CoroutineClosure(args) => args.as_coroutine_closure().tupled_upvars_ty(),
}
}
}
@ -1393,6 +1693,20 @@ pub fn new_closure(
Ty::new(tcx, Closure(def_id, closure_args))
}
#[inline]
pub fn new_coroutine_closure(
tcx: TyCtxt<'tcx>,
def_id: DefId,
closure_args: GenericArgsRef<'tcx>,
) -> Ty<'tcx> {
debug_assert_eq!(
closure_args.len(),
tcx.generics_of(tcx.typeck_root_def_id(def_id)).count() + 5,
"closure constructed with incorrect substitutions"
);
Ty::new(tcx, CoroutineClosure(def_id, closure_args))
}
#[inline]
pub fn new_coroutine(
tcx: TyCtxt<'tcx>,
@ -1401,7 +1715,7 @@ pub fn new_coroutine(
) -> Ty<'tcx> {
debug_assert_eq!(
coroutine_args.len(),
tcx.generics_of(tcx.typeck_root_def_id(def_id)).count() + 5,
tcx.generics_of(tcx.typeck_root_def_id(def_id)).count() + 6,
"coroutine constructed with incorrect number of substitutions"
);
Ty::new(tcx, Coroutine(def_id, coroutine_args))
@ -1727,6 +2041,11 @@ pub fn is_coroutine(self) -> bool {
matches!(self.kind(), Coroutine(..))
}
#[inline]
pub fn is_coroutine_closure(self) -> bool {
matches!(self.kind(), CoroutineClosure(..))
}
#[inline]
pub fn is_integral(self) -> bool {
matches!(self.kind(), Infer(IntVar(_)) | Int(_) | Uint(_))
@ -1795,7 +2114,7 @@ impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for ContainsClosureVisitor {
type BreakTy = ();
fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
if let ty::Closure(_, _) = t.kind() {
if let ty::Closure(..) = t.kind() {
ControlFlow::Break(())
} else {
t.super_visit_with(self)
@ -1942,6 +2261,7 @@ pub fn discriminant_ty(self, tcx: TyCtxt<'tcx>) -> Ty<'tcx> {
| ty::FnPtr(..)
| ty::Dynamic(..)
| ty::Closure(..)
| ty::CoroutineClosure(..)
| ty::CoroutineWitness(..)
| ty::Never
| ty::Tuple(_)
@ -1980,6 +2300,7 @@ pub fn ptr_metadata_ty(
| ty::CoroutineWitness(..)
| ty::Array(..)
| ty::Closure(..)
| ty::CoroutineClosure(..)
| ty::Never
| ty::Error(_)
// Extern types have metadata = ().
@ -2034,7 +2355,7 @@ pub fn to_opt_closure_kind(self) -> Option<ty::ClosureKind> {
// "Bound" types appear in canonical queries when the
// closure type is not yet known
Bound(..) | Infer(_) => None,
Bound(..) | Param(_) | Infer(_) => None,
Error(_) => Some(ty::ClosureKind::Fn),
@ -2077,6 +2398,7 @@ pub fn is_trivially_sized(self, tcx: TyCtxt<'tcx>) -> bool {
| ty::CoroutineWitness(..)
| ty::Array(..)
| ty::Closure(..)
| ty::CoroutineClosure(..)
| ty::Never
| ty::Error(_) => true,
@ -2140,7 +2462,7 @@ pub fn is_trivially_pure_clone_copy(self) -> bool {
ty::Coroutine(..) | ty::CoroutineWitness(..) => false,
// Might be, but not "trivial" so just giving the safe answer.
ty::Adt(..) | ty::Closure(..) => false,
ty::Adt(..) | ty::Closure(..) | ty::CoroutineClosure(..) => false,
// Needs normalization or revealing to determine, so no is the safe answer.
ty::Alias(..) => false,
@ -2212,6 +2534,7 @@ pub fn is_known_rigid(self) -> bool {
| FnPtr(_)
| Dynamic(_, _, _)
| Closure(_, _)
| CoroutineClosure(_, _)
| Coroutine(_, _)
| CoroutineWitness(..)
| Never

View file

@ -1119,6 +1119,7 @@ fn is_trivially_freeze(self) -> bool {
ty::Adt(..)
| ty::Bound(..)
| ty::Closure(..)
| ty::CoroutineClosure(..)
| ty::Dynamic(..)
| ty::Foreign(_)
| ty::Coroutine(..)
@ -1158,6 +1159,7 @@ fn is_trivially_unpin(self) -> bool {
ty::Adt(..)
| ty::Bound(..)
| ty::Closure(..)
| ty::CoroutineClosure(..)
| ty::Dynamic(..)
| ty::Foreign(_)
| ty::Coroutine(..)
@ -1280,7 +1282,11 @@ pub fn is_structural_eq_shallow(self, tcx: TyCtxt<'tcx>) -> bool {
// Conservatively return `false` for all others...
// Anonymous function types
ty::FnDef(..) | ty::Closure(..) | ty::Dynamic(..) | ty::Coroutine(..) => false,
ty::FnDef(..)
| ty::Closure(..)
| ty::CoroutineClosure(..)
| ty::Dynamic(..)
| ty::Coroutine(..) => false,
// Generic or inferred types
//
@ -1424,6 +1430,7 @@ pub fn needs_drop_components<'tcx>(
| ty::Placeholder(..)
| ty::Infer(_)
| ty::Closure(..)
| ty::CoroutineClosure(..)
| ty::Coroutine(..)
| ty::CoroutineWitness(..) => Ok(smallvec![ty]),
}
@ -1456,7 +1463,11 @@ pub fn is_trivially_const_drop(ty: Ty<'_>) -> bool {
// Not trivial because they have components, and instead of looking inside,
// we'll just perform trait selection.
ty::Closure(..) | ty::Coroutine(..) | ty::CoroutineWitness(..) | ty::Adt(..) => false,
ty::Closure(..)
| ty::CoroutineClosure(..)
| ty::Coroutine(..)
| ty::CoroutineWitness(..)
| ty::Adt(..) => false,
ty::Array(ty, _) | ty::Slice(ty) => is_trivially_const_drop(ty),

View file

@ -189,6 +189,7 @@ fn push_inner<'tcx>(stack: &mut TypeWalkerStack<'tcx>, parent: GenericArg<'tcx>)
}
ty::Adt(_, args)
| ty::Closure(_, args)
| ty::CoroutineClosure(_, args)
| ty::Coroutine(_, args)
| ty::CoroutineWitness(_, args)
| ty::FnDef(_, args) => {

View file

@ -483,6 +483,9 @@ pub(crate) fn as_rvalue(
UpvarArgs::Closure(args) => {
Box::new(AggregateKind::Closure(closure_id.to_def_id(), args))
}
UpvarArgs::CoroutineClosure(args) => {
Box::new(AggregateKind::CoroutineClosure(closure_id.to_def_id(), args))
}
};
block.and(Rvalue::Aggregate(result, operands))
}

View file

@ -495,7 +495,7 @@ fn construct_fn<'tcx>(
args.as_coroutine().yield_ty(),
args.as_coroutine().resume_ty(),
))),
ty::Closure(..) | ty::FnDef(..) => None,
ty::Closure(..) | ty::CoroutineClosure(..) | ty::FnDef(..) => None,
ty => span_bug!(span_with_body, "unexpected type of body: {ty:?}"),
};
@ -822,6 +822,7 @@ fn insert_upvar_arg(&mut self) {
let upvar_args = match closure_ty.kind() {
ty::Closure(_, args) => ty::UpvarArgs::Closure(args),
ty::Coroutine(_, args) => ty::UpvarArgs::Coroutine(args),
ty::CoroutineClosure(_, args) => ty::UpvarArgs::CoroutineClosure(args),
_ => return,
};

View file

@ -556,6 +556,9 @@ fn make_mirror_unadjusted(&mut self, expr: &'tcx hir::Expr<'tcx>) -> Expr<'tcx>
ty::Coroutine(def_id, args) => {
(def_id, UpvarArgs::Coroutine(args), Some(tcx.coroutine_movability(def_id)))
}
ty::CoroutineClosure(def_id, args) => {
(def_id, UpvarArgs::CoroutineClosure(args), None)
}
_ => {
span_bug!(expr.span, "closure expr w/o closure type: {:?}", closure_ty);
}

View file

@ -127,10 +127,24 @@ fn closure_env_param(&self, owner_def: LocalDefId, expr_id: HirId) -> Option<Par
ty::Coroutine(..) => {
Param { ty: closure_ty, pat: None, ty_span: None, self_kind: None, hir_id: None }
}
ty::Closure(_, closure_args) => {
ty::Closure(_, args) => {
let closure_env_ty = self.tcx.closure_env_ty(
closure_ty,
closure_args.as_closure().kind(),
args.as_closure().kind(),
self.tcx.lifetimes.re_erased,
);
Param {
ty: closure_env_ty,
pat: None,
ty_span: None,
self_kind: None,
hir_id: None,
}
}
ty::CoroutineClosure(_, args) => {
let closure_env_ty = self.tcx.closure_env_ty(
closure_ty,
args.as_coroutine_closure().kind(),
self.tcx.lifetimes.re_erased,
);
Param {

View file

@ -861,6 +861,9 @@ fn open_drop(&mut self) -> BasicBlock {
let ty = self.place_ty(self.place);
match ty.kind() {
ty::Closure(_, args) => self.open_drop_for_tuple(args.as_closure().upvar_tys()),
ty::CoroutineClosure(_, args) => {
self.open_drop_for_tuple(args.as_coroutine_closure().upvar_tys())
}
// Note that `elaborate_drops` only drops the upvars of a coroutine,
// and this is ok because `open_drop` here can only be reached
// within that own coroutine's resume function.

View file

@ -154,7 +154,8 @@ fn move_path_for(&mut self, place: Place<'tcx>) -> MovePathResult {
| ty::FnDef(_, _)
| ty::FnPtr(_)
| ty::Dynamic(_, _, _)
| ty::Closure(_, _)
| ty::Closure(..)
| ty::CoroutineClosure(..)
| ty::Coroutine(_, _)
| ty::CoroutineWitness(..)
| ty::Never
@ -177,7 +178,10 @@ fn move_path_for(&mut self, place: Place<'tcx>) -> MovePathResult {
union_path.get_or_insert(base);
}
}
ty::Closure(_, _) | ty::Coroutine(_, _) | ty::Tuple(_) => (),
ty::Closure(..)
| ty::CoroutineClosure(..)
| ty::Coroutine(_, _)
| ty::Tuple(_) => (),
ty::Bool
| ty::Char
| ty::Int(_)

View file

@ -1149,6 +1149,12 @@ pub fn iter_fields<'tcx>(
ty::Closure(_, args) => {
iter_fields(args.as_closure().tupled_upvars_ty(), tcx, param_env, f);
}
ty::Coroutine(_, args) => {
iter_fields(args.as_coroutine().tupled_upvars_ty(), tcx, param_env, f);
}
ty::CoroutineClosure(_, args) => {
iter_fields(args.as_coroutine_closure().tupled_upvars_ty(), tcx, param_env, f);
}
_ => (),
}
}

View file

@ -39,6 +39,7 @@ fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
let body_abi = match body_ty.kind() {
ty::FnDef(..) => body_ty.fn_sig(tcx).abi(),
ty::Closure(..) => Abi::RustCall,
ty::CoroutineClosure(..) => Abi::RustCall,
ty::Coroutine(..) => Abi::Rust,
_ => span_bug!(body.span, "unexpected body ty: {:?}", body_ty),
};

View file

@ -128,7 +128,9 @@ fn visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>, location: Location) {
),
}
}
&AggregateKind::Closure(def_id, _) | &AggregateKind::Coroutine(def_id, _) => {
&AggregateKind::Closure(def_id, _)
| &AggregateKind::CoroutineClosure(def_id, _)
| &AggregateKind::Coroutine(def_id, _) => {
let def_id = def_id.expect_local();
let UnsafetyCheckResult { violations, used_unsafe_blocks, .. } =
self.tcx.mir_unsafety_check_result(def_id);

View file

@ -607,7 +607,8 @@ fn eval_rvalue(
AggregateKind::Array(_)
| AggregateKind::Tuple
| AggregateKind::Closure(_, _)
| AggregateKind::Coroutine(_, _) => VariantIdx::new(0),
| AggregateKind::Coroutine(_, _)
| AggregateKind::CoroutineClosure(_, _) => VariantIdx::new(0),
},
},

View file

@ -50,6 +50,9 @@
//! For coroutines with state 1 (returned) and state 2 (poisoned) it does nothing.
//! Otherwise it drops all the values in scope at the last suspension point.
mod by_move_body;
pub use by_move_body::ByMoveBody;
use crate::abort_unwinding_calls;
use crate::deref_separator::deref_finder;
use crate::errors;

View file

@ -0,0 +1,156 @@
//! A MIR pass which duplicates a coroutine's body and removes any derefs which
//! would be present for upvars that are taken by-ref. The result of which will
//! be a coroutine body that takes all of its upvars by-move, and which we stash
//! into the `CoroutineInfo` for all coroutines returned by coroutine-closures.
use rustc_data_structures::fx::FxIndexSet;
use rustc_hir as hir;
use rustc_middle::mir::visit::MutVisitor;
use rustc_middle::mir::{self, dump_mir, MirPass};
use rustc_middle::ty::{self, InstanceDef, Ty, TyCtxt};
use rustc_target::abi::FieldIdx;
pub struct ByMoveBody;
impl<'tcx> MirPass<'tcx> for ByMoveBody {
fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut mir::Body<'tcx>) {
let Some(coroutine_def_id) = body.source.def_id().as_local() else {
return;
};
let Some(hir::CoroutineKind::Desugared(_, hir::CoroutineSource::Closure)) =
tcx.coroutine_kind(coroutine_def_id)
else {
return;
};
let coroutine_ty = body.local_decls[ty::CAPTURE_STRUCT_LOCAL].ty;
let ty::Coroutine(_, args) = *coroutine_ty.kind() else { bug!() };
let coroutine_kind = args.as_coroutine().kind_ty().to_opt_closure_kind().unwrap();
if coroutine_kind == ty::ClosureKind::FnOnce {
return;
}
let mut by_ref_fields = FxIndexSet::default();
let by_move_upvars = Ty::new_tup_from_iter(
tcx,
tcx.closure_captures(coroutine_def_id).iter().enumerate().map(|(idx, capture)| {
if capture.is_by_ref() {
by_ref_fields.insert(FieldIdx::from_usize(idx));
}
capture.place.ty()
}),
);
let by_move_coroutine_ty = Ty::new_coroutine(
tcx,
coroutine_def_id.to_def_id(),
ty::CoroutineArgs::new(
tcx,
ty::CoroutineArgsParts {
parent_args: args.as_coroutine().parent_args(),
kind_ty: Ty::from_closure_kind(tcx, ty::ClosureKind::FnOnce),
resume_ty: args.as_coroutine().resume_ty(),
yield_ty: args.as_coroutine().yield_ty(),
return_ty: args.as_coroutine().return_ty(),
witness: args.as_coroutine().witness(),
tupled_upvars_ty: by_move_upvars,
},
)
.args,
);
let mut by_move_body = body.clone();
MakeByMoveBody { tcx, by_ref_fields, by_move_coroutine_ty }.visit_body(&mut by_move_body);
dump_mir(tcx, false, "coroutine_by_move", &0, &by_move_body, |_, _| Ok(()));
by_move_body.source = mir::MirSource {
instance: InstanceDef::CoroutineKindShim {
coroutine_def_id: coroutine_def_id.to_def_id(),
target_kind: ty::ClosureKind::FnOnce,
},
promoted: None,
};
body.coroutine.as_mut().unwrap().by_move_body = Some(by_move_body);
// If this is coming from an `AsyncFn` coroutine-closure, we must also create a by-mut body.
// This is actually just a copy of the by-ref body, but with a different self type.
// FIXME(async_closures): We could probably unify this with the by-ref body somehow.
if coroutine_kind == ty::ClosureKind::Fn {
let by_mut_coroutine_ty = Ty::new_coroutine(
tcx,
coroutine_def_id.to_def_id(),
ty::CoroutineArgs::new(
tcx,
ty::CoroutineArgsParts {
parent_args: args.as_coroutine().parent_args(),
kind_ty: Ty::from_closure_kind(tcx, ty::ClosureKind::FnMut),
resume_ty: args.as_coroutine().resume_ty(),
yield_ty: args.as_coroutine().yield_ty(),
return_ty: args.as_coroutine().return_ty(),
witness: args.as_coroutine().witness(),
tupled_upvars_ty: args.as_coroutine().tupled_upvars_ty(),
},
)
.args,
);
let mut by_mut_body = body.clone();
by_mut_body.local_decls[ty::CAPTURE_STRUCT_LOCAL].ty = by_mut_coroutine_ty;
dump_mir(tcx, false, "coroutine_by_mut", &0, &by_mut_body, |_, _| Ok(()));
by_mut_body.source = mir::MirSource {
instance: InstanceDef::CoroutineKindShim {
coroutine_def_id: coroutine_def_id.to_def_id(),
target_kind: ty::ClosureKind::FnMut,
},
promoted: None,
};
body.coroutine.as_mut().unwrap().by_mut_body = Some(by_mut_body);
}
}
}
struct MakeByMoveBody<'tcx> {
tcx: TyCtxt<'tcx>,
by_ref_fields: FxIndexSet<FieldIdx>,
by_move_coroutine_ty: Ty<'tcx>,
}
impl<'tcx> MutVisitor<'tcx> for MakeByMoveBody<'tcx> {
fn tcx(&self) -> TyCtxt<'tcx> {
self.tcx
}
fn visit_place(
&mut self,
place: &mut mir::Place<'tcx>,
context: mir::visit::PlaceContext,
location: mir::Location,
) {
if place.local == ty::CAPTURE_STRUCT_LOCAL
&& !place.projection.is_empty()
&& let mir::ProjectionElem::Field(idx, ty) = place.projection[0]
&& self.by_ref_fields.contains(&idx)
{
let (begin, end) = place.projection[1..].split_first().unwrap();
// FIXME(async_closures): I'm actually a bit surprised to see that we always
// initially deref the by-ref upvars. If this is not actually true, then we
// will at least get an ICE that explains why this isn't true :^)
assert_eq!(*begin, mir::ProjectionElem::Deref);
// Peel one ref off of the ty.
let peeled_ty = ty.builtin_deref(true).unwrap().ty;
*place = mir::Place {
local: place.local,
projection: self.tcx.mk_place_elems_from_iter(
[mir::ProjectionElem::Field(idx, peeled_ty)]
.into_iter()
.chain(end.iter().copied()),
),
};
}
self.super_place(place, context, location);
}
fn visit_local_decl(&mut self, local: mir::Local, local_decl: &mut mir::LocalDecl<'tcx>) {
// Replace the type of the self arg.
if local == ty::CAPTURE_STRUCT_LOCAL {
local_decl.ty = self.by_move_coroutine_ty;
}
}
}

View file

@ -156,7 +156,9 @@ fn bcb_to_initial_coverage_spans<'a, 'tcx>(
fn is_closure_or_coroutine(statement: &Statement<'_>) -> bool {
match statement.kind {
StatementKind::Assign(box (_, Rvalue::Aggregate(box ref agg_kind, _))) => match agg_kind {
AggregateKind::Closure(_, _) | AggregateKind::Coroutine(_, _) => true,
AggregateKind::Closure(_, _)
| AggregateKind::Coroutine(_, _)
| AggregateKind::CoroutineClosure(..) => true,
_ => false,
},
_ => false,

View file

@ -696,6 +696,7 @@ fn try_write_constant<'tcx>(
| ty::Bound(..)
| ty::Placeholder(..)
| ty::Closure(..)
| ty::CoroutineClosure(..)
| ty::Coroutine(..)
| ty::Dynamic(..) => throw_machine_stop_str!("unsupported type"),

View file

@ -57,6 +57,7 @@ fn has_ffi_unwind_calls(tcx: TyCtxt<'_>, local_def_id: LocalDefId) -> bool {
let body_abi = match body_ty.kind() {
ty::FnDef(..) => body_ty.fn_sig(tcx).abi(),
ty::Closure(..) => Abi::RustCall,
ty::CoroutineClosure(..) => Abi::RustCall,
ty::Coroutine(..) => Abi::Rust,
_ => span_bug!(body.span, "unexpected body ty: {:?}", body_ty),
};

View file

@ -861,9 +861,10 @@ fn simplify_aggregate(
let tcx = self.tcx;
if fields.is_empty() {
let is_zst = match *kind {
AggregateKind::Array(..) | AggregateKind::Tuple | AggregateKind::Closure(..) => {
true
}
AggregateKind::Array(..)
| AggregateKind::Tuple
| AggregateKind::Closure(..)
| AggregateKind::CoroutineClosure(..) => true,
// Only enums can be non-ZST.
AggregateKind::Adt(did, ..) => tcx.def_kind(did) != DefKind::Enum,
// Coroutines are never ZST, as they at least contain the implicit states.
@ -885,7 +886,9 @@ fn simplify_aggregate(
assert!(!fields.is_empty());
(AggregateTy::Tuple, FIRST_VARIANT)
}
AggregateKind::Closure(did, substs) | AggregateKind::Coroutine(did, substs) => {
AggregateKind::Closure(did, substs)
| AggregateKind::CoroutineClosure(did, substs)
| AggregateKind::Coroutine(did, substs) => {
(AggregateTy::Def(did, substs), FIRST_VARIANT)
}
AggregateKind::Adt(did, variant_index, substs, _, None) => {

View file

@ -317,6 +317,8 @@ fn check_mir_is_available(
| InstanceDef::ReifyShim(_)
| InstanceDef::FnPtrShim(..)
| InstanceDef::ClosureOnceShim { .. }
| InstanceDef::ConstructCoroutineInClosureShim { .. }
| InstanceDef::CoroutineKindShim { .. }
| InstanceDef::DropGlue(..)
| InstanceDef::CloneShim(..)
| InstanceDef::ThreadLocalShim(..)

View file

@ -87,6 +87,8 @@ fn process<'tcx>(
| InstanceDef::ReifyShim(_)
| InstanceDef::FnPtrShim(..)
| InstanceDef::ClosureOnceShim { .. }
| InstanceDef::ConstructCoroutineInClosureShim { .. }
| InstanceDef::CoroutineKindShim { .. }
| InstanceDef::ThreadLocalShim { .. }
| InstanceDef::CloneShim(..) => {}

View file

@ -307,6 +307,10 @@ fn mir_const(tcx: TyCtxt<'_>, def: LocalDefId) -> &Steal<Body<'_>> {
&Lint(check_packed_ref::CheckPackedRef),
&Lint(check_const_item_mutation::CheckConstItemMutation),
&Lint(function_item_references::FunctionItemReferences),
// If this is an async closure's output coroutine, generate
// by-move and by-mut bodies if needed. We do this first so
// they can be optimized in lockstep with their parent bodies.
&coroutine::ByMoveBody,
// What we need to do constant evaluation.
&simplify::SimplifyCfg::Initial,
&rustc_peek::SanityCheck, // Just a lint

View file

@ -189,6 +189,15 @@ fn run_passes_inner<'tcx>(
body.pass_count = 1;
}
if let Some(coroutine) = body.coroutine.as_mut() {
if let Some(by_move_body) = coroutine.by_move_body.as_mut() {
run_passes_inner(tcx, by_move_body, passes, phase_change, validate_each);
}
if let Some(by_mut_body) = coroutine.by_mut_body.as_mut() {
run_passes_inner(tcx, by_mut_body, passes, phase_change, validate_each);
}
}
}
pub fn validate_body<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>, when: String) {

View file

@ -46,6 +46,7 @@ fn maybe_zst(ty: Ty<'_>) -> bool {
ty::Adt(..)
| ty::Array(..)
| ty::Closure(..)
| ty::CoroutineClosure(..)
| ty::Tuple(..)
| ty::Alias(ty::Opaque, ..) => true,
// definitely ZST

View file

@ -3,8 +3,8 @@
use rustc_hir::lang_items::LangItem;
use rustc_middle::mir::*;
use rustc_middle::query::Providers;
use rustc_middle::ty::GenericArgs;
use rustc_middle::ty::{self, CoroutineArgs, EarlyBinder, Ty, TyCtxt};
use rustc_middle::ty::{GenericArgs, CAPTURE_STRUCT_LOCAL};
use rustc_target::abi::{FieldIdx, VariantIdx, FIRST_VARIANT};
use rustc_index::{Idx, IndexVec};
@ -66,11 +66,76 @@ fn make_shim<'tcx>(tcx: TyCtxt<'tcx>, instance: ty::InstanceDef<'tcx>) -> Body<'
build_call_shim(tcx, instance, Some(Adjustment::RefMut), CallKind::Direct(call_mut))
}
ty::InstanceDef::ConstructCoroutineInClosureShim {
coroutine_closure_def_id,
target_kind,
} => match target_kind {
ty::ClosureKind::Fn => unreachable!("shouldn't be building shim for Fn"),
ty::ClosureKind::FnMut => {
// No need to optimize the body, it has already been optimized
// since we steal it from the `AsyncFn::call` body and just fix
// the return type.
return build_construct_coroutine_by_mut_shim(tcx, coroutine_closure_def_id);
}
ty::ClosureKind::FnOnce => {
build_construct_coroutine_by_move_shim(tcx, coroutine_closure_def_id)
}
},
ty::InstanceDef::CoroutineKindShim { coroutine_def_id, target_kind } => match target_kind {
ty::ClosureKind::Fn => unreachable!(),
ty::ClosureKind::FnMut => {
return tcx
.optimized_mir(coroutine_def_id)
.coroutine_by_mut_body()
.unwrap()
.clone();
}
ty::ClosureKind::FnOnce => {
return tcx
.optimized_mir(coroutine_def_id)
.coroutine_by_move_body()
.unwrap()
.clone();
}
},
ty::InstanceDef::DropGlue(def_id, ty) => {
// FIXME(#91576): Drop shims for coroutines aren't subject to the MIR passes at the end
// of this function. Is this intentional?
if let Some(ty::Coroutine(coroutine_def_id, args)) = ty.map(Ty::kind) {
let body = tcx.optimized_mir(*coroutine_def_id).coroutine_drop().unwrap();
let coroutine_body = tcx.optimized_mir(*coroutine_def_id);
let ty::Coroutine(_, id_args) = *tcx.type_of(coroutine_def_id).skip_binder().kind()
else {
bug!()
};
// If this is a regular coroutine, grab its drop shim. If this is a coroutine
// that comes from a coroutine-closure, and the kind ty differs from the "maximum"
// kind that it supports, then grab the appropriate drop shim. This ensures that
// the future returned by `<[coroutine-closure] as AsyncFnOnce>::call_once` will
// drop the coroutine-closure's upvars.
let body = if id_args.as_coroutine().kind_ty() == args.as_coroutine().kind_ty() {
coroutine_body.coroutine_drop().unwrap()
} else {
match args.as_coroutine().kind_ty().to_opt_closure_kind().unwrap() {
ty::ClosureKind::Fn => {
unreachable!()
}
ty::ClosureKind::FnMut => coroutine_body
.coroutine_by_mut_body()
.unwrap()
.coroutine_drop()
.unwrap(),
ty::ClosureKind::FnOnce => coroutine_body
.coroutine_by_move_body()
.unwrap()
.coroutine_drop()
.unwrap(),
}
};
let mut body = EarlyBinder::bind(body.clone()).instantiate(tcx, args);
debug!("make_shim({:?}) = {:?}", instance, body);
@ -981,3 +1046,114 @@ fn build_fn_ptr_addr_shim<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId, self_ty: Ty<'t
let source = MirSource::from_instance(ty::InstanceDef::FnPtrAddrShim(def_id, self_ty));
new_body(source, IndexVec::from_elem_n(start_block, 1), locals, sig.inputs().len(), span)
}
fn build_construct_coroutine_by_move_shim<'tcx>(
tcx: TyCtxt<'tcx>,
coroutine_closure_def_id: DefId,
) -> Body<'tcx> {
let self_ty = tcx.type_of(coroutine_closure_def_id).instantiate_identity();
let ty::CoroutineClosure(_, args) = *self_ty.kind() else {
bug!();
};
let poly_sig = args.as_coroutine_closure().coroutine_closure_sig().map_bound(|sig| {
tcx.mk_fn_sig(
[self_ty].into_iter().chain(sig.tupled_inputs_ty.tuple_fields()),
sig.to_coroutine_given_kind_and_upvars(
tcx,
args.as_coroutine_closure().parent_args(),
tcx.coroutine_for_closure(coroutine_closure_def_id),
ty::ClosureKind::FnOnce,
tcx.lifetimes.re_erased,
args.as_coroutine_closure().tupled_upvars_ty(),
args.as_coroutine_closure().coroutine_captures_by_ref_ty(),
),
sig.c_variadic,
sig.unsafety,
sig.abi,
)
});
let sig = tcx.liberate_late_bound_regions(coroutine_closure_def_id, poly_sig);
let ty::Coroutine(coroutine_def_id, coroutine_args) = *sig.output().kind() else {
bug!();
};
let span = tcx.def_span(coroutine_closure_def_id);
let locals = local_decls_for_sig(&sig, span);
let mut fields = vec![];
for idx in 1..sig.inputs().len() {
fields.push(Operand::Move(Local::from_usize(idx + 1).into()));
}
for (idx, ty) in args.as_coroutine_closure().upvar_tys().iter().enumerate() {
fields.push(Operand::Move(tcx.mk_place_field(
Local::from_usize(1).into(),
FieldIdx::from_usize(idx),
ty,
)));
}
let source_info = SourceInfo::outermost(span);
let rvalue = Rvalue::Aggregate(
Box::new(AggregateKind::Coroutine(coroutine_def_id, coroutine_args)),
IndexVec::from_raw(fields),
);
let stmt = Statement {
source_info,
kind: StatementKind::Assign(Box::new((Place::return_place(), rvalue))),
};
let statements = vec![stmt];
let start_block = BasicBlockData {
statements,
terminator: Some(Terminator { source_info, kind: TerminatorKind::Return }),
is_cleanup: false,
};
let source = MirSource::from_instance(ty::InstanceDef::ConstructCoroutineInClosureShim {
coroutine_closure_def_id,
target_kind: ty::ClosureKind::FnOnce,
});
let body =
new_body(source, IndexVec::from_elem_n(start_block, 1), locals, sig.inputs().len(), span);
dump_mir(tcx, false, "coroutine_closure_by_move", &0, &body, |_, _| Ok(()));
body
}
fn build_construct_coroutine_by_mut_shim<'tcx>(
tcx: TyCtxt<'tcx>,
coroutine_closure_def_id: DefId,
) -> Body<'tcx> {
let mut body = tcx.optimized_mir(coroutine_closure_def_id).clone();
let coroutine_closure_ty = tcx.type_of(coroutine_closure_def_id).instantiate_identity();
let ty::CoroutineClosure(_, args) = *coroutine_closure_ty.kind() else {
bug!();
};
let args = args.as_coroutine_closure();
body.local_decls[RETURN_PLACE].ty =
tcx.instantiate_bound_regions_with_erased(args.coroutine_closure_sig().map_bound(|sig| {
sig.to_coroutine_given_kind_and_upvars(
tcx,
args.parent_args(),
tcx.coroutine_for_closure(coroutine_closure_def_id),
ty::ClosureKind::FnMut,
tcx.lifetimes.re_erased,
args.tupled_upvars_ty(),
args.coroutine_captures_by_ref_ty(),
)
}));
body.local_decls[CAPTURE_STRUCT_LOCAL].ty =
Ty::new_mut_ref(tcx, tcx.lifetimes.re_erased, coroutine_closure_ty);
body.source = MirSource::from_instance(ty::InstanceDef::ConstructCoroutineInClosureShim {
coroutine_closure_def_id,
target_kind: ty::ClosureKind::FnMut,
});
body.pass_count = 0;
dump_mir(tcx, false, "coroutine_closure_by_mut", &0, &body, |_, _| Ok(()));
body
}

View file

@ -983,6 +983,8 @@ fn visit_instance_use<'tcx>(
| ty::InstanceDef::VTableShim(..)
| ty::InstanceDef::ReifyShim(..)
| ty::InstanceDef::ClosureOnceShim { .. }
| ty::InstanceDef::ConstructCoroutineInClosureShim { .. }
| ty::InstanceDef::CoroutineKindShim { .. }
| ty::InstanceDef::Item(..)
| ty::InstanceDef::FnPtrShim(..)
| ty::InstanceDef::CloneShim(..)

View file

@ -620,6 +620,8 @@ fn characteristic_def_id_of_mono_item<'tcx>(
| ty::InstanceDef::ReifyShim(..)
| ty::InstanceDef::FnPtrShim(..)
| ty::InstanceDef::ClosureOnceShim { .. }
| ty::InstanceDef::ConstructCoroutineInClosureShim { .. }
| ty::InstanceDef::CoroutineKindShim { .. }
| ty::InstanceDef::Intrinsic(..)
| ty::InstanceDef::DropGlue(..)
| ty::InstanceDef::Virtual(..)
@ -783,6 +785,8 @@ fn mono_item_visibility<'tcx>(
| InstanceDef::Virtual(..)
| InstanceDef::Intrinsic(..)
| InstanceDef::ClosureOnceShim { .. }
| InstanceDef::ConstructCoroutineInClosureShim { .. }
| InstanceDef::CoroutineKindShim { .. }
| InstanceDef::DropGlue(..)
| InstanceDef::CloneShim(..)
| InstanceDef::FnPtrAddrShim(..) => return Visibility::Hidden,

View file

@ -332,7 +332,8 @@ fn fold_ty(&mut self, t: I::Ty) -> I::Ty
| ty::FnDef(_, _)
| ty::FnPtr(_)
| ty::Dynamic(_, _, _)
| ty::Closure(_, _)
| ty::Closure(..)
| ty::CoroutineClosure(..)
| ty::Coroutine(_, _)
| ty::CoroutineWitness(..)
| ty::Never

View file

@ -719,6 +719,11 @@ fn compute(&mut self, body: &hir::Body<'_>, hir_id: HirId) -> LiveNode {
ty::ClosureKind::FnMut => {}
ty::ClosureKind::FnOnce => return succ,
},
ty::CoroutineClosure(_def_id, args) => match args.as_coroutine_closure().kind() {
ty::ClosureKind::Fn => {}
ty::ClosureKind::FnMut => {}
ty::ClosureKind::FnOnce => return succ,
},
ty::Coroutine(..) => return succ,
_ => {
span_bug!(

View file

@ -423,7 +423,8 @@ pub fn ctors_for_ty(
| ty::FnDef(_, _)
| ty::FnPtr(_)
| ty::Dynamic(_, _, _)
| ty::Closure(_, _)
| ty::Closure(..)
| ty::CoroutineClosure(..)
| ty::Coroutine(_, _)
| ty::Alias(_, _)
| ty::Param(_)

View file

@ -182,6 +182,7 @@ fn visit_ty(&mut self, ty: Ty<'tcx>) -> ControlFlow<V::BreakTy> {
| ty::Foreign(def_id)
| ty::FnDef(def_id, ..)
| ty::Closure(def_id, ..)
| ty::CoroutineClosure(def_id, ..)
| ty::Coroutine(def_id, ..) => {
self.def_id_visitor.visit_def_id(def_id, "type", &ty)?;
if V::SHALLOW {

View file

@ -538,6 +538,9 @@ fn stable(&self, tables: &mut Tables<'_>) -> Self::T {
tables.tcx.coroutine_movability(*def_id).stable(tables),
)
}
mir::AggregateKind::CoroutineClosure(..) => {
todo!("FIXME(async_closures): Lower these to SMIR")
}
}
}
}

View file

@ -383,6 +383,7 @@ fn stable(&self, tables: &mut Tables<'_>) -> Self::T {
tables.closure_def(*def_id),
generic_args.stable(tables),
)),
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),
@ -798,6 +799,8 @@ fn stable(&self, tables: &mut Tables<'_>) -> Self::T {
| ty::InstanceDef::ReifyShim(..)
| ty::InstanceDef::FnPtrAddrShim(..)
| ty::InstanceDef::ClosureOnceShim { .. }
| ty::InstanceDef::ConstructCoroutineInClosureShim { .. }
| ty::InstanceDef::CoroutineKindShim { .. }
| ty::InstanceDef::ThreadLocalShim(..)
| ty::InstanceDef::DropGlue(..)
| ty::InstanceDef::CloneShim(..)

View file

@ -167,6 +167,9 @@
Break,
C,
CStr,
CallFuture,
CallMutFuture,
CallOnceFuture,
Capture,
Center,
Cleanup,
@ -318,6 +321,7 @@
TyCtxt,
TyKind,
Unknown,
Upvars,
Vec,
VecDeque,
Wrapper,
@ -420,6 +424,7 @@
async_closure,
async_fn,
async_fn_in_trait,
async_fn_kind_helper,
async_fn_mut,
async_fn_once,
async_fn_track_caller,

Some files were not shown because too many files have changed in this diff Show more