Skip to content

Commit 60e04d1

Browse files
committed
Compute generator saved locals on MIR.
1 parent 400cb9a commit 60e04d1

File tree

18 files changed

+392
-19
lines changed

18 files changed

+392
-19
lines changed

compiler/rustc_hir_analysis/src/check/check.rs

Lines changed: 35 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ use rustc_hir::{ItemKind, Node, PathSegment};
1414
use rustc_infer::infer::opaque_types::ConstrainOpaqueTypeRegionVisitor;
1515
use rustc_infer::infer::outlives::env::OutlivesEnvironment;
1616
use rustc_infer::infer::{DefiningAnchor, RegionVariableOrigin, TyCtxtInferExt};
17-
use rustc_infer::traits::Obligation;
17+
use rustc_infer::traits::{Obligation, TraitEngineExt as _};
1818
use rustc_lint::builtin::REPR_TRANSPARENT_EXTERNAL_PRIVATE_FIELDS;
1919
use rustc_middle::hir::nested_filter;
2020
use rustc_middle::middle::stability::EvalResult;
@@ -28,7 +28,7 @@ use rustc_span::{self, Span};
2828
use rustc_target::spec::abi::Abi;
2929
use rustc_trait_selection::traits::error_reporting::on_unimplemented::OnUnimplementedDirective;
3030
use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt as _;
31-
use rustc_trait_selection::traits::{self, ObligationCtxt};
31+
use rustc_trait_selection::traits::{self, ObligationCtxt, TraitEngine, TraitEngineExt as _};
3232

3333
use std::ops::ControlFlow;
3434

@@ -1460,7 +1460,8 @@ fn opaque_type_cycle_error(
14601460
for def_id in visitor.opaques {
14611461
let ty_span = tcx.def_span(def_id);
14621462
if !seen.contains(&ty_span) {
1463-
err.span_label(ty_span, &format!("returning this opaque type `{ty}`"));
1463+
let descr = if ty.is_impl_trait() { "opaque " } else { "" };
1464+
err.span_label(ty_span, &format!("returning this {descr}type `{ty}`"));
14641465
seen.insert(ty_span);
14651466
}
14661467
err.span_label(sp, &format!("returning here with type `{ty}`"));
@@ -1507,3 +1508,34 @@ fn opaque_type_cycle_error(
15071508
}
15081509
err.emit()
15091510
}
1511+
1512+
pub(super) fn check_generator_obligations(tcx: TyCtxt<'_>, def_id: LocalDefId) {
1513+
debug_assert!(tcx.sess.opts.unstable_opts.drop_tracking_mir);
1514+
debug_assert!(matches!(tcx.def_kind(def_id), DefKind::Generator));
1515+
1516+
let typeck = tcx.typeck(def_id);
1517+
let param_env = tcx.param_env(def_id);
1518+
1519+
let generator_interior_predicates = &typeck.generator_interior_predicates[&def_id];
1520+
debug!(?generator_interior_predicates);
1521+
1522+
let infcx = tcx
1523+
.infer_ctxt()
1524+
// typeck writeback gives us predicates with their regions erased.
1525+
// As borrowck already has checked lifetimes, we do not need to do it again.
1526+
.ignoring_regions()
1527+
// Bind opaque types to `def_id` as they should have been checked by borrowck.
1528+
.with_opaque_type_inference(DefiningAnchor::Bind(def_id))
1529+
.build();
1530+
1531+
let mut fulfillment_cx = <dyn TraitEngine<'_>>::new(infcx.tcx);
1532+
for (predicate, cause) in generator_interior_predicates {
1533+
let obligation = Obligation::new(tcx, cause.clone(), param_env, *predicate);
1534+
fulfillment_cx.register_predicate_obligation(&infcx, obligation);
1535+
}
1536+
let errors = fulfillment_cx.select_all_or_error(&infcx);
1537+
debug!(?errors);
1538+
if !errors.is_empty() {
1539+
infcx.err_ctxt().report_fulfillment_errors(&errors, None);
1540+
}
1541+
}

compiler/rustc_hir_analysis/src/check/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,7 @@ pub fn provide(providers: &mut Providers) {
105105
region_scope_tree,
106106
collect_return_position_impl_trait_in_trait_tys,
107107
compare_impl_const: compare_impl_item::compare_impl_const_raw,
108+
check_generator_obligations: check::check_generator_obligations,
108109
..*providers
109110
};
110111
}

