Skip to content

Commit e0bf356

Browse files
committed
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 😅
2 parents 956e06c + 2855b92 commit e0bf356

18 files changed

+452
-19
lines changed

compiler/rustc_infer/src/infer/combine.rs

+231-11
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ use rustc_middle::ty::relate::{self, Relate, RelateResult, TypeRelation};
4545
use rustc_middle::ty::subst::SubstsRef;
4646
use rustc_middle::ty::{self, InferConst, ToPredicate, Ty, TyCtxt, TypeFoldable};
4747
use rustc_middle::ty::{IntType, UintType};
48-
use rustc_span::DUMMY_SP;
48+
use rustc_span::{Span, DUMMY_SP};
4949

5050
/// Small-storage-optimized implementation of a map
5151
/// made specifically for caching results.
@@ -219,11 +219,11 @@ impl<'infcx, 'tcx> InferCtxt<'infcx, 'tcx> {
219219
}
220220

221221
(ty::ConstKind::Infer(InferConst::Var(vid)), _) => {
222-
return self.unify_const_variable(a_is_expected, vid, b);
222+
return self.unify_const_variable(relation.param_env(), vid, b, a_is_expected);
223223
}
224224

225225
(_, ty::ConstKind::Infer(InferConst::Var(vid))) => {
226-
return self.unify_const_variable(!a_is_expected, vid, a);
226+
return self.unify_const_variable(relation.param_env(), vid, a, !a_is_expected);
227227
}
228228
(ty::ConstKind::Unevaluated(..), _) if self.tcx.lazy_normalization() => {
229229
// FIXME(#59490): Need to remove the leak check to accommodate
@@ -247,17 +247,66 @@ impl<'infcx, 'tcx> InferCtxt<'infcx, 'tcx> {
247247
ty::relate::super_relate_consts(relation, a, b)
248248
}
249249

250-
pub fn unify_const_variable(
250+
/// Unifies the const variable `target_vid` with the given constant.
251+
///
252+
/// This also tests if the given const `ct` contains an inference variable which was previously
253+
/// unioned with `target_vid`. If this is the case, inferring `target_vid` to `ct`
254+
/// would result in an infinite type as we continously replace an inference variable
255+
/// in `ct` with `ct` itself.
256+
///
257+
/// This is especially important as unevaluated consts use their parents generics.
258+
/// They therefore often contain unused substs, making these errors far more likely.
259+
///
260+
/// A good example of this is the following:
261+
///
262+
/// ```rust
263+
/// #![feature(const_generics)]
264+
///
265+
/// fn bind<const N: usize>(value: [u8; N]) -> [u8; 3 + 4] {
266+
/// todo!()
267+
/// }
268+
///
269+
/// fn main() {
270+
/// let mut arr = Default::default();
271+
/// arr = bind(arr);
272+
/// }
273+
/// ```
274+
///
275+
/// Here `3 + 4` ends up as `ConstKind::Unevaluated` which uses the generics
276+
/// of `fn bind` (meaning that its substs contain `N`).
277+
///
278+
/// `bind(arr)` now infers that the type of `arr` must be `[u8; N]`.
279+
/// The assignment `arr = bind(arr)` now tries to equate `N` with `3 + 4`.
280+
///
281+
/// As `3 + 4` contains `N` in its substs, this must not succeed.
282+
///
283+
/// See `src/test/ui/const-generics/occurs-check/` for more examples where this is relevant.
284+
fn unify_const_variable(
251285
&self,
286+
param_env: ty::ParamEnv<'tcx>,
287+
target_vid: ty::ConstVid<'tcx>,
288+
ct: &'tcx ty::Const<'tcx>,
252289
vid_is_expected: bool,
253-
vid: ty::ConstVid<'tcx>,
254-
value: &'tcx ty::Const<'tcx>,
255290
) -> RelateResult<'tcx, &'tcx ty::Const<'tcx>> {
291+
let (for_universe, span) = {
292+
let mut inner = self.inner.borrow_mut();
293+
let variable_table = &mut inner.const_unification_table();
294+
let var_value = variable_table.probe_value(target_vid);
295+
match var_value.val {
296+
ConstVariableValue::Known { value } => {
297+
bug!("instantiating {:?} which has a known value {:?}", target_vid, value)
298+
}
299+
ConstVariableValue::Unknown { universe } => (universe, var_value.origin.span),
300+
}
301+
};
302+
let value = ConstInferUnifier { infcx: self, span, param_env, for_universe, target_vid }
303+
.relate(ct, ct)?;
304+
256305
self.inner
257306
.borrow_mut()
258307
.const_unification_table()
259308
.unify_var_value(
260-
vid,
309+
target_vid,
261310
ConstVarValue {
262311
origin: ConstVariableOrigin {
263312
kind: ConstVariableOriginKind::ConstInference,
@@ -266,8 +315,8 @@ impl<'infcx, 'tcx> InferCtxt<'infcx, 'tcx> {
266315
val: ConstVariableValue::Known { value },
267316
},
268317
)
269-
.map_err(|e| const_unification_error(vid_is_expected, e))?;
270-
Ok(value)
318+
.map(|()| value)
319+
.map_err(|e| const_unification_error(vid_is_expected, e))
271320
}
272321

273322
fn unify_integral_variable(
@@ -422,7 +471,7 @@ impl<'infcx, 'tcx> CombineFields<'infcx, 'tcx> {
422471

423472
let for_universe = match self.infcx.inner.borrow_mut().type_variables().probe(for_vid) {
424473
v @ TypeVariableValue::Known { .. } => {
425-
panic!("instantiating {:?} which has a known value {:?}", for_vid, v,)
474+
bug!("instantiating {:?} which has a known value {:?}", for_vid, v,)
426475
}
427476
TypeVariableValue::Unknown { universe } => universe,
428477
};
@@ -740,7 +789,6 @@ impl TypeRelation<'tcx> for Generalizer<'_, 'tcx> {
740789
}
741790
}
742791
}
743-
ty::ConstKind::Unevaluated(..) if self.tcx().lazy_normalization() => Ok(c),
744792
_ => relate::super_relate_consts(self, c, c),
745793
}
746794
}
@@ -790,3 +838,175 @@ fn float_unification_error<'tcx>(
790838
let (ty::FloatVarValue(a), ty::FloatVarValue(b)) = v;
791839
TypeError::FloatMismatch(ty::relate::expected_found_bool(a_is_expected, a, b))
792840
}
841+
842+
struct ConstInferUnifier<'cx, 'tcx> {
843+
infcx: &'cx InferCtxt<'cx, 'tcx>,
844+
845+
span: Span,
846+
847+
param_env: ty::ParamEnv<'tcx>,
848+
849+
for_universe: ty::UniverseIndex,
850+
851+
/// The vid of the const variable that is in the process of being
852+
/// instantiated; if we find this within the const we are folding,
853+
/// that means we would have created a cyclic const.
854+
target_vid: ty::ConstVid<'tcx>,
855+
}
856+
857+
// We use `TypeRelation` here to propagate `RelateResult` upwards.
858+
//
859+
// Both inputs are expected to be the same.
860+
impl TypeRelation<'tcx> for ConstInferUnifier<'_, 'tcx> {
861+
fn tcx(&self) -> TyCtxt<'tcx> {
862+
self.infcx.tcx
863+
}
864+
865+
fn param_env(&self) -> ty::ParamEnv<'tcx> {
866+
self.param_env
867+
}
868+
869+
fn tag(&self) -> &'static str {
870+
"ConstInferUnifier"
871+
}
872+
873+
fn a_is_expected(&self) -> bool {
874+
true
875+
}
876+
877+
fn relate_with_variance<T: Relate<'tcx>>(
878+
&mut self,
879+
_variance: ty::Variance,
880+
a: T,
881+
b: T,
882+
) -> RelateResult<'tcx, T> {
883+
// We don't care about variance here.
884+
self.relate(a, b)
885+
}
886+
887+
fn binders<T>(
888+
&mut self,
889+
a: ty::Binder<T>,
890+
b: ty::Binder<T>,
891+
) -> RelateResult<'tcx, ty::Binder<T>>
892+
where
893+
T: Relate<'tcx>,
894+
{
895+
Ok(ty::Binder::bind(self.relate(a.skip_binder(), b.skip_binder())?))
896+
}
897+
898+
fn tys(&mut self, t: Ty<'tcx>, _t: Ty<'tcx>) -> RelateResult<'tcx, Ty<'tcx>> {
899+
debug_assert_eq!(t, _t);
900+
debug!("ConstInferUnifier: t={:?}", t);
901+
902+
match t.kind() {
903+
&ty::Infer(ty::TyVar(vid)) => {
904+
let vid = self.infcx.inner.borrow_mut().type_variables().root_var(vid);
905+
let probe = self.infcx.inner.borrow_mut().type_variables().probe(vid);
906+
match probe {
907+
TypeVariableValue::Known { value: u } => {
908+
debug!("ConstOccursChecker: known value {:?}", u);
909+
self.tys(u, u)
910+
}
911+
TypeVariableValue::Unknown { universe } => {
912+
if self.for_universe.can_name(universe) {
913+
return Ok(t);
914+
}
915+
916+
let origin =
917+
*self.infcx.inner.borrow_mut().type_variables().var_origin(vid);
918+
let new_var_id = self.infcx.inner.borrow_mut().type_variables().new_var(
919+
self.for_universe,
920+
false,
921+
origin,
922+
);
923+
let u = self.tcx().mk_ty_var(new_var_id);
924+
debug!(
925+
"ConstInferUnifier: replacing original vid={:?} with new={:?}",
926+
vid, u
927+
);
928+
Ok(u)
929+
}
930+
}
931+
}
932+
_ => relate::super_relate_tys(self, t, t),
933+
}
934+
}
935+
936+
fn regions(
937+
&mut self,
938+
r: ty::Region<'tcx>,
939+
_r: ty::Region<'tcx>,
940+
) -> RelateResult<'tcx, ty::Region<'tcx>> {
941+
debug_assert_eq!(r, _r);
942+
debug!("ConstInferUnifier: r={:?}", r);
943+
944+
match r {
945+
// Never make variables for regions bound within the type itself,
946+
// nor for erased regions.
947+
ty::ReLateBound(..) | ty::ReErased => {
948+
return Ok(r);
949+
}
950+
951+
ty::RePlaceholder(..)
952+
| ty::ReVar(..)
953+
| ty::ReEmpty(_)
954+
| ty::ReStatic
955+
| ty::ReEarlyBound(..)
956+
| ty::ReFree(..) => {
957+
// see common code below
958+
}
959+
}
960+
961+
let r_universe = self.infcx.universe_of_region(r);
962+
if self.for_universe.can_name(r_universe) {
963+
return Ok(r);
964+
} else {
965+
// FIXME: This is non-ideal because we don't give a
966+
// very descriptive origin for this region variable.
967+
Ok(self.infcx.next_region_var_in_universe(MiscVariable(self.span), self.for_universe))
968+
}
969+
}
970+
971+
fn consts(
972+
&mut self,
973+
c: &'tcx ty::Const<'tcx>,
974+
_c: &'tcx ty::Const<'tcx>,
975+
) -> RelateResult<'tcx, &'tcx ty::Const<'tcx>> {
976+
debug_assert_eq!(c, _c);
977+
debug!("ConstInferUnifier: c={:?}", c);
978+
979+
match c.val {
980+
ty::ConstKind::Infer(InferConst::Var(vid)) => {
981+
let mut inner = self.infcx.inner.borrow_mut();
982+
let variable_table = &mut inner.const_unification_table();
983+
984+
// Check if the current unification would end up
985+
// unifying `target_vid` with a const which contains
986+
// an inference variable which is unioned with `target_vid`.
987+
//
988+
// Not doing so can easily result in stack overflows.
989+
if variable_table.unioned(self.target_vid, vid) {
990+
return Err(TypeError::CyclicConst(c));
991+
}
992+
993+
let var_value = variable_table.probe_value(vid);
994+
match var_value.val {
995+
ConstVariableValue::Known { value: u } => self.consts(u, u),
996+
ConstVariableValue::Unknown { universe } => {
997+
if self.for_universe.can_name(universe) {
998+
Ok(c)
999+
} else {
1000+
let new_var_id = variable_table.new_key(ConstVarValue {
1001+
origin: var_value.origin,
1002+
val: ConstVariableValue::Unknown { universe: self.for_universe },
1003+
});
1004+
Ok(self.tcx().mk_const_var(new_var_id, c.ty))
1005+
}
1006+
}
1007+
}
1008+
}
1009+
_ => relate::super_relate_consts(self, c, c),
1010+
}
1011+
}
1012+
}

