about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2020-09-21 12:52:09 +0000
committerbors <bors@rust-lang.org>2020-09-21 12:52:09 +0000
commite0bf356f9e5f6a8cca1eb656e900ffba79340fa1 (patch)
tree1f5346f9558d02736fecc63390cbdc1b01f67215
parent956e06c6c85e918524b67503c4d65c7baf539585 (diff)
parent2855b92eb4fcc32d940de71dfd5eb383eaecc872 (diff)
downloadrust-e0bf356f9e5f6a8cca1eb656e900ffba79340fa1.tar.gz
rust-e0bf356f9e5f6a8cca1eb656e900ffba79340fa1.zip
Auto merge of #74040 - lcnr:const-occurs-check, r=nikomatsakis
fix unification of const variables

r? `@nikomatsakis` `@varkor` `@eddyb` let's just ping everyone here :sweat_smile:
-rw-r--r--compiler/rustc_infer/src/infer/combine.rs242
-rw-r--r--compiler/rustc_middle/src/ty/error.rs8
-rw-r--r--compiler/rustc_middle/src/ty/structural_impls.rs1
-rw-r--r--compiler/rustc_mir/src/interpret/eval_context.rs6
-rw-r--r--compiler/rustc_mir/src/interpret/operand.rs2
-rw-r--r--compiler/rustc_mir/src/interpret/terminator.rs7
-rw-r--r--src/test/ui/const-generics/issues/issue-69654-run-pass.rs18
-rw-r--r--src/test/ui/const-generics/issues/issue-69654.rs18
-rw-r--r--src/test/ui/const-generics/issues/issue-69654.stderr9
-rw-r--r--src/test/ui/const-generics/occurs-check/bind-param.rs17
-rw-r--r--src/test/ui/const-generics/occurs-check/unify-fixpoint.rs18
-rw-r--r--src/test/ui/const-generics/occurs-check/unify-fixpoint.stderr27
-rw-r--r--src/test/ui/const-generics/occurs-check/unify-n-nplusone.rs17
-rw-r--r--src/test/ui/const-generics/occurs-check/unify-n-nplusone.stderr10
-rw-r--r--src/test/ui/const-generics/occurs-check/unused-substs-1.rs14
-rw-r--r--src/test/ui/const-generics/occurs-check/unused-substs-2.rs27
-rw-r--r--src/test/ui/const-generics/occurs-check/unused-substs-3.rs18
-rw-r--r--src/test/ui/const-generics/occurs-check/unused-substs-4.rs12
18 files changed, 452 insertions, 19 deletions
diff --git a/compiler/rustc_infer/src/infer/combine.rs b/compiler/rustc_infer/src/infer/combine.rs
index 5bd6c667fd7..33d26317c71 100644
--- a/compiler/rustc_infer/src/infer/combine.rs
+++ b/compiler/rustc_infer/src/infer/combine.rs
@@ -45,7 +45,7 @@ use rustc_middle::ty::relate::{self, Relate, RelateResult, TypeRelation};
 use rustc_middle::ty::subst::SubstsRef;
 use rustc_middle::ty::{self, InferConst, ToPredicate, Ty, TyCtxt, TypeFoldable};
 use rustc_middle::ty::{IntType, UintType};
-use rustc_span::DUMMY_SP;
+use rustc_span::{Span, DUMMY_SP};
 
 /// Small-storage-optimized implementation of a map
 /// made specifically for caching results.
