about summary refs log tree commit diff
diff options
context:
space:
mode:
authorlcnr <rust@lcnr.de>2023-10-23 16:49:44 +0200
committerlcnr <rust@lcnr.de>2023-12-04 10:39:00 +0100
commitf69d67221e8333e064620279b46c7c9bcaa88310 (patch)
tree4614bf2e568b95a3606cb0542d6657475066a140
parent2d0ec174e4d9e44347ad121200b8a24f5fd1dc60 (diff)
downloadrust-f69d67221e8333e064620279b46c7c9bcaa88310.tar.gz
rust-f69d67221e8333e064620279b46c7c9bcaa88310.zip
generalize: handle occurs check failure in aliases
-rw-r--r--compiler/rustc_infer/src/infer/combine.rs60
-rw-r--r--compiler/rustc_infer/src/infer/generalize.rs76
-rw-r--r--compiler/rustc_infer/src/infer/nll_relate/mod.rs7
-rw-r--r--tests/ui/traits/new-solver/equating-projection-cyclically.rs5
-rw-r--r--tests/ui/traits/new-solver/equating-projection-cyclically.stderr9
5 files changed, 124 insertions, 33 deletions
diff --git a/compiler/rustc_infer/src/infer/combine.rs b/compiler/rustc_infer/src/infer/combine.rs
index c3d07415bb8..862896e0404 100644
--- a/compiler/rustc_infer/src/infer/combine.rs
+++ b/compiler/rustc_infer/src/infer/combine.rs
@@ -334,6 +334,10 @@ 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 {
@@ -445,7 +449,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: b_ty, needs_wf } = generalize::generalize(
+        let Generalization { value, needs_wf } = generalize::generalize(
             self.infcx,
             &mut CombineDelegate {
                 infcx: self.infcx,
@@ -457,7 +461,7 @@ impl<'infcx, 'tcx> CombineFields<'infcx, 'tcx> {
             ambient_variance,
         )?;
 
-        debug!(?b_ty);
+        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 {
@@ -477,19 +481,47 @@ impl<'infcx, 'tcx> CombineFields<'infcx, 'tcx> {
         // relations wind up attributed to the same spans. We need
         // to associate causes/spans with each of the relations in
         // the stack to get this right.
-        match ambient_variance {
-            ty::Variance::Invariant => self.equate(a_is_expected).relate(a_ty, b_ty),
-            ty::Variance::Covariant => self.sub(a_is_expected).relate(a_ty, b_ty),
-            ty::Variance::Contravariant => self.sub(a_is_expected).relate_with_variance(
-                ty::Contravariant,
-                ty::VarianceDiagInfo::default(),
-                a_ty,
-                b_ty,
-            ),
-            ty::Variance::Bivariant => {
-                unreachable!("no code should be generalizing bivariantly (currently)")
+        if b_ty.is_ty_var() {
+            // This happens for cases like `<?0 as Trait>::Assoc == ?0`.
+            // We can't instantiate `?0` here as that would result in a
+            // cyclic type. We instead delay the unification in case
+            // the alias can be normalized to something which does not
+            // mention `?0`.
+
+            // FIXME(-Ztrait-solver=next): replace this with `AliasRelate`
+            let &ty::Alias(kind, data) = a_ty.kind() else {
+                bug!("generalization should only result in infer vars for aliases");
+            };
+            if !self.infcx.next_trait_solver() {
+                // The old solver only accepts projection predicates for associated types.
+                match kind {
+                    ty::AliasKind::Projection => {}
+                    ty::AliasKind::Inherent | ty::AliasKind::Weak | ty::AliasKind::Opaque => {
+                        return Err(TypeError::CyclicTy(a_ty));
+                    }
+                }
             }
-        }?;
+            self.obligations.push(Obligation::new(
+                self.tcx(),
+                self.trace.cause.clone(),
+                self.param_env,
+                ty::ProjectionPredicate { projection_ty: data, term: b_ty.into() },
+            ))
+        } else {
+            match ambient_variance {
+                ty::Variance::Invariant => self.equate(a_is_expected).relate(a_ty, b_ty),
+                ty::Variance::Covariant => self.sub(a_is_expected).relate(a_ty, b_ty),
+                ty::Variance::Contravariant => self.sub(a_is_expected).relate_with_variance(
+                    ty::Contravariant,
+                    ty::VarianceDiagInfo::default(),
+                    a_ty,
+                    b_ty,
+                ),
+                ty::Variance::Bivariant => {
+                    unreachable!("no code should be generalizing bivariantly (currently)")
+                }
+            }?;
+        }
 
         Ok(())
     }
diff --git a/compiler/rustc_infer/src/infer/generalize.rs b/compiler/rustc_infer/src/infer/generalize.rs
index 05037611262..ba0fa2ff2ae 100644
--- a/compiler/rustc_infer/src/infer/generalize.rs
+++ b/compiler/rustc_infer/src/infer/generalize.rs
@@ -1,13 +1,16 @@
+use std::mem;
+
 use rustc_data_structures::sso::SsoHashMap;
 use rustc_hir::def_id::DefId;
 use rustc_middle::infer::unify_key::{ConstVarValue, ConstVariableValue};
 use rustc_middle::ty::error::TypeError;
 use rustc_middle::ty::relate::{self, Relate, RelateResult, TypeRelation};
-use rustc_middle::ty::{self, InferConst, Term, Ty, TyCtxt, TypeVisitableExt};
+use rustc_middle::ty::visit::MaxUniverse;
+use rustc_middle::ty::{self, InferConst, Term, Ty, TyCtxt, TypeVisitable, TypeVisitableExt};
 use rustc_span::Span;
 
 use crate::infer::nll_relate::TypeRelatingDelegate;
-use crate::infer::type_variable::TypeVariableValue;
+use crate::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind, TypeVariableValue};
 use crate::infer::{InferCtxt, RegionVariableOrigin};
 
 /// Attempts to generalize `term` for the type variable `for_vid`.
@@ -38,6 +41,7 @@ pub(super) fn generalize<'tcx, D: GeneralizerDelegate<'tcx>, T: Into<Term<'tcx>>
         root_vid,
         for_universe,
         root_term: term.into(),
+        in_alias: false,
         needs_wf: false,
         cache: Default::default(),
     };
@@ -45,20 +49,22 @@ 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 needs_wf = generalizer.needs_wf;
-    Ok(Generalization { value, needs_wf })
+    Ok(Generalization { value: HandleProjection(value), needs_wf })
 }
 
 /// Abstracts the handling of region vars between HIR and MIR/NLL typechecking
 /// in the generalizer code.
-pub trait GeneralizerDelegate<'tcx> {
+pub(super) trait GeneralizerDelegate<'tcx> {
     fn param_env(&self) -> ty::ParamEnv<'tcx>;
 
     fn forbid_inference_vars() -> bool;
 
+    fn span(&self) -> Span;
+
     fn generalize_region(&mut self, universe: ty::UniverseIndex) -> ty::Region<'tcx>;
 }
 
-pub struct CombineDelegate<'cx, 'tcx> {
+pub(super) struct CombineDelegate<'cx, 'tcx> {
     pub infcx: &'cx InferCtxt<'tcx>,
     pub param_env: ty::ParamEnv<'tcx>,
     pub span: Span,
@@ -73,6 +79,10 @@ impl<'tcx> GeneralizerDelegate<'tcx> for CombineDelegate<'_, 'tcx> {
         false
     }
 
+    fn span(&self) -> Span {
+        self.span
+    }
+
     fn generalize_region(&mut self, universe: ty::UniverseIndex) -> ty::Region<'tcx> {
         // FIXME: This is non-ideal because we don't give a
         // very descriptive origin for this region variable.
@@ -93,6 +103,10 @@ where
         <Self as TypeRelatingDelegate<'tcx>>::forbid_inference_vars()
     }
 
+    fn span(&self) -> Span {
+        <Self as TypeRelatingDelegate<'tcx>>::span(&self)
+    }
+
     fn generalize_region(&mut self, universe: ty::UniverseIndex) -> ty::Region<'tcx> {
         <Self as TypeRelatingDelegate<'tcx>>::generalize_existential(self, universe)
     }
@@ -139,6 +153,12 @@ 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`.
+    in_alias: bool,
+
     /// See the field `needs_wf` in `Generalization`.
     needs_wf: bool,
 }
@@ -309,6 +329,38 @@ where
                 }
             }
 
+            ty::Alias(kind, data) => {
+                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)),
+                    Err(e) => {
+                        if is_nested_alias {
+                            return Err(e);
+                        } else {
+                            let mut visitor = MaxUniverse::new();
+                            t.visit_with(&mut visitor);
+                            let infer_replacement_is_complete =
+                                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:?}");
+                            }
+
+                            debug!("generalization failure in alias");
+                            Ok(self.infcx.next_ty_var_in_universe(
+                                TypeVariableOrigin {
+                                    kind: TypeVariableOriginKind::MiscVariable,
+                                    span: self.delegate.span(),
+                                },
+                                self.for_universe,
+                            ))
+                        }
+                    }
+                };
+                self.in_alias = is_nested_alias;
+                result
+            }
+
             _ => relate::structurally_relate_tys(self, t, t),
         }?;
 
@@ -452,12 +504,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 struct Generalization<T> {
-    pub value: T,
+pub(super) struct Generalization<T> {
+    pub(super) value: HandleProjection<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
@@ -484,5 +544,5 @@ pub struct Generalization<T> {
     /// will force the calling code to check that `WF(Foo<?C, ?D>)`
     /// holds, which in turn implies that `?C::Item == ?D`. So once
     /// `?C` is constrained, that should suffice to restrict `?D`.
-    pub needs_wf: bool,
+    pub(super) needs_wf: bool,
 }
diff --git a/compiler/rustc_infer/src/infer/nll_relate/mod.rs b/compiler/rustc_infer/src/infer/nll_relate/mod.rs
index 77c1d6a7313..b0b99854cc6 100644
--- a/compiler/rustc_infer/src/infer/nll_relate/mod.rs
+++ b/compiler/rustc_infer/src/infer/nll_relate/mod.rs
@@ -214,13 +214,18 @@ where
     }
 
     fn generalize(&mut self, ty: Ty<'tcx>, for_vid: ty::TyVid) -> RelateResult<'tcx, Ty<'tcx>> {
-        let Generalization { value: ty, needs_wf: _ } = generalize::generalize(
+        let Generalization { value, 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");
+        }
         Ok(ty)
     }
 
diff --git a/tests/ui/traits/new-solver/equating-projection-cyclically.rs b/tests/ui/traits/new-solver/equating-projection-cyclically.rs
index 2668da1b745..845597e9ce1 100644
--- a/tests/ui/traits/new-solver/equating-projection-cyclically.rs
+++ b/tests/ui/traits/new-solver/equating-projection-cyclically.rs
@@ -1,3 +1,4 @@
+// check-pass
 // compile-flags: -Ztrait-solver=next
 
 trait Test {
@@ -22,7 +23,9 @@ fn main() {
     let mut x: Inv<_> = Inv(None);
     // This ends up equating `Inv<?x>` with `Inv<<?x as Test>::Assoc>`
     // which fails the occurs check when generalizing `?x`.
+    //
+    // We end up emitting a delayed obligation, causing this to still
+    // succeed.
     x = transform(x);
-    //~^ ERROR mismatched types
     x = Inv::<i32>(None);
 }
diff --git a/tests/ui/traits/new-solver/equating-projection-cyclically.stderr b/tests/ui/traits/new-solver/equating-projection-cyclically.stderr
deleted file mode 100644
index 91dd3ebc31b..00000000000
--- a/tests/ui/traits/new-solver/equating-projection-cyclically.stderr
+++ /dev/null
@@ -1,9 +0,0 @@
-error[E0308]: mismatched types
-  --> $DIR/equating-projection-cyclically.rs:25:9
-   |
-LL |     x = transform(x);
-   |         ^^^^^^^^^^^^ cyclic type of infinite size
-
-error: aborting due to 1 previous error
-
-For more information about this error, try `rustc --explain E0308`.