about summary refs log tree commit diff
diff options
context:
space:
mode:
authorFrank King <frankking1729@gmail.com>2024-01-04 21:53:06 +0800
committerFrank King <frankking1729@gmail.com>2024-02-12 12:47:23 +0800
commit879a1e571305a0fb35ef6cc4297f9230fca95be5 (patch)
tree232e06c6b3eefcc81f10f825a430f04a7325c498
parent084ce5bdb5f7dc1c725f6770a8de281165ba3b0a (diff)
downloadrust-879a1e571305a0fb35ef6cc4297f9230fca95be5.tar.gz
rust-879a1e571305a0fb35ef6cc4297f9230fca95be5.zip
Lower anonymous structs or unions to HIR
-rw-r--r--compiler/rustc_ast/src/ast.rs4
-rw-r--r--compiler/rustc_ast/src/mut_visit.rs3
-rw-r--r--compiler/rustc_ast/src/visit.rs2
-rw-r--r--compiler/rustc_ast_lowering/src/item.rs5
-rw-r--r--compiler/rustc_ast_lowering/src/lib.rs54
-rw-r--r--compiler/rustc_ast_passes/src/ast_validation.rs2
-rw-r--r--compiler/rustc_ast_pretty/src/pprust/state.rs4
-rw-r--r--compiler/rustc_hir/src/def.rs2
-rw-r--r--compiler/rustc_hir/src/definitions.rs6
-rw-r--r--compiler/rustc_hir/src/hir.rs2
-rw-r--r--compiler/rustc_hir/src/intravisit.rs3
-rw-r--r--compiler/rustc_hir_analysis/src/astconv/mod.rs13
-rw-r--r--compiler/rustc_hir_analysis/src/collect.rs82
-rw-r--r--compiler/rustc_hir_pretty/src/lib.rs37
-rw-r--r--compiler/rustc_metadata/src/rmeta/decoder.rs3
-rw-r--r--compiler/rustc_middle/src/hir/map/mod.rs7
-rw-r--r--compiler/rustc_middle/src/ty/adt.rs17
-rw-r--r--compiler/rustc_middle/src/ty/context.rs10
-rw-r--r--compiler/rustc_middle/src/ty/mod.rs19
-rw-r--r--compiler/rustc_parse/src/parser/ty.rs5
-rw-r--r--compiler/rustc_passes/src/hir_stats.rs1
-rw-r--r--compiler/rustc_span/src/symbol.rs1
-rw-r--r--compiler/rustc_symbol_mangling/src/typeid/typeid_itanium_cxx_abi.rs3
-rw-r--r--compiler/rustc_symbol_mangling/src/v0.rs3
-rw-r--r--compiler/rustc_trait_selection/src/solve/assembly/structural_traits.rs5
-rw-r--r--compiler/rustc_trait_selection/src/traits/select/mod.rs10
-rw-r--r--compiler/rustc_ty_utils/src/representability.rs3
-rw-r--r--src/librustdoc/clean/mod.rs10
-rw-r--r--src/librustdoc/clean/types.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/dereference.rs1
-rw-r--r--src/tools/clippy/clippy_utils/src/hir_utils.rs3
-rw-r--r--src/tools/rustfmt/src/types.rs4
-rw-r--r--tests/ui/feature-gates/feature-gate-unnamed_fields.rs2
-rw-r--r--tests/ui/feature-gates/feature-gate-unnamed_fields.stderr34
-rw-r--r--tests/ui/union/unnamed-fields/restrict_anonymous_structs.rs5
-rw-r--r--tests/ui/union/unnamed-fields/restrict_anonymous_structs.stderr44
-rw-r--r--tests/ui/union/unnamed-fields/restrict_anonymous_unions.rs5
-rw-r--r--tests/ui/union/unnamed-fields/restrict_anonymous_unions.stderr44
38 files changed, 288 insertions, 174 deletions
diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs
index 296a570de6b..e41228bd501 100644
--- a/compiler/rustc_ast/src/ast.rs
+++ b/compiler/rustc_ast/src/ast.rs
@@ -2107,9 +2107,9 @@ pub enum TyKind {
     /// A tuple (`(A, B, C, D,...)`).
     Tup(ThinVec<P<Ty>>),
     /// An anonymous struct type i.e. `struct { foo: Type }`
-    AnonStruct(ThinVec<FieldDef>),
+    AnonStruct(NodeId, ThinVec<FieldDef>),
     /// An anonymous union type i.e. `union { bar: Type }`
-    AnonUnion(ThinVec<FieldDef>),
+    AnonUnion(NodeId, ThinVec<FieldDef>),
     /// A path (`module::module::...::Type`), optionally
     /// "qualified", e.g., `<Vec<T> as SomeTrait>::SomeType`.
     ///
diff --git a/compiler/rustc_ast/src/mut_visit.rs b/compiler/rustc_ast/src/mut_visit.rs
index 90677151d25..d482ada170e 100644
--- a/compiler/rustc_ast/src/mut_visit.rs
+++ b/compiler/rustc_ast/src/mut_visit.rs
@@ -514,7 +514,8 @@ pub fn noop_visit_ty<T: MutVisitor>(ty: &mut P<Ty>, 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) => {
+        TyKind::AnonStruct(id, fields) | TyKind::AnonUnion(id, fields) => {
+            vis.visit_id(id);
             fields.flat_map_in_place(|field| vis.flat_map_field_def(field));
         }
     }
diff --git a/compiler/rustc_ast/src/visit.rs b/compiler/rustc_ast/src/visit.rs
index 8d084ee29a7..4aaaa0ba424 100644
--- a/compiler/rustc_ast/src/visit.rs
+++ b/compiler/rustc_ast/src/visit.rs
@@ -450,7 +450,7 @@ 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, ..) => {
+        TyKind::AnonStruct(_, ref fields) | TyKind::AnonUnion(_, ref fields) => {
             walk_list!(visitor, visit_field_def, fields)
         }
     }
