@@ -73,6 +73,8 @@ use std::cmp;
73
73
use std:: fmt;
74
74
use std:: hash:: { Hash , SipHasher , Hasher } ;
75
75
use std:: mem;
76
+ use std:: num:: ToPrimitive ;
77
+ use std:: num:: wrapping:: WrappingOps ;
76
78
use std:: ops;
77
79
use std:: rc:: Rc ;
78
80
use std:: vec:: IntoIter ;
@@ -83,9 +85,11 @@ use syntax::ast::{CrateNum, DefId, Ident, ItemTrait, LOCAL_CRATE};
83
85
use syntax:: ast:: { MutImmutable , MutMutable , Name , NamedField , NodeId } ;
84
86
use syntax:: ast:: { StmtExpr , StmtSemi , StructField , UnnamedField , Visibility } ;
85
87
use syntax:: ast_util:: { self , is_local, lit_is_str, local_def} ;
86
- use syntax:: attr:: { self , AttrMetaMethods } ;
88
+ use syntax:: attr:: { self , AttrMetaMethods , SignedInt , UnsignedInt } ;
87
89
use syntax:: codemap:: Span ;
88
90
use syntax:: parse:: token:: { self , InternedString , special_idents} ;
91
+ use syntax:: print:: pprust;
92
+ use syntax:: ptr:: P ;
89
93
use syntax:: { ast, ast_map} ;
90
94
91
95
pub type Disr = u64 ;
@@ -5489,63 +5493,268 @@ pub fn type_is_empty(cx: &ctxt, ty: Ty) -> bool {
5489
5493
}
5490
5494
}
5491
5495
5496
+ trait IntTypeExt {
5497
+ fn to_ty < ' tcx > ( & self , cx : & ctxt < ' tcx > ) -> Ty < ' tcx > ;
5498
+ fn i64_to_disr ( & self , val : i64 ) -> Option < Disr > ;
5499
+ fn u64_to_disr ( & self , val : u64 ) -> Option < Disr > ;
5500
+ fn disr_incr ( & self , val : Disr ) -> Option < Disr > ;
5501
+ fn disr_string ( & self , val : Disr ) -> String ;
5502
+ fn disr_wrap_incr ( & self , val : Option < Disr > ) -> Disr ;
5503
+ }
5504
+
5505
+ impl IntTypeExt for attr:: IntType {
5506
+ fn to_ty < ' tcx > ( & self , cx : & ctxt < ' tcx > ) -> Ty < ' tcx > {
5507
+ match * self {
5508
+ SignedInt ( ast:: TyI8 ) => cx. types . i8 ,
5509
+ SignedInt ( ast:: TyI16 ) => cx. types . i16 ,
5510
+ SignedInt ( ast:: TyI32 ) => cx. types . i32 ,
5511
+ SignedInt ( ast:: TyI64 ) => cx. types . i64 ,
5512
+ SignedInt ( ast:: TyIs ) => cx. types . int ,
5513
+ UnsignedInt ( ast:: TyU8 ) => cx. types . u8 ,
5514
+ UnsignedInt ( ast:: TyU16 ) => cx. types . u16 ,
5515
+ UnsignedInt ( ast:: TyU32 ) => cx. types . u32 ,
5516
+ UnsignedInt ( ast:: TyU64 ) => cx. types . u64 ,
5517
+ UnsignedInt ( ast:: TyUs ) => cx. types . uint ,
5518
+ }
5519
+ }
5520
+
5521
+ fn i64_to_disr ( & self , val : i64 ) -> Option < Disr > {
5522
+ match * self {
5523
+ SignedInt ( ast:: TyI8 ) => val. to_i8 ( ) . map ( |v| v as Disr ) ,
5524
+ SignedInt ( ast:: TyI16 ) => val. to_i16 ( ) . map ( |v| v as Disr ) ,
5525
+ SignedInt ( ast:: TyI32 ) => val. to_i32 ( ) . map ( |v| v as Disr ) ,
5526
+ SignedInt ( ast:: TyI64 ) => val. to_i64 ( ) . map ( |v| v as Disr ) ,
5527
+ UnsignedInt ( ast:: TyU8 ) => val. to_u8 ( ) . map ( |v| v as Disr ) ,
5528
+ UnsignedInt ( ast:: TyU16 ) => val. to_u16 ( ) . map ( |v| v as Disr ) ,
5529
+ UnsignedInt ( ast:: TyU32 ) => val. to_u32 ( ) . map ( |v| v as Disr ) ,
5530
+ UnsignedInt ( ast:: TyU64 ) => val. to_u64 ( ) . map ( |v| v as Disr ) ,
5531
+
5532
+ UnsignedInt ( ast:: TyUs ) |
5533
+ SignedInt ( ast:: TyIs ) => unreachable ! ( ) ,
5534
+ }
5535
+ }
5536
+
5537
+ fn u64_to_disr ( & self , val : u64 ) -> Option < Disr > {
5538
+ match * self {
5539
+ SignedInt ( ast:: TyI8 ) => val. to_i8 ( ) . map ( |v| v as Disr ) ,
5540
+ SignedInt ( ast:: TyI16 ) => val. to_i16 ( ) . map ( |v| v as Disr ) ,
5541
+ SignedInt ( ast:: TyI32 ) => val. to_i32 ( ) . map ( |v| v as Disr ) ,
5542
+ SignedInt ( ast:: TyI64 ) => val. to_i64 ( ) . map ( |v| v as Disr ) ,
5543
+ UnsignedInt ( ast:: TyU8 ) => val. to_u8 ( ) . map ( |v| v as Disr ) ,
5544
+ UnsignedInt ( ast:: TyU16 ) => val. to_u16 ( ) . map ( |v| v as Disr ) ,
5545
+ UnsignedInt ( ast:: TyU32 ) => val. to_u32 ( ) . map ( |v| v as Disr ) ,
5546
+ UnsignedInt ( ast:: TyU64 ) => val. to_u64 ( ) . map ( |v| v as Disr ) ,
5547
+
5548
+ UnsignedInt ( ast:: TyUs ) |
5549
+ SignedInt ( ast:: TyIs ) => unreachable ! ( ) ,
5550
+ }
5551
+ }
5552
+
5553
+ fn disr_incr ( & self , val : Disr ) -> Option < Disr > {
5554
+ macro_rules! add1 {
5555
+ ( $e: expr) => { $e. and_then( |v|v. checked_add( 1 ) ) . map( |v| v as Disr ) }
5556
+ }
5557
+ match * self {
5558
+ // SignedInt repr means we *want* to reinterpret the bits
5559
+ // treating the highest bit of Disr as a sign-bit, so
5560
+ // cast to i64 before range-checking.
5561
+ SignedInt ( ast:: TyI8 ) => add1 ! ( ( val as i64 ) . to_i8( ) ) ,
5562
+ SignedInt ( ast:: TyI16 ) => add1 ! ( ( val as i64 ) . to_i16( ) ) ,
5563
+ SignedInt ( ast:: TyI32 ) => add1 ! ( ( val as i64 ) . to_i32( ) ) ,
5564
+ SignedInt ( ast:: TyI64 ) => add1 ! ( Some ( val as i64 ) ) ,
5565
+
5566
+ UnsignedInt ( ast:: TyU8 ) => add1 ! ( val. to_u8( ) ) ,
5567
+ UnsignedInt ( ast:: TyU16 ) => add1 ! ( val. to_u16( ) ) ,
5568
+ UnsignedInt ( ast:: TyU32 ) => add1 ! ( val. to_u32( ) ) ,
5569
+ UnsignedInt ( ast:: TyU64 ) => add1 ! ( Some ( val) ) ,
5570
+
5571
+ UnsignedInt ( ast:: TyUs ) |
5572
+ SignedInt ( ast:: TyIs ) => unreachable ! ( ) ,
5573
+ }
5574
+ }
5575
+
5576
+ // This returns a String because (1.) it is only used for
5577
+ // rendering an error message and (2.) a string can represent the
5578
+ // full range from `i64::MIN` through `u64::MAX`.
5579
+ fn disr_string ( & self , val : Disr ) -> String {
5580
+ match * self {
5581
+ SignedInt ( ast:: TyI8 ) => format ! ( "{}" , val as i8 ) ,
5582
+ SignedInt ( ast:: TyI16 ) => format ! ( "{}" , val as i16 ) ,
5583
+ SignedInt ( ast:: TyI32 ) => format ! ( "{}" , val as i32 ) ,
5584
+ SignedInt ( ast:: TyI64 ) => format ! ( "{}" , val as i64 ) ,
5585
+ UnsignedInt ( ast:: TyU8 ) => format ! ( "{}" , val as u8 ) ,
5586
+ UnsignedInt ( ast:: TyU16 ) => format ! ( "{}" , val as u16 ) ,
5587
+ UnsignedInt ( ast:: TyU32 ) => format ! ( "{}" , val as u32 ) ,
5588
+ UnsignedInt ( ast:: TyU64 ) => format ! ( "{}" , val as u64 ) ,
5589
+
5590
+ UnsignedInt ( ast:: TyUs ) |
5591
+ SignedInt ( ast:: TyIs ) => unreachable ! ( ) ,
5592
+ }
5593
+ }
5594
+
5595
+ fn disr_wrap_incr ( & self , val : Option < Disr > ) -> Disr {
5596
+ macro_rules! add1 {
5597
+ ( $e: expr) => { ( $e) . wrapping_add( 1 ) as Disr }
5598
+ }
5599
+ let val = val. unwrap_or ( ty:: INITIAL_DISCRIMINANT_VALUE ) ;
5600
+ match * self {
5601
+ SignedInt ( ast:: TyI8 ) => add1 ! ( val as i8 ) ,
5602
+ SignedInt ( ast:: TyI16 ) => add1 ! ( val as i16 ) ,
5603
+ SignedInt ( ast:: TyI32 ) => add1 ! ( val as i32 ) ,
5604
+ SignedInt ( ast:: TyI64 ) => add1 ! ( val as i64 ) ,
5605
+ UnsignedInt ( ast:: TyU8 ) => add1 ! ( val as u8 ) ,
5606
+ UnsignedInt ( ast:: TyU16 ) => add1 ! ( val as u16 ) ,
5607
+ UnsignedInt ( ast:: TyU32 ) => add1 ! ( val as u32 ) ,
5608
+ UnsignedInt ( ast:: TyU64 ) => add1 ! ( val as u64 ) ,
5609
+
5610
+ UnsignedInt ( ast:: TyUs ) |
5611
+ SignedInt ( ast:: TyIs ) => unreachable ! ( ) ,
5612
+ }
5613
+ }
5614
+ }
5615
+
5616
+ /// Returns `(normalized_type, ty)`, where `normalized_type` is the
5617
+ /// IntType representation of one of {i64,i32,i16,i8,u64,u32,u16,u8},
5618
+ /// and `ty` is the original type (i.e. may include `isize` or
5619
+ /// `usize`).
5620
+ pub fn enum_repr_type < ' tcx > ( cx : & ctxt < ' tcx > ,
5621
+ opt_hint : Option < & attr:: ReprAttr > )
5622
+ -> ( attr:: IntType , Ty < ' tcx > )
5623
+ {
5624
+ let repr_type = match opt_hint {
5625
+ // Feed in the given type
5626
+ Some ( & attr:: ReprInt ( _, int_t) ) => int_t,
5627
+ // ... but provide sensible default if none provided
5628
+ //
5629
+ // NB. Historically `fn enum_variants` generate i64 here, while
5630
+ // rustc_typeck::check would generate isize.
5631
+ _ => SignedInt ( ast:: TyIs ) ,
5632
+ } ;
5633
+
5634
+ let repr_type_ty = repr_type. to_ty ( cx) ;
5635
+ let repr_type = match repr_type {
5636
+ SignedInt ( ast:: TyIs ) =>
5637
+ SignedInt ( cx. sess . target . int_type ) ,
5638
+ UnsignedInt ( ast:: TyUs ) =>
5639
+ UnsignedInt ( cx. sess . target . uint_type ) ,
5640
+ other => other
5641
+ } ;
5642
+
5643
+ ( repr_type, repr_type_ty)
5644
+ }
5645
+
5646
+ fn report_discrim_overflow ( cx : & ctxt ,
5647
+ variant_span : Span ,
5648
+ variant_name : & str ,
5649
+ repr_type : attr:: IntType ,
5650
+ prev_val : Disr ) {
5651
+ let computed_value = repr_type. disr_wrap_incr ( Some ( prev_val) ) ;
5652
+ let computed_value = repr_type. disr_string ( computed_value) ;
5653
+ let prev_val = repr_type. disr_string ( prev_val) ;
5654
+ let repr_type = repr_type. to_ty ( cx) . user_string ( cx) ;
5655
+ span_err ! ( cx. sess, variant_span, E0370 ,
5656
+ "enum discriminant overflowed on value after {}: {}; \
5657
+ set explicitly via {} = {} if that is desired outcome",
5658
+ prev_val, repr_type, variant_name, computed_value) ;
5659
+ }
5660
+
5661
+ // This computes the discriminant values for the sequence of Variants
5662
+ // attached to a particular enum, taking into account the #[repr] (if
5663
+ // any) provided via the `opt_hint`.
5664
+ fn compute_enum_variants < ' tcx > ( cx : & ctxt < ' tcx > ,
5665
+ vs : & ' tcx [ P < ast:: Variant > ] ,
5666
+ opt_hint : Option < & attr:: ReprAttr > )
5667
+ -> Vec < Rc < ty:: VariantInfo < ' tcx > > > {
5668
+ let mut variants: Vec < Rc < ty:: VariantInfo > > = Vec :: new ( ) ;
5669
+ let mut prev_disr_val: Option < ty:: Disr > = None ;
5670
+
5671
+ let ( repr_type, repr_type_ty) = ty:: enum_repr_type ( cx, opt_hint) ;
5672
+
5673
+ for v in vs {
5674
+ // If the discriminant value is specified explicitly in the
5675
+ // enum, check whether the initialization expression is valid,
5676
+ // otherwise use the last value plus one.
5677
+ let current_disr_val;
5678
+
5679
+ // This closure marks cases where, when an error occurs during
5680
+ // the computation, attempt to assign a (hopefully) fresh
5681
+ // value to avoid spurious error reports downstream.
5682
+ let attempt_fresh_value = move || -> Disr {
5683
+ repr_type. disr_wrap_incr ( prev_disr_val)
5684
+ } ;
5685
+
5686
+ match v. node . disr_expr {
5687
+ Some ( ref e) => {
5688
+ debug ! ( "disr expr, checking {}" , pprust:: expr_to_string( & * * e) ) ;
5689
+
5690
+ // check_expr (from check_const pass) doesn't guarantee
5691
+ // that the expression is in a form that eval_const_expr can
5692
+ // handle, so we may still get an internal compiler error
5693
+ //
5694
+ // pnkfelix: The above comment was transcribed from
5695
+ // the version of this code taken from rustc_typeck.
5696
+ // Presumably the implication is that we need to deal
5697
+ // with such ICE's as they arise.
5698
+ //
5699
+ // Since this can be called from `ty::enum_variants`
5700
+ // anyway, best thing is to make `eval_const_expr`
5701
+ // more robust (on case-by-case basis).
5702
+
5703
+ match const_eval:: eval_const_expr_partial ( cx, & * * e, Some ( repr_type_ty) ) {
5704
+ Ok ( const_eval:: const_int( val) ) => current_disr_val = val as Disr ,
5705
+ Ok ( const_eval:: const_uint( val) ) => current_disr_val = val as Disr ,
5706
+ Ok ( _) => {
5707
+ span_err ! ( cx. sess, e. span, E0079 ,
5708
+ "expected signed integer constant" ) ;
5709
+ current_disr_val = attempt_fresh_value ( ) ;
5710
+ }
5711
+ Err ( ref err) => {
5712
+ span_err ! ( cx. sess, err. span, E0080 ,
5713
+ "constant evaluation error: {}" ,
5714
+ err. description( ) ) ;
5715
+ current_disr_val = attempt_fresh_value ( ) ;
5716
+ }
5717
+ }
5718
+ } ,
5719
+ None => {
5720
+ current_disr_val = match prev_disr_val {
5721
+ Some ( prev_disr_val) => {
5722
+ if let Some ( v) = repr_type. disr_incr ( prev_disr_val) {
5723
+ v
5724
+ } else {
5725
+ report_discrim_overflow ( cx, v. span , v. node . name . as_str ( ) ,
5726
+ repr_type, prev_disr_val) ;
5727
+ attempt_fresh_value ( )
5728
+ }
5729
+ }
5730
+ None => ty:: INITIAL_DISCRIMINANT_VALUE
5731
+ }
5732
+ }
5733
+ }
5734
+
5735
+ let variant_info = Rc :: new ( VariantInfo :: from_ast_variant ( cx, & * * v, current_disr_val) ) ;
5736
+ prev_disr_val = Some ( current_disr_val) ;
5737
+
5738
+ variants. push ( variant_info) ;
5739
+ }
5740
+
5741
+ return variants;
5742
+ }
5743
+
5492
5744
pub fn enum_variants < ' tcx > ( cx : & ctxt < ' tcx > , id : ast:: DefId )
5493
5745
-> Rc < Vec < Rc < VariantInfo < ' tcx > > > > {
5494
5746
memoized ( & cx. enum_var_cache , id, |id : ast:: DefId | {
5495
5747
if ast:: LOCAL_CRATE != id. krate {
5496
5748
Rc :: new ( csearch:: get_enum_variants ( cx, id) )
5497
5749
} else {
5498
- /*
5499
- Although both this code and check_enum_variants in typeck/check
5500
- call eval_const_expr, it should never get called twice for the same
5501
- expr, since check_enum_variants also updates the enum_var_cache
5502
- */
5503
5750
match cx. map . get ( id. node ) {
5504
5751
ast_map:: NodeItem ( ref item) => {
5505
5752
match item. node {
5506
5753
ast:: ItemEnum ( ref enum_definition, _) => {
5507
- let mut last_discriminant: Option < Disr > = None ;
5508
- Rc :: new ( enum_definition. variants . iter ( ) . map ( |variant| {
5509
-
5510
- let mut discriminant = INITIAL_DISCRIMINANT_VALUE ;
5511
- if let Some ( ref e) = variant. node . disr_expr {
5512
- // Preserve all values, and prefer signed.
5513
- let ty = Some ( cx. types . i64 ) ;
5514
- match const_eval:: eval_const_expr_partial ( cx, & * * e, ty) {
5515
- Ok ( const_eval:: const_int( val) ) => {
5516
- discriminant = val as Disr ;
5517
- }
5518
- Ok ( const_eval:: const_uint( val) ) => {
5519
- discriminant = val as Disr ;
5520
- }
5521
- Ok ( _) => {
5522
- span_err ! ( cx. sess, e. span, E0304 ,
5523
- "expected signed integer constant" ) ;
5524
- }
5525
- Err ( err) => {
5526
- span_err ! ( cx. sess, err. span, E0305 ,
5527
- "constant evaluation error: {}" ,
5528
- err. description( ) ) ;
5529
- }
5530
- }
5531
- } else {
5532
- if let Some ( val) = last_discriminant {
5533
- if let Some ( v) = val. checked_add ( 1 ) {
5534
- discriminant = v
5535
- } else {
5536
- cx. sess . span_err (
5537
- variant. span ,
5538
- & format ! ( "Discriminant overflowed!" ) ) ;
5539
- }
5540
- } else {
5541
- discriminant = INITIAL_DISCRIMINANT_VALUE ;
5542
- }
5543
- }
5544
-
5545
- last_discriminant = Some ( discriminant) ;
5546
- Rc :: new ( VariantInfo :: from_ast_variant ( cx, & * * variant,
5547
- discriminant) )
5548
- } ) . collect ( ) )
5754
+ Rc :: new ( compute_enum_variants (
5755
+ cx,
5756
+ & enum_definition. variants ,
5757
+ lookup_repr_hints ( cx, id) . get ( 0 ) ) )
5549
5758
}
5550
5759
_ => {
5551
5760
cx. sess . bug ( "enum_variants: id not bound to an enum" )
0 commit comments