Skip to content

Commit 9964284

Browse files
committed
Auto merge of #84571 - jedel1043:issue-49804-impl, r=petrochenkov
Parse unnamed fields of struct and union type Added the `unnamed_fields` feature gate. This is a prototype of [RFC 2102](#49804), so any suggestions are greatly appreciated. r? `@petrochenkov`
2 parents 44ec846 + 64acb7d commit 9964284

File tree

19 files changed

+707
-112
lines changed

19 files changed

+707
-112
lines changed

compiler/rustc_ast/src/ast.rs

+4
Original file line numberDiff line numberDiff line change
@@ -1861,6 +1861,10 @@ pub enum TyKind {
18611861
Never,
18621862
/// A tuple (`(A, B, C, D,...)`).
18631863
Tup(Vec<P<Ty>>),
1864+
/// An anonymous struct type i.e. `struct { foo: Type }`
1865+
AnonymousStruct(Vec<FieldDef>, bool),
1866+
/// An anonymous union type i.e. `union { bar: Type }`
1867+
AnonymousUnion(Vec<FieldDef>, bool),
18641868
/// A path (`module::module::...::Type`), optionally
18651869
/// "qualified", e.g., `<Vec<T> as SomeTrait>::SomeType`.
18661870
///

compiler/rustc_ast/src/mut_visit.rs

+3
Original file line numberDiff line numberDiff line change
@@ -484,6 +484,9 @@ pub fn noop_visit_ty<T: MutVisitor>(ty: &mut P<Ty>, vis: &mut T) {
484484
visit_vec(bounds, |bound| vis.visit_param_bound(bound));
485485
}
486486
TyKind::MacCall(mac) => vis.visit_mac_call(mac),
487+
TyKind::AnonymousStruct(fields, ..) | TyKind::AnonymousUnion(fields, ..) => {
488+
fields.flat_map_in_place(|field| vis.flat_map_field_def(field));
489+
}
487490
}
488491
vis.visit_span(span);
489492
visit_lazy_tts(tokens, vis);

compiler/rustc_ast/src/visit.rs

+3
Original file line numberDiff line numberDiff line change
@@ -404,6 +404,9 @@ pub fn walk_ty<'a, V: Visitor<'a>>(visitor: &mut V, typ: &'a Ty) {
404404
TyKind::Typeof(ref expression) => visitor.visit_anon_const(expression),
405405
TyKind::Infer | TyKind::ImplicitSelf | TyKind::Err => {}
406406
TyKind::MacCall(ref mac) => visitor.visit_mac_call(mac),
407+
TyKind::AnonymousStruct(ref fields, ..) | TyKind::AnonymousUnion(ref fields, ..) => {
408+
walk_list!(visitor, visit_field_def, fields)
409+
}
407410
TyKind::Never | TyKind::CVarArgs => {}
408411
}
409412
}

compiler/rustc_ast_lowering/src/item.rs

+4-1
Original file line numberDiff line numberDiff line change
@@ -789,7 +789,10 @@ impl<'hir> LoweringContext<'_, 'hir> {
789789
}
790790
}
791791

