Skip to content

Commit 868706d

Browse files
Parse unnamed fields and anonymous structs or unions
Anonymous structs or unions are only allowed in struct field definitions. Co-authored-by: carbotaniuman <41451839+carbotaniuman@users.noreply.github.com>
1 parent 439d066 commit 868706d

File tree

25 files changed

+650
-5
lines changed

25 files changed

+650
-5
lines changed

compiler/rustc_ast/src/ast.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2092,6 +2092,10 @@ pub enum TyKind {
20922092
Never,
20932093
/// A tuple (`(A, B, C, D,...)`).
20942094
Tup(ThinVec<P<Ty>>),
2095+
/// An anonymous struct type i.e. `struct { foo: Type }`
2096+
AnonStruct(ThinVec<FieldDef>),
2097+
/// An anonymous union type i.e. `union { bar: Type }`
2098+
AnonUnion(ThinVec<FieldDef>),
20952099
/// A path (`module::module::...::Type`), optionally
20962100
/// "qualified", e.g., `<Vec<T> as SomeTrait>::SomeType`.
20972101
///

compiler/rustc_ast/src/mut_visit.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -509,6 +509,9 @@ pub fn noop_visit_ty<T: MutVisitor>(ty: &mut P<Ty>, vis: &mut T) {
509509
visit_vec(bounds, |bound| vis.visit_param_bound(bound));
510510
}
511511
TyKind::MacCall(mac) => vis.visit_mac_call(mac),
512+
TyKind::AnonStruct(fields) | TyKind::AnonUnion(fields) => {
513+
fields.flat_map_in_place(|field| vis.flat_map_field_def(field));
514+
}
512515
}
513516
vis.visit_span(span);
514517
visit_lazy_tts(tokens, vis);

compiler/rustc_ast/src/token.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -486,6 +486,8 @@ impl Token {
486486
Lt | BinOp(Shl) | // associated path
487487
ModSep => true, // global path
488488
Interpolated(ref nt) => matches!(**nt, NtTy(..) | NtPath(..)),
489+
// For anonymous structs or unions, which only appear in specific positions
490+
// (type of struct fields or union fields), we don't consider them as regular types
489491
_ => false,
490492
}
491493
}

compiler/rustc_ast/src/visit.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -438,6 +438,9 @@ pub fn walk_ty<'a, V: Visitor<'a>>(visitor: &mut V, typ: &'a Ty) {
438438
TyKind::Infer | TyKind::ImplicitSelf | TyKind::Err => {}
439439
TyKind::MacCall(mac) => visitor.visit_mac_call(mac),
440440
TyKind::Never | TyKind::CVarArgs => {}
441+
TyKind::AnonStruct(ref fields, ..) | TyKind::AnonUnion(ref fields, ..) => {
442+
walk_list!(visitor, visit_field_def, fields)
443+
}
441444
}
442445
}
443446

