Skip to content

Commit b91de35

Browse files
Auto merge of #144405 - lcnr:hir-typeck-uniquify, r=<try>
uniquify root goals during HIR typeck
2 parents b56aaec + 5082eab commit b91de35

File tree

20 files changed

+293
-47
lines changed

20 files changed

+293
-47
lines changed

compiler/rustc_hir_typeck/src/lib.rs

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ use rustc_hir_analysis::check::{check_abi, check_custom_abi};
5353
use rustc_hir_analysis::hir_ty_lowering::HirTyLowerer;
5454
use rustc_infer::traits::{ObligationCauseCode, ObligationInspector, WellFormedLoc};
5555
use rustc_middle::query::Providers;
56-
use rustc_middle::ty::{self, Ty, TyCtxt};
56+
use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt};
5757
use rustc_middle::{bug, span_bug};
5858
use rustc_session::config;
5959
use rustc_span::Span;
@@ -259,6 +259,21 @@ fn typeck_with_inspect<'tcx>(
259259

260260
let typeck_results = fcx.resolve_type_vars_in_body(body);
261261

262+
// Handle potentially region dependent goals, see `InferCtxt::in_hir_typeck`.
263+
if let None = fcx.infcx.tainted_by_errors() {
264+
for obligation in fcx.take_hir_typeck_potentially_region_dependent_goals() {
265+
let obligation = fcx.resolve_vars_if_possible(obligation);
266+
if obligation.has_non_region_infer() {
267+
bug!("unexpected inference variable after writeback: {obligation:?}");
268+
}
269+
fcx.register_predicate(obligation);
270+
}
271+
fcx.select_obligations_where_possible(|_| {});
272+
if let None = fcx.infcx.tainted_by_errors() {
273+
fcx.report_ambiguity_errors();
274+
}
275+
}
276+
262277
fcx.detect_opaque_types_added_during_writeback();
263278

264279
// Consistency check our TypeckResults instance can hold all ItemLocalIds

