Skip to content

Commit 6cd6bad

Browse files
committed
Auto merge of #101692 - cjgillot:generator-lazy-witness, r=oli-obk
Compute generator saved locals on MIR Generators are currently type-checked by introducing a `witness` type variable, which is unified with a `GeneratorWitness(captured types)` whose purpose is to ensure that the auto traits correctly migrate from the captured types to the `witness` type. This requires computing the captured types on HIR during type-checking, only to re-do it on MIR later. This PR proposes to drop the HIR-based computation, and only keep the MIR one. This is done in 3 steps. 1. During type-checking, the `witness` type variable is never unified. This allows to stall all the obligations that depend on it until the end of type-checking. Then, the stalled obligations are marked as successful, and saved into the typeck results for later verification. 2. At type-checking writeback, `witness` is replaced by `GeneratorWitnessMIR(def_id, substs)`. From this point on, all trait selection involving `GeneratorWitnessMIR` will fetch the MIR-computed locals, similar to what opaque types do. There is no lifetime to be preserved here: we consider all the lifetimes appearing in this witness type to be higher-ranked. 3. After borrowck, the stashed obligations are verified against the actually computed types, in the `check_generator_obligations` query. If any obligation was wrongly marked as fulfilled in step 1, it should be reported here. There are still many issues: - ~I am not too happy having to filter out some locals from the checked bounds, I think this is MIR building that introduces raw pointers polluting the analysis;~ solved by a check specific to static variables. - the diagnostics for captured types don't show where they are used/dropped; - I do not attempt to support chalk. cc `@eholk` `@jyn514` for the drop-tracking work r? `@oli-obk` as you warned me of potential unsoundness
2 parents 7d4df2d + d3d6269 commit 6cd6bad

File tree

270 files changed

+6269
-601
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

270 files changed

+6269
-601
lines changed

compiler/rustc_codegen_ssa/src/debuginfo/type_names.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -414,6 +414,7 @@ fn push_debuginfo_type_name<'tcx>(
414414
| ty::Placeholder(..)
415415
| ty::Alias(..)
416416
| ty::Bound(..)
417+
| ty::GeneratorWitnessMIR(..)
417418
| ty::GeneratorWitness(..) => {
418419
bug!(
419420
"debuginfo: Trying to create type name for \

compiler/rustc_const_eval/src/const_eval/valtrees.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,7 @@ pub(crate) fn const_to_valtree_inner<'tcx>(
151151
// FIXME(oli-obk): we can probably encode closures just like structs
152152
| ty::Closure(..)
153153
| ty::Generator(..)
154-
| ty::GeneratorWitness(..) => Err(ValTreeCreationError::NonSupportedType),
154+
| ty::GeneratorWitness(..) |ty::GeneratorWitnessMIR(..)=> Err(ValTreeCreationError::NonSupportedType),
155155
}
156156
}
157157

@@ -314,6 +314,7 @@ pub fn valtree_to_const_value<'tcx>(
314314
| ty::Closure(..)
315315
| ty::Generator(..)
316316
| ty::GeneratorWitness(..)
317+
| ty::GeneratorWitnessMIR(..)
317318
| ty::FnPtr(_)
318319
| ty::RawPtr(_)
319320
| ty::Str

compiler/rustc_const_eval/src/interpret/intrinsics.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,7 @@ pub(crate) fn eval_nullary_intrinsic<'tcx>(
101101
| ty::Closure(_, _)
102102
| ty::Generator(_, _, _)
103103
| ty::GeneratorWitness(_)
104+
| ty::GeneratorWitnessMIR(_, _)
104105
| ty::Never
105106
| ty::Tuple(_)
106107
| ty::Error(_) => ConstValue::from_machine_usize(0u64, &tcx),

compiler/rustc_const_eval/src/interpret/validity.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -602,6 +602,7 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, '
602602
| ty::Bound(..)
603603
| ty::Param(..)
604604
| ty::Alias(..)
605+
| ty::GeneratorWitnessMIR(..)
605606
| ty::GeneratorWitness(..) => bug!("Encountered invalid type {:?}", ty),
606607
}
607608
}

compiler/rustc_const_eval/src/transform/validate.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -372,12 +372,12 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
372372
return;
373373
};
374374

375-
let Some(&f_ty) = layout.field_tys.get(local) else {
375+
let Some(f_ty) = layout.field_tys.get(local) else {
376376
self.fail(location, format!("Out of bounds local {:?} for {:?}", local, parent_ty));
377377
return;
378378
};
379379

380-
f_ty
380+
f_ty.ty
381381
} else {
382382
let Some(f_ty) = substs.as_generator().prefix_tys().nth(f.index()) else {
383383
fail_out_of_bounds(self, location);

compiler/rustc_const_eval/src/util/type_name.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ impl<'tcx> Printer<'tcx> for AbsolutePathPrinter<'tcx> {
6464
ty::Foreign(def_id) => self.print_def_path(def_id, &[]),
6565

6666
ty::GeneratorWitness(_) => bug!("type_name: unexpected `GeneratorWitness`"),
67+
ty::GeneratorWitnessMIR(..) => bug!("type_name: unexpected `GeneratorWitnessMIR`"),
6768
}
6869
}
6970

compiler/rustc_hir/src/hir.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2106,8 +2106,8 @@ pub enum LocalSource {
21062106
}
21072107

21082108
/// Hints at the original code for a `match _ { .. }`.
2109-
#[derive(Copy, Clone, PartialEq, Eq, Encodable, Hash, Debug)]
2110-
#[derive(HashStable_Generic)]
2109+
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
2110+
#[derive(HashStable_Generic, Encodable, Decodable)]
21112111
pub enum MatchSource {
21122112
/// A `match _ { .. }`.
21132113
Normal,

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_analysis/src/coherence/inherent_impls.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -240,6 +240,7 @@ impl<'tcx> InherentCollect<'tcx> {
240240
| ty::Closure(..)
241241
| ty::Generator(..)
242242
| ty::GeneratorWitness(..)
243+
| ty::GeneratorWitnessMIR(..)
243244
| ty::Bound(..)
244245
| ty::Placeholder(_)
245246
| ty::Infer(_) => {

0 commit comments

Comments
 (0)