diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs
index e7f19f06ebef5..62b2d75cc3398 100644
--- a/compiler/rustc_ast/src/ast.rs
+++ b/compiler/rustc_ast/src/ast.rs
@@ -2471,19 +2471,39 @@ impl VisibilityKind {
     }
 }
 
+/// Named field type
+#[derive(Clone, Encodable, Decodable, Debug)]
+pub struct NamedField {
+    // FIXME: Maybe remove option and use UnnamedField::Type instead of NamedField?
+    pub ident: Option<Ident>,
+    pub ty: P<Ty>,
+}
+
+/// Unnamed field type
+#[derive(Clone, Encodable, Decodable, Debug)]
+pub enum UnnamedField {
+    Struct(Vec<FieldDef>),
+    Union(Vec<FieldDef>),
+    Type(P<Ty>),
+}
+
+#[derive(Clone, Encodable, Decodable, Debug)]
+pub enum FieldVariant {
+    Named(NamedField),
+    Unnamed(UnnamedField),
+}
+
 /// Field definition in a struct, variant or union.
 ///
 /// E.g., `bar: usize` as in `struct Foo { bar: usize }`.
 #[derive(Clone, Encodable, Decodable, Debug)]
 pub struct FieldDef {
     pub attrs: Vec<Attribute>,
-    pub id: NodeId,
     pub span: Span,
     pub vis: Visibility,
-    pub ident: Option<Ident>,
-
-    pub ty: P<Ty>,
+    pub id: NodeId,
     pub is_placeholder: bool,
+    pub variant: FieldVariant,
 }
 
 /// Fields and constructor ids of enum variants and structs.
diff --git a/compiler/rustc_ast/src/mut_visit.rs b/compiler/rustc_ast/src/mut_visit.rs
index 05f57f978c7a4..3722075d407be 100644
--- a/compiler/rustc_ast/src/mut_visit.rs
+++ b/compiler/rustc_ast/src/mut_visit.rs
@@ -909,13 +909,19 @@ pub fn noop_flat_map_field_def<T: MutVisitor>(
     mut fd: FieldDef,
     visitor: &mut T,
 ) -> SmallVec<[FieldDef; 1]> {
-    let FieldDef { span, ident, vis, id, ty, attrs, is_placeholder: _ } = &mut fd;
+    let FieldDef { span, vis, id, attrs, is_placeholder: _, variant } = &mut fd;
     visitor.visit_span(span);
-    visit_opt(ident, |ident| visitor.visit_ident(ident));
     visitor.visit_vis(vis);
     visitor.visit_id(id);
-    visitor.visit_ty(ty);
     visit_attrs(attrs, visitor);
+    match variant {
+        FieldVariant::Named(NamedField { ident, ty }) => {
+            visit_opt(ident, |ident| visitor.visit_ident(ident));
+            visitor.visit_ty(ty);
+        }
+        // FIXME: Handle Unnamed variant
+        _ => {}
+    }
     smallvec![fd]
 }
 