792-
fn lower_field_def(&mut self, (index, f): (usize, &FieldDef)) -> hir::FieldDef<'hir> {
792+
pub(super) fn lower_field_def(
793+
&mut self,
794+
(index, f): (usize, &FieldDef),
795+
) -> hir::FieldDef<'hir> {
793796
let ty = if let TyKind::Path(ref qself, ref path) = f.ty.kind {
794797
let t = self.lower_path_ty(
795798
&f.ty,

compiler/rustc_ast_lowering/src/lib.rs

+9
Original file line numberDiff line numberDiff line change
@@ -1267,6 +1267,15 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
12671267
let kind = match t.kind {
12681268
TyKind::Infer => hir::TyKind::Infer,
12691269
TyKind::Err => hir::TyKind::Err,
1270+
// FIXME(unnamed_fields): IMPLEMENTATION IN PROGRESS
1271+
TyKind::AnonymousStruct(ref _fields, _recovered) => {
1272+
self.sess.struct_span_err(t.span, "anonymous structs are unimplemented").emit();
1273+
hir::TyKind::Err
1274+
}
1275+
TyKind::AnonymousUnion(ref _fields, _recovered) => {
1276+
self.sess.struct_span_err(t.span, "anonymous unions are unimplemented").emit();
1277+
hir::TyKind::Err
1278+
}
12701279
TyKind::Slice(ref ty) => hir::TyKind::Slice(self.lower_ty(ty, itctx)),
12711280
TyKind::Ptr(ref mt) => hir::TyKind::Ptr(self.lower_mt(mt, itctx)),
12721281
TyKind::Rptr(ref region, ref mt) => {

compiler/rustc_ast_passes/src/ast_validation.rs

+184-70
Original file line numberDiff line numberDiff line change
@@ -175,10 +175,30 @@ impl<'a> AstValidator<'a> {
175175
}
176176
}
177177
}
178+
TyKind::AnonymousStruct(ref fields, ..) | TyKind::AnonymousUnion(ref fields, ..) => {
179+
self.with_banned_assoc_ty_bound(|this| {
180+
walk_list!(this, visit_struct_field_def, fields)
181+
});
182+
}
178183
_ => visit::walk_ty(self, t),
179184
}
180185
}
181186

187+
fn visit_struct_field_def(&mut self, field: &'a FieldDef) {
188+
if let Some(ident) = field.ident {
189+
if ident.name == kw::Underscore {
190+
self.check_anonymous_field(field);
191+
self.visit_vis(&field.vis);
192+
self.visit_ident(ident);
193+
self.visit_ty_common(&field.ty);
194+
self.walk_ty(&field.ty);
195+
walk_list!(self, visit_attribute, &field.attrs);
196+
return;
197+
}
198+
}
199+
self.visit_field_def(field);
200+
}
201+
182202
fn err_handler(&self) -> &rustc_errors::Handler {
183203
&self.session.diagnostic()
184204
}
@@ -213,6 +233,66 @@ impl<'a> AstValidator<'a> {
213233
err.emit();
214234
}
215235

