Skip to content

Commit a027f16

Browse files
committed
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.
1 parent c8c0876 commit a027f16

File tree

3 files changed

+64
-16
lines changed

3 files changed

+64
-16
lines changed

src/librustc/middle/trans/adt.rs

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,8 @@ fn represent_type_uncached(cx: &mut CrateContext, t: ty::t) -> Repr {
150150

151151
if cases.len() == 0 {
152152
// Uninhabitable; represent as unit
153+
// (Typechecking will reject discriminant-sizing attrs.)
154+
assert_eq!(hint, attr::ReprAny);
153155
return Univariant(mk_struct(cx, [], false), false);
154156
}
155157

@@ -165,13 +167,6 @@ fn represent_type_uncached(cx: &mut CrateContext, t: ty::t) -> Repr {
165167
return mk_cenum(cx, hint, &bounds);
166168
}
167169

168-
if cases.len() == 1 {
169-
// Equivalent to a struct/tuple/newtype.
170-
// FIXME: should this conflict with a discriminant size hint?
171-
assert_eq!(cases[0].discr, 0);
172-
return Univariant(mk_struct(cx, cases[0].tys, false), false)
173-
}
174-
175170
// Since there's at least one
176171
// non-empty body, explicit discriminants should have
177172
// been rejected by a checker before this point.
@@ -181,8 +176,15 @@ fn represent_type_uncached(cx: &mut CrateContext, t: ty::t) -> Repr {
181176
ty::item_path_str(cx.tcx, def_id)))
182177
}
183178

