Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Parse unnamed fields and anonymous structs or unions #114782

Closed
4 changes: 4 additions & 0 deletions compiler/rustc_ast/src/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2092,6 +2092,10 @@ pub enum TyKind {
Never,
/// A tuple (`(A, B, C, D,...)`).
Tup(ThinVec<P<Ty>>),
/// An anonymous struct type i.e. `struct { foo: Type }`
AnonymousStruct(ThinVec<FieldDef>),
/// An anonymous union type i.e. `union { bar: Type }`
AnonymousUnion(ThinVec<FieldDef>),
frank-king marked this conversation as resolved.
Show resolved Hide resolved
/// A path (`module::module::...::Type`), optionally
/// "qualified", e.g., `<Vec<T> as SomeTrait>::SomeType`.
///
Expand Down
3 changes: 3 additions & 0 deletions compiler/rustc_ast/src/mut_visit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -509,6 +509,9 @@ pub fn noop_visit_ty<T: MutVisitor>(ty: &mut P<Ty>, 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);
Expand Down
3 changes: 3 additions & 0 deletions compiler/rustc_ast/src/visit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -438,6 +438,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)
}
}
}

Expand Down
12 changes: 12 additions & 0 deletions compiler/rustc_ast_lowering/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1293,6 +1293,18 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
TyKind::Err => {
hir::TyKind::Err(self.tcx.sess.delay_span_bug(t.span, "TyKind::Err lowered"))
}
// FIXME(unnamed_fields): IMPLEMENTATION IN PROGRESS
#[allow(rustc::untranslatable_diagnostic)]
#[allow(rustc::diagnostic_outside_of_impl)]
TyKind::AnonymousStruct(ref _fields) => hir::TyKind::Err(
self.tcx.sess.span_err(t.span, "anonymous structs are unimplemented"),
),
// FIXME(unnamed_fields): IMPLEMENTATION IN PROGRESS
#[allow(rustc::untranslatable_diagnostic)]
#[allow(rustc::diagnostic_outside_of_impl)]
TyKind::AnonymousUnion(ref _fields) => hir::TyKind::Err(
self.tcx.sess.span_err(t.span, "anonymous unions are unimplemented"),
),
TyKind::Slice(ty) => hir::TyKind::Slice(self.lower_ty(ty, itctx)),
TyKind::Ptr(mt) => hir::TyKind::Ptr(self.lower_mt(mt, itctx)),
TyKind::Ref(region, mt) => {
Expand Down
117 changes: 116 additions & 1 deletion compiler/rustc_ast_passes/src/ast_validation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -219,10 +219,30 @@ impl<'a> AstValidator<'a> {
}
}
}
TyKind::AnonymousStruct(ref fields, ..) | TyKind::AnonymousUnion(ref fields, ..) => {
// self.with_banned_assoc_ty_bound(|this| {
walk_list!(self, visit_struct_field_def, fields)
// });
}
frank-king marked this conversation as resolved.
Show resolved Hide resolved
_ => 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;
}
}
frank-king marked this conversation as resolved.
Show resolved Hide resolved
self.visit_field_def(field);
}

fn err_handler(&self) -> &rustc_errors::Handler {
&self.session.diagnostic()
}
Expand Down Expand Up @@ -260,6 +280,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();
}
}
frank-king marked this conversation as resolved.
Show resolved Hide resolved
}

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();
}
_ => {}
}
frank-king marked this conversation as resolved.
Show resolved Hide resolved
}

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();
}
}
frank-king marked this conversation as resolved.
Show resolved Hide resolved
}

fn check_trait_fn_not_const(&self, constness: Const) {
if let Const::Yes(span) = constness {
self.session.emit_err(errors::TraitFnConst { span });
Expand Down Expand Up @@ -785,6 +865,8 @@ impl<'a> Visitor<'a> for AstValidator<'a> {

fn visit_ty(&mut self, ty: &'a Ty) {
self.visit_ty_common(ty);
tracing::info!(?ty);
self.deny_anonymous_struct(ty);
self.walk_ty(ty)
}

Expand All @@ -799,6 +881,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)
}

Expand Down Expand Up @@ -991,10 +1074,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!(self, 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().emit_err(errors::FieldlessUnion { span: item.span });
}
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| {
frank-king marked this conversation as resolved.
Show resolved Hide resolved
walk_list!(self, visit_struct_field_def, fields);
// });
walk_list!(self, visit_attribute, &item.attrs);
return;
}
_ => {}
}
}
ItemKind::Const(box ConstItem { defaultness, expr: None, .. }) => {
self.check_defaultness(item.span, *defaultness);
Expand Down
1 change: 1 addition & 0 deletions compiler/rustc_ast_passes/src/feature_gate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -570,6 +570,7 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session) {
gate_all!(builtin_syntax, "`builtin #` syntax is unstable");
gate_all!(explicit_tail_calls, "`become` expression is experimental");
gate_all!(generic_const_items, "generic const items are experimental");
gate_all!(unnamed_fields, "unnamed fields are not yet fully implemented");

if !visitor.features.negative_bounds {
for &span in spans.get(&sym::negative_bounds).iter().copied().flatten() {
Expand Down
5 changes: 3 additions & 2 deletions compiler/rustc_ast_passes/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,9 @@
#![feature(iter_is_partitioned)]
#![feature(let_chains)]
#![recursion_limit = "256"]
#![deny(rustc::untranslatable_diagnostic)]
#![deny(rustc::diagnostic_outside_of_impl)]
// FIXME(unnamed_field): unncomment these two lints
// #![deny(rustc::untranslatable_diagnostic)]
// #![deny(rustc::diagnostic_outside_of_impl)]

use rustc_errors::{DiagnosticMessage, SubdiagnosticMessage};
use rustc_fluent_macro::fluent_messages;
Expand Down
8 changes: 8 additions & 0 deletions compiler/rustc_ast_pretty/src/pprust/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1053,6 +1053,14 @@ impl<'a> State<'a> {
}
self.pclose();
}
ast::TyKind::AnonymousStruct(fields) => {
self.head("struct");
self.print_record_struct_body(&fields, ty.span);
}
ast::TyKind::AnonymousUnion(fields) => {
self.head("union");
self.print_record_struct_body(&fields, ty.span);
}
ast::TyKind::Paren(typ) => {
self.popen();
self.print_type(typ);
Expand Down
6 changes: 5 additions & 1 deletion compiler/rustc_ast_pretty/src/pprust/state/item.rs
Original file line number Diff line number Diff line change
Expand Up @@ -443,7 +443,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();

Expand Down
2 changes: 2 additions & 0 deletions compiler/rustc_feature/src/active.rs
Original file line number Diff line number Diff line change
Expand Up @@ -581,6 +581,8 @@ declare_features! (
(active, type_privacy_lints, "1.72.0", Some(48054), 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.
Expand Down
Loading
Loading