Skip to content

Commit 78893e2

Browse files
committed
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. [breaking-change]
1 parent ac50ab5 commit 78893e2

23 files changed

+243
-68
lines changed

src/librustc/middle/lint.rs

+49-11
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ use std::u16;
6060
use std::u32;
6161
use std::u64;
6262
use std::u8;
63+
use std::cell::RefCell;
6364
use collections::SmallIntMap;
6465
use syntax::abi;
6566
use syntax::ast_map;
@@ -482,6 +483,9 @@ struct Context<'a> {
482483
/// Level of lints for certain NodeIds, stored here because the body of
483484
/// the lint needs to run in trans.
484485
node_levels: HashMap<(ast::NodeId, Lint), (Level, LintSource)>,
486+
487+
/// ids of structs which have had a representation note emitted with ctypes
488+
checked_ffi_structs: RefCell<NodeSet>,
485489
}
486490

487491
pub fn emit_lint(level: Level, src: LintSource, msg: &str, span: Span,
@@ -923,26 +927,55 @@ fn check_type_limits(cx: &Context, e: &ast::Expr) {
923927
}
924928

925929
fn check_item_ctypes(cx: &Context, it: &ast::Item) {
926-
fn check_ty(cx: &Context, ty: &ast::Ty) {
927-
match ty.node {
930+
fn check_ty(cx: &Context, aty: &ast::Ty) {
931+
match aty.node {
928932
ast::TyPath(_, _, id) => {
929933
match cx.tcx.def_map.borrow().get_copy(&id) {
930934
ast::DefPrimTy(ast::TyInt(ast::TyI)) => {
931-
cx.span_lint(CTypes, ty.span,
935+
cx.span_lint(CTypes, aty.span,
932936
"found rust type `int` in foreign module, while \
933937
libc::c_int or libc::c_long should be used");
934938
}
935939
ast::DefPrimTy(ast::TyUint(ast::TyU)) => {
936-
cx.span_lint(CTypes, ty.span,
940+
cx.span_lint(CTypes, aty.span,
937941
"found rust type `uint` in foreign module, while \
938942
libc::c_uint or libc::c_ulong should be used");
939943
}
940944
ast::DefTy(def_id) => {
941-
if !adt::is_ffi_safe(cx.tcx, def_id) {
942-
cx.span_lint(CTypes, ty.span,
943-
"found enum type without foreign-function-safe \
944-
representation annotation in foreign module");
945-
// hmm... this message could be more helpful
945+
match adt::is_ffi_safe(cx.tcx, def_id) {
946+
Ok(_) => { },
947+
Err(types) => {
948+
// in the enum case, we don't care about
949+
// "fields".
950+
951+
let ty = ty::get(ty::lookup_item_type(cx.tcx, def_id).ty);
952+
953+
match ty.sty {
954+
ty::ty_struct(_, _) => {
955+
cx.span_lint(CTypes, aty.span, "found struct without \
956+
FFI-safe representation used in FFI");
957+
958+
for def_id in types.iter() {
959+
if !cx.checked_ffi_structs.borrow_mut()
960+
.insert(def_id.node) {
961+
return;
962+
}
963+
964+
match cx.tcx.map.opt_span(def_id.node) {
965+
Some(sp) => cx.tcx.sess.span_note(sp, "consider \
966+
adding `#[repr(C)]` to this type"),
967+
None => { }
968+
}
969+
}
970+
},
971+
ty::ty_enum(_, _) => {
972+
cx.span_lint(CTypes, aty.span,
973+
"found enum without FFI-safe representation \
974+
annotation used in FFI");
975+
}
976+
_ => { }
977+
}
978+
}
946979
}
947980
}
948981
_ => ()
@@ -1100,6 +1133,7 @@ static obsolete_attrs: &'static [(&'static str, &'static str)] = &[
11001133
("fast_ffi", "Remove it"),
11011134
("fixed_stack_segment", "Remove it"),
11021135
("rust_stack", "Remove it"),
1136+
("packed", "Use `#[repr(packed)]` instead")
11031137
];
11041138

11051139
static other_attrs: &'static [&'static str] = &[
@@ -1109,7 +1143,7 @@ static other_attrs: &'static [&'static str] = &[
11091143
"allow", "deny", "forbid", "warn", // lint options
11101144
"deprecated", "experimental", "unstable", "stable", "locked", "frozen", //item stability
11111145
"cfg", "doc", "export_name", "link_section",
1112-
"no_mangle", "static_assert", "unsafe_no_drop_flag", "packed",
1146+
"no_mangle", "static_assert", "unsafe_no_drop_flag",
11131147
"simd", "repr", "deriving", "unsafe_destructor", "link", "phase",
11141148
"macro_export", "must_use", "automatically_derived",
11151149

@@ -1178,6 +1212,10 @@ fn check_unused_attribute(cx: &Context, attrs: &[ast::Attribute]) {
11781212
// FIXME: #14408 whitelist docs since rustdoc looks at them
11791213
"doc",
11801214

1215+
// Just because a struct isn't used for FFI in *this* crate, doesn't
1216+
// mean it won't ever be.
1217+
"repr",
1218+
11811219
// FIXME: #14406 these are processed in trans, which happens after the
11821220
// lint pass
11831221
"address_insignificant",
@@ -1189,7 +1227,6 @@ fn check_unused_attribute(cx: &Context, attrs: &[ast::Attribute]) {
11891227
"no_builtins",
11901228
"no_mangle",
11911229
"no_split_stack",
1192-
"packed",
11931230
"static_assert",
11941231
"thread_local",
11951232

@@ -1977,6 +2014,7 @@ pub fn check_crate(tcx: &ty::ctxt,
19772014
negated_expr_id: -1,
19782015
checked_raw_pointers: NodeSet::new(),
19792016
node_levels: HashMap::new(),
2017+
checked_ffi_structs: RefCell::new(NodeSet::new()),
19802018
};
19812019

19822020
// Install default lint levels, followed by the command line levels, and

src/librustc/middle/trans/adt.rs

+45-12
Original file line numberDiff line numberDiff line change
@@ -45,10 +45,11 @@
4545

4646
#![allow(unsigned_negate)]
4747

48-
use std::container::Map;
4948
use libc::c_ulonglong;
50-
use std::num::{Bitwise};
49+
use std::container::Map;
50+
use std::num::Bitwise;
5151
use std::rc::Rc;
52+
use std;
5253

5354
use lib::llvm::{ValueRef, True, IntEQ, IntNE};
5455
use middle::trans::_match;
@@ -165,7 +166,7 @@ fn represent_type_uncached(cx: &CrateContext, t: ty::t) -> Repr {
165166
}
166167
ty::ty_enum(def_id, ref substs) => {
167168
let cases = get_cases(cx.tcx(), def_id, substs);
168-
let hint = ty::lookup_repr_hint(cx.tcx(), def_id);
169+
let hint = ty::lookup_enum_repr_hint(cx.tcx(), def_id);
169170

170171
if cases.len() == 0 {
171172
// Uninhabitable; represent as unit
@@ -254,31 +255,60 @@ fn represent_type_uncached(cx: &CrateContext, t: ty::t) -> Repr {
254255

255256
/// Determine, without doing translation, whether an ADT must be FFI-safe.
256257
/// For use in lint or similar, where being sound but slightly incomplete is acceptable.
257-
pub fn is_ffi_safe(tcx: &ty::ctxt, def_id: ast::DefId) -> bool {
258+
/// Returns Ok(()) if it is, and Err(causes) which is a vector of the DefId's
259+
/// of the types that are unsafe (either the type being checked itself if it
260+
/// lacks a repr attribute, or the fields of a struct).
261+
pub fn is_ffi_safe(tcx: &ty::ctxt, def_id: ast::DefId) -> std::result::Result<(), Vec<ast::DefId>> {
258262
match ty::get(ty::lookup_item_type(tcx, def_id).ty).sty {
259263
ty::ty_enum(def_id, _) => {
260264
let variants = ty::enum_variants(tcx, def_id);
261265
// Univariant => like struct/tuple.
262266
if variants.len() <= 1 {
263-
return true;
267+
return Ok(());
264268
}
265-
let hint = ty::lookup_repr_hint(tcx, def_id);
269+
let hint = ty::lookup_enum_repr_hint(tcx, def_id);
266270
// Appropriate representation explicitly selected?
267271
if hint.is_ffi_safe() {
268-
return true;
272+
return Ok(());
269273
}
270274
// Option<Box<T>> and similar are used in FFI. Rather than try to
271275
// resolve type parameters and recognize this case exactly, this
272276
// overapproximates -- assuming that if a non-C-like enum is being
273277
// used in FFI then the user knows what they're doing.
274278
if variants.iter().any(|vi| !vi.args.is_empty()) {
275-
return true;
279+
return Ok(());
276280
}
277-
false
278-
}
279-
// struct, tuple, etc.
281+
Err(vec![def_id])
282+
},
283+
ty::ty_struct(def_id, _) => {
284+
let struct_is_safe =
285+
ty::lookup_struct_repr_hint(tcx, def_id).contains(&attr::ReprExtern);
286+
let mut fields_are_safe = true;
287+
let mut bad_fields = Vec::new();
288+
289+
if !struct_is_safe {
290+
bad_fields.push(def_id);
291+
}
292+
293+
for field in ty::lookup_struct_fields(tcx, def_id).iter() {
294+
match is_ffi_safe(tcx, field.id) {
295+
Ok(_) => { }
296+
Err(types) => {
297+
fields_are_safe = false;
298+
bad_fields.extend(types.move_iter())
299+
}
300+
}
301+
}
302+
303+
if struct_is_safe && fields_are_safe {
304+
Ok(())
305+
} else {
306+
Err(bad_fields)
307+
}
308+
},
309+
// tuple, etc.
280310
// (is this right in the present of typedefs?)
281-
_ => true
311+
_ => Ok(())
282312
}
283313
}
284314

@@ -370,6 +400,9 @@ fn range_to_inttype(cx: &CrateContext, hint: Hint, bounds: &IntBounds) -> IntTyp
370400
}
371401
attr::ReprAny => {
372402
attempts = choose_shortest;
403+
},
404+
attr::ReprPacked => {
405+
cx.tcx.sess.bug("range_to_inttype: found ReprPacked on an enum");
373406
}
374407
}
375408
for &ity in attempts.iter() {

src/librustc/middle/ty.rs

+17-5
Original file line numberDiff line numberDiff line change
@@ -3928,26 +3928,38 @@ pub fn has_attr(tcx: &ctxt, did: DefId, attr: &str) -> bool {
39283928
found
39293929
}
39303930

3931-
/// Determine whether an item is annotated with `#[packed]`
3931+
/// Determine whether an item is annotated with `#[repr(packed)]`
39323932
pub fn lookup_packed(tcx: &ctxt, did: DefId) -> bool {
3933-
has_attr(tcx, did, "packed")
3933+
lookup_struct_repr_hint(tcx, did).contains(&attr::ReprPacked)
39343934
}
39353935

39363936
/// Determine whether an item is annotated with `#[simd]`
39373937
pub fn lookup_simd(tcx: &ctxt, did: DefId) -> bool {
39383938
has_attr(tcx, did, "simd")
39393939
}
39403940

3941-
// Obtain the representation annotation for a definition.
3942-
pub fn lookup_repr_hint(tcx: &ctxt, did: DefId) -> attr::ReprAttr {
3941+
/// Obtain the representation annotation for an enum definition.
3942+
pub fn lookup_enum_repr_hint(tcx: &ctxt, did: DefId) -> attr::ReprAttr {
39433943
let mut acc = attr::ReprAny;
39443944
ty::each_attr(tcx, did, |meta| {
3945-
acc = attr::find_repr_attr(tcx.sess.diagnostic(), meta, acc);
3945+
acc = attr::find_enum_repr_attr(tcx.sess.diagnostic(), meta, acc);
39463946
true
39473947
});
39483948
return acc;
39493949
}
39503950

3951+
/// Obtain the representation annotation for a struct definition.
3952+
pub fn lookup_struct_repr_hint(tcx: &ctxt, did: DefId) -> Vec<attr::ReprAttr> {
3953+
let mut acc = Vec::new();
3954+
3955+
ty::each_attr(tcx, did, |meta| {
3956+
acc.extend(attr::find_struct_repr_attrs(tcx.sess.diagnostic(), meta).move_iter());
3957+
true
3958+
});
3959+
3960+
acc
3961+
}
3962+
39513963
// Look up a field ID, whether or not it's local
39523964
// Takes a list of type substs in case the struct is generic
39533965
pub fn lookup_field_type(tcx: &ctxt,

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

+4-1
Original file line numberDiff line numberDiff line change
@@ -3803,6 +3803,9 @@ pub fn check_enum_variants(ccx: &CrateCtxt,
38033803
ccx.tcx.sess.span_note(sp, "discriminant type specified here");
38043804
}
38053805
}
3806+
attr::ReprPacked => {
3807+
ccx.tcx.sess.bug("range_to_inttype: found ReprPacked on an enum");
3808+
}
38063809
}
38073810
disr_vals.push(current_disr_val);
38083811

@@ -3816,7 +3819,7 @@ pub fn check_enum_variants(ccx: &CrateCtxt,
38163819
return variants;
38173820
}
38183821

3819-
let hint = ty::lookup_repr_hint(ccx.tcx, ast::DefId { krate: ast::LOCAL_CRATE, node: id });
3822+
let hint = ty::lookup_enum_repr_hint(ccx.tcx, ast::DefId { krate: ast::LOCAL_CRATE, node: id });
38203823
if hint != attr::ReprAny && vs.len() <= 1 {
38213824
let msg = if vs.len() == 1 {
38223825
"unsupported representation for univariant enum"

src/libstd/rt/backtrace.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -544,7 +544,7 @@ mod imp {
544544
static IMAGE_FILE_MACHINE_IA64: libc::DWORD = 0x0200;
545545
static IMAGE_FILE_MACHINE_AMD64: libc::DWORD = 0x8664;
546546

547-
#[packed]
547+
#[repr(packed)]
548548
struct SYMBOL_INFO {
549549
SizeOfStruct: libc::c_ulong,
550550
TypeIndex: libc::c_ulong,

src/libsyntax/attr.rs

+45-5
Original file line numberDiff line numberDiff line change
@@ -464,7 +464,7 @@ pub fn require_unique_names(diagnostic: &SpanHandler, metas: &[@MetaItem]) {
464464
* present (before fields, if any) with that type; reprensentation
465465
* optimizations which would remove it will not be done.
466466
*/
467-
pub fn find_repr_attr(diagnostic: &SpanHandler, attr: &Attribute, acc: ReprAttr)
467+
pub fn find_enum_repr_attr(diagnostic: &SpanHandler, attr: &Attribute, acc: ReprAttr)
468468
-> ReprAttr {
469469
let mut acc = acc;
470470
match attr.node.value.node {
@@ -496,7 +496,7 @@ pub fn find_repr_attr(diagnostic: &SpanHandler, attr: &Attribute, acc: ReprAttr)
496496
}
497497
}
498498
// Not a word:
499-
_ => diagnostic.span_err(item.span, "unrecognized representation hint")
499+
_ => diagnostic.span_err(item.span, "unrecognized enum representation hint")
500500
}
501501
}
502502
}
@@ -506,6 +506,44 @@ pub fn find_repr_attr(diagnostic: &SpanHandler, attr: &Attribute, acc: ReprAttr)
506506
acc
507507
}
508508

509+
/// A struct `repr` attribute can only take the values `C` and `packed`, or
510+
/// possibly both.
511+
pub fn find_struct_repr_attrs(diagnostic: &SpanHandler, attr: &Attribute) -> Vec<ReprAttr> {
512+
let mut attrs = Vec::new();
513+
match attr.node.value.node {
514+
ast::MetaList(ref s, ref items) if s.equiv(&("repr")) => {
515+
mark_used(attr);
516+
for item in items.iter() {
517+
match item.node {
518+
ast::MetaWord(ref word) => {
519+
let hint = match word.get() {
520+
"C" => Some(ReprExtern),
521+
"packed" => Some(ReprPacked),
522+
_ => {
523+
// Not a word we recognize
524+
diagnostic.span_err(item.span,
525+
"unrecognized struct representation hint");
526+
None
527+
}
528+
};
529+
530+
match hint {
531+
Some(h) => attrs.push(h),
532+
None => { }
533+
}
534+
}
535+
// Not a word:
536+
_ => diagnostic.span_err(item.span, "unrecognized representation hint")
537+
}
538+
}
539+
}
540+
// Not a "repr" hint: ignore.
541+
_ => { }
542+
}
543+
544+
attrs
545+
}
546+
509547
fn int_type_of_word(s: &str) -> Option<IntType> {
510548
match s {
511549
"i8" => Some(SignedInt(ast::TyI8)),
@@ -526,15 +564,17 @@ fn int_type_of_word(s: &str) -> Option<IntType> {
526564
pub enum ReprAttr {
527565
ReprAny,
528566
ReprInt(Span, IntType),
529-
ReprExtern
567+
ReprExtern,
568+
ReprPacked,
530569
}
531570

532571
impl ReprAttr {
533572
pub fn is_ffi_safe(&self) -> bool {
534573
match *self {
535574
ReprAny => false,
536575
ReprInt(_sp, ity) => ity.is_ffi_safe(),
537-
ReprExtern => true
576+
ReprExtern => true,
577+
ReprPacked => false
538578
}
539579
}
540580
}
@@ -559,7 +599,7 @@ impl IntType {
559599
SignedInt(ast::TyI16) | UnsignedInt(ast::TyU16) |
560600
SignedInt(ast::TyI32) | UnsignedInt(ast::TyU32) |
561601
SignedInt(ast::TyI64) | UnsignedInt(ast::TyU64) => true,
562-
_ => false
602+
SignedInt(ast::TyI) | UnsignedInt(ast::TyU) => false
563603
}
564604
}
565605
}

0 commit comments

Comments
 (0)