Lint non-FFI-safe enums.

This commit is contained in:
Jed Davis 2013-06-02 13:03:35 -07:00
parent 01740acd5a
commit 25f953437d
3 changed files with 80 additions and 17 deletions

View File

@ -34,6 +34,7 @@
//! Context itself, span_lint should be used instead of add_lint.
use driver::session;
use middle::trans::adt; // for `adt::is_ffi_safe`
use middle::ty;
use middle::pat_util;
use metadata::csearch;
@ -627,6 +628,14 @@ fn check_ty(cx: &Context, ty: &ast::Ty) {
"found rust type `uint` in foreign module, while \
libc::c_uint or libc::c_ulong should be used");
}
ast::DefTy(def_id) => {
if !adt::is_ffi_safe(cx.tcx, def_id) {
cx.span_lint(ctypes, ty.span,
"found enum type without foreign-function-safe \
representation annotation in foreign module");
// NOTE this message could be more helpful
}
}
_ => ()
}
}

View File

@ -145,22 +145,8 @@ fn represent_type_uncached(cx: &mut CrateContext, t: ty::t) -> Repr {
return Univariant(mk_struct(cx, ftys, packed), dtor)
}
ty::ty_enum(def_id, ref substs) => {
struct Case { discr: Disr, tys: ~[ty::t] };
impl Case {
fn is_zerolen(&self, cx: &mut CrateContext) -> bool {
mk_struct(cx, self.tys, false).size == 0
}
fn find_ptr(&self) -> Option<uint> {
self.tys.iter().position(|&ty| mono_data_classify(ty) == MonoNonNull)
}
}
let cases = do ty::enum_variants(cx.tcx, def_id).map |vi| {
let arg_tys = do vi.args.map |&raw_ty| {
ty::subst(cx.tcx, substs, raw_ty)
};
Case { discr: vi.disr_val, tys: arg_tys }
};
let cases = get_cases(cx.tcx, def_id, substs);
let hint = ty::lookup_repr_hint(cx.tcx, def_id);
if cases.len() == 0 {
// Uninhabitable; represent as unit
@ -170,7 +156,6 @@ fn find_ptr(&self) -> Option<uint> {
if cases.iter().all(|c| c.tys.len() == 0) {
// All bodies empty -> intlike
let discrs = cases.map(|c| c.discr);
let hint = ty::lookup_repr_hint(cx.tcx, def_id);
let bounds = IntBounds {
ulo: *discrs.iter().min().unwrap(),
uhi: *discrs.iter().max().unwrap(),
@ -232,6 +217,56 @@ fn find_ptr(&self) -> Option<uint> {
}
}
/// Determine, without doing translation, whether an ADT must be FFI-safe.
/// For use in lint or similar, where being sound but slightly incomplete is acceptable.
pub fn is_ffi_safe(tcx: ty::ctxt, def_id: ast::DefId) -> bool {
match ty::get(ty::lookup_item_type(tcx, def_id).ty).sty {
ty::ty_enum(def_id, ref substs) => {
let cases = get_cases(tcx, def_id, substs);
// Univariant => like struct/tuple.
if cases.len() <= 2 {
return true;
}
let hint = ty::lookup_repr_hint(tcx, def_id);
// Appropriate representation explicitly selected?
if hint.is_ffi_safe() {
return true;
}
// Conservative approximation of nullable pointers, for Option<~T> etc.
if cases.len() == 2 && hint == attr::ReprAny &&
(cases[0].tys.is_empty() && cases[1].find_ptr().is_some() ||
cases[1].tys.is_empty() && cases[0].find_ptr().is_some()) {
return true;
}
false
}
// struct, tuple, etc.
// (is this right in the present of typedefs?)
_ => true
}
}
// NOTE this should probably all be in ty
struct Case { discr: Disr, tys: ~[ty::t] }
impl Case {
fn is_zerolen(&self, cx: &mut CrateContext) -> bool {
mk_struct(cx, self.tys, false).size == 0
}
fn find_ptr(&self) -> Option<uint> {
self.tys.iter().position(|&ty| mono_data_classify(ty) == MonoNonNull)
}
}
fn get_cases(tcx: ty::ctxt, def_id: ast::DefId, substs: &ty::substs) -> ~[Case] {
do ty::enum_variants(tcx, def_id).map |vi| {
let arg_tys = do vi.args.map |&raw_ty| {
ty::subst(tcx, substs, raw_ty)
};
Case { discr: vi.disr_val, tys: arg_tys }
}
}
fn mk_struct(cx: &mut CrateContext, tys: &[ty::t], packed: bool) -> Struct {
let lltys = tys.map(|&ty| type_of::sizing_type_of(cx, ty));
let llty_rec = Type::struct_(lltys, packed);

View File

@ -442,6 +442,16 @@ pub enum ReprAttr {
ReprExtern
}
impl ReprAttr {
pub fn is_ffi_safe(&self) -> bool {
match *self {
ReprAny => false,
ReprInt(_sp, ity) => ity.is_ffi_safe(),
ReprExtern => true
}
}
}
#[deriving(Eq)]
pub enum IntType {
SignedInt(ast::int_ty),
@ -456,4 +466,13 @@ pub fn is_signed(self) -> bool {
UnsignedInt(*) => false
}
}
fn is_ffi_safe(self) -> bool {
match self {
SignedInt(ast::ty_i8) | UnsignedInt(ast::ty_u8) |
SignedInt(ast::ty_i16) | UnsignedInt(ast::ty_u16) |
SignedInt(ast::ty_i32) | UnsignedInt(ast::ty_u32) |
SignedInt(ast::ty_i64) | UnsignedInt(ast::ty_u64) => true,
_ => false
}
}
}