From d2a6a93201ad707d1bace2ce4fc2efdd5cfe162f Mon Sep 17 00:00:00 2001 From: carbotaniuman <41451839+carbotaniuman@users.noreply.github.com> Date: Sat, 28 Jan 2023 17:39:55 -0600 Subject: [PATCH 1/2] Work on unnamed struct and union fields --- compiler/rustc_ast/src/ast.rs | 4 + compiler/rustc_ast/src/mut_visit.rs | 4 + compiler/rustc_ast/src/visit.rs | 3 + compiler/rustc_ast_lowering/src/item.rs | 5 +- compiler/rustc_ast_lowering/src/lib.rs | 13 +- .../rustc_ast_passes/src/ast_validation.rs | 116 ++++++- compiler/rustc_ast_passes/src/feature_gate.rs | 1 + compiler/rustc_ast_pretty/src/pprust/state.rs | 8 + .../rustc_ast_pretty/src/pprust/state/item.rs | 6 +- compiler/rustc_feature/src/active.rs | 2 + compiler/rustc_parse/src/parser/item.rs | 294 +++++++++++------- compiler/rustc_parse/src/parser/ty.rs | 120 ++++++- compiler/rustc_passes/src/hir_stats.rs | 2 + compiler/rustc_span/src/symbol.rs | 1 + src/tools/rustfmt/src/types.rs | 2 + tests/pretty/anonymous-types.rs | 19 ++ .../feature-gate-unnamed_fields.rs | 26 ++ .../feature-gate-unnamed_fields.stderr | 84 +++++ .../ui/parser/keyword-union-as-identifier.rs | 22 ++ .../restrict_anonymous_structs.rs | 63 ++++ .../restrict_anonymous_structs.stderr | 198 ++++++++++++ .../restrict_anonymous_unions.rs | 62 ++++ .../restrict_anonymous_unions.stderr | 198 ++++++++++++ 23 files changed, 1135 insertions(+), 118 deletions(-) create mode 100644 tests/pretty/anonymous-types.rs create mode 100644 tests/ui/feature-gates/feature-gate-unnamed_fields.rs create mode 100644 tests/ui/feature-gates/feature-gate-unnamed_fields.stderr create mode 100644 tests/ui/parser/keyword-union-as-identifier.rs create mode 100644 tests/ui/unnamed-fields/restrict_anonymous_structs.rs create mode 100644 tests/ui/unnamed-fields/restrict_anonymous_structs.stderr create mode 100644 tests/ui/unnamed-fields/restrict_anonymous_unions.rs create mode 100644 tests/ui/unnamed-fields/restrict_anonymous_unions.stderr diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index 8ad3270c5103e..7d932efbdf055 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -2072,6 +2072,10 @@ pub enum TyKind { Never, /// A tuple (`(A, B, C, D,...)`). Tup(Vec>), + /// An anonymous struct type i.e. `struct { foo: Type }` + AnonymousStruct(Vec, /* recovered */ bool), + /// An anonymous union type i.e. `union { bar: Type }` + AnonymousUnion(Vec, /* recovered */ 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 1dd62626b8f5e..fa2f48610360a 100644 --- a/compiler/rustc_ast/src/mut_visit.rs +++ b/compiler/rustc_ast/src/mut_visit.rs @@ -493,6 +493,10 @@ 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, _recovered) + | TyKind::AnonymousUnion(fields, _recovered) => { + 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 e7b2e4b1cb4b0..af88a3e06b904 100644 --- a/compiler/rustc_ast/src/visit.rs +++ b/compiler/rustc_ast/src/visit.rs @@ -430,6 +430,9 @@ 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::AnonymousStruct(ref fields, ..) | TyKind::AnonymousUnion(ref fields, ..) => { + walk_list!(visitor, visit_field_def, fields) + } } } diff --git a/compiler/rustc_ast_lowering/src/item.rs b/compiler/rustc_ast_lowering/src/item.rs index 5d2589cb2b2f7..5ec96b1448841 100644 --- a/compiler/rustc_ast_lowering/src/item.rs +++ b/compiler/rustc_ast_lowering/src/item.rs @@ -673,7 +673,10 @@ impl<'hir> LoweringContext<'_, 'hir> { } } - fn lower_field_def(&mut self, (index, f): (usize, &FieldDef)) -> hir::FieldDef<'hir> { + pub(super) fn lower_field_def( + &mut self, + (index, f): (usize, &FieldDef), + ) -> hir::FieldDef<'hir> { let ty = if let TyKind::Path(qself, path) = &f.ty.kind { let t = self.lower_path_ty( &f.ty, diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index 35849a6b944e4..60eb3909bca87 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -34,8 +34,8 @@ #![feature(let_chains)] #![feature(never_type)] #![recursion_limit = "256"] -#![deny(rustc::untranslatable_diagnostic)] -#![deny(rustc::diagnostic_outside_of_impl)] +// #![deny(rustc::untranslatable_diagnostic)] +// #![deny(rustc::diagnostic_outside_of_impl)] #[macro_use] extern crate tracing; @@ -1237,6 +1237,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.tcx.sess.struct_span_err(t.span, "anonymous structs are unimplemented").emit(); + hir::TyKind::Err + } + TyKind::AnonymousUnion(ref _fields, _recovered) => { + self.tcx.sess.struct_span_err(t.span, "anonymous unions are unimplemented").emit(); + hir::TyKind::Err + } 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 902b4b1a1ecfe..dc765eb9f5225 100644 --- a/compiler/rustc_ast_passes/src/ast_validation.rs +++ b/compiler/rustc_ast_passes/src/ast_validation.rs @@ -243,10 +243,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() } @@ -288,6 +308,66 @@ impl<'a> AstValidator<'a> { } } + 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_trait_fn_not_const(&self, constness: Const) { if let Const::Yes(span) = constness { self.session.emit_err(TraitFnConst { span }); @@ -974,6 +1054,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> { fn visit_ty(&mut self, ty: &'a Ty) { self.visit_ty_common(ty); + self.deny_anonymous_struct(ty); self.walk_ty(ty) } @@ -988,6 +1069,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> { } fn visit_field_def(&mut self, field: &'a FieldDef) { + self.deny_anonymous_field(field); visit::walk_field_def(self, field) } @@ -1179,10 +1261,42 @@ impl<'a> Visitor<'a> for AstValidator<'a> { self.check_mod_file_item_asciionly(item.ident); } } - ItemKind::Union(vdata, ..) => { + ItemKind::Struct(vdata, 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(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(vdata, generics) => { if vdata.fields().is_empty() { self.err_handler().span_err(item.span, "unions cannot have zero fields"); } + match vdata { + VariantData::Struct(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 89ba6f936d144..ec1e565cbf1bd 100644 --- a/compiler/rustc_ast_passes/src/feature_gate.rs +++ b/compiler/rustc_ast_passes/src/feature_gate.rs @@ -545,6 +545,7 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session) { gate_all!(inline_const_pat, "inline-const in pattern position is experimental"); gate_all!(associated_const_equality, "associated const equality is incomplete"); gate_all!(yeet_expr, "`do yeet` expression is experimental"); + 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 6a8064b0e874e..1fdc85512b16d 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state.rs @@ -1041,6 +1041,14 @@ impl<'a> State<'a> { } self.pclose(); } + ast::TyKind::AnonymousStruct(fields, _recovered) => { + self.head("struct"); + self.print_record_struct_body(&fields, ty.span); + } + ast::TyKind::AnonymousUnion(fields, _recovered) => { + 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 bf2c73a66a2cb..6c047559354cb 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state/item.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state/item.rs @@ -426,7 +426,11 @@ impl<'a> State<'a> { } } - fn print_record_struct_body(&mut self, fields: &[ast::FieldDef], span: rustc_span::Span) { + pub(crate) fn print_record_struct_body( + &mut self, + fields: &[ast::FieldDef], + span: rustc_span::Span, + ) { self.nbsp(); self.bopen(); diff --git a/compiler/rustc_feature/src/active.rs b/compiler/rustc_feature/src/active.rs index 196c31302a0d2..925c3574a9892 100644 --- a/compiler/rustc_feature/src/active.rs +++ b/compiler/rustc_feature/src/active.rs @@ -531,6 +531,8 @@ declare_features! ( (active, type_changing_struct_update, "1.58.0", Some(86555), None), /// Enables rustc to generate code that instructs libstd to NOT ignore SIGPIPE. (active, unix_sigpipe, "1.65.0", Some(97889), None), + /// Allows unnamed fields of struct and union type + (incomplete, unnamed_fields, "1.68.0", Some(49804), None), /// Allows unsized fn parameters. (active, unsized_fn_params, "1.49.0", Some(48055), None), /// Allows unsized rvalues at arguments and parameters. diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs index 53680a82bdc2e..40573a5d1a5b3 100644 --- a/compiler/rustc_parse/src/parser/item.rs +++ b/compiler/rustc_parse/src/parser/item.rs @@ -16,7 +16,10 @@ use rustc_ast::{EnumDef, FieldDef, Generics, TraitRef, Ty, TyKind, Variant, Vari use rustc_ast::{FnHeader, ForeignItem, Path, PathSegment, Visibility, VisibilityKind}; use rustc_ast::{MacCall, MacDelimiter}; use rustc_ast_pretty::pprust; -use rustc_errors::{struct_span_err, Applicability, IntoDiagnostic, PResult, StashKey}; +use rustc_errors::{ + struct_span_err, Applicability, DiagnosticBuilder, ErrorGuaranteed, IntoDiagnostic, PResult, + StashKey, +}; use rustc_span::edition::Edition; use rustc_span::lev_distance::lev_distance; use rustc_span::source_map::{self, Span}; @@ -583,52 +586,103 @@ impl<'a> Parser<'a> { let polarity = self.parse_polarity(); + let mut snapshot_before_last_ty = self.create_snapshot_for_diagnostic(); // Parse both types and traits as a type, then reinterpret if necessary. let err_path = |span| ast::Path::from_ident(Ident::new(kw::Empty, span)); - let ty_first = if self.token.is_keyword(kw::For) && self.look_ahead(1, |t| t != &token::Lt) - { - let span = self.prev_token.span.between(self.token.span); - self.struct_span_err(span, "missing trait in a trait impl") - .span_suggestion( + let mut ty_first = + if self.token.is_keyword(kw::For) && self.look_ahead(1, |t| t != &token::Lt) { + let span = self.prev_token.span.between(self.token.span); + self.struct_span_err(span, "missing trait in a trait impl") + .span_suggestion( + span, + "add a trait here", + " Trait ", + Applicability::HasPlaceholders, + ) + .span_suggestion( + span.to(self.token.span), + "for an inherent impl, drop this `for`", + "", + Applicability::MaybeIncorrect, + ) + .emit(); + P(Ty { + kind: TyKind::Path(None, err_path(span)), span, - "add a trait here", - " Trait ", - Applicability::HasPlaceholders, - ) - .span_suggestion( - span.to(self.token.span), - "for an inherent impl, drop this `for`", - "", - Applicability::MaybeIncorrect, - ) - .emit(); - P(Ty { - kind: TyKind::Path(None, err_path(span)), - span, - id: DUMMY_NODE_ID, - tokens: None, - }) - } else { - self.parse_ty_with_generics_recovery(&generics)? - }; + id: DUMMY_NODE_ID, + tokens: None, + }) + } else { + self.parse_ty_with_generics_recovery(&generics)? + }; // If `for` is missing we try to recover. let has_for = self.eat_keyword(kw::For); let missing_for_span = self.prev_token.span.between(self.token.span); - let ty_second = if self.token == token::DotDot { + let mut ty_second = if self.token == token::DotDot { // We need to report this error after `cfg` expansion for compatibility reasons self.bump(); // `..`, do not add it to expected tokens Some(self.mk_ty(self.prev_token.span, TyKind::Err)) } else if has_for || self.token.can_begin_type() { - Some(self.parse_ty()?) + snapshot_before_last_ty = self.create_snapshot_for_diagnostic(); + Some(self.parse_ty_no_anon_recovery()?) } else { None }; generics.where_clause = self.parse_where_clause()?; - let impl_items = self.parse_item_list(attrs, |p| p.parse_impl_item(ForceCollect::No))?; + let (mut impl_items, err) = + self.parse_item_list(attrs, |p| p.parse_impl_item(ForceCollect::No))?; + + if let Some(mut err) = err { + let mut snapshot = snapshot_before_last_ty; + + if snapshot.can_start_anonymous_type() { + let recover_result = { + let recover_last_ty = match snapshot.parse_ty() { + Ok(ty) => Some(ty), + Err(snapshot_err) => { + snapshot_err.cancel(); + None + } + }; + + let impl_items = match snapshot + .parse_item_list(attrs, |p| p.parse_impl_item(ForceCollect::No)) + { + Ok((impl_items, None)) => Some(impl_items), + Ok((_, Some(snapshot_err))) => { + snapshot_err.cancel(); + None + } + Err(snapshot_err) => { + snapshot_err.cancel(); + None + } + }; + + (recover_last_ty, impl_items) + }; + + if let (Some(recover_last_ty), Some(new_impl_items)) = recover_result { + err.delay_as_bug(); + self.restore_snapshot(snapshot); + + if ty_second.is_some() { + ty_second = Some(recover_last_ty); + } else { + ty_first = recover_last_ty; + } + impl_items = new_impl_items; + } else { + err.emit(); + } + } else { + err.emit(); + } + } let item_kind = match ty_second { Some(ty_second) => { @@ -688,20 +742,21 @@ impl<'a> Parser<'a> { &mut self, attrs: &mut AttrVec, mut parse_item: impl FnMut(&mut Parser<'a>) -> PResult<'a, Option>>, - ) -> PResult<'a, Vec> { + ) -> PResult<'a, (Vec, Option>)> { let open_brace_span = self.token.span; // Recover `impl Ty;` instead of `impl Ty {}` if self.token == TokenKind::Semi { self.sess.emit_err(UseEmptyBlockNotSemi { span: self.token.span }); self.bump(); - return Ok(vec![]); + return Ok((vec![], None)); } self.expect(&token::OpenDelim(Delimiter::Brace))?; attrs.extend(self.parse_inner_attributes()?); let mut items = Vec::new(); + let mut delayed_err = None; while !self.eat(&token::CloseDelim(Delimiter::Brace)) { if self.recover_doc_comment_before_brace() { continue; @@ -763,20 +818,21 @@ impl<'a> Parser<'a> { Applicability::MaybeIncorrect, ); } - err.emit(); + delayed_err = Some(err); break; } Ok(Some(item)) => items.extend(item), Err(mut err) => { self.consume_block(Delimiter::Brace, ConsumeClosingDelim::Yes); err.span_label(open_brace_span, "while parsing this item list starting here") - .span_label(self.prev_token.span, "the item list ends here") - .emit(); + .span_label(self.prev_token.span, "the item list ends here"); + + delayed_err = Some(err); break; } } } - Ok(items) + Ok((items, delayed_err)) } /// Recover on a doc comment before `}`. @@ -872,7 +928,13 @@ impl<'a> Parser<'a> { } else { // It's a normal trait. generics.where_clause = self.parse_where_clause()?; - let items = self.parse_item_list(attrs, |p| p.parse_trait_item(ForceCollect::No))?; + let (items, err) = + self.parse_item_list(attrs, |p| p.parse_trait_item(ForceCollect::No))?; + + if let Some(mut err) = err { + err.emit(); + } + Ok(( ident, ItemKind::Trait(Box::new(Trait { is_auto, unsafety, generics, bounds, items })), @@ -1146,11 +1208,14 @@ impl<'a> Parser<'a> { unsafety = Unsafe::Yes(self.token.span); self.eat_keyword(kw::Unsafe); } - let module = ast::ForeignMod { - unsafety, - abi, - items: self.parse_item_list(attrs, |p| p.parse_foreign_item(ForceCollect::No))?, - }; + + let (items, err) = + self.parse_item_list(attrs, |p| p.parse_foreign_item(ForceCollect::No))?; + if let Some(mut err) = err { + err.emit(); + } + + let module = ast::ForeignMod { unsafety, abi, items }; Ok((Ident::empty(), ItemKind::ForeignMod(module))) } @@ -1537,7 +1602,7 @@ impl<'a> Parser<'a> { Ok((class_name, ItemKind::Union(vdata, generics))) } - fn parse_record_struct_body( + pub(crate) fn parse_record_struct_body( &mut self, adt_ty: &str, ident_span: Span, @@ -1794,7 +1859,7 @@ impl<'a> Parser<'a> { ) -> PResult<'a, FieldDef> { let name = self.parse_field_ident(adt_ty, lo)?; self.expect_field_ty_separator()?; - let ty = self.parse_ty()?; + let ty = self.parse_ty_for_field_def()?; if self.token.kind == token::Colon && self.look_ahead(1, |tok| tok.kind != token::Colon) { self.struct_span_err(self.token.span, "found single colon in a struct field type path") .span_suggestion_verbose( @@ -1835,79 +1900,86 @@ impl<'a> Parser<'a> { let (ident, is_raw) = self.ident_or_err()?; if !is_raw && ident.is_reserved() { let snapshot = self.create_snapshot_for_diagnostic(); - let err = if self.check_fn_front_matter(false, Case::Sensitive) { - let inherited_vis = Visibility { - span: rustc_span::DUMMY_SP, - kind: VisibilityKind::Inherited, - tokens: None, - }; - // We use `parse_fn` to get a span for the function - let fn_parse_mode = FnParseMode { req_name: |_| true, req_body: true }; - match self.parse_fn( - &mut AttrVec::new(), - fn_parse_mode, - lo, - &inherited_vis, - Case::Insensitive, - ) { - Ok(_) => { - let mut err = self.struct_span_err( - lo.to(self.prev_token.span), - &format!("functions are not allowed in {adt_ty} definitions"), - ); - 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 { + let err = if self.check_fn_front_matter(false, Case::Sensitive) { + let inherited_vis = Visibility { + span: rustc_span::DUMMY_SP, + kind: VisibilityKind::Inherited, + tokens: None, + }; + // We use `parse_fn` to get a span for the function + let fn_parse_mode = FnParseMode { req_name: |_| true, req_body: true }; + match self.parse_fn( + &mut AttrVec::new(), + fn_parse_mode, + lo, + &inherited_vis, + Case::Insensitive, + ) { + Ok(_) => { + let mut err = self.struct_span_err( + lo.to(self.prev_token.span), + &format!("functions are not allowed in {adt_ty} definitions"), + ); + 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 + } + Err(err) => { + err.cancel(); + self.restore_snapshot(snapshot); + self.expected_ident_found() + } } - Err(err) => { - err.cancel(); - self.restore_snapshot(snapshot); - self.expected_ident_found() + } else if self.eat_keyword(kw::Struct) { + match self.parse_item_struct() { + Ok((ident, _)) => { + let mut err = self.struct_span_err( + lo.with_hi(ident.span.hi()), + &format!("structs are not allowed in {adt_ty} definitions"), + ); + err.help( + "consider creating a new `struct` definition instead of nesting", + ); + err + } + Err(err) => { + err.cancel(); + self.restore_snapshot(snapshot); + self.expected_ident_found() + } } - } - } else if self.eat_keyword(kw::Struct) { - match self.parse_item_struct() { - Ok((ident, _)) => { - let mut err = self.struct_span_err( - lo.with_hi(ident.span.hi()), - &format!("structs are not allowed in {adt_ty} definitions"), + } else { + let mut err = self.expected_ident_found(); + if self.eat_keyword_noexpect(kw::Let) + && let removal_span = self.prev_token.span.until(self.token.span) + && let Ok(ident) = self.parse_ident_common(false) + // Cancel this error, we don't need it. + .map_err(|err| err.cancel()) + && self.token.kind == TokenKind::Colon + { + err.span_suggestion( + removal_span, + "remove this `let` keyword", + String::new(), + Applicability::MachineApplicable, ); - err.help("consider creating a new `struct` definition instead of nesting"); - err - } - Err(err) => { - err.cancel(); + err.note("the `let` keyword is not allowed in `struct` fields"); + err.note("see for more information"); + err.emit(); + return Ok(ident); + } else { self.restore_snapshot(snapshot); - self.expected_ident_found() } - } - } else { - let mut err = self.expected_ident_found(); - if self.eat_keyword_noexpect(kw::Let) - && let removal_span = self.prev_token.span.until(self.token.span) - && let Ok(ident) = self.parse_ident_common(false) - // Cancel this error, we don't need it. - .map_err(|err| err.cancel()) - && self.token.kind == TokenKind::Colon - { - err.span_suggestion( - removal_span, - "remove this `let` keyword", - String::new(), - Applicability::MachineApplicable, - ); - err.note("the `let` keyword is not allowed in `struct` fields"); - err.note("see for more information"); - err.emit(); - return Ok(ident); - } else { - self.restore_snapshot(snapshot); - } - err - }; - return Err(err); + err + }; + 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 25de0a9e75014..7aad7bca253a2 100644 --- a/compiler/rustc_parse/src/parser/ty.rs +++ b/compiler/rustc_parse/src/parser/ty.rs @@ -62,6 +62,13 @@ pub(super) enum RecoverAnonEnum { No, } +#[derive(Copy, Clone, PartialEq)] +pub(super) enum AllowAnonymousTypes { + Yes, + OnlyRecover, + No, +} + /// Signals whether parsing a type should recover `->`. /// /// More specifically, when parsing a function like: @@ -120,6 +127,20 @@ impl<'a> Parser<'a> { None, RecoverQuestionMark::Yes, RecoverAnonEnum::No, + AllowAnonymousTypes::OnlyRecover, + ) + } + + pub(super) fn parse_ty_no_anon_recovery(&mut self) -> PResult<'a, P> { + self.parse_ty_common( + AllowPlus::Yes, + AllowCVariadic::No, + RecoverQPath::Yes, + RecoverReturnSign::Yes, + None, + RecoverQuestionMark::Yes, + RecoverAnonEnum::No, + AllowAnonymousTypes::No, ) } @@ -135,6 +156,23 @@ impl<'a> Parser<'a> { Some(ty_params), RecoverQuestionMark::Yes, RecoverAnonEnum::No, + AllowAnonymousTypes::No, + ) + } + + /// 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> { + self.parse_ty_common( + AllowPlus::Yes, + AllowCVariadic::No, + RecoverQPath::Yes, + RecoverReturnSign::Yes, + None, + RecoverQuestionMark::Yes, + RecoverAnonEnum::No, + AllowAnonymousTypes::Yes, ) } @@ -150,6 +188,7 @@ impl<'a> Parser<'a> { None, RecoverQuestionMark::Yes, RecoverAnonEnum::Yes, + AllowAnonymousTypes::OnlyRecover, ) } @@ -168,6 +207,7 @@ impl<'a> Parser<'a> { None, RecoverQuestionMark::Yes, RecoverAnonEnum::No, + AllowAnonymousTypes::OnlyRecover, ) } @@ -182,6 +222,7 @@ impl<'a> Parser<'a> { None, RecoverQuestionMark::No, RecoverAnonEnum::No, + AllowAnonymousTypes::OnlyRecover, ) } @@ -194,6 +235,7 @@ impl<'a> Parser<'a> { None, RecoverQuestionMark::No, RecoverAnonEnum::No, + AllowAnonymousTypes::OnlyRecover, ) } @@ -207,6 +249,7 @@ impl<'a> Parser<'a> { None, RecoverQuestionMark::Yes, RecoverAnonEnum::No, + AllowAnonymousTypes::OnlyRecover, ) } @@ -227,6 +270,7 @@ impl<'a> Parser<'a> { None, RecoverQuestionMark::Yes, RecoverAnonEnum::Yes, + AllowAnonymousTypes::OnlyRecover, )?; FnRetTy::Ty(ty) } else if recover_return_sign.can_recover(&self.token.kind) { @@ -249,6 +293,7 @@ impl<'a> Parser<'a> { None, RecoverQuestionMark::Yes, RecoverAnonEnum::Yes, + AllowAnonymousTypes::OnlyRecover, )?; FnRetTy::Ty(ty) } else { @@ -265,6 +310,7 @@ impl<'a> Parser<'a> { ty_generics: Option<&Generics>, recover_question_mark: RecoverQuestionMark, recover_anon_enum: RecoverAnonEnum, + allow_anonymous_types: AllowAnonymousTypes, ) -> PResult<'a, P> { let allow_qpath_recovery = recover_qpath == RecoverQPath::Yes; maybe_recover_from_interpolated_ty_qpath!(self, allow_qpath_recovery); @@ -312,6 +358,10 @@ impl<'a> Parser<'a> { } } else if self.eat_keyword(kw::Impl) { self.parse_impl_ty(&mut impl_dyn_multi)? + } else if allow_anonymous_types == AllowAnonymousTypes::Yes + && self.can_start_anonymous_type() + { + self.parse_anonymous_ty(lo)? } else if self.is_explicit_dyn_type() { self.parse_dyn_ty(&mut impl_dyn_multi)? } else if self.eat_lt() { @@ -319,7 +369,24 @@ impl<'a> Parser<'a> { let (qself, path) = self.parse_qpath(PathStyle::Type)?; TyKind::Path(Some(qself), path) } else if self.check_path() { - self.parse_path_start_ty(lo, allow_plus, ty_generics)? + // `union` is a weak keyword, so it can possibly be a path + if allow_anonymous_types == AllowAnonymousTypes::OnlyRecover + && self.can_start_anonymous_type() + { + // If we can parse an anonymous type, do so and let + // ast validation error out later + let snapshot = self.create_snapshot_for_diagnostic(); + match self.parse_anonymous_ty(lo) { + Ok(ty) => ty, + Err(err) => { + err.cancel(); + self.restore_snapshot(snapshot); + self.parse_path_start_ty(lo, allow_plus, ty_generics)? + } + } + } else { + self.parse_path_start_ty(lo, allow_plus, ty_generics)? + } } else if self.can_begin_bound() { self.parse_bare_trait_object(lo, allow_plus)? } else if self.eat(&token::DotDotDot) { @@ -336,7 +403,27 @@ impl<'a> Parser<'a> { let mut err = self.struct_span_err(self.token.span, &msg); err.span_label(self.token.span, "expected type"); self.maybe_annotate_with_ascription(&mut err, true); - return Err(err); + + // `struct` is a strict keyword, so we can check it here as + // we will be erroring otherwise + // FIXME: or should we not? + if self.token.is_keyword(kw::Struct) { + // Recover the parser from anonymous types anywhere other than field types. + let snapshot = self.create_snapshot_for_diagnostic(); + match self.parse_anonymous_ty(lo) { + Ok(ty) => { + err.delay_as_bug(); + ty + } + Err(snapshot_err) => { + snapshot_err.cancel(); + self.restore_snapshot(snapshot); + return Err(err); + } + } + } else { + return Err(err); + } }; let span = lo.to(self.prev_token.span); @@ -370,6 +457,7 @@ impl<'a> Parser<'a> { ty_generics, recover_question_mark, RecoverAnonEnum::No, + AllowAnonymousTypes::OnlyRecover, )?); } let mut err = self.struct_span_err(pipes, "anonymous enums are not supported"); @@ -395,6 +483,28 @@ impl<'a> Parser<'a> { if allow_qpath_recovery { self.maybe_recover_from_bad_qpath(ty) } else { Ok(ty) } } + pub(crate) fn parse_anonymous_ty(&mut self, lo: Span) -> PResult<'a, TyKind> { + assert!(self.token.is_keyword(kw::Union) || self.token.is_keyword(kw::Struct)); + let is_union = self.token.is_keyword(kw::Union); + + let span = self.token.span; + self.bump(); + + // FIXME: does false go here + self.parse_record_struct_body(if is_union { "union" } else { "struct" }, span, false).map( + |(fields, recovered)| { + 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_anonymous_struct`. + return if is_union { + TyKind::AnonymousUnion(fields, recovered) + } else { + TyKind::AnonymousStruct(fields, recovered) + }; + }, + ) + } + /// Parses either: /// - `(TYPE)`, a parenthesized type. /// - `(TYPE,)`, a tuple with a single field of type TYPE. @@ -824,6 +934,12 @@ impl<'a> Parser<'a> { Ok(bounds) } + pub(super) fn can_start_anonymous_type(&mut self) -> bool { + (self.token.is_keyword(kw::Union) + && self.look_ahead(1, |t| t == &token::OpenDelim(Delimiter::Brace))) + || self.token.is_keyword(kw::Struct) + } + /// 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 d1b896e940e6e..cc1d75b5d3360 100644 --- a/compiler/rustc_passes/src/hir_stats.rs +++ b/compiler/rustc_passes/src/hir_stats.rs @@ -584,6 +584,8 @@ impl<'v> ast_visit::Visitor<'v> for StatCollector<'v> { BareFn, Never, Tup, + AnonymousStruct, + AnonymousUnion, Path, TraitObject, ImplTrait, diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 30ca0b8060d22..20a4e2aa26477 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -1535,6 +1535,7 @@ symbols! { unix_sigpipe, unlikely, unmarked_api, + unnamed_fields, unpin, unreachable, unreachable_2015, diff --git a/src/tools/rustfmt/src/types.rs b/src/tools/rustfmt/src/types.rs index 01e2fb6e61e15..21374bc972a85 100644 --- a/src/tools/rustfmt/src/types.rs +++ b/src/tools/rustfmt/src/types.rs @@ -799,6 +799,8 @@ impl Rewrite for ast::Ty { ast::TyKind::Tup(ref items) => { rewrite_tuple(context, items.iter(), self.span, shape, items.len() == 1) } + ast::TyKind::AnonymousStruct(_, _) => Some(context.snippet(self.span).to_owned()), + ast::TyKind::AnonymousUnion(_, _) => Some(context.snippet(self.span).to_owned()), ast::TyKind::Path(ref q_self, ref path) => { rewrite_path(context, PathContext::Type, q_self, path, shape) } diff --git a/tests/pretty/anonymous-types.rs b/tests/pretty/anonymous-types.rs new file mode 100644 index 0000000000000..8e08c314ed1fb --- /dev/null +++ b/tests/pretty/anonymous-types.rs @@ -0,0 +1,19 @@ +// Test for issue 85480 +// Pretty print anonymous struct and union types + +// pp-exact +// pretty-compare-only + +struct Foo { + _: union { + _: struct { + a: u8, + b: u16, + }, + c: u32, + }, + d: u64, + e: f32, +} + +fn main() {} diff --git a/tests/ui/feature-gates/feature-gate-unnamed_fields.rs b/tests/ui/feature-gates/feature-gate-unnamed_fields.rs new file mode 100644 index 0000000000000..4bbd0c83bfbca --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-unnamed_fields.rs @@ -0,0 +1,26 @@ +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 + foobaz: u8, + barbaz: u16 + } +} + +struct S; +struct Baz { + _: S //~ ERROR unnamed fields are not yet fully implemented [E0658] +} + +fn main(){} diff --git a/tests/ui/feature-gates/feature-gate-unnamed_fields.stderr b/tests/ui/feature-gates/feature-gate-unnamed_fields.stderr new file mode 100644 index 0000000000000..f026f2c3600b3 --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-unnamed_fields.stderr @@ -0,0 +1,84 @@ +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 | | 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:23: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 | | foobaz: u8, +LL | | barbaz: u16 +LL | | } + | |_____^ + +error: aborting due to 7 previous errors + +For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/parser/keyword-union-as-identifier.rs b/tests/ui/parser/keyword-union-as-identifier.rs new file mode 100644 index 0000000000000..f9b88afc8782e --- /dev/null +++ b/tests/ui/parser/keyword-union-as-identifier.rs @@ -0,0 +1,22 @@ +// check-pass + +#![allow(non_camel_case_types)] + +mod union { + type union = i32; + + pub struct Bar { + pub union: union, + } + + pub fn union() -> Bar { + Bar { + union: 5 + } + } +} + +fn main() { + let union = union::union(); + let _ = union.union; +} diff --git a/tests/ui/unnamed-fields/restrict_anonymous_structs.rs b/tests/ui/unnamed-fields/restrict_anonymous_structs.rs new file mode 100644 index 0000000000000..2670e5335885d --- /dev/null +++ b/tests/ui/unnamed-fields/restrict_anonymous_structs.rs @@ -0,0 +1,63 @@ +#![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 + + +struct F { + field: struct { field: u8 } //~ ERROR anonymous structs are not allowed outside of unnamed struct or union fields + //~^ ERROR anonymous structs are unimplemented +} + +union G { + field1: struct { field: u8 } //~ ERROR anonymous structs are not allowed outside of unnamed struct or union fields + //~^ ERROR anonymous structs are unimplemented +} + +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 + } +} + +const L: struct { field: u8 } = 0; //~ ERROR anonymous structs are not allowed outside of unnamed struct or union fields +//~^ ERROR anonymous structs are unimplemented + +static M: struct { field: u8 } = 0; //~ ERROR anonymous structs are not allowed outside of unnamed struct or union fields +//~^ ERROR anonymous structs are unimplemented + +type N = struct { field: u8 }; //~ ERROR anonymous structs are not allowed outside of unnamed struct or union fields +//~^ ERROR anonymous structs are unimplemented + +impl struct { field: u8 } {} //~ ERROR anonymous structs are not allowed outside of unnamed struct or union fields +//~^ ERROR anonymous structs are unimplemented + +trait Foo {} + +impl Foo for struct { field: u8 } {} //~ ERROR anonymous structs are not allowed outside of unnamed struct or union fields +//~^ ERROR anonymous structs are unimplemented + +fn main() { + 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 c = || -> struct { field: u8 } {}; //~ ERROR anonymous structs are not allowed outside of unnamed struct or union fields + //~^ ERROR anonymous structs are unimplemented +} diff --git a/tests/ui/unnamed-fields/restrict_anonymous_structs.stderr b/tests/ui/unnamed-fields/restrict_anonymous_structs.stderr new file mode 100644 index 0000000000000..7d2b5ba627ad4 --- /dev/null +++ b/tests/ui/unnamed-fields/restrict_anonymous_structs.stderr @@ -0,0 +1,198 @@ +error: anonymous structs are not allowed outside of unnamed struct or union fields + --> $DIR/restrict_anonymous_structs.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_structs.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_structs.rs:12: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_structs.rs:17:13 + | +LL | field1: struct { field: u8 } + | ^^^^^^^^^^^^^^^^^^^^ anonymous struct declared here + +error: anonymous structs are not allowed outside of unnamed struct or union fields + --> $DIR/restrict_anonymous_structs.rs:21: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_structs.rs:25:7 + | +LL | K(struct { field: u8 }), + | ^^^^^^^^^^^^^^^^^^^^ anonymous struct declared here + +error: anonymous fields are not allowed outside of structs or unions + --> $DIR/restrict_anonymous_structs.rs:28: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_structs.rs:28:13 + | +LL | _ : struct { field: u8 } + | ^^^^^^^^^^^^^^^^^^^^ anonymous struct declared here + +error: anonymous fields are not allowed outside of structs or unions + --> $DIR/restrict_anonymous_structs.rs:33:9 + | +LL | _ : u8 + | -^^^^^ + | | + | anonymous field declared here + +error: anonymous structs are not allowed outside of unnamed struct or union fields + --> $DIR/restrict_anonymous_structs.rs:37:10 + | +LL | const L: struct { field: u8 } = 0; + | ^^^^^^^^^^^^^^^^^^^^ anonymous struct declared here + +error: anonymous structs are not allowed outside of unnamed struct or union fields + --> $DIR/restrict_anonymous_structs.rs:40:11 + | +LL | static M: struct { field: u8 } = 0; + | ^^^^^^^^^^^^^^^^^^^^ anonymous struct declared here + +error: anonymous structs are not allowed outside of unnamed struct or union fields + --> $DIR/restrict_anonymous_structs.rs:43:10 + | +LL | type N = struct { field: u8 }; + | ^^^^^^^^^^^^^^^^^^^^ anonymous struct declared here + +error: anonymous structs are not allowed outside of unnamed struct or union fields + --> $DIR/restrict_anonymous_structs.rs:46:6 + | +LL | impl struct { field: u8 } {} + | ^^^^^^^^^^^^^^^^^^^^ anonymous struct declared here + +error: anonymous structs are not allowed outside of unnamed struct or union fields + --> $DIR/restrict_anonymous_structs.rs:51:14 + | +LL | impl Foo for struct { field: u8 } {} + | ^^^^^^^^^^^^^^^^^^^^ anonymous struct declared here + +error: anonymous structs are not allowed outside of unnamed struct or union fields + --> $DIR/restrict_anonymous_structs.rs:55: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_structs.rs:58: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_structs.rs:61:19 + | +LL | let c = || -> struct { field: u8 } {}; + | ^^^^^^^^^^^^^^^^^^^^ anonymous struct declared here + +error: anonymous structs are unimplemented + --> $DIR/restrict_anonymous_structs.rs:4:11 + | +LL | fn f() -> struct { field: u8 } {} + | ^^^^^^^^^^^^^^^^^^^^ + +error: anonymous structs are unimplemented + --> $DIR/restrict_anonymous_structs.rs:7:10 + | +LL | fn f2(a: struct { field: u8 } ) {} + | ^^^^^^^^^^^^^^^^^^^^ + +error: anonymous structs are unimplemented + --> $DIR/restrict_anonymous_structs.rs:12:12 + | +LL | field: struct { field: u8 } + | ^^^^^^^^^^^^^^^^^^^^ + +error: anonymous structs are unimplemented + --> $DIR/restrict_anonymous_structs.rs:17:13 + | +LL | field1: struct { field: u8 } + | ^^^^^^^^^^^^^^^^^^^^ + +error: anonymous structs are unimplemented + --> $DIR/restrict_anonymous_structs.rs:21:10 + | +LL | struct I(struct { field: u8 }, u8); + | ^^^^^^^^^^^^^^^^^^^^ + +error: anonymous structs are unimplemented + --> $DIR/restrict_anonymous_structs.rs:25:7 + | +LL | K(struct { field: u8 }), + | ^^^^^^^^^^^^^^^^^^^^ + +error: anonymous structs are unimplemented + --> $DIR/restrict_anonymous_structs.rs:28:13 + | +LL | _ : struct { field: u8 } + | ^^^^^^^^^^^^^^^^^^^^ + +error: anonymous structs are unimplemented + --> $DIR/restrict_anonymous_structs.rs:37:10 + | +LL | const L: struct { field: u8 } = 0; + | ^^^^^^^^^^^^^^^^^^^^ + +error: anonymous structs are unimplemented + --> $DIR/restrict_anonymous_structs.rs:40:11 + | +LL | static M: struct { field: u8 } = 0; + | ^^^^^^^^^^^^^^^^^^^^ + +error: anonymous structs are unimplemented + --> $DIR/restrict_anonymous_structs.rs:43:10 + | +LL | type N = struct { field: u8 }; + | ^^^^^^^^^^^^^^^^^^^^ + +error: anonymous structs are unimplemented + --> $DIR/restrict_anonymous_structs.rs:46:6 + | +LL | impl struct { field: u8 } {} + | ^^^^^^^^^^^^^^^^^^^^ + +error: anonymous structs are unimplemented + --> $DIR/restrict_anonymous_structs.rs:51:14 + | +LL | impl Foo for struct { field: u8 } {} + | ^^^^^^^^^^^^^^^^^^^^ + +error: anonymous structs are unimplemented + --> $DIR/restrict_anonymous_structs.rs:55:13 + | +LL | let p: [struct { field: u8 }; 1]; + | ^^^^^^^^^^^^^^^^^^^^ + +error: anonymous structs are unimplemented + --> $DIR/restrict_anonymous_structs.rs:58:13 + | +LL | let q: (struct { field: u8 }, u8); + | ^^^^^^^^^^^^^^^^^^^^ + +error: anonymous structs are unimplemented + --> $DIR/restrict_anonymous_structs.rs:61:19 + | +LL | let c = || -> struct { field: u8 } {}; + | ^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 32 previous errors + diff --git a/tests/ui/unnamed-fields/restrict_anonymous_unions.rs b/tests/ui/unnamed-fields/restrict_anonymous_unions.rs new file mode 100644 index 0000000000000..910830f1c4806 --- /dev/null +++ b/tests/ui/unnamed-fields/restrict_anonymous_unions.rs @@ -0,0 +1,62 @@ +#![allow(incomplete_features)] +#![feature(unnamed_fields)] + +fn f() -> union { field: u8 } {} //~ ERROR anonymous unions are not allowed outside of unnamed struct or union fields +//~^ ERROR anonymous unions are unimplemented + +fn f2(a: union { field: u8 } ) {} //~ ERROR anonymous unions are not allowed outside of unnamed struct or union fields +//~^ ERROR anonymous unions are unimplemented + +struct F { + field: union { field: u8 } //~ ERROR anonymous unions are not allowed outside of unnamed struct or union fields + //~^ ERROR anonymous unions are unimplemented +} + +union G { + field: union { field: u8 } //~ ERROR anonymous unions are not allowed outside of unnamed struct or union fields + //~^ ERROR anonymous unions are unimplemented +} + +struct I(union { field: u8 }, u8); //~ ERROR anonymous unions are not allowed outside of unnamed struct or union fields +//~^ ERROR anonymous unions are unimplemented + +enum J { + K(union { field: u8 }), //~ ERROR anonymous unions are not allowed outside of unnamed struct or union fields + //~^ ERROR anonymous unions are unimplemented + L { + _ : union { field: u8 } //~ ERROR anonymous unions are not allowed outside of unnamed struct or union fields + //~^ ERROR anonymous fields are not allowed outside of structs or unions + //~| ERROR anonymous unions are unimplemented + }, + M { + _ : u8 //~ ERROR anonymous fields are not allowed outside of structs or unions + } +} + +const L: union { field: u8 } = 0; //~ ERROR anonymous unions are not allowed outside of unnamed struct or union fields +//~^ ERROR anonymous unions are unimplemented + +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 + +impl union { field: u8 } {} //~ ERROR anonymous unions are not allowed outside of unnamed struct or union fields +// //~^ ERROR anonymous unions are unimplemented + +trait Foo {} + +impl Foo for union { field: u8 } {} //~ ERROR anonymous unions are not allowed outside of unnamed struct or union fields +//~^ ERROR anonymous unions are unimplemented + +fn main() { + let p: [union { field: u8 }; 1]; //~ ERROR anonymous unions are not allowed outside of unnamed struct or union fields + //~^ ERROR anonymous unions are unimplemented + + let q: (union { field: u8 }, u8); //~ ERROR anonymous unions are not allowed outside of unnamed struct or union fields + //~^ ERROR anonymous unions are unimplemented + + let c = || -> union { field: u8 } {}; //~ ERROR anonymous unions are not allowed outside of unnamed struct or union fields + //~^ ERROR anonymous unions are unimplemented +} diff --git a/tests/ui/unnamed-fields/restrict_anonymous_unions.stderr b/tests/ui/unnamed-fields/restrict_anonymous_unions.stderr new file mode 100644 index 0000000000000..31fff541d5ce5 --- /dev/null +++ b/tests/ui/unnamed-fields/restrict_anonymous_unions.stderr @@ -0,0 +1,198 @@ +error: anonymous unions are not allowed outside of unnamed struct or union fields + --> $DIR/restrict_anonymous_unions.rs:4:11 + | +LL | fn f() -> union { field: u8 } {} + | ^^^^^^^^^^^^^^^^^^^ anonymous union declared here + +error: anonymous unions are not allowed outside of unnamed struct or union fields + --> $DIR/restrict_anonymous_unions.rs:7:10 + | +LL | fn f2(a: union { field: u8 } ) {} + | ^^^^^^^^^^^^^^^^^^^ anonymous union declared here + +error: anonymous unions are not allowed outside of unnamed struct or union fields + --> $DIR/restrict_anonymous_unions.rs:11:12 + | +LL | field: union { field: u8 } + | ^^^^^^^^^^^^^^^^^^^ anonymous union declared here + +error: anonymous unions are not allowed outside of unnamed struct or union fields + --> $DIR/restrict_anonymous_unions.rs:16:12 + | +LL | field: union { field: u8 } + | ^^^^^^^^^^^^^^^^^^^ anonymous union declared here + +error: anonymous unions are not allowed outside of unnamed struct or union fields + --> $DIR/restrict_anonymous_unions.rs:20:10 + | +LL | struct I(union { field: u8 }, u8); + | ^^^^^^^^^^^^^^^^^^^ anonymous union declared here + +error: anonymous unions are not allowed outside of unnamed struct or union fields + --> $DIR/restrict_anonymous_unions.rs:24:7 + | +LL | K(union { field: u8 }), + | ^^^^^^^^^^^^^^^^^^^ anonymous union declared here + +error: anonymous fields are not allowed outside of structs or unions + --> $DIR/restrict_anonymous_unions.rs:27:9 + | +LL | _ : union { field: u8 } + | -^^^^^^^^^^^^^^^^^^^^^^ + | | + | anonymous field declared here + +error: anonymous unions are not allowed outside of unnamed struct or union fields + --> $DIR/restrict_anonymous_unions.rs:27:13 + | +LL | _ : union { field: u8 } + | ^^^^^^^^^^^^^^^^^^^ anonymous union declared here + +error: anonymous fields are not allowed outside of structs or unions + --> $DIR/restrict_anonymous_unions.rs:32:9 + | +LL | _ : u8 + | -^^^^^ + | | + | anonymous field declared here + +error: anonymous unions are not allowed outside of unnamed struct or union fields + --> $DIR/restrict_anonymous_unions.rs:36:10 + | +LL | const L: union { field: u8 } = 0; + | ^^^^^^^^^^^^^^^^^^^ anonymous union declared here + +error: anonymous unions are not allowed outside of unnamed struct or union fields + --> $DIR/restrict_anonymous_unions.rs:39: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_unions.rs:42:10 + | +LL | type N = union { field: u8 }; + | ^^^^^^^^^^^^^^^^^^^ anonymous union declared here + +error: anonymous unions are not allowed outside of unnamed struct or union fields + --> $DIR/restrict_anonymous_unions.rs:45:6 + | +LL | impl union { field: u8 } {} + | ^^^^^^^^^^^^^^^^^^^ anonymous union declared here + +error: anonymous unions are not allowed outside of unnamed struct or union fields + --> $DIR/restrict_anonymous_unions.rs:50:14 + | +LL | impl Foo for union { field: u8 } {} + | ^^^^^^^^^^^^^^^^^^^ anonymous union declared here + +error: anonymous unions are not allowed outside of unnamed struct or union fields + --> $DIR/restrict_anonymous_unions.rs:54:13 + | +LL | let p: [union { field: u8 }; 1]; + | ^^^^^^^^^^^^^^^^^^^ anonymous union declared here + +error: anonymous unions are not allowed outside of unnamed struct or union fields + --> $DIR/restrict_anonymous_unions.rs:57:13 + | +LL | let q: (union { field: u8 }, u8); + | ^^^^^^^^^^^^^^^^^^^ anonymous union declared here + +error: anonymous unions are not allowed outside of unnamed struct or union fields + --> $DIR/restrict_anonymous_unions.rs:60:19 + | +LL | let c = || -> union { field: u8 } {}; + | ^^^^^^^^^^^^^^^^^^^ anonymous union declared here + +error: anonymous unions are unimplemented + --> $DIR/restrict_anonymous_unions.rs:4:11 + | +LL | fn f() -> union { field: u8 } {} + | ^^^^^^^^^^^^^^^^^^^ + +error: anonymous unions are unimplemented + --> $DIR/restrict_anonymous_unions.rs:7:10 + | +LL | fn f2(a: union { field: u8 } ) {} + | ^^^^^^^^^^^^^^^^^^^ + +error: anonymous unions are unimplemented + --> $DIR/restrict_anonymous_unions.rs:11:12 + | +LL | field: union { field: u8 } + | ^^^^^^^^^^^^^^^^^^^ + +error: anonymous unions are unimplemented + --> $DIR/restrict_anonymous_unions.rs:16:12 + | +LL | field: union { field: u8 } + | ^^^^^^^^^^^^^^^^^^^ + +error: anonymous unions are unimplemented + --> $DIR/restrict_anonymous_unions.rs:20:10 + | +LL | struct I(union { field: u8 }, u8); + | ^^^^^^^^^^^^^^^^^^^ + +error: anonymous unions are unimplemented + --> $DIR/restrict_anonymous_unions.rs:24:7 + | +LL | K(union { field: u8 }), + | ^^^^^^^^^^^^^^^^^^^ + +error: anonymous unions are unimplemented + --> $DIR/restrict_anonymous_unions.rs:27:13 + | +LL | _ : union { field: u8 } + | ^^^^^^^^^^^^^^^^^^^ + +error: anonymous unions are unimplemented + --> $DIR/restrict_anonymous_unions.rs:36:10 + | +LL | const L: union { field: u8 } = 0; + | ^^^^^^^^^^^^^^^^^^^ + +error: anonymous unions are unimplemented + --> $DIR/restrict_anonymous_unions.rs:39:11 + | +LL | static M: union { field: u8 } = 0; + | ^^^^^^^^^^^^^^^^^^^ + +error: anonymous unions are unimplemented + --> $DIR/restrict_anonymous_unions.rs:42:10 + | +LL | type N = union { field: u8 }; + | ^^^^^^^^^^^^^^^^^^^ + +error: anonymous unions are unimplemented + --> $DIR/restrict_anonymous_unions.rs:45:6 + | +LL | impl union { field: u8 } {} + | ^^^^^^^^^^^^^^^^^^^ + +error: anonymous unions are unimplemented + --> $DIR/restrict_anonymous_unions.rs:50:14 + | +LL | impl Foo for union { field: u8 } {} + | ^^^^^^^^^^^^^^^^^^^ + +error: anonymous unions are unimplemented + --> $DIR/restrict_anonymous_unions.rs:54:13 + | +LL | let p: [union { field: u8 }; 1]; + | ^^^^^^^^^^^^^^^^^^^ + +error: anonymous unions are unimplemented + --> $DIR/restrict_anonymous_unions.rs:57:13 + | +LL | let q: (union { field: u8 }, u8); + | ^^^^^^^^^^^^^^^^^^^ + +error: anonymous unions are unimplemented + --> $DIR/restrict_anonymous_unions.rs:60:19 + | +LL | let c = || -> union { field: u8 } {}; + | ^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 32 previous errors + From 0a38b14cc4d9a05377dec4ca7f7b6d1a827cfc6a Mon Sep 17 00:00:00 2001 From: carbotaniuman <41451839+carbotaniuman@users.noreply.github.com> Date: Sun, 29 Jan 2023 06:12:58 -0600 Subject: [PATCH 2/2] Be more conservative --- compiler/rustc_parse/src/parser/ty.rs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/compiler/rustc_parse/src/parser/ty.rs b/compiler/rustc_parse/src/parser/ty.rs index 7aad7bca253a2..9b24470a6634a 100644 --- a/compiler/rustc_parse/src/parser/ty.rs +++ b/compiler/rustc_parse/src/parser/ty.rs @@ -404,10 +404,9 @@ impl<'a> Parser<'a> { err.span_label(self.token.span, "expected type"); self.maybe_annotate_with_ascription(&mut err, true); - // `struct` is a strict keyword, so we can check it here as - // we will be erroring otherwise - // FIXME: or should we not? - if self.token.is_keyword(kw::Struct) { + if allow_anonymous_types == AllowAnonymousTypes::OnlyRecover + && self.token.is_keyword(kw::Struct) + { // Recover the parser from anonymous types anywhere other than field types. let snapshot = self.create_snapshot_for_diagnostic(); match self.parse_anonymous_ty(lo) {