@@ -73,6 +73,8 @@ use std::cmp;
7373use std:: fmt;
7474use std:: hash:: { Hash , SipHasher , Hasher } ;
7575use std:: mem;
76+ use std:: num:: ToPrimitive ;
77+ use std:: num:: wrapping:: WrappingOps ;
7678use std:: ops;
7779use std:: rc:: Rc ;
7880use std:: vec:: IntoIter ;
@@ -83,9 +85,11 @@ use syntax::ast::{CrateNum, DefId, Ident, ItemTrait, LOCAL_CRATE};
8385use syntax:: ast:: { MutImmutable , MutMutable , Name , NamedField , NodeId } ;
8486use syntax:: ast:: { StmtExpr , StmtSemi , StructField , UnnamedField , Visibility } ;
8587use 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 } ;
8789use syntax:: codemap:: Span ;
8890use syntax:: parse:: token:: { self , InternedString , special_idents} ;
91+ use syntax:: print:: pprust;
92+ use syntax:: ptr:: P ;
8993use syntax:: { ast, ast_map} ;
9094
9195pub type Disr = u64 ;
@@ -5489,63 +5493,268 @@ pub fn type_is_empty(cx: &ctxt, ty: Ty) -> bool {
54895493 }
54905494}
54915495
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 . isize ,
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 . usize ,
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+
54925744pub fn enum_variants < ' tcx > ( cx : & ctxt < ' tcx > , id : ast:: DefId )
54935745 -> Rc < Vec < Rc < VariantInfo < ' tcx > > > > {
54945746 memoized ( & cx. enum_var_cache , id, |id : ast:: DefId | {
54955747 if ast:: LOCAL_CRATE != id. krate {
54965748 Rc :: new ( csearch:: get_enum_variants ( cx, id) )
54975749 } 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- */
55035750 match cx. map . get ( id. node ) {
55045751 ast_map:: NodeItem ( ref item) => {
55055752 match item. node {
55065753 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 ) ) )
55495758 }
55505759 _ => {
55515760 cx. sess . bug ( "enum_variants: id not bound to an enum" )
0 commit comments