about summary refs log tree commit diff
diff options
context:
space:
mode:
authorCorey Richardson <corey@octayn.net>2014-05-26 23:56:52 -0700
committerCorey Richardson <corey@octayn.net>2014-08-20 21:02:23 -0400
commit6e8ff999589a7b4c7f62f7128f3e028a7b3ea64f (patch)
tree71eb65a9aefa3503f15b4bd8245f36081f658c27
parent54bd9e6323742de87a526931a15558319db30604 (diff)
downloadrust-6e8ff999589a7b4c7f62f7128f3e028a7b3ea64f.tar.gz
rust-6e8ff999589a7b4c7f62f7128f3e028a7b3ea64f.zip
librustc: handle repr on structs, require it for ffi, unify with packed
As of RFC 18, struct layout is undefined. Opting into a C-compatible struct
layout is now down with #[repr(C)]. For consistency, specifying a packed
layout is now also down with #[repr(packed)]. Both can be specified.

To fix errors caused by this, just add #[repr(C)] to the structs, and change
 #[packed] to #[repr(packed)]

Closes #14309

[breaking-change]
-rw-r--r--src/liblibc/lib.rs1
-rw-r--r--src/librustc/lint/builtin.rs18
-rw-r--r--src/librustc/middle/dead.rs7
-rw-r--r--src/librustc/middle/trans/adt.rs38
-rw-r--r--src/librustc/middle/ty.rs88
-rw-r--r--src/librustc/middle/typeck/check/mod.rs11
-rw-r--r--src/librustrt/libunwind.rs1
-rw-r--r--src/libstd/rt/backtrace.rs2
-rw-r--r--src/libsyntax/attr.rs45
-rw-r--r--src/test/auxiliary/packed.rs2
-rw-r--r--src/test/compile-fail/issue-14309.rs49
-rw-r--r--src/test/compile-fail/lint-ctypes-enum.rs4
-rw-r--r--src/test/compile-fail/packed-struct-generic-transmute.rs2
-rw-r--r--src/test/compile-fail/packed-struct-transmute.rs2
-rw-r--r--src/test/debuginfo/c-style-enum-in-composite.rs2
-rw-r--r--src/test/debuginfo/packed-struct-with-destructor.rs10
-rw-r--r--src/test/debuginfo/packed-struct.rs6
-rw-r--r--src/test/run-make/extern-fn-with-packed-struct/test.rs2
-rw-r--r--src/test/run-pass/packed-struct-borrow-element.rs2
-rw-r--r--src/test/run-pass/packed-struct-generic-layout.rs2
-rw-r--r--src/test/run-pass/packed-struct-generic-size.rs2
-rw-r--r--src/test/run-pass/packed-struct-layout.rs4
-rw-r--r--src/test/run-pass/packed-struct-match.rs2
-rw-r--r--src/test/run-pass/packed-struct-size.rs10
-rw-r--r--src/test/run-pass/packed-struct-vec.rs2
-rw-r--r--src/test/run-pass/packed-tuple-struct-layout.rs4
-rw-r--r--src/test/run-pass/packed-tuple-struct-size.rs10
27 files changed, 210 insertions, 118 deletions
diff --git a/src/liblibc/lib.rs b/src/liblibc/lib.rs
index 9c699a88007..874c7c2409c 100644
--- a/src/liblibc/lib.rs
+++ b/src/liblibc/lib.rs
@@ -334,6 +334,7 @@ pub mod types {
                 __variant1,
                 __variant2,
             }
+
             pub enum FILE {}
             pub enum fpos_t {}
         }
diff --git a/src/librustc/lint/builtin.rs b/src/librustc/lint/builtin.rs
index 7e0ba613e3d..ec25c27539b 100644
--- a/src/librustc/lint/builtin.rs
+++ b/src/librustc/lint/builtin.rs
@@ -27,7 +27,6 @@
 
 use metadata::csearch;
 use middle::def::*;
-use middle::trans::adt; // for `adt::is_ffi_safe`
 use middle::typeck::astconv::ast_ty_to_ty;
 use middle::typeck::infer;
 use middle::{typeck, ty, def, pat_util, stability};
@@ -362,8 +361,13 @@ impl LintPass for CTypes {
                                          "found rust type `uint` in foreign module, while \
                                           libc::c_uint or libc::c_ulong should be used");
                         }