compiler/rustc_ast_lowering/src/lib.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1293,6 +1293,18 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
12931293
TyKind::Err => {
12941294
hir::TyKind::Err(self.tcx.sess.delay_span_bug(t.span, "TyKind::Err lowered"))
12951295
}
1296+
// FIXME(unnamed_fields): IMPLEMENTATION IN PROGRESS
1297+
#[allow(rustc::untranslatable_diagnostic)]
1298+
#[allow(rustc::diagnostic_outside_of_impl)]
1299+
TyKind::AnonStruct(ref _fields) => hir::TyKind::Err(
1300+
self.tcx.sess.span_err(t.span, "anonymous structs are unimplemented"),
1301+
),
1302+
// FIXME(unnamed_fields): IMPLEMENTATION IN PROGRESS
1303+
#[allow(rustc::untranslatable_diagnostic)]
1304+
#[allow(rustc::diagnostic_outside_of_impl)]
1305+
TyKind::AnonUnion(ref _fields) => hir::TyKind::Err(
1306+
self.tcx.sess.span_err(t.span, "anonymous unions are unimplemented"),
1307+
),
12961308
TyKind::Slice(ty) => hir::TyKind::Slice(self.lower_ty(ty, itctx)),
12971309
TyKind::Ptr(mt) => hir::TyKind::Ptr(self.lower_mt(mt, itctx)),
12981310
TyKind::Ref(region, mt) => {

compiler/rustc_ast_passes/messages.ftl

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
ast_passes_anon_struct_or_union_not_allowed =
2+
anonymous {$struct_or_union}s are not allowed outside of unnamed struct or union fields
3+
.label = anonymous {$struct_or_union} declared here
4+
15
ast_passes_assoc_const_without_body =
26
associated constant in `impl` without body
37
.suggestion = provide a definition for the constant
@@ -162,6 +166,14 @@ ast_passes_inherent_cannot_be = inherent impls cannot be {$annotation}
162166
ast_passes_invalid_label =
163167
invalid label name `{$name}`
164168
169+
ast_passes_invalid_unnamed_field =
170+
unnamed fields are not allowed outside of structs or unions
171+
.label = unnamed field declared here
172+
173+
ast_passes_invalid_unnamed_field_ty =
174+
unnamed fields can only have struct or union types
175+
.label = not a struct or union
176+
165177
ast_passes_item_underscore = `{$kind}` items in this context need a name
166178
.label = `_` is not a valid name for this `{$kind}` item
167179

compiler/rustc_ast_passes/src/ast_validation.rs

Lines changed: 84 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -219,10 +219,27 @@ impl<'a> AstValidator<'a> {
219219
}
220220
}
221221
}
222+
TyKind::AnonStruct(ref fields, ..) | TyKind::AnonUnion(ref fields, ..) => {
223+
walk_list!(self, visit_field_def, fields)
224+
}
222225
_ => visit::walk_ty(self, t),
223226
}
224227
}
225228

229+
fn visit_struct_field_def(&mut self, field: &'a FieldDef) {
230+
if let Some(ident) = field.ident &&
231+
ident.name == kw::Underscore {
232+
self.check_unnamed_field_ty(&field.ty, ident.span);
233+
self.visit_vis(&field.vis);
234+
self.visit_ident(ident);
235+
self.visit_ty_common(&field.ty);
236+
self.walk_ty(&field.ty);
237+
walk_list!(self, visit_attribute, &field.attrs);
238+
} else {
239+
self.visit_field_def(field);
240+
}
241+
}
242+
226243
fn err_handler(&self) -> &rustc_errors::Handler {
227244
&self.session.diagnostic()
228245
}
@@ -260,6 +277,42 @@ impl<'a> AstValidator<'a> {
260277
}
261278
}
262279

