@@ -45,7 +45,7 @@ use rustc_middle::ty::relate::{self, Relate, RelateResult, TypeRelation};
4545use rustc_middle:: ty:: subst:: SubstsRef ;
4646use rustc_middle:: ty:: { self , InferConst , ToPredicate , Ty , TyCtxt , TypeFoldable } ;
4747use 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+ }
0 commit comments