Skip to content

Commit 90f22e7

Browse files
committed
Auto merge of rust-lang#117088 - lcnr:generalize-alias, r=compiler-errors
generalize: handle occurs check failure in aliases mostly fixes rust-lang#105787, except for the `for<'a> fn(<<?x as OtherTrait>::Assoc as Trait<'a>>::Assoc) eq ?x` case in rust-lang/trait-system-refactor-initiative#8. r? `@compiler-errors`
2 parents cf8d812 + cf8a2bd commit 90f22e7

12 files changed

+325
-36
lines changed

Diff for: compiler/rustc_infer/src/infer/combine.rs

+49-15
Original file line numberDiff line numberDiff line change
@@ -326,7 +326,9 @@ impl<'tcx> InferCtxt<'tcx> {
326326
) -> RelateResult<'tcx, ty::Const<'tcx>> {
327327
let span =
328328
self.inner.borrow_mut().const_unification_table().probe_value(target_vid).origin.span;
329-
let Generalization { value, needs_wf: _ } = generalize::generalize(
329+
// FIXME(generic_const_exprs): Occurs check failures for unevaluated
330+
// constants and generic expressions are not yet handled correctly.
331+
let Generalization { value_may_be_infer: value, needs_wf: _ } = generalize::generalize(
330332
self,
331333
&mut CombineDelegate { infcx: self, span, param_env },
332334
ct,
@@ -445,7 +447,7 @@ impl<'infcx, 'tcx> CombineFields<'infcx, 'tcx> {
445447
// `'?2` and `?3` are fresh region/type inference
446448
// variables. (Down below, we will relate `a_ty <: b_ty`,
447449
// adding constraints like `'x: '?2` and `?1 <: ?3`.)
448-
let Generalization { value: b_ty, needs_wf } = generalize::generalize(
450+
let Generalization { value_may_be_infer: b_ty, needs_wf } = generalize::generalize(
449451
self.infcx,
450452
&mut CombineDelegate {
451453
infcx: self.infcx,
@@ -457,7 +459,6 @@ impl<'infcx, 'tcx> CombineFields<'infcx, 'tcx> {
457459
ambient_variance,
458460
)?;
459461

460-
debug!(?b_ty);
461462
self.infcx.inner.borrow_mut().type_variables().instantiate(b_vid, b_ty);
462463

463464
if needs_wf {
@@ -477,19 +478,52 @@ impl<'infcx, 'tcx> CombineFields<'infcx, 'tcx> {
477478
// relations wind up attributed to the same spans. We need
478479
// to associate causes/spans with each of the relations in
479480
// the stack to get this right.
480-
match ambient_variance {
481-
ty::Variance::Invariant => self.equate(a_is_expected).relate(a_ty, b_ty),
482-
ty::Variance::Covariant => self.sub(a_is_expected).relate(a_ty, b_ty),
483-
ty::Variance::Contravariant => self.sub(a_is_expected).relate_with_variance(
484-
ty::Contravariant,
485-
ty::VarianceDiagInfo::default(),
486-
a_ty,
487-
b_ty,
488-
),
489-
ty::Variance::Bivariant => {
490-
unreachable!("no code should be generalizing bivariantly (currently)")
481+
if b_ty.is_ty_var() {
482+
// This happens for cases like `<?0 as Trait>::Assoc == ?0`.
483+
// We can't instantiate `?0` here as that would result in a
484+
// cyclic type. We instead delay the unification in case
485+
// the alias can be normalized to something which does not
486+
// mention `?0`.
487+
488+
// FIXME(-Ztrait-solver=next): replace this with `AliasRelate`
489+
let &ty::Alias(kind, data) = a_ty.kind() else {
490+
bug!("generalization should only result in infer vars for aliases");
491+
};
492+
if !self.infcx.next_trait_solver() {
493+
// The old solver only accepts projection predicates for associated types.
494+
match kind {
495+
ty::AliasKind::Projection => {}
496+
ty::AliasKind::Inherent | ty::AliasKind::Weak | ty::AliasKind::Opaque => {
497+
return Err(TypeError::CyclicTy(a_ty));
498+
}
499+
}
491500
}
492-
}?;
501+
502+
// FIXME: This does not handle subtyping correctly, we should switch to
503+
// alias-relate in the new solver and could instead create a new inference
504+
// variable for `a_ty`, emitting `Projection(a_ty, a_infer)` and
505+
// `a_infer <: b_ty`.
506+
self.obligations.push(Obligation::new(
507+
self.tcx(),
508+
self.trace.cause.clone(),
509+
self.param_env,
510+
ty::ProjectionPredicate { projection_ty: data, term: b_ty.into() },
511+
))
512+
} else {
513+
match ambient_variance {
514+
ty::Variance::Invariant => self.equate(a_is_expected).relate(a_ty, b_ty),
515+
ty::Variance::Covariant => self.sub(a_is_expected).relate(a_ty, b_ty),
516+
ty::Variance::Contravariant => self.sub(a_is_expected).relate_with_variance(
517+
ty::Contravariant,
518+
ty::VarianceDiagInfo::default(),
519+
a_ty,
520+
b_ty,
521+
),
522+
ty::Variance::Bivariant => {
523+
unreachable!("no code should be generalizing bivariantly (currently)")
524+
}
525+
}?;
526+
}
493527

494528
Ok(())
495529
}

Diff for: compiler/rustc_infer/src/infer/generalize.rs

+77-10
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,16 @@
1+
use std::mem;
2+
13
use rustc_data_structures::sso::SsoHashMap;
24
use rustc_hir::def_id::DefId;
35
use rustc_middle::infer::unify_key::{ConstVarValue, ConstVariableValue};
46
use rustc_middle::ty::error::TypeError;
57
use rustc_middle::ty::relate::{self, Relate, RelateResult, TypeRelation};
6-
use rustc_middle::ty::{self, InferConst, Term, Ty, TyCtxt, TypeVisitableExt};
8+
use rustc_middle::ty::visit::MaxUniverse;
9+
use rustc_middle::ty::{self, InferConst, Term, Ty, TyCtxt, TypeVisitable, TypeVisitableExt};
710
use rustc_span::Span;
811

912
use crate::infer::nll_relate::TypeRelatingDelegate;
10-
use crate::infer::type_variable::TypeVariableValue;
13+
use crate::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind, TypeVariableValue};
1114
use crate::infer::{InferCtxt, RegionVariableOrigin};
1215

1316
/// Attempts to generalize `term` for the type variable `for_vid`.
@@ -38,27 +41,30 @@ pub(super) fn generalize<'tcx, D: GeneralizerDelegate<'tcx>, T: Into<Term<'tcx>>
3841
root_vid,
3942
for_universe,
4043
root_term: term.into(),
44+
in_alias: false,
4145
needs_wf: false,
4246
cache: Default::default(),
4347
};
4448

4549
assert!(!term.has_escaping_bound_vars());
46-
let value = generalizer.relate(term, term)?;
50+
let value_may_be_infer = generalizer.relate(term, term)?;
4751
let needs_wf = generalizer.needs_wf;
48-
Ok(Generalization { value, needs_wf })
52+
Ok(Generalization { value_may_be_infer, needs_wf })
4953
}
5054

5155
/// Abstracts the handling of region vars between HIR and MIR/NLL typechecking
5256
/// in the generalizer code.
53-
pub trait GeneralizerDelegate<'tcx> {
57+
pub(super) trait GeneralizerDelegate<'tcx> {
5458
fn param_env(&self) -> ty::ParamEnv<'tcx>;
5559

5660
fn forbid_inference_vars() -> bool;
5761

62+
fn span(&self) -> Span;
63+
5864
fn generalize_region(&mut self, universe: ty::UniverseIndex) -> ty::Region<'tcx>;
5965
}
6066

61-
pub struct CombineDelegate<'cx, 'tcx> {
67+
pub(super) struct CombineDelegate<'cx, 'tcx> {
6268
pub infcx: &'cx InferCtxt<'tcx>,
6369
pub param_env: ty::ParamEnv<'tcx>,
6470
pub span: Span,
@@ -73,6 +79,10 @@ impl<'tcx> GeneralizerDelegate<'tcx> for CombineDelegate<'_, 'tcx> {
7379
false
7480
}
7581

82+
fn span(&self) -> Span {
83+
self.span
84+
}
85+
7686
fn generalize_region(&mut self, universe: ty::UniverseIndex) -> ty::Region<'tcx> {
7787
// FIXME: This is non-ideal because we don't give a
7888
// very descriptive origin for this region variable.
@@ -93,6 +103,10 @@ where
93103
<Self as TypeRelatingDelegate<'tcx>>::forbid_inference_vars()
94104
}
95105

106+
fn span(&self) -> Span {
107+
<Self as TypeRelatingDelegate<'tcx>>::span(&self)
108+
}
109+
96110
fn generalize_region(&mut self, universe: ty::UniverseIndex) -> ty::Region<'tcx> {
97111
<Self as TypeRelatingDelegate<'tcx>>::generalize_existential(self, universe)
98112
}
@@ -139,6 +153,13 @@ struct Generalizer<'me, 'tcx, D> {
139153

140154
cache: SsoHashMap<Ty<'tcx>, Ty<'tcx>>,
141155

156+
/// This is set once we're generalizing the arguments of an alias.
157+
///
158+
/// This is necessary to correctly handle
159+
/// `<T as Bar<<?0 as Foo>::Assoc>::Assoc == ?0`. This equality can
160+
/// hold by either normalizing the outer or the inner associated type.
161+
in_alias: bool,
162+
142163
/// See the field `needs_wf` in `Generalization`.
143164
needs_wf: bool,
144165
}
@@ -193,7 +214,7 @@ where
193214
opt_variances,
194215
a_subst,
195216
b_subst,
196-
true,
217+
false,
197218
)
198219
}
199220
}
@@ -309,6 +330,44 @@ where
309330
}
310331
}
311332

