Skip to content

Commit

Permalink
Auto merge of rust-lang#131045 - compiler-errors:remove-unnamed_field…
Browse files Browse the repository at this point in the history
…s, r=wesleywiser

Retire the `unnamed_fields` feature for now

`#![feature(unnamed_fields)]` was implemented in part in rust-lang#115131 and rust-lang#115367, however work on that feature has (afaict) stalled and in the mean time there have been some concerns raised (e.g.[^1][^2]) about whether `unnamed_fields` is worthwhile to have in the language, especially in its current desugaring. Because it represents a compiler implementation burden including a new kind of anonymous ADT and additional complication to field selection, and is quite prone to bugs today, I'm choosing to remove the feature.

However, since I'm not one to really write a bunch of words, I'm specifically *not* going to de-RFC this feature. This PR essentially *rolls back* the state of this feature to "RFC accepted but not yet implemented"; however if anyone wants to formally unapprove the RFC from the t-lang side, then please be my guest. I'm just not totally willing to summarize the various language-facing reasons for why this feature is or is not worthwhile, since I'm coming from the compiler side mostly.

Fixes rust-lang#117942
Fixes rust-lang#121161
Fixes rust-lang#121263
Fixes rust-lang#121299
Fixes rust-lang#121722
Fixes rust-lang#121799
Fixes rust-lang#126969
Fixes rust-lang#131041

Tracking:
* rust-lang#49804

[^1]: https://rust-lang.zulipchat.com/#narrow/stream/213817-t-lang/topic/Unnamed.20struct.2Funion.20fields
[^2]: rust-lang#49804 (comment)
  • Loading branch information
bors committed Oct 11, 2024
2 parents 484c8e7 + 6628bba commit 80325f2
Show file tree
Hide file tree
Showing 66 changed files with 32 additions and 3,708 deletions.
8 changes: 0 additions & 8 deletions compiler/rustc_ast/src/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2167,10 +2167,6 @@ pub enum TyKind {
Never,
/// A tuple (`(A, B, C, D,...)`).
Tup(ThinVec<P<Ty>>),
/// An anonymous struct type i.e. `struct { foo: Type }`.
AnonStruct(NodeId, ThinVec<FieldDef>),
/// An anonymous union type i.e. `union { bar: Type }`.
AnonUnion(NodeId, ThinVec<FieldDef>),
/// A path (`module::module::...::Type`), optionally
/// "qualified", e.g., `<Vec<T> as SomeTrait>::SomeType`.
///
Expand Down Expand Up @@ -2227,10 +2223,6 @@ impl TyKind {
None
}
}

pub fn is_anon_adt(&self) -> bool {
matches!(self, TyKind::AnonStruct(..) | TyKind::AnonUnion(..))
}
}

