Skip to content

Commit 4a5bf7b

Browse files
authored
Rollup merge of #124827 - lcnr:generalize-incomplete, r=compiler-errors
generalize hr alias: avoid unconstrainable infer vars fixes rust-lang/trait-system-refactor-initiative#108 see inline comments for more details r? `@compiler-errors` cc `@BoxyUwU`
2 parents 7af9ad1 + 690d5aa commit 4a5bf7b

File tree

7 files changed

+175
-26
lines changed

7 files changed

+175
-26
lines changed

compiler/rustc_hir_typeck/src/closure.rs

+11-21
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ struct ExpectedSig<'tcx> {
3131
sig: ty::PolyFnSig<'tcx>,
3232
}
3333

34+
#[derive(Debug)]
3435
struct ClosureSignatures<'tcx> {
3536
/// The signature users of the closure see.
3637
bound_sig: ty::PolyFnSig<'tcx>,
@@ -713,25 +714,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
713714
// [c2]: https://github.com/rust-lang/rust/pull/45072#issuecomment-341096796
714715
self.commit_if_ok(|_| {
715716
let mut all_obligations = vec![];
716-
let inputs: Vec<_> = iter::zip(
717-
decl.inputs,
718-
supplied_sig.inputs().skip_binder(), // binder moved to (*) below
719-
)
720-
.map(|(hir_ty, &supplied_ty)| {
721-
// Instantiate (this part of..) S to S', i.e., with fresh variables.
722-
self.instantiate_binder_with_fresh_vars(
723-
hir_ty.span,
724-
BoundRegionConversionTime::FnCall,
725-
// (*) binder moved to here
726-
supplied_sig.inputs().rebind(supplied_ty),
727-
)
728-
})
729-
.collect();
717+
let supplied_sig = self.instantiate_binder_with_fresh_vars(
718+
self.tcx.def_span(expr_def_id),
719+
BoundRegionConversionTime::FnCall,
720+
supplied_sig,
721+
);
730722

731723
// The liberated version of this signature should be a subtype
732724
// of the liberated form of the expectation.
733725
for ((hir_ty, &supplied_ty), expected_ty) in iter::zip(
734-
iter::zip(decl.inputs, &inputs),
726+
iter::zip(decl.inputs, supplied_sig.inputs()),
735727
expected_sigs.liberated_sig.inputs(), // `liberated_sig` is E'.
736728
) {
737729
// Check that E' = S'.
@@ -744,11 +736,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
744736
all_obligations.extend(obligations);
745737
}
746738

747-
let supplied_output_ty = self.instantiate_binder_with_fresh_vars(
748-
decl.output.span(),
749-
BoundRegionConversionTime::FnCall,
750-
supplied_sig.output(),
751-
);
739+
let supplied_output_ty = supplied_sig.output();
752740
let cause = &self.misc(decl.output.span());
753741
let InferOk { value: (), obligations } = self.at(cause, self.param_env).eq(
754742
DefineOpaqueTypes::Yes,
@@ -757,7 +745,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
757745
)?;
758746
all_obligations.extend(obligations);
759747

760-
let inputs = inputs.into_iter().map(|ty| self.resolve_vars_if_possible(ty));
748+
let inputs =
749+
supplied_sig.inputs().into_iter().map(|&ty| self.resolve_vars_if_possible(ty));
761750

762751
expected_sigs.liberated_sig = self.tcx.mk_fn_sig(
763752
inputs,
@@ -1013,6 +1002,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
10131002
result
10141003
}
10151004

1005+
#[instrument(level = "debug", skip(self), ret)]
10161006
fn closure_sigs(
10171007
&self,
10181008
expr_def_id: LocalDefId,

compiler/rustc_infer/src/infer/relate/generalize.rs

+40-4
Original file line numberDiff line numberDiff line change
@@ -350,7 +350,13 @@ impl<'tcx> Generalizer<'_, 'tcx> {
350350
&mut self,
351351
alias: ty::AliasTy<'tcx>,
352352
) -> Result<Ty<'tcx>, TypeError<'tcx>> {
353-
if self.infcx.next_trait_solver() && !alias.has_escaping_bound_vars() {
353+
// We do not eagerly replace aliases with inference variables if they have
354+
// escaping bound vars, see the method comment for details. However, when we
355+
// are inside of an alias with escaping bound vars replacing nested aliases
356+
// with inference variables can cause incorrect ambiguity.
357+
//
358+
// cc trait-system-refactor-initiative#110
359+
if self.infcx.next_trait_solver() && !alias.has_escaping_bound_vars() && !self.in_alias {
354360
return Ok(self.infcx.next_ty_var_in_universe(
355361
TypeVariableOrigin { param_def_id: None, span: self.span },
356362
self.for_universe,
@@ -492,9 +498,30 @@ impl<'tcx> TypeRelation<'tcx> for Generalizer<'_, 'tcx> {
492498
let origin = inner.type_variables().var_origin(vid);
493499
let new_var_id =
494500
inner.type_variables().new_var(self.for_universe, origin);
495-
let u = Ty::new_var(self.tcx(), new_var_id);
496-
debug!("replacing original vid={:?} with new={:?}", vid, u);
497-
Ok(u)
501+
// If we're in the new solver and create a new inference
502+
// variable inside of an alias we eagerly constrain that
503+
// inference variable to prevent unexpected ambiguity errors.
504+
//
505+
// This is incomplete as it pulls down the universe of the
506+
// original inference variable, even though the alias could
507+
// normalize to a type which does not refer to that type at
508+
// all. I don't expect this to cause unexpected errors in
509+
// practice.
510+
//
511+
// We only need to do so for type and const variables, as
512+
// region variables do not impact normalization, and will get
513+
// correctly constrained by `AliasRelate` later on.
514+
//
515+
// cc trait-system-refactor-initiative#108
516+
if self.infcx.next_trait_solver()
517+
&& !self.infcx.intercrate
518+
&& self.in_alias
519+
{
520+
inner.type_variables().equate(vid, new_var_id);
521+
}
522+
523+
debug!("replacing original vid={:?} with new={:?}", vid, new_var_id);
524+
Ok(Ty::new_var(self.tcx(), new_var_id))
498525
}
499526
}
500527
}
@@ -614,6 +641,15 @@ impl<'tcx> TypeRelation<'tcx> for Generalizer<'_, 'tcx> {
614641
universe: self.for_universe,
615642
})
616643
.vid;
644+
645+
// See the comment for type inference variables
646+
// for more details.
647+
if self.infcx.next_trait_solver()
648+
&& !self.infcx.intercrate
649+
&& self.in_alias
650+
{
651+
variable_table.union(vid, new_var_id);
652+
}
617653
Ok(ty::Const::new_var(self.tcx(), new_var_id, c.ty()))
618654
}
619655
}

compiler/rustc_trait_selection/src/solve/inspect/build.rs

+4-1
Original file line numberDiff line numberDiff line change
@@ -376,7 +376,10 @@ impl<'tcx> ProofTreeBuilder<'tcx> {
376376
(
377377
DebugSolver::GoalEvaluation(goal_evaluation),
378378
DebugSolver::CanonicalGoalEvaluation(canonical_goal_evaluation),
379-
) => goal_evaluation.evaluation = Some(canonical_goal_evaluation),
379+
) => {
380+
let prev = goal_evaluation.evaluation.replace(canonical_goal_evaluation);
381+
assert_eq!(prev, None);
382+
}
380383
_ => unreachable!(),
381384
}
382385
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
//@ revisions: old next
2+
//@[next] compile-flags: -Znext-solver
3+
//@ ignore-compare-mode-next-solver (explicit revisions)
4+
//@ check-pass
5+
6+
// Generalizing an alias referencing escaping bound variables
7+
// is hard. We previously didn't replace this alias with inference
8+
// variables but did replace nested alias which do not reference
9+
// any bound variables. This caused us to stall with the following
10+
// goal, which cannot make any progress:
11+
//
12+
// <<T as Id>::Refl as HigherRanked>::Output<'a>
13+
// alias-relate
14+
// <?unconstrained as HigherRanked>::Output<'a>
15+
//
16+
//
17+
// cc trait-system-refactor-initiative#110
18+
19+
#![allow(unused)]
20+
trait HigherRanked {
21+
type Output<'a>;
22+
}
23+
trait Id {
24+
type Refl: HigherRanked;
25+
}
26+
27+
fn foo<T: Id>() -> for<'a> fn(<<T as Id>::Refl as HigherRanked>::Output<'a>) {
28+
todo!()
29+
}
30+
31+
fn bar<T: Id>() {
32+
// we generalize here
33+
let x = foo::<T>();
34+
}
35+
36+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
//@ revisions: old next
2+
//@[next] compile-flags: -Znext-solver
3+
//@ ignore-compare-mode-next-solver (explicit revisions)
4+
//@ check-pass
5+
6+
// A minimization of an ambiguity error in `icu_provider`.
7+
//
8+
// cc trait-system-refactor-initiative#110
9+
10+
trait Yokeable<'a> {
11+
type Output;
12+
}
13+
trait Id {
14+
type Refl;
15+
}
16+
17+
fn into_deserialized<M: Id>() -> M
18+
where
19+
M::Refl: for<'a> Yokeable<'a>,
20+
{
21+
try_map_project::<M, _>(|_| todo!())
22+
}
23+
24+
fn try_map_project<M: Id, F>(_f: F) -> M
25+
where
26+
M::Refl: for<'a> Yokeable<'a>,
27+
F: for<'a> FnOnce(&'a ()) -> <<M as Id>::Refl as Yokeable<'a>>::Output,
28+
{
29+
todo!()
30+
}
31+
32+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
//@ compile-flags: -Znext-solver
2+
//@ check-pass
3+
4+
// A regression test for a fairly subtle issue with how we
5+
// generalize aliases referencing higher-ranked regions
6+
// which previously caused unexpected ambiguity errors.
7+
//
8+
// The explanations in the test may end up being out of date
9+
// in the future as we may refine our approach to generalization
10+
// going forward.
11+
//
12+
// cc trait-system-refactor-initiative#108
13+
trait Trait<'a> {
14+
type Assoc;
15+
}
16+
17+
impl<'a> Trait<'a> for () {
18+
type Assoc = ();
19+
}
20+
21+
fn foo<T: for<'a> Trait<'a>>(x: T) -> for<'a> fn(<T as Trait<'a>>::Assoc) {
22+
|_| ()
23+
}
24+
25+
fn unconstrained<T>() -> T {
26+
todo!()
27+
}
28+
29+
fn main() {
30+
// create `?x.0` in the root universe
31+
let mut x = unconstrained();
32+
33+
// bump the current universe of the inference context
34+
let bump: for<'a, 'b> fn(&'a (), &'b ()) = |_, _| ();
35+
let _: for<'a> fn(&'a (), &'a ()) = bump;
36+
37+
// create `?y.1` in a higher universe
38+
let mut y = Default::default();
39+
40+
// relate `?x.0` with `for<'a> fn(<?y.1 as Trait<'a>>::Assoc)`
41+
// -> instantiate `?x.0` with `for<'a> fn(<?y_new.0 as Trait<'a>>::Assoc)`
42+
x = foo(y);
43+
44+
// Constrain `?y.1` to `()`
45+
let _: () = y;
46+
47+
// `AliasRelate(<?y_new.0 as Trait<'a>>::Assoc, <() as Trait<'a>>::Assoc)`
48+
// remains ambiguous unless we somehow constrain `?y_new.0` during
49+
// generalization to be equal to `?y.1`, which is exactly what we
50+
// did to fix this issue.
51+
}

tests/ui/traits/next-solver/generalize/occurs-check-nested-alias.rs

+1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
//@ revisions: old next
22
//@[next] compile-flags: -Znext-solver
3+
//@ ignore-compare-mode-next-solver (explicit revisions)
34
//@ check-pass
45

56
// case 3 of https://github.com/rust-lang/trait-system-refactor-initiative/issues/8.

0 commit comments

Comments
 (0)