Skip to content

Commit

Permalink
Auto merge of #84571 - jedel1043:issue-49804-impl, r=petrochenkov
Browse files Browse the repository at this point in the history
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`
  • Loading branch information
bors committed May 17, 2021
2 parents 44ec846 + 64acb7d commit 9964284
Show file tree
Hide file tree
Showing 19 changed files with 707 additions and 112 deletions.
4 changes: 4 additions & 0 deletions compiler/rustc_ast/src/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1861,6 +1861,10 @@ pub enum TyKind {
Never,
/// A tuple (`(A, B, C, D,...)`).
Tup(Vec<P<Ty>>),
/// An anonymous struct type i.e. `struct { foo: Type }`
AnonymousStruct(Vec<FieldDef>, bool),
/// An anonymous union type i.e. `union { bar: Type }`
AnonymousUnion(Vec<FieldDef>, bool),
/// 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 @@ -484,6 +484,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 @@ -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 => {}
}
}
Expand Down
5 changes: 4 additions & 1 deletion compiler/rustc_ast_lowering/src/item.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
9 changes: 9 additions & 0 deletions compiler/rustc_ast_lowering/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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) => {
Expand Down
254 changes: 184 additions & 70 deletions compiler/rustc_ast_passes/src/ast_validation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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()
}
Expand Down Expand Up @@ -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<Ident>, bool)) {
for Param { pat, .. } in &decl.inputs {
match pat.kind {
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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)
}

Expand All @@ -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;
Expand Down Expand Up @@ -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);
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 @@ -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).
Expand Down
Loading

0 comments on commit 9964284

Please sign in to comment.