/// Syntax used to declare a trait object.
Expand Down
4 changes: 0 additions & 4 deletions compiler/rustc_ast/src/mut_visit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -519,10 +519,6 @@ pub fn walk_ty<T: MutVisitor>(vis: &mut T, ty: &mut P<Ty>) {
visit_vec(bounds, |bound| vis.visit_param_bound(bound, BoundKind::Impl));
}
TyKind::MacCall(mac) => vis.visit_mac_call(mac),
TyKind::AnonStruct(id, fields) | TyKind::AnonUnion(id, fields) => {
vis.visit_id(id);
fields.flat_map_in_place(|field| vis.flat_map_field_def(field));
}
}
visit_lazy_tts(vis, tokens);
vis.visit_span(span);
Expand Down
6 changes: 0 additions & 6 deletions compiler/rustc_ast/src/util/classify.rs
Original file line number Diff line number Diff line change
Expand Up @@ -287,12 +287,6 @@ fn type_trailing_braced_mac_call(mut ty: &ast::Ty) -> Option<&ast::MacCall> {
| ast::TyKind::Pat(..)
| ast::TyKind::Dummy
| ast::TyKind::Err(..) => break None,

// These end in brace, but cannot occur in a let-else statement.
// They are only parsed as fields of a data structure. For the
// purpose of denying trailing braces in the expression of a
// let-else, we can disregard these.
ast::TyKind::AnonStruct(..) | ast::TyKind::AnonUnion(..) => break None,
}
}
}
Expand Down
3 changes: 0 additions & 3 deletions compiler/rustc_ast/src/visit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -535,9 +535,6 @@ pub fn walk_ty<'a, V: Visitor<'a>>(visitor: &mut V, typ: &'a Ty) -> V::Result {
TyKind::Err(_guar) => {}
TyKind::MacCall(mac) => try_visit!(visitor.visit_mac_call(mac)),
TyKind::Never | TyKind::CVarArgs => {}
TyKind::AnonStruct(_id, ref fields) | TyKind::AnonUnion(_id, ref fields) => {
walk_list!(visitor, visit_field_def, fields);
}
}
V::Result::output()
}
Expand Down
40 changes: 0 additions & 40 deletions compiler/rustc_ast_lowering/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1259,46 +1259,6 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
let kind = match &t.kind {
TyKind::Infer => hir::TyKind::Infer,
TyKind::Err(guar) => hir::TyKind::Err(*guar),
// Lower the anonymous structs or unions in a nested lowering context.
//
// ```
// struct Foo {
// _: union {
// // ^__________________ <-- within the nested lowering context,
// /* fields */ // | we lower all fields defined into an
// } // | owner node of struct or union item
// // ^_____________________|
// }
// ```
TyKind::AnonStruct(node_id, fields) | TyKind::AnonUnion(node_id, fields) => {
// Here its `def_id` is created in `build_reduced_graph`.
let def_id = self.local_def_id(*node_id);
debug!(?def_id);
let owner_id = hir::OwnerId { def_id };
self.with_hir_id_owner(*node_id, |this| {
let fields = this.arena.alloc_from_iter(
fields.iter().enumerate().map(|f| this.lower_field_def(f)),
);
let span = t.span;
let variant_data =
hir::VariantData::Struct { fields, recovered: ast::Recovered::No };
// FIXME: capture the generics from the outer adt.
let generics = hir::Generics::empty();
let kind = match t.kind {
TyKind::AnonStruct(..) => hir::ItemKind::Struct(variant_data, generics),
TyKind::AnonUnion(..) => hir::ItemKind::Union(variant_data, generics),
_ => unreachable!(),
};
hir::OwnerNode::Item(this.arena.alloc(hir::Item {
ident: Ident::new(kw::Empty, span),
owner_id,
kind,
span: this.lower_span(span),
vis_span: this.lower_span(span.shrink_to_lo()),
}))
});
hir::TyKind::AnonAdt(hir::ItemId { owner_id })
}
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
12 changes: 0 additions & 12 deletions compiler/rustc_ast_passes/messages.ftl
Original file line number Diff line number Diff line change
@@ -1,7 +1,3 @@
ast_passes_anon_struct_or_union_not_allowed =
anonymous {$struct_or_union}s are not allowed outside of unnamed struct or union fields
.label = anonymous {$struct_or_union} declared here
ast_passes_assoc_const_without_body =
associated constant in `impl` without body
.suggestion = provide a definition for the constant
Expand Down Expand Up @@ -160,14 +156,6 @@ ast_passes_inherent_cannot_be = inherent impls cannot be {$annotation}
.type = inherent impl for this type
.only_trait = only trait implementations may be annotated with {$annotation}
ast_passes_invalid_unnamed_field =
unnamed fields are not allowed outside of structs or unions
.label = unnamed field declared here
ast_passes_invalid_unnamed_field_ty =
unnamed fields can only have struct or union types
.label = not a struct or union
ast_passes_item_invalid_safety = items outside of `unsafe extern {"{ }"}` cannot be declared with `safe` safety qualifier
.suggestion = remove safe from this item
Expand Down
43 changes: 0 additions & 43 deletions compiler/rustc_ast_passes/src/ast_validation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -244,9 +244,6 @@ impl<'a> AstValidator<'a> {
}
}
}
TyKind::AnonStruct(_, ref fields) | TyKind::AnonUnion(_, ref fields) => {
walk_list!(self, visit_struct_field_def, fields)
}
_ => visit::walk_ty(self, t),
}
}
Expand All @@ -255,7 +252,6 @@ impl<'a> AstValidator<'a> {
if let Some(ident) = field.ident
&& ident.name == kw::Underscore
{
self.check_unnamed_field_ty(&field.ty, ident.span);
self.visit_vis(&field.vis);
self.visit_ident(ident);
self.visit_ty_common(&field.ty);
Expand Down Expand Up @@ -294,39 +290,6 @@ impl<'a> AstValidator<'a> {
}
}

fn check_unnamed_field_ty(&self, ty: &Ty, span: Span) {
if matches!(
&ty.kind,
// We already checked for `kw::Underscore` before calling this function,
// so skip the check
TyKind::AnonStruct(..) | TyKind::AnonUnion(..)
// 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
| TyKind::Path(..)
) {
return;
}
self.dcx().emit_err(errors::InvalidUnnamedFieldTy { span, ty_span: ty.span });
}

fn deny_anon_struct_or_union(&self, ty: &Ty) {
let struct_or_union = match &ty.kind {
TyKind::AnonStruct(..) => "struct",
TyKind::AnonUnion(..) => "union",
_ => return,
};
self.dcx().emit_err(errors::AnonStructOrUnionNotAllowed { struct_or_union, span: ty.span });
}

fn deny_unnamed_field(&self, field: &FieldDef) {
if let Some(ident) = field.ident
&& ident.name == kw::Underscore
{
self.dcx()
.emit_err(errors::InvalidUnnamedField { span: field.span, ident_span: ident.span });
}
}

fn check_trait_fn_not_const(&self, constness: Const, parent: &TraitOrTraitImpl) {
let Const::Yes(span) = constness else {
return;
Expand Down Expand Up @@ -890,15 +853,9 @@ impl<'a> Visitor<'a> for AstValidator<'a> {

fn visit_ty(&mut self, ty: &'a Ty) {
self.visit_ty_common(ty);
self.deny_anon_struct_or_union(ty);
self.walk_ty(ty)
}

fn visit_field_def(&mut self, field: &'a FieldDef) {
self.deny_unnamed_field(field);
visit::walk_field_def(self, field)
}

fn visit_item(&mut self, item: &'a Item) {
if item.attrs.iter().any(|attr| attr.is_proc_macro_attr()) {
self.has_proc_macro_decls = true;
Expand Down
27 changes: 0 additions & 27 deletions compiler/rustc_ast_passes/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -814,33 +814,6 @@ pub(crate) struct NegativeBoundWithParentheticalNotation {
pub span: Span,
}

#[derive(Diagnostic)]
#[diag(ast_passes_invalid_unnamed_field_ty)]
pub(crate) struct InvalidUnnamedFieldTy {
#[primary_span]
pub span: Span,
#[label]
pub ty_span: Span,
}

#[derive(Diagnostic)]
#[diag(ast_passes_invalid_unnamed_field)]
pub(crate) struct InvalidUnnamedField {
#[primary_span]
pub span: Span,
#[label]
pub ident_span: Span,
}

#[derive(Diagnostic)]
#[diag(ast_passes_anon_struct_or_union_not_allowed)]
pub(crate) struct AnonStructOrUnionNotAllowed {
#[primary_span]
#[label]
pub span: Span,
pub struct_or_union: &'static str,
}

#[derive(Diagnostic)]
#[diag(ast_passes_match_arm_with_no_body)]
pub(crate) struct MatchArmWithNoBody {
Expand Down
1 change: 0 additions & 1 deletion compiler/rustc_ast_passes/src/feature_gate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -541,7 +541,6 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session, features: &Features) {
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");
gate_all!(fn_delegation, "functions delegation is not yet fully implemented");
gate_all!(postfix_match, "postfix match is experimental");
gate_all!(mut_ref, "mutable by-reference bindings are experimental");
Expand Down
8 changes: 0 additions & 8 deletions compiler/rustc_ast_pretty/src/pprust/state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1174,14 +1174,6 @@ impl<'a> State<'a> {
}
self.pclose();
}
ast::TyKind::AnonStruct(_, fields) => {
self.head("struct");
self.print_record_struct_body(fields, ty.span);
}
ast::TyKind::AnonUnion(_, fields) => {
self.head("union");
self.print_record_struct_body(fields, ty.span);
}
ast::TyKind::Paren(typ) => {
self.popen();
self.print_type(typ);
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_builtin_macros/src/deriving/clone.rs
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ fn cs_clone_simple(
// Already produced an assertion for this type.
// Anonymous structs or unions must be eliminated as they cannot be
// type parameters.
} else if !field.ty.kind.is_anon_adt() {
} else {
// let _: AssertParamIsClone<FieldTy>;
super::assert_ty_bounds(cx, &mut stmts, field.ty.clone(), field.span, &[
sym::clone,
Expand Down
2 changes: 0 additions & 2 deletions compiler/rustc_builtin_macros/src/deriving/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -124,8 +124,6 @@ fn assert_ty_bounds(
span: Span,
assert_path: &[Symbol],
) {
// Deny anonymous structs or unions to avoid weird errors.
assert!(!ty.kind.is_anon_adt(), "Anonymous structs or unions cannot be type parameters");
// Generate statement `let _: assert_path<ty>;`.
let span = cx.with_def_site_ctxt(span);
let assert_path = cx.path_all(span, true, cx.std_path(assert_path), vec![GenericArg::Type(ty)]);
Expand Down
2 changes: 2 additions & 0 deletions compiler/rustc_feature/src/removed.rs
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,8 @@ declare_features! (
(removed, test_removed_feature, "1.0.0", None, None),
/// Allows using items which are missing stability attributes
(removed, unmarked_api, "1.0.0", None, None),
/// Allows unnamed fields of struct and union type
(removed, unnamed_fields, "CURRENT_RUSTC_VERSION", Some(49804), Some("feature needs redesign")),
(removed, unsafe_no_drop_flag, "1.0.0", None, None),
/// Allows `union` fields that don't implement `Copy` as long as they don't have any drop glue.
(removed, untagged_unions, "1.13.0", Some(55149),
Expand Down
2 changes: 0 additions & 2 deletions compiler/rustc_feature/src/unstable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -623,8 +623,6 @@ declare_features! (
/// Allows creation of instances of a struct by moving fields that have
/// not changed from prior instances of the same struct (RFC #2528)
(unstable, type_changing_struct_update, "1.58.0", Some(86555)),
/// Allows unnamed fields of struct and union type
(incomplete, unnamed_fields, "1.74.0", Some(49804)),
/// Allows const generic parameters to be defined with types that
/// are not `Sized`, e.g. `fn foo<const N: [u8]>() {`.
(incomplete, unsized_const_params, "1.82.0", Some(95174)),
Expand Down
15 changes: 0 additions & 15 deletions compiler/rustc_hir_analysis/messages.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -248,8 +248,6 @@ hir_analysis_invalid_union_field =
hir_analysis_invalid_union_field_sugg =
wrap the field type in `ManuallyDrop<...>`
hir_analysis_invalid_unnamed_field_ty = unnamed fields can only have struct or union types
hir_analysis_late_bound_const_in_apit = `impl Trait` can only mention const parameters from an fn or impl
.label = const parameter declared here
Expand Down Expand Up @@ -538,19 +536,6 @@ hir_analysis_unconstrained_generic_parameter = the {$param_def_kind} `{$param_na
hir_analysis_unconstrained_opaque_type = unconstrained opaque type
.note = `{$name}` must be used in combination with a concrete type within the same {$what}
hir_analysis_unnamed_fields_repr_field_defined = unnamed field defined here
hir_analysis_unnamed_fields_repr_field_missing_repr_c =
named type of unnamed field must have `#[repr(C)]` representation
.label = unnamed field defined here
.field_ty_label = `{$field_ty}` defined here
.suggestion = add `#[repr(C)]` to this {$field_adt_kind}
hir_analysis_unnamed_fields_repr_missing_repr_c =
{$adt_kind} with unnamed fields must have `#[repr(C)]` representation
.label = {$adt_kind} `{$adt_name}` defined here
.suggestion = add `#[repr(C)]` to this {$adt_kind}
hir_analysis_unrecognized_atomic_operation =
unrecognized atomic operation function: `{$op}`
.label = unrecognized atomic operation
Expand Down
56 changes: 0 additions & 56 deletions compiler/rustc_hir_analysis/src/check/check.rs
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,6 @@ fn check_struct(tcx: TyCtxt<'_>, def_id: LocalDefId) {

check_transparent(tcx, def);
check_packed(tcx, span, def);
check_unnamed_fields(tcx, def);
}

fn check_union(tcx: TyCtxt<'_>, def_id: LocalDefId) {
Expand All @@ -86,61 +85,6 @@ fn check_union(tcx: TyCtxt<'_>, def_id: LocalDefId) {
check_transparent(tcx, def);
check_union_fields(tcx, span, def_id);
check_packed(tcx, span, def);
check_unnamed_fields(tcx, def);
}

/// Check the representation of adts with unnamed fields.
fn check_unnamed_fields(tcx: TyCtxt<'_>, def: ty::AdtDef<'_>) {
if def.is_enum() {
return;
}
let variant = def.non_enum_variant();
if !variant.has_unnamed_fields() {
return;
}
if !def.is_anonymous() {
let adt_kind = def.descr();
let span = tcx.def_span(def.did());
let unnamed_fields = variant
.fields
.iter()
.filter(|f| f.is_unnamed())
.map(|f| {
let span = tcx.def_span(f.did);
errors::UnnamedFieldsReprFieldDefined { span }
})
.collect::<Vec<_>>();
debug_assert_ne!(unnamed_fields.len(), 0, "expect unnamed fields in this adt");
let adt_name = tcx.item_name(def.did());
if !def.repr().c() {
tcx.dcx().emit_err(errors::UnnamedFieldsRepr::MissingReprC {
span,
adt_kind,
adt_name,
unnamed_fields,
sugg_span: span.shrink_to_lo(),
});
}
}
for field in variant.fields.iter().filter(|f| f.is_unnamed()) {
let field_ty = tcx.type_of(field.did).instantiate_identity();
if let Some(adt) = field_ty.ty_adt_def()
&& !adt.is_enum()
{
if !adt.is_anonymous() && !adt.repr().c() {
let field_ty_span = tcx.def_span(adt.did());
tcx.dcx().emit_err(errors::UnnamedFieldsRepr::FieldMissingReprC {
span: tcx.def_span(field.did),
field_ty_span,
field_ty,
field_adt_kind: adt.descr(),
sugg_span: field_ty_span.shrink_to_lo(),
});
}
} else {
tcx.dcx().emit_err(errors::InvalidUnnamedFieldTy { span: tcx.def_span(field.did) });
}
}
}

/// Check that the fields of the `union` do not need dropping.
Expand Down
Loading

0 comments on commit 80325f2

Please sign in to comment.