diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index fb012d9802f6c..b3bac1d7ecdef 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -1861,6 +1861,10 @@ pub enum TyKind { Never, /// A tuple (`(A, B, C, D,...)`). Tup(Vec>), + /// An anonymous struct type i.e. `struct { foo: Type }` + AnonymousStruct(Vec, bool), + /// An anonymous union type i.e. `union { bar: Type }` + AnonymousUnion(Vec, bool), /// A path (`module::module::...::Type`), optionally /// "qualified", e.g., ` as SomeTrait>::SomeType`. /// diff --git a/compiler/rustc_ast/src/mut_visit.rs b/compiler/rustc_ast/src/mut_visit.rs index 374a6ec972fba..071d41ea2b2c7 100644 --- a/compiler/rustc_ast/src/mut_visit.rs +++ b/compiler/rustc_ast/src/mut_visit.rs @@ -484,6 +484,9 @@ pub fn noop_visit_ty(ty: &mut P, vis: &mut T) { visit_vec(bounds, |bound| vis.visit_param_bound(bound)); } TyKind::MacCall(mac) => vis.visit_mac_call(mac), + TyKind::AnonymousStruct(fields, ..) | TyKind::AnonymousUnion(fields, ..) => { + fields.flat_map_in_place(|field| vis.flat_map_field_def(field)); + } } vis.visit_span(span); visit_lazy_tts(tokens, vis); diff --git a/compiler/rustc_ast/src/visit.rs b/compiler/rustc_ast/src/visit.rs index c50b334d3e949..f1a99bc51c96d 100644 --- a/compiler/rustc_ast/src/visit.rs +++ b/compiler/rustc_ast/src/visit.rs @@ -404,6 +404,9 @@ pub fn walk_ty<'a, V: Visitor<'a>>(visitor: &mut V, typ: &'a Ty) { TyKind::Typeof(ref expression) => visitor.visit_anon_const(expression), TyKind::Infer | TyKind::ImplicitSelf | TyKind::Err => {} TyKind::MacCall(ref mac) => visitor.visit_mac_call(mac), + TyKind::AnonymousStruct(ref fields, ..) | TyKind::AnonymousUnion(ref fields, ..) => { + walk_list!(visitor, visit_field_def, fields) + } TyKind::Never | TyKind::CVarArgs => {} } } diff --git a/compiler/rustc_ast_lowering/src/item.rs b/compiler/rustc_ast_lowering/src/item.rs index aa236a690ec79..292643d6d7510 100644 --- a/compiler/rustc_ast_lowering/src/item.rs +++ b/compiler/rustc_ast_lowering/src/item.rs @@ -789,7 +789,10 @@ impl<'hir> LoweringContext<'_, 'hir> { } } - fn lower_field_def(&mut self, (index, f): (usize, &FieldDef)) -> hir::FieldDef<'hir> { + pub(super) fn lower_field_def( + &mut self, + (index, f): (usize, &FieldDef), + ) -> hir::FieldDef<'hir> { let ty = if let TyKind::Path(ref qself, ref path) = f.ty.kind { let t = self.lower_path_ty( &f.ty, diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index 0439de0ee7bf9..740dfc65df8c5 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -1267,6 +1267,15 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { let kind = match t.kind { TyKind::Infer => hir::TyKind::Infer, TyKind::Err => hir::TyKind::Err, + // FIXME(unnamed_fields): IMPLEMENTATION IN PROGRESS + TyKind::AnonymousStruct(ref _fields, _recovered) => { + self.sess.struct_span_err(t.span, "anonymous structs are unimplemented").emit(); + hir::TyKind::Err + } + TyKind::AnonymousUnion(ref _fields, _recovered) => { + self.sess.struct_span_err(t.span, "anonymous unions are unimplemented").emit(); + hir::TyKind::Err + } TyKind::Slice(ref ty) => hir::TyKind::Slice(self.lower_ty(ty, itctx)), TyKind::Ptr(ref mt) => hir::TyKind::Ptr(self.lower_mt(mt, itctx)), TyKind::Rptr(ref region, ref mt) => { diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs index 6d6438920c09b..ba2da7694978d 100644 --- a/compiler/rustc_ast_passes/src/ast_validation.rs +++ b/compiler/rustc_ast_passes/src/ast_validation.rs @@ -175,10 +175,30 @@ impl<'a> AstValidator<'a> { } } } + TyKind::AnonymousStruct(ref fields, ..) | TyKind::AnonymousUnion(ref fields, ..) => { + self.with_banned_assoc_ty_bound(|this| { + walk_list!(this, visit_struct_field_def, fields) + }); + } _ => visit::walk_ty(self, t), } } + fn visit_struct_field_def(&mut self, field: &'a FieldDef) { + if let Some(ident) = field.ident { + if ident.name == kw::Underscore { + self.check_anonymous_field(field); + self.visit_vis(&field.vis); + self.visit_ident(ident); + self.visit_ty_common(&field.ty); + self.walk_ty(&field.ty); + walk_list!(self, visit_attribute, &field.attrs); + return; + } + } + self.visit_field_def(field); + } + fn err_handler(&self) -> &rustc_errors::Handler { &self.session.diagnostic() } @@ -213,6 +233,66 @@ impl<'a> AstValidator<'a> { err.emit(); } + fn check_anonymous_field(&self, field: &FieldDef) { + let FieldDef { ty, .. } = field; + match &ty.kind { + TyKind::AnonymousStruct(..) | TyKind::AnonymousUnion(..) => { + // We already checked for `kw::Underscore` before calling this function, + // so skip the check + } + TyKind::Path(..) => { + // 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 + } + _ => { + let msg = "unnamed fields can only have struct or union types"; + let label = "not a struct or union"; + self.err_handler() + .struct_span_err(field.span, msg) + .span_label(ty.span, label) + .emit(); + } + } + } + + fn deny_anonymous_struct(&self, ty: &Ty) { + match &ty.kind { + TyKind::AnonymousStruct(..) => { + self.err_handler() + .struct_span_err( + ty.span, + "anonymous structs are not allowed outside of unnamed struct or union fields", + ) + .span_label(ty.span, "anonymous struct declared here") + .emit(); + } + TyKind::AnonymousUnion(..) => { + self.err_handler() + .struct_span_err( + ty.span, + "anonymous unions are not allowed outside of unnamed struct or union fields", + ) + .span_label(ty.span, "anonymous union declared here") + .emit(); + } + _ => {} + } + } + + fn deny_anonymous_field(&self, field: &FieldDef) { + if let Some(ident) = field.ident { + if ident.name == kw::Underscore { + self.err_handler() + .struct_span_err( + field.span, + "anonymous fields are not allowed outside of structs or unions", + ) + .span_label(ident.span, "anonymous field declared here") + .emit() + } + } + } + fn check_decl_no_pat(decl: &FnDecl, mut report_err: impl FnMut(Span, Option, bool)) { for Param { pat, .. } in &decl.inputs { match pat.kind { @@ -732,6 +812,71 @@ impl<'a> AstValidator<'a> { ) .emit(); } + + fn visit_ty_common(&mut self, ty: &'a Ty) { + match ty.kind { + TyKind::BareFn(ref bfty) => { + self.check_fn_decl(&bfty.decl, SelfSemantic::No); + Self::check_decl_no_pat(&bfty.decl, |span, _, _| { + struct_span_err!( + self.session, + span, + E0561, + "patterns aren't allowed in function pointer types" + ) + .emit(); + }); + self.check_late_bound_lifetime_defs(&bfty.generic_params); + } + TyKind::TraitObject(ref bounds, ..) => { + let mut any_lifetime_bounds = false; + for bound in bounds { + if let GenericBound::Outlives(ref lifetime) = *bound { + if any_lifetime_bounds { + struct_span_err!( + self.session, + lifetime.ident.span, + E0226, + "only a single explicit lifetime bound is permitted" + ) + .emit(); + break; + } + any_lifetime_bounds = true; + } + } + self.no_questions_in_bounds(bounds, "trait object types", false); + } + TyKind::ImplTrait(_, ref bounds) => { + if self.is_impl_trait_banned { + struct_span_err!( + self.session, + ty.span, + E0667, + "`impl Trait` is not allowed in path parameters" + ) + .emit(); + } + + if let Some(outer_impl_trait_sp) = self.outer_impl_trait { + struct_span_err!( + self.session, + ty.span, + E0666, + "nested `impl Trait` is not allowed" + ) + .span_label(outer_impl_trait_sp, "outer `impl Trait`") + .span_label(ty.span, "nested `impl Trait` here") + .emit(); + } + + if !bounds.iter().any(|b| matches!(b, GenericBound::Trait(..))) { + self.err_handler().span_err(ty.span, "at least one trait must be specified"); + } + } + _ => {} + } + } } /// Checks that generic parameters are in the correct order, @@ -850,72 +995,8 @@ impl<'a> Visitor<'a> for AstValidator<'a> { } fn visit_ty(&mut self, ty: &'a Ty) { - match ty.kind { - TyKind::BareFn(ref bfty) => { - self.check_fn_decl(&bfty.decl, SelfSemantic::No); - Self::check_decl_no_pat(&bfty.decl, |span, _, _| { - struct_span_err!( - self.session, - span, - E0561, - "patterns aren't allowed in function pointer types" - ) - .emit(); - }); - self.check_late_bound_lifetime_defs(&bfty.generic_params); - } - TyKind::TraitObject(ref bounds, ..) => { - let mut any_lifetime_bounds = false; - for bound in bounds { - if let GenericBound::Outlives(ref lifetime) = *bound { - if any_lifetime_bounds { - struct_span_err!( - self.session, - lifetime.ident.span, - E0226, - "only a single explicit lifetime bound is permitted" - ) - .emit(); - break; - } - any_lifetime_bounds = true; - } - } - self.no_questions_in_bounds(bounds, "trait object types", false); - } - TyKind::ImplTrait(_, ref bounds) => { - if self.is_impl_trait_banned { - struct_span_err!( - self.session, - ty.span, - E0667, - "`impl Trait` is not allowed in path parameters" - ) - .emit(); - } - - if let Some(outer_impl_trait_sp) = self.outer_impl_trait { - struct_span_err!( - self.session, - ty.span, - E0666, - "nested `impl Trait` is not allowed" - ) - .span_label(outer_impl_trait_sp, "outer `impl Trait`") - .span_label(ty.span, "nested `impl Trait` here") - .emit(); - } - - if !bounds.iter().any(|b| matches!(b, GenericBound::Trait(..))) { - self.err_handler().span_err(ty.span, "at least one trait must be specified"); - } - - self.walk_ty(ty); - return; - } - _ => {} - } - + self.visit_ty_common(ty); + self.deny_anonymous_struct(ty); self.walk_ty(ty) } @@ -929,6 +1010,11 @@ impl<'a> Visitor<'a> for AstValidator<'a> { visit::walk_lifetime(self, lifetime); } + fn visit_field_def(&mut self, s: &'a FieldDef) { + self.deny_anonymous_field(s); + visit::walk_field_def(self, s) + } + fn visit_item(&mut self, item: &'a Item) { if item.attrs.iter().any(|attr| self.session.is_proc_macro_attr(attr)) { self.has_proc_macro_decls = true; @@ -1084,14 +1170,42 @@ impl<'a> Visitor<'a> for AstValidator<'a> { self.check_mod_file_item_asciionly(item.ident); } } - ItemKind::Union(ref vdata, _) => { - if let VariantData::Tuple(..) | VariantData::Unit(..) = vdata { - self.err_handler() - .span_err(item.span, "tuple and unit unions are not permitted"); + ItemKind::Struct(ref vdata, ref generics) => match vdata { + // Duplicating the `Visitor` logic allows catching all cases + // of `Anonymous(Struct, Union)` outside of a field struct or union. + // + // Inside `visit_ty` the validator catches every `Anonymous(Struct, Union)` it + // encounters, and only on `ItemKind::Struct` and `ItemKind::Union` + // it uses `visit_ty_common`, which doesn't contain that specific check. + VariantData::Struct(ref fields, ..) => { + self.visit_vis(&item.vis); + self.visit_ident(item.ident); + self.visit_generics(generics); + self.with_banned_assoc_ty_bound(|this| { + walk_list!(this, visit_struct_field_def, fields); + }); + walk_list!(self, visit_attribute, &item.attrs); + return; } + _ => {} + }, + ItemKind::Union(ref vdata, ref generics) => { if vdata.fields().is_empty() { self.err_handler().span_err(item.span, "unions cannot have zero fields"); } + match vdata { + VariantData::Struct(ref fields, ..) => { + self.visit_vis(&item.vis); + self.visit_ident(item.ident); + self.visit_generics(generics); + self.with_banned_assoc_ty_bound(|this| { + walk_list!(this, visit_struct_field_def, fields); + }); + walk_list!(self, visit_attribute, &item.attrs); + return; + } + _ => {} + } } ItemKind::Const(def, .., None) => { self.check_defaultness(item.span, def); diff --git a/compiler/rustc_ast_passes/src/feature_gate.rs b/compiler/rustc_ast_passes/src/feature_gate.rs index dc3383dae843e..4215d5c55a049 100644 --- a/compiler/rustc_ast_passes/src/feature_gate.rs +++ b/compiler/rustc_ast_passes/src/feature_gate.rs @@ -725,6 +725,7 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session) { // involved, so we only emit errors where there are no other parsing errors. gate_all!(destructuring_assignment, "destructuring assignments are 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 a7908f7006062..da9d89745a824 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state.rs @@ -954,6 +954,14 @@ impl<'a> State<'a> { } self.pclose(); } + ast::TyKind::AnonymousStruct(ref fields, ..) => { + self.s.word("struct"); + self.print_record_struct_body(fields, ty.span); + } + ast::TyKind::AnonymousUnion(ref fields, ..) => { + self.s.word("union"); + self.print_record_struct_body(fields, ty.span); + } ast::TyKind::Paren(ref typ) => { self.popen(); self.print_type(typ); @@ -1389,6 +1397,29 @@ impl<'a> State<'a> { } } + crate fn print_record_struct_body( + &mut self, + fields: &Vec, + span: rustc_span::Span, + ) { + self.nbsp(); + self.bopen(); + self.hardbreak_if_not_bol(); + + for field in fields { + self.hardbreak_if_not_bol(); + self.maybe_print_comment(field.span.lo()); + self.print_outer_attributes(&field.attrs); + self.print_visibility(&field.vis); + self.print_ident(field.ident.unwrap()); + self.word_nbsp(":"); + self.print_type(&field.ty); + self.s.word(","); + } + + self.bclose(span) + } + crate fn print_struct( &mut self, struct_def: &ast::VariantData, @@ -1418,24 +1449,9 @@ impl<'a> State<'a> { self.end(); self.end(); // Close the outer-box. } - ast::VariantData::Struct(..) => { + ast::VariantData::Struct(ref fields, ..) => { self.print_where_clause(&generics.where_clause); - self.nbsp(); - self.bopen(); - self.hardbreak_if_not_bol(); - - for field in struct_def.fields() { - self.hardbreak_if_not_bol(); - self.maybe_print_comment(field.span.lo()); - self.print_outer_attributes(&field.attrs); - self.print_visibility(&field.vis); - self.print_ident(field.ident.unwrap()); - self.word_nbsp(":"); - self.print_type(&field.ty); - self.s.word(","); - } - - self.bclose(span) + self.print_record_struct_body(fields, span); } } } diff --git a/compiler/rustc_feature/src/active.rs b/compiler/rustc_feature/src/active.rs index 9d96a9baa50c8..eb6abc00be329 100644 --- a/compiler/rustc_feature/src/active.rs +++ b/compiler/rustc_feature/src/active.rs @@ -668,6 +668,9 @@ declare_features! ( /// Allows specifying the as-needed link modifier (active, native_link_modifiers_as_needed, "1.53.0", Some(81490), None), + /// Allows unnamed fields of struct and union type + (active, unnamed_fields, "1.53.0", Some(49804), None), + // ------------------------------------------------------------------------- // feature-group-end: actual feature gates // ------------------------------------------------------------------------- @@ -701,6 +704,7 @@ pub const INCOMPLETE_FEATURES: &[Symbol] = &[ sym::native_link_modifiers_whole_archive, sym::native_link_modifiers_as_needed, sym::rustc_insignificant_dtor, + 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 b2b578f1ed44a..c64fab0507c94 100644 --- a/compiler/rustc_parse/src/parser/item.rs +++ b/compiler/rustc_parse/src/parser/item.rs @@ -1236,7 +1236,7 @@ impl<'a> Parser<'a> { Ok((class_name, ItemKind::Union(vdata, generics))) } - fn parse_record_struct_body( + pub(super) fn parse_record_struct_body( &mut self, adt_ty: &str, ) -> PResult<'a, (Vec, /* recovered */ bool)> { @@ -1470,19 +1470,25 @@ impl<'a> Parser<'a> { fn parse_field_ident(&mut self, adt_ty: &str, lo: Span) -> PResult<'a, Ident> { let (ident, is_raw) = self.ident_or_err()?; if !is_raw && ident.is_reserved() { - let err = if self.check_fn_front_matter(false) { - let _ = self.parse_fn(&mut Vec::new(), |_| true, lo); - let mut err = self.struct_span_err( - lo.to(self.prev_token.span), - &format!("functions are not allowed in {} definitions", adt_ty), - ); - err.help("unlike in C++, Java, and C#, functions are declared in `impl` blocks"); - err.help("see https://doc.rust-lang.org/book/ch05-03-method-syntax.html for more information"); - err + if ident.name == kw::Underscore { + self.sess.gated_spans.gate(sym::unnamed_fields, lo); } else { - self.expected_ident_found() - }; - return Err(err); + let err = if self.check_fn_front_matter(false) { + let _ = self.parse_fn(&mut Vec::new(), |_| true, lo); + let mut err = self.struct_span_err( + lo.to(self.prev_token.span), + &format!("functions are not allowed in {} definitions", adt_ty), + ); + err.help( + "unlike in C++, Java, and C#, functions are declared in `impl` blocks", + ); + err.help("see https://doc.rust-lang.org/book/ch05-03-method-syntax.html for more information"); + err + } else { + self.expected_ident_found() + }; + return Err(err); + } } self.bump(); Ok(ident) diff --git a/compiler/rustc_parse/src/parser/ty.rs b/compiler/rustc_parse/src/parser/ty.rs index d537741c749c5..89cf2d7876e1d 100644 --- a/compiler/rustc_parse/src/parser/ty.rs +++ b/compiler/rustc_parse/src/parser/ty.rs @@ -226,6 +226,19 @@ impl<'a> Parser<'a> { } } else if self.eat_keyword(kw::Impl) { self.parse_impl_ty(&mut impl_dyn_multi)? + } else if self.token.is_keyword(kw::Union) + && self.look_ahead(1, |t| t == &token::OpenDelim(token::Brace)) + { + self.bump(); + let (fields, recovered) = self.parse_record_struct_body("union")?; + let span = lo.to(self.prev_token.span); + self.sess.gated_spans.gate(sym::unnamed_fields, span); + TyKind::AnonymousUnion(fields, recovered) + } else if self.eat_keyword(kw::Struct) { + let (fields, recovered) = self.parse_record_struct_body("struct")?; + let span = lo.to(self.prev_token.span); + self.sess.gated_spans.gate(sym::unnamed_fields, span); + TyKind::AnonymousStruct(fields, recovered) } else if self.is_explicit_dyn_type() { self.parse_dyn_ty(&mut impl_dyn_multi)? } else if self.eat_lt() { diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index c9816c2d59913..24d2618f1b275 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -1269,6 +1269,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..bd815dbcc9242 --- /dev/null +++ b/src/test/ui/feature-gates/feature-gate-unnamed_fields.rs @@ -0,0 +1,27 @@ +struct Foo { + foo: u8, + _: union { //~ ERROR unnamed fields are not yet fully implemented [E0658] + //~^ ERROR unnamed fields are not yet fully implemented [E0658] + //~| ERROR anonymous unions are unimplemented + bar: u8, + baz: u16 + } +} + +union Bar { + foobar: u8, + _: struct { //~ ERROR unnamed fields are not yet fully implemented [E0658] + //~^ ERROR unnamed fields are not yet fully implemented [E0658] + //~| ERROR anonymous structs are unimplemented + //~| ERROR unions may not contain fields that need dropping [E0740] + foobaz: u8, + barbaz: u16 + } +} + +struct S; +struct Baz { + _: S //~ ERROR unnamed fields are not yet fully implemented [E0658] +} + +fn main(){} diff --git a/src/test/ui/feature-gates/feature-gate-unnamed_fields.stderr b/src/test/ui/feature-gates/feature-gate-unnamed_fields.stderr new file mode 100644 index 0000000000000..4f3ab85c98792 --- /dev/null +++ b/src/test/ui/feature-gates/feature-gate-unnamed_fields.stderr @@ -0,0 +1,111 @@ +error[E0658]: unnamed fields are not yet fully implemented + --> $DIR/feature-gate-unnamed_fields.rs:3:5 + | +LL | _: union { + | ^ + | + = note: see issue #49804 for more information + = help: add `#![feature(unnamed_fields)]` to the crate attributes to enable + +error[E0658]: unnamed fields are not yet fully implemented + --> $DIR/feature-gate-unnamed_fields.rs:3:8 + | +LL | _: union { + | ________^ +LL | | +LL | | +LL | | bar: u8, +LL | | baz: u16 +LL | | } + | |_____^ + | + = note: see issue #49804 for more information + = help: add `#![feature(unnamed_fields)]` to the crate attributes to enable + +error[E0658]: unnamed fields are not yet fully implemented + --> $DIR/feature-gate-unnamed_fields.rs:13:5 + | +LL | _: struct { + | ^ + | + = note: see issue #49804 for more information + = help: add `#![feature(unnamed_fields)]` to the crate attributes to enable + +error[E0658]: unnamed fields are not yet fully implemented + --> $DIR/feature-gate-unnamed_fields.rs:13:8 + | +LL | _: struct { + | ________^ +LL | | +LL | | +LL | | +LL | | foobaz: u8, +LL | | barbaz: u16 +LL | | } + | |_____^ + | + = note: see issue #49804 for more information + = help: add `#![feature(unnamed_fields)]` to the crate attributes to enable + +error[E0658]: unnamed fields are not yet fully implemented + --> $DIR/feature-gate-unnamed_fields.rs:24:5 + | +LL | _: S + | ^ + | + = note: see issue #49804 for more information + = help: add `#![feature(unnamed_fields)]` to the crate attributes to enable + +error: anonymous unions are unimplemented + --> $DIR/feature-gate-unnamed_fields.rs:3:8 + | +LL | _: union { + | ________^ +LL | | +LL | | +LL | | bar: u8, +LL | | baz: u16 +LL | | } + | |_____^ + +error: anonymous structs are unimplemented + --> $DIR/feature-gate-unnamed_fields.rs:13:8 + | +LL | _: struct { + | ________^ +LL | | +LL | | +LL | | +LL | | foobaz: u8, +LL | | barbaz: u16 +LL | | } + | |_____^ + +error[E0740]: unions may not contain fields that need dropping + --> $DIR/feature-gate-unnamed_fields.rs:13:5 + | +LL | / _: struct { +LL | | +LL | | +LL | | +LL | | foobaz: u8, +LL | | barbaz: u16 +LL | | } + | |_____^ + | +note: `std::mem::ManuallyDrop` can be used to wrap the type + --> $DIR/feature-gate-unnamed_fields.rs:13:5 + | +LL | / _: struct { +LL | | +LL | | +LL | | +LL | | foobaz: u8, +LL | | barbaz: u16 +LL | | } + | |_____^ + +error: aborting due to 8 previous errors + +Some errors have detailed explanations: E0658, E0740. +For more information about an error, try `rustc --explain E0658`. diff --git a/src/test/ui/unnamed_fields/restrict_anonymous.rs b/src/test/ui/unnamed_fields/restrict_anonymous.rs new file mode 100644 index 0000000000000..99637d1105301 --- /dev/null +++ b/src/test/ui/unnamed_fields/restrict_anonymous.rs @@ -0,0 +1,52 @@ +#![allow(incomplete_features)] +#![feature(unnamed_fields)] + +fn f() -> struct { field: u8 } {} //~ ERROR anonymous structs are not allowed outside of unnamed struct or union fields +//~^ ERROR anonymous structs are unimplemented + +fn f2(a: struct { field: u8 } ) {} //~ ERROR anonymous structs are not allowed outside of unnamed struct or union fields +//~^ ERROR anonymous structs are unimplemented + +union G { + field: struct { field: u8 } //~ ERROR anonymous structs are not allowed outside of unnamed struct or union fields + //~^ ERROR anonymous structs are unimplemented +} +//~| ERROR unions may not contain fields that need dropping [E0740] + +struct H { _: u8 } // Should error after hir checks + +struct I(struct { field: u8 }, u8); //~ ERROR anonymous structs are not allowed outside of unnamed struct or union fields +//~^ ERROR anonymous structs are unimplemented + +enum J { + K(struct { field: u8 }), //~ ERROR anonymous structs are not allowed outside of unnamed struct or union fields + //~^ ERROR anonymous structs are unimplemented + L { + _ : struct { field: u8 } //~ ERROR anonymous structs are not allowed outside of unnamed struct or union fields + //~^ ERROR anonymous fields are not allowed outside of structs or unions + //~| ERROR anonymous structs are unimplemented + }, + M { + _ : u8 //~ ERROR anonymous fields are not allowed outside of structs or unions + } +} + +static M: union { field: u8 } = 0; //~ ERROR anonymous unions are not allowed outside of unnamed struct or union fields +//~^ ERROR anonymous unions are unimplemented + +type N = union { field: u8 }; //~ ERROR anonymous unions are not allowed outside of unnamed struct or union fields +//~^ ERROR anonymous unions are unimplemented + +fn main() { + const O: struct { field: u8 } = 0; //~ ERROR anonymous structs are not allowed outside of unnamed struct or union fields + //~^ ERROR anonymous structs are unimplemented + + let p: [struct { field: u8 }; 1]; //~ ERROR anonymous structs are not allowed outside of unnamed struct or union fields + //~^ ERROR anonymous structs are unimplemented + + let q: (struct { field: u8 }, u8); //~ ERROR anonymous structs are not allowed outside of unnamed struct or union fields + //~^ ERROR anonymous structs are unimplemented + + let cl = || -> struct { field: u8 } {}; //~ ERROR anonymous structs are not allowed outside of unnamed struct or union fields + //~^ ERROR anonymous structs are unimplemented +} diff --git a/src/test/ui/unnamed_fields/restrict_anonymous.stderr b/src/test/ui/unnamed_fields/restrict_anonymous.stderr new file mode 100644 index 0000000000000..efcf544fde4dc --- /dev/null +++ b/src/test/ui/unnamed_fields/restrict_anonymous.stderr @@ -0,0 +1,175 @@ +error: anonymous structs are not allowed outside of unnamed struct or union fields + --> $DIR/restrict_anonymous.rs:4:11 + | +LL | fn f() -> struct { field: u8 } {} + | ^^^^^^^^^^^^^^^^^^^^ anonymous struct declared here + +error: anonymous structs are not allowed outside of unnamed struct or union fields + --> $DIR/restrict_anonymous.rs:7:10 + | +LL | fn f2(a: struct { field: u8 } ) {} + | ^^^^^^^^^^^^^^^^^^^^ anonymous struct declared here + +error: anonymous structs are not allowed outside of unnamed struct or union fields + --> $DIR/restrict_anonymous.rs:11:12 + | +LL | field: struct { field: u8 } + | ^^^^^^^^^^^^^^^^^^^^ anonymous struct declared here + +error: anonymous structs are not allowed outside of unnamed struct or union fields + --> $DIR/restrict_anonymous.rs:18:10 + | +LL | struct I(struct { field: u8 }, u8); + | ^^^^^^^^^^^^^^^^^^^^ anonymous struct declared here + +error: anonymous structs are not allowed outside of unnamed struct or union fields + --> $DIR/restrict_anonymous.rs:22:7 + | +LL | K(struct { field: u8 }), + | ^^^^^^^^^^^^^^^^^^^^ anonymous struct declared here + +error: anonymous fields are not allowed outside of structs or unions + --> $DIR/restrict_anonymous.rs:25:9 + | +LL | _ : struct { field: u8 } + | -^^^^^^^^^^^^^^^^^^^^^^^ + | | + | anonymous field declared here + +error: anonymous structs are not allowed outside of unnamed struct or union fields + --> $DIR/restrict_anonymous.rs:25:13 + | +LL | _ : struct { field: u8 } + | ^^^^^^^^^^^^^^^^^^^^ anonymous struct declared here + +error: anonymous fields are not allowed outside of structs or unions + --> $DIR/restrict_anonymous.rs:30:9 + | +LL | _ : u8 + | -^^^^^ + | | + | anonymous field declared here + +error: anonymous unions are not allowed outside of unnamed struct or union fields + --> $DIR/restrict_anonymous.rs:34:11 + | +LL | static M: union { field: u8 } = 0; + | ^^^^^^^^^^^^^^^^^^^ anonymous union declared here + +error: anonymous unions are not allowed outside of unnamed struct or union fields + --> $DIR/restrict_anonymous.rs:37:10 + | +LL | type N = union { field: u8 }; + | ^^^^^^^^^^^^^^^^^^^ anonymous union declared here + +error: anonymous structs are not allowed outside of unnamed struct or union fields + --> $DIR/restrict_anonymous.rs:41:14 + | +LL | const O: struct { field: u8 } = 0; + | ^^^^^^^^^^^^^^^^^^^^ anonymous struct declared here + +error: anonymous structs are not allowed outside of unnamed struct or union fields + --> $DIR/restrict_anonymous.rs:44:13 + | +LL | let p: [struct { field: u8 }; 1]; + | ^^^^^^^^^^^^^^^^^^^^ anonymous struct declared here + +error: anonymous structs are not allowed outside of unnamed struct or union fields + --> $DIR/restrict_anonymous.rs:47:13 + | +LL | let q: (struct { field: u8 }, u8); + | ^^^^^^^^^^^^^^^^^^^^ anonymous struct declared here + +error: anonymous structs are not allowed outside of unnamed struct or union fields + --> $DIR/restrict_anonymous.rs:50:20 + | +LL | let cl = || -> struct { field: u8 } {}; + | ^^^^^^^^^^^^^^^^^^^^ anonymous struct declared here + +error: anonymous structs are unimplemented + --> $DIR/restrict_anonymous.rs:4:11 + | +LL | fn f() -> struct { field: u8 } {} + | ^^^^^^^^^^^^^^^^^^^^ + +error: anonymous structs are unimplemented + --> $DIR/restrict_anonymous.rs:7:10 + | +LL | fn f2(a: struct { field: u8 } ) {} + | ^^^^^^^^^^^^^^^^^^^^ + +error: anonymous structs are unimplemented + --> $DIR/restrict_anonymous.rs:11:12 + | +LL | field: struct { field: u8 } + | ^^^^^^^^^^^^^^^^^^^^ + +error: anonymous structs are unimplemented + --> $DIR/restrict_anonymous.rs:18:10 + | +LL | struct I(struct { field: u8 }, u8); + | ^^^^^^^^^^^^^^^^^^^^ + +error: anonymous structs are unimplemented + --> $DIR/restrict_anonymous.rs:22:7 + | +LL | K(struct { field: u8 }), + | ^^^^^^^^^^^^^^^^^^^^ + +error: anonymous structs are unimplemented + --> $DIR/restrict_anonymous.rs:25:13 + | +LL | _ : struct { field: u8 } + | ^^^^^^^^^^^^^^^^^^^^ + +error: anonymous unions are unimplemented + --> $DIR/restrict_anonymous.rs:34:11 + | +LL | static M: union { field: u8 } = 0; + | ^^^^^^^^^^^^^^^^^^^ + +error: anonymous unions are unimplemented + --> $DIR/restrict_anonymous.rs:37:10 + | +LL | type N = union { field: u8 }; + | ^^^^^^^^^^^^^^^^^^^ + +error: anonymous structs are unimplemented + --> $DIR/restrict_anonymous.rs:44:13 + | +LL | let p: [struct { field: u8 }; 1]; + | ^^^^^^^^^^^^^^^^^^^^ + +error: anonymous structs are unimplemented + --> $DIR/restrict_anonymous.rs:47:13 + | +LL | let q: (struct { field: u8 }, u8); + | ^^^^^^^^^^^^^^^^^^^^ + +error: anonymous structs are unimplemented + --> $DIR/restrict_anonymous.rs:50:20 + | +LL | let cl = || -> struct { field: u8 } {}; + | ^^^^^^^^^^^^^^^^^^^^ + +error: anonymous structs are unimplemented + --> $DIR/restrict_anonymous.rs:41:14 + | +LL | const O: struct { field: u8 } = 0; + | ^^^^^^^^^^^^^^^^^^^^ + +error[E0740]: unions may not contain fields that need dropping + --> $DIR/restrict_anonymous.rs:11:5 + | +LL | field: struct { field: u8 } + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: `std::mem::ManuallyDrop` can be used to wrap the type + --> $DIR/restrict_anonymous.rs:11:5 + | +LL | field: struct { field: u8 } + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 27 previous errors + +For more information about this error, try `rustc --explain E0740`. diff --git a/src/tools/rustfmt/src/items.rs b/src/tools/rustfmt/src/items.rs index ecbd0bd12ec6e..420484c0ba11e 100644 --- a/src/tools/rustfmt/src/items.rs +++ b/src/tools/rustfmt/src/items.rs @@ -6,7 +6,7 @@ use std::cmp::{max, min, Ordering}; use regex::Regex; use rustc_ast::visit; use rustc_ast::{ast, ptr}; -use rustc_span::{symbol, BytePos, Span, DUMMY_SP}; +use rustc_span::{symbol, BytePos, Span}; use crate::attr::filter_inline_attrs; use crate::comment::{ @@ -31,12 +31,7 @@ use crate::stmt::Stmt; use crate::utils::*; use crate::vertical::rewrite_with_alignment; use crate::visitor::FmtVisitor; - -const DEFAULT_VISIBILITY: ast::Visibility = ast::Visibility { - kind: ast::VisibilityKind::Inherited, - span: DUMMY_SP, - tokens: None, -}; +use crate::DEFAULT_VISIBILITY; fn type_annotation_separator(config: &Config) -> &str { colon_spaces(config) @@ -976,7 +971,7 @@ impl<'a> StructParts<'a> { format_header(context, self.prefix, self.ident, self.vis, offset) } - fn from_variant(variant: &'a ast::Variant) -> Self { + pub(crate) fn from_variant(variant: &'a ast::Variant) -> Self { StructParts { prefix: "", ident: variant.ident, diff --git a/src/tools/rustfmt/src/lib.rs b/src/tools/rustfmt/src/lib.rs index 8a798777e0e80..cde5d390cf259 100644 --- a/src/tools/rustfmt/src/lib.rs +++ b/src/tools/rustfmt/src/lib.rs @@ -31,7 +31,7 @@ use std::rc::Rc; use ignore; use rustc_ast::ast; -use rustc_span::symbol; +use rustc_span::{symbol, DUMMY_SP}; use thiserror::Error; use crate::comment::LineClasses; @@ -95,6 +95,11 @@ mod types; mod vertical; pub(crate) mod visitor; +const DEFAULT_VISIBILITY: ast::Visibility = ast::Visibility { + kind: ast::VisibilityKind::Inherited, + span: DUMMY_SP, + tokens: None, +}; /// The various errors that can occur during formatting. Note that not all of /// these can currently be propagated to clients. #[derive(Error, Debug)] diff --git a/src/tools/rustfmt/src/types.rs b/src/tools/rustfmt/src/types.rs index cda17e13eebb0..5597af9ee320c 100644 --- a/src/tools/rustfmt/src/types.rs +++ b/src/tools/rustfmt/src/types.rs @@ -2,14 +2,14 @@ use std::iter::ExactSizeIterator; use std::ops::Deref; use rustc_ast::ast::{self, FnRetTy, Mutability}; -use rustc_span::{symbol::kw, BytePos, Pos, Span}; +use rustc_span::{symbol::kw, symbol::Ident, BytePos, Pos, Span}; -use crate::comment::{combine_strs_with_missing_comments, contains_comment}; use crate::config::lists::*; use crate::config::{IndentStyle, TypeDensity, Version}; use crate::expr::{ format_expr, rewrite_assign_rhs, rewrite_call, rewrite_tuple, rewrite_unary_prefix, ExprType, }; +use crate::items::StructParts; use crate::lists::{ definitive_tactic, itemize_list, write_list, ListFormatting, ListItem, Separator, }; @@ -24,6 +24,11 @@ use crate::utils::{ colon_spaces, extra_offset, first_line_width, format_extern, format_mutability, last_line_extendable, last_line_width, mk_sp, rewrite_ident, }; +use crate::DEFAULT_VISIBILITY; +use crate::{ + comment::{combine_strs_with_missing_comments, contains_comment}, + items::format_struct_struct, +}; #[derive(Copy, Clone, Debug, Eq, PartialEq)] pub(crate) enum PathContext { @@ -764,6 +769,54 @@ impl Rewrite for ast::Ty { ast::TyKind::Tup(ref items) => { rewrite_tuple(context, items.iter(), self.span, shape, items.len() == 1) } + ast::TyKind::AnonymousStruct(ref fields, recovered) => { + let ident = Ident::new( + kw::Struct, + mk_sp(self.span.lo(), self.span.lo() + BytePos(6)), + ); + let data = ast::VariantData::Struct(fields.clone(), recovered); + let variant = ast::Variant { + attrs: vec![], + id: self.id, + span: self.span, + vis: DEFAULT_VISIBILITY, + ident, + data, + disr_expr: None, + is_placeholder: false, + }; + format_struct_struct( + &context, + &StructParts::from_variant(&variant), + fields, + shape.indent, + None, + ) + } + ast::TyKind::AnonymousUnion(ref fields, recovered) => { + let ident = Ident::new( + kw::Union, + mk_sp(self.span.lo(), self.span.lo() + BytePos(5)), + ); + let data = ast::VariantData::Struct(fields.clone(), recovered); + let variant = ast::Variant { + attrs: vec![], + id: self.id, + span: self.span, + vis: DEFAULT_VISIBILITY, + ident, + data, + disr_expr: None, + is_placeholder: false, + }; + format_struct_struct( + &context, + &StructParts::from_variant(&variant), + fields, + shape.indent, + None, + ) + } ast::TyKind::Path(ref q_self, ref path) => { rewrite_path(context, PathContext::Type, q_self.as_ref(), path, shape) }