Skip to content

Commit bf6482b

Browse files
committed
Check representation of unnamed fields
1 parent 6ee6111 commit bf6482b

File tree

9 files changed

+153
-20
lines changed

9 files changed

+153
-20
lines changed

compiler/rustc_hir_analysis/messages.ftl

+6
Original file line numberDiff line numberDiff line change
@@ -422,6 +422,12 @@ hir_analysis_typeof_reserved_keyword_used =
422422
hir_analysis_unconstrained_opaque_type = unconstrained opaque type
423423
.note = `{$name}` must be used in combination with a concrete type within the same {$what}
424424
425+
hir_analysis_unnamed_fields_repr_missing_repr_c =
426+
{$adt_kind} with unnamed fields must have `#[repr(C)]` representation
427+
.label = {$adt_kind} defined here
428+
429+
hir_analysis_unnamed_fields_repr_defined = unnamed field defined here
430+
425431
hir_analysis_unrecognized_atomic_operation =
426432
unrecognized atomic operation function: `{$op}`
427433
.label = unrecognized atomic operation

compiler/rustc_hir_analysis/src/check/check.rs

+35
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ fn check_struct(tcx: TyCtxt<'_>, def_id: LocalDefId) {
7979

8080
check_transparent(tcx, def);
8181
check_packed(tcx, span, def);
82+
check_unnamed_fields(tcx, def);
8283
}
8384