@@ -219,11 +219,11 @@ impl<'infcx, 'tcx> InferCtxt<'infcx, 'tcx> {
             }
 
             (ty::ConstKind::Infer(InferConst::Var(vid)), _) => {
-                return self.unify_const_variable(a_is_expected, vid, b);
+                return self.unify_const_variable(relation.param_env(), vid, b, a_is_expected);
             }
 
             (_, ty::ConstKind::Infer(InferConst::Var(vid))) => {
-                return self.unify_const_variable(!a_is_expected, vid, a);
+                return self.unify_const_variable(relation.param_env(), vid, a, !a_is_expected);
             }
             (ty::ConstKind::Unevaluated(..), _) if self.tcx.lazy_normalization() => {
                 // FIXME(#59490): Need to remove the leak check to accommodate
@@ -247,17 +247,66 @@ impl<'infcx, 'tcx> InferCtxt<'infcx, 'tcx> {
         ty::relate::super_relate_consts(relation, a, b)
     }
 
-    pub fn unify_const_variable(
+    /// Unifies the const variable `target_vid` with the given constant.
+    ///
+    /// This also tests if the given const `ct` contains an inference variable which was previously
+    /// unioned with `target_vid`. If this is the case, inferring `target_vid` to `ct`
+    /// would result in an infinite type as we continously replace an inference variable
+    /// in `ct` with `ct` itself.
+    ///
+    /// This is especially important as unevaluated consts use their parents generics.
+    /// They therefore often contain unused substs, making these errors far more likely.
+    ///
+    /// A good example of this is the following:
+    ///
+    /// ```rust
+    /// #![feature(const_generics)]
+    ///
+    /// fn bind<const N: usize>(value: [u8; N]) -> [u8; 3 + 4] {
+    ///     todo!()
+    /// }
+    ///
+    /// fn main() {
+    ///     let mut arr = Default::default();
+    ///     arr = bind(arr);
+    /// }
+    /// ```
+    ///
+    /// Here `3 + 4` ends up as `ConstKind::Unevaluated` which uses the generics
+    /// of `fn bind` (meaning that its substs contain `N`).
+    ///
+    /// `bind(arr)` now infers that the type of `arr` must be `[u8; N]`.
+    /// The assignment `arr = bind(arr)` now tries to equate `N` with `3 + 4`.
+    ///
+    /// As `3 + 4` contains `N` in its substs, this must not succeed.
+    ///
+    /// See `src/test/ui/const-generics/occurs-check/` for more examples where this is relevant.
+    fn unify_const_variable(
         &self,
+        param_env: ty::ParamEnv<'tcx>,
+        target_vid: ty::ConstVid<'tcx>,
+        ct: &'tcx ty::Const<'tcx>,
         vid_is_expected: bool,
-        vid: ty::ConstVid<'tcx>,
-        value: &'tcx ty::Const<'tcx>,
     ) -> RelateResult<'tcx, &'tcx ty::Const<'tcx>> {
+        let (for_universe, span) = {
+            let mut inner = self.inner.borrow_mut();
+            let variable_table = &mut inner.const_unification_table();
+            let var_value = variable_table.probe_value(target_vid);
+            match var_value.val {
+                ConstVariableValue::Known { value } => {
+                    bug!("instantiating {:?} which has a known value {:?}", target_vid, value)
+                }
+                ConstVariableValue::Unknown { universe } => (universe, var_value.origin.span),
+            }
+        };
+        let value = ConstInferUnifier { infcx: self, span, param_env, for_universe, target_vid }
+            .relate(ct, ct)?;
+
         self.inner
             .borrow_mut()
             .const_unification_table()
             .unify_var_value(
-                vid,
+                target_vid,
                 ConstVarValue {
                     origin: ConstVariableOrigin {
                         kind: ConstVariableOriginKind::ConstInference,
@@ -266,8 +315,8 @@ impl<'infcx, 'tcx> InferCtxt<'infcx, 'tcx> {
                     val: ConstVariableValue::Known { value },
                 },
             )
-            .map_err(|e| const_unification_error(vid_is_expected, e))?;
-        Ok(value)
+            .map(|()| value)
+            .map_err(|e| const_unification_error(vid_is_expected, e))
     }
 
     fn unify_integral_variable(
@@ -422,7 +471,7 @@ impl<'infcx, 'tcx> CombineFields<'infcx, 'tcx> {
 
         let for_universe = match self.infcx.inner.borrow_mut().type_variables().probe(for_vid) {
             v @ TypeVariableValue::Known { .. } => {
-                panic!("instantiating {:?} which has a known value {:?}", for_vid, v,)
+                bug!("instantiating {:?} which has a known value {:?}", for_vid, v,)
             }
             TypeVariableValue::Unknown { universe } => universe,
         };
@@ -740,7 +789,6 @@ impl TypeRelation<'tcx> for Generalizer<'_, 'tcx> {
                     }
                 }
             }
-            ty::ConstKind::Unevaluated(..) if self.tcx().lazy_normalization() => Ok(c),
             _ => relate::super_relate_consts(self, c, c),
         }
     }
