Skip to content

Commit bdc1592

Browse files
committed
Auto merge of rust-lang#115367 - frank-king:feature/unnamed-fields-hir, r=davidtwco
Lowering unnamed fields and anonymous adt This implements rust-lang#49804. Goals: - [x] lowering anonymous ADTs from AST to HIR - [x] generating definitions of anonymous ADTs - [x] uniqueness check of the unnamed fields - [x] field projection of anonymous ADTs - [x] `#[repr(C)]` check of the anonymous ADTs Non-Goals (will be in the next PRs) - capturing generic params for the anonymous ADTs from the parent ADT - pattern matching of anonymous ADTs - structural expressions of anonymous ADTs - rustdoc support of anonymous ADTs
2 parents ed19532 + 0dbd6e9 commit bdc1592

File tree

60 files changed

+3273
-259
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

60 files changed

+3273
-259
lines changed

compiler/rustc_ast/src/ast.rs

+6-2
Original file line numberDiff line numberDiff line change
@@ -2107,9 +2107,9 @@ pub enum TyKind {
21072107
/// A tuple (`(A, B, C, D,...)`).
21082108
Tup(ThinVec<P<Ty>>),
21092109
/// An anonymous struct type i.e. `struct { foo: Type }`
2110-
AnonStruct(ThinVec<FieldDef>),
2110+
AnonStruct(NodeId, ThinVec<FieldDef>),
21112111
/// An anonymous union type i.e. `union { bar: Type }`
2112-
AnonUnion(ThinVec<FieldDef>),
2112+
AnonUnion(NodeId, ThinVec<FieldDef>),
21132113
/// A path (`module::module::...::Type`), optionally
21142114
/// "qualified", e.g., `<Vec<T> as SomeTrait>::SomeType`.
21152115
///
@@ -2161,6 +2161,10 @@ impl TyKind {
21612161
None
21622162
}
21632163
}
2164+
2165+
pub fn is_anon_adt(&self) -> bool {
2166+
matches!(self, TyKind::AnonStruct(..) | TyKind::AnonUnion(..))
2167+
}
21642168
}
21652169

21662170
/// Syntax used to declare a trait object.

compiler/rustc_ast/src/mut_visit.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -514,7 +514,8 @@ pub fn noop_visit_ty<T: MutVisitor>(ty: &mut P<Ty>, vis: &mut T) {
514514
visit_vec(bounds, |bound| vis.visit_param_bound(bound));
515515
}
516516
TyKind::MacCall(mac) => vis.visit_mac_call(mac),
517-
TyKind::AnonStruct(fields) | TyKind::AnonUnion(fields) => {
517+
TyKind::AnonStruct(id, fields) | TyKind::AnonUnion(id, fields) => {
518+
vis.visit_id(id);
518519
fields.flat_map_in_place(|field| vis.flat_map_field_def(field));
519520
}
520521
}

compiler/rustc_ast/src/visit.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -450,7 +450,7 @@ pub fn walk_ty<'a, V: Visitor<'a>>(visitor: &mut V, typ: &'a Ty) {
450450
TyKind::Infer | TyKind::ImplicitSelf | TyKind::Err => {}
451451
TyKind::MacCall(mac) => visitor.visit_mac_call(mac),
452452
TyKind::Never | TyKind::CVarArgs => {}
453-
TyKind::AnonStruct(ref fields, ..) | TyKind::AnonUnion(ref fields, ..) => {
453+
TyKind::AnonStruct(_, ref fields) | TyKind::AnonUnion(_, ref fields) => {
454454
walk_list!(visitor, visit_field_def, fields)
455455
}
456456
}

compiler/rustc_ast_lowering/src/item.rs

+4-1
Original file line numberDiff line numberDiff line change
@@ -720,7 +720,10 @@ impl<'hir> LoweringContext<'_, 'hir> {
720720
}
721721
}
722722

