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)
}