compiler/rustc_middle/src/ty/error.rs

+5-3
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ pub enum TypeError<'tcx> {
5656
/// created a cycle (because it appears somewhere within that
5757
/// type).
5858
CyclicTy(Ty<'tcx>),
59+
CyclicConst(&'tcx ty::Const<'tcx>),
5960
ProjectionMismatched(ExpectedFound<DefId>),
6061
ExistentialMismatch(ExpectedFound<&'tcx ty::List<ty::ExistentialPredicate<'tcx>>>),
6162
ObjectUnsafeCoercion(DefId),
@@ -100,6 +101,7 @@ impl<'tcx> fmt::Display for TypeError<'tcx> {
100101

101102
match *self {
102103
CyclicTy(_) => write!(f, "cyclic type of infinite size"),
104+
CyclicConst(_) => write!(f, "encountered a self-referencing constant"),
103105
Mismatch => write!(f, "types differ"),
104106
UnsafetyMismatch(values) => {
105107
write!(f, "expected {} fn, found {} fn", values.expected, values.found)
@@ -195,9 +197,9 @@ impl<'tcx> TypeError<'tcx> {
195197
pub fn must_include_note(&self) -> bool {
196198
use self::TypeError::*;
197199
match self {
198-
CyclicTy(_) | UnsafetyMismatch(_) | Mismatch | AbiMismatch(_) | FixedArraySize(_)
199-
| Sorts(_) | IntMismatch(_) | FloatMismatch(_) | VariadicMismatch(_)
200-
| TargetFeatureCast(_) => false,
200+
CyclicTy(_) | CyclicConst(_) | UnsafetyMismatch(_) | Mismatch | AbiMismatch(_)
201+
| FixedArraySize(_) | Sorts(_) | IntMismatch(_) | FloatMismatch(_)
202+
| VariadicMismatch(_) | TargetFeatureCast(_) => false,
201203

202204
Mutability
203205
| TupleSize(_)

compiler/rustc_middle/src/ty/structural_impls.rs

+1
Original file line numberDiff line numberDiff line change
@@ -689,6 +689,7 @@ impl<'a, 'tcx> Lift<'tcx> for ty::error::TypeError<'a> {
689689
Traits(x) => Traits(x),
690690
VariadicMismatch(x) => VariadicMismatch(x),
691691
CyclicTy(t) => return tcx.lift(&t).map(|t| CyclicTy(t)),
692+
CyclicConst(ct) => return tcx.lift(&ct).map(|ct| CyclicConst(ct)),
692693
ProjectionMismatched(x) => ProjectionMismatched(x),
693694
Sorts(ref x) => return tcx.lift(x).map(Sorts),
694695
ExistentialMismatch(ref x) => return tcx.lift(x).map(ExistentialMismatch),

compiler/rustc_mir/src/interpret/eval_context.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -482,13 +482,13 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
482482
/// The `substs` are assumed to already be in our interpreter "universe" (param_env).
483483
pub(super) fn resolve(
484484
&self,
485-
def_id: DefId,
485+
def: ty::WithOptConstParam<DefId>,
486486
substs: SubstsRef<'tcx>,
487487
) -> InterpResult<'tcx, ty::Instance<'tcx>> {
488-
trace!("resolve: {:?}, {:#?}", def_id, substs);
488+
trace!("resolve: {:?}, {:#?}", def, substs);
489489
trace!("param_env: {:#?}", self.param_env);
490490
trace!("substs: {:#?}", substs);
491-
match ty::Instance::resolve(*self.tcx, self.param_env, def_id, substs) {
491+
match ty::Instance::resolve_opt_const_arg(*self.tcx, self.param_env, def, substs) {
492492
Ok(Some(instance)) => Ok(instance),
493493
Ok(None) => throw_inval!(TooGeneric),
494494

compiler/rustc_mir/src/interpret/operand.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -552,7 +552,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
552552
ty::ConstKind::Param(_) => throw_inval!(TooGeneric),
553553
ty::ConstKind::Error(_) => throw_inval!(TypeckError(ErrorReported)),
554554
ty::ConstKind::Unevaluated(def, substs, promoted) => {
555-
let instance = self.resolve(def.did, substs)?;
555+
let instance = self.resolve(def, substs)?;
556556
return Ok(self.eval_to_allocation(GlobalId { instance, promoted })?.into());
557557
}
558558
ty::ConstKind::Infer(..)

compiler/rustc_mir/src/interpret/terminator.rs

+6-1
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,12 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
6464
}
6565
ty::FnDef(def_id, substs) => {
6666
let sig = func.layout.ty.fn_sig(*self.tcx);
67-
(FnVal::Instance(self.resolve(def_id, substs)?), sig.abi())
67+
(
68+
FnVal::Instance(
69+
self.resolve(ty::WithOptConstParam::unknown(def_id), substs)?,
70+
),
71+
sig.abi(),
72+
)
6873
}
6974
_ => span_bug!(
7075
terminator.source_info.span,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
// run-pass
2+
#![feature(const_generics)]
3+
#![allow(incomplete_features, unused_braces)]
4+
5+
trait Bar<T> {}
6+
impl<T> Bar<T> for [u8; {7}] {}
7+
8+
struct Foo<const N: usize> {}
9+
impl<const N: usize> Foo<N>
10+
where
11+
[u8; N]: Bar<[(); N]>,
12+
{
13+
fn foo() {}
14+
}
15+
16+
fn main() {
17+
Foo::foo();
18+
}

0 commit comments

Comments
 (0)