Skip to content

Commit 25f9534

Browse files
committed
Lint non-FFI-safe enums.
1 parent 01740ac commit 25f9534

File tree

3 files changed

+80
-17
lines changed

3 files changed

+80
-17
lines changed

src/librustc/middle/lint.rs

+9
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
//! Context itself, span_lint should be used instead of add_lint.
3535
3636
use driver::session;
37+
use middle::trans::adt; // for `adt::is_ffi_safe`
3738
use middle::ty;
3839
use middle::pat_util;
3940
use metadata::csearch;
@@ -627,6 +628,14 @@ fn check_item_ctypes(cx: &Context, it: &ast::item) {
627628
"found rust type `uint` in foreign module, while \
628629
libc::c_uint or libc::c_ulong should be used");
629630
}
631+
ast::DefTy(def_id) => {
632+
if !adt::is_ffi_safe(cx.tcx, def_id) {
633+
cx.span_lint(ctypes, ty.span,
634+
"found enum type without foreign-function-safe \
635+
representation annotation in foreign module");
636+
// NOTE this message could be more helpful
637+
}
638+
}
630639
_ => ()
631640
}
632641
}

src/librustc/middle/trans/adt.rs

+52-17
Original file line numberDiff line numberDiff line change
@@ -145,22 +145,8 @@ fn represent_type_uncached(cx: &mut CrateContext, t: ty::t) -> Repr {
145145
return Univariant(mk_struct(cx, ftys, packed), dtor)
146146
}
147147
ty::ty_enum(def_id, ref substs) => {
148-
struct Case { discr: Disr, tys: ~[ty::t] };
149-
impl Case {
150-
fn is_zerolen(&self, cx: &mut CrateContext) -> bool {
151-
mk_struct(cx, self.tys, false).size == 0
152-
}
153-
fn find_ptr(&self) -> Option<uint> {
154-
self.tys.iter().position(|&ty| mono_data_classify(ty) == MonoNonNull)
155-
}
156-
}
157-
158-
let cases = do ty::enum_variants(cx.tcx, def_id).map |vi| {
159-
let arg_tys = do vi.args.map |&raw_ty| {
160-
ty::subst(cx.tcx, substs, raw_ty)
161-
};
162-
Case { discr: vi.disr_val, tys: arg_tys }
163-
};
148+
let cases = get_cases(cx.tcx, def_id, substs);
149+
let hint = ty::lookup_repr_hint(cx.tcx, def_id);
164150

165151
if cases.len() == 0 {
166152
// Uninhabitable; represent as unit
@@ -170,7 +156,6 @@ fn represent_type_uncached(cx: &mut CrateContext, t: ty::t) -> Repr {
170156
if cases.iter().all(|c| c.tys.len() == 0) {
171157
// All bodies empty -> intlike
172158
let discrs = cases.map(|c| c.discr);
173-
let hint = ty::lookup_repr_hint(cx.tcx, def_id);
174159
let bounds = IntBounds {
175160
ulo: *discrs.iter().min().unwrap(),
176161
uhi: *discrs.iter().max().unwrap(),
@@ -232,6 +217,56 @@ fn represent_type_uncached(cx: &mut CrateContext, t: ty::t) -> Repr {
232217
}
233218
}
234219

220+
/// Determine, without doing translation, whether an ADT must be FFI-safe.
221+
/// For use in lint or similar, where being sound but slightly incomplete is acceptable.
222+
pub fn is_ffi_safe(tcx: ty::ctxt, def_id: ast::DefId) -> bool {
223+
match ty::get(ty::lookup_item_type(tcx, def_id).ty).sty {
224+
ty::ty_enum(def_id, ref substs) => {
225+
let cases = get_cases(tcx, def_id, substs);
226+
// Univariant => like struct/tuple.
227+
if cases.len() <= 2 {
228+
return true;
229+
}
230+
let hint = ty::lookup_repr_hint(tcx, def_id);
231+
// Appropriate representation explicitly selected?
232+
if hint.is_ffi_safe() {
233+
return true;
234+
}
235+
// Conservative approximation of nullable pointers, for Option<~T> etc.
236+
if cases.len() == 2 && hint == attr::ReprAny &&
237+
(cases[0].tys.is_empty() && cases[1].find_ptr().is_some() ||
238+
cases[1].tys.is_empty() && cases[0].find_ptr().is_some()) {
239+
return true;
240+
}
241+
false
242+
}
243+
// struct, tuple, etc.
244+
// (is this right in the present of typedefs?)
245+
_ => true
246+
}
247+
}
248+
249+
// NOTE this should probably all be in ty
250+
struct Case { discr: Disr, tys: ~[ty::t] }
251+
impl Case {
252+
fn is_zerolen(&self, cx: &mut CrateContext) -> bool {
253+
mk_struct(cx, self.tys, false).size == 0
254+
}
255+
fn find_ptr(&self) -> Option<uint> {
256+
self.tys.iter().position(|&ty| mono_data_classify(ty) == MonoNonNull)
257+
}
258+
}
259+
260+
fn get_cases(tcx: ty::ctxt, def_id: ast::DefId, substs: &ty::substs) -> ~[Case] {
261+
do ty::enum_variants(tcx, def_id).map |vi| {
262+
let arg_tys = do vi.args.map |&raw_ty| {
263+
ty::subst(tcx, substs, raw_ty)
264+
};
265+
Case { discr: vi.disr_val, tys: arg_tys }
266+
}
267+
}
268+
269+
235270
fn mk_struct(cx: &mut CrateContext, tys: &[ty::t], packed: bool) -> Struct {
236271
let lltys = tys.map(|&ty| type_of::sizing_type_of(cx, ty));
237272
let llty_rec = Type::struct_(lltys, packed);

src/libsyntax/attr.rs

+19
Original file line numberDiff line numberDiff line change
@@ -442,6 +442,16 @@ pub enum ReprAttr {
442442
ReprExtern
443443
}
444444

445+
impl ReprAttr {
446+
pub fn is_ffi_safe(&self) -> bool {
447+
match *self {
448+
ReprAny => false,
449+
ReprInt(_sp, ity) => ity.is_ffi_safe(),
450+
ReprExtern => true
451+
}
452+
}
453+
}
454+
445455
#[deriving(Eq)]
446456
pub enum IntType {
447457
SignedInt(ast::int_ty),
@@ -456,4 +466,13 @@ impl IntType {
456466
UnsignedInt(*) => false
457467
}
458468
}
469+
fn is_ffi_safe(self) -> bool {
470+
match self {
471+
SignedInt(ast::ty_i8) | UnsignedInt(ast::ty_u8) |
472+
SignedInt(ast::ty_i16) | UnsignedInt(ast::ty_u16) |
473+
SignedInt(ast::ty_i32) | UnsignedInt(ast::ty_u32) |
474+
SignedInt(ast::ty_i64) | UnsignedInt(ast::ty_u64) => true,
475+
_ => false
476+
}
477+
}
459478
}

0 commit comments

Comments
 (0)