Check repr attribute consistency at check time, not translation.

Note that raising an error during trans doesn't stop the compile or cause
rustc to exit with a failure status, currently, so this is of more than
cosmetic importance.
This commit is contained in:
Jed Davis 2013-08-29 23:45:06 -07:00
parent c8c08763ec
commit a027f164bc
3 changed files with 64 additions and 16 deletions

View File

@ -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

View File

@ -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(&current_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);

View File

@ -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 ...