Skip to content

Commit 0cb0427

Browse files
committed
handle region dependent goals due to infer vars
1 parent 0b323ea commit 0cb0427

File tree

7 files changed

+126
-19
lines changed

7 files changed

+126
-19
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_infer/src/infer/mod.rs

Lines changed: 40 additions & 15 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
}
@@ -247,24 +255,25 @@ pub struct InferCtxt<'tcx> {
247255
/// 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,
250-
/// Whether this inference context is used by HIR typeck. If so, we uniquify regions
251-
/// with `-Znext-solver`. This is necessary as borrowck will start by replacing each
252-
/// occurance of a free region with a unique inference variable so if HIR typeck
253-
/// ends up depending on two regions being equal we'd get unexpected mismatches
254-
/// between HIR typeck and MIR typeck, resulting in an ICE.
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 for
260+
/// type equality. This is necessary as borrowck starts by replacing each occurrence of a
261+
/// free region with a unique inference variable so 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.
255264
///
256265
/// The trait solver sometimes depends on regions being identical. As a concrete example
257266
/// the trait solver ignores other candidates if one candidate exists without any constraints.
258267
/// The goal `&'a u32: Equals<&'a u32>` has no constraints right now, but if we replace
259-
/// each occurance of `'a` with a unique region the goal now equates these regions.
260-
///
261-
/// See the tests in trait-system-refactor-initiative#27 for concrete examples.
268+
/// each occurance 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.
262270
///
263-
/// FIXME(-Znext-solver): This is insufficient in theory as a goal `T: Trait<?x, ?x>`
264-
/// may rely on the two occurances of `?x` being identical. If `?x` gets inferred to a
265-
/// type containing regions, this will no longer be the case. We can handle this case
266-
/// by storing goals which hold while still depending on inference vars and then
267-
/// reproving them before writeback.
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.
268277
pub in_hir_typeck: bool,
269278

270279
/// If set, this flag causes us to skip the 'leak check' during
@@ -1010,6 +1019,22 @@ impl<'tcx> InferCtxt<'tcx> {
10101019
}
10111020
}
10121021

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+
10131038
pub fn ty_to_string(&self, t: Ty<'tcx>) -> String {
10141039
self.resolve_vars_if_possible(t).to_string()
10151040
}

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
}

compiler/rustc_trait_selection/src/solve/fulfill.rs

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -197,7 +197,11 @@ where
197197
delegate.compute_goal_fast_path(goal, obligation.cause.span)
198198
{
199199
match certainty {
200-
Certainty::Yes => {}
200+
Certainty::Yes => {
201+
if infcx.in_hir_typeck && obligation.has_non_region_infer() {
202+
infcx.push_hir_typeck_potentially_region_dependent_goal(obligation);
203+
}
204+
}
201205
Certainty::Maybe(_) => {
202206
self.obligations.register(obligation, None);
203207
}
@@ -230,7 +234,11 @@ where
230234
}
231235

232236
match certainty {
233-
Certainty::Yes => {}
237+
Certainty::Yes => {
238+
if infcx.in_hir_typeck && obligation.has_non_region_infer() {
239+
infcx.push_hir_typeck_potentially_region_dependent_goal(obligation);
240+
}
241+
}
234242
Certainty::Maybe(_) => self.obligations.register(obligation, stalled_on),
235243
}
236244
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
error[E0283]: type annotations needed: cannot satisfy `(dyn Object<&(), &()> + 'static): Trait<&()>`
2+
--> $DIR/ambiguity-due-to-uniquification-3.rs:28:17
3+
|
4+
LL | impls_trait(obj, t);
5+
| ----------- ^^^
6+
| |
7+
| required by a bound introduced by this call
8+
|
9+
= note: cannot satisfy `(dyn Object<&(), &()> + 'static): Trait<&()>`
10+
= help: the trait `Trait<T>` is implemented for `()`
11+
note: required by a bound in `impls_trait`
12+
--> $DIR/ambiguity-due-to-uniquification-3.rs:24:19
13+
|
14+
LL | fn impls_trait<T: Trait<U>, U>(_: Inv<T>, _: Inv<U>) {}
15+
| ^^^^^^^^ required by this bound in `impls_trait`
16+
17+
error: aborting due to 1 previous error
18+
19+
For more information about this error, try `rustc --explain E0283`.
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
//@ revisions: current next
2+
//@[next] compile-flags: -Znext-solver
3+
//@ ignore-compare-mode-next-solver (explicit revisions)
4+
//@[current] check-pass
5+
6+
// Regression test from trait-system-refactor-initiative#27.
7+
//
8+
// Unlike in the previous two tests, `dyn Object<?x, ?y>: Trait<?x>` relies
9+
// on structural identity of type inference variables. This inference variable
10+
// gets constrained to a type containing a region later on. To prevent this
11+
// from causing an ICE during MIR borrowck, we stash goals which depend on
12+
// inference variables and then reprove them at the end of HIR typeck.
13+
14+
#![feature(rustc_attrs)]
15+
#![rustc_no_implicit_bounds]
16+
trait Trait<T> {}
17+
impl<T> Trait<T> for () {}
18+
19+
trait Object<T, U>: Trait<T> + Trait<U> {}
20+
21+
#[derive(Clone, Copy)]
22+
struct Inv<T>(*mut T);
23+
fn foo<T: Sized, U: Sized>() -> (Inv<dyn Object<T, U>>, Inv<T>) { todo!() }
24+
fn impls_trait<T: Trait<U>, U>(_: Inv<T>, _: Inv<U>) {}
25+
26+
fn bar() {
27+
let (obj, t) = foo();
28+
impls_trait(obj, t);
29+
//[next]~^ ERROR type annotations needed
30+
let _: Inv<dyn Object<&(), &()>> = obj;
31+
}
32+
33+
fn main() {}

0 commit comments

Comments
 (0)