333+
ty::Alias(kind, data) => {
334+
// An occurs check failure inside of an alias does not mean
335+
// that the types definitely don't unify. We may be able
336+
// to normalize the alias after all.
337+
//
338+
// We handle this by lazily equating the alias and generalizing
339+
// it to an inference variable.
340+
let is_nested_alias = mem::replace(&mut self.in_alias, true);
341+
let result = match self.relate(data, data) {
342+
Ok(data) => Ok(Ty::new_alias(self.tcx(), kind, data)),
343+
Err(e) => {
344+
if is_nested_alias {
345+
return Err(e);
346+
} else {
347+
let mut visitor = MaxUniverse::new();
348+
t.visit_with(&mut visitor);
349+
let infer_replacement_is_complete =
350+
self.for_universe.can_name(visitor.max_universe())
351+
&& !t.has_escaping_bound_vars();
352+
if !infer_replacement_is_complete {
353+
warn!("may incompletely handle alias type: {t:?}");
354+
}
355+
356+
debug!("generalization failure in alias");
357+
Ok(self.infcx.next_ty_var_in_universe(
358+
TypeVariableOrigin {
359+
kind: TypeVariableOriginKind::MiscVariable,
360+
span: self.delegate.span(),
361+
},
362+
self.for_universe,
363+
))
364+
}
365+
}
366+
};
367+
self.in_alias = is_nested_alias;
368+
result
369+
}
370+
312371
_ => relate::structurally_relate_tys(self, t, t),
313372
}?;
314373

