@@ -16,14 +16,15 @@ 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 } ;
23
23
use syntax:: parse:: token:: InternedString ;
24
24
use syntax:: ptr:: P ;
25
25
use syntax:: { ast_map, ast_util, codemap} ;
26
26
27
+ use std:: cmp:: Ordering ;
27
28
use std:: collections:: hash_map:: Entry :: Vacant ;
28
29
use std:: rc:: Rc ;
29
30
@@ -204,17 +205,23 @@ pub fn const_expr_to_pat(tcx: &ty::ctxt, expr: &Expr) -> P<ast::Pat> {
204
205
}
205
206
206
207
pub fn eval_const_expr ( tcx : & ty:: ctxt , e : & Expr ) -> const_val {
207
- match eval_const_expr_partial ( tcx, e) {
208
+ match eval_const_expr_partial ( tcx, e, None ) {
208
209
Ok ( r) => r,
209
210
Err ( s) => tcx. sess . span_fatal ( e. span , & s[ ] )
210
211
}
211
212
}
212
213
213
- pub fn eval_const_expr_partial ( tcx : & ty:: ctxt , e : & Expr ) -> Result < const_val , String > {
214
+ pub fn eval_const_expr_partial < ' tcx > ( tcx : & ty:: ctxt < ' tcx > ,
215
+ e : & Expr ,
216
+ ty_hint : Option < Ty < ' tcx > > )
217
+ -> Result < const_val , String > {
214
218
fn fromb ( b : bool ) -> Result < const_val , String > { Ok ( const_int ( b as i64 ) ) }
219
+
220
+ let ety = ty_hint. or_else ( || ty:: expr_ty_opt ( tcx, e) ) ;
221
+
215
222
match e. node {
216
223
ast:: ExprUnary ( ast:: UnNeg , ref inner) => {
217
- match eval_const_expr_partial ( tcx, & * * inner) {
224
+ match eval_const_expr_partial ( tcx, & * * inner, ety ) {
218
225
Ok ( const_float( f) ) => Ok ( const_float ( -f) ) ,
219
226
Ok ( const_int( i) ) => Ok ( const_int ( -i) ) ,
220
227
Ok ( const_uint( i) ) => Ok ( const_uint ( -i) ) ,
@@ -224,16 +231,20 @@ pub fn eval_const_expr_partial(tcx: &ty::ctxt, e: &Expr) -> Result<const_val, St
224
231
}
225
232
}
226
233
ast:: ExprUnary ( ast:: UnNot , ref inner) => {
227
- match eval_const_expr_partial ( tcx, & * * inner) {
234
+ match eval_const_expr_partial ( tcx, & * * inner, ety ) {
228
235
Ok ( const_int( i) ) => Ok ( const_int ( !i) ) ,
229
236
Ok ( const_uint( i) ) => Ok ( const_uint ( !i) ) ,
230
237
Ok ( const_bool( b) ) => Ok ( const_bool ( !b) ) ,
231
238
_ => Err ( "not on float or string" . to_string ( ) )
232
239
}
233
240
}
234
241
ast:: ExprBinary ( op, ref a, ref b) => {
235
- match ( eval_const_expr_partial ( tcx, & * * a) ,
236
- eval_const_expr_partial ( tcx, & * * b) ) {
242
+ let b_ty = match op. node {
243
+ ast:: BiShl | ast:: BiShr => Some ( tcx. types . uint ) ,
244
+ _ => ety
245
+ } ;
246
+ match ( eval_const_expr_partial ( tcx, & * * a, ety) ,
247
+ eval_const_expr_partial ( tcx, & * * b, b_ty) ) {
237
248
( Ok ( const_float( a) ) , Ok ( const_float( b) ) ) => {
238
249
match op. node {
239
250
ast:: BiAdd => Ok ( const_float ( a + b) ) ,
@@ -338,63 +349,53 @@ pub fn eval_const_expr_partial(tcx: &ty::ctxt, e: &Expr) -> Result<const_val, St
338
349
// This tends to get called w/o the type actually having been
339
350
// populated in the ctxt, which was causing things to blow up
340
351
// (#5900). Fall back to doing a limited lookup to get past it.
341
- let ety = ty:: expr_ty_opt ( tcx, e)
342
- . or_else ( || ast_ty_to_prim_ty ( tcx, & * * target_ty) )
352
+ let ety = ety. or_else ( || ast_ty_to_prim_ty ( tcx, & * * target_ty) )
343
353
. unwrap_or_else ( || {
344
354
tcx. sess . span_fatal ( target_ty. span ,
345
355
"target type not found for const cast" )
346
356
} ) ;
347
-
348
- macro_rules! define_casts {
349
- ( $val: ident, {
350
- $( $ty_pat: pat => (
351
- $intermediate_ty: ty,
352
- $const_type: ident,
353
- $target_ty: ty
354
- ) ) ,*
355
- } ) => ( match ety. sty {
356
- $( $ty_pat => {
357
- match $val {
358
- const_bool( b) => Ok ( $const_type( b as $intermediate_ty as $target_ty) ) ,
359
- const_uint( u) => Ok ( $const_type( u as $intermediate_ty as $target_ty) ) ,
360
- const_int( i) => Ok ( $const_type( i as $intermediate_ty as $target_ty) ) ,
361
- const_float( f) => Ok ( $const_type( f as $intermediate_ty as $target_ty) ) ,
362
- _ => Err ( concat!(
363
- "can't cast this type to " , stringify!( $const_type)
364
- ) . to_string( ) )
365
- }
366
- } , ) *
367
- _ => Err ( "can't cast this type" . to_string( ) )
368
- } )
369
- }
370
-
371
- eval_const_expr_partial ( tcx, & * * base)
372
- . and_then ( |val| define_casts ! ( val, {
373
- ty:: ty_int( ast:: TyIs ( _) ) => ( int, const_int, i64 ) ,
374
- ty:: ty_int( ast:: TyI8 ) => ( i8 , const_int, i64 ) ,
375
- ty:: ty_int( ast:: TyI16 ) => ( i16 , const_int, i64 ) ,
376
- ty:: ty_int( ast:: TyI32 ) => ( i32 , const_int, i64 ) ,
377
- ty:: ty_int( ast:: TyI64 ) => ( i64 , const_int, i64 ) ,
378
- ty:: ty_uint( ast:: TyUs ( _) ) => ( uint, const_uint, u64 ) ,
379
- ty:: ty_uint( ast:: TyU8 ) => ( u8 , const_uint, u64 ) ,
380
- ty:: ty_uint( ast:: TyU16 ) => ( u16 , const_uint, u64 ) ,
381
- ty:: ty_uint( ast:: TyU32 ) => ( u32 , const_uint, u64 ) ,
382
- ty:: ty_uint( ast:: TyU64 ) => ( u64 , const_uint, u64 ) ,
383
- ty:: ty_float( ast:: TyF32 ) => ( f32 , const_float, f64 ) ,
384
- ty:: ty_float( ast:: TyF64 ) => ( f64 , const_float, f64 )
385
- } ) )
357
+ // Prefer known type to noop, but always have a type hint.
358
+ let base_hint = ty:: expr_ty_opt ( tcx, & * * base) . unwrap_or ( ety) ;
359
+ let val = try!( eval_const_expr_partial ( tcx, & * * base, Some ( base_hint) ) ) ;
360
+ cast_const ( val, ety)
386
361
}
387
362
ast:: ExprPath ( _) | ast:: ExprQPath ( _) => {
388
- match lookup_const ( tcx, e) {
389
- Some ( actual_e) => eval_const_expr_partial ( tcx, & * actual_e) ,
390
- None => Err ( "non-constant path in constant expr" . to_string ( ) )
391
- }
363
+ let opt_def = tcx. def_map . borrow ( ) . get ( & e. id ) . cloned ( ) ;
364
+ let ( const_expr, const_ty) = match opt_def {
365
+ Some ( def:: DefConst ( def_id) ) => {
366
+ if ast_util:: is_local ( def_id) {
367
+ match tcx. map . find ( def_id. node ) {
368
+ Some ( ast_map:: NodeItem ( it) ) => match it. node {
369
+ ast:: ItemConst ( ref ty, ref expr) => {
370
+ ( Some ( & * * expr) , Some ( & * * ty) )
371
+ }
372
+ _ => ( None , None )
373
+ } ,
374
+ _ => ( None , None )
375
+ }
376
+ } else {
377
+ ( lookup_const_by_id ( tcx, def_id) , None )
378
+ }
379
+ }
380
+ Some ( def:: DefVariant ( enum_def, variant_def, _) ) => {
381
+ ( lookup_variant_by_id ( tcx, enum_def, variant_def) , None )
382
+ }
383
+ _ => ( None , None )
384
+ } ;
385
+ let const_expr = match const_expr {
386
+ Some ( actual_e) => actual_e,
387
+ None => return Err ( "non-constant path in constant expr" . to_string ( ) )
388
+ } ;
389
+ let ety = ety. or_else ( || const_ty. and_then ( |ty| ast_ty_to_prim_ty ( tcx, ty) ) ) ;
390
+ eval_const_expr_partial ( tcx, const_expr, ety)
392
391
}
393
- ast:: ExprLit ( ref lit) => Ok ( lit_to_const ( & * * lit) ) ,
394
- ast:: ExprParen ( ref e) => eval_const_expr_partial ( tcx, & * * e) ,
392
+ ast:: ExprLit ( ref lit) => {
393
+ Ok ( lit_to_const ( & * * lit, ety) )
394
+ }
395
+ ast:: ExprParen ( ref e) => eval_const_expr_partial ( tcx, & * * e, ety) ,
395
396
ast:: ExprBlock ( ref block) => {
396
397
match block. expr {
397
- Some ( ref expr) => eval_const_expr_partial ( tcx, & * * expr) ,
398
+ Some ( ref expr) => eval_const_expr_partial ( tcx, & * * expr, ety ) ,
398
399
None => Ok ( const_int ( 0i64 ) )
399
400
}
400
401
}
@@ -403,7 +404,7 @@ pub fn eval_const_expr_partial(tcx: &ty::ctxt, e: &Expr) -> Result<const_val, St
403
404
if let Some ( & ast:: ExprTup ( ref fields) ) = lookup_const ( tcx, & * * base) . map ( |s| & s. node ) {
404
405
// Check that the given index is within bounds and evaluate its value
405
406
if fields. len ( ) > index. node {
406
- return eval_const_expr_partial ( tcx, & * fields[ index. node ] )
407
+ return eval_const_expr_partial ( tcx, & * fields[ index. node ] , None )
407
408
} else {
408
409
return Err ( "tuple index out of bounds" . to_string ( ) )
409
410
}
@@ -418,7 +419,7 @@ pub fn eval_const_expr_partial(tcx: &ty::ctxt, e: &Expr) -> Result<const_val, St
418
419
// Check that the given field exists and evaluate it
419
420
if let Some ( f) = fields. iter ( ) . find ( |f|
420
421
f. ident . node . as_str ( ) == field_name. node . as_str ( ) ) {
421
- return eval_const_expr_partial ( tcx, & * f. expr )
422
+ return eval_const_expr_partial ( tcx, & * f. expr , None )
422
423
} else {
423
424
return Err ( "nonexistent struct field" . to_string ( ) )
424
425
}
@@ -430,16 +431,58 @@ pub fn eval_const_expr_partial(tcx: &ty::ctxt, e: &Expr) -> Result<const_val, St
430
431
}
431
432
}
432
433
433
- pub fn lit_to_const ( lit : & ast:: Lit ) -> const_val {
434
+ fn cast_const ( val : const_val , ty : Ty ) -> Result < const_val , String > {
435
+ macro_rules! define_casts {
436
+ ( $( $ty_pat: pat => (
437
+ $intermediate_ty: ty,
438
+ $const_type: ident,
439
+ $target_ty: ty
440
+ ) ) ,* ) => ( match ty. sty {
441
+ $( $ty_pat => {
442
+ match val {
443
+ const_bool( b) => Ok ( $const_type( b as $intermediate_ty as $target_ty) ) ,
444
+ const_uint( u) => Ok ( $const_type( u as $intermediate_ty as $target_ty) ) ,
445
+ const_int( i) => Ok ( $const_type( i as $intermediate_ty as $target_ty) ) ,
446
+ const_float( f) => Ok ( $const_type( f as $intermediate_ty as $target_ty) ) ,
447
+ _ => Err ( concat!( "can't cast this type to " ,
448
+ stringify!( $const_type) ) . to_string( ) )
449
+ }
450
+ } , ) *
451
+ _ => Err ( "can't cast this type" . to_string( ) )
452
+ } )
453
+ }
454
+
455
+ define_casts ! {
456
+ ty:: ty_int( ast:: TyIs ( _) ) => ( int, const_int, i64 ) ,
457
+ ty:: ty_int( ast:: TyI8 ) => ( i8 , const_int, i64 ) ,
458
+ ty:: ty_int( ast:: TyI16 ) => ( i16 , const_int, i64 ) ,
459
+ ty:: ty_int( ast:: TyI32 ) => ( i32 , const_int, i64 ) ,
460
+ ty:: ty_int( ast:: TyI64 ) => ( i64 , const_int, i64 ) ,
461
+ ty:: ty_uint( ast:: TyUs ( _) ) => ( uint, const_uint, u64 ) ,
462
+ ty:: ty_uint( ast:: TyU8 ) => ( u8 , const_uint, u64 ) ,
463
+ ty:: ty_uint( ast:: TyU16 ) => ( u16 , const_uint, u64 ) ,
464
+ ty:: ty_uint( ast:: TyU32 ) => ( u32 , const_uint, u64 ) ,
465
+ ty:: ty_uint( ast:: TyU64 ) => ( u64 , const_uint, u64 ) ,
466
+ ty:: ty_float( ast:: TyF32 ) => ( f32 , const_float, f64 ) ,
467
+ ty:: ty_float( ast:: TyF64 ) => ( f64 , const_float, f64 )
468
+ }
469
+ }
470
+
471
+ fn lit_to_const ( lit : & ast:: Lit , ty_hint : Option < Ty > ) -> const_val {
434
472
match lit. node {
435
473
ast:: LitStr ( ref s, _) => const_str ( ( * s) . clone ( ) ) ,
436
474
ast:: LitBinary ( ref data) => {
437
475
const_binary ( Rc :: new ( data. iter ( ) . map ( |x| * x) . collect ( ) ) )
438
476
}
439
477
ast:: LitByte ( n) => const_uint ( n as u64 ) ,
440
478
ast:: LitChar ( n) => const_uint ( n as u64 ) ,
441
- ast:: LitInt ( n, ast:: SignedIntLit ( _, ast:: Plus ) ) |
442
- ast:: LitInt ( n, ast:: UnsuffixedIntLit ( ast:: Plus ) ) => const_int ( n as i64 ) ,
479
+ ast:: LitInt ( n, ast:: SignedIntLit ( _, ast:: Plus ) ) => const_int ( n as i64 ) ,
480
+ ast:: LitInt ( n, ast:: UnsuffixedIntLit ( ast:: Plus ) ) => {
481
+ match ty_hint. map ( |ty| & ty. sty ) {
482
+ Some ( & ty:: ty_uint( _) ) => const_uint ( n) ,
483
+ _ => const_int ( n as i64 )
484
+ }
485
+ }
443
486
ast:: LitInt ( n, ast:: SignedIntLit ( _, ast:: Minus ) ) |
444
487
ast:: LitInt ( n, ast:: UnsuffixedIntLit ( ast:: Minus ) ) => const_int ( -( n as i64 ) ) ,
445
488
ast:: LitInt ( n, ast:: UnsignedIntLit ( _) ) => const_uint ( n) ,
@@ -451,21 +494,45 @@ pub fn lit_to_const(lit: &ast::Lit) -> const_val {
451
494
}
452
495
}
453
496
454
- fn compare_vals < T : PartialOrd > ( a : T , b : T ) -> Option < int > {
455
- Some ( if a == b { 0 } else if a < b { -1 } else { 1 } )
456
- }
457
- pub fn compare_const_vals ( a : & const_val , b : & const_val ) -> Option < int > {
458
- match ( a, b) {
459
- ( & const_int( a) , & const_int( b) ) => compare_vals ( a, b) ,
460
- ( & const_uint( a) , & const_uint( b) ) => compare_vals ( a, b) ,
461
- ( & const_float( a) , & const_float( b) ) => compare_vals ( a, b) ,
462
- ( & const_str( ref a) , & const_str( ref b) ) => compare_vals ( a, b) ,
463
- ( & const_bool( a) , & const_bool( b) ) => compare_vals ( a, b) ,
464
- ( & const_binary( ref a) , & const_binary( ref b) ) => compare_vals ( a, b) ,
465
- _ => None
466
- }
497
+ pub fn compare_const_vals ( a : & const_val , b : & const_val ) -> Option < Ordering > {
498
+ Some ( match ( a, b) {
499
+ ( & const_int( a) , & const_int( b) ) => a. cmp ( & b) ,
500
+ ( & const_uint( a) , & const_uint( b) ) => a. cmp ( & b) ,
501
+ ( & const_float( a) , & const_float( b) ) => {
502
+ // This is pretty bad but it is the existing behavior.
503
+ if a == b {
504
+ Ordering :: Equal
505
+ } else if a < b {
506
+ Ordering :: Less
507
+ } else {
508
+ Ordering :: Greater
509
+ }
510
+ }
511
+ ( & const_str( ref a) , & const_str( ref b) ) => a. cmp ( b) ,
512
+ ( & const_bool( a) , & const_bool( b) ) => a. cmp ( & b) ,
513
+ ( & const_binary( ref a) , & const_binary( ref b) ) => a. cmp ( b) ,
514
+ _ => return None
515
+ } )
467
516
}
468
517
469
- pub fn compare_lit_exprs ( tcx : & ty:: ctxt , a : & Expr , b : & Expr ) -> Option < int > {
470
- compare_const_vals ( & eval_const_expr ( tcx, a) , & eval_const_expr ( tcx, b) )
518
+ pub fn compare_lit_exprs < ' tcx > ( tcx : & ty:: ctxt < ' tcx > ,
519
+ a : & Expr ,
520
+ b : & Expr ,
521
+ ty_hint : Option < Ty < ' tcx > > )
522
+ -> Option < Ordering > {
523
+ let a = match eval_const_expr_partial ( tcx, a, ty_hint) {
524
+ Ok ( a) => a,
525
+ Err ( s) => {
526
+ tcx. sess . span_err ( a. span , & s[ ] ) ;
527
+ return None ;
528
+ }
529
+ } ;
530
+ let b = match eval_const_expr_partial ( tcx, b, ty_hint) {
531
+ Ok ( b) => b,
532
+ Err ( s) => {
533
+ tcx. sess . span_err ( b. span , & s[ ] ) ;
534
+ return None ;
535
+ }
536
+ } ;
537
+ compare_const_vals ( & a, & b)
471
538
}
0 commit comments