@@ -45,7 +45,7 @@ use rustc_middle::ty::relate::{self, Relate, RelateResult, TypeRelation};
45
45
use rustc_middle:: ty:: subst:: SubstsRef ;
46
46
use rustc_middle:: ty:: { self , InferConst , ToPredicate , Ty , TyCtxt , TypeFoldable } ;
47
47
use rustc_middle:: ty:: { IntType , UintType } ;
48
- use rustc_span:: DUMMY_SP ;
48
+ use rustc_span:: { Span , DUMMY_SP } ;
49
49
50
50
/// Small-storage-optimized implementation of a map
51
51
/// made specifically for caching results.
@@ -219,11 +219,11 @@ impl<'infcx, 'tcx> InferCtxt<'infcx, 'tcx> {
219
219
}
220
220
221
221
( 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 ) ;
223
223
}
224
224
225
225
( _, 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 ) ;
227
227
}
228
228
( ty:: ConstKind :: Unevaluated ( ..) , _) if self . tcx . lazy_normalization ( ) => {
229
229
// FIXME(#59490): Need to remove the leak check to accommodate
@@ -247,17 +247,66 @@ impl<'infcx, 'tcx> InferCtxt<'infcx, 'tcx> {
247
247
ty:: relate:: super_relate_consts ( relation, a, b)
248
248
}
249
249
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 (
251
285
& self ,
286
+ param_env : ty:: ParamEnv < ' tcx > ,
287
+ target_vid : ty:: ConstVid < ' tcx > ,
288
+ ct : & ' tcx ty:: Const < ' tcx > ,
252
289
vid_is_expected : bool ,
253
- vid : ty:: ConstVid < ' tcx > ,
254
- value : & ' tcx ty:: Const < ' tcx > ,
255
290
) -> 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
+
256
305
self . inner
257
306
. borrow_mut ( )
258
307
. const_unification_table ( )
259
308
. unify_var_value (
260
- vid ,
309
+ target_vid ,
261
310
ConstVarValue {
262
311
origin : ConstVariableOrigin {
263
312
kind : ConstVariableOriginKind :: ConstInference ,
@@ -266,8 +315,8 @@ impl<'infcx, 'tcx> InferCtxt<'infcx, 'tcx> {
266
315
val : ConstVariableValue :: Known { value } ,
267
316
} ,
268
317
)
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 ) )
271
320
}
272
321
273
322
fn unify_integral_variable (
@@ -422,7 +471,7 @@ impl<'infcx, 'tcx> CombineFields<'infcx, 'tcx> {
422
471
423
472
let for_universe = match self . infcx . inner . borrow_mut ( ) . type_variables ( ) . probe ( for_vid) {
424
473
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, )
426
475
}
427
476
TypeVariableValue :: Unknown { universe } => universe,
428
477
} ;
@@ -740,7 +789,6 @@ impl TypeRelation<'tcx> for Generalizer<'_, 'tcx> {
740
789
}
741
790
}
742
791
}
743
- ty:: ConstKind :: Unevaluated ( ..) if self . tcx ( ) . lazy_normalization ( ) => Ok ( c) ,
744
792
_ => relate:: super_relate_consts ( self , c, c) ,
745
793
}
746
794
}
@@ -790,3 +838,175 @@ fn float_unification_error<'tcx>(
790
838
let ( ty:: FloatVarValue ( a) , ty:: FloatVarValue ( b) ) = v;
791
839
TypeError :: FloatMismatch ( ty:: relate:: expected_found_bool ( a_is_expected, a, b) )
792
840
}
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