-                        def::DefTy(def_id) => {
-                            if !adt::is_ffi_safe(cx.tcx, def_id) {
+                        def::DefTy(..) => {
+                            let tty = match cx.tcx.ast_ty_to_ty_cache.borrow().find(&ty.id) {
+                                Some(&ty::atttce_resolved(t)) => t,
+                                _ => fail!("ast_ty_to_ty_cache was incomplete after typeck!")
+                            };
+
+                            if !ty::is_ffi_safe(cx.tcx, tty) {
                                 cx.span_lint(CTYPES, ty.span,
                                              "found enum type without foreign-function-safe
                                              representation annotation in foreign module, consider \
@@ -770,9 +774,10 @@ impl LintPass for NonCamelCaseTypes {
             }
         }
 
-        let has_extern_repr = it.attrs.iter().fold(attr::ReprAny, |acc, attr| {
-            attr::find_repr_attr(cx.tcx.sess.diagnostic(), attr, acc)
-        }) == attr::ReprExtern;
+        let has_extern_repr = it.attrs.iter().map(|attr| {
+            attr::find_repr_attrs(cx.tcx.sess.diagnostic(), attr).iter()
+                .any(|r| r == &attr::ReprExtern)
+        }).any(|x| x);
         if has_extern_repr { return }
 
         match it.node {
@@ -783,6 +788,7 @@ impl LintPass for NonCamelCaseTypes {
                 check_case(cx, "trait", it.ident, it.span)
             }
             ast::ItemEnum(ref enum_definition, _) => {
+                if has_extern_repr { return }
                 check_case(cx, "type", it.ident, it.span);
                 for variant in enum_definition.variants.iter() {
                     check_case(cx, "variant", variant.node.name, variant.span);
diff --git a/src/librustc/middle/dead.rs b/src/librustc/middle/dead.rs
index 9a7bfb0e6dc..0dfdf455743 100644
--- a/src/librustc/middle/dead.rs
+++ b/src/librustc/middle/dead.rs
@@ -195,9 +195,10 @@ impl<'a> MarkSymbolVisitor<'a> {
             ast_map::NodeItem(item) => {
                 match item.node {
                     ast::ItemStruct(..) => {
-                        let has_extern_repr = item.attrs.iter().fold(attr::ReprAny, |acc, attr| {
-                            attr::find_repr_attr(self.tcx.sess.diagnostic(), attr, acc)
-                        }) == attr::ReprExtern;
+                        let has_extern_repr = item.attrs.iter().fold(false, |acc, attr| {
+                            acc || attr::find_repr_attrs(self.tcx.sess.diagnostic(), attr)
+                                         .iter().any(|&x| x == attr::ReprExtern)
+                        });
 
                         visit::walk_item(self, &*item, MarkSymbolVisitorContext {
                             struct_has_extern_repr: has_extern_repr,
diff --git a/src/librustc/middle/trans/adt.rs b/src/librustc/middle/trans/adt.rs
index 9c68631baa4..483937edd9f 100644
--- a/src/librustc/middle/trans/adt.rs
+++ b/src/librustc/middle/trans/adt.rs
@@ -46,6 +46,8 @@
 #![allow(unsigned_negate)]
 
 use libc::c_ulonglong;
+use std::collections::Map;
+use std::num::Int;
 use std::rc::Rc;
 
 use llvm::{ValueRef, True, IntEQ, IntNE};
@@ -178,7 +180,8 @@ fn represent_type_uncached(cx: &CrateContext, t: ty::t) -> Repr {
         }
         ty::ty_enum(def_id, ref substs) => {
             let cases = get_cases(cx.tcx(), def_id, substs);
-            let hint = ty::lookup_repr_hint(cx.tcx(), def_id);
+            let hint = *ty::lookup_repr_hints(cx.tcx(), def_id).as_slice().get(0)
+                .unwrap_or(&attr::ReprAny);
 
             let dtor = ty::ty_dtor(cx.tcx(), def_id).has_drop_flag();
 
@@ -266,36 +269,6 @@ fn represent_type_uncached(cx: &CrateContext, t: ty::t) -> Repr {
     }
 }
 
-/// 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, _) => {
-            let variants = ty::enum_variants(tcx, def_id);
-            // Univariant => like struct/tuple.
-            if variants.len() <= 1 {
-                return true;
-            }
-            let hint = ty::lookup_repr_hint(tcx, def_id);
-            // Appropriate representation explicitly selected?
-            if hint.is_ffi_safe() {
-                return true;
-            }
-            // Option<Box<T>> and similar are used in FFI.  Rather than try to
-            // resolve type parameters and recognize this case exactly, this
-            // overapproximates -- assuming that if a non-C-like enum is being
-            // used in FFI then the user knows what they're doing.
-            if variants.iter().any(|vi| !vi.args.is_empty()) {
-                return true;
-            }
-            false
-        }
-        // struct, tuple, etc.
-        // (is this right in the present of typedefs?)
-        _ => true
-    }
-}
-
 // this should probably all be in ty
 struct Case {
     discr: Disr,
@@ -427,6 +400,9 @@ fn range_to_inttype(cx: &CrateContext, hint: Hint, bounds: &IntBounds) -> IntTyp
         }
         attr::ReprAny => {
             attempts = choose_shortest;
+        },
+        attr::ReprPacked => {
+            cx.tcx.sess.bug("range_to_inttype: found ReprPacked on an enum");
         }
     }
     for &ity in attempts.iter() {
diff --git a/src/librustc/middle/ty.rs b/src/librustc/middle/ty.rs
index ae96937757f..83eb84a326a 100644
--- a/src/librustc/middle/ty.rs
+++ b/src/librustc/middle/ty.rs
@@ -481,7 +481,7 @@ pub struct t { inner: *const t_opaque }
 
 impl fmt::Show for t {
     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
-        "*t_opaque".fmt(f)
+        write!(f, "{}", get(*self))
     }
 }
 
@@ -1962,7 +1962,8 @@ def_type_content_sets!(
         // ReachesManaged /* see [1] below */  = 0b0000_0100__0000_0000__0000,
         ReachesMutable                      = 0b0000_1000__0000_0000__0000,
         ReachesNoSync                       = 0b0001_0000__0000_0000__0000,
-        ReachesAll                          = 0b0001_1111__0000_0000__0000,
+        ReachesFfiUnsafe                    = 0b0010_0000__0000_0000__0000,
+        ReachesAll                          = 0b0011_1111__0000_0000__0000,
 
         // Things that cause values to *move* rather than *copy*
         Moves                               = 0b0000_0000__0000_1011__0000,
@@ -2199,6 +2200,11 @@ pub fn type_contents(cx: &ctxt, ty: t) -> TypeContents {
         cache.insert(ty_id, TC::None);
 
         let result = match get(ty).sty {
+            // uint and int are ffi-unsafe
+            ty_uint(ast::TyU) | ty_int(ast::TyI) => {
+                TC::ReachesFfiUnsafe
+            }
+
             // Scalar and unique types are sendable, and durable
             ty_nil | ty_bot | ty_bool | ty_int(_) | ty_uint(_) | ty_float(_) |
             ty_bare_fn(_) | ty::ty_char | ty_str => {
@@ -2206,22 +2212,22 @@ pub fn type_contents(cx: &ctxt, ty: t) -> TypeContents {
             }
 
             ty_closure(ref c) => {
-                closure_contents(cx, &**c)
+                closure_contents(cx, &**c) | TC::ReachesFfiUnsafe
             }
 
             ty_box(typ) => {
-                tc_ty(cx, typ, cache).managed_pointer()
+                tc_ty(cx, typ, cache).managed_pointer() | TC::ReachesFfiUnsafe
             }
 
             ty_uniq(typ) => {
-                match get(typ).sty {
+                TC::ReachesFfiUnsafe | match get(typ).sty {
                     ty_str => TC::OwnsOwned,
                     _ => tc_ty(cx, typ, cache).owned_pointer(),
                 }
             }
 
             ty_trait(box ty::TyTrait { bounds, .. }) => {
-                object_contents(cx, bounds)
+                object_contents(cx, bounds) | TC::ReachesFfiUnsafe
             }
 
             ty_ptr(ref mt) => {
@@ -2229,8 +2235,9 @@ pub fn type_contents(cx: &ctxt, ty: t) -> TypeContents {
             }
 
             ty_rptr(r, ref mt) => {
-                match get(mt.ty).sty {
+                TC::ReachesFfiUnsafe | match get(mt.ty).sty {
                     ty_str => borrowed_contents(r, ast::MutImmutable),
+                    ty_vec(..) => tc_ty(cx, mt.ty, cache).reference(borrowed_contents(r, mt.mutbl)),
                     _ => tc_ty(cx, mt.ty, cache).reference(borrowed_contents(r, mt.mutbl)),
                 }
             }
@@ -2244,6 +2251,11 @@ pub fn type_contents(cx: &ctxt, ty: t) -> TypeContents {
                 let mut res =
                     TypeContents::union(flds.as_slice(),
                                         |f| tc_mt(cx, f.mt, cache));
+
+                if !lookup_repr_hints(cx, did).contains(&attr::ReprExtern) {
+                    res = res | TC::ReachesFfiUnsafe;
+                }
+
                 if ty::has_dtor(cx, did) {
                     res = res | TC::OwnsDtor;
                 }
@@ -2273,9 +2285,49 @@ pub fn type_contents(cx: &ctxt, ty: t) -> TypeContents {
                             tc_ty(cx, *arg_ty, cache)
                         })
                     });
+
                 if ty::has_dtor(cx, did) {
                     res = res | TC::OwnsDtor;
                 }
+
+                if variants.len() != 0 {
+                    let repr_hints = lookup_repr_hints(cx, did);
+                    if repr_hints.len() > 1 {
+                        // this is an error later on, but this type isn't safe
+                        res = res | TC::ReachesFfiUnsafe;
+                    }
+
+                    match repr_hints.as_slice().get(0) {
+                        Some(h) => if !h.is_ffi_safe() {
+                            res = res | TC::ReachesFfiUnsafe;
+                        },
+                        // ReprAny
+                        None => {
+                            res = res | TC::ReachesFfiUnsafe;
+
+                            // We allow ReprAny enums if they are eligible for
+                            // the nullable pointer optimization and the
+                            // contained type is an `extern fn`
+
+                            if variants.len() == 2 {
+                                let mut data_idx = 0;
+
+                                if variants.get(0).args.len() == 0 {
+                                    data_idx = 1;
+                                }
+
+                                if variants.get(data_idx).args.len() == 1 {
+                                    match get(*variants.get(data_idx).args.get(0)).sty {
+                                        ty_bare_fn(..) => { res = res - TC::ReachesFfiUnsafe; }
+                                        _ => { }
+                                    }
+                                }
+                            }
+                        }
+                    }
+                }
+
+
                 apply_lang_items(cx, did, res)
             }
 
@@ -2427,6 +2479,10 @@ pub fn type_moves_by_default(cx: &ctxt, ty: t) -> bool {
     type_contents(cx, ty).moves_by_default(cx)
 }
 
+pub fn is_ffi_safe(cx: &ctxt, ty: t) -> bool {
+    !type_contents(cx, ty).intersects(TC::ReachesFfiUnsafe)
+}
+
 // True if instantiating an instance of `r_ty` requires an instance of `r_ty`.
 pub fn is_instantiable(cx: &ctxt, r_ty: t) -> bool {
     fn type_requires(cx: &ctxt, seen: &mut Vec<DefId>,
@@ -3945,7 +4001,7 @@ pub fn substd_enum_variants(cx: &ctxt,
                          -> Vec<Rc<VariantInfo>> {
     enum_variants(cx, id).iter().map(|variant_info| {
         let substd_args = variant_info.args.iter()
-            .map(|aty| aty.subst(cx, substs)).collect();
+            .map(|aty| aty.subst(cx, substs)).collect::<Vec<_>>();
 
         let substd_ctor_ty = variant_info.ctor_ty.subst(cx, substs);
 
@@ -4168,9 +4224,9 @@ pub fn has_attr(tcx: &ctxt, did: DefId, attr: &str) -> bool {
     found
 }
 
-/// Determine whether an item is annotated with `#[packed]`
+/// Determine whether an item is annotated with `#[repr(packed)]`
 pub fn lookup_packed(tcx: &ctxt, did: DefId) -> bool {
-    has_attr(tcx, did, "packed")
+    lookup_repr_hints(tcx, did).contains(&attr::ReprPacked)
 }
 
 /// Determine whether an item is annotated with `#[simd]`
@@ -4178,14 +4234,16 @@ pub fn lookup_simd(tcx: &ctxt, did: DefId) -> bool {
     has_attr(tcx, did, "simd")
 }
 
-// Obtain the representation annotation for a definition.
-pub fn lookup_repr_hint(tcx: &ctxt, did: DefId) -> attr::ReprAttr {
-    let mut acc = attr::ReprAny;
+/// Obtain the representation annotation for a struct definition.
+pub fn lookup_repr_hints(tcx: &ctxt, did: DefId) -> Vec<attr::ReprAttr> {
+    let mut acc = Vec::new();
+
     ty::each_attr(tcx, did, |meta| {
-        acc = attr::find_repr_attr(tcx.sess.diagnostic(), meta, acc);
+        acc.extend(attr::find_repr_attrs(tcx.sess.diagnostic(), meta).move_iter());
         true
     });
-    return acc;
+
+    acc
 }
 
 // Look up a field ID, whether or not it's local
diff --git a/src/librustc/middle/typeck/check/mod.rs b/src/librustc/middle/typeck/check/mod.rs
index 1cec680ff2c..5f89a1f48bd 100644
--- a/src/librustc/middle/typeck/check/mod.rs
+++ b/src/librustc/middle/typeck/check/mod.rs
@@ -4180,13 +4180,13 @@ pub fn check_enum_variants(ccx: &CrateCtxt,
                     let inh = blank_inherited_fields(ccx);
                     let fcx = blank_fn_ctxt(ccx, &inh, rty, e.id);
                     let declty = match hint {
-                        attr::ReprAny | attr::ReprExtern => ty::mk_int(),
+                        attr::ReprAny | attr::ReprPacked | attr::ReprExtern => ty::mk_int(),
                         attr::ReprInt(_, attr::SignedInt(ity)) => {
                             ty::mk_mach_int(ity)
                         }
                         attr::ReprInt(_, attr::UnsignedInt(ity)) => {
                             ty::mk_mach_uint(ity)
-                        }
+                        },
                     };
                     check_const_with_ty(&fcx, e.span, &*e, declty);
                     // check_expr (from check_const pass) doesn't guarantee
@@ -4225,6 +4225,9 @@ pub fn check_enum_variants(ccx: &CrateCtxt,
                             "discriminant type specified here");
                     }
                 }
+                attr::ReprPacked => {
+                    ccx.tcx.sess.bug("range_to_inttype: found ReprPacked on an enum");
+                }
             }
             disr_vals.push(current_disr_val);
 
@@ -4238,7 +4241,9 @@ pub fn check_enum_variants(ccx: &CrateCtxt,
         return variants;
     }
 
-    let hint = ty::lookup_repr_hint(ccx.tcx, ast::DefId { krate: ast::LOCAL_CRATE, node: id });
+    let hint = *ty::lookup_repr_hints(ccx.tcx, ast::DefId { krate: ast::LOCAL_CRATE, node: id })
+                    .as_slice().get(0).unwrap_or(&attr::ReprAny);
+
     if hint != attr::ReprAny && vs.len() <= 1 {
         if vs.len() == 1 {
             span_err!(ccx.tcx.sess, sp, E0083,
diff --git a/src/librustrt/libunwind.rs b/src/librustrt/libunwind.rs
index 72d78d84aa3..8d2305a8a80 100644
--- a/src/librustrt/libunwind.rs
+++ b/src/librustrt/libunwind.rs
@@ -72,6 +72,7 @@ pub static unwinder_private_data_size: uint = 5;
 #[cfg(target_arch = "mipsel")]
 pub static unwinder_private_data_size: uint = 2;
 
+#[repr(C)]
 pub struct _Unwind_Exception {
     pub exception_class: _Unwind_Exception_Class,
     pub exception_cleanup: _Unwind_Exception_Cleanup_Fn,
diff --git a/src/libstd/rt/backtrace.rs b/src/libstd/rt/backtrace.rs
index a4491b2ab1d..5ef1286500c 100644
--- a/src/libstd/rt/backtrace.rs
+++ b/src/libstd/rt/backtrace.rs
@@ -701,7 +701,7 @@ mod imp {
     static IMAGE_FILE_MACHINE_IA64: libc::DWORD = 0x0200;
     static IMAGE_FILE_MACHINE_AMD64: libc::DWORD = 0x8664;
 
-    #[packed]
+    #[repr(packed)]
     struct SYMBOL_INFO {
         SizeOfStruct: libc::c_ulong,
         TypeIndex: libc::c_ulong,
diff --git a/src/libsyntax/attr.rs b/src/libsyntax/attr.rs
index 21252619d11..4b2a3073755 100644
--- a/src/libsyntax/attr.rs
+++ b/src/libsyntax/attr.rs
@@ -418,19 +418,14 @@ pub fn require_unique_names(diagnostic: &SpanHandler, metas: &[Gc<MetaItem>]) {
 }
 
 
-/// Fold this over attributes to parse #[repr(...)] forms.
+/// Parse #[repr(...)] forms.
 ///
 /// Valid repr contents: any of the primitive integral type names (see
-/// `int_type_of_word`, below) to specify the discriminant type; and `C`, to use
-/// the same discriminant size that the corresponding C enum would.  These are
-/// not allowed on univariant or zero-variant enums, which have no discriminant.
-///
-/// If a discriminant type is so specified, then the discriminant will be
-/// present (before fields, if any) with that type; representation
-/// optimizations which would remove it will not be done.
-pub fn find_repr_attr(diagnostic: &SpanHandler, attr: &Attribute, acc: ReprAttr)
-    -> ReprAttr {
-    let mut acc = acc;
+/// `int_type_of_word`, below) to specify enum discriminant type; `C`, to use
+/// the same discriminant size that the corresponding C enum would or C
+/// structure layout, and `packed` to remove padding.
+pub fn find_repr_attrs(diagnostic: &SpanHandler, attr: &Attribute) -> Vec<ReprAttr> {
+    let mut acc = Vec::new();
     match attr.node.value.node {
         ast::MetaList(ref s, ref items) if s.equiv(&("repr")) => {
             mark_used(attr);
@@ -439,28 +434,26 @@ pub fn find_repr_attr(diagnostic: &SpanHandler, attr: &Attribute, acc: ReprAttr)
                     ast::MetaWord(ref word) => {
                         let hint = match word.get() {
                             // Can't use "extern" because it's not a lexical identifier.
-                            "C" => ReprExtern,
+                            "C" => Some(ReprExtern),
+                            "packed" => Some(ReprPacked),
                             _ => match int_type_of_word(word.get()) {
-                                Some(ity) => ReprInt(item.span, ity),
+                                Some(ity) => Some(ReprInt(item.span, ity)),
                                 None => {
                                     // Not a word we recognize
                                     diagnostic.span_err(item.span,
                                                         "unrecognized representation hint");
-                                    ReprAny
+                                    None
                                 }
                             }
                         };
-                        if hint != ReprAny {
-                            if acc == ReprAny {
-                                acc = hint;
-                            } else if acc != hint {
-                                diagnostic.span_warn(item.span,
-                                                     "conflicting representation hint ignored")
-                            }
+
+                        match hint {
+                            Some(h) => acc.push(h),
+                            None => { }
                         }
                     }
                     // Not a word:
-                    _ => diagnostic.span_err(item.span, "unrecognized representation hint")
+                    _ => diagnostic.span_err(item.span, "unrecognized enum representation hint")
                 }
             }
         }
@@ -490,7 +483,8 @@ fn int_type_of_word(s: &str) -> Option<IntType> {
 pub enum ReprAttr {
     ReprAny,
     ReprInt(Span, IntType),
-    ReprExtern
+    ReprExtern,
+    ReprPacked,
 }
 
 impl ReprAttr {
@@ -498,7 +492,8 @@ impl ReprAttr {
         match *self {
             ReprAny => false,
             ReprInt(_sp, ity) => ity.is_ffi_safe(),
-            ReprExtern => true
+            ReprExtern => true,
+            ReprPacked => false
         }
     }
 }
@@ -523,7 +518,7 @@ impl IntType {
             SignedInt(ast::TyI16) | UnsignedInt(ast::TyU16) |
             SignedInt(ast::TyI32) | UnsignedInt(ast::TyU32) |
             SignedInt(ast::TyI64) | UnsignedInt(ast::TyU64) => true,
-            _ => false
+            SignedInt(ast::TyI) | UnsignedInt(ast::TyU) => false
         }
     }
 }
diff --git a/src/test/auxiliary/packed.rs b/src/test/auxiliary/packed.rs
index 54b2658e380..86f5f93e3cf 100644
--- a/src/test/auxiliary/packed.rs
+++ b/src/test/auxiliary/packed.rs
@@ -8,7 +8,7 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-#[packed]
+#[repr(packed)]
 pub struct S {
     a: u8,
     b: u32
diff --git a/src/test/compile-fail/issue-14309.rs b/src/test/compile-fail/issue-14309.rs
new file mode 100644
index 00000000000..eb8e75eae78
--- /dev/null
+++ b/src/test/compile-fail/issue-14309.rs
@@ -0,0 +1,49 @@
+// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![deny(ctypes)]
+#![allow(dead_code)]
+
+struct A { //~ NOTE consider adding `#[repr(C)]` to this type
+    x: int
+}
+
+#[repr(C, packed)]
+struct B {
+    x: int,
+    y: A
+}
+
+#[repr(C)]
+struct C {
+    x: int
+}
+
+type A2 = A;
+type B2 = B;
+type C2 = C;
+
+#[repr(C)]
+struct D {
+    x: C,
+    y: A
+}
+
+extern "C" {
+    fn foo(x: A); //~ ERROR found struct without FFI-safe representation used in FFI
+    fn bar(x: B); //~ ERROR FFI-safe
+    fn baz(x: C);
+    fn qux(x: A2); //~ ERROR FFI-safe
+    fn quux(x: B2); //~ ERROR FFI-safe
+    fn corge(x: C2);
+    fn fred(x: D); //~ ERROR FFI-safe
+}
+
+fn main() { }
diff --git a/src/test/compile-fail/lint-ctypes-enum.rs b/src/test/compile-fail/lint-ctypes-enum.rs
index e968bd601c5..d597006e29d 100644
--- a/src/test/compile-fail/lint-ctypes-enum.rs
+++ b/src/test/compile-fail/lint-ctypes-enum.rs
@@ -19,8 +19,8 @@ enum T { E, F, G }
 extern {
    fn zf(x: Z);
    fn uf(x: U);
-   fn bf(x: B); //~ ERROR found enum type without foreign-function-safe
-   fn tf(x: T); //~ ERROR found enum type without foreign-function-safe
+   fn bf(x: B); //~ ERROR found enum without FFI-safe
+   fn tf(x: T); //~ ERROR found enum without FFI-safe
 }
 
 pub fn main() { }
diff --git a/src/test/compile-fail/packed-struct-generic-transmute.rs b/src/test/compile-fail/packed-struct-generic-transmute.rs
index b5bb3314dcf..7a1ff574488 100644
--- a/src/test/compile-fail/packed-struct-generic-transmute.rs
+++ b/src/test/compile-fail/packed-struct-generic-transmute.rs
@@ -19,7 +19,7 @@ extern crate debug;
 
 use std::mem;
 
-#[packed]
+#[repr(packed)]
 struct Foo<T,S> {
     bar: T,
     baz: S
diff --git a/src/test/compile-fail/packed-struct-transmute.rs b/src/test/compile-fail/packed-struct-transmute.rs
index 0611c470d10..f92cc4b1344 100644
--- a/src/test/compile-fail/packed-struct-transmute.rs
+++ b/src/test/compile-fail/packed-struct-transmute.rs
@@ -19,7 +19,7 @@ extern crate debug;
 
 use std::mem;
 
-#[packed]
+#[repr(packed)]
 struct Foo {
     bar: u8,
     baz: uint
diff --git a/src/test/debuginfo/c-style-enum-in-composite.rs b/src/test/debuginfo/c-style-enum-in-composite.rs
index f7351728290..ef23e7ee876 100644
--- a/src/test/debuginfo/c-style-enum-in-composite.rs
+++ b/src/test/debuginfo/c-style-enum-in-composite.rs
@@ -87,7 +87,7 @@ struct PaddedStruct {
     e: i16
 }
 
-#[packed]
+#[repr(packed)]
 struct PackedStruct {
     a: i16,
     b: AnEnum,
diff --git a/src/test/debuginfo/packed-struct-with-destructor.rs b/src/test/debuginfo/packed-struct-with-destructor.rs
index 215d961b71b..00a560edbf0 100644
--- a/src/test/debuginfo/packed-struct-with-destructor.rs
+++ b/src/test/debuginfo/packed-struct-with-destructor.rs
@@ -77,7 +77,7 @@
 
 #![allow(unused_variable)]
 
-#[packed]
+#[repr(packed)]
 struct Packed {
     x: i16,
     y: i32,
@@ -88,7 +88,7 @@ impl Drop for Packed {
     fn drop(&mut self) {}
 }
 
-#[packed]
+#[repr(packed)]
 struct PackedInPacked {
     a: i32,
     b: Packed,
@@ -113,7 +113,7 @@ impl Drop for Unpacked {
     fn drop(&mut self) {}
 }
 
-#[packed]
+#[repr(packed)]
 struct UnpackedInPacked {
     a: i16,
     b: Unpacked,
@@ -121,7 +121,7 @@ struct UnpackedInPacked {
     d: i64
 }
 
-#[packed]
+#[repr(packed)]
 struct PackedInPackedWithDrop {
     a: i32,
     b: Packed,
@@ -144,7 +144,7 @@ impl Drop for PackedInUnpackedWithDrop {
     fn drop(&mut self) {}
 }
 
-#[packed]
+#[repr(packed)]
 struct UnpackedInPackedWithDrop {
     a: i16,
     b: Unpacked,
diff --git a/src/test/debuginfo/packed-struct.rs b/src/test/debuginfo/packed-struct.rs
index 8201afe3a01..bf2213509cf 100644
--- a/src/test/debuginfo/packed-struct.rs
+++ b/src/test/debuginfo/packed-struct.rs
@@ -63,14 +63,14 @@
 
 #![allow(unused_variable)]
 
-#[packed]
+#[repr(packed)]
 struct Packed {
     x: i16,
     y: i32,
     z: i64
 }
 
-#[packed]
+#[repr(packed)]
 struct PackedInPacked {
     a: i32,
     b: Packed,
@@ -95,7 +95,7 @@ struct Unpacked {
 }
 
 // layout (64 bit): aabb bbbb bbbb bbbb bbbb bbbb bbcc cccc cccc cccc cccc cccc ccdd dddd dd
-#[packed]
+#[repr(packed)]
 struct UnpackedInPacked {
     a: i16,
     b: Unpacked,
diff --git a/src/test/run-make/extern-fn-with-packed-struct/test.rs b/src/test/run-make/extern-fn-with-packed-struct/test.rs
index 4b07b7f39f5..8d8daed1393 100644
--- a/src/test/run-make/extern-fn-with-packed-struct/test.rs
+++ b/src/test/run-make/extern-fn-with-packed-struct/test.rs
@@ -8,7 +8,7 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-#[packed]
+#[repr(packed)]
 #[deriving(PartialEq, Show)]
 struct Foo {
     a: i8,
diff --git a/src/test/run-pass/packed-struct-borrow-element.rs b/src/test/run-pass/packed-struct-borrow-element.rs
index 1434e1da4c7..c6c74fe3fda 100644
--- a/src/test/run-pass/packed-struct-borrow-element.rs
+++ b/src/test/run-pass/packed-struct-borrow-element.rs
@@ -8,7 +8,7 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-#[packed]
+#[repr(packed)]
 struct Foo {
     bar: u8,
     baz: uint
diff --git a/src/test/run-pass/packed-struct-generic-layout.rs b/src/test/run-pass/packed-struct-generic-layout.rs
index 20269766158..999e4aeeb59 100644
--- a/src/test/run-pass/packed-struct-generic-layout.rs
+++ b/src/test/run-pass/packed-struct-generic-layout.rs
@@ -10,7 +10,7 @@
 
 use std::mem;
 
-#[packed]
+#[repr(packed)]
 struct S<T, S> {
     a: T,
     b: u8,
diff --git a/src/test/run-pass/packed-struct-generic-size.rs b/src/test/run-pass/packed-struct-generic-size.rs
index 80f37a0fa3d..45791332bbe 100644
--- a/src/test/run-pass/packed-struct-generic-size.rs
+++ b/src/test/run-pass/packed-struct-generic-size.rs
@@ -10,7 +10,7 @@
 
 use std::mem;
 
-#[packed]
+#[repr(packed)]
 struct S<T, S> {
     a: T,
     b: u8,
diff --git a/src/test/run-pass/packed-struct-layout.rs b/src/test/run-pass/packed-struct-layout.rs
index 7f9bf8e7d57..b4fbf0820cd 100644
--- a/src/test/run-pass/packed-struct-layout.rs
+++ b/src/test/run-pass/packed-struct-layout.rs
@@ -10,13 +10,13 @@
 
 use std::mem;
 
-#[packed]
+#[repr(packed)]
 struct S4 {
     a: u8,
     b: [u8, .. 3],
 }
 
-#[packed]
+#[repr(packed)]
 struct S5 {
     a: u8,
     b: u32
diff --git a/src/test/run-pass/packed-struct-match.rs b/src/test/run-pass/packed-struct-match.rs
index 27ab2c83e55..46ffed0cba9 100644
--- a/src/test/run-pass/packed-struct-match.rs
+++ b/src/test/run-pass/packed-struct-match.rs
@@ -8,7 +8,7 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
-#[packed]
+#[repr(packed)]
 struct Foo {
     bar: u8,
     baz: uint
diff --git a/src/test/run-pass/packed-struct-size.rs b/src/test/run-pass/packed-struct-size.rs
index 8dbc7cf327d..cfea444d7ff 100644
--- a/src/test/run-pass/packed-struct-size.rs
+++ b/src/test/run-pass/packed-struct-size.rs
@@ -12,19 +12,19 @@
 use std::mem;
 use std::gc::Gc;
 
-#[packed]
+#[repr(packed)]
 struct S4 {
     a: u8,
     b: [u8, .. 3],
 }
 
-#[packed]
+#[repr(packed)]
 struct S5 {
     a: u8,
     b: u32
 }
 
-#[packed]
+#[repr(packed)]
 struct S13 {
     a: i64,
     b: f32,
@@ -36,14 +36,14 @@ enum Foo {
     Baz = 2
 }
 
-#[packed]
+#[repr(packed)]
 struct S3_Foo {
     a: u8,
     b: u16,
     c: Foo
 }
 
-#[packed]
+#[repr(packed)]
 struct S7_Option {
     a: f32,
     b: u8,
diff --git a/src/test/run-pass/packed-struct-vec.rs b/src/test/run-pass/packed-struct-vec.rs
index 8309e95820c..c20e62351a6 100644
--- a/src/test/run-pass/packed-struct-vec.rs
+++ b/src/test/run-pass/packed-struct-vec.rs
@@ -12,7 +12,7 @@
 
 use std::mem;
 
-#[packed]
+#[repr(packed)]
 #[deriving(PartialEq, Show)]
 struct Foo {
     bar: u8,
diff --git a/src/test/run-pass/packed-tuple-struct-layout.rs b/src/test/run-pass/packed-tuple-struct-layout.rs
index 3ec6182beb2..5fb43503ccb 100644
--- a/src/test/run-pass/packed-tuple-struct-layout.rs
+++ b/src/test/run-pass/packed-tuple-struct-layout.rs
@@ -10,10 +10,10 @@
 
 use std::mem;
 
-#[packed]
+#[repr(packed)]
 struct S4(u8,[u8, .. 3]);
 
-#[packed]
+#[repr(packed)]
 struct S5(u8,u32);
 
 pub fn main() {
diff --git a/src/test/run-pass/packed-tuple-struct-size.rs b/src/test/run-pass/packed-tuple-struct-size.rs
index da6e9a9dac0..f23166288fb 100644
--- a/src/test/run-pass/packed-tuple-struct-size.rs
+++ b/src/test/run-pass/packed-tuple-struct-size.rs
@@ -12,13 +12,13 @@
 use std::gc::Gc;
 use std::mem;
 
-#[packed]
+#[repr(packed)]
 struct S4(u8,[u8, .. 3]);
 
-#[packed]
+#[repr(packed)]
 struct S5(u8, u32);
 
-#[packed]
+#[repr(packed)]
 struct S13(i64, f32, u8);
 
 enum Foo {
@@ -26,10 +26,10 @@ enum Foo {
     Baz = 2
 }
 
-#[packed]
+#[repr(packed)]
 struct S3_Foo(u8, u16, Foo);
 
-#[packed]
+#[repr(packed)]
 struct S7_Option(f32, u8, u16, Option<Gc<f64>>);
 
 pub fn main() {