Skip to content

Commit 4fd2dde

Browse files
Auto merge of #146562 - cjgillot:resume-ty, r=<try>
[EXPERIMENT] Replace ResumeTy with an unsafe binder ty.
2 parents 52618eb + 20b1084 commit 4fd2dde

24 files changed

+399
-511
lines changed

compiler/rustc_ast_lowering/src/expr.rs

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -927,10 +927,13 @@ impl<'hir> LoweringContext<'_, 'hir> {
927927
hir::LangItem::PinNewUnchecked,
928928
arena_vec![self; ref_mut_awaitee],
929929
);
930-
let get_context = self.expr_call_lang_item_fn_mut(
930+
let get_context = self.expr(
931931
gen_future_span,
932-
hir::LangItem::GetContext,
933-
arena_vec![self; task_context],
932+
hir::ExprKind::UnsafeBinderCast(
933+
UnsafeBinderCastKind::Unwrap,
934+
self.arena.alloc(task_context),
935+
None,
936+
),
934937
);
935938
let call = match await_kind {
936939
FutureKind::Future => self.expr_call_lang_item_fn(

compiler/rustc_borrowck/src/places_conflict.rs

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -307,6 +307,11 @@ fn place_projection_conflict<'tcx>(
307307
debug!("place_element_conflict: DISJOINT-OR-EQ-OPAQUE");
308308
Overlap::EqualOrDisjoint
309309
}
310+
(ProjectionElem::UnwrapUnsafeBinder(_), ProjectionElem::UnwrapUnsafeBinder(_)) => {
311+
// casts to other types may always conflict irrespective of the type being cast to.
312+
debug!("place_element_conflict: DISJOINT-OR-EQ-OPAQUE");
313+
Overlap::EqualOrDisjoint
314+
}
310315
(ProjectionElem::Field(f1, _), ProjectionElem::Field(f2, _)) => {
311316
if f1 == f2 {
312317
// same field (e.g., `a.y` vs. `a.y`) - recur.
@@ -512,6 +517,7 @@ fn place_projection_conflict<'tcx>(
512517
| ProjectionElem::ConstantIndex { .. }
513518
| ProjectionElem::Subtype(_)
514519
| ProjectionElem::OpaqueCast { .. }
520+
| ProjectionElem::UnwrapUnsafeBinder { .. }
515521
| ProjectionElem::Subslice { .. }
516522
| ProjectionElem::Downcast(..),
517523
_,
@@ -520,9 +526,5 @@ fn place_projection_conflict<'tcx>(
520526
pi1_elem,
521527
pi2_elem
522528
),
523-
524-
(ProjectionElem::UnwrapUnsafeBinder(_), _) => {
525-
todo!()
526-
}
527529
}
528530
}

compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -474,6 +474,9 @@ pub(crate) fn spanned_type_di_node<'ll, 'tcx>(
474474
AdtKind::Enum => enums::build_enum_type_di_node(cx, unique_type_id, span),
475475
},
476476
ty::Tuple(_) => build_tuple_type_di_node(cx, unique_type_id),
477+
ty::UnsafeBinder(binder) => {
478+
return type_di_node(cx, cx.tcx.instantiate_bound_regions_with_erased(*binder));
479+
}
477480
_ => bug!("debuginfo: unexpected type in type_di_node(): {:?}", t),
478481
};
479482