236+
fn check_anonymous_field(&self, field: &FieldDef) {
237+
let FieldDef { ty, .. } = field;
238+
match &ty.kind {
239+
TyKind::AnonymousStruct(..) | TyKind::AnonymousUnion(..) => {
240+
// We already checked for `kw::Underscore` before calling this function,
241+
// so skip the check
242+
}
243+
TyKind::Path(..) => {
244+
// If the anonymous field contains a Path as type, we can't determine
245+
// if the path is a valid struct or union, so skip the check
246+
}
247+
_ => {
248+
let msg = "unnamed fields can only have struct or union types";
249+
let label = "not a struct or union";
250+
self.err_handler()
251+
.struct_span_err(field.span, msg)
252+
.span_label(ty.span, label)
253+
.emit();
254+
}
255+
}
256+
}
257+
258+
fn deny_anonymous_struct(&self, ty: &Ty) {
259+
match &ty.kind {
260+
TyKind::AnonymousStruct(..) => {
261+
self.err_handler()
262+
.struct_span_err(
263+
ty.span,
264+
"anonymous structs are not allowed outside of unnamed struct or union fields",
265+
)
266+
.span_label(ty.span, "anonymous struct declared here")
267+
.emit();
268+
}
269+
TyKind::AnonymousUnion(..) => {
270+
self.err_handler()
271+
.struct_span_err(
272+
ty.span,
273+
"anonymous unions are not allowed outside of unnamed struct or union fields",
274+
)
275+
.span_label(ty.span, "anonymous union declared here")
276+
.emit();
277+
}
278+
_ => {}
279+
}
280+
}
281+
282+
fn deny_anonymous_field(&self, field: &FieldDef) {
283+
if let Some(ident) = field.ident {
284+
if ident.name == kw::Underscore {
285+
self.err_handler()
286+
.struct_span_err(
287+
field.span,
288+
"anonymous fields are not allowed outside of structs or unions",
289+
)
290+
.span_label(ident.span, "anonymous field declared here")
291+
.emit()
292+
}
293+
}
294+
}
295+
216296
fn check_decl_no_pat(decl: &FnDecl, mut report_err: impl FnMut(Span, Option<Ident>, bool)) {
217297
for Param { pat, .. } in &decl.inputs {
218298
match pat.kind {
@@ -732,6 +812,71 @@ impl<'a> AstValidator<'a> {
732812
)
733813
.emit();
734814
}
815+
816+
fn visit_ty_common(&mut self, ty: &'a Ty) {
817+
match ty.kind {
818+
TyKind::BareFn(ref bfty) => {
819+
self.check_fn_decl(&bfty.decl, SelfSemantic::No);
820+
Self::check_decl_no_pat(&bfty.decl, |span, _, _| {
821+
struct_span_err!(
822+
self.session,
823+
span,
824+
E0561,
825+
"patterns aren't allowed in function pointer types"
826+
)
827+
.emit();
828+
});
829+
self.check_late_bound_lifetime_defs(&bfty.generic_params);
830+
}
831+
TyKind::TraitObject(ref bounds, ..) => {
832+
let mut any_lifetime_bounds = false;
833+
for bound in bounds {
834+
if let GenericBound::Outlives(ref lifetime) = *bound {
835+
if any_lifetime_bounds {
836+
struct_span_err!(
837+
self.session,
838+
lifetime.ident.span,
839+
E0226,
840+
"only a single explicit lifetime bound is permitted"
841+
)
842+
.emit();
843+
break;
844+
}
845+
any_lifetime_bounds = true;
846+
}
847+
}
848+
self.no_questions_in_bounds(bounds, "trait object types", false);
849+
}
850+
TyKind::ImplTrait(_, ref bounds) => {
851+
if self.is_impl_trait_banned {
852+
struct_span_err!(
853+
self.session,
854+
ty.span,
855+
E0667,
856+
"`impl Trait` is not allowed in path parameters"
857+
)
858+
.emit();
859+
}
860+
861+
if let Some(outer_impl_trait_sp) = self.outer_impl_trait {
862+
struct_span_err!(
863+
self.session,
864+
ty.span,
865+
E0666,
866+
"nested `impl Trait` is not allowed"
867+
)
868+
.span_label(outer_impl_trait_sp, "outer `impl Trait`")
869+
.span_label(ty.span, "nested `impl Trait` here")
870+
.emit();
871+
}
872+
873+
if !bounds.iter().any(|b| matches!(b, GenericBound::Trait(..))) {
874+
self.err_handler().span_err(ty.span, "at least one trait must be specified");
875+
}
876+
}
877+
_ => {}
878+
}
879+
}
735880
}
736881

737882
/// Checks that generic parameters are in the correct order,
@@ -850,72 +995,8 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
850995
}
851996

852997
fn visit_ty(&mut self, ty: &'a Ty) {
853-
match ty.kind {
854-
TyKind::BareFn(ref bfty) => {
855-
self.check_fn_decl(&bfty.decl, SelfSemantic::No);
856-
Self::check_decl_no_pat(&bfty.decl, |span, _, _| {
857-
struct_span_err!(
858-
self.session,
859-
span,
860-
E0561,
861-
"patterns aren't allowed in function pointer types"
862-
)
863-
.emit();
864-
});
865-
self.check_late_bound_lifetime_defs(&bfty.generic_params);
866-
}
867-
TyKind::TraitObject(ref bounds, ..) => {
868-
let mut any_lifetime_bounds = false;
869-
for bound in bounds {
870-
if let GenericBound::Outlives(ref lifetime) = *bound {
871-
if any_lifetime_bounds {
872-
struct_span_err!(
873-
self.session,
874-
lifetime.ident.span,
875-
E0226,
876-
"only a single explicit lifetime bound is permitted"
877-
)
878-
.emit();
879-
break;
880-
}
881-
any_lifetime_bounds = true;
882-
}
883-
}
884-
self.no_questions_in_bounds(bounds, "trait object types", false);
885-
}
886-
TyKind::ImplTrait(_, ref bounds) => {
887-
if self.is_impl_trait_banned {
888-
struct_span_err!(
889-
self.session,
890-
ty.span,
891-
E0667,
892-
"`impl Trait` is not allowed in path parameters"
893-
)
894-
.emit();
895-
}
896-
897-
if let Some(outer_impl_trait_sp) = self.outer_impl_trait {
898-
struct_span_err!(
899-
self.session,
900-
ty.span,
901-
E0666,
902-
"nested `impl Trait` is not allowed"
903-
)
904-
.span_label(outer_impl_trait_sp, "outer `impl Trait`")
905-
.span_label(ty.span, "nested `impl Trait` here")
906-
.emit();
907-
}
908-
909-
if !bounds.iter().any(|b| matches!(b, GenericBound::Trait(..))) {
910-
self.err_handler().span_err(ty.span, "at least one trait must be specified");
911-
}
912-
913-
self.walk_ty(ty);
914-
return;
915-
}
916-
_ => {}
917-
}
918-
998+
self.visit_ty_common(ty);
999+
self.deny_anonymous_struct(ty);
9191000
self.walk_ty(ty)
9201001
}
9211002

