Skip to content

Commit

Permalink
Check representation of unnamed fields
Browse files Browse the repository at this point in the history
  • Loading branch information
frank-king committed Jan 6, 2024
1 parent 6ee6111 commit bf6482b
Show file tree
Hide file tree
Showing 9 changed files with 153 additions and 20 deletions.
6 changes: 6 additions & 0 deletions compiler/rustc_hir_analysis/messages.ftl
Expand Up @@ -422,6 +422,12 @@ hir_analysis_typeof_reserved_keyword_used =
hir_analysis_unconstrained_opaque_type = unconstrained opaque type
.note = `{$name}` must be used in combination with a concrete type within the same {$what}
hir_analysis_unnamed_fields_repr_missing_repr_c =
{$adt_kind} with unnamed fields must have `#[repr(C)]` representation
.label = {$adt_kind} defined here
hir_analysis_unnamed_fields_repr_defined = unnamed field defined here
hir_analysis_unrecognized_atomic_operation =
unrecognized atomic operation function: `{$op}`
.label = unrecognized atomic operation
Expand Down
35 changes: 35 additions & 0 deletions compiler/rustc_hir_analysis/src/check/check.rs
Expand Up @@ -79,6 +79,7 @@ fn check_struct(tcx: TyCtxt<'_>, def_id: LocalDefId) {

check_transparent(tcx, def);
check_packed(tcx, span, def);
check_unnamed_fields(tcx, def);
}

