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> }; 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>>, - /// 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 `::Assoc`. + /// This is set once we're generalizing the arguments of an alias. + /// + /// This is necessary to correctly handle + /// `::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); -impl HandleProjection { - 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 { - pub(super) value: HandleProjection, + /// When generalizing `::Assoc` or + /// `::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 fn(&'a (), ())>` for type `for<'a> fn(&'a (), ())` + --> $DIR/associated-type.rs:31:1 + | +LL | impl Overlap for T { + | ------------------------ first implementation here +... +LL | / impl Overlap 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 fn(&'a (), _)>` for type `for<'a> fn(&'a (), _)` + --> $DIR/associated-type.rs:31:1 + | +LL | impl Overlap for T { + | ------------------------ first implementation here +... +LL | / impl Overlap 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 { + type Assoc; +} + +type Assoc<'a, T> = <*const T as ToUnit<'a>>::Unit; + +impl Overlap for T { + type Assoc = usize; +} + +impl Overlap fn(&'a (), Assoc<'a, T>)> for T +//~^ ERROR conflicting implementations of trait +where + for<'a> *const T: ToUnit<'a>, +{ + type Assoc = Box; +} + +fn foo, U>(x: T::Assoc) -> T::Assoc { + x +} + +fn main() { + foo:: 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>` for type `Alias<_>` + --> $DIR/opaques.rs:29:1 + | +LL | impl Trait for T { + | ---------------------- first implementation here +... +LL | impl Trait for defining_scope::Alias { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 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 = impl Sized; + + pub fn cast(x: Container, T>) -> Container { + x + } +} + +struct Container, U> { + x: >::Assoc, +} + +trait Trait { + type Assoc; +} + +impl Trait for T { + type Assoc = Box; +} +impl Trait for defining_scope::Alias { + //[next]~^ ERROR conflicting implementations of trait + type Assoc = usize; +} + +fn main() { + let x: Box = 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 `<>::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 { + type Id; +} +impl Id for U { + type Id = U; +} + +struct Inv(*mut T); + +fn unconstrained() -> T { + todo!() +} + +fn create( + x: &U, +) -> (Inv, Inv<<>::Id as Unnormalizable>::Assoc>) { + todo!() +} + +fn foo() { + let q = unconstrained(); + let (mut x, y) = create::<_, _>(&q); + x = y; + drop::(q); +}