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