280+
fn check_unnamed_field_ty(&self, ty: &Ty, span: Span) {
281+
if matches!(
282+
&ty.kind,
283+
// We already checked for `kw::Underscore` before calling this function,
284+
// so skip the check
285+
TyKind::AnonStruct(..) | TyKind::AnonUnion(..)
286+
// If the anonymous field contains a Path as type, we can't determine
287+
// if the path is a valid struct or union, so skip the check
288+
| TyKind::Path(..)
289+
) {
290+
return;
291+
}
292+
self.err_handler().emit_err(errors::InvalidUnnamedFieldTy { span, ty_span: ty.span });
293+
}
294+
295+
fn deny_anon_struct_or_union(&self, ty: &Ty) {
296+
let struct_or_union = match &ty.kind {
297+
TyKind::AnonStruct(..) => "struct",
298+
TyKind::AnonUnion(..) => "union",
299+
_ => return,
300+
};
301+
self.err_handler()
302+
.emit_err(errors::AnonStructOrUnionNotAllowed { struct_or_union, span: ty.span });
303+
}
304+
305+
fn deny_unnamed_field(&self, field: &FieldDef) {
306+
if let Some(ident) = field.ident &&
307+
ident.name == kw::Underscore {
308+
self.err_handler()
309+
.emit_err(errors::InvalidUnnamedField {
310+
span: field.span,
311+
ident_span: ident.span
312+
});
313+
}
314+
}
315+
263316
fn check_trait_fn_not_const(&self, constness: Const) {
264317
if let Const::Yes(span) = constness {
265318
self.session.emit_err(errors::TraitFnConst { span });
@@ -785,6 +838,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
785838

786839
fn visit_ty(&mut self, ty: &'a Ty) {
787840
self.visit_ty_common(ty);
841+
self.deny_anon_struct_or_union(ty);
788842
self.walk_ty(ty)
789843
}
790844

@@ -799,6 +853,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
799853
}
800854

801855
fn visit_field_def(&mut self, field: &'a FieldDef) {
856+
self.deny_unnamed_field(field);
802857
visit::walk_field_def(self, field)
803858
}
804859

@@ -991,10 +1046,38 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
9911046
self.check_mod_file_item_asciionly(item.ident);
9921047
}
9931048
}
994-
ItemKind::Union(vdata, ..) => {
1049+
ItemKind::Struct(vdata, generics) => match vdata {
1050+
// Duplicating the `Visitor` logic allows catching all cases
1051+
// of `Anonymous(Struct, Union)` outside of a field struct or union.
1052+
//
1053+
// Inside `visit_ty` the validator catches every `Anonymous(Struct, Union)` it
1054+
// encounters, and only on `ItemKind::Struct` and `ItemKind::Union`
1055+
// it uses `visit_ty_common`, which doesn't contain that specific check.
1056+
VariantData::Struct(fields, ..) => {
1057+
self.visit_vis(&item.vis);
1058+
self.visit_ident(item.ident);
1059+
self.visit_generics(generics);
1060+
walk_list!(self, visit_struct_field_def, fields);
1061+
walk_list!(self, visit_attribute, &item.attrs);
1062+
return;
1063+
}
1064+
_ => {}
1065+
},
1066+
ItemKind::Union(vdata, generics) => {
9951067
if vdata.fields().is_empty() {
9961068
self.err_handler().emit_err(errors::FieldlessUnion { span: item.span });
9971069
}
1070+
match vdata {
1071+
VariantData::Struct(fields, ..) => {
1072+
self.visit_vis(&item.vis);
1073+
self.visit_ident(item.ident);
1074+
self.visit_generics(generics);
1075+
walk_list!(self, visit_struct_field_def, fields);
1076+
walk_list!(self, visit_attribute, &item.attrs);
1077+
return;
1078+
}
1079+
_ => {}
1080+
}
9981081
}
9991082
ItemKind::Const(box ConstItem { defaultness, expr: None, .. }) => {
10001083
self.check_defaultness(item.span, *defaultness);

compiler/rustc_ast_passes/src/errors.rs

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -701,3 +701,30 @@ pub struct ConstraintOnNegativeBound {
701701
#[primary_span]
702702
pub span: Span,
703703
}
704+
705+
#[derive(Diagnostic)]
706+
#[diag(ast_passes_invalid_unnamed_field_ty)]
707+
pub struct InvalidUnnamedFieldTy {
708+
#[primary_span]
709+
pub span: Span,
710+
#[label]
711+
pub ty_span: Span,
712+
}
713+
714+
#[derive(Diagnostic)]
715+
#[diag(ast_passes_invalid_unnamed_field)]
716+
pub struct InvalidUnnamedField {
717+
#[primary_span]
718+
pub span: Span,
719+
#[label]
720+
pub ident_span: Span,
721+
}
722+
723+
#[derive(Diagnostic)]
724+
#[diag(ast_passes_anon_struct_or_union_not_allowed)]
725+
pub struct AnonStructOrUnionNotAllowed {
726+
#[primary_span]
727+
#[label]
728+
pub span: Span,
729+
pub struct_or_union: &'static str,
730+
}

compiler/rustc_ast_passes/src/feature_gate.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -570,6 +570,7 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session) {
570570
gate_all!(builtin_syntax, "`builtin #` syntax is unstable");
571571
gate_all!(explicit_tail_calls, "`become` expression is experimental");
572572
gate_all!(generic_const_items, "generic const items are experimental");
573+
gate_all!(unnamed_fields, "unnamed fields are not yet fully implemented");
573574

574575
if !visitor.features.negative_bounds {
575576
for &span in spans.get(&sym::negative_bounds).iter().copied().flatten() {

compiler/rustc_ast_pretty/src/pprust/state.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1053,6 +1053,14 @@ impl<'a> State<'a> {
10531053
}
10541054
self.pclose();
10551055
}
1056+
ast::TyKind::AnonStruct(fields) => {
1057+
self.head("struct");
1058+
self.print_record_struct_body(&fields, ty.span);
1059+
}
1060+
ast::TyKind::AnonUnion(fields) => {
1061+
self.head("union");
1062+
self.print_record_struct_body(&fields, ty.span);
1063+
}
10561064
ast::TyKind::Paren(typ) => {
10571065
self.popen();
10581066
self.print_type(typ);

0 commit comments

Comments
 (0)