diff --git a/src/librustc/middle/trans/adt.rs b/src/librustc/middle/trans/adt.rs index 26f8ad6bd478..687af6e6c8c9 100644 --- a/src/librustc/middle/trans/adt.rs +++ b/src/librustc/middle/trans/adt.rs @@ -150,6 +150,8 @@ fn represent_type_uncached(cx: &mut CrateContext, t: ty::t) -> Repr { if cases.len() == 0 { // Uninhabitable; represent as unit + // (Typechecking will reject discriminant-sizing attrs.) + assert_eq!(hint, attr::ReprAny); return Univariant(mk_struct(cx, [], false), false); } @@ -165,13 +167,6 @@ fn represent_type_uncached(cx: &mut CrateContext, t: ty::t) -> Repr { return mk_cenum(cx, hint, &bounds); } - if cases.len() == 1 { - // Equivalent to a struct/tuple/newtype. - // FIXME: should this conflict with a discriminant size hint? - assert_eq!(cases[0].discr, 0); - return Univariant(mk_struct(cx, cases[0].tys, false), false) - } - // Since there's at least one // non-empty body, explicit discriminants should have // been rejected by a checker before this point. @@ -181,8 +176,15 @@ fn represent_type_uncached(cx: &mut CrateContext, t: ty::t) -> Repr { ty::item_path_str(cx.tcx, def_id))) } - if cases.len() == 2 { - // FIXME: disable if size hint present? + if cases.len() == 1 { + // Equivalent to a struct/tuple/newtype. + // (Typechecking will reject discriminant-sizing attrs.) + assert_eq!(hint, attr::ReprAny); + return Univariant(mk_struct(cx, cases[0].tys, false), false) + } + + if cases.len() == 2 && hint == attr::ReprAny { + // Nullable pointer optimization let mut discr = 0; while discr < 2 { if cases[1 - discr].is_zerolen(cx) { @@ -205,7 +207,6 @@ fn represent_type_uncached(cx: &mut CrateContext, t: ty::t) -> Repr { } // The general case. - let hint = ty::lookup_repr_hint(cx.tcx, def_id); assert!((cases.len() - 1) as i64 >= 0); let bounds = IntBounds { ulo: 0, uhi: (cases.len() - 1) as u64, slo: 0, shi: (cases.len() - 1) as i64 }; @@ -307,7 +308,7 @@ fn range_to_inttype(cx: &mut CrateContext, hint: Hint, bounds: &IntBounds) -> In match hint { attr::ReprInt(span, ity) => { if !bounds_usable(cx, ity, bounds) { - cx.sess.span_err(span, "representation hint insufficient for discriminant range") + cx.sess.span_bug(span, "representation hint insufficient for discriminant range") } return ity; } @@ -365,6 +366,7 @@ fn ty_of_inttype(ity: IntType) -> ty::t { } } + /** * Returns the fields of a struct for the given representation. * All nominal types are LLVM structs, in order to be able to use diff --git a/src/librustc/middle/typeck/check/mod.rs b/src/librustc/middle/typeck/check/mod.rs index d71aa7766892..15660618f8e1 100644 --- a/src/librustc/middle/typeck/check/mod.rs +++ b/src/librustc/middle/typeck/check/mod.rs @@ -121,6 +121,7 @@ use syntax::ast; use syntax::ast_map; use syntax::ast_util::local_def; use syntax::ast_util; +use syntax::attr; use syntax::codemap::Span; use syntax::codemap; use syntax::opt_vec::OptVec; @@ -3159,9 +3160,38 @@ pub fn check_enum_variants(ccx: @mut CrateCtxt, sp: Span, vs: &[ast::variant], id: ast::NodeId) { + + fn disr_in_range(ccx: @mut CrateCtxt, + ty: attr::IntType, + disr: ty::Disr) -> bool { + fn uint_in_range(ccx: @mut CrateCtxt, ty: ast::uint_ty, disr: ty::Disr) -> bool { + match ty { + ast::ty_u8 => disr as u8 as Disr == disr, + ast::ty_u16 => disr as u16 as Disr == disr, + ast::ty_u32 => disr as u32 as Disr == disr, + ast::ty_u64 => disr as u64 as Disr == disr, + ast::ty_u => uint_in_range(ccx, ccx.tcx.sess.targ_cfg.uint_type, disr) + } + } + fn int_in_range(ccx: @mut CrateCtxt, ty: ast::int_ty, disr: ty::Disr) -> bool { + match ty { + ast::ty_i8 => disr as i8 as Disr == disr, + ast::ty_i16 => disr as i16 as Disr == disr, + ast::ty_i32 => disr as i32 as Disr == disr, + ast::ty_i64 => disr as i64 as Disr == disr, + ast::ty_i => int_in_range(ccx, ccx.tcx.sess.targ_cfg.int_type, disr) + } + } + match ty { + attr::UnsignedInt(ty) => uint_in_range(ccx, ty, disr), + attr::SignedInt(ty) => int_in_range(ccx, ty, disr) + } + } + fn do_check(ccx: @mut CrateCtxt, vs: &[ast::variant], - id: ast::NodeId) + id: ast::NodeId, + hint: attr::ReprAttr) -> ~[@ty::VariantInfo] { let rty = ty::node_id_to_type(ccx.tcx, id); @@ -3203,9 +3233,20 @@ pub fn check_enum_variants(ccx: @mut CrateCtxt, None => () }; - // Check for duplicate discriminator values + // Check for duplicate discriminant values if disr_vals.contains(¤t_disr_val) { - ccx.tcx.sess.span_err(v.span, "discriminator value already exists"); + ccx.tcx.sess.span_err(v.span, "discriminant value already exists"); + } + // Check for unrepresentable discriminant values + match hint { + attr::ReprAny | attr::ReprExtern => (), + attr::ReprInt(sp, ity) => { + if !disr_in_range(ccx, ity, current_disr_val) { + ccx.tcx.sess.span_err(v.span, + "discriminant value outside specified type"); + ccx.tcx.sess.span_note(sp, "discriminant type specified here"); + } + } } disr_vals.push(current_disr_val); @@ -3219,8 +3260,13 @@ pub fn check_enum_variants(ccx: @mut CrateCtxt, } let rty = ty::node_id_to_type(ccx.tcx, id); + let hint = ty::lookup_repr_hint(ccx.tcx, ast::DefId { crate: ast::LOCAL_CRATE, node: id }); + if hint != attr::ReprAny && vs.len() <= 1 { + ccx.tcx.sess.span_err(sp, format!("unsupported representation for {}variant enum", + if vs.len() == 1 { "uni" } else { "zero-" })) + } - let variants = do_check(ccx, vs, id); + let variants = do_check(ccx, vs, id, hint); // cache so that ty::enum_variants won't repeat this work ccx.tcx.enum_var_cache.insert(local_def(id), @variants); diff --git a/src/test/compile-fail/tag-variant-disr-dup.rs b/src/test/compile-fail/tag-variant-disr-dup.rs index a5f85a685e69..d0608ec4c192 100644 --- a/src/test/compile-fail/tag-variant-disr-dup.rs +++ b/src/test/compile-fail/tag-variant-disr-dup.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -//error-pattern:discriminator value already exists +//error-pattern:discriminant value already exists // black and white have the same discriminator value ...