compiler/rustc_hir/src/lang_items.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -382,8 +382,7 @@ language_item_table! {
382382

383383
// FIXME(swatinem): the following lang items are used for async lowering and
384384
// should become obsolete eventually.
385-
ResumeTy, sym::ResumeTy, resume_ty, Target::Struct, GenericRequirement::None;
386-
GetContext, sym::get_context, get_context_fn, Target::Fn, GenericRequirement::None;
385+
ResumeTy, sym::ResumeTy, resume_ty, Target::TyAlias, GenericRequirement::None;
387386

388387
Context, sym::Context, context, Target::Struct, GenericRequirement::None;
389388
FuturePoll, sym::poll, future_poll_fn, Target::Method(MethodKind::Trait { body: false }), GenericRequirement::None;

compiler/rustc_middle/src/ty/sty.rs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -909,6 +909,29 @@ impl<'tcx> Ty<'tcx> {
909909
Ty::new_generic_adt(tcx, def_id, ty)
910910
}
911911

912+
/// Creates a `unsafe<'a, 'b> &'a mut Context<'b>` [`Ty`].
913+
pub fn new_resume_ty(tcx: TyCtxt<'tcx>) -> Ty<'tcx> {
914+
let context_did = tcx.require_lang_item(LangItem::Context, DUMMY_SP);
915+
let context_adt_ref = tcx.adt_def(context_did);
916+
917+
let lt = |n| {
918+
ty::Region::new_bound(
919+
tcx,
920+
ty::INNERMOST,
921+
ty::BoundRegion { var: ty::BoundVar::from_u32(n), kind: BoundRegionKind::Anon },
922+
)
923+
};
924+
925+
let context_args = tcx.mk_args(&[lt(1).into()]);
926+
let context_ty = Ty::new_adt(tcx, context_adt_ref, context_args);
927+
let context_mut_ref = Ty::new_mut_ref(tcx, lt(0), context_ty);
928+
let bound_vars = tcx.mk_bound_variable_kinds(&[
929+
BoundVariableKind::Region(BoundRegionKind::Anon),
930+
BoundVariableKind::Region(BoundRegionKind::Anon),
931+
]);
932+
Ty::new_unsafe_binder(tcx, ty::Binder::bind_with_vars(context_mut_ref, bound_vars))
933+
}
934+
912935
/// Creates a `&mut Context<'_>` [`Ty`] with erased lifetimes.
913936
pub fn new_task_context(tcx: TyCtxt<'tcx>) -> Ty<'tcx> {
914937
let context_did = tcx.require_lang_item(LangItem::Context, DUMMY_SP);

compiler/rustc_mir_transform/src/coroutine.rs

Lines changed: 20 additions & 110 deletions
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,9 @@ struct TransformVisitor<'tcx> {
211211
old_yield_ty: Ty<'tcx>,
212212

213213
old_ret_ty: Ty<'tcx>,
214+
215+
/// The rvalue that should be assigned to yield `resume_arg` place.
216+
resume_rvalue: Rvalue<'tcx>,
214217
}
215218

216219
impl<'tcx> TransformVisitor<'tcx> {
@@ -533,100 +536,6 @@ fn replace_local<'tcx>(
533536
new_local
534537
}
535538

536-
/// Transforms the `body` of the coroutine applying the following transforms:
537-
///
538-
/// - Eliminates all the `get_context` calls that async lowering created.
539-
/// - Replace all `Local` `ResumeTy` types with `&mut Context<'_>` (`context_mut_ref`).
540-
///
541-
/// The `Local`s that have their types replaced are:
542-
/// - The `resume` argument itself.
543-
/// - The argument to `get_context`.
544-
/// - The yielded value of a `yield`.
545-
///
546-
/// The `ResumeTy` hides a `&mut Context<'_>` behind an unsafe raw pointer, and the
547-
/// `get_context` function is being used to convert that back to a `&mut Context<'_>`.
548-
///
549-
/// Ideally the async lowering would not use the `ResumeTy`/`get_context` indirection,
550-
/// but rather directly use `&mut Context<'_>`, however that would currently
551-
/// lead to higher-kinded lifetime errors.
552-
/// See <https://github.com/rust-lang/rust/issues/105501>.
553-
///
554-
/// The async lowering step and the type / lifetime inference / checking are
555-
/// still using the `ResumeTy` indirection for the time being, and that indirection
556-
/// is removed here. After this transform, the coroutine body only knows about `&mut Context<'_>`.
557-
fn transform_async_context<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) -> Ty<'tcx> {
558-
let context_mut_ref = Ty::new_task_context(tcx);
559-
560-
// replace the type of the `resume` argument
561-
replace_resume_ty_local(tcx, body, CTX_ARG, context_mut_ref);
562-
563-
let get_context_def_id = tcx.require_lang_item(LangItem::GetContext, body.span);
564-
565-
for bb in body.basic_blocks.indices() {
566-
let bb_data = &body[bb];
567-
if bb_data.is_cleanup {
568-
continue;
569-
}
570-
571-
match &bb_data.terminator().kind {
572-
TerminatorKind::Call { func, .. } => {
573-
let func_ty = func.ty(body, tcx);
574-
if let ty::FnDef(def_id, _) = *func_ty.kind()
575-
&& def_id == get_context_def_id
576-
{
577-
let local = eliminate_get_context_call(&mut body[bb]);
578-
replace_resume_ty_local(tcx, body, local, context_mut_ref);
579-
}
580-
}
581-
TerminatorKind::Yield { resume_arg, .. } => {
582-
replace_resume_ty_local(tcx, body, resume_arg.local, context_mut_ref);
583-
}
584-
_ => {}
585-
}
586-
}
587-
context_mut_ref
588-
}
589-
590-
fn eliminate_get_context_call<'tcx>(bb_data: &mut BasicBlockData<'tcx>) -> Local {
591-
let terminator = bb_data.terminator.take().unwrap();
592-
let TerminatorKind::Call { args, destination, target, .. } = terminator.kind else {
593-
bug!();
594-
};
595-
let [arg] = *Box::try_from(args).unwrap();
596-
let local = arg.node.place().unwrap().local;
597-
598-
let arg = Rvalue::Use(arg.node);
599-
let assign =
600-
Statement::new(terminator.source_info, StatementKind::Assign(Box::new((destination, arg))));
601-
bb_data.statements.push(assign);
602-
bb_data.terminator = Some(Terminator {
603-
source_info: terminator.source_info,
604-
kind: TerminatorKind::Goto { target: target.unwrap() },
605-
});
606-
local
607-
}
608-
609-
#[cfg_attr(not(debug_assertions), allow(unused))]
610-
fn replace_resume_ty_local<'tcx>(
611-
tcx: TyCtxt<'tcx>,
612-
body: &mut Body<'tcx>,
613-
local: Local,
614-
context_mut_ref: Ty<'tcx>,
615-
) {
616-
let local_ty = std::mem::replace(&mut body.local_decls[local].ty, context_mut_ref);
617-
// We have to replace the `ResumeTy` that is used for type and borrow checking
618-
// with `&mut Context<'_>` in MIR.
619-
#[cfg(debug_assertions)]
620-
{
621-
if let ty::Adt(resume_ty_adt, _) = local_ty.kind() {
622-
let expected_adt = tcx.adt_def(tcx.require_lang_item(LangItem::ResumeTy, body.span));
623-
assert_eq!(*resume_ty_adt, expected_adt);
624-
} else {
625-
panic!("expected `ResumeTy`, found `{:?}`", local_ty);
626-
};
627-
}
628-
}
629-
630539
/// Transforms the `body` of the coroutine applying the following transform:
631540
///
632541
/// - Remove the `resume` argument.
@@ -1342,12 +1251,11 @@ fn create_cases<'tcx>(
13421251

