diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs
index 58725a08c7c5f..395540764ea05 100644
--- a/compiler/rustc_ast/src/ast.rs
+++ b/compiler/rustc_ast/src/ast.rs
@@ -2092,6 +2092,10 @@ pub enum TyKind {
Never,
/// A tuple (`(A, B, C, D,...)`).
Tup(ThinVec
>),
+ /// An anonymous struct type i.e. `struct { foo: Type }`
+ AnonStruct(ThinVec),
+ /// An anonymous union type i.e. `union { bar: Type }`
+ AnonUnion(ThinVec),
/// A path (`module::module::...::Type`), optionally
/// "qualified", e.g., ` as SomeTrait>::SomeType`.
///
diff --git a/compiler/rustc_ast/src/mut_visit.rs b/compiler/rustc_ast/src/mut_visit.rs
index 48e9b180b74fc..e3504a5638e47 100644
--- a/compiler/rustc_ast/src/mut_visit.rs
+++ b/compiler/rustc_ast/src/mut_visit.rs
@@ -510,6 +510,9 @@ pub fn noop_visit_ty(ty: &mut P, vis: &mut T) {
visit_vec(bounds, |bound| vis.visit_param_bound(bound));
}
TyKind::MacCall(mac) => vis.visit_mac_call(mac),
+ TyKind::AnonStruct(fields) | TyKind::AnonUnion(fields) => {
+ fields.flat_map_in_place(|field| vis.flat_map_field_def(field));
+ }
}
vis.visit_span(span);
visit_lazy_tts(tokens, vis);
diff --git a/compiler/rustc_ast/src/token.rs b/compiler/rustc_ast/src/token.rs
index f4ad0efa42344..300b1486f9ba0 100644
--- a/compiler/rustc_ast/src/token.rs
+++ b/compiler/rustc_ast/src/token.rs
@@ -486,6 +486,8 @@ impl Token {
Lt | BinOp(Shl) | // associated path
ModSep => true, // global path
Interpolated(ref nt) => matches!(**nt, NtTy(..) | NtPath(..)),
+ // For anonymous structs or unions, which only appear in specific positions
+ // (type of struct fields or union fields), we don't consider them as regular types
_ => false,
}
}
diff --git a/compiler/rustc_ast/src/visit.rs b/compiler/rustc_ast/src/visit.rs
index 6d474de2d15f1..ddbbf5a10bcdd 100644
--- a/compiler/rustc_ast/src/visit.rs
+++ b/compiler/rustc_ast/src/visit.rs
@@ -438,6 +438,9 @@ pub fn walk_ty<'a, V: Visitor<'a>>(visitor: &mut V, typ: &'a Ty) {
TyKind::Infer | TyKind::ImplicitSelf | TyKind::Err => {}
TyKind::MacCall(mac) => visitor.visit_mac_call(mac),
TyKind::Never | TyKind::CVarArgs => {}
+ TyKind::AnonStruct(ref fields, ..) | TyKind::AnonUnion(ref fields, ..) => {
+ walk_list!(visitor, visit_field_def, fields)
+ }
}
}
diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs
index 4a47de1280c63..1827e42368f92 100644
--- a/compiler/rustc_ast_lowering/src/lib.rs
+++ b/compiler/rustc_ast_lowering/src/lib.rs
@@ -1293,6 +1293,18 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
TyKind::Err => {
hir::TyKind::Err(self.tcx.sess.delay_span_bug(t.span, "TyKind::Err lowered"))
}
+ // FIXME(unnamed_fields): IMPLEMENTATION IN PROGRESS
+ #[allow(rustc::untranslatable_diagnostic)]
+ #[allow(rustc::diagnostic_outside_of_impl)]
+ TyKind::AnonStruct(ref _fields) => hir::TyKind::Err(
+ self.tcx.sess.span_err(t.span, "anonymous structs are unimplemented"),
+ ),
+ // FIXME(unnamed_fields): IMPLEMENTATION IN PROGRESS
+ #[allow(rustc::untranslatable_diagnostic)]
+ #[allow(rustc::diagnostic_outside_of_impl)]
+ TyKind::AnonUnion(ref _fields) => hir::TyKind::Err(
+ self.tcx.sess.span_err(t.span, "anonymous unions are unimplemented"),
+ ),
TyKind::Slice(ty) => hir::TyKind::Slice(self.lower_ty(ty, itctx)),
TyKind::Ptr(mt) => hir::TyKind::Ptr(self.lower_mt(mt, itctx)),
TyKind::Ref(region, mt) => {
diff --git a/compiler/rustc_ast_passes/messages.ftl b/compiler/rustc_ast_passes/messages.ftl
index f323bb4c254e0..ee5007027de96 100644
--- a/compiler/rustc_ast_passes/messages.ftl
+++ b/compiler/rustc_ast_passes/messages.ftl
@@ -1,3 +1,7 @@
+ast_passes_anon_struct_or_union_not_allowed =
+ anonymous {$struct_or_union}s are not allowed outside of unnamed struct or union fields
+ .label = anonymous {$struct_or_union} declared here
+
ast_passes_assoc_const_without_body =
associated constant in `impl` without body
.suggestion = provide a definition for the constant
@@ -162,6 +166,14 @@ ast_passes_inherent_cannot_be = inherent impls cannot be {$annotation}
ast_passes_invalid_label =
invalid label name `{$name}`
+ast_passes_invalid_unnamed_field =
+ unnamed fields are not allowed outside of structs or unions
+ .label = unnamed field declared here
+
+ast_passes_invalid_unnamed_field_ty =
+ unnamed fields can only have struct or union types
+ .label = not a struct or union
+
ast_passes_item_underscore = `{$kind}` items in this context need a name
.label = `_` is not a valid name for this `{$kind}` item
diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs
index bd3e676daa4c7..ad367d05f0478 100644
--- a/compiler/rustc_ast_passes/src/ast_validation.rs
+++ b/compiler/rustc_ast_passes/src/ast_validation.rs
@@ -223,10 +223,27 @@ impl<'a> AstValidator<'a> {
}
}
}
+ TyKind::AnonStruct(ref fields, ..) | TyKind::AnonUnion(ref fields, ..) => {
+ walk_list!(self, visit_field_def, fields)
+ }
_ => visit::walk_ty(self, t),
}
}
+ fn visit_struct_field_def(&mut self, field: &'a FieldDef) {
+ if let Some(ident) = field.ident &&
+ ident.name == kw::Underscore {
+ self.check_unnamed_field_ty(&field.ty, ident.span);
+ self.visit_vis(&field.vis);
+ self.visit_ident(ident);
+ self.visit_ty_common(&field.ty);
+ self.walk_ty(&field.ty);
+ walk_list!(self, visit_attribute, &field.attrs);
+ } else {
+ self.visit_field_def(field);
+ }
+ }
+
fn err_handler(&self) -> &rustc_errors::Handler {
&self.session.diagnostic()
}
@@ -264,6 +281,42 @@ impl<'a> AstValidator<'a> {
}
}
+ fn check_unnamed_field_ty(&self, ty: &Ty, span: Span) {
+ if matches!(
+ &ty.kind,
+ // We already checked for `kw::Underscore` before calling this function,
+ // so skip the check
+ TyKind::AnonStruct(..) | TyKind::AnonUnion(..)
+ // If the anonymous field contains a Path as type, we can't determine
+ // if the path is a valid struct or union, so skip the check
+ | TyKind::Path(..)
+ ) {
+ return;
+ }
+ self.err_handler().emit_err(errors::InvalidUnnamedFieldTy { span, ty_span: ty.span });
+ }
+
+ fn deny_anon_struct_or_union(&self, ty: &Ty) {
+ let struct_or_union = match &ty.kind {
+ TyKind::AnonStruct(..) => "struct",
+ TyKind::AnonUnion(..) => "union",
+ _ => return,
+ };
+ self.err_handler()
+ .emit_err(errors::AnonStructOrUnionNotAllowed { struct_or_union, span: ty.span });
+ }
+
+ fn deny_unnamed_field(&self, field: &FieldDef) {
+ if let Some(ident) = field.ident &&
+ ident.name == kw::Underscore {
+ self.err_handler()
+ .emit_err(errors::InvalidUnnamedField {
+ span: field.span,
+ ident_span: ident.span
+ });
+ }
+ }
+
fn check_trait_fn_not_const(&self, constness: Const) {
if let Const::Yes(span) = constness {
self.session.emit_err(errors::TraitFnConst { span });
@@ -789,6 +842,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
fn visit_ty(&mut self, ty: &'a Ty) {
self.visit_ty_common(ty);
+ self.deny_anon_struct_or_union(ty);
self.walk_ty(ty)
}
@@ -803,6 +857,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
}
fn visit_field_def(&mut self, field: &'a FieldDef) {
+ self.deny_unnamed_field(field);
visit::walk_field_def(self, field)
}
@@ -995,10 +1050,38 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
self.check_mod_file_item_asciionly(item.ident);
}
}
- ItemKind::Union(vdata, ..) => {
+ ItemKind::Struct(vdata, generics) => match vdata {
+ // Duplicating the `Visitor` logic allows catching all cases
+ // of `Anonymous(Struct, Union)` outside of a field struct or union.
+ //
+ // Inside `visit_ty` the validator catches every `Anonymous(Struct, Union)` it
+ // encounters, and only on `ItemKind::Struct` and `ItemKind::Union`
+ // it uses `visit_ty_common`, which doesn't contain that specific check.
+ VariantData::Struct(fields, ..) => {
+ self.visit_vis(&item.vis);
+ self.visit_ident(item.ident);
+ self.visit_generics(generics);
+ walk_list!(self, visit_struct_field_def, fields);
+ walk_list!(self, visit_attribute, &item.attrs);
+ return;
+ }
+ _ => {}
+ },
+ ItemKind::Union(vdata, generics) => {
if vdata.fields().is_empty() {
self.err_handler().emit_err(errors::FieldlessUnion { span: item.span });
}
+ match vdata {
+ VariantData::Struct(fields, ..) => {
+ self.visit_vis(&item.vis);
+ self.visit_ident(item.ident);
+ self.visit_generics(generics);
+ walk_list!(self, visit_struct_field_def, fields);
+ walk_list!(self, visit_attribute, &item.attrs);
+ return;
+ }
+ _ => {}
+ }
}
ItemKind::Const(box ConstItem { defaultness, expr: None, .. }) => {
self.check_defaultness(item.span, *defaultness);
diff --git a/compiler/rustc_ast_passes/src/errors.rs b/compiler/rustc_ast_passes/src/errors.rs
index a6f217d4780fd..74ab48c06ff6d 100644
--- a/compiler/rustc_ast_passes/src/errors.rs
+++ b/compiler/rustc_ast_passes/src/errors.rs
@@ -727,3 +727,30 @@ pub struct ConstraintOnNegativeBound {
#[primary_span]
pub span: Span,
}
+
+#[derive(Diagnostic)]
+#[diag(ast_passes_invalid_unnamed_field_ty)]
+pub struct InvalidUnnamedFieldTy {
+ #[primary_span]
+ pub span: Span,
+ #[label]
+ pub ty_span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(ast_passes_invalid_unnamed_field)]
+pub struct InvalidUnnamedField {
+ #[primary_span]
+ pub span: Span,
+ #[label]
+ pub ident_span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(ast_passes_anon_struct_or_union_not_allowed)]
+pub struct AnonStructOrUnionNotAllowed {
+ #[primary_span]
+ #[label]
+ pub span: Span,
+ pub struct_or_union: &'static str,
+}
diff --git a/compiler/rustc_ast_passes/src/feature_gate.rs b/compiler/rustc_ast_passes/src/feature_gate.rs
index 10c9c3ef11188..8405ae6ff8e8b 100644
--- a/compiler/rustc_ast_passes/src/feature_gate.rs
+++ b/compiler/rustc_ast_passes/src/feature_gate.rs
@@ -570,6 +570,7 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session, features: &Features) {
gate_all!(builtin_syntax, "`builtin #` syntax is unstable");
gate_all!(explicit_tail_calls, "`become` expression is experimental");
gate_all!(generic_const_items, "generic const items are experimental");
+ gate_all!(unnamed_fields, "unnamed fields are not yet fully implemented");
if !visitor.features.negative_bounds {
for &span in spans.get(&sym::negative_bounds).iter().copied().flatten() {
diff --git a/compiler/rustc_ast_pretty/src/pprust/state.rs b/compiler/rustc_ast_pretty/src/pprust/state.rs
index 58ce73047bcec..8b7e91882fcc7 100644
--- a/compiler/rustc_ast_pretty/src/pprust/state.rs
+++ b/compiler/rustc_ast_pretty/src/pprust/state.rs
@@ -1064,6 +1064,14 @@ impl<'a> State<'a> {
}
self.pclose();
}
+ ast::TyKind::AnonStruct(fields) => {
+ self.head("struct");
+ self.print_record_struct_body(&fields, ty.span);
+ }
+ ast::TyKind::AnonUnion(fields) => {
+ self.head("union");
+ self.print_record_struct_body(&fields, ty.span);
+ }
ast::TyKind::Paren(typ) => {
self.popen();
self.print_type(typ);
diff --git a/compiler/rustc_ast_pretty/src/pprust/state/item.rs b/compiler/rustc_ast_pretty/src/pprust/state/item.rs
index d27a44f1206dd..3393f034bc3b5 100644
--- a/compiler/rustc_ast_pretty/src/pprust/state/item.rs
+++ b/compiler/rustc_ast_pretty/src/pprust/state/item.rs
@@ -443,7 +443,11 @@ impl<'a> State<'a> {
}
}
- fn print_record_struct_body(&mut self, fields: &[ast::FieldDef], span: rustc_span::Span) {
+ pub(crate) fn print_record_struct_body(
+ &mut self,
+ fields: &[ast::FieldDef],
+ span: rustc_span::Span,
+ ) {
self.nbsp();
self.bopen();
diff --git a/compiler/rustc_feature/src/active.rs b/compiler/rustc_feature/src/active.rs
index f5bc140c049f2..1941390dc4a1c 100644
--- a/compiler/rustc_feature/src/active.rs
+++ b/compiler/rustc_feature/src/active.rs
@@ -584,6 +584,8 @@ declare_features! (
(active, type_privacy_lints, "1.72.0", Some(48054), None),
/// Enables rustc to generate code that instructs libstd to NOT ignore SIGPIPE.
(active, unix_sigpipe, "1.65.0", Some(97889), None),
+ /// Allows unnamed fields of struct and union type
+ (incomplete, unnamed_fields, "CURRENT_RUSTC_VERSION", Some(49804), None),
/// Allows unsized fn parameters.
(active, unsized_fn_params, "1.49.0", Some(48055), None),
/// Allows unsized rvalues at arguments and parameters.
diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs
index 24c65d061f95a..5db31c2347833 100644
--- a/compiler/rustc_parse/src/parser/item.rs
+++ b/compiler/rustc_parse/src/parser/item.rs
@@ -1594,7 +1594,7 @@ impl<'a> Parser<'a> {
Ok((class_name, ItemKind::Union(vdata, generics)))
}
- fn parse_record_struct_body(
+ pub(crate) fn parse_record_struct_body(
&mut self,
adt_ty: &str,
ident_span: Span,
@@ -1869,7 +1869,7 @@ impl<'a> Parser<'a> {
}
}
self.expect_field_ty_separator()?;
- let ty = self.parse_ty()?;
+ let ty = self.parse_ty_for_field_def()?;
if self.token.kind == token::Colon && self.look_ahead(1, |tok| tok.kind != token::Colon) {
self.sess.emit_err(errors::SingleColonStructType { span: self.token.span });
}
@@ -1894,7 +1894,9 @@ impl<'a> Parser<'a> {
/// for better diagnostics and suggestions.
fn parse_field_ident(&mut self, adt_ty: &str, lo: Span) -> PResult<'a, Ident> {
let (ident, is_raw) = self.ident_or_err(true)?;
- if !is_raw && ident.is_reserved() {
+ if ident.name == kw::Underscore {
+ self.sess.gated_spans.gate(sym::unnamed_fields, lo);
+ } else if !is_raw && ident.is_reserved() {
let snapshot = self.create_snapshot_for_diagnostic();
let err = if self.check_fn_front_matter(false, Case::Sensitive) {
let inherited_vis = Visibility {
diff --git a/compiler/rustc_parse/src/parser/ty.rs b/compiler/rustc_parse/src/parser/ty.rs
index 2d888efb1f384..661113666cd8e 100644
--- a/compiler/rustc_parse/src/parser/ty.rs
+++ b/compiler/rustc_parse/src/parser/ty.rs
@@ -136,6 +136,17 @@ impl<'a> Parser<'a> {
)
}
+ /// Parse a type suitable for a field defintion.
+ /// The difference from `parse_ty` is that this version
+ /// allows anonymous structs and unions.
+ pub fn parse_ty_for_field_def(&mut self) -> PResult<'a, P> {
+ if self.can_begin_anon_struct_or_union() {
+ self.parse_anon_struct_or_union()
+ } else {
+ self.parse_ty()
+ }
+ }
+
/// Parse a type suitable for a function or function pointer parameter.
/// The difference from `parse_ty` is that this version allows `...`
/// (`CVarArgs`) at the top level of the type.
@@ -336,6 +347,36 @@ impl<'a> Parser<'a> {
if allow_qpath_recovery { self.maybe_recover_from_bad_qpath(ty) } else { Ok(ty) }
}
+ /// Parse an anonymous struct or union (only for field definitions):
+ /// ```ignore (feature-not-ready)
+ /// #[repr(C)]
+ /// struct Foo {
+ /// _: struct { // anonymous struct
+ /// x: u32,
+ /// y: f64,
+ /// }
+ /// _: union { // anonymous union
+ /// z: u32,
+ /// w: f64,
+ /// }
+ /// }
+ /// ```
+ fn parse_anon_struct_or_union(&mut self) -> PResult<'a, P> {
+ assert!(self.token.is_keyword(kw::Union) || self.token.is_keyword(kw::Struct));
+ let is_union = self.token.is_keyword(kw::Union);
+
+ let lo = self.token.span;
+ self.bump();
+
+ let (fields, _recovered) =
+ self.parse_record_struct_body(if is_union { "union" } else { "struct" }, lo, false)?;
+ let span = lo.to(self.prev_token.span);
+ self.sess.gated_spans.gate(sym::unnamed_fields, span);
+ // These can be rejected during AST validation in `deny_anon_struct_or_union`.
+ let kind = if is_union { TyKind::AnonUnion(fields) } else { TyKind::AnonStruct(fields) };
+ Ok(self.mk_ty(span, kind))
+ }
+
/// Parses either:
/// - `(TYPE)`, a parenthesized type.
/// - `(TYPE,)`, a tuple with a single field of type TYPE.
@@ -696,6 +737,11 @@ impl<'a> Parser<'a> {
Ok(bounds)
}
+ pub(super) fn can_begin_anon_struct_or_union(&mut self) -> bool {
+ (self.token.is_keyword(kw::Struct) || self.token.is_keyword(kw::Union))
+ && self.look_ahead(1, |t| t == &token::OpenDelim(Delimiter::Brace))
+ }
+
/// Can the current token begin a bound?
fn can_begin_bound(&mut self) -> bool {
// This needs to be synchronized with `TokenKind::can_begin_bound`.
diff --git a/compiler/rustc_passes/src/hir_stats.rs b/compiler/rustc_passes/src/hir_stats.rs
index 5aa8aef6a859a..24087a4eabbcd 100644
--- a/compiler/rustc_passes/src/hir_stats.rs
+++ b/compiler/rustc_passes/src/hir_stats.rs
@@ -587,6 +587,8 @@ impl<'v> ast_visit::Visitor<'v> for StatCollector<'v> {
BareFn,
Never,
Tup,
+ AnonStruct,
+ AnonUnion,
Path,
TraitObject,
ImplTrait,
diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs
index 8aec12f128ec6..c0eff6dfd8fec 100644
--- a/compiler/rustc_span/src/symbol.rs
+++ b/compiler/rustc_span/src/symbol.rs
@@ -1618,6 +1618,7 @@ symbols! {
unix_sigpipe,
unlikely,
unmarked_api,
+ unnamed_fields,
unpin,
unreachable,
unreachable_2015,
diff --git a/src/tools/rustfmt/src/types.rs b/src/tools/rustfmt/src/types.rs
index 18a08f17ba02f..5e8edd8f8bf4e 100644
--- a/src/tools/rustfmt/src/types.rs
+++ b/src/tools/rustfmt/src/types.rs
@@ -819,6 +819,8 @@ impl Rewrite for ast::Ty {
ast::TyKind::Tup(ref items) => {
rewrite_tuple(context, items.iter(), self.span, shape, items.len() == 1)
}
+ ast::TyKind::AnonStruct(_) => Some(context.snippet(self.span).to_owned()),
+ ast::TyKind::AnonUnion(_) => Some(context.snippet(self.span).to_owned()),
ast::TyKind::Path(ref q_self, ref path) => {
rewrite_path(context, PathContext::Type, q_self, path, shape)
}
diff --git a/src/tools/rustfmt/tests/target/anonymous-types.rs b/src/tools/rustfmt/tests/target/anonymous-types.rs
new file mode 100644
index 0000000000000..8e08c314ed1fb
--- /dev/null
+++ b/src/tools/rustfmt/tests/target/anonymous-types.rs
@@ -0,0 +1,19 @@
+// Test for issue 85480
+// Pretty print anonymous struct and union types
+
+// pp-exact
+// pretty-compare-only
+
+struct Foo {
+ _: union {
+ _: struct {
+ a: u8,
+ b: u16,
+ },
+ c: u32,
+ },
+ d: u64,
+ e: f32,
+}
+
+fn main() {}
diff --git a/tests/ui/feature-gates/feature-gate-unnamed_fields.rs b/tests/ui/feature-gates/feature-gate-unnamed_fields.rs
new file mode 100644
index 0000000000000..4bbd0c83bfbca
--- /dev/null
+++ b/tests/ui/feature-gates/feature-gate-unnamed_fields.rs
@@ -0,0 +1,26 @@
+struct Foo {
+ foo: u8,
+ _: union { //~ ERROR unnamed fields are not yet fully implemented [E0658]
+ //~^ ERROR unnamed fields are not yet fully implemented [E0658]
+ //~| ERROR anonymous unions are unimplemented
+ bar: u8,
+ baz: u16
+ }
+}
+
+union Bar {
+ foobar: u8,
+ _: struct { //~ ERROR unnamed fields are not yet fully implemented [E0658]
+ //~^ ERROR unnamed fields are not yet fully implemented [E0658]
+ //~| ERROR anonymous structs are unimplemented
+ foobaz: u8,
+ barbaz: u16
+ }
+}
+
+struct S;
+struct Baz {
+ _: S //~ ERROR unnamed fields are not yet fully implemented [E0658]
+}
+
+fn main(){}
diff --git a/tests/ui/feature-gates/feature-gate-unnamed_fields.stderr b/tests/ui/feature-gates/feature-gate-unnamed_fields.stderr
new file mode 100644
index 0000000000000..f026f2c3600b3
--- /dev/null
+++ b/tests/ui/feature-gates/feature-gate-unnamed_fields.stderr
@@ -0,0 +1,84 @@
+error[E0658]: unnamed fields are not yet fully implemented
+ --> $DIR/feature-gate-unnamed_fields.rs:3:5
+ |
+LL | _: union {
+ | ^
+ |
+ = note: see issue #49804 for more information
+ = help: add `#![feature(unnamed_fields)]` to the crate attributes to enable
+
+error[E0658]: unnamed fields are not yet fully implemented
+ --> $DIR/feature-gate-unnamed_fields.rs:3:8
+ |
+LL | _: union {
+ | ________^
+LL | |
+LL | |
+LL | | bar: u8,
+LL | | baz: u16
+LL | | }
+ | |_____^
+ |
+ = note: see issue #49804 for more information
+ = help: add `#![feature(unnamed_fields)]` to the crate attributes to enable
+
+error[E0658]: unnamed fields are not yet fully implemented
+ --> $DIR/feature-gate-unnamed_fields.rs:13:5
+ |
+LL | _: struct {
+ | ^
+ |
+ = note: see issue #49804 for more information
+ = help: add `#![feature(unnamed_fields)]` to the crate attributes to enable
+
+error[E0658]: unnamed fields are not yet fully implemented
+ --> $DIR/feature-gate-unnamed_fields.rs:13:8
+ |
+LL | _: struct {
+ | ________^
+LL | |
+LL | |
+LL | | foobaz: u8,
+LL | | barbaz: u16
+LL | | }
+ | |_____^
+ |
+ = note: see issue #49804 for more information
+ = help: add `#![feature(unnamed_fields)]` to the crate attributes to enable
+
+error[E0658]: unnamed fields are not yet fully implemented
+ --> $DIR/feature-gate-unnamed_fields.rs:23:5
+ |
+LL | _: S
+ | ^
+ |
+ = note: see issue #49804 for more information
+ = help: add `#![feature(unnamed_fields)]` to the crate attributes to enable
+
+error: anonymous unions are unimplemented
+ --> $DIR/feature-gate-unnamed_fields.rs:3:8
+ |
+LL | _: union {
+ | ________^
+LL | |
+LL | |
+LL | | bar: u8,
+LL | | baz: u16
+LL | | }
+ | |_____^
+
+error: anonymous structs are unimplemented
+ --> $DIR/feature-gate-unnamed_fields.rs:13:8
+ |
+LL | _: struct {
+ | ________^
+LL | |
+LL | |
+LL | | foobaz: u8,
+LL | | barbaz: u16
+LL | | }
+ | |_____^
+
+error: aborting due to 7 previous errors
+
+For more information about this error, try `rustc --explain E0658`.
diff --git a/tests/ui/parser/keyword-union-as-identifier.rs b/tests/ui/parser/keyword-union-as-identifier.rs
new file mode 100644
index 0000000000000..7062557d73126
--- /dev/null
+++ b/tests/ui/parser/keyword-union-as-identifier.rs
@@ -0,0 +1,72 @@
+// check-pass
+
+#![allow(non_camel_case_types)]
+#![allow(non_upper_case_globals)]
+
+mod union {
+ type union = i32;
+
+ pub struct Bar {
+ pub union: union,
+ }
+
+ pub fn union() -> Bar {
+ Bar {
+ union: 5
+ }
+ }
+}
+
+mod struct_union {
+ pub struct union {
+ pub union: u32
+ }
+ static union: union = union { union: 0 };
+
+ impl union {
+ pub fn union<'union>() -> &'union union {
+ &union
+ }
+ }
+ impl union {}
+ trait Foo {}
+ impl Foo for union {}
+ trait Bar {
+ fn bar() {}
+ }
+ impl Bar for union {}
+}
+
+mod union_union {
+ pub union union {
+ pub union: u32
+ }
+ const union: union = union { union: 0 };
+ impl union {
+ pub fn union() -> union {
+ union
+ }
+ }
+}
+
+mod trait_union {
+ pub trait union {
+ fn union() {}
+ }
+ impl union for () {}
+}
+
+macro_rules! ty {
+ ($ty:ty { $($field:ident:$field_ty:ty)* }) => {};
+}
+
+fn main() {
+ let union = union::union();
+ let _ = union.union;
+ let _ = struct_union::union::union().union;
+ let union = union_union::union::union();
+ let _ = unsafe { union.union };
+ <() as trait_union::union>::union();
+ ty!(union {});
+ ty!(union { union: union });
+}
diff --git a/tests/ui/union/unnamed-fields/restrict_anonymous_structs.rs b/tests/ui/union/unnamed-fields/restrict_anonymous_structs.rs
new file mode 100644
index 0000000000000..192bbba5a5b3a
--- /dev/null
+++ b/tests/ui/union/unnamed-fields/restrict_anonymous_structs.rs
@@ -0,0 +1,37 @@
+#![allow(incomplete_features)]
+#![feature(unnamed_fields)]
+
+struct F {
+ field: struct { field: u8 }, //~ ERROR anonymous structs are not allowed outside of unnamed struct or union fields
+ //~^ ERROR anonymous structs are unimplemented
+ _: struct { field: u8 },
+ //~^ ERROR anonymous structs are unimplemented
+}
+
+struct G {
+ _: (u8, u8), //~ ERROR unnamed fields can only have struct or union types
+}
+
+union H {
+ field: struct { field: u8 }, //~ ERROR anonymous structs are not allowed outside of unnamed struct or union fields
+ //~^ ERROR anonymous structs are unimplemented
+ _: struct { field: u8 },
+ //~^ ERROR anonymous structs are unimplemented
+}
+
+union I {
+ _: (u8, u8), //~ ERROR unnamed fields can only have struct or union types
+}
+
+enum K {
+ M {
+ _ : struct { field: u8 }, //~ ERROR anonymous structs are not allowed outside of unnamed struct or union fields
+ //~^ ERROR unnamed fields are not allowed outside of structs or unions
+ //~| ERROR anonymous structs are unimplemented
+ },
+ N {
+ _ : u8, //~ ERROR unnamed fields are not allowed outside of structs or unions
+ }
+}
+
+fn main() {}
diff --git a/tests/ui/union/unnamed-fields/restrict_anonymous_structs.stderr b/tests/ui/union/unnamed-fields/restrict_anonymous_structs.stderr
new file mode 100644
index 0000000000000..fd731766c01ff
--- /dev/null
+++ b/tests/ui/union/unnamed-fields/restrict_anonymous_structs.stderr
@@ -0,0 +1,78 @@
+error: anonymous structs are not allowed outside of unnamed struct or union fields
+ --> $DIR/restrict_anonymous_structs.rs:5:12
+ |
+LL | field: struct { field: u8 },
+ | ^^^^^^^^^^^^^^^^^^^^ anonymous struct declared here
+
+error: unnamed fields can only have struct or union types
+ --> $DIR/restrict_anonymous_structs.rs:12:5
+ |
+LL | _: (u8, u8),
+ | ^ -------- not a struct or union
+
+error: anonymous structs are not allowed outside of unnamed struct or union fields
+ --> $DIR/restrict_anonymous_structs.rs:16:12
+ |
+LL | field: struct { field: u8 },
+ | ^^^^^^^^^^^^^^^^^^^^ anonymous struct declared here
+
+error: unnamed fields can only have struct or union types
+ --> $DIR/restrict_anonymous_structs.rs:23:5
+ |
+LL | _: (u8, u8),
+ | ^ -------- not a struct or union
+
+error: unnamed fields are not allowed outside of structs or unions
+ --> $DIR/restrict_anonymous_structs.rs:28:9
+ |
+LL | _ : struct { field: u8 },
+ | -^^^^^^^^^^^^^^^^^^^^^^^
+ | |
+ | unnamed field declared here
+
+error: anonymous structs are not allowed outside of unnamed struct or union fields
+ --> $DIR/restrict_anonymous_structs.rs:28:13
+ |
+LL | _ : struct { field: u8 },
+ | ^^^^^^^^^^^^^^^^^^^^ anonymous struct declared here
+
+error: unnamed fields are not allowed outside of structs or unions
+ --> $DIR/restrict_anonymous_structs.rs:33:9
+ |
+LL | _ : u8,
+ | -^^^^^
+ | |
+ | unnamed field declared here
+
+error: anonymous structs are unimplemented
+ --> $DIR/restrict_anonymous_structs.rs:5:12
+ |
+LL | field: struct { field: u8 },
+ | ^^^^^^^^^^^^^^^^^^^^
+
+error: anonymous structs are unimplemented
+ --> $DIR/restrict_anonymous_structs.rs:7:8
+ |
+LL | _: struct { field: u8 },
+ | ^^^^^^^^^^^^^^^^^^^^
+
+error: anonymous structs are unimplemented
+ --> $DIR/restrict_anonymous_structs.rs:16:12
+ |
+LL | field: struct { field: u8 },
+ | ^^^^^^^^^^^^^^^^^^^^
+
+error: anonymous structs are unimplemented
+ --> $DIR/restrict_anonymous_structs.rs:18:8
+ |
+LL | _: struct { field: u8 },
+ | ^^^^^^^^^^^^^^^^^^^^
+
+error: anonymous structs are unimplemented
+ --> $DIR/restrict_anonymous_structs.rs:28:13
+ |
+LL | _ : struct { field: u8 },
+ | ^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to 12 previous errors
+
diff --git a/tests/ui/union/unnamed-fields/restrict_anonymous_unions.rs b/tests/ui/union/unnamed-fields/restrict_anonymous_unions.rs
new file mode 100644
index 0000000000000..c69266089bba5
--- /dev/null
+++ b/tests/ui/union/unnamed-fields/restrict_anonymous_unions.rs
@@ -0,0 +1,37 @@
+#![allow(incomplete_features)]
+#![feature(unnamed_fields)]
+
+struct F {
+ field: union { field: u8 }, //~ ERROR anonymous unions are not allowed outside of unnamed struct or union fields
+ //~^ ERROR anonymous unions are unimplemented
+ _: union { field: u8 },
+ //~^ ERROR anonymous unions are unimplemented
+}
+
+struct G {
+ _: (u8, u8), //~ ERROR unnamed fields can only have struct or union types
+}
+
+union H {
+ field: union { field: u8 }, //~ ERROR anonymous unions are not allowed outside of unnamed struct or union fields
+ //~^ ERROR anonymous unions are unimplemented
+ _: union { field: u8 },
+ //~^ ERROR anonymous unions are unimplemented
+}
+
+union I {
+ _: (u8, u8), //~ ERROR unnamed fields can only have struct or union types
+}
+
+enum K {
+ M {
+ _ : union { field: u8 }, //~ ERROR anonymous unions are not allowed outside of unnamed struct or union fields
+ //~^ ERROR unnamed fields are not allowed outside of structs or unions
+ //~| ERROR anonymous unions are unimplemented
+ },
+ N {
+ _ : u8, //~ ERROR unnamed fields are not allowed outside of structs or unions
+ }
+}
+
+fn main() {}
diff --git a/tests/ui/union/unnamed-fields/restrict_anonymous_unions.stderr b/tests/ui/union/unnamed-fields/restrict_anonymous_unions.stderr
new file mode 100644
index 0000000000000..c65cad775a90c
--- /dev/null
+++ b/tests/ui/union/unnamed-fields/restrict_anonymous_unions.stderr
@@ -0,0 +1,78 @@
+error: anonymous unions are not allowed outside of unnamed struct or union fields
+ --> $DIR/restrict_anonymous_unions.rs:5:12
+ |
+LL | field: union { field: u8 },
+ | ^^^^^^^^^^^^^^^^^^^ anonymous union declared here
+
+error: unnamed fields can only have struct or union types
+ --> $DIR/restrict_anonymous_unions.rs:12:5
+ |
+LL | _: (u8, u8),
+ | ^ -------- not a struct or union
+
+error: anonymous unions are not allowed outside of unnamed struct or union fields
+ --> $DIR/restrict_anonymous_unions.rs:16:12
+ |
+LL | field: union { field: u8 },
+ | ^^^^^^^^^^^^^^^^^^^ anonymous union declared here
+
+error: unnamed fields can only have struct or union types
+ --> $DIR/restrict_anonymous_unions.rs:23:5
+ |
+LL | _: (u8, u8),
+ | ^ -------- not a struct or union
+
+error: unnamed fields are not allowed outside of structs or unions
+ --> $DIR/restrict_anonymous_unions.rs:28:9
+ |
+LL | _ : union { field: u8 },
+ | -^^^^^^^^^^^^^^^^^^^^^^
+ | |
+ | unnamed field declared here
+
+error: anonymous unions are not allowed outside of unnamed struct or union fields
+ --> $DIR/restrict_anonymous_unions.rs:28:13
+ |
+LL | _ : union { field: u8 },
+ | ^^^^^^^^^^^^^^^^^^^ anonymous union declared here
+
+error: unnamed fields are not allowed outside of structs or unions
+ --> $DIR/restrict_anonymous_unions.rs:33:9
+ |
+LL | _ : u8,
+ | -^^^^^
+ | |
+ | unnamed field declared here
+
+error: anonymous unions are unimplemented
+ --> $DIR/restrict_anonymous_unions.rs:5:12
+ |
+LL | field: union { field: u8 },
+ | ^^^^^^^^^^^^^^^^^^^
+
+error: anonymous unions are unimplemented
+ --> $DIR/restrict_anonymous_unions.rs:7:8
+ |
+LL | _: union { field: u8 },
+ | ^^^^^^^^^^^^^^^^^^^
+
+error: anonymous unions are unimplemented
+ --> $DIR/restrict_anonymous_unions.rs:16:12
+ |
+LL | field: union { field: u8 },
+ | ^^^^^^^^^^^^^^^^^^^
+
+error: anonymous unions are unimplemented
+ --> $DIR/restrict_anonymous_unions.rs:18:8
+ |
+LL | _: union { field: u8 },
+ | ^^^^^^^^^^^^^^^^^^^
+
+error: anonymous unions are unimplemented
+ --> $DIR/restrict_anonymous_unions.rs:28:13
+ |
+LL | _ : union { field: u8 },
+ | ^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to 12 previous errors
+