@@ -790,3 +838,175 @@ fn float_unification_error<'tcx>(
     let (ty::FloatVarValue(a), ty::FloatVarValue(b)) = v;
     TypeError::FloatMismatch(ty::relate::expected_found_bool(a_is_expected, a, b))
 }
+
+struct ConstInferUnifier<'cx, 'tcx> {
+    infcx: &'cx InferCtxt<'cx, 'tcx>,
+
+    span: Span,
+
+    param_env: ty::ParamEnv<'tcx>,
+
+    for_universe: ty::UniverseIndex,
+
+    /// The vid of the const variable that is in the process of being
+    /// instantiated; if we find this within the const we are folding,
+    /// that means we would have created a cyclic const.
+    target_vid: ty::ConstVid<'tcx>,
+}
+
+// We use `TypeRelation` here to propagate `RelateResult` upwards.
+//
+// Both inputs are expected to be the same.
+impl TypeRelation<'tcx> for ConstInferUnifier<'_, 'tcx> {
+    fn tcx(&self) -> TyCtxt<'tcx> {
+        self.infcx.tcx
+    }
+
+    fn param_env(&self) -> ty::ParamEnv<'tcx> {
+        self.param_env
+    }
+
+    fn tag(&self) -> &'static str {
+        "ConstInferUnifier"
+    }
+
+    fn a_is_expected(&self) -> bool {
+        true
+    }
+
+    fn relate_with_variance<T: Relate<'tcx>>(
+        &mut self,
+        _variance: ty::Variance,
+        a: T,
+        b: T,
+    ) -> RelateResult<'tcx, T> {
+        // We don't care about variance here.
+        self.relate(a, b)
+    }
+
+    fn binders<T>(
+        &mut self,
+        a: ty::Binder<T>,
+        b: ty::Binder<T>,
+    ) -> RelateResult<'tcx, ty::Binder<T>>
+    where
+        T: Relate<'tcx>,
+    {
+        Ok(ty::Binder::bind(self.relate(a.skip_binder(), b.skip_binder())?))
+    }
+
+    fn tys(&mut self, t: Ty<'tcx>, _t: Ty<'tcx>) -> RelateResult<'tcx, Ty<'tcx>> {
+        debug_assert_eq!(t, _t);
+        debug!("ConstInferUnifier: t={:?}", t);
+
+        match t.kind() {
+            &ty::Infer(ty::TyVar(vid)) => {
+                let vid = self.infcx.inner.borrow_mut().type_variables().root_var(vid);
+                let probe = self.infcx.inner.borrow_mut().type_variables().probe(vid);
+                match probe {
+                    TypeVariableValue::Known { value: u } => {
+                        debug!("ConstOccursChecker: known value {:?}", u);
+                        self.tys(u, u)
+                    }
+                    TypeVariableValue::Unknown { universe } => {
+                        if self.for_universe.can_name(universe) {
+                            return Ok(t);
+                        }
+
+                        let origin =
+                            *self.infcx.inner.borrow_mut().type_variables().var_origin(vid);
+                        let new_var_id = self.infcx.inner.borrow_mut().type_variables().new_var(
+                            self.for_universe,
+                            false,
+                            origin,
+                        );
+                        let u = self.tcx().mk_ty_var(new_var_id);
+                        debug!(
+                            "ConstInferUnifier: replacing original vid={:?} with new={:?}",
+                            vid, u
+                        );
+                        Ok(u)
+                    }
+                }
+            }
+            _ => relate::super_relate_tys(self, t, t),
+        }
+    }
+
+    fn regions(
+        &mut self,
+        r: ty::Region<'tcx>,
+        _r: ty::Region<'tcx>,
+    ) -> RelateResult<'tcx, ty::Region<'tcx>> {
+        debug_assert_eq!(r, _r);
+        debug!("ConstInferUnifier: r={:?}", r);
+
+        match r {
+            // Never make variables for regions bound within the type itself,
+            // nor for erased regions.
+            ty::ReLateBound(..) | ty::ReErased => {
+                return Ok(r);
+            }
+
+            ty::RePlaceholder(..)
+            | ty::ReVar(..)
+            | ty::ReEmpty(_)
+            | ty::ReStatic
+            | ty::ReEarlyBound(..)
+            | ty::ReFree(..) => {
+                // see common code below
+            }
+        }
+
+        let r_universe = self.infcx.universe_of_region(r);
+        if self.for_universe.can_name(r_universe) {
+            return Ok(r);
+        } else {
+            // FIXME: This is non-ideal because we don't give a
+            // very descriptive origin for this region variable.
+            Ok(self.infcx.next_region_var_in_universe(MiscVariable(self.span), self.for_universe))
+        }
+    }
+
+    fn consts(
+        &mut self,
+        c: &'tcx ty::Const<'tcx>,
+        _c: &'tcx ty::Const<'tcx>,
+    ) -> RelateResult<'tcx, &'tcx ty::Const<'tcx>> {
+        debug_assert_eq!(c, _c);
+        debug!("ConstInferUnifier: c={:?}", c);
+
+        match c.val {
+            ty::ConstKind::Infer(InferConst::Var(vid)) => {
+                let mut inner = self.infcx.inner.borrow_mut();
+                let variable_table = &mut inner.const_unification_table();
+
+                // Check if the current unification would end up
+                // unifying `target_vid` with a const which contains
+                // an inference variable which is unioned with `target_vid`.
+                //
+                // Not doing so can easily result in stack overflows.
+                if variable_table.unioned(self.target_vid, vid) {
+                    return Err(TypeError::CyclicConst(c));
+                }
+
+                let var_value = variable_table.probe_value(vid);
+                match var_value.val {
+                    ConstVariableValue::Known { value: u } => self.consts(u, u),
+                    ConstVariableValue::Unknown { universe } => {
+                        if self.for_universe.can_name(universe) {
+                            Ok(c)
+                        } else {
+                            let new_var_id = variable_table.new_key(ConstVarValue {
+                                origin: var_value.origin,
+                                val: ConstVariableValue::Unknown { universe: self.for_universe },
+                            });
+                            Ok(self.tcx().mk_const_var(new_var_id, c.ty))
+                        }
+                    }
+                }
+            }
+            _ => relate::super_relate_consts(self, c, c),
+        }
+    }
+}
diff --git a/compiler/rustc_middle/src/ty/error.rs b/compiler/rustc_middle/src/ty/error.rs
index 475c3101c1e..ff98a8b9bf0 100644
--- a/compiler/rustc_middle/src/ty/error.rs
+++ b/compiler/rustc_middle/src/ty/error.rs
@@ -56,6 +56,7 @@ pub enum TypeError<'tcx> {
     /// created a cycle (because it appears somewhere within that
     /// type).
     CyclicTy(Ty<'tcx>),
+    CyclicConst(&'tcx ty::Const<'tcx>),
     ProjectionMismatched(ExpectedFound<DefId>),
     ExistentialMismatch(ExpectedFound<&'tcx ty::List<ty::ExistentialPredicate<'tcx>>>),
     ObjectUnsafeCoercion(DefId),
@@ -100,6 +101,7 @@ impl<'tcx> fmt::Display for TypeError<'tcx> {
 
         match *self {
             CyclicTy(_) => write!(f, "cyclic type of infinite size"),
+            CyclicConst(_) => write!(f, "encountered a self-referencing constant"),
             Mismatch => write!(f, "types differ"),
             UnsafetyMismatch(values) => {
                 write!(f, "expected {} fn, found {} fn", values.expected, values.found)
@@ -195,9 +197,9 @@ impl<'tcx> TypeError<'tcx> {
     pub fn must_include_note(&self) -> bool {
         use self::TypeError::*;
         match self {
-            CyclicTy(_) | UnsafetyMismatch(_) | Mismatch | AbiMismatch(_) | FixedArraySize(_)
-            | Sorts(_) | IntMismatch(_) | FloatMismatch(_) | VariadicMismatch(_)
-            | TargetFeatureCast(_) => false,
+            CyclicTy(_) | CyclicConst(_) | UnsafetyMismatch(_) | Mismatch | AbiMismatch(_)
+            | FixedArraySize(_) | Sorts(_) | IntMismatch(_) | FloatMismatch(_)
+            | VariadicMismatch(_) | TargetFeatureCast(_) => false,
 
             Mutability
             | TupleSize(_)
diff --git a/compiler/rustc_middle/src/ty/structural_impls.rs b/compiler/rustc_middle/src/ty/structural_impls.rs
index 6d9d23836fc..597ceac9386 100644
--- a/compiler/rustc_middle/src/ty/structural_impls.rs
+++ b/compiler/rustc_middle/src/ty/structural_impls.rs
@@ -689,6 +689,7 @@ impl<'a, 'tcx> Lift<'tcx> for ty::error::TypeError<'a> {
             Traits(x) => Traits(x),
             VariadicMismatch(x) => VariadicMismatch(x),
             CyclicTy(t) => return tcx.lift(&t).map(|t| CyclicTy(t)),
+            CyclicConst(ct) => return tcx.lift(&ct).map(|ct| CyclicConst(ct)),
             ProjectionMismatched(x) => ProjectionMismatched(x),
             Sorts(ref x) => return tcx.lift(x).map(Sorts),
             ExistentialMismatch(ref x) => return tcx.lift(x).map(ExistentialMismatch),
diff --git a/compiler/rustc_mir/src/interpret/eval_context.rs b/compiler/rustc_mir/src/interpret/eval_context.rs
index 00d6ffb14ea..f97096984fa 100644
--- a/compiler/rustc_mir/src/interpret/eval_context.rs
+++ b/compiler/rustc_mir/src/interpret/eval_context.rs
@@ -482,13 +482,13 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
     /// The `substs` are assumed to already be in our interpreter "universe" (param_env).
     pub(super) fn resolve(
         &self,
-        def_id: DefId,
+        def: ty::WithOptConstParam<DefId>,
         substs: SubstsRef<'tcx>,
     ) -> InterpResult<'tcx, ty::Instance<'tcx>> {
-        trace!("resolve: {:?}, {:#?}", def_id, substs);
+        trace!("resolve: {:?}, {:#?}", def, substs);
         trace!("param_env: {:#?}", self.param_env);
         trace!("substs: {:#?}", substs);
-        match ty::Instance::resolve(*self.tcx, self.param_env, def_id, substs) {
+        match ty::Instance::resolve_opt_const_arg(*self.tcx, self.param_env, def, substs) {
             Ok(Some(instance)) => Ok(instance),
             Ok(None) => throw_inval!(TooGeneric),
 
diff --git a/compiler/rustc_mir/src/interpret/operand.rs b/compiler/rustc_mir/src/interpret/operand.rs
index 8c4bb19866e..9fcbd60d4a3 100644
--- a/compiler/rustc_mir/src/interpret/operand.rs
+++ b/compiler/rustc_mir/src/interpret/operand.rs
@@ -552,7 +552,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
             ty::ConstKind::Param(_) => throw_inval!(TooGeneric),
             ty::ConstKind::Error(_) => throw_inval!(TypeckError(ErrorReported)),
             ty::ConstKind::Unevaluated(def, substs, promoted) => {
-                let instance = self.resolve(def.did, substs)?;
+                let instance = self.resolve(def, substs)?;
                 return Ok(self.eval_to_allocation(GlobalId { instance, promoted })?.into());
             }
             ty::ConstKind::Infer(..)
diff --git a/compiler/rustc_mir/src/interpret/terminator.rs b/compiler/rustc_mir/src/interpret/terminator.rs
index d3c0b497a16..b789cb76e9f 100644
--- a/compiler/rustc_mir/src/interpret/terminator.rs
+++ b/compiler/rustc_mir/src/interpret/terminator.rs
@@ -64,7 +64,12 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
                     }
                     ty::FnDef(def_id, substs) => {
                         let sig = func.layout.ty.fn_sig(*self.tcx);
-                        (FnVal::Instance(self.resolve(def_id, substs)?), sig.abi())
+                        (
+                            FnVal::Instance(
+                                self.resolve(ty::WithOptConstParam::unknown(def_id), substs)?,
+                            ),
+                            sig.abi(),
+                        )
                     }
                     _ => span_bug!(
                         terminator.source_info.span,
diff --git a/src/test/ui/const-generics/issues/issue-69654-run-pass.rs b/src/test/ui/const-generics/issues/issue-69654-run-pass.rs
new file mode 100644
index 00000000000..bbfd2183b06
--- /dev/null
+++ b/src/test/ui/const-generics/issues/issue-69654-run-pass.rs
@@ -0,0 +1,18 @@
+// run-pass
+#![feature(const_generics)]
+#![allow(incomplete_features, unused_braces)]
+
+trait Bar<T> {}
+impl<T> Bar<T> for [u8; {7}] {}
+
+struct Foo<const N: usize> {}
+impl<const N: usize> Foo<N>
+where
+    [u8; N]: Bar<[(); N]>,
+{
+    fn foo() {}
+}
+
+fn main() {
+    Foo::foo();
+}
diff --git a/src/test/ui/const-generics/issues/issue-69654.rs b/src/test/ui/const-generics/issues/issue-69654.rs
new file mode 100644
index 00000000000..7e775999ebd
--- /dev/null
+++ b/src/test/ui/const-generics/issues/issue-69654.rs
@@ -0,0 +1,18 @@
+#![feature(const_generics)]
+#![allow(incomplete_features)]
+
+trait Bar<T> {}
+impl<T> Bar<T> for [u8; T] {}
+//~^ ERROR expected value, found type parameter `T`
+
+struct Foo<const N: usize> {}
+impl<const N: usize> Foo<N>
+where
+    [u8; N]: Bar<[(); N]>,
+{
+    fn foo() {}
+}
+
+fn main() {
+    Foo::foo();
+}
diff --git a/src/test/ui/const-generics/issues/issue-69654.stderr b/src/test/ui/const-generics/issues/issue-69654.stderr
new file mode 100644
index 00000000000..70af7bf25d8
--- /dev/null
+++ b/src/test/ui/const-generics/issues/issue-69654.stderr
@@ -0,0 +1,9 @@
+error[E0423]: expected value, found type parameter `T`
+  --> $DIR/issue-69654.rs:5:25
+   |
+LL | impl<T> Bar<T> for [u8; T] {}
+   |                         ^ not a value
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0423`.
diff --git a/src/test/ui/const-generics/occurs-check/bind-param.rs b/src/test/ui/const-generics/occurs-check/bind-param.rs
new file mode 100644
index 00000000000..68d18650009
--- /dev/null
+++ b/src/test/ui/const-generics/occurs-check/bind-param.rs
@@ -0,0 +1,17 @@
+// build-pass
+#![feature(const_generics)]
+#![allow(incomplete_features)]
+
+// This test does not use any "unevaluated" consts, so it should compile just fine.
+
+fn bind<const N: usize>(value: [u8; N]) -> [u8; N] {
+    todo!()
+}
+
+fn sink(_: [u8; 5]) {}
+
+fn main() {
+    let mut arr = Default::default();
+    arr = bind(arr);
+    sink(arr);
+}
diff --git a/src/test/ui/const-generics/occurs-check/unify-fixpoint.rs b/src/test/ui/const-generics/occurs-check/unify-fixpoint.rs
new file mode 100644
index 00000000000..3cb9b7b9da8
--- /dev/null
+++ b/src/test/ui/const-generics/occurs-check/unify-fixpoint.rs
@@ -0,0 +1,18 @@
+#![feature(const_generics)] //~ WARN the feature `const_generics` is incomplete
+
+// It depends on how we normalize constants and how const equate works if this
+// compiles.
+//
+// Please ping @lcnr if the output if this test changes.
+
+
+fn bind<const N: usize>(value: [u8; N + 2]) -> [u8; N * 2] {
+    //~^ ERROR constant expression depends on a generic parameter
+    //~| ERROR constant expression depends on a generic parameter
+    todo!()
+}
+
+fn main() {
+    let mut arr = Default::default();
+    arr = bind::<2>(arr);
+}
diff --git a/src/test/ui/const-generics/occurs-check/unify-fixpoint.stderr b/src/test/ui/const-generics/occurs-check/unify-fixpoint.stderr
new file mode 100644
index 00000000000..671f1103dcc
--- /dev/null
+++ b/src/test/ui/const-generics/occurs-check/unify-fixpoint.stderr
@@ -0,0 +1,27 @@
+warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes
+  --> $DIR/unify-fixpoint.rs:1:12
+   |
+LL | #![feature(const_generics)]
+   |            ^^^^^^^^^^^^^^
+   |
+   = note: `#[warn(incomplete_features)]` on by default
+   = note: see issue #44580 <https://github.com/rust-lang/rust/issues/44580> for more information
+
+error: constant expression depends on a generic parameter
+  --> $DIR/unify-fixpoint.rs:9:32
+   |
+LL | fn bind<const N: usize>(value: [u8; N + 2]) -> [u8; N * 2] {
+   |                                ^^^^^^^^^^^
+   |
+   = note: this may fail depending on what value the parameter takes
+
+error: constant expression depends on a generic parameter
+  --> $DIR/unify-fixpoint.rs:9:48
+   |
+LL | fn bind<const N: usize>(value: [u8; N + 2]) -> [u8; N * 2] {
+   |                                                ^^^^^^^^^^^
+   |
+   = note: this may fail depending on what value the parameter takes
+
+error: aborting due to 2 previous errors; 1 warning emitted
+
diff --git a/src/test/ui/const-generics/occurs-check/unify-n-nplusone.rs b/src/test/ui/const-generics/occurs-check/unify-n-nplusone.rs
new file mode 100644
index 00000000000..552b1b2a66a
--- /dev/null
+++ b/src/test/ui/const-generics/occurs-check/unify-n-nplusone.rs
@@ -0,0 +1,17 @@
+#![feature(const_generics)]
+#![allow(incomplete_features)]
+
+// This test would try to unify `N` with `N + 1` which must fail the occurs check.
+
+fn bind<const N: usize>(value: [u8; N]) -> [u8; N + 1] {
+    //~^ ERROR constant expression depends on a generic parameter
+    todo!()
+}
+
+fn sink(_: [u8; 5]) {}
+
+fn main() {
+    let mut arr = Default::default();
+    arr = bind(arr);
+    sink(arr);
+}
diff --git a/src/test/ui/const-generics/occurs-check/unify-n-nplusone.stderr b/src/test/ui/const-generics/occurs-check/unify-n-nplusone.stderr
new file mode 100644
index 00000000000..c1ac7eec1e7
--- /dev/null
+++ b/src/test/ui/const-generics/occurs-check/unify-n-nplusone.stderr
@@ -0,0 +1,10 @@
+error: constant expression depends on a generic parameter
+  --> $DIR/unify-n-nplusone.rs:6:44
+   |
+LL | fn bind<const N: usize>(value: [u8; N]) -> [u8; N + 1] {
+   |                                            ^^^^^^^^^^^
+   |
+   = note: this may fail depending on what value the parameter takes
+
+error: aborting due to previous error
+
diff --git a/src/test/ui/const-generics/occurs-check/unused-substs-1.rs b/src/test/ui/const-generics/occurs-check/unused-substs-1.rs
new file mode 100644
index 00000000000..f56687ecd93
--- /dev/null
+++ b/src/test/ui/const-generics/occurs-check/unused-substs-1.rs
@@ -0,0 +1,14 @@
+// build-pass
+#![feature(const_generics)]
+#![allow(incomplete_features)]
+
+trait Bar<const M: usize> {}
+impl<const N: usize> Bar<N> for A<{ 6 + 1 }> {}
+
+struct A<const N: usize>
+where
+    A<N>: Bar<N>;
+
+fn main() {
+    let _ = A;
+}
diff --git a/src/test/ui/const-generics/occurs-check/unused-substs-2.rs b/src/test/ui/const-generics/occurs-check/unused-substs-2.rs
new file mode 100644
index 00000000000..12444ec5312
--- /dev/null
+++ b/src/test/ui/const-generics/occurs-check/unused-substs-2.rs
@@ -0,0 +1,27 @@
+// check-pass
+#![feature(const_generics)]
+#![allow(incomplete_features)]
+
+// The goal is is to get an unevaluated const `ct` with a `Ty::Infer(TyVar(_#1t)` subst.
+//
+// If we are then able to infer `ty::Infer(TyVar(_#1t) := Ty<ct>` we introduced an
+// artificial inference cycle.
+struct Foo<const N: usize>;
+
+trait Bind<T> {
+    fn bind() -> (T, Self);
+}
+
+// `N` has to be `ConstKind::Unevaluated`.
+impl<T> Bind<T> for Foo<{ 6 + 1 }> {
+    fn bind() -> (T, Self) {
+        (panic!(), Foo)
+    }
+}
+
+fn main() {
+    let (mut t, foo) = Foo::bind();
+    // `t` is `ty::Infer(TyVar(_#1t))`
+    // `foo` contains `ty::Infer(TyVar(_#1t))` in its substs
+    t = foo;
+}
diff --git a/src/test/ui/const-generics/occurs-check/unused-substs-3.rs b/src/test/ui/const-generics/occurs-check/unused-substs-3.rs
new file mode 100644
index 00000000000..187e27382fc
--- /dev/null
+++ b/src/test/ui/const-generics/occurs-check/unused-substs-3.rs
@@ -0,0 +1,18 @@
+// check-pass
+#![feature(const_generics)]
+#![allow(incomplete_features)]
+
+// The goal is is to get an unevaluated const `ct` with a `Ty::Infer(TyVar(_#1t)` subst.
+//
+// If we are then able to infer `ty::Infer(TyVar(_#1t) := Ty<ct>` we introduced an
+// artificial inference cycle.
+fn bind<T>() -> (T, [u8; 6 + 1]) {
+    todo!()
+}
+
+fn main() {
+    let (mut t, foo) = bind();
+    // `t` is `ty::Infer(TyVar(_#1t))`
+    // `foo` contains `ty::Infer(TyVar(_#1t))` in its substs
+    t = foo;
+}
diff --git a/src/test/ui/const-generics/occurs-check/unused-substs-4.rs b/src/test/ui/const-generics/occurs-check/unused-substs-4.rs
new file mode 100644
index 00000000000..8e42ceb6d70
--- /dev/null
+++ b/src/test/ui/const-generics/occurs-check/unused-substs-4.rs
@@ -0,0 +1,12 @@
+// build-pass
+#![feature(const_generics)]
+#![allow(incomplete_features)]
+
+fn bind<const N: usize>(value: [u8; N]) -> [u8; 3 + 4] {
+    todo!()
+}
+
+fn main() {
+    let mut arr = Default::default();
+    arr = bind(arr);
+}