13431252
if operation == Operation::Resume {
13441253
// Move the resume argument to the destination place of the `Yield` terminator
1345-
let resume_arg = CTX_ARG;
13461254
statements.push(Statement::new(
13471255
source_info,
13481256
StatementKind::Assign(Box::new((
13491257
point.resume_arg,
1350-
Rvalue::Use(Operand::Move(resume_arg.into())),
1258+
transform.resume_rvalue.clone(),
13511259
))),
13521260
));
13531261
}
@@ -1504,12 +1412,8 @@ impl<'tcx> crate::MirPass<'tcx> for StateTransform {
15041412
) && has_expandable_async_drops(tcx, body, coroutine_ty);
15051413

15061414
// Replace all occurrences of `ResumeTy` with `&mut Context<'_>` within async bodies.
1507-
if matches!(
1508-
coroutine_kind,
1509-
CoroutineKind::Desugared(CoroutineDesugaring::Async | CoroutineDesugaring::AsyncGen, _)
1510-
) {
1511-
let context_mut_ref = transform_async_context(tcx, body);
1512-
expand_async_drops(tcx, body, context_mut_ref, coroutine_kind, coroutine_ty);
1415+
if has_async_drops {
1416+
expand_async_drops(tcx, body, coroutine_kind, coroutine_ty);
15131417

15141418
if let Some(dumper) = MirDumper::new(tcx, "coroutine_async_drop_expand", body) {
15151419
dumper.dump_mir(body);
@@ -1522,22 +1426,27 @@ impl<'tcx> crate::MirPass<'tcx> for StateTransform {
15221426
// This is needed because the resume argument `_2` might be live across a `yield`, in which
15231427
// case there is no `Assign` to it that the transform can turn into a store to the coroutine
15241428
// state. After the yield the slot in the coroutine state would then be uninitialized.
1525-
let resume_local = CTX_ARG;
1526-
let resume_ty = body.local_decls[resume_local].ty;
1527-
let old_resume_local = replace_local(resume_local, resume_ty, body, tcx);
1429+
let resume_ty = body.local_decls[CTX_ARG].ty;
1430+
let old_resume_local = replace_local(CTX_ARG, resume_ty, body, tcx);
15281431

15291432
// When first entering the coroutine, move the resume argument into its old local
15301433
// (which is now a generator interior).
15311434
let source_info = SourceInfo::outermost(body.span);
1532-
let stmts = &mut body.basic_blocks_mut()[START_BLOCK].statements;
1435+
let stmts = &mut body.basic_blocks.as_mut()[START_BLOCK].statements;
1436+
let resume_rvalue = if matches!(
1437+
coroutine_kind,
1438+
CoroutineKind::Desugared(CoroutineDesugaring::Async | CoroutineDesugaring::AsyncGen, _)
1439+
) {
1440+
body.local_decls[CTX_ARG].ty = Ty::new_task_context(tcx);
1441+
Rvalue::WrapUnsafeBinder(Operand::Move(CTX_ARG.into()), resume_ty)
1442+
} else {
1443+
Rvalue::Use(Operand::Move(CTX_ARG.into()))
1444+
};
15331445
stmts.insert(
15341446
0,
15351447
Statement::new(
15361448
source_info,
1537-
StatementKind::Assign(Box::new((
1538-
old_resume_local.into(),
1539-
Rvalue::Use(Operand::Move(resume_local.into())),
1540-
))),
1449+
StatementKind::Assign(Box::new((old_resume_local.into(), resume_rvalue.clone()))),
15411450
),
15421451
);
15431452

@@ -1580,6 +1489,7 @@ impl<'tcx> crate::MirPass<'tcx> for StateTransform {
15801489
discr_ty,
15811490
old_ret_ty,
15821491
old_yield_ty,
1492+
resume_rvalue,
15831493
};
15841494
transform.visit_body(body);
15851495

compiler/rustc_mir_transform/src/coroutine/drop.rs

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -252,10 +252,11 @@ pub(super) fn has_expandable_async_drops<'tcx>(
252252
pub(super) fn expand_async_drops<'tcx>(
253253
tcx: TyCtxt<'tcx>,
254254
body: &mut Body<'tcx>,
255-
context_mut_ref: Ty<'tcx>,
256255
coroutine_kind: hir::CoroutineKind,
257256
coroutine_ty: Ty<'tcx>,
258257
) {
258+
let resume_ty = Ty::new_resume_ty(tcx);
259+
let context_mut_ref = Ty::new_task_context(tcx);
259260
let dropline = gather_dropline_blocks(body);
260261
// Clean drop and async_fut fields if potentially async drop is not expanded (stays sync)
261262
let remove_asyncness = |block: &mut BasicBlockData<'tcx>| {
@@ -323,8 +324,8 @@ pub(super) fn expand_async_drops<'tcx>(
323324

324325
// First state-loop yield for mainline
325326
let context_ref_place =
326-
Place::from(body.local_decls.push(LocalDecl::new(context_mut_ref, source_info.span)));
327-
let arg = Rvalue::Use(Operand::Move(Place::from(CTX_ARG)));
327+
Place::from(body.local_decls.push(LocalDecl::new(resume_ty, source_info.span)));
328+
let arg = Rvalue::Use(Operand::Move(CTX_ARG.into()));
328329
body[bb].statements.push(Statement::new(
329330
source_info,
330331
StatementKind::Assign(Box::new((context_ref_place, arg))),
@@ -358,8 +359,11 @@ pub(super) fn expand_async_drops<'tcx>(
358359
let mut dropline_context_ref: Option<Place<'_>> = None;
359360
let mut dropline_call_bb: Option<BasicBlock> = None;
360361
if !is_dropline_bb {
361-
let context_ref_place2: Place<'_> = Place::from(
362-
body.local_decls.push(LocalDecl::new(context_mut_ref, source_info.span)),
362+
let context_ref_local2 =
363+
body.local_decls.push(LocalDecl::new(resume_ty, source_info.span));
364+
let context_ref_place2 = tcx.mk_place_elem(
365+
context_ref_local2.into(),
366+
PlaceElem::UnwrapUnsafeBinder(context_mut_ref),
363367
);
364368
let drop_yield_block = insert_term_block(body, TerminatorKind::Unreachable); // `kind` replaced later to yield
365369
let (pin_bb2, fut_pin_place2) =
@@ -385,7 +389,7 @@ pub(super) fn expand_async_drops<'tcx>(
385389
);
386390
dropline_transition_bb = Some(pin_bb2);
387391
dropline_yield_bb = Some(drop_yield_block);
388-
dropline_context_ref = Some(context_ref_place2);
392+
dropline_context_ref = Some(context_ref_local2.into());
389393
dropline_call_bb = Some(drop_call_bb);
390394
}
391395

compiler/rustc_mir_transform/src/shim/async_destructor_ctor.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,8 +64,7 @@ pub(super) fn build_async_drop_shim<'tcx>(
6464
let needs_async_drop = drop_ty.needs_async_drop(tcx, typing_env);
6565
let needs_sync_drop = !needs_async_drop && drop_ty.needs_drop(tcx, typing_env);
6666

67-
let resume_adt = tcx.adt_def(tcx.require_lang_item(LangItem::ResumeTy, DUMMY_SP));
68-
let resume_ty = Ty::new_adt(tcx, resume_adt, ty::List::empty());
67+
let resume_ty = Ty::new_resume_ty(tcx);
6968

7069
let fn_sig = ty::Binder::dummy(tcx.mk_fn_sig(
7170
[ty, resume_ty],

compiler/rustc_ty_utils/src/abi.rs

Lines changed: 7 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -159,16 +159,11 @@ fn fn_sig_for_fn_abi<'tcx>(
159159
// with `&mut Context<'_>` which is used in codegen.
160160
#[cfg(debug_assertions)]
161161
{
162-
if let ty::Adt(resume_ty_adt, _) = sig.resume_ty.kind() {
163-
let expected_adt =
164-
tcx.adt_def(tcx.require_lang_item(LangItem::ResumeTy, DUMMY_SP));
165-
assert_eq!(*resume_ty_adt, expected_adt);
166-
} else {
167-
panic!("expected `ResumeTy`, found `{:?}`", sig.resume_ty);
168-
};
162+
let resume_ty = Ty::new_resume_ty(tcx);
163+
assert_eq!(resume_ty, sig.resume_ty);
169164
}
170-
let context_mut_ref = Ty::new_task_context(tcx);
171165

166+
let context_mut_ref = Ty::new_task_context(tcx);
172167
(Some(context_mut_ref), ret_ty)
173168
}
174169
hir::CoroutineKind::Desugared(hir::CoroutineDesugaring::Gen, _) => {
@@ -191,20 +186,15 @@ fn fn_sig_for_fn_abi<'tcx>(
191186
// Yield type is already `Poll<Option<yield_ty>>`
192187
let ret_ty = sig.yield_ty;
193188

194-
// We have to replace the `ResumeTy` that is used for type and borrow checking
189+
// We have to replace the `{:?}` that is used for type and borrow checking
195190
// with `&mut Context<'_>` which is used in codegen.
196191
#[cfg(debug_assertions)]
197192
{
198-
if let ty::Adt(resume_ty_adt, _) = sig.resume_ty.kind() {
199-
let expected_adt =
200-
tcx.adt_def(tcx.require_lang_item(LangItem::ResumeTy, DUMMY_SP));
201-
assert_eq!(*resume_ty_adt, expected_adt);
202-
} else {
203-
panic!("expected `ResumeTy`, found `{:?}`", sig.resume_ty);
204-
};
193+
let resume_ty = Ty::new_resume_ty(tcx);
194+
assert_eq!(resume_ty, sig.resume_ty);
205195
}
206-
let context_mut_ref = Ty::new_task_context(tcx);
207196

197+
let context_mut_ref = Ty::new_task_context(tcx);
208198
(Some(context_mut_ref), ret_ty)
209199
}
210200
hir::CoroutineKind::Coroutine(_) => {

0 commit comments

Comments
 (0)