diff --git a/compiler/rustc_ast/src/visit.rs b/compiler/rustc_ast/src/visit.rs
index 3f35919ae6a2a..05a68c57048e0 100644
--- a/compiler/rustc_ast/src/visit.rs
+++ b/compiler/rustc_ast/src/visit.rs
@@ -673,10 +673,16 @@ pub fn walk_struct_def<'a, V: Visitor<'a>>(visitor: &mut V, struct_definition: &
 
 pub fn walk_field_def<'a, V: Visitor<'a>>(visitor: &mut V, field: &'a FieldDef) {
     visitor.visit_vis(&field.vis);
-    if let Some(ident) = field.ident {
-        visitor.visit_ident(ident);
+    match &field.variant {
+        FieldVariant::Named(NamedField { ident, ty }) => {
+            if let Some(ident) = ident {
+                visitor.visit_ident(*ident);
+            }
+            visitor.visit_ty(&ty);
+        }
+        // FIXME: Handle Unnamed variant
+        _ => {}
     }
-    visitor.visit_ty(&field.ty);
     walk_list!(visitor, visit_attribute, &field.attrs);
 }
 
diff --git a/compiler/rustc_ast_lowering/src/item.rs b/compiler/rustc_ast_lowering/src/item.rs
index 5fd8f7eb33a1f..93243fb3e6d4b 100644
--- a/compiler/rustc_ast_lowering/src/item.rs
+++ b/compiler/rustc_ast_lowering/src/item.rs
@@ -792,31 +792,42 @@ impl<'hir> LoweringContext<'_, 'hir> {
     }
 
     fn lower_field_def(&mut self, (index, f): (usize, &FieldDef)) -> hir::FieldDef<'hir> {
-        let ty = if let TyKind::Path(ref qself, ref path) = f.ty.kind {
-            let t = self.lower_path_ty(
-                &f.ty,
-                qself,
-                path,
-                ParamMode::ExplicitNamed, // no `'_` in declarations (Issue #61124)
-                ImplTraitContext::disallowed(),
-            );
-            self.arena.alloc(t)
-        } else {
-            self.lower_ty(&f.ty, ImplTraitContext::disallowed())
-        };
         let hir_id = self.lower_node_id(f.id);
+
+        let (ident, ty) = match &f.variant {
+            FieldVariant::Named(NamedField { ident, ty }) => {
+                let ident = match ident {
+                    Some(ident) => *ident,
+                    // FIXME(jseyfried): positional field hygiene.
+                    None => Ident::new(sym::integer(index), f.span),
+                };
+
+                let ty = if let TyKind::Path(ref qself, ref path) = ty.kind {
+                    let t = self.lower_path_ty(
+                        ty,
+                        qself,
+                        path,
+                        ParamMode::ExplicitNamed, // no `'_` in declarations (Issue #61124)
+                        ImplTraitContext::disallowed(),
+                    );
+                    self.arena.alloc(t)
+                } else {
+                    self.lower_ty(&ty, ImplTraitContext::disallowed())
+                };
+
+                (ident, ty)
+            }
+            // FIXME: Handle Unnamed variant. Currently creates useless data to pass the typecheck
+            _ => {
+                let ident = Ident::from_str_and_span("", f.span);
+                let ty = self.arena.alloc(hir::Ty { hir_id, kind: hir::TyKind::Err, span: f.span });
+                (ident, &*ty)
+            }
+        };
+
         self.lower_attrs(hir_id, &f.attrs);
-        hir::FieldDef {
-            span: f.span,
-            hir_id,
-            ident: match f.ident {
-                Some(ident) => ident,
-                // FIXME(jseyfried): positional field hygiene.
-                None => Ident::new(sym::integer(index), f.span),
-            },
-            vis: self.lower_visibility(&f.vis, None),
-            ty,
-        }
+
+        hir::FieldDef { span: f.span, hir_id, ident, vis: self.lower_visibility(&f.vis, None), ty }
     }
 
     fn lower_trait_item(&mut self, i: &AssocItem) -> hir::TraitItem<'hir> {
diff --git a/compiler/rustc_ast_passes/src/feature_gate.rs b/compiler/rustc_ast_passes/src/feature_gate.rs
index c803c8a83b15c..30b287209f6dd 100644
--- a/compiler/rustc_ast_passes/src/feature_gate.rs
+++ b/compiler/rustc_ast_passes/src/feature_gate.rs
@@ -300,6 +300,21 @@ impl<'a> PostExpansionVisitor<'a> {
 }
 
 impl<'a> Visitor<'a> for PostExpansionVisitor<'a> {
+    fn visit_field_def(&mut self, s: &'a ast::FieldDef) {
+        match s.variant {
+            ast::FieldVariant::Unnamed(_) => {
+                gate_feature_post!(
+                    &self,
+                    unnamed_fields,
+                    s.span,
+                    "unnamed fields are not yet fully implemented"
+                )
+            }
+            _ => {}
+        }
+        visit::walk_field_def(self, s);
+    }
+
     fn visit_attribute(&mut self, attr: &ast::Attribute) {
         let attr_info =
             attr.ident().and_then(|ident| BUILTIN_ATTRIBUTE_MAP.get(&ident.name)).map(|a| **a);
@@ -692,6 +707,7 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session) {
         gate_all!(destructuring_assignment, "destructuring assignments are unstable");
     }
     gate_all!(pub_macro_rules, "`pub` on `macro_rules` items is unstable");
+    gate_all!(unnamed_fields, "unnamed fields are not yet fully implemented");
 
     // All uses of `gate_all!` below this point were added in #65742,
     // and subsequently disabled (with the non-early gating readded).
