12
12
#![ allow( unsigned_negation) ]
13
13
14
14
use self :: ConstVal :: * ;
15
-
16
15
use self :: ErrKind :: * ;
16
+ use self :: EvalHint :: * ;
17
17
18
18
use ast_map;
19
19
use ast_map:: blocks:: FnLikeNode ;
@@ -331,7 +331,7 @@ pub fn const_expr_to_pat(tcx: &ty::ctxt, expr: &Expr, span: Span) -> P<ast::Pat>
331
331
}
332
332
333
333
pub fn eval_const_expr ( tcx : & ty:: ctxt , e : & Expr ) -> ConstVal {
334
- match eval_const_expr_partial ( tcx, e, None ) {
334
+ match eval_const_expr_partial ( tcx, e, ExprTypeChecked ) {
335
335
Ok ( r) => r,
336
336
Err ( s) => tcx. sess . span_fatal ( s. span , & s. description ( ) )
337
337
}
@@ -436,6 +436,28 @@ impl ConstEvalErr {
436
436
pub type EvalResult = Result < ConstVal , ConstEvalErr > ;
437
437
pub type CastResult = Result < ConstVal , ErrKind > ;
438
438
439
+ // FIXME: Long-term, this enum should go away: trying to evaluate
440
+ // an expression which hasn't been type-checked is a recipe for
441
+ // disaster. That said, it's not clear how to fix ast_ty_to_ty
442
+ // to avoid the ordering issue.
443
+
444
+ /// Hint to determine how to evaluate constant expressions which
445
+ /// might not be type-checked.
446
+ #[ derive( Copy , Clone , Debug ) ]
447
+ pub enum EvalHint < ' tcx > {
448
+ /// We have a type-checked expression.
449
+ ExprTypeChecked ,
450
+ /// We have an expression which hasn't been type-checked, but we have
451
+ /// an idea of what the type will be because of the context. For example,
452
+ /// the length of an array is always `usize`. (This is referred to as
453
+ /// a hint because it isn't guaranteed to be consistent with what
454
+ /// type-checking would compute.)
455
+ UncheckedExprHint ( Ty < ' tcx > ) ,
456
+ /// We have an expression which has not yet been type-checked, and
457
+ /// and we have no clue what the type will be.
458
+ UncheckedExprNoHint ,
459
+ }
460
+
439
461
#[ derive( Copy , Clone , PartialEq , Debug ) ]
440
462
pub enum IntTy { I8 , I16 , I32 , I64 }
441
463
#[ derive( Copy , Clone , PartialEq , Debug ) ]
@@ -706,26 +728,34 @@ pub_fn_checked_op!{ const_uint_checked_shr_via_int(a: u64, b: i64,.. UintTy) {
706
728
uint_shift_body overflowing_shr Uint ShiftRightWithOverflow
707
729
} }
708
730
709
- // After type checking, `eval_const_expr_partial` should always suffice. The
710
- // reason for providing `eval_const_expr_with_substs ` is to allow
711
- // trait-associated consts to be evaluated * during* type checking, when the
712
- // substs for each expression have not been written into `tcx` yet.
731
+ /// Evaluate a constant expression in a context where the expression isn't
732
+ /// guaranteed to be evaluatable. `ty_hint ` is usually ExprTypeChecked,
733
+ /// but a few places need to evaluate constants during type- checking, like
734
+ /// computing the length of an array. (See also the FIXME above EvalHint.)
713
735
pub fn eval_const_expr_partial < ' tcx > ( tcx : & ty:: ctxt < ' tcx > ,
714
736
e : & Expr ,
715
- ty_hint : Option < Ty < ' tcx > > ) -> EvalResult {
716
- eval_const_expr_with_substs ( tcx, e, ty_hint, |id| {
717
- tcx. node_id_item_substs ( id) . substs
718
- } )
719
- }
720
-
721
- pub fn eval_const_expr_with_substs < ' tcx , S > ( tcx : & ty:: ctxt < ' tcx > ,
722
- e : & Expr ,
723
- ty_hint : Option < Ty < ' tcx > > ,
724
- get_substs : S ) -> EvalResult
725
- where S : Fn ( ast:: NodeId ) -> subst:: Substs < ' tcx > {
737
+ ty_hint : EvalHint < ' tcx > ) -> EvalResult {
726
738
fn fromb ( b : bool ) -> ConstVal { Int ( b as i64 ) }
727
739
728
- let ety = ty_hint. or_else ( || tcx. expr_ty_opt ( e) ) ;
740
+ // Try to compute the type of the expression based on the EvalHint.
741
+ // (See also the definition of EvalHint, and the FIXME above EvalHint.)
742
+ let ety = match ty_hint {
743
+ ExprTypeChecked => {
744
+ // After type-checking, expr_ty is guaranteed to succeed.
745
+ Some ( tcx. expr_ty ( e) )
746
+ }
747
+ UncheckedExprHint ( ty) => {
748
+ // Use the type hint; it's not guaranteed to be right, but it's
749
+ // usually good enough.
750
+ Some ( ty)
751
+ }
752
+ UncheckedExprNoHint => {
753
+ // This expression might not be type-checked, and we have no hint.
754
+ // Try to query the context for a type anyway; we might get lucky
755
+ // (for example, if the expression was imported from another crate).
756
+ tcx. expr_ty_opt ( e)
757
+ }
758
+ } ;
729
759
730
760
// If type of expression itself is int or uint, normalize in these
731
761
// bindings so that isize/usize is mapped to a type with an
@@ -741,7 +771,7 @@ pub fn eval_const_expr_with_substs<'tcx, S>(tcx: &ty::ctxt<'tcx>,
741
771
742
772
let result = match e. node {
743
773
ast:: ExprUnary ( ast:: UnNeg , ref inner) => {
744
- match try!( eval_const_expr_partial ( tcx, & * * inner, ety ) ) {
774
+ match try!( eval_const_expr_partial ( tcx, & * * inner, ty_hint ) ) {
745
775
Float ( f) => Float ( -f) ,
746
776
Int ( n) => try!( const_int_checked_neg ( n, e, expr_int_type) ) ,
747
777
Uint ( i) => {
@@ -762,7 +792,7 @@ pub fn eval_const_expr_with_substs<'tcx, S>(tcx: &ty::ctxt<'tcx>,
762
792
}
763
793
}
764
794
ast:: ExprUnary ( ast:: UnNot , ref inner) => {
765
- match try!( eval_const_expr_partial ( tcx, & * * inner, ety ) ) {
795
+ match try!( eval_const_expr_partial ( tcx, & * * inner, ty_hint ) ) {
766
796
Int ( i) => Int ( !i) ,
767
797
Uint ( i) => const_uint_not ( i, expr_uint_type) ,
768
798
Bool ( b) => Bool ( !b) ,
@@ -775,10 +805,16 @@ pub fn eval_const_expr_with_substs<'tcx, S>(tcx: &ty::ctxt<'tcx>,
775
805
}
776
806
ast:: ExprBinary ( op, ref a, ref b) => {
777
807
let b_ty = match op. node {
778
- ast:: BiShl | ast:: BiShr => Some ( tcx. types . usize ) ,
779
- _ => ety
808
+ ast:: BiShl | ast:: BiShr => {
809
+ if let ExprTypeChecked = ty_hint {
810
+ ExprTypeChecked
811
+ } else {
812
+ UncheckedExprHint ( tcx. types . usize )
813
+ }
814
+ }
815
+ _ => ty_hint
780
816
} ;
781
- match ( try!( eval_const_expr_partial ( tcx, & * * a, ety ) ) ,
817
+ match ( try!( eval_const_expr_partial ( tcx, & * * a, ty_hint ) ) ,
782
818
try!( eval_const_expr_partial ( tcx, & * * b, b_ty) ) ) {
783
819
( Float ( a) , Float ( b) ) => {
784
820
match op. node {
@@ -868,22 +904,25 @@ pub fn eval_const_expr_with_substs<'tcx, S>(tcx: &ty::ctxt<'tcx>,
868
904
}
869
905
}
870
906
ast:: ExprCast ( ref base, ref target_ty) => {
871
- // This tends to get called w/o the type actually having been
872
- // populated in the ctxt, which was causing things to blow up
873
- // (#5900). Fall back to doing a limited lookup to get past it.
874
907
let ety = ety. or_else ( || ast_ty_to_prim_ty ( tcx, & * * target_ty) )
875
908
. unwrap_or_else ( || {
876
909
tcx. sess . span_fatal ( target_ty. span ,
877
910
"target type not found for const cast" )
878
911
} ) ;
879
912
880
- // Prefer known type to noop, but always have a type hint.
881
- //
882
- // FIXME (#23833): the type-hint can cause problems,
883
- // e.g. `(i8::MAX + 1_i8) as u32` feeds in `u32` as result
884
- // type to the sum, and thus no overflow is signaled.
885
- let base_hint = tcx. expr_ty_opt ( & * * base) . unwrap_or ( ety) ;
886
- let val = try!( eval_const_expr_partial ( tcx, & * * base, Some ( base_hint) ) ) ;
913
+ let base_hint = if let ExprTypeChecked = ty_hint {
914
+ ExprTypeChecked
915
+ } else {
916
+ // FIXME (#23833): the type-hint can cause problems,
917
+ // e.g. `(i8::MAX + 1_i8) as u32` feeds in `u32` as result
918
+ // type to the sum, and thus no overflow is signaled.
919
+ match tcx. expr_ty_opt ( & base) {
920
+ Some ( t) => UncheckedExprHint ( t) ,
921
+ None => ty_hint
922
+ }
923
+ } ;
924
+
925
+ let val = try!( eval_const_expr_partial ( tcx, & * * base, base_hint) ) ;
887
926
match cast_const ( tcx, val, ety) {
888
927
Ok ( val) => val,
889
928
Err ( kind) => return Err ( ConstEvalErr { span : e. span , kind : kind } ) ,
@@ -913,12 +952,16 @@ pub fn eval_const_expr_with_substs<'tcx, S>(tcx: &ty::ctxt<'tcx>,
913
952
def:: FromTrait ( trait_id) => match tcx. map . find ( def_id. node ) {
914
953
Some ( ast_map:: NodeTraitItem ( ti) ) => match ti. node {
915
954
ast:: ConstTraitItem ( ref ty, _) => {
916
- let substs = get_substs ( e. id ) ;
917
- ( resolve_trait_associated_const ( tcx,
918
- ti,
919
- trait_id,
920
- substs) ,
921
- Some ( & * * ty) )
955
+ if let ExprTypeChecked = ty_hint {
956
+ let substs = tcx. node_id_item_substs ( e. id ) . substs ;
957
+ ( resolve_trait_associated_const ( tcx,
958
+ ti,
959
+ trait_id,
960
+ substs) ,
961
+ Some ( & * * ty) )
962
+ } else {
963
+ ( None , None )
964
+ }
922
965
}
923
966
_ => ( None , None )
924
967
} ,
@@ -947,27 +990,42 @@ pub fn eval_const_expr_with_substs<'tcx, S>(tcx: &ty::ctxt<'tcx>,
947
990
Some ( actual_e) => actual_e,
948
991
None => signal ! ( e, NonConstPath )
949
992
} ;
950
- let ety = ety. or_else ( || const_ty. and_then ( |ty| ast_ty_to_prim_ty ( tcx, ty) ) ) ;
951
- try!( eval_const_expr_partial ( tcx, const_expr, ety) )
993
+ let item_hint = if let UncheckedExprNoHint = ty_hint {
994
+ match const_ty {
995
+ Some ( ty) => match ast_ty_to_prim_ty ( tcx, ty) {
996
+ Some ( ty) => UncheckedExprHint ( ty) ,
997
+ None => UncheckedExprNoHint
998
+ } ,
999
+ None => UncheckedExprNoHint
1000
+ }
1001
+ } else {
1002
+ ty_hint
1003
+ } ;
1004
+ try!( eval_const_expr_partial ( tcx, const_expr, item_hint) )
952
1005
}
953
1006
ast:: ExprLit ( ref lit) => {
954
1007
lit_to_const ( & * * lit, ety)
955
1008
}
956
- ast:: ExprParen ( ref e) => try!( eval_const_expr_partial ( tcx, & * * e, ety ) ) ,
1009
+ ast:: ExprParen ( ref e) => try!( eval_const_expr_partial ( tcx, & * * e, ty_hint ) ) ,
957
1010
ast:: ExprBlock ( ref block) => {
958
1011
match block. expr {
959
- Some ( ref expr) => try!( eval_const_expr_partial ( tcx, & * * expr, ety ) ) ,
1012
+ Some ( ref expr) => try!( eval_const_expr_partial ( tcx, & * * expr, ty_hint ) ) ,
960
1013
None => Int ( 0 )
961
1014
}
962
1015
}
963
1016
ast:: ExprTup ( _) => Tuple ( e. id ) ,
964
1017
ast:: ExprStruct ( ..) => Struct ( e. id ) ,
965
1018
ast:: ExprTupField ( ref base, index) => {
966
- if let Ok ( c) = eval_const_expr_partial ( tcx, base, None ) {
1019
+ let base_hint = if let ExprTypeChecked = ty_hint {
1020
+ ExprTypeChecked
1021
+ } else {
1022
+ UncheckedExprNoHint
1023
+ } ;
1024
+ if let Ok ( c) = eval_const_expr_partial ( tcx, base, base_hint) {
967
1025
if let Tuple ( tup_id) = c {
968
1026
if let ast:: ExprTup ( ref fields) = tcx. map . expect_expr ( tup_id) . node {
969
1027
if index. node < fields. len ( ) {
970
- return eval_const_expr_partial ( tcx, & fields[ index. node ] , None )
1028
+ return eval_const_expr_partial ( tcx, & fields[ index. node ] , base_hint )
971
1029
} else {
972
1030
signal ! ( e, TupleIndexOutOfBounds ) ;
973
1031
}
@@ -983,13 +1041,18 @@ pub fn eval_const_expr_with_substs<'tcx, S>(tcx: &ty::ctxt<'tcx>,
983
1041
}
984
1042
ast:: ExprField ( ref base, field_name) => {
985
1043
// Get the base expression if it is a struct and it is constant
986
- if let Ok ( c) = eval_const_expr_partial ( tcx, base, None ) {
1044
+ let base_hint = if let ExprTypeChecked = ty_hint {
1045
+ ExprTypeChecked
1046
+ } else {
1047
+ UncheckedExprNoHint
1048
+ } ;
1049
+ if let Ok ( c) = eval_const_expr_partial ( tcx, base, base_hint) {
987
1050
if let Struct ( struct_id) = c {
988
1051
if let ast:: ExprStruct ( _, ref fields, _) = tcx. map . expect_expr ( struct_id) . node {
989
1052
// Check that the given field exists and evaluate it
990
1053
if let Some ( f) = fields. iter ( ) . find ( |f| f. ident . node . as_str ( )
991
1054
== field_name. node . as_str ( ) ) {
992
- return eval_const_expr_partial ( tcx, & * f. expr , None )
1055
+ return eval_const_expr_partial ( tcx, & * f. expr , base_hint )
993
1056
} else {
994
1057
signal ! ( e, MissingStructField ) ;
995
1058
}
@@ -1165,21 +1228,17 @@ pub fn compare_const_vals(a: &ConstVal, b: &ConstVal) -> Option<Ordering> {
1165
1228
} )
1166
1229
}
1167
1230
1168
- pub fn compare_lit_exprs < ' tcx , S > ( tcx : & ty:: ctxt < ' tcx > ,
1169
- a : & Expr ,
1170
- b : & Expr ,
1171
- ty_hint : Option < Ty < ' tcx > > ,
1172
- get_substs : S ) -> Option < Ordering >
1173
- where S : Fn ( ast:: NodeId ) -> subst:: Substs < ' tcx > {
1174
- let a = match eval_const_expr_with_substs ( tcx, a, ty_hint,
1175
- |id| { get_substs ( id) } ) {
1231
+ pub fn compare_lit_exprs < ' tcx > ( tcx : & ty:: ctxt < ' tcx > ,
1232
+ a : & Expr ,
1233
+ b : & Expr ) -> Option < Ordering > {
1234
+ let a = match eval_const_expr_partial ( tcx, a, ExprTypeChecked ) {
1176
1235
Ok ( a) => a,
1177
1236
Err ( e) => {
1178
1237
tcx. sess . span_err ( a. span , & e. description ( ) ) ;
1179
1238
return None ;
1180
1239
}
1181
1240
} ;
1182
- let b = match eval_const_expr_with_substs ( tcx, b, ty_hint , get_substs ) {
1241
+ let b = match eval_const_expr_partial ( tcx, b, ExprTypeChecked ) {
1183
1242
Ok ( b) => b,
1184
1243
Err ( e) => {
1185
1244
tcx. sess . span_err ( b. span , & e. description ( ) ) ;
0 commit comments