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 of struct and union type #84571

Merged
merged 4 commits into from
May 17, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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) {
petrochenkov marked this conversation as resolved.
Show resolved Hide resolved
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);
jedel1043 marked this conversation as resolved.
Show resolved Hide resolved
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