diff --git a/compiler/rustc_ast_pretty/src/pprust/state.rs b/compiler/rustc_ast_pretty/src/pprust/state.rs
index 789d2c296e291..373e0bbc0f003 100644
--- a/compiler/rustc_ast_pretty/src/pprust/state.rs
+++ b/compiler/rustc_ast_pretty/src/pprust/state.rs
@@ -1408,7 +1408,13 @@ impl<'a> State<'a> {
                         s.maybe_print_comment(field.span.lo());
                         s.print_outer_attributes(&field.attrs);
                         s.print_visibility(&field.vis);
-                        s.print_type(&field.ty)
+                        match &field.variant {
+                            ast::FieldVariant::Named(ast::NamedField { ident: _, ty }) => {
+                                s.print_type(ty)
+                            }
+                            // FIXME: Handle Unnamed variant
+                            _ => {}
+                        }
                     });
                     self.pclose();
                 }
@@ -1430,10 +1436,15 @@ impl<'a> State<'a> {
                     self.maybe_print_comment(field.span.lo());
                     self.print_outer_attributes(&field.attrs);
                     self.print_visibility(&field.vis);
-                    self.print_ident(field.ident.unwrap());
-                    self.word_nbsp(":");
-                    self.print_type(&field.ty);
-                    self.s.word(",");
+                    match &field.variant {
+                        ast::FieldVariant::Named(ast::NamedField { ident, ty }) => {
+                            self.print_ident(ident.unwrap());
+                            self.word_nbsp(":");
+                            self.print_type(ty);
+                            self.s.word(",");
+                        }
+                        _ => {}
+                    }
                 }
 
                 self.bclose(span)
