Skip to content

Commit 019c65c

Browse files
committed
uniquify root goals during HIR typeck
1 parent 3c30dbb commit 019c65c

File tree

13 files changed

+175
-37
lines changed

13 files changed

+175
-37
lines changed

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: 35 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -244,9 +244,28 @@ pub struct InferCtxt<'tcx> {
244244
typing_mode: TypingMode<'tcx>,
245245

246246
/// Whether this inference context should care about region obligations in
247-
/// the root universe. Most notably, this is used during hir typeck as region
247+
/// the root universe. Most notably, this is used during HIR typeck as region
248248
/// solving is left to borrowck instead.
249249
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 unexpected mismatches between
254+
/// HIR typeck and MIR typeck, resulting in an ICE.
255+
///
256+
/// The trait solver sometimes depends on regions being identical. As a concrete example
257+
/// the trait solver ignores other candidates if one candidate exists without any constraints.
258+
/// 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.
262+
///
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.
268+
pub in_hir_typeck: bool,
250269

251270
/// If set, this flag causes us to skip the 'leak check' during
252271
/// higher-ranked subtyping operations. This flag is a temporary one used
@@ -506,6 +525,7 @@ pub struct TypeOutlivesConstraint<'tcx> {
506525
pub struct InferCtxtBuilder<'tcx> {
507526
tcx: TyCtxt<'tcx>,
508527
considering_regions: bool,
528+
in_hir_typeck: bool,
509529
skip_leak_check: bool,
510530
/// Whether we should use the new trait solver in the local inference context,
511531
/// which affects things like which solver is used in `predicate_may_hold`.
@@ -518,6 +538,7 @@ impl<'tcx> TyCtxt<'tcx> {
518538
InferCtxtBuilder {
519539
tcx: self,
520540
considering_regions: true,
541+
in_hir_typeck: false,
521542
skip_leak_check: false,
522543
next_trait_solver: self.next_trait_solver_globally(),
523544
}
@@ -535,6 +556,11 @@ impl<'tcx> InferCtxtBuilder<'tcx> {
535556
self
536557
}
537558

559+
pub fn in_hir_typeck(mut self) -> Self {
560+
self.in_hir_typeck = true;
561+
self
562+
}
563+
538564
pub fn skip_leak_check(mut self, skip_leak_check: bool) -> Self {
539565
self.skip_leak_check = skip_leak_check;
540566
self
@@ -568,12 +594,18 @@ impl<'tcx> InferCtxtBuilder<'tcx> {
568594
}
569595

570596
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;
597+
let InferCtxtBuilder {
598+
tcx,
599+
considering_regions,
600+
in_hir_typeck,
601+
skip_leak_check,
602+
next_trait_solver,
603+
} = *self;
573604
InferCtxt {
574605
tcx,
575606
typing_mode,
576607
considering_regions,
608+
in_hir_typeck,
577609
skip_leak_check,
578610
inner: RefCell::new(InferCtxtInner::new()),
579611
lexical_region_resolutions: RefCell::new(None),

compiler/rustc_next_trait_solver/src/canonicalizer.rs

Lines changed: 46 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -19,17 +19,28 @@ const NEEDS_CANONICAL: TypeFlags = TypeFlags::from_bits(
1919
)
2020
.unwrap();
2121

22+
#[derive(Debug, Clone, Copy)]
23+
enum CanonicalizeInputKind {
24+
/// When canonicalizing the `param_env`, we keep `'static` as merging
25+
/// trait candidates relies on it when deciding whether a where-bound
26+
/// is trivial.
27+
ParamEnv,
28+
/// When canonicalizing predicates, we don't keep `'static`. If we're
29+
/// currently outside of the trait solver and canonicalize the root goal
30+
/// during HIR typeck, we replace each occurance of a region with a
31+
/// unique region variable. See the comment on `InferCtxt::in_hir_typeck`
32+
/// for more details.
33+
Predicate { is_hir_typeck_root_goal: bool },
34+
}
35+
2236
/// Whether we're canonicalizing a query input or the query response.
2337
///
2438
/// When canonicalizing an input we're in the context of the caller
2539
/// while canonicalizing the response happens in the context of the
2640
/// query.
2741
#[derive(Debug, Clone, Copy)]
2842
enum CanonicalizeMode {
29-
/// When canonicalizing the `param_env`, we keep `'static` as merging
30-
/// trait candidates relies on it when deciding whether a where-bound
31-
/// is trivial.
32-
Input { keep_static: bool },
43+
Input(CanonicalizeInputKind),
3344
/// FIXME: We currently return region constraints referring to
3445
/// placeholders and inference variables from a binder instantiated
3546
/// inside of the query.
@@ -122,7 +133,7 @@ impl<'a, D: SolverDelegate<Interner = I>, I: Interner> Canonicalizer<'a, D, I> {
122133
let mut variables = Vec::new();
123134
let mut env_canonicalizer = Canonicalizer {
124135
delegate,
125-
canonicalize_mode: CanonicalizeMode::Input { keep_static: true },
136+
canonicalize_mode: CanonicalizeMode::Input(CanonicalizeInputKind::ParamEnv),
126137

127138
variables: &mut variables,
128139
variable_lookup_table: Default::default(),
@@ -154,7 +165,7 @@ impl<'a, D: SolverDelegate<Interner = I>, I: Interner> Canonicalizer<'a, D, I> {
154165
} else {
155166
let mut env_canonicalizer = Canonicalizer {
156167
delegate,
157-
canonicalize_mode: CanonicalizeMode::Input { keep_static: true },
168+
canonicalize_mode: CanonicalizeMode::Input(CanonicalizeInputKind::ParamEnv),
158169

159170
variables,
160171
variable_lookup_table: Default::default(),
@@ -180,6 +191,7 @@ impl<'a, D: SolverDelegate<Interner = I>, I: Interner> Canonicalizer<'a, D, I> {
180191
pub fn canonicalize_input<P: TypeFoldable<I>>(
181192
delegate: &'a D,
182193
variables: &'a mut Vec<I::GenericArg>,
194+
is_hir_typeck_root_goal: bool,
183195
input: QueryInput<I, P>,
184196
) -> ty::Canonical<I, QueryInput<I, P>> {
185197
// First canonicalize the `param_env` while keeping `'static`
@@ -189,7 +201,9 @@ impl<'a, D: SolverDelegate<Interner = I>, I: Interner> Canonicalizer<'a, D, I> {
189201
// while *mostly* reusing the canonicalizer from above.
190202
let mut rest_canonicalizer = Canonicalizer {
191203
delegate,
192-
canonicalize_mode: CanonicalizeMode::Input { keep_static: false },
204+
canonicalize_mode: CanonicalizeMode::Input(CanonicalizeInputKind::Predicate {
205+
is_hir_typeck_root_goal,
206+
}),
193207

194208
variables,
195209
variable_lookup_table,
@@ -413,10 +427,10 @@ impl<D: SolverDelegate<Interner = I>, I: Interner> TypeFolder<I> for Canonicaliz
413427
// We don't canonicalize `ReStatic` in the `param_env` as we use it
414428
// when checking whether a `ParamEnv` candidate is global.
415429
ty::ReStatic => match self.canonicalize_mode {
416-
CanonicalizeMode::Input { keep_static: false } => {
430+
CanonicalizeMode::Input(CanonicalizeInputKind::Predicate { .. }) => {
417431
CanonicalVarKind::Region(ty::UniverseIndex::ROOT)
418432
}
419-
CanonicalizeMode::Input { keep_static: true }
433+
CanonicalizeMode::Input(CanonicalizeInputKind::ParamEnv)
420434
| CanonicalizeMode::Response { .. } => return r,
421435
},
422436

@@ -428,20 +442,20 @@ impl<D: SolverDelegate<Interner = I>, I: Interner> TypeFolder<I> for Canonicaliz
428442
// `ReErased`. We may be able to short-circuit registering region
429443
// obligations if we encounter a `ReErased` on one side, for example.
430444
ty::ReErased | ty::ReError(_) => match self.canonicalize_mode {
431-
CanonicalizeMode::Input { .. } => CanonicalVarKind::Region(ty::UniverseIndex::ROOT),
445+
CanonicalizeMode::Input(_) => CanonicalVarKind::Region(ty::UniverseIndex::ROOT),
432446
CanonicalizeMode::Response { .. } => return r,
433447
},
434448

435449
ty::ReEarlyParam(_) | ty::ReLateParam(_) => match self.canonicalize_mode {
436-
CanonicalizeMode::Input { .. } => CanonicalVarKind::Region(ty::UniverseIndex::ROOT),
450+
CanonicalizeMode::Input(_) => CanonicalVarKind::Region(ty::UniverseIndex::ROOT),
437451
CanonicalizeMode::Response { .. } => {
438452
panic!("unexpected region in response: {r:?}")
439453
}
440454
},
441455

442456
ty::RePlaceholder(placeholder) => match self.canonicalize_mode {
443457
// We canonicalize placeholder regions as existentials in query inputs.
444-
CanonicalizeMode::Input { .. } => CanonicalVarKind::Region(ty::UniverseIndex::ROOT),
458+
CanonicalizeMode::Input(_) => CanonicalVarKind::Region(ty::UniverseIndex::ROOT),
445459
CanonicalizeMode::Response { max_input_universe } => {
446460
// If we have a placeholder region inside of a query, it must be from
447461
// a new universe.
@@ -459,23 +473,36 @@ impl<D: SolverDelegate<Interner = I>, I: Interner> TypeFolder<I> for Canonicaliz
459473
"region vid should have been resolved fully before canonicalization"
460474
);
461475
match self.canonicalize_mode {
462-
CanonicalizeMode::Input { keep_static: _ } => {
463-
CanonicalVarKind::Region(ty::UniverseIndex::ROOT)
464-
}
476+
CanonicalizeMode::Input(_) => CanonicalVarKind::Region(ty::UniverseIndex::ROOT),
465477
CanonicalizeMode::Response { .. } => {
466478
CanonicalVarKind::Region(self.delegate.universe_of_lt(vid).unwrap())
467479
}
468480
}
469481
}
470482
};
471483

472-
let var = self.get_or_insert_bound_var(r, kind);
484+
let var = if let CanonicalizeMode::Input(CanonicalizeInputKind::Predicate {
485+
is_hir_typeck_root_goal: true,
486+
}) = self.canonicalize_mode
487+
{
488+
let var = ty::BoundVar::from(self.variables.len());
489+
self.variables.push(r.into());
490+
self.var_kinds.push(kind);
491+
var
492+
} else {
493+
self.get_or_insert_bound_var(r, kind)
494+
};
473495

474496
Region::new_anon_bound(self.cx(), self.binder_index, var)
475497
}
476498

477499
fn fold_ty(&mut self, t: I::Ty) -> I::Ty {
478-
if let Some(&ty) = self.cache.get(&(self.binder_index, t)) {
500+
if let CanonicalizeMode::Input(CanonicalizeInputKind::Predicate {
501+
is_hir_typeck_root_goal: true,
502+
}) = self.canonicalize_mode
503+
{
504+
self.cached_fold_ty(t)
505+
} else if let Some(&ty) = self.cache.get(&(self.binder_index, t)) {
479506
ty
480507
} else {
481508
let res = self.cached_fold_ty(t);
@@ -541,9 +568,9 @@ impl<D: SolverDelegate<Interner = I>, I: Interner> TypeFolder<I> for Canonicaliz
541568

542569
fn fold_clauses(&mut self, c: I::Clauses) -> I::Clauses {
543570
match self.canonicalize_mode {
544-
CanonicalizeMode::Input { keep_static: true }
571+
CanonicalizeMode::Input(CanonicalizeInputKind::ParamEnv)
545572
| CanonicalizeMode::Response { max_input_universe: _ } => {}
546-
CanonicalizeMode::Input { keep_static: false } => {
573+
CanonicalizeMode::Input(CanonicalizeInputKind::Predicate { .. }) => {
547574
panic!("erasing 'static in env")
548575
}
549576
}

compiler/rustc_next_trait_solver/src/solve/eval_ctxt/canonical.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ where
5555
/// for each bound variable.
5656
pub(super) fn canonicalize_goal(
5757
&self,
58+
is_hir_typeck_root_goal: bool,
5859
goal: Goal<I, I::Predicate>,
5960
) -> (Vec<I::GenericArg>, CanonicalInput<I, I::Predicate>) {
6061
// We only care about one entry per `OpaqueTypeKey` here,
@@ -67,6 +68,7 @@ where
6768
let canonical = Canonicalizer::canonicalize_input(
6869
self.delegate,
6970
&mut orig_values,
71+
is_hir_typeck_root_goal,
7072
QueryInput {
7173
goal,
7274
predefined_opaques_in_body: self

compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -447,7 +447,10 @@ where
447447
));
448448
}
449449

450-
let (orig_values, canonical_goal) = self.canonicalize_goal(goal);
450+
let is_hir_typeck_root_goal = matches!(goal_evaluation_kind, GoalEvaluationKind::Root)
451+
&& self.delegate.in_hir_typeck();
452+
453+
let (orig_values, canonical_goal) = self.canonicalize_goal(is_hir_typeck_root_goal, goal);
451454
let mut goal_evaluation =
452455
self.inspect.new_goal_evaluation(goal, &orig_values, goal_evaluation_kind);
453456
let canonical_result = self.search_graph.evaluate_goal(

compiler/rustc_type_ir/src/infer_ctxt.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,10 @@ pub trait InferCtxtLike: Sized {
148148
true
149149
}
150150

151+
fn in_hir_typeck(&self) -> bool {
152+
false
153+
}
154+
151155
fn typing_mode(&self) -> TypingMode<Self::Interner>;
152156

153157
fn universe(&self) -> ty::UniverseIndex;

tests/crashes/139409.rs

Lines changed: 0 additions & 12 deletions
This file was deleted.
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 D<&(), &()>: B<&()>`
2+
--> $DIR/ambiguity-due-to-uniquification-1.rs:15:31
3+
|
4+
LL | (&() as &dyn D<&(), &()>).f()
5+
| ^
6+
|
7+
= note: cannot satisfy `dyn D<&(), &()>: B<&()>`
8+
= help: the trait `B<C>` is implemented for `()`
9+
note: required by a bound in `D::f`
10+
--> $DIR/ambiguity-due-to-uniquification-1.rs:10:16
11+
|
12+
LL | trait D<C, E>: B<C> + B<E> {
13+
| ^^^^ required by this bound in `D::f`
14+
LL | fn f(&self) {}
15+
| - required by a bound in this associated function
16+
17+
error: aborting due to 1 previous error
18+
19+
For more information about this error, try `rustc --explain E0283`.

0 commit comments

Comments
 (0)