diff --git a/compiler/rustc_ast_lowering/src/item.rs b/compiler/rustc_ast_lowering/src/item.rs
index fb52f9cf58f..933372fae4e 100644
--- a/compiler/rustc_ast_lowering/src/item.rs
+++ b/compiler/rustc_ast_lowering/src/item.rs
@@ -720,7 +720,10 @@ impl<'hir> LoweringContext<'_, 'hir> {
         }
     }
 
-    fn lower_field_def(&mut self, (index, f): (usize, &FieldDef)) -> hir::FieldDef<'hir> {
+    pub(super) fn lower_field_def(
+        &mut self,
+        (index, f): (usize, &FieldDef),
+    ) -> hir::FieldDef<'hir> {
         let ty = if let TyKind::Path(qself, path) = &f.ty.kind {
             let t = self.lower_path_ty(
                 &f.ty,
diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs
index 5f7439060b3..7a071242256 100644
--- a/compiler/rustc_ast_lowering/src/lib.rs
+++ b/compiler/rustc_ast_lowering/src/lib.rs
@@ -1288,17 +1288,49 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
             TyKind::Err => {
                 hir::TyKind::Err(self.dcx().span_delayed_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.dcx().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.dcx().span_err(t.span, "anonymous unions are unimplemented"))
+            // Lower the anonymous structs or unions in a nested lowering context.
+            //
+            // ```
+            // struct Foo {
+            //     _: union {
+            // //     ^__________________  <-- within the nested lowering context,
+            //         /* fields */ //   |     we lower all fields defined into an
+            //     } //                  |     owner node of struct or union item
+            // //  ^_____________________|
+            // }
+            // ```
+            TyKind::AnonStruct(def_node_id, fields) | TyKind::AnonUnion(def_node_id, fields) => {
+                let (def_kind, item_kind): (DefKind, fn(_, _) -> _) = match t.kind {
+                    TyKind::AnonStruct(..) => (DefKind::Struct, hir::ItemKind::Struct),
+                    TyKind::AnonUnion(..) => (DefKind::Union, hir::ItemKind::Union),
+                    _ => unreachable!(),
+                };
+                let def_id = self.create_def(
+                    self.current_hir_id_owner.def_id,
+                    *def_node_id,
+                    kw::Empty,
+                    def_kind,
+                    t.span,
+                );
+                debug!(?def_id);
+                let owner_id = hir::OwnerId { def_id };
+                self.with_hir_id_owner(*def_node_id, |this| {
+                    let fields = this.arena.alloc_from_iter(
+                        fields.iter().enumerate().map(|f| this.lower_field_def(f)),
+                    );
+                    let span = t.span;
+                    let variant_data = hir::VariantData::Struct(fields, false);
+                    // FIXME: capture the generics from the outer adt.
+                    let generics = hir::Generics::empty();
+                    hir::OwnerNode::Item(this.arena.alloc(hir::Item {
+                        ident: Ident::new(kw::Empty, span),
+                        owner_id,
+                        kind: item_kind(variant_data, generics),
+                        span: this.lower_span(span),
+                        vis_span: this.lower_span(span.shrink_to_lo()),
+                    }))
+                });
+                hir::TyKind::AnonAdt(hir::ItemId { owner_id })
             }
             TyKind::Slice(ty) => hir::TyKind::Slice(self.lower_ty(ty, itctx)),
             TyKind::Ptr(mt) => hir::TyKind::Ptr(self.lower_mt(mt, itctx)),
diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs
index 9ea5d1ed5fa..86597d20c19 100644
--- a/compiler/rustc_ast_passes/src/ast_validation.rs
+++ b/compiler/rustc_ast_passes/src/ast_validation.rs
@@ -219,7 +219,7 @@ impl<'a> AstValidator<'a> {
                     }
                 }
             }
-            TyKind::AnonStruct(ref fields, ..) | TyKind::AnonUnion(ref fields, ..) => {
+            TyKind::AnonStruct(_, ref fields) | TyKind::AnonUnion(_, ref fields) => {
                 walk_list!(self, visit_field_def, fields)
             }
             _ => visit::walk_ty(self, t),
diff --git a/compiler/rustc_ast_pretty/src/pprust/state.rs b/compiler/rustc_ast_pretty/src/pprust/state.rs
index 731232bce65..cda746894e8 100644
--- a/compiler/rustc_ast_pretty/src/pprust/state.rs
+++ b/compiler/rustc_ast_pretty/src/pprust/state.rs
@@ -1003,11 +1003,11 @@ impl<'a> State<'a> {
                 }
                 self.pclose();
             }
-            ast::TyKind::AnonStruct(fields) => {
+            ast::TyKind::AnonStruct(_, fields) => {
                 self.head("struct");
                 self.print_record_struct_body(fields, ty.span);
             }
-            ast::TyKind::AnonUnion(fields) => {
+            ast::TyKind::AnonUnion(_, fields) => {
                 self.head("union");
                 self.print_record_struct_body(fields, ty.span);
             }
diff --git a/compiler/rustc_hir/src/def.rs b/compiler/rustc_hir/src/def.rs
index 81ec7ddb629..f08ab4bfc09 100644
--- a/compiler/rustc_hir/src/def.rs
+++ b/compiler/rustc_hir/src/def.rs
@@ -8,6 +8,7 @@ use rustc_data_structures::unord::UnordMap;
 use rustc_macros::HashStable_Generic;
 use rustc_span::def_id::{DefId, LocalDefId};
 use rustc_span::hygiene::MacroKind;
+use rustc_span::symbol::kw;
 use rustc_span::Symbol;
 
 use std::array::IntoIter;
@@ -225,6 +226,7 @@ impl DefKind {
 
     pub fn def_path_data(self, name: Symbol) -> DefPathData {
         match self {
+            DefKind::Struct | DefKind::Union if name == kw::Empty => DefPathData::AnonAdt,
             DefKind::Mod
             | DefKind::Struct
             | DefKind::Union
diff --git a/compiler/rustc_hir/src/definitions.rs b/compiler/rustc_hir/src/definitions.rs
index 9fb1fc19bf4..b81ad8b1946 100644
--- a/compiler/rustc_hir/src/definitions.rs
+++ b/compiler/rustc_hir/src/definitions.rs
@@ -287,6 +287,8 @@ pub enum DefPathData {
     /// An existential `impl Trait` type node.
     /// Argument position `impl Trait` have a `TypeNs` with their pretty-printed name.
     OpaqueTy,
+    /// An anonymous struct or union type i.e. `struct { foo: Type }` or `union { bar: Type }`
+    AnonAdt,
 }
 
 impl Definitions {
@@ -409,8 +411,9 @@ impl DefPathData {
         match *self {
             TypeNs(name) if name == kw::Empty => None,
             TypeNs(name) | ValueNs(name) | MacroNs(name) | LifetimeNs(name) => Some(name),
+
             Impl | ForeignMod | CrateRoot | Use | GlobalAsm | Closure | Ctor | AnonConst
-            | OpaqueTy => None,
+            | OpaqueTy | AnonAdt => None,
         }
     }
 
@@ -431,6 +434,7 @@ impl DefPathData {
             Ctor => DefPathDataName::Anon { namespace: sym::constructor },
             AnonConst => DefPathDataName::Anon { namespace: sym::constant },
             OpaqueTy => DefPathDataName::Anon { namespace: sym::opaque },
+            AnonAdt => DefPathDataName::Anon { namespace: sym::anon_adt },
         }
     }
 }
diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs
index a7a1c69b9be..00b2f984483 100644
--- a/compiler/rustc_hir/src/hir.rs
+++ b/compiler/rustc_hir/src/hir.rs
@@ -2587,6 +2587,8 @@ pub enum TyKind<'hir> {
     Never,
     /// A tuple (`(A, B, C, D, ...)`).
     Tup(&'hir [Ty<'hir>]),
+    /// An anonymous struct or union type i.e. `struct { foo: Type }` or `union { foo: Type }`
+    AnonAdt(ItemId),
     /// A path to a type definition (`module::module::...::Type`), or an
     /// associated type (e.g., `<Vec<T> as Trait>::Type` or `<T>::Target`).
     ///
diff --git a/compiler/rustc_hir/src/intravisit.rs b/compiler/rustc_hir/src/intravisit.rs
index 52e1109ff92..e9337dd3586 100644
--- a/compiler/rustc_hir/src/intravisit.rs
+++ b/compiler/rustc_hir/src/intravisit.rs
@@ -852,6 +852,9 @@ pub fn walk_ty<'v, V: Visitor<'v>>(visitor: &mut V, typ: &'v Ty<'v>) {
         }
         TyKind::Typeof(ref expression) => visitor.visit_anon_const(expression),
         TyKind::Infer | TyKind::InferDelegation(..) | TyKind::Err(_) => {}
+        TyKind::AnonAdt(item_id) => {
+            visitor.visit_nested_item(item_id);
+        }
     }
 }
 
diff --git a/compiler/rustc_hir_analysis/src/astconv/mod.rs b/compiler/rustc_hir_analysis/src/astconv/mod.rs
index 1ae3ebaebbb..a643614d33d 100644
--- a/compiler/rustc_hir_analysis/src/astconv/mod.rs
+++ b/compiler/rustc_hir_analysis/src/astconv/mod.rs
@@ -2457,6 +2457,19 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
             hir::TyKind::Tup(fields) => {
                 Ty::new_tup_from_iter(tcx, fields.iter().map(|t| self.ast_ty_to_ty(t)))
             }
+            hir::TyKind::AnonAdt(item_id) => {
+                let did = item_id.owner_id.def_id;
+                let adt_def = tcx.adt_def(did);
+                let generics = tcx.generics_of(did);
+
+                debug!("ast_ty_to_ty_inner(AnonAdt): generics={:?}", generics);
+                let args = ty::GenericArgs::for_item(tcx, did.to_def_id(), |param, _| {
+                    tcx.mk_param_from_def(param)
+                });
+                debug!("ast_ty_to_ty_inner(AnonAdt): args={:?}", args);
+
+                Ty::new_adt(tcx, adt_def, tcx.mk_args(args))
+            }
             hir::TyKind::BareFn(bf) => {
                 require_c_abi_if_c_variadic(tcx, bf.decl, bf.abi, ast_ty.span);
 
diff --git a/compiler/rustc_hir_analysis/src/collect.rs b/compiler/rustc_hir_analysis/src/collect.rs
index f458ff01c10..768b07069c8 100644
--- a/compiler/rustc_hir_analysis/src/collect.rs
+++ b/compiler/rustc_hir_analysis/src/collect.rs
@@ -789,6 +789,65 @@ fn convert_enum_variant_types(tcx: TyCtxt<'_>, def_id: DefId) {
     }
 }
 
+/*
+/// In a type definition, we check that unnamed field names are distinct.
+fn check_unnamed_fields_defn<'tcx>(tcx: TyCtxt<'tcx>, item: &hir::Item<'tcx>) {
+    let mut seen_fields: FxHashMap<Ident, Option<Span>> = Default::default();
+    fn check_fields_anon_adt_defn<'tcx>(tcx: TyCtxt<'tcx>, item: &hir::Item<'tcx>, seen_fields: &mut FxHashMap<Ident, Option<Span>>) {
+        let fields = match &item.kind {
+            hir::ItemKind::Struct(fields, _) | hir::ItemKind::Union(fields, _) => fields,
+            _ => return,
+        };
+        for field in fields.fields() {
+            if field.ident.name == kw::Underscore {
+                if let hir::TyKind::AnonAdt(item_id) = field.ty.kind() {
+                    let item = tcx.hir().item(item_id);
+                    check_fields_anon_adt_defn(tcx, item, &mut *seen_fields);
+                } else {
+                    let field_ty = match tcx.type_of(field.def_id).instantiate_identity().ty_adt_def() {
+                        Some(adt_ty) => adt_ty,
+                        None => {
+                            tcx.sess.emit_err(err);
+                            return;
+                        }
+                    };
+                    if let Some(def_id) = field_ty.did().as_local() {
+                        let item = tcx.hir().item(hir::ItemId { owner_id: hir::OwnerId { def_id }});
+                        check_fields_anon_adt_defn(tcx, item, &mut *seen_fields);
+                    }
+                }
+                field_ty.flags()
+                let inner_adt_def = field_ty.ty_adt_def().expect("expect an adt");
+                check_fields_anon_adt_defn(tcx, adt_def, &mut *seen_fields);
+            } else {
+                let span = field.did.as_local().map(|did| {
+                    let hir_id = tcx.hir().local_def_id_to_hir_id(did);
+                    tcx.hir().span(hir_id)
+                });
+                match seen_fields.get(&ident.normalize_to_macros_2_0()).cloned() {
+                    Some(Some(prev_span)) => {
+                        tcx.sess.emit_err(errors::FieldAlreadyDeclared {
+                            field_name: ident,
+                            span: f.span,
+                            prev_span,
+                        });
+                    }
+                    Some(None) => {
+                        tcx.sess.emit_err(errors::FieldAlreadyDeclared {
+                            field_name: f.ident,
+                            span: f.span,
+                            prev_span,
+                        });
+                    }
+                    None =>
+                        seen_fields.insert(f.ident.normalize_to_macros_2_0(), f.span);
+                }
+            }
+        }
+    }
+}
+ */
+
 fn convert_variant(
     tcx: TyCtxt<'_>,
     variant_did: Option<LocalDefId>,
@@ -798,11 +857,17 @@ fn convert_variant(
     adt_kind: ty::AdtKind,
     parent_did: LocalDefId,
 ) -> ty::VariantDef {
+    let mut has_unnamed_fields = false;
     let mut seen_fields: FxHashMap<Ident, Span> = Default::default();
     let fields = def
         .fields()
         .iter()
-        .map(|f| {
+        .inspect(|f| {
+            // Skip the unnamed field here, we will check it later.
+            if f.ident.name == kw::Underscore {
+                has_unnamed_fields = true;
+                return;
+            }
             let dup_span = seen_fields.get(&f.ident.normalize_to_macros_2_0()).cloned();
             if let Some(prev_span) = dup_span {
                 tcx.dcx().emit_err(errors::FieldAlreadyDeclared {
@@ -813,12 +878,11 @@ fn convert_variant(
             } else {
                 seen_fields.insert(f.ident.normalize_to_macros_2_0(), f.span);
             }
-
-            ty::FieldDef {
-                did: f.def_id.to_def_id(),
-                name: f.ident.name,
-                vis: tcx.visibility(f.def_id),
-            }
+        })
+        .map(|f| ty::FieldDef {
+            did: f.def_id.to_def_id(),
+            name: f.ident.name,
+            vis: tcx.visibility(f.def_id),
         })
         .collect();
     let recovered = match def {
@@ -837,6 +901,7 @@ fn convert_variant(
         adt_kind == AdtKind::Struct && tcx.has_attr(parent_did, sym::non_exhaustive)
             || variant_did
                 .is_some_and(|variant_did| tcx.has_attr(variant_did, sym::non_exhaustive)),
+        has_unnamed_fields,
     )
 }
 
@@ -847,6 +912,7 @@ fn adt_def(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::AdtDef<'_> {
         bug!("expected ADT to be an item");
     };
 
+    let is_anonymous = item.ident.name == kw::Empty;
     let repr = tcx.repr_options_of_def(def_id.to_def_id());
     let (kind, variants) = match &item.kind {
         ItemKind::Enum(def, _) => {
@@ -897,7 +963,7 @@ fn adt_def(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::AdtDef<'_> {
         }
         _ => bug!("{:?} is not an ADT", item.owner_id.def_id),
     };
-    tcx.mk_adt_def(def_id.to_def_id(), kind, variants, repr)
+    tcx.mk_adt_def(def_id.to_def_id(), kind, variants, repr, is_anonymous)
 }
 
 fn trait_def(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::TraitDef {
diff --git a/compiler/rustc_hir_pretty/src/lib.rs b/compiler/rustc_hir_pretty/src/lib.rs
index b90fa03a3dc..8f8f747339b 100644
--- a/compiler/rustc_hir_pretty/src/lib.rs
+++ b/compiler/rustc_hir_pretty/src/lib.rs
@@ -328,6 +328,7 @@ impl<'a> State<'a> {
             hir::TyKind::Infer | hir::TyKind::InferDelegation(..) => {
                 self.word("_");
             }
+            hir::TyKind::AnonAdt(..) => self.word("/* anonymous adt */"),
         }
         self.end()
     }
@@ -728,26 +729,30 @@ impl<'a> State<'a> {
             }
             hir::VariantData::Struct { .. } => {
                 self.print_where_clause(generics);
-                self.nbsp();
-                self.bopen();
-                self.hardbreak_if_not_bol();
-
-                for field in struct_def.fields() {
-                    self.hardbreak_if_not_bol();
-                    self.maybe_print_comment(field.span.lo());
-                    self.print_outer_attributes(self.attrs(field.hir_id));
-                    self.print_ident(field.ident);
-                    self.word_nbsp(":");
-                    self.print_type(field.ty);
-                    self.word(",");
-                }
-
-                self.bclose(span)
+                self.print_variant_struct(span, struct_def.fields())
             }
         }
     }
 
-    fn print_variant(&mut self, v: &hir::Variant<'_>) {
+    fn print_variant_struct(&mut self, span: rustc_span::Span, fields: &[hir::FieldDef<'_>]) {
+        self.nbsp();
+        self.bopen();
+        self.hardbreak_if_not_bol();
+
+        for field in fields {
+            self.hardbreak_if_not_bol();
+            self.maybe_print_comment(field.span.lo());
+            self.print_outer_attributes(self.attrs(field.hir_id));
+            self.print_ident(field.ident);
+            self.word_nbsp(":");
+            self.print_type(field.ty);
+            self.word(",");
+        }
+
+        self.bclose(span)
+    }
+
+    pub fn print_variant(&mut self, v: &hir::Variant<'_>) {
         self.head("");
         let generics = hir::Generics::empty();
         self.print_struct(&v.data, generics, v.ident.name, v.span, false);
diff --git a/compiler/rustc_metadata/src/rmeta/decoder.rs b/compiler/rustc_metadata/src/rmeta/decoder.rs
index 11cb1bb6d9e..72e9744295b 100644
--- a/compiler/rustc_metadata/src/rmeta/decoder.rs
+++ b/compiler/rustc_metadata/src/rmeta/decoder.rs
@@ -1084,6 +1084,8 @@ impl<'a, 'tcx> CrateMetadataRef<'a> {
                 parent_did,
                 false,
                 data.is_non_exhaustive,
+                // FIXME: unnamed fields in crate metadata is unimplemented yet.
+                false,
             ),
         )
     }
@@ -1126,6 +1128,7 @@ impl<'a, 'tcx> CrateMetadataRef<'a> {
             adt_kind,
             variants.into_iter().map(|(_, variant)| variant).collect(),
             repr,
+            false,
         )
     }
 
diff --git a/compiler/rustc_middle/src/hir/map/mod.rs b/compiler/rustc_middle/src/hir/map/mod.rs
index 50817dd0a80..8e1cb6a514f 100644
--- a/compiler/rustc_middle/src/hir/map/mod.rs
+++ b/compiler/rustc_middle/src/hir/map/mod.rs
@@ -756,6 +756,13 @@ impl<'hir> Map<'hir> {
         }
     }
 
+    pub fn expect_field(self, id: HirId) -> &'hir FieldDef<'hir> {
+        match self.tcx.hir_node(id) {
+            Node::Field(field) => field,
+            _ => bug!("expected field, found {}", self.node_to_string(id)),
+        }
+    }
+
     pub fn expect_foreign_item(self, id: OwnerId) -> &'hir ForeignItem<'hir> {
         match self.tcx.hir_owner_node(id) {
             OwnerNode::ForeignItem(item) => item,
diff --git a/compiler/rustc_middle/src/ty/adt.rs b/compiler/rustc_middle/src/ty/adt.rs
index 685c3e87dac..854046c9ceb 100644
--- a/compiler/rustc_middle/src/ty/adt.rs
+++ b/compiler/rustc_middle/src/ty/adt.rs
@@ -50,6 +50,8 @@ bitflags! {
         const IS_VARIANT_LIST_NON_EXHAUSTIVE = 1 << 8;
         /// Indicates whether the type is `UnsafeCell`.
         const IS_UNSAFE_CELL              = 1 << 9;
+        /// Indicates whether the type is anonymous.
+        const IS_ANONYMOUS                = 1 << 10;
     }
 }
 rustc_data_structures::external_bitflags_debug! { AdtFlags }
@@ -233,8 +235,12 @@ impl AdtDefData {
         kind: AdtKind,
         variants: IndexVec<VariantIdx, VariantDef>,
         repr: ReprOptions,
+        is_anonymous: bool,
     ) -> Self {
-        debug!("AdtDef::new({:?}, {:?}, {:?}, {:?})", did, kind, variants, repr);
+        debug!(
+            "AdtDef::new({:?}, {:?}, {:?}, {:?}, {:?})",
+            did, kind, variants, repr, is_anonymous
+        );
         let mut flags = AdtFlags::NO_ADT_FLAGS;
 
         if kind == AdtKind::Enum && tcx.has_attr(did, sym::non_exhaustive) {
@@ -267,6 +273,9 @@ impl AdtDefData {
         if Some(did) == tcx.lang_items().unsafe_cell_type() {
             flags |= AdtFlags::IS_UNSAFE_CELL;
         }
+        if is_anonymous {
+            flags |= AdtFlags::IS_ANONYMOUS;
+        }
 
         AdtDefData { did, variants, flags, repr }
     }
@@ -365,6 +374,12 @@ impl<'tcx> AdtDef<'tcx> {
         self.flags().contains(AdtFlags::IS_MANUALLY_DROP)
     }
 
+    /// Returns `true` if this is an anonymous adt
+    #[inline]
+    pub fn is_anonymous(self) -> bool {
+        self.flags().contains(AdtFlags::IS_ANONYMOUS)
+    }
+
     /// Returns `true` if this type has a destructor.
     pub fn has_dtor(self, tcx: TyCtxt<'tcx>) -> bool {
         self.destructor(tcx).is_some()
diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs
index b747f0a4fb6..9a0eea6592f 100644
--- a/compiler/rustc_middle/src/ty/context.rs
+++ b/compiler/rustc_middle/src/ty/context.rs
@@ -715,8 +715,16 @@ impl<'tcx> TyCtxt<'tcx> {
         kind: AdtKind,
         variants: IndexVec<VariantIdx, ty::VariantDef>,
         repr: ReprOptions,
+        is_anonymous: bool,
     ) -> ty::AdtDef<'tcx> {
-        self.mk_adt_def_from_data(ty::AdtDefData::new(self, did, kind, variants, repr))
+        self.mk_adt_def_from_data(ty::AdtDefData::new(
+            self,
+            did,
+            kind,
+            variants,
+            repr,
+            is_anonymous,
+        ))
     }
 
     /// Allocates a read-only byte or string literal for `mir::interpret`.
diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs
index c9137f374a2..3cbe8f287e1 100644
--- a/compiler/rustc_middle/src/ty/mod.rs
+++ b/compiler/rustc_middle/src/ty/mod.rs
@@ -1130,6 +1130,8 @@ bitflags! {
         /// Indicates whether this variant was obtained as part of recovering from
         /// a syntactic error. May be incomplete or bogus.
         const IS_RECOVERED = 1 << 1;
+        /// Indicates whether this variant has unnamed fields.
+        const HAS_UNNAMED_FIELDS = 1 << 2;
     }
 }
 rustc_data_structures::external_bitflags_debug! { VariantFlags }
@@ -1143,7 +1145,7 @@ pub struct VariantDef {
     /// `DefId` that identifies the variant's constructor.
     /// If this variant is a struct variant, then this is `None`.
     pub ctor: Option<(CtorKind, DefId)>,
-    /// Variant or struct name.
+    /// Variant or struct name, maybe empty for anonymous adt (struct or union).
     pub name: Symbol,
     /// Discriminant of this variant.
     pub discr: VariantDiscr,
@@ -1180,11 +1182,12 @@ impl VariantDef {
         parent_did: DefId,
         recovered: bool,
         is_field_list_non_exhaustive: bool,
+        has_unnamed_fields: bool,
     ) -> Self {
         debug!(
             "VariantDef::new(name = {:?}, variant_did = {:?}, ctor = {:?}, discr = {:?},
-             fields = {:?}, adt_kind = {:?}, parent_did = {:?})",
-            name, variant_did, ctor, discr, fields, adt_kind, parent_did,
+             fields = {:?}, adt_kind = {:?}, parent_did = {:?}, has_unnamed_fields = {:?})",
+            name, variant_did, ctor, discr, fields, adt_kind, parent_did, has_unnamed_fields,
         );
 
         let mut flags = VariantFlags::NO_VARIANT_FLAGS;
@@ -1196,6 +1199,10 @@ impl VariantDef {
             flags |= VariantFlags::IS_RECOVERED;
         }
 
+        if has_unnamed_fields {
+            flags |= VariantFlags::HAS_UNNAMED_FIELDS;
+        }
+
         VariantDef { def_id: variant_did.unwrap_or(parent_did), ctor, name, discr, fields, flags }
     }
 
@@ -1211,6 +1218,12 @@ impl VariantDef {
         self.flags.intersects(VariantFlags::IS_RECOVERED)
     }
 
+    /// Does this variant contains unnamed fields
+    #[inline]
+    pub fn has_unnamed_fields(&self) -> bool {
+        self.flags.intersects(VariantFlags::HAS_UNNAMED_FIELDS)
+    }
+
     /// Computes the `Ident` of this variant by looking up the `Span`
     pub fn ident(&self, tcx: TyCtxt<'_>) -> Ident {
         Ident::new(self.name, tcx.def_ident_span(self.def_id).unwrap())
diff --git a/compiler/rustc_parse/src/parser/ty.rs b/compiler/rustc_parse/src/parser/ty.rs
index 5fe54a536a7..157fb9e505a 100644
--- a/compiler/rustc_parse/src/parser/ty.rs
+++ b/compiler/rustc_parse/src/parser/ty.rs
@@ -396,8 +396,9 @@ impl<'a> Parser<'a> {
             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) };
+        let id = ast::DUMMY_NODE_ID;
+        let kind =
+            if is_union { TyKind::AnonUnion(id, fields) } else { TyKind::AnonStruct(id, fields) };
         Ok(self.mk_ty(span, kind))
     }
 
diff --git a/compiler/rustc_passes/src/hir_stats.rs b/compiler/rustc_passes/src/hir_stats.rs
index e94d8c4c932..d02e86dd456 100644
--- a/compiler/rustc_passes/src/hir_stats.rs
+++ b/compiler/rustc_passes/src/hir_stats.rs
@@ -345,6 +345,7 @@ impl<'v> hir_visit::Visitor<'v> for StatCollector<'v> {
                 BareFn,
                 Never,
                 Tup,
+                AnonAdt,
                 Path,
                 OpaqueDef,
                 TraitObject,
diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs
index aa912c93c08..c2d02665ef5 100644
--- a/compiler/rustc_span/src/symbol.rs
+++ b/compiler/rustc_span/src/symbol.rs
@@ -382,6 +382,7 @@ symbols! {
         and,
         and_then,
         anon,
+        anon_adt,
         anonymous_lifetime_in_impl_trait,
         any,
         append_const_msg,
diff --git a/compiler/rustc_symbol_mangling/src/typeid/typeid_itanium_cxx_abi.rs b/compiler/rustc_symbol_mangling/src/typeid/typeid_itanium_cxx_abi.rs
index 9d1b92e1068..011d52bc65b 100644
--- a/compiler/rustc_symbol_mangling/src/typeid/typeid_itanium_cxx_abi.rs
+++ b/compiler/rustc_symbol_mangling/src/typeid/typeid_itanium_cxx_abi.rs
@@ -387,7 +387,8 @@ fn encode_ty_name(tcx: TyCtxt<'_>, def_id: DefId) -> String {
             | hir::definitions::DefPathData::Use
             | hir::definitions::DefPathData::GlobalAsm
             | hir::definitions::DefPathData::MacroNs(..)
-            | hir::definitions::DefPathData::LifetimeNs(..) => {
+            | hir::definitions::DefPathData::LifetimeNs(..)
+            | hir::definitions::DefPathData::AnonAdt => {
                 bug!("encode_ty_name: unexpected `{:?}`", disambiguated_data.data);
             }
         });
diff --git a/compiler/rustc_symbol_mangling/src/v0.rs b/compiler/rustc_symbol_mangling/src/v0.rs
index 530221555c5..ce065efb4c6 100644
--- a/compiler/rustc_symbol_mangling/src/v0.rs
+++ b/compiler/rustc_symbol_mangling/src/v0.rs
@@ -748,7 +748,8 @@ impl<'tcx> Printer<'tcx> for SymbolMangler<'tcx> {
             | DefPathData::GlobalAsm
             | DefPathData::Impl
             | DefPathData::MacroNs(_)
-            | DefPathData::LifetimeNs(_) => {
+            | DefPathData::LifetimeNs(_)
+            | DefPathData::AnonAdt => {
                 bug!("symbol_names: unexpected DefPathData: {:?}", disambiguated_data.data)
             }
         };
diff --git a/compiler/rustc_trait_selection/src/solve/assembly/structural_traits.rs b/compiler/rustc_trait_selection/src/solve/assembly/structural_traits.rs
index 819b070cf8b..b320ded40a7 100644
--- a/compiler/rustc_trait_selection/src/solve/assembly/structural_traits.rs
+++ b/compiler/rustc_trait_selection/src/solve/assembly/structural_traits.rs
@@ -181,6 +181,11 @@ pub(in crate::solve) fn instantiate_constituent_tys_for_copy_clone_trait<'tcx>(
         | ty::Ref(_, _, Mutability::Not)
         | ty::Array(..) => Err(NoSolution),
 
+        // Check for anonymous adts.
+        ty::Adt(adt, generics) if adt.is_anonymous() => {
+            Ok(adt.all_fields().map(|f| f.ty(ecx.tcx(), generics)).collect())
+        }
+
         ty::Dynamic(..)
         | ty::Str
         | ty::Slice(_)
diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs
index ac6cfcdeb59..c2f7d5160f6 100644
--- a/compiler/rustc_trait_selection/src/traits/select/mod.rs
+++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs
@@ -2212,6 +2212,16 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
 
             // FIXME(async_closures): These are never clone, for now.
             ty::CoroutineClosure(_, _) => None,
+            // `Copy` and `Clone` are automatically impelemented for an anonymous adt
+            // if all of its fields are `Copy` and `Clone`
+            ty::Adt(adt, args) if adt.is_anonymous() => {
+                // (*) binder moved here
+                Where(
+                    obligation
+                        .predicate
+                        .rebind(adt.all_fields().map(|f| f.ty(self.tcx(), args)).collect()),
+                )
+            }
 
             ty::Adt(..) | ty::Alias(..) | ty::Param(..) | ty::Placeholder(..) => {
                 // Fallback to whatever user-defined impls exist in this case.
diff --git a/compiler/rustc_ty_utils/src/representability.rs b/compiler/rustc_ty_utils/src/representability.rs
index 70f1f099688..ade509123ac 100644
--- a/compiler/rustc_ty_utils/src/representability.rs
+++ b/compiler/rustc_ty_utils/src/representability.rs
@@ -21,8 +21,7 @@ macro_rules! rtry {
 fn representability(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Representability {
     match tcx.def_kind(def_id) {
         DefKind::Struct | DefKind::Union | DefKind::Enum => {
-            let adt_def = tcx.adt_def(def_id);
-            for variant in adt_def.variants() {
+            for variant in tcx.adt_def(def_id).variants() {
                 for field in variant.fields.iter() {
                     rtry!(tcx.representability(field.did.expect_local()));
                 }
diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs
index 89977934cde..b697f945f20 100644
--- a/src/librustdoc/clean/mod.rs
+++ b/src/librustdoc/clean/mod.rs
@@ -1892,6 +1892,16 @@ pub(crate) fn clean_ty<'tcx>(ty: &hir::Ty<'tcx>, cx: &mut DocContext<'tcx>) -> T
         TyKind::BareFn(barefn) => BareFunction(Box::new(clean_bare_fn_ty(barefn, cx))),
         // Rustdoc handles `TyKind::Err`s by turning them into `Type::Infer`s.
         TyKind::Infer | TyKind::Err(_) | TyKind::Typeof(..) | TyKind::InferDelegation(..) => Infer,
+        TyKind::AnonAdt(item_id) => {
+            let path = external_path(
+                cx,
+                item_id.owner_id.def_id.to_def_id(),
+                false,
+                ThinVec::new(),
+                ty::Binder::dummy(ty::GenericArgs::empty()),
+            );
+            Type::Path { path }
+        }
     }
 }
 
diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs
index 6710193f961..324ce03dcfd 100644
--- a/src/librustdoc/clean/types.rs
+++ b/src/librustdoc/clean/types.rs
@@ -1511,6 +1511,10 @@ pub(crate) enum Type {
 
     /// An `impl Trait`: `impl TraitA + TraitB + ...`
     ImplTrait(Vec<GenericBound>),
+    // /// An anonymous struct type i.e. `struct { foo: Type }`
+    // AnonStruct(VariantStruct),
+    // /// An anonymous union type i.e. `union { bar: Type }`
+    // AnonUnion(VariantStruct),
 }
 
 impl Type {
diff --git a/src/tools/clippy/clippy_lints/src/dereference.rs b/src/tools/clippy/clippy_lints/src/dereference.rs
index cdbb52f497b..0ddfeaa0ae0 100644
--- a/src/tools/clippy/clippy_lints/src/dereference.rs
+++ b/src/tools/clippy/clippy_lints/src/dereference.rs
@@ -831,6 +831,7 @@ impl TyCoercionStability {
                 | TyKind::Typeof(..)
                 | TyKind::TraitObject(..)
                 | TyKind::InferDelegation(..)
+                | TyKind::AnonAdt(..)
                 | TyKind::Err(_) => Self::Reborrow,
             };
         }
diff --git a/src/tools/clippy/clippy_utils/src/hir_utils.rs b/src/tools/clippy/clippy_utils/src/hir_utils.rs
index 4fa93ad23c3..d50332e82da 100644
--- a/src/tools/clippy/clippy_utils/src/hir_utils.rs
+++ b/src/tools/clippy/clippy_utils/src/hir_utils.rs
@@ -515,6 +515,7 @@ impl HirEqInterExpr<'_, '_, '_> {
             (TyKind::Path(l), TyKind::Path(r)) => self.eq_qpath(l, r),
             (&TyKind::Tup(l), &TyKind::Tup(r)) => over(l, r, |l, r| self.eq_ty(l, r)),
             (&TyKind::Infer, &TyKind::Infer) => true,
+            (TyKind::AnonAdt(l_item_id), TyKind::AnonAdt(r_item_id)) => l_item_id == r_item_id,
             _ => false,
         }
     }
@@ -1108,7 +1109,7 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> {
             TyKind::Typeof(anon_const) => {
                 self.hash_body(anon_const.body);
             },
-            TyKind::Err(_) | TyKind::Infer | TyKind::Never | TyKind::InferDelegation(..) => {},
+            TyKind::Err(_) | TyKind::Infer | TyKind::Never | TyKind::InferDelegation(..) | TyKind::AnonAdt(_) => {},
         }
     }
 
diff --git a/src/tools/rustfmt/src/types.rs b/src/tools/rustfmt/src/types.rs
index cd2582e66be..aaef80f4aef 100644
--- a/src/tools/rustfmt/src/types.rs
+++ b/src/tools/rustfmt/src/types.rs
@@ -806,8 +806,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::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/tests/ui/feature-gates/feature-gate-unnamed_fields.rs b/tests/ui/feature-gates/feature-gate-unnamed_fields.rs
index 4bbd0c83bfb..6ee8de89564 100644
--- a/tests/ui/feature-gates/feature-gate-unnamed_fields.rs
+++ b/tests/ui/feature-gates/feature-gate-unnamed_fields.rs
@@ -2,7 +2,6 @@ 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
     }
@@ -12,7 +11,6 @@ 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
     }
diff --git a/tests/ui/feature-gates/feature-gate-unnamed_fields.stderr b/tests/ui/feature-gates/feature-gate-unnamed_fields.stderr
index 82f08912bc8..8fa342c08ae 100644
--- a/tests/ui/feature-gates/feature-gate-unnamed_fields.stderr
+++ b/tests/ui/feature-gates/feature-gate-unnamed_fields.stderr
@@ -14,7 +14,6 @@ error[E0658]: unnamed fields are not yet fully implemented
 LL |       _: union {
    |  ________^
 LL | |
-LL | |
 LL | |         bar: u8,
 LL | |         baz: u16
 LL | |     }
@@ -25,7 +24,7 @@ LL | |     }
    = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
 
 error[E0658]: unnamed fields are not yet fully implemented
-  --> $DIR/feature-gate-unnamed_fields.rs:13:5
+  --> $DIR/feature-gate-unnamed_fields.rs:12:5
    |
 LL |     _: struct {
    |     ^
@@ -35,12 +34,11 @@ LL |     _: struct {
    = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
 
 error[E0658]: unnamed fields are not yet fully implemented
-  --> $DIR/feature-gate-unnamed_fields.rs:13:8
+  --> $DIR/feature-gate-unnamed_fields.rs:12:8
    |
 LL |       _: struct {
    |  ________^
 LL | |
-LL | |
 LL | |         foobaz: u8,
 LL | |         barbaz: u16
 LL | |     }
@@ -51,7 +49,7 @@ LL | |     }
    = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
 
 error[E0658]: unnamed fields are not yet fully implemented
-  --> $DIR/feature-gate-unnamed_fields.rs:23:5
+  --> $DIR/feature-gate-unnamed_fields.rs:21:5
    |
 LL |     _: S
    |     ^
@@ -60,30 +58,6 @@ LL |     _: S
    = help: add `#![feature(unnamed_fields)]` to the crate attributes to enable
    = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
 
-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
+error: aborting due to 5 previous errors
 
 For more information about this error, try `rustc --explain E0658`.
diff --git a/tests/ui/union/unnamed-fields/restrict_anonymous_structs.rs b/tests/ui/union/unnamed-fields/restrict_anonymous_structs.rs
index 192bbba5a5b..76525ec0bb1 100644
--- a/tests/ui/union/unnamed-fields/restrict_anonymous_structs.rs
+++ b/tests/ui/union/unnamed-fields/restrict_anonymous_structs.rs
@@ -3,9 +3,7 @@
 
 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 {
@@ -14,9 +12,7 @@ struct G {
 
 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 {
@@ -27,7 +23,6 @@ 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
diff --git a/tests/ui/union/unnamed-fields/restrict_anonymous_structs.stderr b/tests/ui/union/unnamed-fields/restrict_anonymous_structs.stderr
index fd731766c01..846e3451a71 100644
--- a/tests/ui/union/unnamed-fields/restrict_anonymous_structs.stderr
+++ b/tests/ui/union/unnamed-fields/restrict_anonymous_structs.stderr
@@ -5,25 +5,25 @@ 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
+  --> $DIR/restrict_anonymous_structs.rs:10: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
+  --> $DIR/restrict_anonymous_structs.rs:14: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
+  --> $DIR/restrict_anonymous_structs.rs:19: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
+  --> $DIR/restrict_anonymous_structs.rs:24:9
    |
 LL |         _ : struct { field: u8 },
    |         -^^^^^^^^^^^^^^^^^^^^^^^
@@ -31,48 +31,18 @@ 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
+  --> $DIR/restrict_anonymous_structs.rs:24: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
+  --> $DIR/restrict_anonymous_structs.rs:28: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
+error: aborting due to 7 previous errors
 
diff --git a/tests/ui/union/unnamed-fields/restrict_anonymous_unions.rs b/tests/ui/union/unnamed-fields/restrict_anonymous_unions.rs
index c69266089bb..c049ba92ed2 100644
--- a/tests/ui/union/unnamed-fields/restrict_anonymous_unions.rs
+++ b/tests/ui/union/unnamed-fields/restrict_anonymous_unions.rs
@@ -3,9 +3,7 @@
 
 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 {
@@ -14,9 +12,7 @@ struct G {
 
 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 {
@@ -27,7 +23,6 @@ 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
diff --git a/tests/ui/union/unnamed-fields/restrict_anonymous_unions.stderr b/tests/ui/union/unnamed-fields/restrict_anonymous_unions.stderr
index c65cad775a9..c916e37a3e9 100644
--- a/tests/ui/union/unnamed-fields/restrict_anonymous_unions.stderr
+++ b/tests/ui/union/unnamed-fields/restrict_anonymous_unions.stderr
@@ -5,25 +5,25 @@ 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
+  --> $DIR/restrict_anonymous_unions.rs:10: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
+  --> $DIR/restrict_anonymous_unions.rs:14: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
+  --> $DIR/restrict_anonymous_unions.rs:19: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
+  --> $DIR/restrict_anonymous_unions.rs:24:9
    |
 LL |         _ : union { field: u8 },
    |         -^^^^^^^^^^^^^^^^^^^^^^
@@ -31,48 +31,18 @@ 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
+  --> $DIR/restrict_anonymous_unions.rs:24: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
+  --> $DIR/restrict_anonymous_unions.rs:28: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
+error: aborting due to 7 previous errors