@@ -16,7 +16,7 @@ pub use self::const_val::*;
1616use metadata:: csearch;
1717use middle:: { astencode, def} ;
1818use middle:: pat_util:: def_to_path;
19- use middle:: ty:: { self } ;
19+ use middle:: ty:: { self , Ty } ;
2020use middle:: astconv_util:: { ast_ty_to_prim_ty} ;
2121
2222use syntax:: ast:: { self , Expr } ;
@@ -25,6 +25,7 @@ use syntax::parse::token::InternedString;
2525use syntax:: ptr:: P ;
2626use syntax:: { ast_map, ast_util, codemap} ;
2727
28+ use std:: cmp:: Ordering ;
2829use std:: collections:: hash_map:: Entry :: Vacant ;
2930use std:: rc:: Rc ;
3031
@@ -205,17 +206,23 @@ pub fn const_expr_to_pat(tcx: &ty::ctxt, expr: &Expr, span: Span) -> P<ast::Pat>
205206}
206207
207208pub fn eval_const_expr ( tcx : & ty:: ctxt , e : & Expr ) -> const_val {
208- match eval_const_expr_partial ( tcx, e) {
209+ match eval_const_expr_partial ( tcx, e, None ) {
209210 Ok ( r) => r,
210211 Err ( s) => tcx. sess . span_fatal ( e. span , & s[ ] )
211212 }
212213}
213214
214- pub fn eval_const_expr_partial ( tcx : & ty:: ctxt , e : & Expr ) -> Result < const_val , String > {
215+ pub fn eval_const_expr_partial < ' tcx > ( tcx : & ty:: ctxt < ' tcx > ,
216+ e : & Expr ,
217+ ty_hint : Option < Ty < ' tcx > > )
218+ -> Result < const_val , String > {
215219 fn fromb ( b : bool ) -> Result < const_val , String > { Ok ( const_int ( b as i64 ) ) }
220+
221+ let ety = ty_hint. or_else ( || ty:: expr_ty_opt ( tcx, e) ) ;
222+
216223 match e. node {
217224 ast:: ExprUnary ( ast:: UnNeg , ref inner) => {
218- match eval_const_expr_partial ( tcx, & * * inner) {
225+ match eval_const_expr_partial ( tcx, & * * inner, ety ) {
219226 Ok ( const_float( f) ) => Ok ( const_float ( -f) ) ,
220227 Ok ( const_int( i) ) => Ok ( const_int ( -i) ) ,
221228 Ok ( const_uint( i) ) => Ok ( const_uint ( -i) ) ,
@@ -225,16 +232,20 @@ pub fn eval_const_expr_partial(tcx: &ty::ctxt, e: &Expr) -> Result<const_val, St
225232 }
226233 }
227234 ast:: ExprUnary ( ast:: UnNot , ref inner) => {
228- match eval_const_expr_partial ( tcx, & * * inner) {
235+ match eval_const_expr_partial ( tcx, & * * inner, ety ) {
229236 Ok ( const_int( i) ) => Ok ( const_int ( !i) ) ,
230237 Ok ( const_uint( i) ) => Ok ( const_uint ( !i) ) ,
231238 Ok ( const_bool( b) ) => Ok ( const_bool ( !b) ) ,
232239 _ => Err ( "not on float or string" . to_string ( ) )
233240 }
234241 }
235242 ast:: ExprBinary ( op, ref a, ref b) => {
236- match ( eval_const_expr_partial ( tcx, & * * a) ,
237- eval_const_expr_partial ( tcx, & * * b) ) {
243+ let b_ty = match op. node {
244+ ast:: BiShl | ast:: BiShr => Some ( tcx. types . uint ) ,
245+ _ => ety
246+ } ;
247+ match ( eval_const_expr_partial ( tcx, & * * a, ety) ,
248+ eval_const_expr_partial ( tcx, & * * b, b_ty) ) {
238249 ( Ok ( const_float( a) ) , Ok ( const_float( b) ) ) => {
239250 match op. node {
240251 ast:: BiAdd => Ok ( const_float ( a + b) ) ,
@@ -339,63 +350,53 @@ pub fn eval_const_expr_partial(tcx: &ty::ctxt, e: &Expr) -> Result<const_val, St
339350 // This tends to get called w/o the type actually having been
340351 // populated in the ctxt, which was causing things to blow up
341352 // (#5900). Fall back to doing a limited lookup to get past it.
342- let ety = ty:: expr_ty_opt ( tcx, e)
343- . or_else ( || ast_ty_to_prim_ty ( tcx, & * * target_ty) )
353+ let ety = ety. or_else ( || ast_ty_to_prim_ty ( tcx, & * * target_ty) )
344354 . unwrap_or_else ( || {
345355 tcx. sess . span_fatal ( target_ty. span ,
346356 "target type not found for const cast" )
347357 } ) ;
348-
349- macro_rules! define_casts {
350- ( $val: ident, {
351- $( $ty_pat: pat => (
352- $intermediate_ty: ty,
353- $const_type: ident,
354- $target_ty: ty
355- ) ) ,*
356- } ) => ( match ety. sty {
357- $( $ty_pat => {
358- match $val {
359- const_bool( b) => Ok ( $const_type( b as $intermediate_ty as $target_ty) ) ,
360- const_uint( u) => Ok ( $const_type( u as $intermediate_ty as $target_ty) ) ,
361- const_int( i) => Ok ( $const_type( i as $intermediate_ty as $target_ty) ) ,
362- const_float( f) => Ok ( $const_type( f as $intermediate_ty as $target_ty) ) ,
363- _ => Err ( concat!(
364- "can't cast this type to " , stringify!( $const_type)
365- ) . to_string( ) )
366- }
367- } , ) *
368- _ => Err ( "can't cast this type" . to_string( ) )
369- } )
370- }
371-
372- eval_const_expr_partial ( tcx, & * * base)
373- . and_then ( |val| define_casts ! ( val, {
374- ty:: ty_int( ast:: TyIs ( _) ) => ( int, const_int, i64 ) ,
375- ty:: ty_int( ast:: TyI8 ) => ( i8 , const_int, i64 ) ,
376- ty:: ty_int( ast:: TyI16 ) => ( i16 , const_int, i64 ) ,
377- ty:: ty_int( ast:: TyI32 ) => ( i32 , const_int, i64 ) ,
378- ty:: ty_int( ast:: TyI64 ) => ( i64 , const_int, i64 ) ,
379- ty:: ty_uint( ast:: TyUs ( _) ) => ( uint, const_uint, u64 ) ,
380- ty:: ty_uint( ast:: TyU8 ) => ( u8 , const_uint, u64 ) ,
381- ty:: ty_uint( ast:: TyU16 ) => ( u16 , const_uint, u64 ) ,
382- ty:: ty_uint( ast:: TyU32 ) => ( u32 , const_uint, u64 ) ,
383- ty:: ty_uint( ast:: TyU64 ) => ( u64 , const_uint, u64 ) ,
384- ty:: ty_float( ast:: TyF32 ) => ( f32 , const_float, f64 ) ,
385- ty:: ty_float( ast:: TyF64 ) => ( f64 , const_float, f64 )
386- } ) )
358+ // Prefer known type to noop, but always have a type hint.
359+ let base_hint = ty:: expr_ty_opt ( tcx, & * * base) . unwrap_or ( ety) ;
360+ let val = try!( eval_const_expr_partial ( tcx, & * * base, Some ( base_hint) ) ) ;
361+ cast_const ( val, ety)
387362 }
388363 ast:: ExprPath ( _) | ast:: ExprQPath ( _) => {
389- match lookup_const ( tcx, e) {
390- Some ( actual_e) => eval_const_expr_partial ( tcx, & * actual_e) ,
391- None => Err ( "non-constant path in constant expr" . to_string ( ) )
392- }
364+ let opt_def = tcx. def_map . borrow ( ) . get ( & e. id ) . cloned ( ) ;
365+ let ( const_expr, const_ty) = match opt_def {
366+ Some ( def:: DefConst ( def_id) ) => {
367+ if ast_util:: is_local ( def_id) {
368+ match tcx. map . find ( def_id. node ) {
369+ Some ( ast_map:: NodeItem ( it) ) => match it. node {
370+ ast:: ItemConst ( ref ty, ref expr) => {
371+ ( Some ( & * * expr) , Some ( & * * ty) )
372+ }
373+ _ => ( None , None )
374+ } ,
375+ _ => ( None , None )
376+ }
377+ } else {
378+ ( lookup_const_by_id ( tcx, def_id) , None )
379+ }
380+ }
381+ Some ( def:: DefVariant ( enum_def, variant_def, _) ) => {
382+ ( lookup_variant_by_id ( tcx, enum_def, variant_def) , None )
383+ }
384+ _ => ( None , None )
385+ } ;
386+ let const_expr = match const_expr {
387+ Some ( actual_e) => actual_e,
388+ None => return Err ( "non-constant path in constant expr" . to_string ( ) )
389+ } ;
390+ let ety = ety. or_else ( || const_ty. and_then ( |ty| ast_ty_to_prim_ty ( tcx, ty) ) ) ;
391+ eval_const_expr_partial ( tcx, const_expr, ety)
393392 }
394- ast:: ExprLit ( ref lit) => Ok ( lit_to_const ( & * * lit) ) ,
395- ast:: ExprParen ( ref e) => eval_const_expr_partial ( tcx, & * * e) ,
393+ ast:: ExprLit ( ref lit) => {
394+ Ok ( lit_to_const ( & * * lit, ety) )
395+ }
396+ ast:: ExprParen ( ref e) => eval_const_expr_partial ( tcx, & * * e, ety) ,
396397 ast:: ExprBlock ( ref block) => {
397398 match block. expr {
398- Some ( ref expr) => eval_const_expr_partial ( tcx, & * * expr) ,
399+ Some ( ref expr) => eval_const_expr_partial ( tcx, & * * expr, ety ) ,
399400 None => Ok ( const_int ( 0i64 ) )
400401 }
401402 }
@@ -404,7 +405,7 @@ pub fn eval_const_expr_partial(tcx: &ty::ctxt, e: &Expr) -> Result<const_val, St
404405 if let Some ( & ast:: ExprTup ( ref fields) ) = lookup_const ( tcx, & * * base) . map ( |s| & s. node ) {
405406 // Check that the given index is within bounds and evaluate its value
406407 if fields. len ( ) > index. node {
407- return eval_const_expr_partial ( tcx, & * fields[ index. node ] )
408+ return eval_const_expr_partial ( tcx, & * fields[ index. node ] , None )
408409 } else {
409410 return Err ( "tuple index out of bounds" . to_string ( ) )
410411 }
@@ -419,7 +420,7 @@ pub fn eval_const_expr_partial(tcx: &ty::ctxt, e: &Expr) -> Result<const_val, St
419420 // Check that the given field exists and evaluate it
420421 if let Some ( f) = fields. iter ( ) . find ( |f|
421422 f. ident . node . as_str ( ) == field_name. node . as_str ( ) ) {
422- return eval_const_expr_partial ( tcx, & * f. expr )
423+ return eval_const_expr_partial ( tcx, & * f. expr , None )
423424 } else {
424425 return Err ( "nonexistent struct field" . to_string ( ) )
425426 }
@@ -431,16 +432,58 @@ pub fn eval_const_expr_partial(tcx: &ty::ctxt, e: &Expr) -> Result<const_val, St
431432 }
432433}
433434
434- pub fn lit_to_const ( lit : & ast:: Lit ) -> const_val {
435+ fn cast_const ( val : const_val , ty : Ty ) -> Result < const_val , String > {
436+ macro_rules! define_casts {
437+ ( $( $ty_pat: pat => (
438+ $intermediate_ty: ty,
439+ $const_type: ident,
440+ $target_ty: ty
441+ ) ) ,* ) => ( match ty. sty {
442+ $( $ty_pat => {
443+ match val {
444+ const_bool( b) => Ok ( $const_type( b as $intermediate_ty as $target_ty) ) ,
445+ const_uint( u) => Ok ( $const_type( u as $intermediate_ty as $target_ty) ) ,
446+ const_int( i) => Ok ( $const_type( i as $intermediate_ty as $target_ty) ) ,
447+ const_float( f) => Ok ( $const_type( f as $intermediate_ty as $target_ty) ) ,
448+ _ => Err ( concat!( "can't cast this type to " ,
449+ stringify!( $const_type) ) . to_string( ) )
450+ }
451+ } , ) *
452+ _ => Err ( "can't cast this type" . to_string( ) )
453+ } )
454+ }
455+
456+ define_casts ! {
457+ ty:: ty_int( ast:: TyIs ( _) ) => ( int, const_int, i64 ) ,
458+ ty:: ty_int( ast:: TyI8 ) => ( i8 , const_int, i64 ) ,
459+ ty:: ty_int( ast:: TyI16 ) => ( i16 , const_int, i64 ) ,
460+ ty:: ty_int( ast:: TyI32 ) => ( i32 , const_int, i64 ) ,
461+ ty:: ty_int( ast:: TyI64 ) => ( i64 , const_int, i64 ) ,
462+ ty:: ty_uint( ast:: TyUs ( _) ) => ( uint, const_uint, u64 ) ,
463+ ty:: ty_uint( ast:: TyU8 ) => ( u8 , const_uint, u64 ) ,
464+ ty:: ty_uint( ast:: TyU16 ) => ( u16 , const_uint, u64 ) ,
465+ ty:: ty_uint( ast:: TyU32 ) => ( u32 , const_uint, u64 ) ,
466+ ty:: ty_uint( ast:: TyU64 ) => ( u64 , const_uint, u64 ) ,
467+ ty:: ty_float( ast:: TyF32 ) => ( f32 , const_float, f64 ) ,
468+ ty:: ty_float( ast:: TyF64 ) => ( f64 , const_float, f64 )
469+ }
470+ }
471+
472+ fn lit_to_const ( lit : & ast:: Lit , ty_hint : Option < Ty > ) -> const_val {
435473 match lit. node {
436474 ast:: LitStr ( ref s, _) => const_str ( ( * s) . clone ( ) ) ,
437475 ast:: LitBinary ( ref data) => {
438476 const_binary ( Rc :: new ( data. iter ( ) . map ( |x| * x) . collect ( ) ) )
439477 }
440478 ast:: LitByte ( n) => const_uint ( n as u64 ) ,
441479 ast:: LitChar ( n) => const_uint ( n as u64 ) ,
442- ast:: LitInt ( n, ast:: SignedIntLit ( _, ast:: Plus ) ) |
443- ast:: LitInt ( n, ast:: UnsuffixedIntLit ( ast:: Plus ) ) => const_int ( n as i64 ) ,
480+ ast:: LitInt ( n, ast:: SignedIntLit ( _, ast:: Plus ) ) => const_int ( n as i64 ) ,
481+ ast:: LitInt ( n, ast:: UnsuffixedIntLit ( ast:: Plus ) ) => {
482+ match ty_hint. map ( |ty| & ty. sty ) {
483+ Some ( & ty:: ty_uint( _) ) => const_uint ( n) ,
484+ _ => const_int ( n as i64 )
485+ }
486+ }
444487 ast:: LitInt ( n, ast:: SignedIntLit ( _, ast:: Minus ) ) |
445488 ast:: LitInt ( n, ast:: UnsuffixedIntLit ( ast:: Minus ) ) => const_int ( -( n as i64 ) ) ,
446489 ast:: LitInt ( n, ast:: UnsignedIntLit ( _) ) => const_uint ( n) ,
@@ -452,21 +495,45 @@ pub fn lit_to_const(lit: &ast::Lit) -> const_val {
452495 }
453496}
454497
455- fn compare_vals < T : PartialOrd > ( a : T , b : T ) -> Option < int > {
456- Some ( if a == b { 0 } else if a < b { -1 } else { 1 } )
457- }
458- pub fn compare_const_vals ( a : & const_val , b : & const_val ) -> Option < int > {
459- match ( a, b) {
460- ( & const_int( a) , & const_int( b) ) => compare_vals ( a, b) ,
461- ( & const_uint( a) , & const_uint( b) ) => compare_vals ( a, b) ,
462- ( & const_float( a) , & const_float( b) ) => compare_vals ( a, b) ,
463- ( & const_str( ref a) , & const_str( ref b) ) => compare_vals ( a, b) ,
464- ( & const_bool( a) , & const_bool( b) ) => compare_vals ( a, b) ,
465- ( & const_binary( ref a) , & const_binary( ref b) ) => compare_vals ( a, b) ,
466- _ => None
467- }
498+ pub fn compare_const_vals ( a : & const_val , b : & const_val ) -> Option < Ordering > {
499+ Some ( match ( a, b) {
500+ ( & const_int( a) , & const_int( b) ) => a. cmp ( & b) ,
501+ ( & const_uint( a) , & const_uint( b) ) => a. cmp ( & b) ,
502+ ( & const_float( a) , & const_float( b) ) => {
503+ // This is pretty bad but it is the existing behavior.
504+ if a == b {
505+ Ordering :: Equal
506+ } else if a < b {
507+ Ordering :: Less
508+ } else {
509+ Ordering :: Greater
510+ }
511+ }
512+ ( & const_str( ref a) , & const_str( ref b) ) => a. cmp ( b) ,
513+ ( & const_bool( a) , & const_bool( b) ) => a. cmp ( & b) ,
514+ ( & const_binary( ref a) , & const_binary( ref b) ) => a. cmp ( b) ,
515+ _ => return None
516+ } )
468517}
469518
470- pub fn compare_lit_exprs ( tcx : & ty:: ctxt , a : & Expr , b : & Expr ) -> Option < int > {
471- compare_const_vals ( & eval_const_expr ( tcx, a) , & eval_const_expr ( tcx, b) )
519+ pub fn compare_lit_exprs < ' tcx > ( tcx : & ty:: ctxt < ' tcx > ,
520+ a : & Expr ,
521+ b : & Expr ,
522+ ty_hint : Option < Ty < ' tcx > > )
523+ -> Option < Ordering > {
524+ let a = match eval_const_expr_partial ( tcx, a, ty_hint) {
525+ Ok ( a) => a,
526+ Err ( s) => {
527+ tcx. sess . span_err ( a. span , & s[ ] ) ;
528+ return None ;
529+ }
530+ } ;
531+ let b = match eval_const_expr_partial ( tcx, b, ty_hint) {
532+ Ok ( b) => b,
533+ Err ( s) => {
534+ tcx. sess . span_err ( b. span , & s[ ] ) ;
535+ return None ;
536+ }
537+ } ;
538+ compare_const_vals ( & a, & b)
472539}
0 commit comments