compiler/rustc_hir_typeck/src/typeck_root_ctxt.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -85,8 +85,11 @@ impl<'tcx> TypeckRootCtxt<'tcx> {
8585
pub(crate) fn new(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> Self {
8686
let hir_owner = tcx.local_def_id_to_hir_id(def_id).owner;
8787

88-
let infcx =
89-
tcx.infer_ctxt().ignoring_regions().build(TypingMode::typeck_for_body(tcx, def_id));
88+
let infcx = tcx
89+
.infer_ctxt()
90+
.ignoring_regions()
91+
.in_hir_typeck()
92+
.build(TypingMode::typeck_for_body(tcx, def_id));
9093
let typeck_results = RefCell::new(ty::TypeckResults::new(hir_owner));
9194
let fulfillment_cx = RefCell::new(<dyn TraitEngine<'_, _>>::new(&infcx));
9295

compiler/rustc_infer/src/infer/at.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ impl<'tcx> InferCtxt<'tcx> {
7171
tcx: self.tcx,
7272
typing_mode: self.typing_mode,
7373
considering_regions: self.considering_regions,
74+
in_hir_typeck: self.in_hir_typeck,
7475
skip_leak_check: self.skip_leak_check,
7576
inner: self.inner.clone(),
7677
lexical_region_resolutions: self.lexical_region_resolutions.clone(),
@@ -95,6 +96,7 @@ impl<'tcx> InferCtxt<'tcx> {
9596
tcx: self.tcx,
9697
typing_mode,
9798
considering_regions: self.considering_regions,
99+
in_hir_typeck: self.in_hir_typeck,
98100
skip_leak_check: self.skip_leak_check,
99101
inner: self.inner.clone(),
100102
lexical_region_resolutions: self.lexical_region_resolutions.clone(),

compiler/rustc_infer/src/infer/context.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,10 @@ impl<'tcx> rustc_type_ir::InferCtxtLike for InferCtxt<'tcx> {
2222
self.next_trait_solver
2323
}
2424

25+
fn in_hir_typeck(&self) -> bool {
26+
self.in_hir_typeck
27+
}
28+
2529
fn typing_mode(&self) -> ty::TypingMode<'tcx> {
2630
self.typing_mode()
2731
}

compiler/rustc_infer/src/infer/mod.rs

Lines changed: 62 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -37,10 +37,11 @@ use snapshot::undo_log::InferCtxtUndoLogs;
3737
use tracing::{debug, instrument};
3838
use type_variable::TypeVariableOrigin;
3939

40-
use crate::infer::region_constraints::UndoLog;
40+
use crate::infer::snapshot::undo_log::UndoLog;
4141
use crate::infer::unify_key::{ConstVariableOrigin, ConstVariableValue, ConstVidKey};
4242
use crate::traits::{
43-
self, ObligationCause, ObligationInspector, PredicateObligations, TraitEngine,
43+
self, ObligationCause, ObligationInspector, PredicateObligation, PredicateObligations,
44+
TraitEngine,
4445
};
4546

4647
pub mod at;
@@ -156,6 +157,12 @@ pub struct InferCtxtInner<'tcx> {
156157
/// which may cause types to no longer be considered well-formed.
157158
region_assumptions: Vec<ty::ArgOutlivesPredicate<'tcx>>,
158159

160+
/// `-Znext-solver`: Successfully proven goals during HIR typeck which
161+
/// reference inference variables and get reproven after writeback.
162+
///
163+
/// See the documentation of `InferCtxt::in_hir_typeck` for more details.
164+
hir_typeck_potentially_region_dependent_goals: Vec<PredicateObligation<'tcx>>,
165+
159166
/// Caches for opaque type inference.
160167
opaque_type_storage: OpaqueTypeStorage<'tcx>,
161168
}
@@ -173,6 +180,7 @@ impl<'tcx> InferCtxtInner<'tcx> {
173180
region_constraint_storage: Some(Default::default()),
174181
region_obligations: Default::default(),
175182
region_assumptions: Default::default(),
183+
hir_typeck_potentially_region_dependent_goals: Default::default(),
176184
opaque_type_storage: Default::default(),
177185
}
178186
}
@@ -244,9 +252,29 @@ pub struct InferCtxt<'tcx> {
244252
typing_mode: TypingMode<'tcx>,
245253

246254
/// Whether this inference context should care about region obligations in
247-
/// the root universe. Most notably, this is used during hir typeck as region
255+
/// the root universe. Most notably, this is used during HIR typeck as region
248256
/// solving is left to borrowck instead.
249257
pub considering_regions: bool,
258+
/// `-Znext-solver`: Whether this inference context is used by HIR typeck. If so, we
259+
/// need to make sure we don't rely on region identity in the trait solver or when
260+
/// relating types. This is necessary as borrowck starts by replacing each occurrence of a
261+
/// free region with a unique inference variable. If HIR typeck ends up depending on two
262+
/// regions being equal we'd get unexpected mismatches between HIR typeck and MIR typeck,
263+
/// resulting in an ICE.
264+
///
265+
/// The trait solver sometimes depends on regions being identical. As a concrete example
266+
/// the trait solver ignores other candidates if one candidate exists without any constraints.
267+
/// The goal `&'a u32: Equals<&'a u32>` has no constraints right now. If we replace each
268+
/// occurrence of `'a` with a unique region the goal now equates these regions. See
269+
/// the tests in trait-system-refactor-initiative#27 for concrete examples.
270+
///
271+
/// We handle this by *uniquifying* region when canonicalizing root goals during HIR typeck.
272+
/// This is still insufficient as inference variables may *hide* region variables, so e.g.
273+
/// `dyn TwoSuper<?x, ?x>: Super<?x>` may hold but MIR typeck could end up having to prove
274+
/// `dyn TwoSuper<&'0 (), &'1 ()>: Super<&'2 ()>` which is now ambiguous. Because of this we
275+
/// stash all successfully proven goals which reference inference variables and then reprove
276+
/// them after writeback.
277+
pub in_hir_typeck: bool,
250278

251279
/// If set, this flag causes us to skip the 'leak check' during
252280
/// higher-ranked subtyping operations. This flag is a temporary one used
@@ -506,6 +534,7 @@ pub struct TypeOutlivesConstraint<'tcx> {
506534
pub struct InferCtxtBuilder<'tcx> {
507535
tcx: TyCtxt<'tcx>,
508536
considering_regions: bool,
537+
in_hir_typeck: bool,
509538
skip_leak_check: bool,
510539
/// Whether we should use the new trait solver in the local inference context,
511540
/// which affects things like which solver is used in `predicate_may_hold`.
@@ -518,6 +547,7 @@ impl<'tcx> TyCtxt<'tcx> {
518547
InferCtxtBuilder {
519548
tcx: self,
520549
considering_regions: true,
550+
in_hir_typeck: false,
521551
skip_leak_check: false,
522552
next_trait_solver: self.next_trait_solver_globally(),
523553
}
@@ -535,6 +565,11 @@ impl<'tcx> InferCtxtBuilder<'tcx> {
535565
self
536566
}
537567

568+
pub fn in_hir_typeck(mut self) -> Self {
569+
self.in_hir_typeck = true;
570+
self
571+
}
572+
538573
pub fn skip_leak_check(mut self, skip_leak_check: bool) -> Self {
539574
self.skip_leak_check = skip_leak_check;
540575
self
@@ -568,12 +603,18 @@ impl<'tcx> InferCtxtBuilder<'tcx> {
568603
}
569604

570605
pub fn build(&mut self, typing_mode: TypingMode<'tcx>) -> InferCtxt<'tcx> {
571-
let InferCtxtBuilder { tcx, considering_regions, skip_leak_check, next_trait_solver } =
572-
*self;
606+
let InferCtxtBuilder {
607+
tcx,
608+
considering_regions,
609+
in_hir_typeck,
610+
skip_leak_check,
611+
next_trait_solver,
612+
} = *self;
573613
InferCtxt {
574614
tcx,
575615
typing_mode,
576616
considering_regions,
617+
in_hir_typeck,
577618
skip_leak_check,
578619
inner: RefCell::new(InferCtxtInner::new()),
579620
lexical_region_resolutions: RefCell::new(None),
@@ -978,6 +1019,22 @@ impl<'tcx> InferCtxt<'tcx> {
9781019
}
9791020
}
9801021

1022+
pub fn push_hir_typeck_potentially_region_dependent_goal(
1023+
&self,
1024+
goal: PredicateObligation<'tcx>,
1025+
) {
1026+
let mut inner = self.inner.borrow_mut();
1027+
inner.undo_log.push(UndoLog::PushHirTypeckPotentiallyRegionDependentGoal);
1028+
inner.hir_typeck_potentially_region_dependent_goals.push(goal);
1029+
}
1030+
1031+
pub fn take_hir_typeck_potentially_region_dependent_goals(
1032+
&self,
1033+
) -> Vec<PredicateObligation<'tcx>> {
1034+
assert!(!self.in_snapshot(), "cannot take goals in a snapshot");
1035+
std::mem::take(&mut self.inner.borrow_mut().hir_typeck_potentially_region_dependent_goals)
1036+
}
1037+
9811038
pub fn ty_to_string(&self, t: Ty<'tcx>) -> String {
9821039
self.resolve_vars_if_possible(t).to_string()
9831040
}

compiler/rustc_infer/src/infer/outlives/obligations.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,7 @@ impl<'tcx> InferCtxt<'tcx> {
177177
}
178178

179179
pub fn take_registered_region_assumptions(&self) -> Vec<ty::ArgOutlivesPredicate<'tcx>> {
180+
assert!(!self.in_snapshot(), "cannot take registered region assumptions in a snapshot");
180181
std::mem::take(&mut self.inner.borrow_mut().region_assumptions)
181182
}
182183

compiler/rustc_infer/src/infer/snapshot/undo_log.rs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ pub(crate) enum UndoLog<'tcx> {
2929
ProjectionCache(traits::UndoLog<'tcx>),
3030
PushTypeOutlivesConstraint,
3131
PushRegionAssumption,
32+
PushHirTypeckPotentiallyRegionDependentGoal,
3233
}
3334

3435
macro_rules! impl_from {
@@ -79,7 +80,12 @@ impl<'tcx> Rollback<UndoLog<'tcx>> for InferCtxtInner<'tcx> {
7980
assert_matches!(popped, Some(_), "pushed region constraint but could not pop it");
8081
}
8182
UndoLog::PushRegionAssumption => {
82-
self.region_assumptions.pop();
83+
let popped = self.region_assumptions.pop();
84+
assert_matches!(popped, Some(_), "pushed region assumption but could not pop it");
85+
}
86+
UndoLog::PushHirTypeckPotentiallyRegionDependentGoal => {
87+
let popped = self.hir_typeck_potentially_region_dependent_goals.pop();
88+
assert_matches!(popped, Some(_), "pushed goal but could not pop it");
8389
}
8490
}
8591
}

0 commit comments

Comments
 (0)