mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-22 14:55:26 +00:00
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:
commit
4a2fe4491e
@ -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}`
|
||||
|
@ -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 {
|
||||
|
@ -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 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
||||
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 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
||||
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 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
||||
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)
|
||||
|
@ -1091,7 +1091,6 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
||||
body.span,
|
||||
coroutine_kind,
|
||||
hir::CoroutineSource::Fn,
|
||||
None,
|
||||
);
|
||||
|
||||
// FIXME(async_fn_track_caller): Can this be moved above?
|
||||
@ -1113,7 +1112,6 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
||||
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 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
||||
};
|
||||
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,
|
||||
|
@ -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 @@ impl std::fmt::Display for ImplTraitPosition {
|
||||
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",
|
||||
|
@ -858,7 +858,9 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
|
||||
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 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
|
||||
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 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
|
||||
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 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
|
||||
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 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
|
||||
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 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
|
||||
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 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
|
||||
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 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
|
||||
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 }
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -614,7 +614,7 @@ impl UseSpans<'_> {
|
||||
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 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
|
||||
hir::ClosureKind::Coroutine(_) => {
|
||||
CaptureVarCause::PartialMoveUseInCoroutine { var_span, is_partial }
|
||||
}
|
||||
hir::ClosureKind::Closure => {
|
||||
hir::ClosureKind::Closure | hir::ClosureKind::CoroutineClosure(_) => {
|
||||
CaptureVarCause::PartialMoveUseInClosure { var_span, is_partial }
|
||||
}
|
||||
})
|
||||
|
@ -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.
|
||||
|
@ -324,9 +324,13 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> {
|
||||
ty::BoundRegionKind::BrEnv => {
|
||||
let def_ty = self.regioncx.universal_regions().defining_ty;
|
||||
|
||||
let DefiningTy::Closure(_, args) = def_ty else {
|
||||
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 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> {
|
||||
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 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> {
|
||||
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 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> {
|
||||
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 @@ impl<'tcx> MirBorrowckCtxt<'_, 'tcx> {
|
||||
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,
|
||||
|
@ -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 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
|
||||
// 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 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
|
||||
| ty::FnPtr(_)
|
||||
| ty::Dynamic(_, _, _)
|
||||
| ty::Closure(_, _)
|
||||
| ty::CoroutineClosure(_, _)
|
||||
| ty::Coroutine(_, _)
|
||||
| ty::CoroutineWitness(..)
|
||||
| ty::Never
|
||||
@ -1633,7 +1637,10 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
|
||||
return;
|
||||
}
|
||||
}
|
||||
ty::Closure(_, _) | ty::Coroutine(_, _) | ty::Tuple(_) => (),
|
||||
ty::Closure(..)
|
||||
| ty::CoroutineClosure(..)
|
||||
| ty::Coroutine(_, _)
|
||||
| ty::Tuple(_) => (),
|
||||
ty::Bool
|
||||
| ty::Char
|
||||
| ty::Int(_)
|
||||
|
@ -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)
|
||||
|
@ -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 @@ impl<'a, 'tcx> TypeChecker<'a, '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();
|
||||
|
||||
|
@ -808,6 +808,14 @@ impl<'a, 'b, 'tcx> TypeVerifier<'a, 'b, 'tcx> {
|
||||
}),
|
||||
};
|
||||
}
|
||||
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 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
|
||||
}),
|
||||
}
|
||||
}
|
||||
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 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
|
||||
AggregateKind::Tuple => None,
|
||||
AggregateKind::Closure(_, _) => None,
|
||||
AggregateKind::Coroutine(_, _) => None,
|
||||
AggregateKind::CoroutineClosure(_, _) => None,
|
||||
},
|
||||
}
|
||||
}
|
||||
@ -2705,7 +2722,9 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
|
||||
// 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 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
|
||||
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),
|
||||
};
|
||||
|
@ -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 @@ impl<'tcx> DefiningTy<'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 @@ impl<'tcx> DefiningTy<'tcx> {
|
||||
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 @@ impl<'tcx> UniversalRegions<'tcx> {
|
||||
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 @@ impl<'cx, 'tcx> UniversalRegionsBuilder<'cx, '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 @@ impl<'cx, 'tcx> UniversalRegionsBuilder<'cx, 'tcx> {
|
||||
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 @@ impl<'cx, 'tcx> UniversalRegionsBuilder<'cx, 'tcx> {
|
||||
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);
|
||||
|
@ -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());
|
||||
|
@ -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() =>
|
||||
{
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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(_)
|
||||
|
@ -1007,6 +1007,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
||||
| ty::CoroutineWitness(..)
|
||||
| ty::Array(..)
|
||||
| ty::Closure(..)
|
||||
| ty::CoroutineClosure(..)
|
||||
| ty::Never
|
||||
| ty::Error(_) => true,
|
||||
|
||||
|
@ -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
|
||||
|
@ -545,6 +545,8 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
||||
ty::InstanceDef::VTableShim(..)
|
||||
| ty::InstanceDef::ReifyShim(..)
|
||||
| ty::InstanceDef::ClosureOnceShim { .. }
|
||||
| ty::InstanceDef::ConstructCoroutineInClosureShim { .. }
|
||||
| ty::InstanceDef::CoroutineKindShim { .. }
|
||||
| ty::InstanceDef::FnPtrShim(..)
|
||||
| ty::InstanceDef::DropGlue(..)
|
||||
| ty::InstanceDef::CloneShim(..)
|
||||
|
@ -644,6 +644,7 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, '
|
||||
| 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.
|
||||
|
@ -58,6 +58,7 @@ impl<'tcx> MirPass<'tcx> for Validator {
|
||||
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 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
|
||||
};
|
||||
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 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
|
||||
}
|
||||
}
|
||||
}
|
||||
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) {
|
||||
|
@ -51,6 +51,7 @@ impl<'tcx> Printer<'tcx> for AbsolutePathPrinter<'tcx> {
|
||||
| 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, &[]),
|
||||
|
||||
|
@ -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 @@ impl<'hir> Node<'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;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -209,6 +209,7 @@ language_item_table! {
|
||||
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;
|
||||
|
||||
|
@ -171,6 +171,7 @@ impl<'tcx> InherentCollect<'tcx> {
|
||||
}
|
||||
ty::FnDef(..)
|
||||
| ty::Closure(..)
|
||||
| ty::CoroutineClosure(..)
|
||||
| ty::Coroutine(..)
|
||||
| ty::CoroutineWitness(..)
|
||||
| ty::Bound(..)
|
||||
|
@ -244,6 +244,7 @@ fn do_orphan_check_impl<'tcx>(
|
||||
| ty::Tuple(..) => (LocalImpl::Allow, NonlocalImpl::DisallowOther),
|
||||
|
||||
ty::Closure(..)
|
||||
| ty::CoroutineClosure(..)
|
||||
| ty::Coroutine(..)
|
||||
| ty::CoroutineWitness(..)
|
||||
| ty::Bound(..)
|
||||
|
@ -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), .. }) => {
|
||||
|
@ -349,6 +349,13 @@ pub(super) fn generics_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Generics {
|
||||
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 {
|
||||
|
@ -235,8 +235,8 @@ impl<'a, 'tcx> ConstraintContext<'a, 'tcx> {
|
||||
// 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) => {
|
||||
|
@ -141,13 +141,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
return Some(CallStep::Builtin(adjusted_ty));
|
||||
}
|
||||
|
||||
ty::Closure(def_id, args) => {
|
||||
let def_id = def_id.expect_local();
|
||||
|
||||
// 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() {
|
||||
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,
|
||||
@ -160,14 +158,60 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
DeferredCallResolution {
|
||||
call_expr,
|
||||
callee_expr,
|
||||
adjusted_ty,
|
||||
closure_ty: 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 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
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 @@ impl<'a, 'tcx> DeferredCallResolution<'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 @@ impl<'a, 'tcx> DeferredCallResolution<'tcx> {
|
||||
span_bug!(
|
||||
self.call_expr.span,
|
||||
"Expected to find a suitable `Fn`/`FnMut`/`FnOnce` implementation for `{}`",
|
||||
self.adjusted_ty
|
||||
self.closure_ty
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -133,6 +133,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
| ty::FnDef(..)
|
||||
| ty::FnPtr(..)
|
||||
| ty::Closure(..)
|
||||
| ty::CoroutineClosure(..)
|
||||
| ty::Coroutine(..)
|
||||
| ty::Adt(..)
|
||||
| ty::Never
|
||||
|
@ -63,7 +63,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
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 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
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 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
// 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 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
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 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
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 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
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 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
_,
|
||||
))
|
||||
| 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())
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -57,6 +57,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
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`.
|
||||
_ => {
|
||||
|
@ -170,9 +170,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
) {
|
||||
// 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 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
};
|
||||
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 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
|
||||
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 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
}
|
||||
}
|
||||
|
||||
// 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 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
};
|
||||
|
||||
// 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
|
||||
|
@ -414,6 +414,7 @@ impl<'cx, 'tcx> TypeFolder<TyCtxt<'tcx>> for Canonicalizer<'cx, 'tcx> {
|
||||
}
|
||||
|
||||
ty::Closure(..)
|
||||
| ty::CoroutineClosure(..)
|
||||
| ty::Coroutine(..)
|
||||
| ty::CoroutineWitness(..)
|
||||
| ty::Bool
|
||||
|
@ -2839,7 +2839,11 @@ impl<'tcx> ObligationCauseExt<'tcx> for ObligationCause<'tcx> {
|
||||
// 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 @@ impl<'tcx> ObligationCauseExt<'tcx> for ObligationCause<'tcx> {
|
||||
// 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 => {
|
||||
|
@ -883,7 +883,10 @@ impl<'a, 'tcx> FindInferSourceVisitor<'a, 'tcx> {
|
||||
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.
|
||||
//
|
||||
|
@ -228,7 +228,10 @@ impl<T> Trait<T> for 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 @@ impl<T> Trait<T> for 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\
|
||||
|
@ -1538,9 +1538,13 @@ impl<'tcx> InferCtxt<'tcx> {
|
||||
/// 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()
|
||||
}
|
||||
|
||||
|
@ -456,6 +456,17 @@ where
|
||||
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.
|
||||
|
@ -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();
|
||||
|
@ -1435,6 +1435,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
|
||||
| ty::Bound(..)
|
||||
| ty::Error(_)
|
||||
| ty::Closure(..)
|
||||
| ty::CoroutineClosure(..)
|
||||
| ty::Coroutine(..)
|
||||
| ty::CoroutineWitness(..)
|
||||
| ty::Placeholder(..)
|
||||
|
@ -355,7 +355,7 @@ impl<'tcx> LateLintPass<'tcx> for UnusedResults {
|
||||
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) {
|
||||
|
@ -23,7 +23,7 @@ impl<'tcx> TyCtxt<'tcx> {
|
||||
})
|
||||
}
|
||||
|
||||
/// 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 @@ impl<'tcx> TyCtxt<'tcx> {
|
||||
}
|
||||
}
|
||||
|
||||
/// 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> {
|
||||
|
@ -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 @@ impl<'tcx> CoroutineInfo<'tcx> {
|
||||
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 @@ impl<'tcx> 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)
|
||||
|
@ -402,6 +402,8 @@ impl<'tcx> CodegenUnit<'tcx> {
|
||||
| InstanceDef::FnPtrShim(..)
|
||||
| InstanceDef::Virtual(..)
|
||||
| InstanceDef::ClosureOnceShim { .. }
|
||||
| InstanceDef::ConstructCoroutineInClosureShim { .. }
|
||||
| InstanceDef::CoroutineKindShim { .. }
|
||||
| InstanceDef::DropGlue(..)
|
||||
| InstanceDef::CloneShim(..)
|
||||
| InstanceDef::ThreadLocalShim(..)
|
||||
|
@ -990,7 +990,8 @@ impl<'tcx> Debug for Rvalue<'tcx> {
|
||||
})
|
||||
}
|
||||
|
||||
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),)
|
||||
|
@ -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)]
|
||||
|
@ -202,6 +202,9 @@ impl<'tcx> Rvalue<'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,
|
||||
|
@ -345,6 +345,8 @@ macro_rules! make_mir_visitor {
|
||||
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 @@ macro_rules! make_mir_visitor {
|
||||
) => {
|
||||
self.visit_args(coroutine_args, location);
|
||||
}
|
||||
AggregateKind::CoroutineClosure(
|
||||
_,
|
||||
coroutine_closure_args,
|
||||
) => {
|
||||
self.visit_args(coroutine_closure_args, location);
|
||||
}
|
||||
}
|
||||
|
||||
for operand in operands {
|
||||
|
@ -755,6 +755,11 @@ rustc_queries! {
|
||||
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
|
||||
|
@ -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,
|
||||
|
@ -1544,6 +1544,7 @@ impl<'tcx> TyCtxt<'tcx> {
|
||||
CoroutineWitness,
|
||||
Dynamic,
|
||||
Closure,
|
||||
CoroutineClosure,
|
||||
Tuple,
|
||||
Bound,
|
||||
Param,
|
||||
|
@ -299,7 +299,7 @@ impl<'tcx> Ty<'tcx> {
|
||||
},
|
||||
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()
|
||||
}
|
||||
|
@ -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 @@ impl DeepRejectCtxt {
|
||||
| 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 @@ impl DeepRejectCtxt {
|
||||
},
|
||||
|
||||
// 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,
|
||||
|
@ -136,6 +136,22 @@ impl FlagComputation {
|
||||
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);
|
||||
|
@ -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 @@ impl<'tcx> GenericArgs<'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;
|
||||
|
@ -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 @@ impl<'tcx> InstanceDef<'tcx> {
|
||||
| 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 @@ impl<'tcx> InstanceDef<'tcx> {
|
||||
| InstanceDef::Virtual(..)
|
||||
| InstanceDef::Intrinsic(..)
|
||||
| InstanceDef::ClosureOnceShim { .. }
|
||||
| ty::InstanceDef::ConstructCoroutineInClosureShim { .. }
|
||||
| ty::InstanceDef::CoroutineKindShim { .. }
|
||||
| InstanceDef::DropGlue(..)
|
||||
| InstanceDef::CloneShim(..)
|
||||
| InstanceDef::FnPtrAddrShim(..) => None,
|
||||
@ -282,6 +311,8 @@ impl<'tcx> InstanceDef<'tcx> {
|
||||
| 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 @@ impl<'tcx> Instance<'tcx> {
|
||||
};
|
||||
|
||||
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
|
||||
|
@ -906,6 +906,12 @@ where
|
||||
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()
|
||||
|
@ -105,9 +105,10 @@ pub use self::region::{
|
||||
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 @@ impl<'tcx> TyCtxt<'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(..)
|
||||
|
@ -3,6 +3,7 @@ use crate::ty::{self, Ty, TyCtxt};
|
||||
|
||||
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 @@ pub trait Printer<'tcx>: Sized {
|
||||
parent_args = &args[..generics.parent_count.min(args.len())];
|
||||
|
||||
match key.disambiguated_data.data {
|
||||
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.
|
||||
DefPathData::Closure => {}
|
||||
}
|
||||
}
|
||||
// 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),
|
||||
|
@ -874,6 +874,48 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write {
|
||||
}
|
||||
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), "]"),
|
||||
}
|
||||
|
@ -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))
|
||||
|
@ -584,6 +584,9 @@ impl<'tcx> TypeSuperFoldable<TyCtxt<'tcx>> for Ty<'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 @@ impl<'tcx> TypeSuperVisitable<TyCtxt<'tcx>> for Ty<'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
|
||||
|
@ -36,6 +36,7 @@ use rustc_type_ir::TyKind as IrTyKind;
|
||||
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 @@ impl<'tcx> ClosureArgs<'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 @@ impl<'tcx> CoroutineArgs<'tcx> {
|
||||
/// 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 {
|
||||
[
|
||||
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 @@ impl<'tcx> CoroutineArgs<'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 @@ impl<'tcx> CoroutineArgs<'tcx> {
|
||||
pub enum UpvarArgs<'tcx> {
|
||||
Closure(GenericArgsRef<'tcx>),
|
||||
Coroutine(GenericArgsRef<'tcx>),
|
||||
CoroutineClosure(GenericArgsRef<'tcx>),
|
||||
}
|
||||
|
||||
impl<'tcx> UpvarArgs<'tcx> {
|
||||
@ -490,6 +788,7 @@ impl<'tcx> UpvarArgs<'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 @@ impl<'tcx> UpvarArgs<'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 @@ impl<'tcx> Ty<'tcx> {
|
||||
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 @@ impl<'tcx> Ty<'tcx> {
|
||||
) -> 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 @@ impl<'tcx> Ty<'tcx> {
|
||||
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> Ty<'tcx> {
|
||||
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 @@ impl<'tcx> Ty<'tcx> {
|
||||
| ty::FnPtr(..)
|
||||
| ty::Dynamic(..)
|
||||
| ty::Closure(..)
|
||||
| ty::CoroutineClosure(..)
|
||||
| ty::CoroutineWitness(..)
|
||||
| ty::Never
|
||||
| ty::Tuple(_)
|
||||
@ -1980,6 +2300,7 @@ impl<'tcx> Ty<'tcx> {
|
||||
| ty::CoroutineWitness(..)
|
||||
| ty::Array(..)
|
||||
| ty::Closure(..)
|
||||
| ty::CoroutineClosure(..)
|
||||
| ty::Never
|
||||
| ty::Error(_)
|
||||
// Extern types have metadata = ().
|
||||
@ -2034,7 +2355,7 @@ impl<'tcx> Ty<'tcx> {
|
||||
|
||||
// "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 @@ impl<'tcx> Ty<'tcx> {
|
||||
| ty::CoroutineWitness(..)
|
||||
| ty::Array(..)
|
||||
| ty::Closure(..)
|
||||
| ty::CoroutineClosure(..)
|
||||
| ty::Never
|
||||
| ty::Error(_) => true,
|
||||
|
||||
@ -2140,7 +2462,7 @@ impl<'tcx> Ty<'tcx> {
|
||||
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 @@ impl<'tcx> Ty<'tcx> {
|
||||
| FnPtr(_)
|
||||
| Dynamic(_, _, _)
|
||||
| Closure(_, _)
|
||||
| CoroutineClosure(_, _)
|
||||
| Coroutine(_, _)
|
||||
| CoroutineWitness(..)
|
||||
| Never
|
||||
|
@ -1119,6 +1119,7 @@ impl<'tcx> Ty<'tcx> {
|
||||
ty::Adt(..)
|
||||
| ty::Bound(..)
|
||||
| ty::Closure(..)
|
||||
| ty::CoroutineClosure(..)
|
||||
| ty::Dynamic(..)
|
||||
| ty::Foreign(_)
|
||||
| ty::Coroutine(..)
|
||||
@ -1158,6 +1159,7 @@ impl<'tcx> Ty<'tcx> {
|
||||
ty::Adt(..)
|
||||
| ty::Bound(..)
|
||||
| ty::Closure(..)
|
||||
| ty::CoroutineClosure(..)
|
||||
| ty::Dynamic(..)
|
||||
| ty::Foreign(_)
|
||||
| ty::Coroutine(..)
|
||||
@ -1280,7 +1282,11 @@ impl<'tcx> Ty<'tcx> {
|
||||
// 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),
|
||||
|
||||
|
@ -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) => {
|
||||
|
@ -483,6 +483,9 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||
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))
|
||||
}
|
||||
|
@ -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 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||
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,
|
||||
};
|
||||
|
||||
|
@ -556,6 +556,9 @@ impl<'tcx> Cx<'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);
|
||||
}
|
||||
|
@ -127,10 +127,24 @@ impl<'tcx> Cx<'tcx> {
|
||||
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 {
|
||||
|
@ -861,6 +861,9 @@ where
|
||||
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.
|
||||
|
@ -154,7 +154,8 @@ impl<'b, 'a, 'tcx, F: Fn(Ty<'tcx>) -> bool> Gatherer<'b, 'a, 'tcx, F> {
|
||||
| ty::FnDef(_, _)
|
||||
| ty::FnPtr(_)
|
||||
| ty::Dynamic(_, _, _)
|
||||
| ty::Closure(_, _)
|
||||
| ty::Closure(..)
|
||||
| ty::CoroutineClosure(..)
|
||||
| ty::Coroutine(_, _)
|
||||
| ty::CoroutineWitness(..)
|
||||
| ty::Never
|
||||
@ -177,7 +178,10 @@ impl<'b, 'a, 'tcx, F: Fn(Ty<'tcx>) -> bool> Gatherer<'b, 'a, 'tcx, F> {
|
||||
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(_)
|
||||
|
@ -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);
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
@ -39,6 +39,7 @@ impl<'tcx> MirPass<'tcx> for AbortUnwindingCalls {
|
||||
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),
|
||||
};
|
||||
|
@ -128,7 +128,9 @@ impl<'tcx> Visitor<'tcx> for UnsafetyChecker<'_, 'tcx> {
|
||||
),
|
||||
}
|
||||
}
|
||||
&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);
|
||||
|
@ -607,7 +607,8 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
|
||||
AggregateKind::Array(_)
|
||||
| AggregateKind::Tuple
|
||||
| AggregateKind::Closure(_, _)
|
||||
| AggregateKind::Coroutine(_, _) => VariantIdx::new(0),
|
||||
| AggregateKind::Coroutine(_, _)
|
||||
| AggregateKind::CoroutineClosure(_, _) => VariantIdx::new(0),
|
||||
},
|
||||
},
|
||||
|
||||
|
@ -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;
|
||||
|
156
compiler/rustc_mir_transform/src/coroutine/by_move_body.rs
Normal file
156
compiler/rustc_mir_transform/src/coroutine/by_move_body.rs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
@ -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,
|
||||
|
@ -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"),
|
||||
|
||||
|
@ -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),
|
||||
};
|
||||
|
@ -861,9 +861,10 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
|
||||
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 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
|
||||
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) => {
|
||||
|
@ -317,6 +317,8 @@ impl<'tcx> Inliner<'tcx> {
|
||||
| InstanceDef::ReifyShim(_)
|
||||
| InstanceDef::FnPtrShim(..)
|
||||
| InstanceDef::ClosureOnceShim { .. }
|
||||
| InstanceDef::ConstructCoroutineInClosureShim { .. }
|
||||
| InstanceDef::CoroutineKindShim { .. }
|
||||
| InstanceDef::DropGlue(..)
|
||||
| InstanceDef::CloneShim(..)
|
||||
| InstanceDef::ThreadLocalShim(..)
|
||||
|
@ -87,6 +87,8 @@ pub(crate) fn mir_callgraph_reachable<'tcx>(
|
||||
| InstanceDef::ReifyShim(_)
|
||||
| InstanceDef::FnPtrShim(..)
|
||||
| InstanceDef::ClosureOnceShim { .. }
|
||||
| InstanceDef::ConstructCoroutineInClosureShim { .. }
|
||||
| InstanceDef::CoroutineKindShim { .. }
|
||||
| InstanceDef::ThreadLocalShim { .. }
|
||||
| InstanceDef::CloneShim(..) => {}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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) {
|
||||
|
@ -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
|
||||
|
@ -3,8 +3,8 @@ use rustc_hir::def_id::DefId;
|
||||
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
|
||||
}
|
||||
|
@ -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(..)
|
||||
|
@ -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,
|
||||
|
@ -332,7 +332,8 @@ impl<Infcx: InferCtxtLike<Interner = I>, I: Interner> TypeFolder<I>
|
||||
| ty::FnDef(_, _)
|
||||
| ty::FnPtr(_)
|
||||
| ty::Dynamic(_, _, _)
|
||||
| ty::Closure(_, _)
|
||||
| ty::Closure(..)
|
||||
| ty::CoroutineClosure(..)
|
||||
| ty::Coroutine(_, _)
|
||||
| ty::CoroutineWitness(..)
|
||||
| ty::Never
|
||||
|
@ -719,6 +719,11 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
|
||||
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!(
|
||||
|
@ -423,7 +423,8 @@ impl<'p, 'tcx> RustcMatchCheckCtxt<'p, 'tcx> {
|
||||
| ty::FnDef(_, _)
|
||||
| ty::FnPtr(_)
|
||||
| ty::Dynamic(_, _, _)
|
||||
| ty::Closure(_, _)
|
||||
| ty::Closure(..)
|
||||
| ty::CoroutineClosure(..)
|
||||
| ty::Coroutine(_, _)
|
||||
| ty::Alias(_, _)
|
||||
| ty::Param(_)
|
||||
|
@ -182,6 +182,7 @@ where
|
||||
| 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 {
|
||||
|
@ -538,6 +538,9 @@ impl<'tcx> Stable<'tcx> for mir::AggregateKind<'tcx> {
|
||||
tables.tcx.coroutine_movability(*def_id).stable(tables),
|
||||
)
|
||||
}
|
||||
mir::AggregateKind::CoroutineClosure(..) => {
|
||||
todo!("FIXME(async_closures): Lower these to SMIR")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -383,6 +383,7 @@ impl<'tcx> Stable<'tcx> for ty::TyKind<'tcx> {
|
||||
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 @@ impl<'tcx> Stable<'tcx> for ty::Instance<'tcx> {
|
||||
| ty::InstanceDef::ReifyShim(..)
|
||||
| ty::InstanceDef::FnPtrAddrShim(..)
|
||||
| ty::InstanceDef::ClosureOnceShim { .. }
|
||||
| ty::InstanceDef::ConstructCoroutineInClosureShim { .. }
|
||||
| ty::InstanceDef::CoroutineKindShim { .. }
|
||||
| ty::InstanceDef::ThreadLocalShim(..)
|
||||
| ty::InstanceDef::DropGlue(..)
|
||||
| ty::InstanceDef::CloneShim(..)
|
||||
|
@ -167,6 +167,9 @@ symbols! {
|
||||
Break,
|
||||
C,
|
||||
CStr,
|
||||
CallFuture,
|
||||
CallMutFuture,
|
||||
CallOnceFuture,
|
||||
Capture,
|
||||
Center,
|
||||
Cleanup,
|
||||
@ -318,6 +321,7 @@ symbols! {
|
||||
TyCtxt,
|
||||
TyKind,
|
||||
Unknown,
|
||||
Upvars,
|
||||
Vec,
|
||||
VecDeque,
|
||||
Wrapper,
|
||||
@ -420,6 +424,7 @@ symbols! {
|
||||
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
Loading…
Reference in New Issue
Block a user