Skip to content

Commit

Permalink
cleanup and comments
Browse files Browse the repository at this point in the history
  • Loading branch information
lcnr committed Oct 24, 2023
1 parent 3f53f55 commit ea6e393
Show file tree
Hide file tree
Showing 11 changed files with 231 additions and 27 deletions.
16 changes: 9 additions & 7 deletions compiler/rustc_infer/src/infer/combine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -326,18 +326,16 @@ 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,
target_vid,
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 {
Expand Down Expand Up @@ -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,
Expand All @@ -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 {
Expand Down Expand Up @@ -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(),
Expand Down
39 changes: 23 additions & 16 deletions compiler/rustc_infer/src/infer/generalize.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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`.
Expand Down Expand Up @@ -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)),
Expand All @@ -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");
Expand Down Expand Up @@ -500,20 +507,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
Expand Down
5 changes: 2 additions & 3 deletions compiler/rustc_infer/src/infer/nll_relate/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -217,17 +217,16 @@ 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,
for_vid,
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)
}
Expand Down
8 changes: 7 additions & 1 deletion compiler/rustc_trait_selection/src/traits/coherence.rs
Original file line number Diff line number Diff line change
Expand Up @@ -930,7 +930,13 @@ impl<'a, 'tcx> ProofTreeVisitor<'tcx> for AmbiguityCausesVisitor<'a> {
let trait_ref = match predicate.kind().no_bound_vars() {
Some(ty::PredicateKind::Clause(ty::ClauseKind::Trait(tr))) => tr.trait_ref,
Some(ty::PredicateKind::Clause(ty::ClauseKind::Projection(proj))) => {
proj.projection_ty.trait_ref(infcx.tcx)
match proj.projection_ty.kind(infcx.tcx) {
ty::AliasKind::Projection => proj.projection_ty.trait_ref(infcx.tcx),
ty::AliasKind::Inherent | ty::AliasKind::Opaque | ty::AliasKind::Weak => {
// No intercrate ambiguity causes here.
return ControlFlow::Continue(());
}
}
}
_ => return ControlFlow::Continue(()),
};
Expand Down
25 changes: 25 additions & 0 deletions tests/ui/coherence/occurs-check/associated-type.next.stderr
Original file line number Diff line number Diff line change
@@ -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`.
25 changes: 25 additions & 0 deletions tests/ui/coherence/occurs-check/associated-type.old.stderr
Original file line number Diff line number Diff line change
@@ -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`.
45 changes: 45 additions & 0 deletions tests/ui/coherence/occurs-check/associated-type.rs
Original file line number Diff line number Diff line change
@@ -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);
}
12 changes: 12 additions & 0 deletions tests/ui/coherence/occurs-check/opaques.next.stderr
Original file line number Diff line number Diff line change
@@ -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`.
37 changes: 37 additions & 0 deletions tests/ui/coherence/occurs-check/opaques.rs
Original file line number Diff line number Diff line change
@@ -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);
}
Original file line number Diff line number Diff line change
@@ -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`.
37 changes: 37 additions & 0 deletions tests/ui/traits/new-solver/generalize/occurs-check-nested-alias.rs
Original file line number Diff line number Diff line change
@@ -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);
}

0 comments on commit ea6e393

Please sign in to comment.