about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--compiler/rustc_infer/src/infer/combine.rs16
-rw-r--r--compiler/rustc_infer/src/infer/generalize.rs39
-rw-r--r--compiler/rustc_infer/src/infer/nll_relate/mod.rs5
-rw-r--r--tests/ui/coherence/occurs-check/associated-type.next.stderr25
-rw-r--r--tests/ui/coherence/occurs-check/associated-type.old.stderr25
-rw-r--r--tests/ui/coherence/occurs-check/associated-type.rs45
-rw-r--r--tests/ui/coherence/occurs-check/opaques.next.stderr12
-rw-r--r--tests/ui/coherence/occurs-check/opaques.rs37
-rw-r--r--tests/ui/traits/new-solver/generalize/occurs-check-nested-alias.next.stderr9
-rw-r--r--tests/ui/traits/new-solver/generalize/occurs-check-nested-alias.rs37
10 files changed, 224 insertions, 26 deletions
diff --git a/compiler/rustc_infer/src/infer/combine.rs b/compiler/rustc_infer/src/infer/combine.rs
index 862896e0404..759ebaa1d1e 100644
--- a/compiler/rustc_infer/src/infer/combine.rs
+++ b/compiler/rustc_infer/src/infer/combine.rs
@@ -326,7 +326,9 @@ impl<'tcx> InferCtxt<'tcx> {
     ) -> RelateResult<'tcx, ty::Const<'tcx>> {
         let span =
             self.inner.borrow_mut().const_unification_table().probe_value(target_vid).origin.span;
-        let Generalization { value, needs_wf: _ } = generalize::generalize(
+        // FIXME(generic_const_exprs): Occurs check failures for unevaluated
+        // constants and generic expressions are not yet handled correctly.
+        let Generalization { value_may_be_infer: value, needs_wf: _ } = generalize::generalize(
             self,
             &mut CombineDelegate { infcx: self, span, param_env },
             ct,
@@ -334,10 +336,6 @@ impl<'tcx> InferCtxt<'tcx> {
             ty::Variance::Invariant,
         )?;
 
-        // FIXME(generic_const_exprs): Occurs check failures for unevaluated
-        // constants and generic expressions are not yet handled correctly.
-        let value = value.may_be_infer();
-
         self.inner.borrow_mut().const_unification_table().union_value(
             target_vid,
             ConstVarValue {
@@ -449,7 +447,7 @@ impl<'infcx, 'tcx> CombineFields<'infcx, 'tcx> {
         // `'?2` and `?3` are fresh region/type inference
         // variables. (Down below, we will relate `a_ty <: b_ty`,
         // adding constraints like `'x: '?2` and `?1 <: ?3`.)
-        let Generalization { value, needs_wf } = generalize::generalize(
+        let Generalization { value_may_be_infer: b_ty, needs_wf } = generalize::generalize(
             self.infcx,
             &mut CombineDelegate {
                 infcx: self.infcx,
@@ -461,7 +459,6 @@ impl<'infcx, 'tcx> CombineFields<'infcx, 'tcx> {
             ambient_variance,
         )?;
 
-        let b_ty = value.may_be_infer(); // we handle this further down.
         self.infcx.inner.borrow_mut().type_variables().instantiate(b_vid, b_ty);
 
         if needs_wf {
@@ -501,6 +498,11 @@ impl<'infcx, 'tcx> CombineFields<'infcx, 'tcx> {
                     }
                 }
             }
+
+            // FIXME: This does not handle subtyping correctly, we should switch to
+            // alias-relate in the new solver and could instead create a new inference
+            // variable for `a_ty`, emitting `Projection(a_ty, a_infer)` and
+            // `a_infer <: b_ty`.
             self.obligations.push(Obligation::new(
                 self.tcx(),
                 self.trace.cause.clone(),
diff --git a/compiler/rustc_infer/src/infer/generalize.rs b/compiler/rustc_infer/src/infer/generalize.rs
index ba0fa2ff2ae..4b4017cec57 100644
--- a/compiler/rustc_infer/src/infer/generalize.rs
+++ b/compiler/rustc_infer/src/infer/generalize.rs
@@ -47,9 +47,9 @@ pub(super) fn generalize<'tcx, D: GeneralizerDelegate<'tcx>, T: Into<Term<'tcx>>
     };
 
     assert!(!term.has_escaping_bound_vars());
-    let value = generalizer.relate(term, term)?;
+    let value_may_be_infer = generalizer.relate(term, term)?;
     let needs_wf = generalizer.needs_wf;
-    Ok(Generalization { value: HandleProjection(value), needs_wf })
+    Ok(Generalization { value_may_be_infer, needs_wf })
 }
 
 /// Abstracts the handling of region vars between HIR and MIR/NLL typechecking
@@ -153,10 +153,11 @@ struct Generalizer<'me, 'tcx, D> {
 
     cache: SsoHashMap<Ty<'tcx>, Ty<'tcx>>,
 
-    /// This is set once we're generalizing the arguments of an alias. In case
-    /// we encounter an occurs check failure we generalize the alias to an
-    /// inference variable instead of erroring. This is necessary to avoid
-    /// incorrect errors when relating `?0` with `<?0 as Trait>::Assoc`.
+    /// This is set once we're generalizing the arguments of an alias.
+    ///
+    /// This is necessary to correctly handle
+    /// `<T as Bar<<?0 as Foo>::Assoc>::Assoc == ?0`. This equality can
+    /// hold by either normalizing the outer or the inner associated type.
     in_alias: bool,
 
     /// See the field `needs_wf` in `Generalization`.
@@ -330,6 +331,12 @@ where
             }
 
             ty::Alias(kind, data) => {
+                // An occurs check failure inside of an alias does not mean
+                // that the types definitely don't unify. We may be able
+                // to normalize the alias after all.
+                //
+                // We handle this by lazily equating the alias and generalizing
+                // it to an inference variable.
                 let is_nested_alias = mem::replace(&mut self.in_alias, true);
                 let result = match self.relate(data, data) {
                     Ok(data) => Ok(Ty::new_alias(self.tcx(), kind, data)),
@@ -343,7 +350,7 @@ where
                                 self.for_universe.can_name(visitor.max_universe())
                                     && !t.has_escaping_bound_vars();
                             if !infer_replacement_is_complete {
-                                warn!("incomplete generalization of an alias type: {t:?}");
+                                warn!("may incompletely handle alias type: {t:?}");
                             }
 
                             debug!("generalization failure in alias");
@@ -504,20 +511,20 @@ where
     }
 }
 
-#[derive(Debug)]
-pub(super) struct HandleProjection<T>(T);
-impl<T> HandleProjection<T> {
-    pub(super) fn may_be_infer(self) -> T {
-        self.0
-    }
-}
-
 /// Result from a generalization operation. This includes
 /// not only the generalized type, but also a bool flag
 /// indicating whether further WF checks are needed.
 #[derive(Debug)]
 pub(super) struct Generalization<T> {
-    pub(super) value: HandleProjection<T>,
+    /// When generalizing `<?0 as Trait>::Assoc` or
+    /// `<T as Bar<<?0 as Foo>::Assoc>>::Assoc`
+    /// for `?0` generalization returns an inference
+    /// variable.
+    ///
+    /// This has to be handled wotj care as it can
+    /// otherwise very easily result in infinite
+    /// recursion.
+    pub(super) value_may_be_infer: T,
 
     /// If true, then the generalized type may not be well-formed,
     /// even if the source type is well-formed, so we should add an
diff --git a/compiler/rustc_infer/src/infer/nll_relate/mod.rs b/compiler/rustc_infer/src/infer/nll_relate/mod.rs
index b0b99854cc6..d707c30206d 100644
--- a/compiler/rustc_infer/src/infer/nll_relate/mod.rs
+++ b/compiler/rustc_infer/src/infer/nll_relate/mod.rs
@@ -214,7 +214,7 @@ where
     }
 
     fn generalize(&mut self, ty: Ty<'tcx>, for_vid: ty::TyVid) -> RelateResult<'tcx, Ty<'tcx>> {
-        let Generalization { value, needs_wf: _ } = generalize::generalize(
+        let Generalization { value_may_be_infer: ty, needs_wf: _ } = generalize::generalize(
             self.infcx,
             &mut self.delegate,
             ty,
@@ -222,9 +222,8 @@ where
             self.ambient_variance,
         )?;
 
-        let ty = value.may_be_infer();
         if ty.is_ty_var() {
-            warn!("occurs check failure in MIR typeck");
+            span_bug!(self.delegate.span(), "occurs check failure in MIR typeck");
         }
         Ok(ty)
     }
diff --git a/tests/ui/coherence/occurs-check/associated-type.next.stderr b/tests/ui/coherence/occurs-check/associated-type.next.stderr
new file mode 100644
index 00000000000..0aa8e702ddf
--- /dev/null
+++ b/tests/ui/coherence/occurs-check/associated-type.next.stderr
@@ -0,0 +1,25 @@
+WARN rustc_infer::infer::generalize may incompletely handle alias type: Alias(Projection, AliasTy { args: [*const ?1t, ReLateBound(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) })
+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) })
+WARN rustc_infer::infer::generalize may incompletely handle alias type: Alias(Projection, AliasTy { args: [*const ?1t, ReLateBound(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) })
+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) })
+WARN rustc_infer::infer::generalize may incompletely handle alias type: Alias(Projection, AliasTy { args: [*const ?1t, ReLateBound(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) })
+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) })
+WARN rustc_infer::infer::generalize may incompletely handle alias type: Alias(Projection, AliasTy { args: [*const ?1t, ReLateBound(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) })
+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) })
+error[E0119]: conflicting implementations of trait `Overlap<for<'a> fn(&'a (), ())>` for type `for<'a> fn(&'a (), ())`
+  --> $DIR/associated-type.rs:31:1
+   |
+LL |   impl<T> Overlap<T> for T {
+   |   ------------------------ first implementation here
+...
+LL | / impl<T> Overlap<for<'a> fn(&'a (), Assoc<'a, T>)> for T
+LL | |
+LL | | where
+LL | |     for<'a> *const T: ToUnit<'a>,
+   | |_________________________________^ conflicting implementation for `for<'a> fn(&'a (), ())`
+   |
+   = note: this behavior recently changed as a result of a bug fix; see rust-lang/rust#56105 for details
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0119`.
diff --git a/tests/ui/coherence/occurs-check/associated-type.old.stderr b/tests/ui/coherence/occurs-check/associated-type.old.stderr
new file mode 100644
index 00000000000..918f6d19787
--- /dev/null
+++ b/tests/ui/coherence/occurs-check/associated-type.old.stderr
@@ -0,0 +1,25 @@
+WARN rustc_infer::infer::generalize may incompletely handle alias type: Alias(Projection, AliasTy { args: [*const ?1t, ReLateBound(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) })
+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) })
+WARN rustc_infer::infer::generalize may incompletely handle alias type: Alias(Projection, AliasTy { args: [*const ?1t, ReLateBound(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) })
+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) })
+WARN rustc_infer::infer::generalize may incompletely handle alias type: Alias(Projection, AliasTy { args: [*const ?1t, ReLateBound(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) })
+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) })
+WARN rustc_infer::infer::generalize may incompletely handle alias type: Alias(Projection, AliasTy { args: [*const ?1t, ReLateBound(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) })
+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) })
+error[E0119]: conflicting implementations of trait `Overlap<for<'a> fn(&'a (), _)>` for type `for<'a> fn(&'a (), _)`
+  --> $DIR/associated-type.rs:31:1
+   |
+LL |   impl<T> Overlap<T> for T {
+   |   ------------------------ first implementation here
+...
+LL | / impl<T> Overlap<for<'a> fn(&'a (), Assoc<'a, T>)> for T
+LL | |
+LL | | where
+LL | |     for<'a> *const T: ToUnit<'a>,
+   | |_________________________________^ conflicting implementation for `for<'a> fn(&'a (), _)`
+   |
+   = note: this behavior recently changed as a result of a bug fix; see rust-lang/rust#56105 for details
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0119`.
diff --git a/tests/ui/coherence/occurs-check/associated-type.rs b/tests/ui/coherence/occurs-check/associated-type.rs
new file mode 100644
index 00000000000..909551f65be
--- /dev/null
+++ b/tests/ui/coherence/occurs-check/associated-type.rs
@@ -0,0 +1,45 @@
+// revisions: old next
+//[next] compile-flags: -Ztrait-solver=next
+
+// A regression test for #105787
+
+// Using the higher ranked projection hack to prevent us from replacing the projection
+// with an inference variable.
+trait ToUnit<'a> {
+    type Unit;
+}
+
+struct LocalTy;
+impl<'a> ToUnit<'a> for *const LocalTy {
+    type Unit = ();
+}
+
+impl<'a, T: Copy + ?Sized> ToUnit<'a> for *const T {
+    type Unit = ();
+}
+
+trait Overlap<T> {
+    type Assoc;
+}
+
+type Assoc<'a, T> = <*const T as ToUnit<'a>>::Unit;
+
+impl<T> Overlap<T> for T {
+    type Assoc = usize;
+}
+
+impl<T> Overlap<for<'a> fn(&'a (), Assoc<'a, T>)> for T
+//~^ ERROR conflicting implementations of trait
+where
+    for<'a> *const T: ToUnit<'a>,
+{
+    type Assoc = Box<usize>;
+}
+
+fn foo<T: Overlap<U>, U>(x: T::Assoc) -> T::Assoc {
+    x
+}
+
+fn main() {
+    foo::<for<'a> fn(&'a (), ()), for<'a> fn(&'a (), ())>(3usize);
+}
diff --git a/tests/ui/coherence/occurs-check/opaques.next.stderr b/tests/ui/coherence/occurs-check/opaques.next.stderr
new file mode 100644
index 00000000000..57e2f3ae7f5
--- /dev/null
+++ b/tests/ui/coherence/occurs-check/opaques.next.stderr
@@ -0,0 +1,12 @@
+error[E0119]: conflicting implementations of trait `Trait<Alias<_>>` for type `Alias<_>`
+  --> $DIR/opaques.rs:29:1
+   |
+LL | impl<T> Trait<T> for T {
+   | ---------------------- first implementation here
+...
+LL | impl<T> Trait<T> for defining_scope::Alias<T> {
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `Alias<_>`
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0119`.
diff --git a/tests/ui/coherence/occurs-check/opaques.rs b/tests/ui/coherence/occurs-check/opaques.rs
new file mode 100644
index 00000000000..9d31a3dc82d
--- /dev/null
+++ b/tests/ui/coherence/occurs-check/opaques.rs
@@ -0,0 +1,37 @@
+//revisions: old next
+//[next] compile-flags: -Ztrait-solver=next
+
+// A regression test for #105787
+
+//[old] known-bug: #105787
+//[old] check-pass
+#![feature(type_alias_impl_trait)]
+mod defining_scope {
+    use super::*;
+    pub type Alias<T> = impl Sized;
+
+    pub fn cast<T>(x: Container<Alias<T>, T>) -> Container<T, T> {
+        x
+    }
+}
+
+struct Container<T: Trait<U>, U> {
+    x: <T as Trait<U>>::Assoc,
+}
+
+trait Trait<T> {
+    type Assoc;
+}
+
+impl<T> Trait<T> for T {
+    type Assoc = Box<u32>;
+}
+impl<T> Trait<T> for defining_scope::Alias<T> {
+    //[next]~^ ERROR conflicting implementations of trait
+    type Assoc = usize;
+}
+
+fn main() {
+    let x: Box<u32> = defining_scope::cast::<()>(Container { x: 0 }).x;
+    println!("{}", *x);
+}
diff --git a/tests/ui/traits/new-solver/generalize/occurs-check-nested-alias.next.stderr b/tests/ui/traits/new-solver/generalize/occurs-check-nested-alias.next.stderr
new file mode 100644
index 00000000000..7e74ce0a5eb
--- /dev/null
+++ b/tests/ui/traits/new-solver/generalize/occurs-check-nested-alias.next.stderr
@@ -0,0 +1,9 @@
+error[E0271]: type mismatch resolving `<<T as Id<_>>::Id as Unnormalizable>::Assoc == _`
+  --> $DIR/occurs-check-nested-alias.rs:31:9
+   |
+LL |     x = y;
+   |         ^ types differ
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0271`.
diff --git a/tests/ui/traits/new-solver/generalize/occurs-check-nested-alias.rs b/tests/ui/traits/new-solver/generalize/occurs-check-nested-alias.rs
new file mode 100644
index 00000000000..a2113b2a8b3
--- /dev/null
+++ b/tests/ui/traits/new-solver/generalize/occurs-check-nested-alias.rs
@@ -0,0 +1,37 @@
+// revisions: old next
+//[old] check-pass
+
+// Need to emit an alias-relate instead of a `Projection` goal here.
+//[next] compile-flags: -Ztrait-solver=next
+//[next] known-bug: trait-system-refactor-initiative#8
+#![crate_type = "lib"]
+#![allow(unused)]
+trait Unnormalizable {
+    type Assoc;
+}
+
+trait Id<T> {
+    type Id;
+}
+impl<T, U> Id<T> for U {
+    type Id = U;
+}
+
+struct Inv<T>(*mut T);
+
+fn unconstrained<T>() -> T {
+    todo!()
+}
+
+fn create<T, U: Unnormalizable>(
+    x: &U,
+) -> (Inv<T>, Inv<<<U as Id<T>>::Id as Unnormalizable>::Assoc>) {
+    todo!()
+}
+
+fn foo<T: Unnormalizable>() {
+    let q = unconstrained();
+    let (mut x, y) = create::<_, _>(&q);
+    x = y;
+    drop::<T>(q);
+}