@@ -929,6 +1010,11 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
9291010
visit::walk_lifetime(self, lifetime);
9301011
}
9311012

1013+
fn visit_field_def(&mut self, s: &'a FieldDef) {
1014+
self.deny_anonymous_field(s);
1015+
visit::walk_field_def(self, s)
1016+
}
1017+
9321018
fn visit_item(&mut self, item: &'a Item) {
9331019
if item.attrs.iter().any(|attr| self.session.is_proc_macro_attr(attr)) {
9341020
self.has_proc_macro_decls = true;
@@ -1084,14 +1170,42 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
10841170
self.check_mod_file_item_asciionly(item.ident);
10851171
}
10861172
}
1087-
ItemKind::Union(ref vdata, _) => {
1088-
if let VariantData::Tuple(..) | VariantData::Unit(..) = vdata {
1089-
self.err_handler()
1090-
.span_err(item.span, "tuple and unit unions are not permitted");
1173+
ItemKind::Struct(ref vdata, ref generics) => match vdata {
1174+
// Duplicating the `Visitor` logic allows catching all cases
1175+
// of `Anonymous(Struct, Union)` outside of a field struct or union.
1176+
//
1177+
// Inside `visit_ty` the validator catches every `Anonymous(Struct, Union)` it
1178+
// encounters, and only on `ItemKind::Struct` and `ItemKind::Union`
1179+
// it uses `visit_ty_common`, which doesn't contain that specific check.
1180+
VariantData::Struct(ref fields, ..) => {
1181+
self.visit_vis(&item.vis);
1182+
self.visit_ident(item.ident);
1183+
self.visit_generics(generics);
1184+
self.with_banned_assoc_ty_bound(|this| {
1185+
walk_list!(this, visit_struct_field_def, fields);
1186+
});
1187+
walk_list!(self, visit_attribute, &item.attrs);
1188+
return;
10911189
}
1190+
_ => {}
1191+
},
1192+
ItemKind::Union(ref vdata, ref generics) => {
10921193
if vdata.fields().is_empty() {
10931194
self.err_handler().span_err(item.span, "unions cannot have zero fields");
10941195
}
1196+
match vdata {
1197+
VariantData::Struct(ref fields, ..) => {
1198+
self.visit_vis(&item.vis);
1199+
self.visit_ident(item.ident);
1200+
self.visit_generics(generics);
1201+
self.with_banned_assoc_ty_bound(|this| {
1202+
walk_list!(this, visit_struct_field_def, fields);
1203+
});
1204+
walk_list!(self, visit_attribute, &item.attrs);
1205+
return;
1206+
}
1207+
_ => {}
1208+
}
10951209
}
10961210
ItemKind::Const(def, .., None) => {
10971211
self.check_defaultness(item.span, def);

compiler/rustc_ast_passes/src/feature_gate.rs

+1
Original file line numberDiff line numberDiff line change
@@ -725,6 +725,7 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session) {
725725
// involved, so we only emit errors where there are no other parsing errors.
726726
gate_all!(destructuring_assignment, "destructuring assignments are unstable");
727727
}
728+
gate_all!(unnamed_fields, "unnamed fields are not yet fully implemented");
728729

729730
// All uses of `gate_all!` below this point were added in #65742,
730731
// and subsequently disabled (with the non-early gating readded).

0 commit comments

Comments
 (0)