fn check_union(tcx: TyCtxt<'_>, def_id: LocalDefId) {
Expand All @@ -88,6 +89,40 @@ fn check_union(tcx: TyCtxt<'_>, def_id: LocalDefId) {
check_transparent(tcx, def);
check_union_fields(tcx, span, def_id);
check_packed(tcx, span, def);
check_unnamed_fields(tcx, def);
}

/// Check the representation of adts with unnamed fields.
fn check_unnamed_fields(tcx: TyCtxt<'_>, def: ty::AdtDef<'_>) {
let adt_kind = match def.adt_kind() {
ty::AdtKind::Struct => "struct",
ty::AdtKind::Union => "union",
_ => return,
};
let variant = def.non_enum_variant();
if variant.has_unnamed_fields() {
let span = tcx.def_span(def.did());
let unnamed_fields = variant
.fields
.iter()
.filter(|f| f.is_unnamed())
.map(|f| {
let span = tcx.def_span(f.did);
errors::UnnamedFieldsReprDefined { span }
})
.collect::<Vec<_>>();
debug_assert_ne!(unnamed_fields.len(), 0, "expect unnamed fields in this adt");
if !def.is_anonymous() {
let repr = def.repr();
if !repr.c() {
tcx.dcx().emit_err(errors::UnnamedFieldsReprMissingReprC {
span,
adt_kind,
unnamed_fields,
});
}
}
}
}

/// Check that the fields of the `union` do not need dropping.
Expand Down
6 changes: 5 additions & 1 deletion compiler/rustc_hir_analysis/src/collect.rs
Expand Up @@ -989,7 +989,11 @@ fn adt_def(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::AdtDef<'_> {
};

let is_anonymous = item.ident.name == kw::Empty;
let repr = tcx.repr_options_of_def(def_id.to_def_id());
let repr = if is_anonymous {
tcx.adt_def(tcx.local_parent(def_id)).repr()
} else {
tcx.repr_options_of_def(def_id.to_def_id())
};
let (kind, variants) = match &item.kind {
ItemKind::Enum(def, _) => {
let mut distance_from_explicit = 0;
Expand Down
18 changes: 18 additions & 0 deletions compiler/rustc_hir_analysis/src/errors.rs
Expand Up @@ -1447,3 +1447,21 @@ pub struct OnlyCurrentTraitsPointerSugg<'a> {
pub mut_key: &'a str,
pub ptr_ty: Ty<'a>,
}

#[derive(Diagnostic)]
#[diag(hir_analysis_unnamed_fields_repr_missing_repr_c)]
pub struct UnnamedFieldsReprMissingReprC {
#[primary_span]
#[label]
pub span: Span,
pub adt_kind: &'static str,
#[subdiagnostic]
pub unnamed_fields: Vec<UnnamedFieldsReprDefined>,
}

#[derive(Subdiagnostic)]
#[label(hir_analysis_unnamed_fields_repr_defined)]
pub struct UnnamedFieldsReprDefined {
#[primary_span]
pub span: Span,
}
31 changes: 18 additions & 13 deletions compiler/rustc_resolve/src/def_collector.rs
Expand Up @@ -80,6 +80,22 @@ impl<'a, 'b, 'tcx> DefCollector<'a, 'b, 'tcx> {
let name = field.ident.map_or_else(|| sym::integer(index(self)), |ident| ident.name);
let def = self.create_def(field.id, name, DefKind::Field, field.span);
self.with_parent(def, |this| visit::walk_field_def(this, field));
self.visit_anon_adt(&field.ty);
}
}

fn visit_anon_adt(&mut self, ty: &'a Ty) {
let def_kind = match &ty.kind {
TyKind::AnonStruct(..) => DefKind::Struct,
TyKind::AnonUnion(..) => DefKind::Union,
_ => return,
};
match &ty.kind {
TyKind::AnonStruct(node_id, _) | TyKind::AnonUnion(node_id, _) => {
let def_id = self.create_def(*node_id, kw::Empty, def_kind, ty.span);
self.with_parent(def_id, |this| visit::walk_ty(this, ty));
}
_ => {},
}
}

Expand Down Expand Up @@ -320,19 +336,8 @@ impl<'a, 'b, 'tcx> visit::Visitor<'a> for DefCollector<'a, 'b, 'tcx> {
fn visit_ty(&mut self, ty: &'a Ty) {
match &ty.kind {
TyKind::MacCall(..) => self.visit_macro_invoc(ty.id),
TyKind::AnonStruct(node_id, fields) | TyKind::AnonUnion(node_id, fields) => {
let def_kind = match &ty.kind {
TyKind::AnonStruct(..) => DefKind::Struct,
TyKind::AnonUnion(..) => DefKind::Union,
_ => unreachable!(),
};
let def_id = self.create_def(*node_id, kw::Empty, def_kind, ty.span);
self.with_parent(def_id, |this| {
for f in fields {
this.visit_field_def(f);
}
});
}
// Anonymous structs or unions are visited later after defined.
TyKind::AnonStruct(..) | TyKind::AnonUnion(..) => {}
_ => visit::walk_ty(self, ty),
}
}
Expand Down
Expand Up @@ -26,7 +26,7 @@ fn bar(_1: Bar) -> () {
StorageDead(_2);
StorageLive(_4);
StorageLive(_5);
_5 = ((_1.1: Bar::_::{anon_adt#0}).0: i8);
_5 = ((_1.1: Bar::{anon_adt#0}).0: i8);
_4 = access::<i8>(move _5) -> [return: bb2, unwind: bb5];
}

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

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

Expand Down
Expand Up @@ -24,7 +24,7 @@ fn foo(_1: Foo) -> () {
StorageDead(_2);
StorageLive(_4);
StorageLive(_5);
_5 = ((_1.1: Foo::_::{anon_adt#0}).0: i8);
_5 = ((_1.1: Foo::{anon_adt#0}).0: i8);
_4 = access::<i8>(move _5) -> [return: bb2, unwind: bb5];
}

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

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

Expand Down
27 changes: 27 additions & 0 deletions tests/ui/union/unnamed-fields/repr_check.rs
@@ -0,0 +1,27 @@
#![allow(incomplete_features)]
#![feature(unnamed_fields)]

struct A { //~ ERROR struct with unnamed fields must have `#[repr(C)]` representation
_: struct {
a: i32,
}
}

union B { //~ ERROR union with unnamed fields must have `#[repr(C)]` representation
_: struct {
b: i32,
}
}

#[derive(Clone, Copy)]
struct Foo {}

struct C { //~ ERROR struct with unnamed fields must have `#[repr(C)]` representation
_: Foo,
}

union D { //~ ERROR union with unnamed fields must have `#[repr(C)]` representation
_: Foo,
}

fn main() {}
38 changes: 38 additions & 0 deletions tests/ui/union/unnamed-fields/repr_check.stderr
@@ -0,0 +1,38 @@
error: struct with unnamed fields must have `#[repr(C)]` representation
--> $DIR/repr_check.rs:4:1
|
LL | struct A {
| ^^^^^^^^ struct defined here
LL | / _: struct {
LL | | a: i32,
LL | | }
| |_____- unnamed field defined here

error: union with unnamed fields must have `#[repr(C)]` representation
--> $DIR/repr_check.rs:10:1
|
LL | union B {
| ^^^^^^^ union defined here
LL | / _: struct {
LL | | b: i32,
LL | | }
| |_____- unnamed field defined here

error: struct with unnamed fields must have `#[repr(C)]` representation
--> $DIR/repr_check.rs:19:1
|
LL | struct C {
| ^^^^^^^^ struct defined here
LL | _: Foo,
| ------ unnamed field defined here

error: union with unnamed fields must have `#[repr(C)]` representation
--> $DIR/repr_check.rs:23:1
|
LL | union D {
| ^^^^^^^ union defined here
LL | _: Foo,
| ------ unnamed field defined here

error: aborting due to 4 previous errors

0 comments on commit bf6482b

Please sign in to comment.