diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs
index d6c2bfacf66a6..2f75f618abf03 100644
--- a/compiler/rustc_ast/src/ast.rs
+++ b/compiler/rustc_ast/src/ast.rs
@@ -2074,10 +2074,6 @@ 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`.
///
@@ -2721,6 +2717,93 @@ impl VisibilityKind {
}
}
+#[derive(Clone, Copy, Encodable, Decodable, Debug)]
+pub enum AnonRecordKind {
+ Struct,
+ Union,
+}
+
+impl AnonRecordKind {
+ /// Returns the lowercase name.
+ pub fn name(self) -> &'static str {
+ match self {
+ Self::Struct => "struct",
+ Self::Union => "union",
+ }
+ }
+}
+
+/// An anonymous struct or union, i.e. `struct { foo: Foo }` or `union { foo: Foo }`.
+#[derive(Clone, Encodable, Decodable, Debug)]
+pub struct AnonRecord {
+ pub id: NodeId,
+ pub span: Span,
+ pub fields: ThinVec,
+ pub kind: AnonRecordKind,
+ pub recovered: bool,
+}
+
+impl AnonRecord {
+ pub fn is_union(&self) -> bool {
+ matches!(self.kind, AnonRecordKind::Union)
+ }
+ pub fn is_struct(&self) -> bool {
+ matches!(self.kind, AnonRecordKind::Struct)
+ }
+}
+
+/// Type of fields in a struct, variant or union.
+#[derive(Clone, Encodable, Decodable, Debug)]
+pub enum FieldTy {
+ Ty(P),
+ /// An anonymous struct or union, i.e. `struct { foo: Foo }` or `union { foo: Foo }`.
+ // AnonRecord(P- ),
+ AnonRecord(P),
+}
+
+impl From
> for FieldTy {
+ fn from(ty: P) -> Self {
+ Self::Ty(ty)
+ }
+}
+
+impl From for FieldTy {
+ fn from(anon_record: AnonRecord) -> Self {
+ Self::AnonRecord(P(anon_record))
+ }
+}
+
+impl FieldTy {
+ pub fn id(&self) -> NodeId {
+ match self {
+ Self::Ty(ty) => ty.id,
+ Self::AnonRecord(anon_record) => anon_record.id,
+ }
+ }
+
+ pub fn span(&self) -> Span {
+ match self {
+ Self::Ty(ty) => ty.span,
+ Self::AnonRecord(anon_record) => anon_record.span,
+ }
+ }
+
+ pub fn as_ty(&self) -> Option<&P> {
+ match self {
+ Self::Ty(ty) => Some(ty),
+ _ => None,
+ }
+ }
+
+ /// Expects a `Ty`, otherwise panics.
+ pub fn expect_ty(&self) -> &P {
+ let FieldTy::Ty(ty) = &self else {
+ panic!("expect a type, found {self:?}");
+ };
+ ty
+ }
+}
+
/// Field definition in a struct, variant or union.
///
/// E.g., `bar: usize` as in `struct Foo { bar: usize }`.
@@ -2732,7 +2815,7 @@ pub struct FieldDef {
pub vis: Visibility,
pub ident: Option,
- pub ty: P,
+ pub ty: FieldTy,
pub is_placeholder: bool,
}
@@ -2762,6 +2845,27 @@ impl VariantData {
}
}
+ /// Return all fields of this variant, with anonymous structs or unions flattened.
+ pub fn all_fields(&self) -> AllFields<'_> {
+ AllFields { iters: smallvec::smallvec![self.fields().iter()] }
+ }
+
+ /// Return whether this variant contains inner anonymous unions.
+ pub fn find_inner_union(&self) -> Option {
+ // We only check the record-like structs
+ let VariantData::Struct(fields, ..) = self else { return None };
+ fn find_union(fields: &[FieldDef]) -> Option {
+ fields.iter().find_map(|field| {
+ let FieldTy::AnonRecord(anon_record) = &field.ty else { return None };
+ anon_record
+ .is_union()
+ .then(|| anon_record.span)
+ .or_else(|| find_union(&anon_record.fields))
+ })
+ }
+ find_union(&fields)
+ }
+
/// Return the `NodeId` of this variant's constructor, if it has one.
pub fn ctor_node_id(&self) -> Option {
match *self {
@@ -2771,6 +2875,44 @@ impl VariantData {
}
}
+/// Iterator of all fields of a `VariantData`.
+///
+/// It iterates on the field tree in preorder, where the unnamed fields with anonymous structs or unions
+/// are flattened to their inner fields.
+pub struct AllFields<'a> {
+ iters: smallvec::SmallVec<[std::slice::Iter<'a, FieldDef>; 1]>,
+}
+
+impl<'a> Iterator for AllFields<'a> {
+ type Item = &'a FieldDef;
+
+ fn next(&mut self) -> Option {
+ let mut top = self.iters.last_mut()?;
+ loop {
+ if let Some(field) = top.next() {
+ if let FieldTy::AnonRecord(anon_record) = &field.ty {
+ self.iters.push(anon_record.fields.iter());
+ top = self.iters.last_mut().unwrap();
+ continue;
+ }
+ return Some(field);
+ }
+ self.iters.pop();
+ top = self.iters.last_mut()?;
+ }
+ }
+
+ fn size_hint(&self) -> (usize, Option) {
+ match &self.iters[..] {
+ [] => (0, Some(0)),
+ [single] => (single.size_hint().0, None),
+ [first, .., last] => (first.size_hint().0 + last.size_hint().0, None),
+ }
+ }
+}
+
+impl std::iter::FusedIterator for AllFields<'_> {}
+
/// An item definition.
#[derive(Clone, Encodable, Decodable, Debug)]
pub struct Item {
diff --git a/compiler/rustc_ast/src/mut_visit.rs b/compiler/rustc_ast/src/mut_visit.rs
index c6a31fbdbc31c..60dfe4a9e93b1 100644
--- a/compiler/rustc_ast/src/mut_visit.rs
+++ b/compiler/rustc_ast/src/mut_visit.rs
@@ -251,6 +251,10 @@ pub trait MutVisitor: Sized {
noop_visit_variant_data(vdata, self);
}
+ fn visit_anon_record(&mut self, anon_record: &mut AnonRecord) {
+ noop_visit_anon_record(anon_record, self);
+ }
+
fn flat_map_generic_param(&mut self, param: GenericParam) -> SmallVec<[GenericParam; 1]> {
noop_flat_map_generic_param(param, self)
}
@@ -514,9 +518,6 @@ 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);
@@ -986,6 +987,13 @@ pub fn noop_visit_variant_data(vdata: &mut VariantData, vis: &mut
}
}
+pub fn noop_visit_anon_record(anon_record: &mut AnonRecord, vis: &mut T) {
+ let AnonRecord { span, id, fields, recovered: _recovered, kind: _kind } = anon_record;
+ vis.visit_span(span);
+ vis.visit_id(id);
+ fields.flat_map_in_place(|field| vis.flat_map_field_def(field));
+}
+
pub fn noop_visit_trait_ref(TraitRef { path, ref_id }: &mut TraitRef, vis: &mut T) {
vis.visit_path(path);
vis.visit_id(ref_id);
@@ -1007,7 +1015,12 @@ pub fn noop_flat_map_field_def(
visit_opt(ident, |ident| visitor.visit_ident(ident));
visitor.visit_vis(vis);
visitor.visit_id(id);
- visitor.visit_ty(ty);
+ match ty {
+ FieldTy::Ty(ty) => {
+ visitor.visit_ty(ty);
+ }
+ FieldTy::AnonRecord(anon_record) => visitor.visit_anon_record(anon_record),
+ }
visit_attrs(attrs, visitor);
smallvec![fd]
}
diff --git a/compiler/rustc_ast/src/visit.rs b/compiler/rustc_ast/src/visit.rs
index a303d6584f44f..4c5ceddd525d1 100644
--- a/compiler/rustc_ast/src/visit.rs
+++ b/compiler/rustc_ast/src/visit.rs
@@ -441,9 +441,6 @@ 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)
- }
}
}
@@ -716,7 +713,14 @@ pub fn walk_field_def<'a, V: Visitor<'a>>(visitor: &mut V, field: &'a FieldDef)
if let Some(ident) = field.ident {
visitor.visit_ident(ident);
}
- visitor.visit_ty(&field.ty);
+ match &field.ty {
+ FieldTy::Ty(ty) => {
+ visitor.visit_ty(ty);
+ }
+ FieldTy::AnonRecord(anon_record) => {
+ walk_list!(visitor, visit_field_def, &anon_record.fields);
+ }
+ }
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 80854c8a6c08b..3b5d1df2b99be 100644
--- a/compiler/rustc_ast_lowering/src/item.rs
+++ b/compiler/rustc_ast_lowering/src/item.rs
@@ -675,17 +675,35 @@ impl<'hir> LoweringContext<'_, 'hir> {
}
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,
- qself,
- path,
- ParamMode::ExplicitNamed, // no `'_` in declarations (Issue #61124)
- &ImplTraitContext::Disallowed(ImplTraitPosition::FieldTy),
- );
- self.arena.alloc(t)
- } else {
- self.lower_ty(&f.ty, &ImplTraitContext::Disallowed(ImplTraitPosition::FieldTy))
+ let ty = match &f.ty {
+ FieldTy::Ty(ty) if let TyKind::Path(qself, path) = &ty.kind => {
+ let t = self.lower_path_ty(
+ ty,
+ qself,
+ path,
+ ParamMode::ExplicitNamed, // no `'_` in declarations (Issue #61124)
+ &ImplTraitContext::Disallowed(ImplTraitPosition::FieldTy),
+ );
+ self.arena.alloc(t)
+ }
+ FieldTy::Ty(ty) => {
+ self.lower_ty(ty, &ImplTraitContext::Disallowed(ImplTraitPosition::FieldTy))
+ }
+ FieldTy::AnonRecord(anon_record) => {
+ let struct_or_union = anon_record.kind.name();
+ let hir_id = self.lower_node_id(anon_record.id);
+ let span = anon_record.span;
+ // FIXME(unnamed_fields): IMPLEMENTATION IN PROGRESS
+ #[allow(rustc::untranslatable_diagnostic)]
+ #[allow(rustc::diagnostic_outside_of_impl)]
+ let kind = hir::TyKind::Err(
+ self.tcx
+ .sess
+ .span_err(span, format!("anonymous {struct_or_union}s are unimplemented")),
+ );
+ let ty = hir::Ty { hir_id, kind, span };
+ self.arena.alloc(ty)
+ }
};
let hir_id = self.lower_node_id(f.id);
self.lower_attrs(hir_id, &f.attrs);
diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs
index 5dda8f5a6a328..504d7bfae437a 100644
--- a/compiler/rustc_ast_lowering/src/lib.rs
+++ b/compiler/rustc_ast_lowering/src/lib.rs
@@ -35,6 +35,8 @@
#![doc(rust_logo)]
#![feature(box_patterns)]
#![feature(let_chains)]
+#![feature(if_let_guard)]
+#![feature(never_type)]
#![recursion_limit = "256"]
#![deny(rustc::untranslatable_diagnostic)]
#![deny(rustc::diagnostic_outside_of_impl)]
@@ -1323,18 +1325,6 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
TyKind::Err => {
hir::TyKind::Err(self.tcx.sess.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.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/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs
index 554ed36b814e6..f83da8dcb6b0b 100644
--- a/compiler/rustc_ast_passes/src/ast_validation.rs
+++ b/compiler/rustc_ast_passes/src/ast_validation.rs
@@ -198,9 +198,6 @@ impl<'a> AstValidator<'a> {
}
}
}
- TyKind::AnonStruct(ref fields, ..) | TyKind::AnonUnion(ref fields, ..) => {
- walk_list!(self, visit_field_def, fields)
- }
_ => visit::walk_ty(self, t),
}
}
@@ -209,11 +206,13 @@ impl<'a> AstValidator<'a> {
if let Some(ident) = field.ident
&& ident.name == kw::Underscore
{
- self.check_unnamed_field_ty(&field.ty, ident.span);
+ let ty = 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);
+ if let Some(ty) = ty {
+ self.visit_ty_common(&ty);
+ self.walk_ty(&ty);
+ }
walk_list!(self, visit_attribute, &field.attrs);
} else {
self.visit_field_def(field);
@@ -257,29 +256,24 @@ 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;
+ fn check_unnamed_field_ty(&self, f: &'a FieldTy, span: Span) -> Option<&'a Ty> {
+ // We already checked for `kw::Underscore` before calling this function,
+ // so skip the check
+ let ty = &f.as_ty()?;
+ // 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
+ if matches!(&ty.kind, | TyKind::Path(..)) {
+ return Some(ty);
}
self.err_handler().emit_err(errors::InvalidUnnamedFieldTy { span, ty_span: ty.span });
+ Some(ty)
}
- 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_anon_struct_or_union(&self, f: &'a FieldTy) {
+ let FieldTy::AnonRecord(anon_record) = f else { return };
+ let struct_or_union = anon_record.kind.name();
+ let span = anon_record.span;
+ self.err_handler().emit_err(errors::AnonStructOrUnionNotAllowed { struct_or_union, span });
}
fn deny_unnamed_field(&self, field: &FieldDef) {
@@ -778,7 +772,6 @@ 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)
}
@@ -794,6 +787,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
fn visit_field_def(&mut self, field: &'a FieldDef) {
self.deny_unnamed_field(field);
+ self.deny_anon_struct_or_union(&field.ty);
visit::walk_field_def(self, field)
}
diff --git a/compiler/rustc_ast_pretty/src/pprust/state.rs b/compiler/rustc_ast_pretty/src/pprust/state.rs
index 1ad28ffbf2bb6..168b1416608ba 100644
--- a/compiler/rustc_ast_pretty/src/pprust/state.rs
+++ b/compiler/rustc_ast_pretty/src/pprust/state.rs
@@ -973,14 +973,6 @@ 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 fd5b529b1d4de..d2c43db9867fb 100644
--- a/compiler/rustc_ast_pretty/src/pprust/state/item.rs
+++ b/compiler/rustc_ast_pretty/src/pprust/state/item.rs
@@ -462,7 +462,15 @@ impl<'a> State<'a> {
self.print_visibility(&field.vis);
self.print_ident(field.ident.unwrap());
self.word_nbsp(":");
- self.print_type(&field.ty);
+ match &field.ty {
+ ast::FieldTy::Ty(ty) => {
+ self.print_type(&ty);
+ }
+ ast::FieldTy::AnonRecord(anon_record) => {
+ self.head(anon_record.kind.name());
+ self.print_record_struct_body(&anon_record.fields, anon_record.span);
+ }
+ }
self.word(",");
}
}
@@ -488,7 +496,7 @@ 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)
+ s.print_type(field.ty.expect_ty())
});
self.pclose();
}
diff --git a/compiler/rustc_builtin_macros/src/deriving/clone.rs b/compiler/rustc_builtin_macros/src/deriving/clone.rs
index 1649cc76c8d52..94cc70bac6806 100644
--- a/compiler/rustc_builtin_macros/src/deriving/clone.rs
+++ b/compiler/rustc_builtin_macros/src/deriving/clone.rs
@@ -106,7 +106,7 @@ fn cs_clone_simple(
// This basic redundancy checking only prevents duplication of
// assertions like `AssertParamIsClone` where the type is a
// simple name. That's enough to get a lot of cases, though.
- if let Some(name) = field.ty.kind.is_simple_path()
+ if let Some(name) = field.ty.expect_ty().kind.is_simple_path()
&& !seen_type_names.insert(name)
{
// Already produced an assertion for this type.
@@ -115,7 +115,7 @@ fn cs_clone_simple(
super::assert_ty_bounds(
cx,
&mut stmts,
- field.ty.clone(),
+ field.ty.expect_ty().clone(),
field.span,
&[sym::clone, sym::AssertParamIsClone],
);
diff --git a/compiler/rustc_builtin_macros/src/deriving/cmp/eq.rs b/compiler/rustc_builtin_macros/src/deriving/cmp/eq.rs
index 8a6d219379fca..7e5efd1dc54be 100644
--- a/compiler/rustc_builtin_macros/src/deriving/cmp/eq.rs
+++ b/compiler/rustc_builtin_macros/src/deriving/cmp/eq.rs
@@ -73,7 +73,7 @@ fn cs_total_eq_assert(
// This basic redundancy checking only prevents duplication of
// assertions like `AssertParamIsEq` where the type is a
// simple name. That's enough to get a lot of cases, though.
- if let Some(name) = field.ty.kind.is_simple_path()
+ if let Some(name) = field.ty.expect_ty().kind.is_simple_path()
&& !seen_type_names.insert(name)
{
// Already produced an assertion for this type.
@@ -82,7 +82,7 @@ fn cs_total_eq_assert(
super::assert_ty_bounds(
cx,
&mut stmts,
- field.ty.clone(),
+ field.ty.expect_ty().clone(),
field.span,
&[sym::cmp, sym::AssertParamIsEq],
);
diff --git a/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs
index 23502b6eafc6d..294243bd527ef 100644
--- a/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs
+++ b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs
@@ -808,7 +808,7 @@ impl<'a> TraitDef<'a> {
is_packed: bool,
) -> P {
let field_tys: Vec> =
- struct_def.fields().iter().map(|field| field.ty.clone()).collect();
+ struct_def.fields().iter().map(|field| field.ty.expect_ty().clone()).collect();
let methods = self
.methods
@@ -863,7 +863,8 @@ 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().map(|field| field.ty.expect_ty().clone()));
}
let methods = self
@@ -1608,11 +1609,12 @@ impl<'a> TraitDef<'a> {
}
};
- let exception = if let TyKind::Slice(ty) = &struct_field.ty.kind &&
+ let struct_field_ty = struct_field.ty.expect_ty();
+ let exception = if let TyKind::Slice(ty) = &struct_field_ty.kind &&
is_simple_path(ty, sym::u8)
{
Some("byte")
- } else if is_simple_path(&struct_field.ty, sym::str) {
+ } else if is_simple_path(struct_field_ty, sym::str) {
Some("string")
} else {
None
diff --git a/compiler/rustc_expand/src/placeholders.rs b/compiler/rustc_expand/src/placeholders.rs
index 1292a8552303e..8842ae7044eec 100644
--- a/compiler/rustc_expand/src/placeholders.rs
+++ b/compiler/rustc_expand/src/placeholders.rs
@@ -168,7 +168,7 @@ pub fn placeholder(
id,
ident: None,
span,
- ty: ty(),
+ ty: ty().into(),
vis,
is_placeholder: true,
}]),
diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs
index 086e8d5cf9b7f..866591dff0c8d 100644
--- a/compiler/rustc_parse/src/parser/item.rs
+++ b/compiler/rustc_parse/src/parser/item.rs
@@ -1720,7 +1720,7 @@ impl<'a> Parser<'a> {
vis,
ident: None,
id: DUMMY_NODE_ID,
- ty,
+ ty: ty.into(),
attrs,
is_placeholder: false,
},
@@ -1805,7 +1805,9 @@ 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 FieldTy::Ty(ty) = &a_var.ty
+ && let TyKind::Path(_, Path { segments, .. }) = &ty.kind
+ {
if let Some(last_segment) = segments.last() {
recovered = self.check_trailing_angle_brackets(
last_segment,
@@ -1883,6 +1885,42 @@ impl<'a> Parser<'a> {
Ok(())
}
+ 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))
+ }
+
+ /// 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, AnonRecord> {
+ let kind = match &self.token {
+ token if token.is_keyword(kw::Union) => AnonRecordKind::Union,
+ token if token.is_keyword(kw::Struct) => AnonRecordKind::Struct,
+ token => unreachable!("Expect `struct` or `union`, found {token:?}"),
+ };
+
+ let lo = self.token.span;
+ self.bump();
+
+ let (fields, recovered) = self.parse_record_struct_body(kind.name(), lo, false)?;
+ let span = lo.to(self.prev_token.span);
+ self.sess.gated_spans.gate(sym::unnamed_fields, span);
+ // This can be rejected during AST validation in `deny_anon_adt`.
+ Ok(AnonRecord { id: DUMMY_NODE_ID, span, kind, fields, recovered })
+ }
+
/// Parses a structure field.
fn parse_name_and_ty(
&mut self,
@@ -1900,7 +1938,19 @@ impl<'a> Parser<'a> {
}
}
self.expect_field_ty_separator()?;
- let ty = self.parse_ty_for_field_def()?;
+ if self.can_begin_anon_struct_or_union() {
+ let anon_record = self.parse_anon_struct_or_union()?;
+ return Ok(FieldDef {
+ span: lo.to(self.prev_token.span),
+ ident: Some(name),
+ vis,
+ id: DUMMY_NODE_ID,
+ ty: anon_record.into(),
+ attrs,
+ is_placeholder: false,
+ });
+ }
+ let ty = self.parse_ty()?;
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 });
}
@@ -1915,7 +1965,7 @@ impl<'a> Parser<'a> {
ident: Some(name),
vis,
id: DUMMY_NODE_ID,
- ty,
+ ty: ty.into(),
attrs,
is_placeholder: false,
})
diff --git a/compiler/rustc_parse/src/parser/ty.rs b/compiler/rustc_parse/src/parser/ty.rs
index f349140e8c347..6007cf49dba4c 100644
--- a/compiler/rustc_parse/src/parser/ty.rs
+++ b/compiler/rustc_parse/src/parser/ty.rs
@@ -135,17 +135,6 @@ impl<'a> Parser<'a> {
)
}
- /// Parse a type suitable for a field definition.
- /// 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.
@@ -382,36 +371,6 @@ 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.
@@ -773,11 +732,6 @@ 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 28354ab0986e1..001513dc22753 100644
--- a/compiler/rustc_passes/src/hir_stats.rs
+++ b/compiler/rustc_passes/src/hir_stats.rs
@@ -602,8 +602,6 @@ impl<'v> ast_visit::Visitor<'v> for StatCollector<'v> {
BareFn,
Never,
Tup,
- AnonStruct,
- AnonUnion,
Path,
TraitObject,
ImplTrait,
diff --git a/compiler/rustc_resolve/src/build_reduced_graph.rs b/compiler/rustc_resolve/src/build_reduced_graph.rs
index 65901eedb2148..1158569e6f1ef 100644
--- a/compiler/rustc_resolve/src/build_reduced_graph.rs
+++ b/compiler/rustc_resolve/src/build_reduced_graph.rs
@@ -313,15 +313,16 @@ impl<'a, 'b, 'tcx> BuildReducedGraphVisitor<'a, 'b, 'tcx> {
// The fields are not expanded yet.
return;
}
- let def_ids = vdata.fields().iter().map(|field| self.r.local_def_id(field.id).to_def_id());
+ let def_ids = vdata.all_fields().map(|field| self.r.local_def_id(field.id).to_def_id());
self.r.field_def_ids.insert(def_id, self.r.tcx.arena.alloc_from_iter(def_ids));
}
fn insert_field_visibilities_local(&mut self, def_id: DefId, vdata: &ast::VariantData) {
let field_vis = vdata
- .fields()
- .iter()
- .map(|field| field.vis.span.until(field.ident.map_or(field.ty.span, |i| i.span)))
+ .all_fields()
+ .map(|field| {
+ field.vis.span.until(field.ident.map_or(field.ty.expect_ty().span, |i| i.span))
+ })
.collect();
self.r.field_visibility_spans.insert(def_id, field_vis);
}
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 545b122930e5f..330df13ba862f 100644
--- a/src/tools/clippy/clippy_lints/src/manual_non_exhaustive.rs
+++ b/src/tools/clippy/clippy_lints/src/manual_non_exhaustive.rs
@@ -117,7 +117,7 @@ impl EarlyLintPass for ManualNonExhaustiveStruct {
});
if let Some(Ok(field)) = iter.next()
&& iter.next().is_none()
- && field.ty.kind.is_unit()
+ && field.ty.expect_ty().kind.is_unit()
{
span_lint_and_then(
cx,
diff --git a/src/tools/clippy/clippy_utils/src/ast_utils.rs b/src/tools/clippy/clippy_utils/src/ast_utils.rs
index 12403bbff3c92..5ad3f3d24222e 100644
--- a/src/tools/clippy/clippy_utils/src/ast_utils.rs
+++ b/src/tools/clippy/clippy_utils/src/ast_utils.rs
@@ -556,7 +556,7 @@ pub fn eq_struct_field(l: &FieldDef, r: &FieldDef) -> bool {
&& over(&l.attrs, &r.attrs, eq_attr)
&& eq_vis(&l.vis, &r.vis)
&& both(&l.ident, &r.ident, |l, r| eq_id(*l, *r))
- && eq_ty(&l.ty, &r.ty)
+ && eq_ty(l.ty.expect_ty(), r.ty.expect_ty())
}
pub fn eq_fn_sig(l: &FnSig, r: &FnSig) -> bool {
diff --git a/src/tools/rustfmt/src/items.rs b/src/tools/rustfmt/src/items.rs
index 4dff65f8cd0a6..e371608dab80e 100644
--- a/src/tools/rustfmt/src/items.rs
+++ b/src/tools/rustfmt/src/items.rs
@@ -1087,6 +1087,59 @@ impl<'a> StructParts<'a> {
}
}
+pub(crate) struct StructStructParts<'a> {
+ prefix: &'a str,
+ ident: symbol::Ident,
+ vis: Option<&'a ast::Visibility>,
+ generics: Option<&'a ast::Generics>,
+ span: Span,
+}
+
+impl<'a> StructStructParts<'a> {
+ fn format_header(&self, context: &RewriteContext<'_>, offset: Indent) -> String {
+ let vis = &ast::Visibility {
+ kind: ast::VisibilityKind::Inherited,
+ span: self.span.shrink_to_lo(),
+ tokens: None,
+ };
+ format_header(
+ context,
+ self.prefix,
+ self.ident,
+ self.vis.unwrap_or(vis),
+ offset,
+ )
+ }
+ fn from_struct(struct_parts: &'a StructParts<'a>) -> Self {
+ StructStructParts {
+ prefix: struct_parts.prefix,
+ ident: struct_parts.ident,
+ vis: Some(struct_parts.vis),
+ generics: struct_parts.generics,
+ span: struct_parts.span,
+ }
+ }
+
+ fn from_anon_record(
+ anon_record: &'a ast::AnonRecord,
+ snippet_provider: &impl SpanUtils,
+ ) -> Self {
+ let prefix = match anon_record.kind {
+ ast::AnonRecordKind::Union => "union ",
+ ast::AnonRecordKind::Struct => "struct ",
+ };
+ let span = anon_record.span;
+ let ident_pos = snippet_provider.span_after(span, prefix);
+ StructStructParts {
+ prefix,
+ ident: symbol::Ident::new(symbol::kw::Empty, mk_sp(ident_pos, ident_pos)),
+ vis: None,
+ generics: None,
+ span,
+ }
+ }
+}
+
fn enum_variant_span(variant: &ast::Variant, context: &RewriteContext<'_>) -> Span {
use ast::VariantData::*;
if let Some(ref anon_const) = variant.disr_expr {
@@ -1117,9 +1170,14 @@ fn format_struct(
ast::VariantData::Tuple(ref fields, _) => {
format_tuple_struct(context, struct_parts, fields, offset)
}
- ast::VariantData::Struct(ref fields, _) => {
- format_struct_struct(context, struct_parts, fields, offset, one_line_width)
- }
+ ast::VariantData::Struct(ref fields, _) => format_struct_struct(
+ context,
+ &StructStructParts::from_struct(struct_parts),
+ fields,
+ offset,
+ one_line_width,
+ false,
+ ),
}
}
@@ -1391,15 +1449,17 @@ fn format_unit_struct(
pub(crate) fn format_struct_struct(
context: &RewriteContext<'_>,
- struct_parts: &StructParts<'_>,
+ struct_parts: &StructStructParts<'_>,
fields: &[ast::FieldDef],
offset: Indent,
one_line_width: Option,
+ is_anon: bool,
) -> Option {
let mut result = String::with_capacity(1024);
let span = struct_parts.span;
- let header_str = struct_parts.format_header(context, offset);
+ let header_offset = if is_anon { Indent::empty() } else { offset };
+ let header_str = struct_parts.format_header(context, header_offset);
result.push_str(&header_str);
let header_hi = struct_parts.ident.span.hi();
@@ -1908,7 +1968,7 @@ pub(crate) fn rewrite_struct_field(
let is_prefix_empty = prefix.is_empty();
// We must use multiline. We are going to put attributes and a field on different lines.
- let field_str = rewrite_assign_rhs(context, prefix, &*field.ty, &RhsAssignKind::Ty, shape)?;
+ let field_str = rewrite_assign_rhs(context, prefix, &field.ty, &RhsAssignKind::Ty, shape)?;
// Remove a leading white-space from `rewrite_assign_rhs()` when rewriting a tuple struct.
let field_str = if is_prefix_empty {
field_str.trim_start()
@@ -1918,6 +1978,28 @@ pub(crate) fn rewrite_struct_field(
combine_strs_with_missing_comments(context, &attrs_str, field_str, missing_span, shape, false)
}
+impl Rewrite for ast::FieldTy {
+ fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option {
+ match self {
+ ast::FieldTy::Ty(ty) => ty.rewrite(context, shape),
+ ast::FieldTy::AnonRecord(anon_record) => anon_record.rewrite(context, shape),
+ }
+ }
+}
+
+impl Rewrite for ast::AnonRecord {
+ fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option {
+ format_struct_struct(
+ context,
+ &StructStructParts::from_anon_record(self, context.snippet_provider),
+ &self.fields,
+ shape.indent,
+ None,
+ true,
+ )
+ }
+}
+
pub(crate) struct StaticParts<'a> {
prefix: &'a str,
vis: &'a ast::Visibility,
diff --git a/src/tools/rustfmt/src/spanned.rs b/src/tools/rustfmt/src/spanned.rs
index 2136cfeae1af1..f548f9dc297ba 100644
--- a/src/tools/rustfmt/src/spanned.rs
+++ b/src/tools/rustfmt/src/spanned.rs
@@ -138,7 +138,7 @@ impl Spanned for ast::GenericParam {
impl Spanned for ast::FieldDef {
fn span(&self) -> Span {
- span_with_attrs_lo_hi!(self, self.span.lo(), self.ty.span.hi())
+ span_with_attrs_lo_hi!(self, self.span.lo(), self.ty.span().hi())
}
}
diff --git a/src/tools/rustfmt/src/types.rs b/src/tools/rustfmt/src/types.rs
index 8ca2715037115..9e15408976aaf 100644
--- a/src/tools/rustfmt/src/types.rs
+++ b/src/tools/rustfmt/src/types.rs
@@ -816,8 +816,6 @@ 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
index 8e08c314ed1fb..f2ebfb58d0448 100644
--- a/src/tools/rustfmt/tests/target/anonymous-types.rs
+++ b/src/tools/rustfmt/tests/target/anonymous-types.rs
@@ -16,4 +16,16 @@ struct Foo {
e: f32,
}
+// Test for issue 117942
+struct Foo {
+ _: union {
+ #[rustfmt::skip]
+ f: String,
+ },
+ #[rustfmt::skip]
+ _: struct {
+ g: i32,
+ },
+}
+
fn main() {}
diff --git a/tests/ui/stats/hir-stats.stderr b/tests/ui/stats/hir-stats.stderr
index e6da83296ce16..cb8b2118d8e8b 100644
--- a/tests/ui/stats/hir-stats.stderr
+++ b/tests/ui/stats/hir-stats.stderr
@@ -15,12 +15,12 @@ ast-stats-1 Arm 96 ( 1.5%) 2 48
ast-stats-1 ForeignItem 96 ( 1.5%) 1 96
ast-stats-1 - Fn 96 ( 1.5%) 1
ast-stats-1 FnDecl 120 ( 1.8%) 5 24
-ast-stats-1 FieldDef 160 ( 2.5%) 2 80
-ast-stats-1 Stmt 160 ( 2.5%) 5 32
+ast-stats-1 Stmt 160 ( 2.4%) 5 32
ast-stats-1 - Local 32 ( 0.5%) 1
ast-stats-1 - MacCall 32 ( 0.5%) 1
ast-stats-1 - Expr 96 ( 1.5%) 3
-ast-stats-1 Param 160 ( 2.5%) 4 40
+ast-stats-1 Param 160 ( 2.4%) 4 40
+ast-stats-1 FieldDef 176 ( 2.7%) 2 88
ast-stats-1 Block 192 ( 2.9%) 6 32
ast-stats-1 Variant 208 ( 3.2%) 2 104
ast-stats-1 GenericBound 256 ( 3.9%) 4 64
@@ -28,7 +28,7 @@ ast-stats-1 - Trait 256 ( 3.9%) 4
ast-stats-1 AssocItem 352 ( 5.4%) 4 88
ast-stats-1 - Type 176 ( 2.7%) 2
ast-stats-1 - Fn 176 ( 2.7%) 2
-ast-stats-1 GenericParam 480 ( 7.4%) 5 96
+ast-stats-1 GenericParam 480 ( 7.3%) 5 96
ast-stats-1 Pat 504 ( 7.7%) 7 72
ast-stats-1 - Struct 72 ( 1.1%) 1
ast-stats-1 - Wild 72 ( 1.1%) 1
@@ -45,15 +45,15 @@ ast-stats-1 - Ptr 64 ( 1.0%) 1
ast-stats-1 - Ref 64 ( 1.0%) 1
ast-stats-1 - ImplicitSelf 128 ( 2.0%) 2
ast-stats-1 - Path 640 ( 9.8%) 10
-ast-stats-1 Item 1_224 (18.8%) 9 136
+ast-stats-1 Item 1_224 (18.7%) 9 136
ast-stats-1 - Trait 136 ( 2.1%) 1
ast-stats-1 - Enum 136 ( 2.1%) 1
ast-stats-1 - ForeignMod 136 ( 2.1%) 1
ast-stats-1 - Impl 136 ( 2.1%) 1
ast-stats-1 - Fn 272 ( 4.2%) 2
-ast-stats-1 - Use 408 ( 6.3%) 3
+ast-stats-1 - Use 408 ( 6.2%) 3
ast-stats-1 ----------------------------------------------------------------
-ast-stats-1 Total 6_520
+ast-stats-1 Total 6_536
ast-stats-1
ast-stats-2 POST EXPANSION AST STATS
ast-stats-2 Name Accumulated Size Count Item Size
@@ -73,12 +73,12 @@ ast-stats-2 FnDecl 120 ( 1.7%) 5 24
ast-stats-2 Attribute 128 ( 1.8%) 4 32
ast-stats-2 - DocComment 32 ( 0.4%) 1
ast-stats-2 - Normal 96 ( 1.3%) 3
-ast-stats-2 FieldDef 160 ( 2.2%) 2 80
ast-stats-2 Stmt 160 ( 2.2%) 5 32
ast-stats-2 - Local 32 ( 0.4%) 1
ast-stats-2 - Semi 32 ( 0.4%) 1
ast-stats-2 - Expr 96 ( 1.3%) 3
ast-stats-2 Param 160 ( 2.2%) 4 40
+ast-stats-2 FieldDef 176 ( 2.5%) 2 88
ast-stats-2 Block 192 ( 2.7%) 6 32
ast-stats-2 Variant 208 ( 2.9%) 2 104
ast-stats-2 GenericBound 256 ( 3.6%) 4 64
@@ -90,7 +90,7 @@ ast-stats-2 GenericParam 480 ( 6.7%) 5 96
ast-stats-2 Pat 504 ( 7.1%) 7 72
ast-stats-2 - Struct 72 ( 1.0%) 1
ast-stats-2 - Wild 72 ( 1.0%) 1
-ast-stats-2 - Ident 360 ( 5.1%) 5
+ast-stats-2 - Ident 360 ( 5.0%) 5
ast-stats-2 Expr 648 ( 9.1%) 9 72
ast-stats-2 - Path 72 ( 1.0%) 1
ast-stats-2 - Match 72 ( 1.0%) 1
@@ -113,7 +113,7 @@ ast-stats-2 - Impl 136 ( 1.9%) 1
ast-stats-2 - Fn 272 ( 3.8%) 2
ast-stats-2 - Use 544 ( 7.6%) 4
ast-stats-2 ----------------------------------------------------------------
-ast-stats-2 Total 7_120
+ast-stats-2 Total 7_136
ast-stats-2
hir-stats HIR STATS
hir-stats Name Accumulated Size Count Item Size