@@ -16,7 +16,7 @@ pub use self::const_val::*;
16
16
use metadata:: csearch;
17
17
use middle:: { astencode, def} ;
18
18
use middle:: pat_util:: def_to_path;
19
- use middle:: ty:: { self } ;
19
+ use middle:: ty:: { self , Ty } ;
20
20
use middle:: astconv_util:: { ast_ty_to_prim_ty} ;
21
21
22
22
use syntax:: ast:: { self , Expr } ;
@@ -25,6 +25,7 @@ use syntax::parse::token::InternedString;
25
25
use syntax:: ptr:: P ;
26
26
use syntax:: { ast_map, ast_util, codemap} ;
27
27
28
+ use std:: cmp:: Ordering ;
28
29
use std:: collections:: hash_map:: Entry :: Vacant ;
29
30
use std:: rc:: Rc ;
30
31
@@ -205,17 +206,23 @@ pub fn const_expr_to_pat(tcx: &ty::ctxt, expr: &Expr, span: Span) -> P<ast::Pat>
205
206
}
206
207
207
208
pub 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 ) {
209
210
Ok ( r) => r,
210
211
Err ( s) => tcx. sess . span_fatal ( e. span , & s[ ] )
211
212
}
212
213
}
213
214
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 > {
215
219
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
+
216
223
match e. node {
217
224
ast:: ExprUnary ( ast:: UnNeg , ref inner) => {
218
- match eval_const_expr_partial ( tcx, & * * inner) {
225
+ match eval_const_expr_partial ( tcx, & * * inner, ety ) {
219
226
Ok ( const_float( f) ) => Ok ( const_float ( -f) ) ,
220
227
Ok ( const_int( i) ) => Ok ( const_int ( -i) ) ,
221
228
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
225
232
}
226
233
}
227
234
ast:: ExprUnary ( ast:: UnNot , ref inner) => {
228
- match eval_const_expr_partial ( tcx, & * * inner) {
235
+ match eval_const_expr_partial ( tcx, & * * inner, ety ) {
229
236
Ok ( const_int( i) ) => Ok ( const_int ( !i) ) ,
230
237
Ok ( const_uint( i) ) => Ok ( const_uint ( !i) ) ,
231
238
Ok ( const_bool( b) ) => Ok ( const_bool ( !b) ) ,
232
239
_ => Err ( "not on float or string" . to_string ( ) )
233
240
}
234
241
}
235
242
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) ) {
238
249
( Ok ( const_float( a) ) , Ok ( const_float( b) ) ) => {
239
250
match op. node {
240
251
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
339
350
// This tends to get called w/o the type actually having been
340
351
// populated in the ctxt, which was causing things to blow up
341
352
// (#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) )
344
354
. unwrap_or_else ( || {
345
355
tcx. sess . span_fatal ( target_ty. span ,
346
356
"target type not found for const cast" )
347
357
} ) ;
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)
387
362
}
388
363
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)
393
392
}
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) ,
396
397
ast:: ExprBlock ( ref block) => {
397
398
match block. expr {
398
- Some ( ref expr) => eval_const_expr_partial ( tcx, & * * expr) ,
399
+ Some ( ref expr) => eval_const_expr_partial ( tcx, & * * expr, ety ) ,
399
400
None => Ok ( const_int ( 0i64 ) )
400
401
}
401
402
}
@@ -404,7 +405,7 @@ pub fn eval_const_expr_partial(tcx: &ty::ctxt, e: &Expr) -> Result<const_val, St
404
405
if let Some ( & ast:: ExprTup ( ref fields) ) = lookup_const ( tcx, & * * base) . map ( |s| & s. node ) {
405
406
// Check that the given index is within bounds and evaluate its value
406
407
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 )
408
409
} else {
409
410
return Err ( "tuple index out of bounds" . to_string ( ) )
410
411
}
@@ -419,7 +420,7 @@ pub fn eval_const_expr_partial(tcx: &ty::ctxt, e: &Expr) -> Result<const_val, St
419
420
// Check that the given field exists and evaluate it
420
421
if let Some ( f) = fields. iter ( ) . find ( |f|
421
422
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 )
423
424
} else {
424
425
return Err ( "nonexistent struct field" . to_string ( ) )
425
426
}
@@ -431,16 +432,58 @@ pub fn eval_const_expr_partial(tcx: &ty::ctxt, e: &Expr) -> Result<const_val, St
431
432
}
432
433
}
433
434
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 {
435
473
match lit. node {
436
474
ast:: LitStr ( ref s, _) => const_str ( ( * s) . clone ( ) ) ,
437
475
ast:: LitBinary ( ref data) => {
438
476
const_binary ( Rc :: new ( data. iter ( ) . map ( |x| * x) . collect ( ) ) )
439
477
}
440
478
ast:: LitByte ( n) => const_uint ( n as u64 ) ,
441
479
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
+ }
444
487
ast:: LitInt ( n, ast:: SignedIntLit ( _, ast:: Minus ) ) |
445
488
ast:: LitInt ( n, ast:: UnsuffixedIntLit ( ast:: Minus ) ) => const_int ( -( n as i64 ) ) ,
446
489
ast:: LitInt ( n, ast:: UnsignedIntLit ( _) ) => const_uint ( n) ,
@@ -452,21 +495,45 @@ pub fn lit_to_const(lit: &ast::Lit) -> const_val {
452
495
}
453
496
}
454
497
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
+ } )
468
517
}
469
518
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)
472
539
}
0 commit comments