184-
if cases.len() == 2 {
185-
// FIXME: disable if size hint present?
179+
if cases.len() == 1 {
180+
// Equivalent to a struct/tuple/newtype.
181+
// (Typechecking will reject discriminant-sizing attrs.)
182+
assert_eq!(hint, attr::ReprAny);
183+
return Univariant(mk_struct(cx, cases[0].tys, false), false)
184+
}
185+
186+
if cases.len() == 2 && hint == attr::ReprAny {
187+
// Nullable pointer optimization
186188
let mut discr = 0;
187189
while discr < 2 {
188190
if cases[1 - discr].is_zerolen(cx) {
@@ -205,7 +207,6 @@ fn represent_type_uncached(cx: &mut CrateContext, t: ty::t) -> Repr {
205207
}
206208

207209
// The general case.
208-
let hint = ty::lookup_repr_hint(cx.tcx, def_id);
209210
assert!((cases.len() - 1) as i64 >= 0);
210211
let bounds = IntBounds { ulo: 0, uhi: (cases.len() - 1) as u64,
211212
slo: 0, shi: (cases.len() - 1) as i64 };
@@ -307,7 +308,7 @@ fn range_to_inttype(cx: &mut CrateContext, hint: Hint, bounds: &IntBounds) -> In
307308
match hint {
308309
attr::ReprInt(span, ity) => {
309310
if !bounds_usable(cx, ity, bounds) {
310-
cx.sess.span_err(span, "representation hint insufficient for discriminant range")
311+
cx.sess.span_bug(span, "representation hint insufficient for discriminant range")
311312
}
312313
return ity;
313314
}
@@ -365,6 +366,7 @@ fn ty_of_inttype(ity: IntType) -> ty::t {
365366
}
366367
}
367368

369+
368370
/**
369371
* Returns the fields of a struct for the given representation.
370372
* All nominal types are LLVM structs, in order to be able to use

src/librustc/middle/typeck/check/mod.rs

Lines changed: 50 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,7 @@ use syntax::ast;
121121
use syntax::ast_map;
122122
use syntax::ast_util::local_def;
123123
use syntax::ast_util;
124+
use syntax::attr;
124125
use syntax::codemap::Span;
125126
use syntax::codemap;
126127
use syntax::opt_vec::OptVec;
@@ -3159,9 +3160,38 @@ pub fn check_enum_variants(ccx: @mut CrateCtxt,
31593160
sp: Span,
31603161
vs: &[ast::variant],
31613162
id: ast::NodeId) {
3163+
3164+
fn disr_in_range(ccx: @mut CrateCtxt,
3165+
ty: attr::IntType,
3166+
disr: ty::Disr) -> bool {
3167+
fn uint_in_range(ccx: @mut CrateCtxt, ty: ast::uint_ty, disr: ty::Disr) -> bool {
3168+
match ty {
3169+
ast::ty_u8 => disr as u8 as Disr == disr,
3170+
ast::ty_u16 => disr as u16 as Disr == disr,
3171+
ast::ty_u32 => disr as u32 as Disr == disr,
3172+
ast::ty_u64 => disr as u64 as Disr == disr,
3173+
ast::ty_u => uint_in_range(ccx, ccx.tcx.sess.targ_cfg.uint_type, disr)
3174+
}
3175+
}
3176+
fn int_in_range(ccx: @mut CrateCtxt, ty: ast::int_ty, disr: ty::Disr) -> bool {
3177+
match ty {
3178+
ast::ty_i8 => disr as i8 as Disr == disr,
3179+
ast::ty_i16 => disr as i16 as Disr == disr,
3180+
ast::ty_i32 => disr as i32 as Disr == disr,
3181+
ast::ty_i64 => disr as i64 as Disr == disr,
3182+
ast::ty_i => int_in_range(ccx, ccx.tcx.sess.targ_cfg.int_type, disr)
3183+
}
3184+
}
3185+
match ty {
3186+
attr::UnsignedInt(ty) => uint_in_range(ccx, ty, disr),
3187+
attr::SignedInt(ty) => int_in_range(ccx, ty, disr)
3188+
}
3189+
}
3190+
31623191
fn do_check(ccx: @mut CrateCtxt,
31633192
vs: &[ast::variant],
3164-
id: ast::NodeId)
3193+
id: ast::NodeId,
3194+
hint: attr::ReprAttr)
31653195
-> ~[@ty::VariantInfo] {
31663196

31673197
let rty = ty::node_id_to_type(ccx.tcx, id);
@@ -3203,9 +3233,20 @@ pub fn check_enum_variants(ccx: @mut CrateCtxt,
32033233
None => ()
32043234
};
32053235

3206-
// Check for duplicate discriminator values
3236+
// Check for duplicate discriminant values
32073237
if disr_vals.contains(&current_disr_val) {
3208-
ccx.tcx.sess.span_err(v.span, "discriminator value already exists");
3238+
ccx.tcx.sess.span_err(v.span, "discriminant value already exists");
3239+
}
3240+
// Check for unrepresentable discriminant values
3241+
match hint {
3242+
attr::ReprAny | attr::ReprExtern => (),
3243+
attr::ReprInt(sp, ity) => {
3244+
if !disr_in_range(ccx, ity, current_disr_val) {
3245+
ccx.tcx.sess.span_err(v.span,
3246+
"discriminant value outside specified type");
3247+
ccx.tcx.sess.span_note(sp, "discriminant type specified here");
3248+
}
3249+
}
32093250
}
32103251
disr_vals.push(current_disr_val);
32113252

@@ -3219,8 +3260,13 @@ pub fn check_enum_variants(ccx: @mut CrateCtxt,
32193260
}
32203261

32213262
let rty = ty::node_id_to_type(ccx.tcx, id);
3263+
let hint = ty::lookup_repr_hint(ccx.tcx, ast::DefId { crate: ast::LOCAL_CRATE, node: id });
3264+
if hint != attr::ReprAny && vs.len() <= 1 {
3265+
ccx.tcx.sess.span_err(sp, format!("unsupported representation for {}variant enum",
3266+
if vs.len() == 1 { "uni" } else { "zero-" }))
3267+
}
32223268

3223-
let variants = do_check(ccx, vs, id);
3269+
let variants = do_check(ccx, vs, id, hint);
32243270

32253271
// cache so that ty::enum_variants won't repeat this work
32263272
ccx.tcx.enum_var_cache.insert(local_def(id), @variants);

src/test/compile-fail/tag-variant-disr-dup.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
// option. This file may not be copied, modified, or distributed
99
// except according to those terms.
1010

11-
//error-pattern:discriminator value already exists
11+
//error-pattern:discriminant value already exists
1212

1313
// black and white have the same discriminator value ...
1414

0 commit comments

Comments
 (0)