@@ -456,8 +515,16 @@ where
456515
/// not only the generalized type, but also a bool flag
457516
/// indicating whether further WF checks are needed.
458517
#[derive(Debug)]
459-
pub struct Generalization<T> {
460-
pub value: T,
518+
pub(super) struct Generalization<T> {
519+
/// When generalizing `<?0 as Trait>::Assoc` or
520+
/// `<T as Bar<<?0 as Foo>::Assoc>>::Assoc`
521+
/// for `?0` generalization returns an inference
522+
/// variable.
523+
///
524+
/// This has to be handled wotj care as it can
525+
/// otherwise very easily result in infinite
526+
/// recursion.
527+
pub(super) value_may_be_infer: T,
461528

462529
/// If true, then the generalized type may not be well-formed,
463530
/// even if the source type is well-formed, so we should add an
@@ -484,5 +551,5 @@ pub struct Generalization<T> {
484551
/// will force the calling code to check that `WF(Foo<?C, ?D>)`
485552
/// holds, which in turn implies that `?C::Item == ?D`. So once
486553
/// `?C` is constrained, that should suffice to restrict `?D`.
487-
pub needs_wf: bool,
554+
pub(super) needs_wf: bool,
488555
}

Diff for: compiler/rustc_infer/src/infer/nll_relate/mod.rs

+5-1
Original file line numberDiff line numberDiff line change
@@ -214,13 +214,17 @@ where
214214
}
215215

216216
fn generalize(&mut self, ty: Ty<'tcx>, for_vid: ty::TyVid) -> RelateResult<'tcx, Ty<'tcx>> {
217-
let Generalization { value: ty, needs_wf: _ } = generalize::generalize(
217+
let Generalization { value_may_be_infer: ty, needs_wf: _ } = generalize::generalize(
218218
self.infcx,
219219
&mut self.delegate,
220220
ty,
221221
for_vid,
222222
self.ambient_variance,
223223
)?;
224+
225+
if ty.is_ty_var() {
226+
span_bug!(self.delegate.span(), "occurs check failure in MIR typeck");
227+
}
224228
Ok(ty)
225229
}
226230

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
WARN rustc_infer::infer::generalize may incompletely handle alias type: Alias(Projection, AliasTy { args: [*const ?1t, ReBound(DebruijnIndex(0), BoundRegion { var: 0, kind: BrNamed(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1), 'a) })], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit) })
2+
WARN rustc_infer::infer::generalize may incompletely handle alias type: Alias(Projection, AliasTy { args: [*const ?1t, RePlaceholder(!2_BoundRegion { var: 0, kind: BrNamed(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1), 'a) })], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit) })
3+
WARN rustc_infer::infer::generalize may incompletely handle alias type: Alias(Projection, AliasTy { args: [*const ?1t, ReBound(DebruijnIndex(0), BoundRegion { var: 0, kind: BrNamed(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1), 'a) })], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit) })
4+
WARN rustc_infer::infer::generalize may incompletely handle alias type: Alias(Projection, AliasTy { args: [*const ?1t, RePlaceholder(!2_BoundRegion { var: 0, kind: BrNamed(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1), 'a) })], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit) })
5+
WARN rustc_infer::infer::generalize may incompletely handle alias type: Alias(Projection, AliasTy { args: [*const ?1t, ReBound(DebruijnIndex(0), BoundRegion { var: 0, kind: BrNamed(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1), 'a) })], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit) })
6+
WARN rustc_infer::infer::generalize may incompletely handle alias type: Alias(Projection, AliasTy { args: [*const ?1t, RePlaceholder(!2_BoundRegion { var: 0, kind: BrNamed(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1), 'a) })], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit) })
7+
WARN rustc_infer::infer::generalize may incompletely handle alias type: Alias(Projection, AliasTy { args: [*const ?1t, ReBound(DebruijnIndex(0), BoundRegion { var: 0, kind: BrNamed(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1), 'a) })], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit) })
8+
WARN rustc_infer::infer::generalize may incompletely handle alias type: Alias(Projection, AliasTy { args: [*const ?1t, RePlaceholder(!2_BoundRegion { var: 0, kind: BrNamed(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1), 'a) })], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit) })
9+
error[E0119]: conflicting implementations of trait `Overlap<for<'a> fn(&'a (), ())>` for type `for<'a> fn(&'a (), ())`
10+
--> $DIR/associated-type.rs:31:1
11+
|
12+
LL | impl<T> Overlap<T> for T {
13+
| ------------------------ first implementation here
14+
...
15+
LL | / impl<T> Overlap<for<'a> fn(&'a (), Assoc<'a, T>)> for T
16+
LL | |
17+
LL | | where
18+
LL | | for<'a> *const T: ToUnit<'a>,
19+
| |_________________________________^ conflicting implementation for `for<'a> fn(&'a (), ())`
20+
|
21+
= note: this behavior recently changed as a result of a bug fix; see rust-lang/rust#56105 for details
22+
23+
error: aborting due to 1 previous error
24+
25+
For more information about this error, try `rustc --explain E0119`.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
WARN rustc_infer::infer::generalize may incompletely handle alias type: Alias(Projection, AliasTy { args: [*const ?1t, ReBound(DebruijnIndex(0), BoundRegion { var: 0, kind: BrNamed(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1), 'a) })], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit) })
2+
WARN rustc_infer::infer::generalize may incompletely handle alias type: Alias(Projection, AliasTy { args: [*const ?1t, RePlaceholder(!3_BoundRegion { var: 0, kind: BrNamed(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1), 'a) })], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit) })
3+
WARN rustc_infer::infer::generalize may incompletely handle alias type: Alias(Projection, AliasTy { args: [*const ?1t, ReBound(DebruijnIndex(0), BoundRegion { var: 0, kind: BrNamed(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1), 'a) })], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit) })
4+
WARN rustc_infer::infer::generalize may incompletely handle alias type: Alias(Projection, AliasTy { args: [*const ?1t, RePlaceholder(!3_BoundRegion { var: 0, kind: BrNamed(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1), 'a) })], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit) })
5+
WARN rustc_infer::infer::generalize may incompletely handle alias type: Alias(Projection, AliasTy { args: [*const ?1t, ReBound(DebruijnIndex(0), BoundRegion { var: 0, kind: BrNamed(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1), 'a) })], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit) })
6+
WARN rustc_infer::infer::generalize may incompletely handle alias type: Alias(Projection, AliasTy { args: [*const ?1t, RePlaceholder(!3_BoundRegion { var: 0, kind: BrNamed(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1), 'a) })], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit) })
7+
WARN rustc_infer::infer::generalize may incompletely handle alias type: Alias(Projection, AliasTy { args: [*const ?1t, ReBound(DebruijnIndex(0), BoundRegion { var: 0, kind: BrNamed(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1), 'a) })], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit) })
8+
WARN rustc_infer::infer::generalize may incompletely handle alias type: Alias(Projection, AliasTy { args: [*const ?1t, RePlaceholder(!3_BoundRegion { var: 0, kind: BrNamed(DefId(0:27 ~ associated_type[f554]::{impl#3}::'a#1), 'a) })], def_id: DefId(0:5 ~ associated_type[f554]::ToUnit::Unit) })
9+
error[E0119]: conflicting implementations of trait `Overlap<for<'a> fn(&'a (), _)>` for type `for<'a> fn(&'a (), _)`
10+
--> $DIR/associated-type.rs:31:1
11+
|
12+
LL | impl<T> Overlap<T> for T {
13+
| ------------------------ first implementation here
14+
...
15+
LL | / impl<T> Overlap<for<'a> fn(&'a (), Assoc<'a, T>)> for T
16+
LL | |
17+
LL | | where
18+
LL | | for<'a> *const T: ToUnit<'a>,
19+
| |_________________________________^ conflicting implementation for `for<'a> fn(&'a (), _)`
20+
|
21+
= note: this behavior recently changed as a result of a bug fix; see rust-lang/rust#56105 for details
22+
23+
error: aborting due to 1 previous error
24+
25+
For more information about this error, try `rustc --explain E0119`.

Diff for: tests/ui/coherence/occurs-check/associated-type.rs

+45
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
// revisions: old next
2+
//[next] compile-flags: -Ztrait-solver=next
3+
4+
// A regression test for #105787
5+
6+
// Using the higher ranked projection hack to prevent us from replacing the projection
7+
// with an inference variable.
8+
trait ToUnit<'a> {
9+
type Unit;
10+
}
11+
12+
struct LocalTy;
13+
impl<'a> ToUnit<'a> for *const LocalTy {
14+
type Unit = ();
15+
}
16+
17+
impl<'a, T: Copy + ?Sized> ToUnit<'a> for *const T {
18+
type Unit = ();
19+
}
20+
21+
trait Overlap<T> {
22+
type Assoc;
23+
}
24+
25+
type Assoc<'a, T> = <*const T as ToUnit<'a>>::Unit;
26+
27+
impl<T> Overlap<T> for T {
28+
type Assoc = usize;
29+
}
30+
31+
impl<T> Overlap<for<'a> fn(&'a (), Assoc<'a, T>)> for T
32+
//~^ ERROR conflicting implementations of trait
33+
where
34+
for<'a> *const T: ToUnit<'a>,
35+
{
36+
type Assoc = Box<usize>;
37+
}
38+
39+
fn foo<T: Overlap<U>, U>(x: T::Assoc) -> T::Assoc {
40+
x
41+
}
42+
43+
fn main() {
44+
foo::<for<'a> fn(&'a (), ()), for<'a> fn(&'a (), ())>(3usize);
45+
}

Diff for: tests/ui/coherence/occurs-check/opaques.next.stderr

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
error[E0119]: conflicting implementations of trait `Trait<Alias<_>>` for type `Alias<_>`
2+
--> $DIR/opaques.rs:29:1
3+
|
4+
LL | impl<T> Trait<T> for T {
5+
| ---------------------- first implementation here
6+
...
7+
LL | impl<T> Trait<T> for defining_scope::Alias<T> {
8+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `Alias<_>`
9+
10+
error: aborting due to 1 previous error
11+
12+
For more information about this error, try `rustc --explain E0119`.

0 commit comments

Comments
 (0)