723-
fn lower_field_def(&mut self, (index, f): (usize, &FieldDef)) -> hir::FieldDef<'hir> {
723+
pub(super) fn lower_field_def(
724+
&mut self,
725+
(index, f): (usize, &FieldDef),
726+
) -> hir::FieldDef<'hir> {
724727
let ty = if let TyKind::Path(qself, path) = &f.ty.kind {
725728
let t = self.lower_path_ty(
726729
&f.ty,

compiler/rustc_ast_lowering/src/lib.rs

+38-11
Original file line numberDiff line numberDiff line change
@@ -1288,17 +1288,44 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
12881288
TyKind::Err => {
12891289
hir::TyKind::Err(self.dcx().span_delayed_bug(t.span, "TyKind::Err lowered"))
12901290
}
1291-
// FIXME(unnamed_fields): IMPLEMENTATION IN PROGRESS
1292-
#[allow(rustc::untranslatable_diagnostic)]
1293-
#[allow(rustc::diagnostic_outside_of_impl)]
1294-
TyKind::AnonStruct(ref _fields) => {
1295-
hir::TyKind::Err(self.dcx().span_err(t.span, "anonymous structs are unimplemented"))
1296-
}
1297-
// FIXME(unnamed_fields): IMPLEMENTATION IN PROGRESS
1298-
#[allow(rustc::untranslatable_diagnostic)]
1299-
#[allow(rustc::diagnostic_outside_of_impl)]
1300-
TyKind::AnonUnion(ref _fields) => {
1301-
hir::TyKind::Err(self.dcx().span_err(t.span, "anonymous unions are unimplemented"))
1291+
// Lower the anonymous structs or unions in a nested lowering context.
1292+
//
1293+
// ```
1294+
// struct Foo {
1295+
// _: union {
1296+
// // ^__________________ <-- within the nested lowering context,
1297+
// /* fields */ // | we lower all fields defined into an
1298+
// } // | owner node of struct or union item
1299+
// // ^_____________________|
1300+
// }
1301+
// ```
1302+
TyKind::AnonStruct(node_id, fields) | TyKind::AnonUnion(node_id, fields) => {
1303+
// Here its `def_id` is created in `build_reduced_graph`.
1304+
let def_id = self.local_def_id(*node_id);
1305+
debug!(?def_id);
1306+
let owner_id = hir::OwnerId { def_id };
1307+
self.with_hir_id_owner(*node_id, |this| {
1308+
let fields = this.arena.alloc_from_iter(
1309+
fields.iter().enumerate().map(|f| this.lower_field_def(f)),
1310+
);
1311+
let span = t.span;
1312+
let variant_data = hir::VariantData::Struct { fields, recovered: false };
1313+
// FIXME: capture the generics from the outer adt.
1314+
let generics = hir::Generics::empty();
1315+
let kind = match t.kind {
1316+
TyKind::AnonStruct(..) => hir::ItemKind::Struct(variant_data, generics),
1317+
TyKind::AnonUnion(..) => hir::ItemKind::Union(variant_data, generics),
1318+
_ => unreachable!(),
1319+
};
1320+
hir::OwnerNode::Item(this.arena.alloc(hir::Item {
1321+
ident: Ident::new(kw::Empty, span),
1322+
owner_id,
1323+
kind,
1324+
span: this.lower_span(span),
1325+
vis_span: this.lower_span(span.shrink_to_lo()),
1326+
}))
1327+
});
1328+
hir::TyKind::AnonAdt(hir::ItemId { owner_id })
13021329
}
13031330
TyKind::Slice(ty) => hir::TyKind::Slice(self.lower_ty(ty, itctx)),
13041331
TyKind::Ptr(mt) => hir::TyKind::Ptr(self.lower_mt(mt, itctx)),

compiler/rustc_ast_passes/src/ast_validation.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -219,8 +219,8 @@ impl<'a> AstValidator<'a> {
219219
}
220220
}
221221
}
222-
TyKind::AnonStruct(ref fields, ..) | TyKind::AnonUnion(ref fields, ..) => {
223-
walk_list!(self, visit_field_def, fields)
222+
TyKind::AnonStruct(_, ref fields) | TyKind::AnonUnion(_, ref fields) => {
223+
walk_list!(self, visit_struct_field_def, fields)
224224
}
225225
_ => visit::walk_ty(self, t),
226226
}

compiler/rustc_ast_pretty/src/pprust/state.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -1003,11 +1003,11 @@ impl<'a> State<'a> {
10031003
}
10041004
self.pclose();
10051005
}
1006-
ast::TyKind::AnonStruct(fields) => {
1006+
ast::TyKind::AnonStruct(_, fields) => {
10071007
self.head("struct");
10081008
self.print_record_struct_body(fields, ty.span);
10091009
}
1010-
ast::TyKind::AnonUnion(fields) => {
1010+
ast::TyKind::AnonUnion(_, fields) => {
10111011
self.head("union");
10121012
self.print_record_struct_body(fields, ty.span);
10131013
}

compiler/rustc_builtin_macros/src/deriving/clone.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,9 @@ fn cs_clone_simple(
110110
&& !seen_type_names.insert(name)
111111
{
112112
// Already produced an assertion for this type.
113-
} else {
113+
// Anonymous structs or unions must be eliminated as they cannot be
114+
// type parameters.
115+
} else if !field.ty.kind.is_anon_adt() {
114116
// let _: AssertParamIsClone<FieldTy>;
115117
super::assert_ty_bounds(
116118
cx,

compiler/rustc_builtin_macros/src/deriving/mod.rs

+2
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,8 @@ fn assert_ty_bounds(
123123
span: Span,
124124
assert_path: &[Symbol],
125125
) {
126+
// Deny anonymous structs or unions to avoid wierd errors.
127+
assert!(!ty.kind.is_anon_adt(), "Anonymous structs or unions cannot be type parameters");
126128
// Generate statement `let _: assert_path<ty>;`.
127129
let span = cx.with_def_site_ctxt(span);
128130
let assert_path = cx.path_all(span, true, cx.std_path(assert_path), vec![GenericArg::Type(ty)]);

compiler/rustc_hir/src/def.rs

+2
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ use rustc_data_structures::unord::UnordMap;
88
use rustc_macros::HashStable_Generic;
99
use rustc_span::def_id::{DefId, LocalDefId};
1010
use rustc_span::hygiene::MacroKind;
11+
use rustc_span::symbol::kw;
1112
use rustc_span::Symbol;
1213

1314
use std::array::IntoIter;
@@ -225,6 +226,7 @@ impl DefKind {
225226

226227
pub fn def_path_data(self, name: Symbol) -> DefPathData {
227228
match self {
229+
DefKind::Struct | DefKind::Union if name == kw::Empty => DefPathData::AnonAdt,
228230
DefKind::Mod
229231
| DefKind::Struct
230232
| DefKind::Union

compiler/rustc_hir/src/definitions.rs

+5-1
Original file line numberDiff line numberDiff line change
@@ -287,6 +287,8 @@ pub enum DefPathData {
287287
/// An existential `impl Trait` type node.
288288
/// Argument position `impl Trait` have a `TypeNs` with their pretty-printed name.
289289
OpaqueTy,
290+
/// An anonymous struct or union type i.e. `struct { foo: Type }` or `union { bar: Type }`
291+
AnonAdt,
290292
}
291293

292294
impl Definitions {
@@ -409,8 +411,9 @@ impl DefPathData {
409411
match *self {
410412
TypeNs(name) if name == kw::Empty => None,
411413
TypeNs(name) | ValueNs(name) | MacroNs(name) | LifetimeNs(name) => Some(name),
414+
412415
Impl | ForeignMod | CrateRoot | Use | GlobalAsm | Closure | Ctor | AnonConst
413-
| OpaqueTy => None,
416+
| OpaqueTy | AnonAdt => None,
414417
}
415418
}
416419

@@ -431,6 +434,7 @@ impl DefPathData {
431434
Ctor => DefPathDataName::Anon { namespace: sym::constructor },
432435
AnonConst => DefPathDataName::Anon { namespace: sym::constant },
433436
OpaqueTy => DefPathDataName::Anon { namespace: sym::opaque },
437+
AnonAdt => DefPathDataName::Anon { namespace: sym::anon_adt },
434438
}
435439
}
436440
}

compiler/rustc_hir/src/hir.rs

+2
Original file line numberDiff line numberDiff line change
@@ -2587,6 +2587,8 @@ pub enum TyKind<'hir> {
25872587
Never,
25882588
/// A tuple (`(A, B, C, D, ...)`).
25892589
Tup(&'hir [Ty<'hir>]),
2590+
/// An anonymous struct or union type i.e. `struct { foo: Type }` or `union { foo: Type }`
2591+
AnonAdt(ItemId),
25902592
/// A path to a type definition (`module::module::...::Type`), or an
25912593
/// associated type (e.g., `<Vec<T> as Trait>::Type` or `<T>::Target`).
25922594
///

compiler/rustc_hir/src/intravisit.rs

+3
Original file line numberDiff line numberDiff line change
@@ -852,6 +852,9 @@ pub fn walk_ty<'v, V: Visitor<'v>>(visitor: &mut V, typ: &'v Ty<'v>) {
852852
}
853853
TyKind::Typeof(ref expression) => visitor.visit_anon_const(expression),
854854
TyKind::Infer | TyKind::InferDelegation(..) | TyKind::Err(_) => {}
855+
TyKind::AnonAdt(item_id) => {
856+
visitor.visit_nested_item(item_id);
857+
}
855858
}
856859
}
857860

compiler/rustc_hir_analysis/messages.ftl

+35
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,28 @@ hir_analysis_field_already_declared =
123123
.label = field already declared
124124
.previous_decl_label = `{$field_name}` first declared here
125125
126+
hir_analysis_field_already_declared_both_nested =
127+
field `{$field_name}` is already declared
128+
.label = field `{$field_name}` declared in this unnamed field
129+
.nested_field_decl_note = field `{$field_name}` declared here
130+
.previous_decl_label = `{$field_name}` first declared here in this unnamed field
131+
.previous_nested_field_decl_note = field `{$field_name}` first declared here
132+
133+
hir_analysis_field_already_declared_current_nested =
134+
field `{$field_name}` is already declared
135+
.label = field `{$field_name}` declared in this unnamed field
136+
.nested_field_decl_note = field `{$field_name}` declared here
137+
.previous_decl_label = `{$field_name}` first declared here
138+
139+
hir_analysis_field_already_declared_nested_help =
140+
fields from the type of this unnamed field are considered fields of the outer type
141+
142+
hir_analysis_field_already_declared_previous_nested =
143+
field `{$field_name}` is already declared
144+
.label = field already declared
145+
.previous_decl_label = `{$field_name}` first declared here in this unnamed field
146+
.previous_nested_field_decl_note = field `{$field_name}` first declared here
147+
126148
hir_analysis_function_not_found_in_trait = function not found in this trait
127149
128150
hir_analysis_function_not_have_default_implementation = function doesn't have a default implementation
@@ -420,6 +442,19 @@ hir_analysis_typeof_reserved_keyword_used =
420442
hir_analysis_unconstrained_opaque_type = unconstrained opaque type
421443
.note = `{$name}` must be used in combination with a concrete type within the same {$what}
422444
445+
hir_analysis_unnamed_fields_repr_field_defined = unnamed field defined here
446+
447+
hir_analysis_unnamed_fields_repr_field_missing_repr_c =
448+
named type of unnamed field must have `#[repr(C)]` representation
449+
.label = unnamed field defined here
450+
.field_ty_label = `{$field_ty}` defined here
451+
.suggestion = add `#[repr(C)]` to this {$field_adt_kind}
452+
453+
hir_analysis_unnamed_fields_repr_missing_repr_c =
454+
{$adt_kind} with unnamed fields must have `#[repr(C)]` representation
455+
.label = {$adt_kind} `{$adt_name}` defined here
456+
.suggestion = add `#[repr(C)]` to this {$adt_kind}
457+
423458
hir_analysis_unrecognized_atomic_operation =
424459
unrecognized atomic operation function: `{$op}`
425460
.label = unrecognized atomic operation

compiler/rustc_hir_analysis/src/astconv/mod.rs

+13
Original file line numberDiff line numberDiff line change
@@ -2457,6 +2457,19 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
24572457
hir::TyKind::Tup(fields) => {
24582458
Ty::new_tup_from_iter(tcx, fields.iter().map(|t| self.ast_ty_to_ty(t)))
24592459
}
2460+
hir::TyKind::AnonAdt(item_id) => {
2461+
let did = item_id.owner_id.def_id;
2462+
let adt_def = tcx.adt_def(did);
2463+
let generics = tcx.generics_of(did);
2464+
2465+
debug!("ast_ty_to_ty_inner(AnonAdt): generics={:?}", generics);
2466+
let args = ty::GenericArgs::for_item(tcx, did.to_def_id(), |param, _| {
2467+
tcx.mk_param_from_def(param)
2468+
});
2469+
debug!("ast_ty_to_ty_inner(AnonAdt): args={:?}", args);
2470+
2471+
Ty::new_adt(tcx, adt_def, tcx.mk_args(args))
2472+
}
24602473
hir::TyKind::BareFn(bf) => {
24612474
require_c_abi_if_c_variadic(tcx, bf.decl, bf.abi, ast_ty.span);
24622475

compiler/rustc_hir_analysis/src/check/check.rs

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

8181
check_transparent(tcx, def);
8282
check_packed(tcx, span, def);
83+
check_unnamed_fields(tcx, def);
8384
}
8485

8586
fn check_union(tcx: TyCtxt<'_>, def_id: LocalDefId) {
@@ -89,6 +90,58 @@ fn check_union(tcx: TyCtxt<'_>, def_id: LocalDefId) {
8990
check_transparent(tcx, def);
9091
check_union_fields(tcx, span, def_id);
9192
check_packed(tcx, span, def);
93+
check_unnamed_fields(tcx, def);
94+
}
95+
96+
/// Check the representation of adts with unnamed fields.
97+
fn check_unnamed_fields(tcx: TyCtxt<'_>, def: ty::AdtDef<'_>) {
98+
if def.is_enum() {
99+
return;
100+
}
101+
let variant = def.non_enum_variant();
102+
if !variant.has_unnamed_fields() {
103+
return;
104+
}
105+
if !def.is_anonymous() {
106+
let adt_kind = def.descr();
107+
let span = tcx.def_span(def.did());
108+
let unnamed_fields = variant
109+
.fields
110+
.iter()
111+
.filter(|f| f.is_unnamed())
112+
.map(|f| {
113+
let span = tcx.def_span(f.did);
114+
errors::UnnamedFieldsReprFieldDefined { span }
115+
})
116+
.collect::<Vec<_>>();
117+
debug_assert_ne!(unnamed_fields.len(), 0, "expect unnamed fields in this adt");
118+
let adt_name = tcx.item_name(def.did());
119+
if !def.repr().c() {
120+
tcx.dcx().emit_err(errors::UnnamedFieldsRepr::MissingReprC {
121+
span,
122+
adt_kind,
123+
adt_name,
124+
unnamed_fields,
125+
sugg_span: span.shrink_to_lo(),
126+
});
127+
}
128+
}
129+
for field in variant.fields.iter().filter(|f| f.is_unnamed()) {
130+
let field_ty = tcx.type_of(field.did).instantiate_identity();
131+
if let Some(adt) = field_ty.ty_adt_def()
132+
&& !adt.is_anonymous()
133+
&& !adt.repr().c()
134+
{
135+
let field_ty_span = tcx.def_span(adt.did());
136+
tcx.dcx().emit_err(errors::UnnamedFieldsRepr::FieldMissingReprC {
137+
span: tcx.def_span(field.did),
138+
field_ty_span,
139+
field_ty,
140+
field_adt_kind: adt.descr(),
141+
sugg_span: field_ty_span.shrink_to_lo(),
142+
});
143+
}
144+
}
92145
}
93146

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

0 commit comments

Comments
 (0)