8485
fn check_union(tcx: TyCtxt<'_>, def_id: LocalDefId) {
@@ -88,6 +89,40 @@ fn check_union(tcx: TyCtxt<'_>, def_id: LocalDefId) {
8889
check_transparent(tcx, def);
8990
check_union_fields(tcx, span, def_id);
9091
check_packed(tcx, span, def);
92+
check_unnamed_fields(tcx, def);
93+
}
94+
95+
/// Check the representation of adts with unnamed fields.
96+
fn check_unnamed_fields(tcx: TyCtxt<'_>, def: ty::AdtDef<'_>) {
97+
let adt_kind = match def.adt_kind() {
98+
ty::AdtKind::Struct => "struct",
99+
ty::AdtKind::Union => "union",
100+
_ => return,
101+
};
102+
let variant = def.non_enum_variant();
103+
if variant.has_unnamed_fields() {
104+
let span = tcx.def_span(def.did());
105+
let unnamed_fields = variant
106+
.fields
107+
.iter()
108+
.filter(|f| f.is_unnamed())
109+
.map(|f| {
110+
let span = tcx.def_span(f.did);
111+
errors::UnnamedFieldsReprDefined { span }
112+
})
113+
.collect::<Vec<_>>();
114+
debug_assert_ne!(unnamed_fields.len(), 0, "expect unnamed fields in this adt");
115+
if !def.is_anonymous() {
116+
let repr = def.repr();
117+
if !repr.c() {
118+
tcx.dcx().emit_err(errors::UnnamedFieldsReprMissingReprC {
119+
span,
120+
adt_kind,
121+
unnamed_fields,
122+
});
123+
}
124+
}
125+
}
91126
}
92127

93128
/// Check that the fields of the `union` do not need dropping.

compiler/rustc_hir_analysis/src/collect.rs

+5-1
Original file line numberDiff line numberDiff line change
@@ -989,7 +989,11 @@ fn adt_def(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::AdtDef<'_> {
989989
};
990990

991991
let is_anonymous = item.ident.name == kw::Empty;
992-
let repr = tcx.repr_options_of_def(def_id.to_def_id());
992+
let repr = if is_anonymous {
993+
tcx.adt_def(tcx.local_parent(def_id)).repr()
994+
} else {
995+
tcx.repr_options_of_def(def_id.to_def_id())
996+
};
993997
let (kind, variants) = match &item.kind {
994998
ItemKind::Enum(def, _) => {
995999
let mut distance_from_explicit = 0;

compiler/rustc_hir_analysis/src/errors.rs

+18
Original file line numberDiff line numberDiff line change
@@ -1447,3 +1447,21 @@ pub struct OnlyCurrentTraitsPointerSugg<'a> {
14471447
pub mut_key: &'a str,
14481448
pub ptr_ty: Ty<'a>,
14491449
}
1450+
1451+
#[derive(Diagnostic)]
1452+
#[diag(hir_analysis_unnamed_fields_repr_missing_repr_c)]
1453+
pub struct UnnamedFieldsReprMissingReprC {
1454+
#[primary_span]
1455+
#[label]
1456+
pub span: Span,
1457+
pub adt_kind: &'static str,
1458+
#[subdiagnostic]
1459+
pub unnamed_fields: Vec<UnnamedFieldsReprDefined>,
1460+
}
1461+
1462+
#[derive(Subdiagnostic)]
1463+
#[label(hir_analysis_unnamed_fields_repr_defined)]
1464+
pub struct UnnamedFieldsReprDefined {
1465+
#[primary_span]
1466+
pub span: Span,
1467+
}

compiler/rustc_resolve/src/def_collector.rs

+18-13
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,22 @@ impl<'a, 'b, 'tcx> DefCollector<'a, 'b, 'tcx> {
8080
let name = field.ident.map_or_else(|| sym::integer(index(self)), |ident| ident.name);
8181
let def = self.create_def(field.id, name, DefKind::Field, field.span);
8282
self.with_parent(def, |this| visit::walk_field_def(this, field));
83+
self.visit_anon_adt(&field.ty);
84+
}
85+
}
86+
87+
fn visit_anon_adt(&mut self, ty: &'a Ty) {
88+
let def_kind = match &ty.kind {
89+
TyKind::AnonStruct(..) => DefKind::Struct,
90+
TyKind::AnonUnion(..) => DefKind::Union,
91+
_ => return,
92+
};
93+
match &ty.kind {
94+
TyKind::AnonStruct(node_id, _) | TyKind::AnonUnion(node_id, _) => {
95+
let def_id = self.create_def(*node_id, kw::Empty, def_kind, ty.span);
96+
self.with_parent(def_id, |this| visit::walk_ty(this, ty));
97+
}
98+
_ => {},
8399
}
84100
}
85101

@@ -320,19 +336,8 @@ impl<'a, 'b, 'tcx> visit::Visitor<'a> for DefCollector<'a, 'b, 'tcx> {
320336
fn visit_ty(&mut self, ty: &'a Ty) {
321337
match &ty.kind {
322338
TyKind::MacCall(..) => self.visit_macro_invoc(ty.id),
323-
TyKind::AnonStruct(node_id, fields) | TyKind::AnonUnion(node_id, fields) => {
324-
let def_kind = match &ty.kind {
325-
TyKind::AnonStruct(..) => DefKind::Struct,
326-
TyKind::AnonUnion(..) => DefKind::Union,
327-
_ => unreachable!(),
328-
};
329-
let def_id = self.create_def(*node_id, kw::Empty, def_kind, ty.span);
330-
self.with_parent(def_id, |this| {
331-
for f in fields {
332-
this.visit_field_def(f);
333-
}
334-
});
335-
}
339+
// Anonymous structs or unions are visited later after defined.
340+
TyKind::AnonStruct(..) | TyKind::AnonUnion(..) => {}
336341
_ => visit::walk_ty(self, ty),
337342
}
338343
}

tests/mir-opt/unnamed-fields/field_access.bar.SimplifyCfg-initial.after.mir

+3-3
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ fn bar(_1: Bar) -> () {
2626
StorageDead(_2);
2727
StorageLive(_4);
2828
StorageLive(_5);
29-
_5 = ((_1.1: Bar::_::{anon_adt#0}).0: i8);
29+
_5 = ((_1.1: Bar::{anon_adt#0}).0: i8);
3030
_4 = access::<i8>(move _5) -> [return: bb2, unwind: bb5];
3131
}
3232

@@ -35,7 +35,7 @@ fn bar(_1: Bar) -> () {
3535
StorageDead(_4);
3636
StorageLive(_6);
3737
StorageLive(_7);
38-
_7 = ((_1.1: Bar::_::{anon_adt#0}).1: bool);
38+
_7 = ((_1.1: Bar::{anon_adt#0}).1: bool);
3939
_6 = access::<bool>(move _7) -> [return: bb3, unwind: bb5];
4040
}
4141

@@ -44,7 +44,7 @@ fn bar(_1: Bar) -> () {
4444
StorageDead(_6);
4545
StorageLive(_8);
4646
StorageLive(_9);
47-
_9 = (((_1.2: Bar::_::{anon_adt#0}).0: Bar::_::{anon_adt#0}::_::{anon_adt#0}).0: [u8; 1]);
47+
_9 = (((_1.2: Bar::{anon_adt#1}).0: Bar::{anon_adt#1}::{anon_adt#0}).0: [u8; 1]);
4848
_8 = access::<[u8; 1]>(move _9) -> [return: bb4, unwind: bb5];
4949
}
5050

tests/mir-opt/unnamed-fields/field_access.foo.SimplifyCfg-initial.after.mir

+3-3
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ fn foo(_1: Foo) -> () {
2424
StorageDead(_2);
2525
StorageLive(_4);
2626
StorageLive(_5);
27-
_5 = ((_1.1: Foo::_::{anon_adt#0}).0: i8);
27+
_5 = ((_1.1: Foo::{anon_adt#0}).0: i8);
2828
_4 = access::<i8>(move _5) -> [return: bb2, unwind: bb5];
2929
}
3030

@@ -33,7 +33,7 @@ fn foo(_1: Foo) -> () {
3333
StorageDead(_4);
3434
StorageLive(_6);
3535
StorageLive(_7);
36-
_7 = ((_1.1: Foo::_::{anon_adt#0}).1: bool);
36+
_7 = ((_1.1: Foo::{anon_adt#0}).1: bool);
3737
_6 = access::<bool>(move _7) -> [return: bb3, unwind: bb5];
3838
}
3939

@@ -42,7 +42,7 @@ fn foo(_1: Foo) -> () {
4242
StorageDead(_6);
4343
StorageLive(_8);
4444
StorageLive(_9);
45-
_9 = (((_1.2: Foo::_::{anon_adt#0}).0: Foo::_::{anon_adt#0}::_::{anon_adt#0}).0: [u8; 1]);
45+
_9 = (((_1.2: Foo::{anon_adt#1}).0: Foo::{anon_adt#1}::{anon_adt#0}).0: [u8; 1]);
4646
_8 = access::<[u8; 1]>(move _9) -> [return: bb4, unwind: bb5];
4747
}
4848

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
#![allow(incomplete_features)]
2+
#![feature(unnamed_fields)]
3+
4+
struct A { //~ ERROR struct with unnamed fields must have `#[repr(C)]` representation
5+
_: struct {
6+
a: i32,
7+
}
8+
}
9+
10+
union B { //~ ERROR union with unnamed fields must have `#[repr(C)]` representation
11+
_: struct {
12+
b: i32,
13+
}
14+
}
15+
16+
#[derive(Clone, Copy)]
17+
struct Foo {}
18+
19+
struct C { //~ ERROR struct with unnamed fields must have `#[repr(C)]` representation
20+
_: Foo,
21+
}
22+
23+
union D { //~ ERROR union with unnamed fields must have `#[repr(C)]` representation
24+
_: Foo,
25+
}
26+
27+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
error: struct with unnamed fields must have `#[repr(C)]` representation
2+
--> $DIR/repr_check.rs:4:1
3+
|
4+
LL | struct A {
5+
| ^^^^^^^^ struct defined here
6+
LL | / _: struct {
7+
LL | | a: i32,
8+
LL | | }
9+
| |_____- unnamed field defined here
10+
11+
error: union with unnamed fields must have `#[repr(C)]` representation
12+
--> $DIR/repr_check.rs:10:1
13+
|
14+
LL | union B {
15+
| ^^^^^^^ union defined here
16+
LL | / _: struct {
17+
LL | | b: i32,
18+
LL | | }
19+
| |_____- unnamed field defined here
20+
21+
error: struct with unnamed fields must have `#[repr(C)]` representation
22+
--> $DIR/repr_check.rs:19:1
23+
|
24+
LL | struct C {
25+
| ^^^^^^^^ struct defined here
26+
LL | _: Foo,
27+
| ------ unnamed field defined here
28+
29+
error: union with unnamed fields must have `#[repr(C)]` representation
30+
--> $DIR/repr_check.rs:23:1
31+
|
32+
LL | union D {
33+
| ^^^^^^^ union defined here
34+
LL | _: Foo,
35+
| ------ unnamed field defined here
36+
37+
error: aborting due to 4 previous errors
38+

0 commit comments

Comments
 (0)