compiler/rustc_hir_typeck/src/check.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,7 @@ pub(super) fn check_fn<'a, 'tcx>(
130130
let gen_ty = if let (Some(_), Some(gen_kind)) = (can_be_generator, body.generator_kind) {
131131
let interior = fcx
132132
.next_ty_var(TypeVariableOrigin { kind: TypeVariableOriginKind::MiscVariable, span });
133-
fcx.deferred_generator_interiors.borrow_mut().push((body.id(), interior, gen_kind));
133+
fcx.deferred_generator_interiors.borrow_mut().push((fn_id, body.id(), interior, gen_kind));
134134

135135
let (resume_ty, yield_ty) = fcx.resume_yield_tys.unwrap();
136136
Some(GeneratorTypes {

compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs

Lines changed: 59 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -517,10 +517,67 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
517517
}
518518

519519
pub(in super::super) fn resolve_generator_interiors(&self, def_id: DefId) {
520+
if self.tcx.sess.opts.unstable_opts.drop_tracking_mir {
521+
self.save_generator_interior_predicates(def_id);
522+
return;
523+
}
524+
525+
self.select_obligations_where_possible(|_| {});
526+
520527
let mut generators = self.deferred_generator_interiors.borrow_mut();
521-
for (body_id, interior, kind) in generators.drain(..) {
522-
self.select_obligations_where_possible(|_| {});
528+
for (_, body_id, interior, kind) in generators.drain(..) {
523529
crate::generator_interior::resolve_interior(self, def_id, body_id, interior, kind);
530+
self.select_obligations_where_possible(|_| {});
531+
}
532+
}
533+
534+
/// Unify the inference variables corresponding to generator witnesses, and save all the
535+
/// predicates that were stalled on those inference variables.
536+
///
537+
/// This process allows to conservatively save all predicates that do depend on the generator
538+
/// interior types, for later processing by `check_generator_obligations`.
539+
///
540+
/// We must not attempt to select obligations after this method has run, or risk query cycle
541+
/// ICE.
542+
#[instrument(level = "debug", skip(self))]
543+
fn save_generator_interior_predicates(&self, def_id: DefId) {
544+
// Try selecting all obligations that are not blocked on inference variables.
545+
// Once we start unifying generator witnesses, trying to select obligations on them will
546+
// trigger query cycle ICEs, as doing so requires MIR.
547+
self.select_obligations_where_possible(|_| {});
548+
549+
let generators = std::mem::take(&mut *self.deferred_generator_interiors.borrow_mut());
550+
debug!(?generators);
551+
552+
for &(expr_hir_id, body_id, interior, _) in generators.iter() {
553+
let expr_def_id = self.tcx.hir().local_def_id(expr_hir_id);
554+
debug!(?expr_def_id);
555+
556+
// Create the `GeneratorWitness` type that we will unify with `interior`.
557+
let substs = ty::InternalSubsts::identity_for_item(
558+
self.tcx,
559+
self.tcx.typeck_root_def_id(expr_def_id.to_def_id()),
560+
);
561+
let witness = self.tcx.mk_generator_witness_mir(expr_def_id.to_def_id(), substs);
562+
563+
// Unify `interior` with `witness` and collect all the resulting obligations.
564+
let span = self.tcx.hir().body(body_id).value.span;
565+
let ok = self
566+
.at(&self.misc(span), self.param_env)
567+
.eq(interior, witness)
568+
.expect("Failed to unify generator interior type");
569+
let mut obligations = ok.obligations;
570+
571+
// Also collect the obligations that were unstalled by this unification.
572+
obligations
573+
.extend(self.fulfillment_cx.borrow_mut().drain_unstalled_obligations(&self.infcx));
574+
575+
let obligations = obligations.into_iter().map(|o| (o.predicate, o.cause)).collect();
576+
debug!(?obligations);
577+
self.typeck_results
578+
.borrow_mut()
579+
.generator_interior_predicates
580+
.insert(expr_def_id, obligations);
524581
}
525582
}
526583

compiler/rustc_hir_typeck/src/inherited.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ pub struct Inherited<'tcx> {
5656
pub(super) deferred_asm_checks: RefCell<Vec<(&'tcx hir::InlineAsm<'tcx>, hir::HirId)>>,
5757

5858
pub(super) deferred_generator_interiors:
59-
RefCell<Vec<(hir::BodyId, Ty<'tcx>, hir::GeneratorKind)>>,
59+
RefCell<Vec<(hir::HirId, hir::BodyId, Ty<'tcx>, hir::GeneratorKind)>>,
6060

6161
pub(super) body_id: Option<hir::BodyId>,
6262

compiler/rustc_hir_typeck/src/lib.rs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -294,7 +294,6 @@ fn typeck_with_fallback<'tcx>(
294294
// Before the generator analysis, temporary scopes shall be marked to provide more
295295
// precise information on types to be captured.
296296
fcx.resolve_rvalue_scopes(def_id.to_def_id());
297-
fcx.resolve_generator_interiors(def_id.to_def_id());
298297

299298
for (ty, span, code) in fcx.deferred_sized_obligations.borrow_mut().drain(..) {
300299
let ty = fcx.normalize(span, ty);
@@ -303,6 +302,13 @@ fn typeck_with_fallback<'tcx>(
303302

304303
fcx.select_obligations_where_possible(|_| {});
305304

305+
debug!(pending_obligations = ?fcx.fulfillment_cx.borrow().pending_obligations());
306+
307+
// This must be the last thing before `report_ambiguity_errors`.
308+
fcx.resolve_generator_interiors(def_id.to_def_id());
309+
310+
debug!(pending_obligations = ?fcx.fulfillment_cx.borrow().pending_obligations());
311+
306312
if let None = fcx.infcx.tainted_by_errors() {
307313
fcx.report_ambiguity_errors();
308314
}

compiler/rustc_hir_typeck/src/writeback.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -545,6 +545,10 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> {
545545
assert_eq!(fcx_typeck_results.hir_owner, self.typeck_results.hir_owner);
546546
self.typeck_results.generator_interior_types =
547547
fcx_typeck_results.generator_interior_types.clone();
548+
for (&expr_def_id, predicates) in fcx_typeck_results.generator_interior_predicates.iter() {
549+
let predicates = self.resolve(predicates.clone(), &self.fcx.tcx.def_span(expr_def_id));
550+
self.typeck_results.generator_interior_predicates.insert(expr_def_id, predicates);
551+
}
548552
}
549553

550554
#[instrument(skip(self), level = "debug")]

compiler/rustc_infer/src/traits/engine.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,14 @@ pub trait TraitEngine<'tcx>: 'tcx {
4141
fn collect_remaining_errors(&mut self) -> Vec<FulfillmentError<'tcx>>;
4242

4343
fn pending_obligations(&self) -> Vec<PredicateObligation<'tcx>>;
44+
45+
/// Among all pending obligations, collect those are stalled on a inference variable which has
46+
/// changed since the last call to `select_where_possible`. Those obligations are marked as
47+
/// successful and returned.
48+
fn drain_unstalled_obligations(
49+
&mut self,
50+
infcx: &InferCtxt<'tcx>,
51+
) -> Vec<PredicateObligation<'tcx>>;
4452
}
4553

4654
pub trait TraitEngineExt<'tcx> {

compiler/rustc_interface/src/passes.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -893,6 +893,15 @@ fn analysis(tcx: TyCtxt<'_>, (): ()) -> Result<()> {
893893
}
894894
});
895895

896+
if tcx.sess.opts.unstable_opts.drop_tracking_mir {
897+
tcx.hir().par_body_owners(|def_id| {
898+
if let rustc_hir::def::DefKind::Generator = tcx.def_kind(def_id) {
899+
tcx.ensure().mir_generator_witnesses(def_id);
900+
tcx.ensure().check_generator_obligations(def_id);
901+
}
902+
});
903+
}
904+
896905
sess.time("layout_testing", || layout_test::test_layout(tcx));
897906

898907
// Avoid overwhelming user with errors if borrow checking failed.

compiler/rustc_middle/src/query/mod.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -478,6 +478,10 @@ rustc_queries! {
478478
separate_provide_extern
479479
}
480480

481+
query check_generator_obligations(key: LocalDefId) {
482+
desc { |tcx| "verify auto trait bounds for generator interior type `{}`", tcx.def_path_str(key.to_def_id()) }
483+
}
484+
481485
/// MIR after our optimization passes have run. This is MIR that is ready
482486
/// for codegen. This is also the only query that can fetch non-local MIR, at present.
483487
query optimized_mir(key: DefId) -> &'tcx mir::Body<'tcx> {

compiler/rustc_middle/src/ty/typeck_results.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
use crate::{
22
hir::place::Place as HirPlace,
33
infer::canonical::Canonical,
4+
traits::ObligationCause,
45
ty::{
56
self, tls, BindingMode, BoundVar, CanonicalPolyFnSig, ClosureSizeProfileData,
67
GenericArgKind, InternalSubsts, SubstsRef, Ty, UserSubsts,
@@ -193,6 +194,11 @@ pub struct TypeckResults<'tcx> {
193194
/// that are live across the yield of this generator (if a generator).
194195
pub generator_interior_types: ty::Binder<'tcx, Vec<GeneratorInteriorTypeCause<'tcx>>>,
195196

197+
/// Stores the predicates that apply on generator witness types.
198+
/// formatting modified file tests/ui/generator/retain-resume-ref.rs
199+
pub generator_interior_predicates:
200+
FxHashMap<LocalDefId, Vec<(ty::Predicate<'tcx>, ObligationCause<'tcx>)>>,
201+
196202
/// We sometimes treat byte string literals (which are of type `&[u8; N]`)
197203
/// as `&[u8]`, depending on the pattern in which they are used.
198204
/// This hashset records all instances where we behave
@@ -271,6 +277,7 @@ impl<'tcx> TypeckResults<'tcx> {
271277
closure_fake_reads: Default::default(),
272278
rvalue_scopes: Default::default(),
273279
generator_interior_types: ty::Binder::dummy(Default::default()),
280+
generator_interior_predicates: Default::default(),
274281
treat_byte_string_as_slice: Default::default(),
275282
closure_size_eval: Default::default(),
276283
}

compiler/rustc_middle/src/ty/util.rs

Lines changed: 72 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -615,6 +615,36 @@ impl<'tcx> TyCtxt<'tcx> {
615615
}
616616
}
617617

618+
/// Return the set of types that should be taken into accound when checking
619+
/// trait bounds on a generator's internal state.
620+
pub fn generator_hidden_types(
621+
self,
622+
def_id: DefId,
623+
) -> impl Iterator<Item = ty::EarlyBinder<Ty<'tcx>>> {
624+
let generator_layout = &self.mir_generator_witnesses(def_id);
625+
generator_layout
626+
.field_tys
627+
.iter()
628+
.filter(|decl| !decl.is_static_ptr)
629+
.map(|decl| ty::EarlyBinder(decl.ty))
630+
}
631+
632+
/// Normalizes all opaque types in the given value, replacing them
633+
/// with their underlying types.
634+
pub fn expand_opaque_types(self, val: Ty<'tcx>) -> Ty<'tcx> {
635+
let mut visitor = OpaqueTypeExpander {
636+
seen_opaque_tys: FxHashSet::default(),
637+
expanded_cache: FxHashMap::default(),
638+
primary_def_id: None,
639+
found_recursion: false,
640+
found_any_recursion: false,
641+
check_recursion: false,
642+
expand_generators: false,
643+
tcx: self,
644+
};
645+
val.fold_with(&mut visitor)
646+
}
647+
618648
/// Expands the given impl trait type, stopping if the type is recursive.
619649
#[instrument(skip(self), level = "debug", ret)]
620650
pub fn try_expand_impl_trait_type(
@@ -629,6 +659,7 @@ impl<'tcx> TyCtxt<'tcx> {
629659
found_recursion: false,
630660
found_any_recursion: false,
631661
check_recursion: true,
662+
expand_generators: true,
632663
tcx: self,
633664
};
634665

@@ -741,6 +772,7 @@ struct OpaqueTypeExpander<'tcx> {
741772
primary_def_id: Option<DefId>,
742773
found_recursion: bool,
743774
found_any_recursion: bool,
775+
expand_generators: bool,
744776
/// Whether or not to check for recursive opaque types.
745777
/// This is `true` when we're explicitly checking for opaque type
746778
/// recursion, and 'false' otherwise to avoid unnecessary work.
@@ -777,6 +809,37 @@ impl<'tcx> OpaqueTypeExpander<'tcx> {
777809
None
778810
}
779811
}
812+
813+
fn expand_generator(&mut self, def_id: DefId, substs: SubstsRef<'tcx>) -> Option<Ty<'tcx>> {
814+
if self.found_any_recursion {
815+
return None;
816+
}
817+
let substs = substs.fold_with(self);
818+
if !self.check_recursion || self.seen_opaque_tys.insert(def_id) {
819+
let expanded_ty = match self.expanded_cache.get(&(def_id, substs)) {
820+
Some(expanded_ty) => *expanded_ty,
821+
None => {
822+
for bty in self.tcx.generator_hidden_types(def_id) {
823+
let hidden_ty = bty.subst(self.tcx, substs);
824+
self.fold_ty(hidden_ty);
825+
}
826+
let expanded_ty = self.tcx.mk_generator_witness_mir(def_id, substs);
827+
self.expanded_cache.insert((def_id, substs), expanded_ty);
828+
expanded_ty
829+
}
830+
};
831+
if self.check_recursion {
832+
self.seen_opaque_tys.remove(&def_id);
833+
}
834+
Some(expanded_ty)
835+
} else {
836+
// If another opaque type that we contain is recursive, then it
837+
// will report the error, so we don't have to.
838+
self.found_any_recursion = true;
839+
self.found_recursion = def_id == *self.primary_def_id.as_ref().unwrap();
840+
None
841+
}
842+
}
780843
}
781844

782845
impl<'tcx> TypeFolder<'tcx> for OpaqueTypeExpander<'tcx> {
@@ -785,13 +848,19 @@ impl<'tcx> TypeFolder<'tcx> for OpaqueTypeExpander<'tcx> {
785848
}
786849

787850
fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> {
788-
if let ty::Alias(ty::Opaque, ty::AliasTy { def_id, substs, .. }) = *t.kind() {
851+
let mut t = if let ty::Alias(ty::Opaque, ty::AliasTy { def_id, substs, .. }) = *t.kind() {
789852
self.expand_opaque_ty(def_id, substs).unwrap_or(t)
790-
} else if t.has_opaque_types() {
853+
} else if t.has_opaque_types() || t.has_generators() {
791854
t.super_fold_with(self)
792855
} else {
793856
t
857+
};
858+
if self.expand_generators {
859+
if let ty::GeneratorWitnessMIR(def_id, substs) = *t.kind() {
860+
t = self.expand_generator(def_id, substs).unwrap_or(t);
861+
}
794862
}
863+
t
795864
}
796865
}
797866

@@ -1299,6 +1368,7 @@ pub fn reveal_opaque_types_in_bounds<'tcx>(
12991368
found_recursion: false,
13001369
found_any_recursion: false,
13011370
check_recursion: false,
1371+
expand_generators: false,
13021372
tcx,
13031373
};
13041374
val.fold_with(&mut visitor)

compiler/rustc_trait_selection/src/solve/fulfill.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,4 +139,11 @@ impl<'tcx> TraitEngine<'tcx> for FulfillmentCtxt<'tcx> {
139139
fn pending_obligations(&self) -> Vec<PredicateObligation<'tcx>> {
140140
self.obligations.clone()
141141
}
142+
143+
fn drain_unstalled_obligations(
144+
&mut self,
145+
_: &InferCtxt<'tcx>,
146+
) -> Vec<PredicateObligation<'tcx>> {
147+
unimplemented!()
148+
}
142149
}

0 commit comments

Comments
 (0)