diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index 395540764ea05..4a0d72af59764 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -2092,10 +2092,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`. /// @@ -2715,6 +2711,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 }`. @@ -2726,7 +2809,7 @@ pub struct FieldDef { pub vis: Visibility, pub ident: Option, - pub ty: P, + pub ty: FieldTy, pub is_placeholder: bool, } @@ -2756,6 +2839,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 { @@ -2765,6 +2869,44 @@ impl VariantData { } } +/// Iterator of all fields of a `VariantData`. +/// +/// It iteartes 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 e3504a5638e47..08f5bc1939690 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) } @@ -510,9 +514,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); @@ -978,6 +979,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); @@ -994,12 +1002,17 @@ pub fn noop_flat_map_field_def( mut fd: FieldDef, visitor: &mut T, ) -> SmallVec<[FieldDef; 1]> { - let FieldDef { span, ident, vis, id, ty, attrs, is_placeholder: _ } = &mut fd; + let FieldDef { span, ident, vis, ty, id, attrs, is_placeholder: _ } = &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); + 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 ddbbf5a10bcdd..c38446a058948 100644 --- a/compiler/rustc_ast/src/visit.rs +++ b/compiler/rustc_ast/src/visit.rs @@ -438,9 +438,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) - } } } @@ -713,7 +710,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 a59c83de0f46f..26e49fc71b202 100644 --- a/compiler/rustc_ast_lowering/src/item.rs +++ b/compiler/rustc_ast_lowering/src/item.rs @@ -709,17 +709,31 @@ 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 41db61d391a13..a770a38353df0 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -32,6 +32,7 @@ #![feature(box_patterns)] #![feature(let_chains)] +#![feature(if_let_guard)] #![feature(never_type)] #![recursion_limit = "256"] #![deny(rustc::untranslatable_diagnostic)] @@ -1303,18 +1304,6 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { TyKind::Err => { hir::TyKind::Err(self.tcx.sess.delay_span_bug(t.span, "TyKind::Err lowered")) } - // FIXME(unnamed_fields): IMPLEMENTATION IN PROGRESS - #[allow(rustc::untranslatable_diagnostic)] - #[allow(rustc::diagnostic_outside_of_impl)] - TyKind::AnonStruct(ref _fields) => hir::TyKind::Err( - self.tcx.sess.span_err(t.span, "anonymous structs are unimplemented"), - ), - // FIXME(unnamed_fields): IMPLEMENTATION IN PROGRESS - #[allow(rustc::untranslatable_diagnostic)] - #[allow(rustc::diagnostic_outside_of_impl)] - TyKind::AnonUnion(ref _fields) => hir::TyKind::Err( - self.tcx.sess.span_err(t.span, "anonymous unions are unimplemented"), - ), TyKind::Slice(ty) => hir::TyKind::Slice(self.lower_ty(ty, itctx)), TyKind::Ptr(mt) => hir::TyKind::Ptr(self.lower_mt(mt, itctx)), TyKind::Ref(region, mt) => { diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs index ad367d05f0478..781313e288b65 100644 --- a/compiler/rustc_ast_passes/src/ast_validation.rs +++ b/compiler/rustc_ast_passes/src/ast_validation.rs @@ -223,9 +223,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), } } @@ -233,11 +230,13 @@ impl<'a> AstValidator<'a> { fn visit_struct_field_def(&mut self, field: &'a FieldDef) { if let Some(ident) = field.ident && ident.name == kw::Underscore { - self.check_unnamed_field_ty(&field.ty, ident.span); + 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); @@ -281,29 +280,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) { @@ -842,7 +836,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) } @@ -858,6 +851,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 8b7e91882fcc7..58ce73047bcec 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state.rs @@ -1064,14 +1064,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 3393f034bc3b5..35bc59455e13c 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/messages.ftl b/compiler/rustc_builtin_macros/messages.ftl index 8d8db4c13fac9..3007318586c87 100644 --- a/compiler/rustc_builtin_macros/messages.ftl +++ b/compiler/rustc_builtin_macros/messages.ftl @@ -59,6 +59,8 @@ builtin_macros_bad_derive_target = `derive` may only be applied to `struct`s, `e builtin_macros_bench_sig = functions used as benches must have signature `fn(&mut Bencher) -> impl Termination` +builtin_macros_cannot_derive_inner_union = this trait cannot be derived for structs with inner unions + .label = inner anonymous union defined here builtin_macros_cannot_derive_union = this trait cannot be derived for unions diff --git a/compiler/rustc_builtin_macros/src/deriving/clone.rs b/compiler/rustc_builtin_macros/src/deriving/clone.rs index b468abe3249af..387b7962d33b8 100644 --- a/compiler/rustc_builtin_macros/src/deriving/clone.rs +++ b/compiler/rustc_builtin_macros/src/deriving/clone.rs @@ -38,6 +38,10 @@ pub fn expand_deriving_clone( | ItemKind::Enum(_, Generics { params, .. }) => { let container_id = cx.current_expansion.id.expn_data().parent.expect_local(); let has_derive_copy = cx.resolver.has_derive_copy(container_id); + let has_union = match &annitem.kind { + ItemKind::Struct(variant, _) => variant.find_inner_union().is_some(), + _ => false, + }; if has_derive_copy && !params .iter() @@ -45,14 +49,20 @@ pub fn expand_deriving_clone( { bounds = vec![]; is_simple = true; - substructure = combine_substructure(Box::new(|c, s, sub| { - cs_clone_simple("Clone", c, s, sub, false) + substructure = combine_substructure(Box::new(move |c, s, sub| { + cs_clone_simple("Clone", c, s, sub, has_union) })); } else { - bounds = vec![]; + if has_union { + bounds = vec![Path(path_std!(marker::Copy))]; + } else { + bounds = vec![]; + } is_simple = false; - substructure = - combine_substructure(Box::new(|c, s, sub| cs_clone("Clone", c, s, sub))); + + substructure = combine_substructure(Box::new(move |c, s, sub| { + cs_clone("Clone", c, s, sub, has_union) + })); } } ItemKind::Union(..) => { @@ -97,23 +107,23 @@ fn cs_clone_simple( cx: &mut ExtCtxt<'_>, trait_span: Span, substr: &Substructure<'_>, - is_union: bool, + has_union: bool, ) -> BlockOrExpr { let mut stmts = ThinVec::new(); let mut seen_type_names = FxHashSet::default(); let mut process_variant = |variant: &VariantData| { - for field in variant.fields() { + for field in variant.all_fields() { // 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() && !seen_type_names.insert(name) { + if let Some(name) = field.ty.expect_ty().kind.is_simple_path() && !seen_type_names.insert(name) { // Already produced an assertion for this type. } else { // let _: AssertParamIsClone; super::assert_ty_bounds( cx, &mut stmts, - field.ty.clone(), + field.ty.expect_ty().clone(), field.span, &[sym::clone, sym::AssertParamIsClone], ); @@ -121,17 +131,8 @@ fn cs_clone_simple( } }; - if is_union { - // Just a single assertion for unions, that the union impls `Copy`. - // let _: AssertParamIsCopy; - let self_ty = cx.ty_path(cx.path_ident(trait_span, Ident::with_dummy_span(kw::SelfUpper))); - super::assert_ty_bounds( - cx, - &mut stmts, - self_ty, - trait_span, - &[sym::clone, sym::AssertParamIsCopy], - ); + if has_union { + assert_union_copy(cx, trait_span, &mut stmts); } else { match *substr.fields { StaticStruct(vdata, ..) => { @@ -156,6 +157,7 @@ fn cs_clone( cx: &mut ExtCtxt<'_>, trait_span: Span, substr: &Substructure<'_>, + has_union: bool, ) -> BlockOrExpr { let ctor_path; let all_fields; @@ -185,6 +187,11 @@ fn cs_clone( } } + let mut stmts = ThinVec::new(); + if has_union { + assert_union_copy(cx, trait_span, &mut stmts); + } + let expr = match *vdata { VariantData::Struct(..) => { let fields = all_fields @@ -210,5 +217,12 @@ fn cs_clone( } VariantData::Unit(..) => cx.expr_path(ctor_path), }; - BlockOrExpr::new_expr(expr) + BlockOrExpr::new_mixed(stmts, Some(expr)) +} + +fn assert_union_copy(cx: &mut ExtCtxt<'_>, trait_span: Span, stmts: &mut ThinVec) { + // Just a single assertion for unions, that the union impls `Copy`. + // let _: AssertParamIsCopy; + let self_ty = cx.ty_path(cx.path_ident(trait_span, Ident::with_dummy_span(kw::SelfUpper))); + super::assert_ty_bounds(cx, stmts, self_ty, trait_span, &[sym::clone, sym::AssertParamIsCopy]); } diff --git a/compiler/rustc_builtin_macros/src/deriving/cmp/eq.rs b/compiler/rustc_builtin_macros/src/deriving/cmp/eq.rs index c78a0eb04a074..6938b2cb02962 100644 --- a/compiler/rustc_builtin_macros/src/deriving/cmp/eq.rs +++ b/compiler/rustc_builtin_macros/src/deriving/cmp/eq.rs @@ -58,18 +58,18 @@ fn cs_total_eq_assert( let mut stmts = ThinVec::new(); let mut seen_type_names = FxHashSet::default(); let mut process_variant = |variant: &ast::VariantData| { - for field in variant.fields() { + for field in variant.all_fields() { // 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() && !seen_type_names.insert(name) { + if let Some(name) = field.ty.expect_ty().kind.is_simple_path() && !seen_type_names.insert(name) { // Already produced an assertion for this type. } else { // let _: AssertParamIsEq; 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 6597ee3cf1b6c..0968c8aed468e 100644 --- a/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs +++ b/compiler/rustc_builtin_macros/src/deriving/generic/mod.rs @@ -461,14 +461,20 @@ impl<'a> TraitDef<'a> { }); let newitem = match &item.kind { - ast::ItemKind::Struct(struct_def, generics) => self.expand_struct_def( - cx, - &struct_def, - item.ident, - generics, - from_scratch, - is_packed, - ), + ast::ItemKind::Struct(struct_def, generics) => { + if !self.supports_unions && let Some(union_span) = struct_def.find_inner_union() { + cx.emit_err(errors::DeriveInnerUnion { span: mitem.span, union_span }); + return; + }; + self.expand_struct_def( + cx, + &struct_def, + item.ident, + generics, + from_scratch, + is_packed, + ) + } ast::ItemKind::Enum(enum_def, generics) => { // We ignore `is_packed` here, because `repr(packed)` // enums cause an error later on. @@ -791,7 +797,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.all_fields().map(|field| field.ty.expect_ty().clone()).collect(); let methods = self .methods @@ -846,7 +852,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 @@ -1500,8 +1507,7 @@ impl<'a> TraitDef<'a> { F: Fn(usize, &ast::FieldDef, Span) -> Vec>, { struct_def - .fields() - .iter() + .all_fields() .enumerate() .map(|(i, struct_field)| { // For this field, get an expr for each selflike_arg. E.g. for @@ -1586,11 +1592,11 @@ impl<'a> TraitDef<'a> { } }; - let exception = if let TyKind::Slice(ty) = &struct_field.ty.kind && + let exception = if let TyKind::Slice(ty) = &struct_field.ty.expect_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.expect_ty(), sym::str) { Some("string") } else { None diff --git a/compiler/rustc_builtin_macros/src/errors.rs b/compiler/rustc_builtin_macros/src/errors.rs index fbf0395bb05ac..337a96d02818d 100644 --- a/compiler/rustc_builtin_macros/src/errors.rs +++ b/compiler/rustc_builtin_macros/src/errors.rs @@ -433,6 +433,15 @@ pub(crate) struct DeriveUnion { pub(crate) span: Span, } +#[derive(Diagnostic)] +#[diag(builtin_macros_cannot_derive_inner_union)] +pub(crate) struct DeriveInnerUnion { + #[primary_span] + pub(crate) span: Span, + #[label] + pub(crate) union_span: Span, +} + #[derive(Diagnostic)] #[diag(builtin_macros_env_takes_args)] pub(crate) struct EnvTakesArgs { diff --git a/compiler/rustc_expand/src/placeholders.rs b/compiler/rustc_expand/src/placeholders.rs index 82cac229284d8..ecfb42e559206 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 aad4edaba90b5..0f0115db88a00 100644 --- a/compiler/rustc_parse/src/parser/item.rs +++ b/compiler/rustc_parse/src/parser/item.rs @@ -1683,7 +1683,7 @@ impl<'a> Parser<'a> { vis, ident: None, id: DUMMY_NODE_ID, - ty, + ty: FieldTy::Ty(ty), attrs, is_placeholder: false, }, @@ -1768,7 +1768,7 @@ 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, @@ -1846,6 +1846,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, @@ -1863,7 +1899,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 }); } @@ -1878,7 +1926,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 a25b0f1f8930e..d32a0a8e24e07 100644 --- a/compiler/rustc_parse/src/parser/ty.rs +++ b/compiler/rustc_parse/src/parser/ty.rs @@ -136,17 +136,6 @@ impl<'a> Parser<'a> { ) } - /// Parse a type suitable for a field defintion. - /// The difference from `parse_ty` is that this version - /// allows anonymous structs and unions. - pub fn parse_ty_for_field_def(&mut self) -> PResult<'a, P> { - if self.can_begin_anon_struct_or_union() { - self.parse_anon_struct_or_union() - } else { - self.parse_ty() - } - } - /// Parse a type suitable for a function or function pointer parameter. /// The difference from `parse_ty` is that this version allows `...` /// (`CVarArgs`) at the top level of the type. @@ -347,36 +336,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. @@ -737,11 +696,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 24087a4eabbcd..5aa8aef6a859a 100644 --- a/compiler/rustc_passes/src/hir_stats.rs +++ b/compiler/rustc_passes/src/hir_stats.rs @@ -587,8 +587,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 30db450870b60..27af3cce0fc0f 100644 --- a/compiler/rustc_resolve/src/build_reduced_graph.rs +++ b/compiler/rustc_resolve/src/build_reduced_graph.rs @@ -320,15 +320,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/tests/ui/derives/derive-Clone-anonymous-union-in-struct.rs b/tests/ui/derives/derive-Clone-anonymous-union-in-struct.rs new file mode 100644 index 0000000000000..7434196e8f50f --- /dev/null +++ b/tests/ui/derives/derive-Clone-anonymous-union-in-struct.rs @@ -0,0 +1,23 @@ +#![feature(unnamed_fields)] +#![allow(incomplete_features)] + +// `Clone` is not allowed with a `!Copy` field +#[derive(Clone)] +//~^ ERROR: the trait bound `Foo: Copy` is not satisfied +//~| NOTE: required by a bound in `AssertParamIsCopy` +//~| NOTE: in this expansion of #[derive(Clone)] +//~| NOTE: in this expansion of #[derive(Clone)] +//~| NOTE: the trait `Copy` is not implemented for `Foo` +struct Foo { +//~^ HELP: consider annotating `Foo` with `#[derive(Copy)]` + x: u32, + _: union { //~ ERROR: anonymous unions are unimplemented + y: i32, // FIXME: field access check should be done at `rustc_hir_typeck` + //~^ ERROR: struct `Foo` has no field named `y` [E0560] + //~| HELP: a field with a similar name exists + //~| ERROR: no field `y` on type `&Foo` [E0609] + //~| HELP: a field with a similar name exists + }, +} + +fn main() {} diff --git a/tests/ui/derives/derive-Clone-anonymous-union-in-struct.stderr b/tests/ui/derives/derive-Clone-anonymous-union-in-struct.stderr new file mode 100644 index 0000000000000..a9016faca797c --- /dev/null +++ b/tests/ui/derives/derive-Clone-anonymous-union-in-struct.stderr @@ -0,0 +1,44 @@ +error: anonymous unions are unimplemented + --> $DIR/derive-Clone-anonymous-union-in-struct.rs:14:8 + | +LL | _: union { + | ________^ +LL | | y: i32, // FIXME: field access check should be done at `rustc_hir_typeck` +LL | | +LL | | +LL | | +LL | | +LL | | }, + | |_____^ + +error[E0277]: the trait bound `Foo: Copy` is not satisfied + --> $DIR/derive-Clone-anonymous-union-in-struct.rs:5:10 + | +LL | #[derive(Clone)] + | ^^^^^ the trait `Copy` is not implemented for `Foo` + | +note: required by a bound in `AssertParamIsCopy` + --> $SRC_DIR/core/src/clone.rs:LL:COL + = note: this error originates in the derive macro `Clone` (in Nightly builds, run with -Z macro-backtrace for more info) +help: consider annotating `Foo` with `#[derive(Copy)]` + | +LL + #[derive(Copy)] +LL | struct Foo { + | + +error[E0560]: struct `Foo` has no field named `y` + --> $DIR/derive-Clone-anonymous-union-in-struct.rs:15:9 + | +LL | y: i32, // FIXME: field access check should be done at `rustc_hir_typeck` + | ^^^^^^ help: a field with a similar name exists: `_` + +error[E0609]: no field `y` on type `&Foo` + --> $DIR/derive-Clone-anonymous-union-in-struct.rs:15:9 + | +LL | y: i32, // FIXME: field access check should be done at `rustc_hir_typeck` + | ^ help: a field with a similar name exists: `x` + +error: aborting due to 4 previous errors + +Some errors have detailed explanations: E0277, E0560, E0609. +For more information about an error, try `rustc --explain E0277`. diff --git a/tests/ui/derives/derive-Copy-anonymous-union-in-struct.rs b/tests/ui/derives/derive-Copy-anonymous-union-in-struct.rs new file mode 100644 index 0000000000000..211789d084077 --- /dev/null +++ b/tests/ui/derives/derive-Copy-anonymous-union-in-struct.rs @@ -0,0 +1,13 @@ +#![feature(unnamed_fields)] +#![allow(incomplete_features)] + +// `Clone` with `Copy` is allowed +#[derive(Clone, Copy)] +struct Foo { + x: u32, + _: union { //~ ERROR: anonymous unions are unimplemented + y: i32, + }, +} + +fn main() {} diff --git a/tests/ui/derives/derive-Copy-anonymous-union-in-struct.stderr b/tests/ui/derives/derive-Copy-anonymous-union-in-struct.stderr new file mode 100644 index 0000000000000..3dd85a0259e9f --- /dev/null +++ b/tests/ui/derives/derive-Copy-anonymous-union-in-struct.stderr @@ -0,0 +1,11 @@ +error: anonymous unions are unimplemented + --> $DIR/derive-Copy-anonymous-union-in-struct.rs:8:8 + | +LL | _: union { + | ________^ +LL | | y: i32, +LL | | }, + | |_____^ + +error: aborting due to previous error + diff --git a/tests/ui/derives/derive-anonymous-union-in-struct.rs b/tests/ui/derives/derive-anonymous-union-in-struct.rs new file mode 100644 index 0000000000000..381dc86e5d30f --- /dev/null +++ b/tests/ui/derives/derive-anonymous-union-in-struct.rs @@ -0,0 +1,20 @@ +#![feature(unnamed_fields)] +#![allow(incomplete_features)] + +// Disallowed derives +#[derive(Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)] +//~^ ERROR: this trait cannot be derived for structs with inner unions +//~| ERROR: this trait cannot be derived for structs with inner unions +//~| ERROR: this trait cannot be derived for structs with inner unions +//~| ERROR: this trait cannot be derived for structs with inner unions +//~| ERROR: this trait cannot be derived for structs with inner unions +//~| ERROR: this trait cannot be derived for structs with inner unions +//~| ERROR: can't compare `Foo` with `Foo` +struct Foo { + x: u32, + _: union { //~ ERROR: anonymous unions are unimplemented + y: i32, + }, +} + +fn main() {} diff --git a/tests/ui/derives/derive-anonymous-union-in-struct.stderr b/tests/ui/derives/derive-anonymous-union-in-struct.stderr new file mode 100644 index 0000000000000..d33351f5af760 --- /dev/null +++ b/tests/ui/derives/derive-anonymous-union-in-struct.stderr @@ -0,0 +1,95 @@ +error: this trait cannot be derived for structs with inner unions + --> $DIR/derive-anonymous-union-in-struct.rs:5:10 + | +LL | #[derive(Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)] + | ^^^^^ +... +LL | _: union { + | ________- +LL | | y: i32, +LL | | }, + | |_____- inner anonymous union defined here + +error: this trait cannot be derived for structs with inner unions + --> $DIR/derive-anonymous-union-in-struct.rs:5:17 + | +LL | #[derive(Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)] + | ^^^^^^^ +... +LL | _: union { + | ________- +LL | | y: i32, +LL | | }, + | |_____- inner anonymous union defined here + +error: this trait cannot be derived for structs with inner unions + --> $DIR/derive-anonymous-union-in-struct.rs:5:26 + | +LL | #[derive(Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)] + | ^^^^^^^^^ +... +LL | _: union { + | ________- +LL | | y: i32, +LL | | }, + | |_____- inner anonymous union defined here + +error: this trait cannot be derived for structs with inner unions + --> $DIR/derive-anonymous-union-in-struct.rs:5:41 + | +LL | #[derive(Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)] + | ^^^^^^^^^^ +... +LL | _: union { + | ________- +LL | | y: i32, +LL | | }, + | |_____- inner anonymous union defined here + +error: this trait cannot be derived for structs with inner unions + --> $DIR/derive-anonymous-union-in-struct.rs:5:53 + | +LL | #[derive(Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)] + | ^^^ +... +LL | _: union { + | ________- +LL | | y: i32, +LL | | }, + | |_____- inner anonymous union defined here + +error: this trait cannot be derived for structs with inner unions + --> $DIR/derive-anonymous-union-in-struct.rs:5:58 + | +LL | #[derive(Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)] + | ^^^^ +... +LL | _: union { + | ________- +LL | | y: i32, +LL | | }, + | |_____- inner anonymous union defined here + +error: anonymous unions are unimplemented + --> $DIR/derive-anonymous-union-in-struct.rs:15:8 + | +LL | _: union { + | ________^ +LL | | y: i32, +LL | | }, + | |_____^ + +error[E0277]: can't compare `Foo` with `Foo` + --> $DIR/derive-anonymous-union-in-struct.rs:5:37 + | +LL | #[derive(Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)] + | ^^ no implementation for `Foo == Foo` + | + = help: the trait `PartialEq` is not implemented for `Foo` +note: required by a bound in `Eq` + --> $SRC_DIR/core/src/cmp.rs:LL:COL + = note: this error originates in the derive macro `Eq` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 8 previous errors + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/stats/hir-stats.stderr b/tests/ui/stats/hir-stats.stderr index d723ff538a886..827db20d37264 100644 --- a/tests/ui/stats/hir-stats.stderr +++ b/tests/ui/stats/hir-stats.stderr @@ -15,21 +15,21 @@ 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 - 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 FieldDef 176 ( 2.7%) 2 88 ast-stats-1 Block 192 ( 3.0%) 6 32 ast-stats-1 Variant 208 ( 3.2%) 2 104 -ast-stats-1 GenericBound 224 ( 3.5%) 4 56 -ast-stats-1 - Trait 224 ( 3.5%) 4 +ast-stats-1 GenericBound 224 ( 3.4%) 4 56 +ast-stats-1 - Trait 224 ( 3.4%) 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 Pat 504 ( 7.8%) 7 72 +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 ast-stats-1 - Ident 360 ( 5.5%) 5 @@ -44,8 +44,8 @@ ast-stats-1 Ty 896 (13.8%) 14 64 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.9%) 10 -ast-stats-1 Item 1_224 (18.9%) 9 136 +ast-stats-1 - Path 640 ( 9.8%) 10 +ast-stats-1 Item 1_224 (18.8%) 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 @@ -53,7 +53,7 @@ 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 ---------------------------------------------------------------- -ast-stats-1 Total 6_488 +ast-stats-1 Total 6_504 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.5%) 1 ast-stats-2 - Normal 96 ( 1.4%) 3 -ast-stats-2 FieldDef 160 ( 2.3%) 2 80 ast-stats-2 Stmt 160 ( 2.3%) 5 32 ast-stats-2 - Local 32 ( 0.5%) 1 ast-stats-2 - Semi 32 ( 0.5%) 1 ast-stats-2 - Expr 96 ( 1.4%) 3 ast-stats-2 Param 160 ( 2.3%) 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 224 ( 3.2%) 4 56 @@ -98,7 +98,7 @@ ast-stats-2 - Struct 72 ( 1.0%) 1 ast-stats-2 - InlineAsm 72 ( 1.0%) 1 ast-stats-2 - Lit 144 ( 2.0%) 2 ast-stats-2 - Block 216 ( 3.0%) 3 -ast-stats-2 PathSegment 792 (11.2%) 33 24 +ast-stats-2 PathSegment 792 (11.1%) 33 24 ast-stats-2 Ty 896 (12.6%) 14 64 ast-stats-2 - Ptr 64 ( 0.9%) 1 ast-stats-2 - Ref 64 ( 0.9%) 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.7%) 4 ast-stats-2 ---------------------------------------------------------------- -ast-stats-2 Total 7_088 +ast-stats-2 Total 7_104 ast-stats-2 hir-stats HIR STATS hir-stats Name Accumulated Size Count Item Size