diff --git a/compiler/rustc_builtin_macros/src/deriving/clone.rs b/compiler/rustc_builtin_macros/src/deriving/clone.rs
index ca1226b445d97..2f2cac6b9c0dd 100644
--- a/compiler/rustc_builtin_macros/src/deriving/clone.rs
+++ b/compiler/rustc_builtin_macros/src/deriving/clone.rs
@@ -127,7 +127,13 @@ fn cs_clone_shallow(
     fn process_variant(cx: &mut ExtCtxt<'_>, stmts: &mut Vec<ast::Stmt>, variant: &VariantData) {
         for field in variant.fields() {
             // let _: AssertParamIsClone<FieldTy>;
-            assert_ty_bounds(cx, stmts, field.ty.clone(), field.span, "AssertParamIsClone");
+            match &field.variant {
+                ast::FieldVariant::Named(ast::NamedField { ident: _, ty }) => {
+                    assert_ty_bounds(cx, stmts, ty.clone(), field.span, "AssertParamIsClone")
+                }
+                // FIXME: Handle Unnamed variant
+                _ => {}
+            }
         }
     }
 
diff --git a/compiler/rustc_builtin_macros/src/deriving/cmp/eq.rs b/compiler/rustc_builtin_macros/src/deriving/cmp/eq.rs
index 79f35ad5819f1..33146eccc62be 100644
--- a/compiler/rustc_builtin_macros/src/deriving/cmp/eq.rs
+++ b/compiler/rustc_builtin_macros/src/deriving/cmp/eq.rs
@@ -78,7 +78,13 @@ fn cs_total_eq_assert(
     ) {
         for field in variant.fields() {
             // let _: AssertParamIsEq<FieldTy>;
-            assert_ty_bounds(cx, stmts, field.ty.clone(), field.span, "AssertParamIsEq");
+            match &field.variant {
+                ast::FieldVariant::Named(ast::NamedField { ident: _, ty }) => {
+                    assert_ty_bounds(cx, stmts, ty.clone(), field.span, "AssertParamIsEq")
+                }
+                // FIXME: Handle Unnamed variant
+                _ => {}
+            }
         }
     }
 
diff --git a/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs
index a3decff3ae7e1..afa66e08cc06d 100644
--- a/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs
+++ b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs
@@ -720,8 +720,15 @@ impl<'a> TraitDef<'a> {
         from_scratch: bool,
         use_temporaries: bool,
     ) -> P<ast::Item> {
-        let field_tys: Vec<P<ast::Ty>> =
-            struct_def.fields().iter().map(|field| field.ty.clone()).collect();
+        let field_tys: Vec<P<ast::Ty>> = struct_def
+            .fields()
+            .iter()
+            .filter_map(|field| match &field.variant {
+                ast::FieldVariant::Named(ast::NamedField { ident: _, ty }) => Some(ty.clone()),
+                // FIXME: Handle Unnamed variant
+                _ => None,
+            })
+            .collect();
 
         let methods = self
             .methods
@@ -769,7 +776,13 @@ impl<'a> TraitDef<'a> {
         let mut field_tys = Vec::new();
 
         for variant in &enum_def.variants {
-            field_tys.extend(variant.data.fields().iter().map(|field| field.ty.clone()));
+            field_tys.extend(variant.data.fields().iter().filter_map(
+                |field| match &field.variant {
+                    ast::FieldVariant::Named(ast::NamedField { ident: _, ty }) => Some(ty.clone()),
+                    // FIXME: Handle Unnamed variant
+                    _ => None,
+                },
+            ))
         }
 
         let methods = self
@@ -1515,8 +1528,11 @@ impl<'a> TraitDef<'a> {
         let mut just_spans = Vec::new();
         for field in struct_def.fields() {
             let sp = field.span.with_ctxt(self.span.ctxt());
-            match field.ident {
-                Some(ident) => named_idents.push((ident, sp)),
+            match field.variant {
+                ast::FieldVariant::Named(ast::NamedField { ident: Some(ident), ty: _ }) => {
+                    named_idents.push((ident, sp))
+                }
+                // FIXME: Handle Unnamed variant
                 _ => just_spans.push(sp),
             }
         }
@@ -1576,7 +1592,13 @@ impl<'a> TraitDef<'a> {
             let val = if use_temporaries { val } else { cx.expr_deref(sp, val) };
             let val = cx.expr(sp, ast::ExprKind::Paren(val));
 
-            ident_exprs.push((sp, struct_field.ident, val, &struct_field.attrs[..]));
+            match struct_field.variant {
+                ast::FieldVariant::Named(ast::NamedField { ident, ty: _ }) => {
+                    ident_exprs.push((sp, ident, val, &struct_field.attrs[..]))
+                }
+                // FIXME: Handle Unnamed variant
+                _ => {}
+            }
         }
 
         let subpats = self.create_subpatterns(cx, paths, mutbl, use_temporaries);
diff --git a/compiler/rustc_expand/src/expand.rs b/compiler/rustc_expand/src/expand.rs
index 529ef7e4611e2..176f8e99433ab 100644
--- a/compiler/rustc_expand/src/expand.rs
+++ b/compiler/rustc_expand/src/expand.rs
@@ -328,11 +328,15 @@ impl InvocationKind {
         // and it holds because only inert attributes are supported in this position.
         match self {
             InvocationKind::Attr { item: Annotatable::FieldDef(field), .. }
-            | InvocationKind::Derive { item: Annotatable::FieldDef(field), .. }
-                if field.ident.is_none() =>
+            | InvocationKind::Derive { item: Annotatable::FieldDef(field), .. } => match field
+                .variant
             {
-                Some(field.vis.clone())
-            }
+                ast::FieldVariant::Named(ast::NamedField { ident, ty: _ }) if ident.is_none() => {
+                    Some(field.vis.clone())
+                }
+                // FIXME: Handle Unnamed variant
+                _ => None,
+            },
             _ => None,
         }
     }
diff --git a/compiler/rustc_expand/src/placeholders.rs b/compiler/rustc_expand/src/placeholders.rs
index 6586ba138fb99..c88408b3bacef 100644
--- a/compiler/rustc_expand/src/placeholders.rs
+++ b/compiler/rustc_expand/src/placeholders.rs
@@ -155,12 +155,12 @@ pub fn placeholder(
         }]),
         AstFragmentKind::StructFields => AstFragment::StructFields(smallvec![ast::FieldDef {
             attrs: Default::default(),
-            id,
-            ident: None,
             span,
-            ty: ty(),
             vis,
+            id,
             is_placeholder: true,
+            // FIXME: Maybe change to UnnamedField?
+            variant: ast::FieldVariant::Named(ast::NamedField { ident: None, ty: ty() })
         }]),
         AstFragmentKind::Variants => AstFragment::Variants(smallvec![ast::Variant {
             attrs: Default::default(),
diff --git a/compiler/rustc_feature/src/active.rs b/compiler/rustc_feature/src/active.rs
index a410826d3fda6..76bc4d4780715 100644
--- a/compiler/rustc_feature/src/active.rs
+++ b/compiler/rustc_feature/src/active.rs
@@ -642,6 +642,9 @@ declare_features! (
     /// Allows `extern "wasm" fn`
     (active, wasm_abi, "1.53.0", Some(83788), None),
 
+    /// Allows unnamed fields of struct and union type
+    (active, unnamed_fields, "1.53.0", Some(49804), None),
+
     // -------------------------------------------------------------------------
     // feature-group-end: actual feature gates
     // -------------------------------------------------------------------------
@@ -669,6 +672,7 @@ pub const INCOMPLETE_FEATURES: &[Symbol] = &[
     sym::const_generics_defaults,
     sym::inherent_associated_types,
     sym::type_alias_impl_trait,
+    sym::unnamed_fields,
 ];
 
 /// Some features are not allowed to be used together at the same time, if
diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs
index 299b9a959c56f..33fbdd036294a 100644
--- a/compiler/rustc_parse/src/parser/item.rs
+++ b/compiler/rustc_parse/src/parser/item.rs
@@ -832,16 +832,20 @@ impl<'a> Parser<'a> {
     }
 
     fn parse_rename(&mut self) -> PResult<'a, Option<Ident>> {
-        if self.eat_keyword(kw::As) { self.parse_ident_or_underscore().map(Some) } else { Ok(None) }
+        if self.eat_keyword(kw::As) {
+            self.parse_ident_or_underscore(true).map(Some)
+        } else {
+            Ok(None)
+        }
     }
 
-    fn parse_ident_or_underscore(&mut self) -> PResult<'a, Ident> {
+    fn parse_ident_or_underscore(&mut self, recover: bool) -> PResult<'a, Ident> {
         match self.token.ident() {
             Some((ident @ Ident { name: kw::Underscore, .. }, false)) => {
                 self.bump();
                 Ok(ident)
             }
-            _ => self.parse_ident(),
+            _ => self.parse_ident_common(recover),
         }
     }
 
@@ -1056,7 +1060,8 @@ impl<'a> Parser<'a> {
         &mut self,
         m: Option<Mutability>,
     ) -> PResult<'a, (Ident, P<Ty>, Option<P<ast::Expr>>)> {
-        let id = if m.is_none() { self.parse_ident_or_underscore() } else { self.parse_ident() }?;
+        let id =
+            if m.is_none() { self.parse_ident_or_underscore(true) } else { self.parse_ident() }?;
 
         // Parse the type of a `const` or `static mut?` item.
         // That is, the `":" $ty` fragment.
@@ -1280,11 +1285,11 @@ impl<'a> Parser<'a> {
                     FieldDef {
                         span: lo.to(ty.span),
                         vis,
-                        ident: None,
-                        id: DUMMY_NODE_ID,
-                        ty,
                         attrs,
+                        id: DUMMY_NODE_ID,
                         is_placeholder: false,
+                        // FIXME: Maybe change to UnnamedField?
+                        variant: FieldVariant::Named(NamedField { ident: None, ty }),
                     },
                     TrailingToken::MaybeComma,
                 ))
@@ -1354,20 +1359,25 @@ impl<'a> Parser<'a> {
 
                 // Try to recover extra trailing angle brackets
                 let mut recovered = false;
-                if let TyKind::Path(_, Path { segments, .. }) = &a_var.ty.kind {
-                    if let Some(last_segment) = segments.last() {
-                        recovered = self.check_trailing_angle_brackets(
-                            last_segment,
-                            &[&token::Comma, &token::CloseDelim(token::Brace)],
-                        );
-                        if recovered {
-                            // Handle a case like `Vec<u8>>,` where we can continue parsing fields
-                            // after the comma
-                            self.eat(&token::Comma);
-                            // `check_trailing_angle_brackets` already emitted a nicer error
-                            err.cancel();
+                match &a_var.variant {
+                    FieldVariant::Named(NamedField { ident: _, ty }) => {
+                        if let TyKind::Path(_, Path { segments, .. }) = &ty.kind {
+                            if let Some(last_segment) = segments.last() {
+                                recovered = self.check_trailing_angle_brackets(
+                                    last_segment,
+                                    &[&token::Comma, &token::CloseDelim(token::Brace)],
+                                );
+                                if recovered {
+                                    // Handle a case like `Vec<u8>>,` where we can continue parsing fields
+                                    // after the comma
+                                    self.eat(&token::Comma);
+                                    // `check_trailing_angle_brackets` already emitted a nicer error
+                                    err.cancel();
+                                }
+                            }
                         }
                     }
+                    _ => {}
                 }
 
                 if self.token.is_ident() {
@@ -1395,27 +1405,85 @@ impl<'a> Parser<'a> {
         Ok(a_var)
     }
 
-    /// Parses a structure field.
-    fn parse_name_and_ty(
+    fn parse_named_field_type(
         &mut self,
         lo: Span,
         vis: Visibility,
         attrs: Vec<Attribute>,
+        name: Ident,
     ) -> PResult<'a, FieldDef> {
-        let name = self.parse_ident_common(false)?;
-        self.expect(&token::Colon)?;
         let ty = self.parse_ty()?;
         Ok(FieldDef {
+            attrs,
             span: lo.to(self.prev_token.span),
-            ident: Some(name),
             vis,
             id: DUMMY_NODE_ID,
-            ty,
-            attrs,
             is_placeholder: false,
+            variant: FieldVariant::Named(NamedField { ident: Some(name), ty }),
         })
     }
 
+    fn parse_unnamed_field_type(
+        &mut self,
+        lo: Span,
+        vis: Visibility,
+        attrs: Vec<Attribute>,
+    ) -> PResult<'a, FieldDef> {
+        if self.eat_keyword(kw::Struct) {
+            let (fields, _) = self.parse_record_struct_body()?;
+            Ok(FieldDef {
+                attrs,
+                span: lo.to(self.prev_token.span),
+                vis,
+                id: DUMMY_NODE_ID,
+                is_placeholder: false,
+                variant: FieldVariant::Unnamed(UnnamedField::Struct(fields)),
+            })
+        }
+        // Can't directly eat_keyword because technically a union could
+        // be a user defined type, so disambiguate by looking ahead for a `{`
+        else if self.token.is_keyword(kw::Union)
+            && self.look_ahead(1, |t| t == &token::OpenDelim(token::Brace))
+        {
+            self.bump(); // `union`
+            let (fields, _) = self.parse_record_struct_body()?;
+            Ok(FieldDef {
+                attrs,
+                span: lo.to(self.prev_token.span),
+                vis,
+                id: DUMMY_NODE_ID,
+                is_placeholder: false,
+                variant: FieldVariant::Unnamed(UnnamedField::Union(fields)),
+            })
+        } else {
+            let ty = self.parse_ty()?;
+            Ok(FieldDef {
+                attrs,
+                span: lo.to(self.prev_token.span),
+                vis,
+                id: DUMMY_NODE_ID,
+                is_placeholder: false,
+                variant: FieldVariant::Unnamed(UnnamedField::Type(ty)),
+            })
+        }
+    }
+
+    /// Parses a structure field.
+    fn parse_name_and_ty(
+        &mut self,
+        lo: Span,
+        vis: Visibility,
+        attrs: Vec<Attribute>,
+    ) -> PResult<'a, FieldDef> {
+        let name = self.parse_ident_or_underscore(false)?;
+        self.expect(&token::Colon)?;
+        if name.name == kw::Underscore {
+            self.parse_unnamed_field_type(lo, vis, attrs)
+        } else {
+            self.parse_named_field_type(lo, vis, attrs, name)
+        }
+    }
+
     /// Parses a declarative macro 2.0 definition.
     /// The `macro` keyword has already been parsed.
     /// ```
diff --git a/compiler/rustc_resolve/src/build_reduced_graph.rs b/compiler/rustc_resolve/src/build_reduced_graph.rs
index d77022a65e439..36bcd966911c3 100644
--- a/compiler/rustc_resolve/src/build_reduced_graph.rs
+++ b/compiler/rustc_resolve/src/build_reduced_graph.rs
@@ -342,7 +342,13 @@ impl<'a, 'b> BuildReducedGraphVisitor<'a, 'b> {
         let field_names = vdata
             .fields()
             .iter()
-            .map(|field| respan(field.span, field.ident.map_or(kw::Empty, |ident| ident.name)))
+            .filter_map(|field| match &field.variant {
+                ast::FieldVariant::Named(ast::NamedField { ident, ty: _ }) => {
+                    Some(respan(field.span, ident.map_or(kw::Empty, |ident| ident.name)))
+                }
+                // FIXME: Handle Unnamed variant
+                _ => None,
+            })
             .collect();
         self.insert_field_names(def_id, field_names);
     }
diff --git a/compiler/rustc_resolve/src/def_collector.rs b/compiler/rustc_resolve/src/def_collector.rs
index 17f0c39e39735..7f8501c9a6612 100644
--- a/compiler/rustc_resolve/src/def_collector.rs
+++ b/compiler/rustc_resolve/src/def_collector.rs
@@ -58,15 +58,20 @@ impl<'a, 'b> DefCollector<'a, 'b> {
                 this.resolver.placeholder_field_indices[&node_id]
             })
         };
-
         if field.is_placeholder {
             let old_index = self.resolver.placeholder_field_indices.insert(field.id, index(self));
             assert!(old_index.is_none(), "placeholder field index is reset for a node ID");
             self.visit_macro_invoc(field.id);
         } else {
-            let name = field.ident.map_or_else(|| sym::integer(index(self)), |ident| ident.name);
-            let def = self.create_def(field.id, DefPathData::ValueNs(name), field.span);
-            self.with_parent(def, |this| visit::walk_field_def(this, field));
+            match field.variant {
+                ast::FieldVariant::Named(ast::NamedField { ident, ty: _ }) => {
+                    let name = ident.map_or_else(|| sym::integer(index(self)), |ident| ident.name);
+                    let def = self.create_def(field.id, DefPathData::ValueNs(name), field.span);
+                    self.with_parent(def, |this| visit::walk_field_def(this, field));
+                }
+                // FIXME: Handle Unnamed variant
+                _ => {}
+            }
         }
     }
 
diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs
index 52270f0e6277b..2b1cd25a516d0 100644
--- a/compiler/rustc_span/src/symbol.rs
+++ b/compiler/rustc_span/src/symbol.rs
@@ -1254,6 +1254,7 @@ symbols! {
         unix,
         unlikely,
         unmarked_api,
+        unnamed_fields,
         unpin,
         unreachable,
         unreachable_code,
diff --git a/src/test/ui/feature-gates/feature-gate-unnamed_fields.rs b/src/test/ui/feature-gates/feature-gate-unnamed_fields.rs
new file mode 100644
index 0000000000000..b9f916bdea63a
--- /dev/null
+++ b/src/test/ui/feature-gates/feature-gate-unnamed_fields.rs
@@ -0,0 +1,16 @@
+struct Foo {
+    _: struct { //~ ERROR unnamed fields are not yet fully implemented
+        foo: u8
+    }
+}
+
+union Bar {
+    _: union { //~ ERROR unnamed fields are not yet fully implemented
+        bar: u8
+    }
+}
+
+struct S;
+struct Baz {
+    _: S //~ ERROR unnamed fields are not yet fully implemented
+}
diff --git a/src/tools/clippy/clippy_lints/src/excessive_bools.rs b/src/tools/clippy/clippy_lints/src/excessive_bools.rs
index 249ee27330bf9..8f878f9481ba4 100644
--- a/src/tools/clippy/clippy_lints/src/excessive_bools.rs
+++ b/src/tools/clippy/clippy_lints/src/excessive_bools.rs
@@ -1,6 +1,6 @@
 use clippy_utils::diagnostics::span_lint_and_help;
 use clippy_utils::{in_macro, match_path_ast};
-use rustc_ast::ast::{AssocItemKind, Extern, FnKind, FnSig, ImplKind, Item, ItemKind, TraitKind, Ty, TyKind};
+use rustc_ast::ast::{AssocItemKind, Extern, FnKind, FnSig, ImplKind, Item, ItemKind, TraitKind, Ty, TyKind, FieldVariant, NamedField};
 use rustc_lint::{EarlyContext, EarlyLintPass};
 use rustc_session::{declare_tool_lint, impl_lint_pass};
 use rustc_span::{sym, Span};
@@ -145,7 +145,11 @@ impl EarlyLintPass for ExcessiveBools {
                 let struct_bools = variant_data
                     .fields()
                     .iter()
-                    .filter(|field| is_bool_ty(&field.ty))
+                    .filter(|field| match &field.variant {
+                        FieldVariant::Named(NamedField{ty, ident:_}) if is_bool_ty(ty) => true,
+                        // FIXME: Handle Unnamed variant
+                        _ => false
+                    })
                     .count()
                     .try_into()
                     .unwrap();
diff --git a/src/tools/clippy/clippy_lints/src/manual_non_exhaustive.rs b/src/tools/clippy/clippy_lints/src/manual_non_exhaustive.rs
index dc19805b50abd..22cb2b771e5cd 100644
--- a/src/tools/clippy/clippy_lints/src/manual_non_exhaustive.rs
+++ b/src/tools/clippy/clippy_lints/src/manual_non_exhaustive.rs
@@ -3,7 +3,7 @@ use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::meets_msrv;
 use clippy_utils::source::snippet_opt;
 use if_chain::if_chain;
-use rustc_ast::ast::{FieldDef, Item, ItemKind, Variant, VariantData, VisibilityKind};
+use rustc_ast::ast::{FieldDef, Item, ItemKind, Variant, VariantData, VisibilityKind, FieldVariant, NamedField};
 use rustc_errors::Applicability;
 use rustc_lint::{EarlyContext, EarlyLintPass};
 use rustc_semver::RustcVersion;
@@ -141,7 +141,12 @@ fn check_manual_non_exhaustive_struct(cx: &EarlyContext<'_>, item: &Item, data:
     }
 
     fn is_non_exhaustive_marker(field: &FieldDef) -> bool {
-        is_private(field) && field.ty.kind.is_unit() && field.ident.map_or(true, |n| n.as_str().starts_with('_'))
+        match &field.variant {
+            FieldVariant::Named(NamedField{ty, ident}) =>
+            is_private(field) && ty.kind.is_unit() && ident.map_or(true, |n| n.as_str().starts_with('_')),
+            // FIXME: Handle Unnamed variant
+            _ => false
+        }
     }
 
     fn find_header_span(cx: &EarlyContext<'_>, item: &Item, data: &VariantData) -> Span {
diff --git a/src/tools/clippy/clippy_utils/src/ast_utils.rs b/src/tools/clippy/clippy_utils/src/ast_utils.rs
index eaea3e636f9c3..8b859d3a9f9a2 100644
--- a/src/tools/clippy/clippy_utils/src/ast_utils.rs
+++ b/src/tools/clippy/clippy_utils/src/ast_utils.rs
@@ -362,11 +362,30 @@ pub fn eq_variant_data(l: &VariantData, r: &VariantData) -> bool {
 }
 
 pub fn eq_struct_field(l: &FieldDef, r: &FieldDef) -> bool {
-    l.is_placeholder == r.is_placeholder
-        && over(&l.attrs, &r.attrs, |l, r| eq_attr(l, r))
-        && eq_vis(&l.vis, &r.vis)
-        && both(&l.ident, &r.ident, |l, r| eq_id(*l, *r))
-        && eq_ty(&l.ty, &r.ty)
+    if l.is_placeholder != r.is_placeholder
+    || !over(&l.attrs, &r.attrs, |l, r| eq_attr(l, r))
+    || !eq_vis(&l.vis, &r.vis)
+    {
+        false
+    } else {
+        match (&l.variant, &r.variant) {
+            (FieldVariant::Named(NamedField{ident: l_ident, ty: l_ty}),
+            FieldVariant::Named(NamedField{ident: r_ident, ty: r_ty})) =>
+            both(l_ident, r_ident, |l, r| eq_id(*l, *r)) && eq_ty(l_ty, r_ty),
+
+            (FieldVariant::Unnamed(UnnamedField::Struct(l_v)),
+            FieldVariant::Unnamed(UnnamedField::Struct(r_v)))
+            | (FieldVariant::Unnamed(UnnamedField::Union(l_v)),
+            FieldVariant::Unnamed(UnnamedField::Union(r_v))) =>
+            l_v.iter().zip(r_v.iter()).all(|(l_f, r_f)| eq_struct_field(l_f, r_f)),
+
+            (FieldVariant::Unnamed(UnnamedField::Type(l_ty)),
+            FieldVariant::Unnamed(UnnamedField::Type(r_ty))) =>
+            eq_ty(l_ty, r_ty),
+
+            _ => false
+        }
+    }
 }
 
 pub fn eq_fn_sig(l: &FnSig, r: &FnSig) -> bool {