Auto merge of #115367 - frank-king:feature/unnamed-fields-hir, r=davidtwco
Lowering unnamed fields and anonymous adt This implements #49804. Goals: - [x] lowering anonymous ADTs from AST to HIR - [x] generating definitions of anonymous ADTs - [x] uniqueness check of the unnamed fields - [x] field projection of anonymous ADTs - [x] `#[repr(C)]` check of the anonymous ADTs Non-Goals (will be in the next PRs) - capturing generic params for the anonymous ADTs from the parent ADT - pattern matching of anonymous ADTs - structural expressions of anonymous ADTs - rustdoc support of anonymous ADTs
This commit is contained in:
commit
bdc15928c8
60 changed files with 3274 additions and 260 deletions
|
@ -2457,6 +2457,19 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
|
|||
hir::TyKind::Tup(fields) => {
|
||||
Ty::new_tup_from_iter(tcx, fields.iter().map(|t| self.ast_ty_to_ty(t)))
|
||||
}
|
||||
hir::TyKind::AnonAdt(item_id) => {
|
||||
let did = item_id.owner_id.def_id;
|
||||
let adt_def = tcx.adt_def(did);
|
||||
let generics = tcx.generics_of(did);
|
||||
|
||||
debug!("ast_ty_to_ty_inner(AnonAdt): generics={:?}", generics);
|
||||
let args = ty::GenericArgs::for_item(tcx, did.to_def_id(), |param, _| {
|
||||
tcx.mk_param_from_def(param)
|
||||
});
|
||||
debug!("ast_ty_to_ty_inner(AnonAdt): args={:?}", args);
|
||||
|
||||
Ty::new_adt(tcx, adt_def, tcx.mk_args(args))
|
||||
}
|
||||
hir::TyKind::BareFn(bf) => {
|
||||
require_c_abi_if_c_variadic(tcx, bf.decl, bf.abi, ast_ty.span);
|
||||
|
||||
|
|
|
@ -80,6 +80,7 @@ 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) {
|
||||
|
@ -89,6 +90,58 @@ 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_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(),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Check that the fields of the `union` do not need dropping.
|
||||
|
|
|
@ -31,6 +31,7 @@ use rustc_middle::ty::util::{Discr, IntTypeExt};
|
|||
use rustc_middle::ty::{self, AdtKind, Const, IsSuggestable, ToPredicate, Ty, TyCtxt};
|
||||
use rustc_span::symbol::{kw, sym, Ident, Symbol};
|
||||
use rustc_span::Span;
|
||||
use rustc_target::abi::FieldIdx;
|
||||
use rustc_target::spec::abi;
|
||||
use rustc_trait_selection::infer::InferCtxtExt;
|
||||
use rustc_trait_selection::traits::error_reporting::suggestions::NextTypeParamName;
|
||||
|
@ -84,6 +85,7 @@ pub fn provide(providers: &mut Providers) {
|
|||
coroutine_for_closure,
|
||||
collect_mod_item_types,
|
||||
is_type_alias_impl_trait,
|
||||
find_field,
|
||||
..*providers
|
||||
};
|
||||
}
|
||||
|
@ -789,6 +791,175 @@ fn convert_enum_variant_types(tcx: TyCtxt<'_>, def_id: DefId) {
|
|||
}
|
||||
}
|
||||
|
||||
fn find_field(tcx: TyCtxt<'_>, (def_id, ident): (DefId, Ident)) -> Option<FieldIdx> {
|
||||
tcx.adt_def(def_id).non_enum_variant().fields.iter_enumerated().find_map(|(idx, field)| {
|
||||
if field.is_unnamed() {
|
||||
let field_ty = tcx.type_of(field.did).instantiate_identity();
|
||||
let adt_def = field_ty.ty_adt_def().expect("expect Adt for unnamed field");
|
||||
tcx.find_field((adt_def.did(), ident)).map(|_| idx)
|
||||
} else {
|
||||
(field.ident(tcx).normalize_to_macros_2_0() == ident).then_some(idx)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
struct NestedSpan {
|
||||
span: Span,
|
||||
nested_field_span: Span,
|
||||
}
|
||||
|
||||
impl NestedSpan {
|
||||
fn to_field_already_declared_nested_help(&self) -> errors::FieldAlreadyDeclaredNestedHelp {
|
||||
errors::FieldAlreadyDeclaredNestedHelp { span: self.span }
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
enum FieldDeclSpan {
|
||||
NotNested(Span),
|
||||
Nested(NestedSpan),
|
||||
}
|
||||
|
||||
impl From<Span> for FieldDeclSpan {
|
||||
fn from(span: Span) -> Self {
|
||||
Self::NotNested(span)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<NestedSpan> for FieldDeclSpan {
|
||||
fn from(span: NestedSpan) -> Self {
|
||||
Self::Nested(span)
|
||||
}
|
||||
}
|
||||
|
||||
struct FieldUniquenessCheckContext<'tcx> {
|
||||
tcx: TyCtxt<'tcx>,
|
||||
seen_fields: FxHashMap<Ident, FieldDeclSpan>,
|
||||
}
|
||||
|
||||
impl<'tcx> FieldUniquenessCheckContext<'tcx> {
|
||||
fn new(tcx: TyCtxt<'tcx>) -> Self {
|
||||
Self { tcx, seen_fields: FxHashMap::default() }
|
||||
}
|
||||
|
||||
/// Check if a given field `ident` declared at `field_decl` has been declared elsewhere before.
|
||||
fn check_field_decl(&mut self, ident: Ident, field_decl: FieldDeclSpan) {
|
||||
use FieldDeclSpan::*;
|
||||
let field_name = ident.name;
|
||||
let ident = ident.normalize_to_macros_2_0();
|
||||
match (field_decl, self.seen_fields.get(&ident).copied()) {
|
||||
(NotNested(span), Some(NotNested(prev_span))) => {
|
||||
self.tcx.dcx().emit_err(errors::FieldAlreadyDeclared::NotNested {
|
||||
field_name,
|
||||
span,
|
||||
prev_span,
|
||||
});
|
||||
}
|
||||
(NotNested(span), Some(Nested(prev))) => {
|
||||
self.tcx.dcx().emit_err(errors::FieldAlreadyDeclared::PreviousNested {
|
||||
field_name,
|
||||
span,
|
||||
prev_span: prev.span,
|
||||
prev_nested_field_span: prev.nested_field_span,
|
||||
prev_help: prev.to_field_already_declared_nested_help(),
|
||||
});
|
||||
}
|
||||
(
|
||||
Nested(current @ NestedSpan { span, nested_field_span, .. }),
|
||||
Some(NotNested(prev_span)),
|
||||
) => {
|
||||
self.tcx.dcx().emit_err(errors::FieldAlreadyDeclared::CurrentNested {
|
||||
field_name,
|
||||
span,
|
||||
nested_field_span,
|
||||
help: current.to_field_already_declared_nested_help(),
|
||||
prev_span,
|
||||
});
|
||||
}
|
||||
(Nested(current @ NestedSpan { span, nested_field_span }), Some(Nested(prev))) => {
|
||||
self.tcx.dcx().emit_err(errors::FieldAlreadyDeclared::BothNested {
|
||||
field_name,
|
||||
span,
|
||||
nested_field_span,
|
||||
help: current.to_field_already_declared_nested_help(),
|
||||
prev_span: prev.span,
|
||||
prev_nested_field_span: prev.nested_field_span,
|
||||
prev_help: prev.to_field_already_declared_nested_help(),
|
||||
});
|
||||
}
|
||||
(field_decl, None) => {
|
||||
self.seen_fields.insert(ident, field_decl);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Check the uniqueness of fields across adt where there are
|
||||
/// nested fields imported from an unnamed field.
|
||||
fn check_field_in_nested_adt(&mut self, adt_def: ty::AdtDef<'_>, unnamed_field_span: Span) {
|
||||
for field in adt_def.all_fields() {
|
||||
if field.is_unnamed() {
|
||||
// Here we don't care about the generic parameters, so `instantiate_identity` is enough.
|
||||
match self.tcx.type_of(field.did).instantiate_identity().kind() {
|
||||
ty::Adt(adt_def, _) => {
|
||||
self.check_field_in_nested_adt(*adt_def, unnamed_field_span);
|
||||
}
|
||||
ty_kind => span_bug!(
|
||||
self.tcx.def_span(field.did),
|
||||
"Unexpected TyKind in FieldUniquenessCheckContext::check_field_in_nested_adt(): {ty_kind:?}"
|
||||
),
|
||||
}
|
||||
} else {
|
||||
self.check_field_decl(
|
||||
field.ident(self.tcx),
|
||||
NestedSpan {
|
||||
span: unnamed_field_span,
|
||||
nested_field_span: self.tcx.def_span(field.did),
|
||||
}
|
||||
.into(),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Check the uniqueness of fields in a struct variant, and recursively
|
||||
/// check the nested fields if it is an unnamed field with type of an
|
||||
/// annoymous adt.
|
||||
fn check_field(&mut self, field: &hir::FieldDef<'_>) {
|
||||
if field.ident.name != kw::Underscore {
|
||||
self.check_field_decl(field.ident, field.span.into());
|
||||
return;
|
||||
}
|
||||
match &field.ty.kind {
|
||||
hir::TyKind::AnonAdt(item_id) => {
|
||||
match &self.tcx.hir_node(item_id.hir_id()).expect_item().kind {
|
||||
hir::ItemKind::Struct(variant_data, ..)
|
||||
| hir::ItemKind::Union(variant_data, ..) => {
|
||||
variant_data.fields().iter().for_each(|f| self.check_field(f));
|
||||
}
|
||||
item_kind => span_bug!(
|
||||
field.ty.span,
|
||||
"Unexpected ItemKind in FieldUniquenessCheckContext::check_field(): {item_kind:?}"
|
||||
),
|
||||
}
|
||||
}
|
||||
hir::TyKind::Path(hir::QPath::Resolved(_, hir::Path { res, .. })) => {
|
||||
self.check_field_in_nested_adt(self.tcx.adt_def(res.def_id()), field.span);
|
||||
}
|
||||
// Abort due to errors (there must be an error if an unnamed field
|
||||
// has any type kind other than an anonymous adt or a named adt)
|
||||
ty_kind => {
|
||||
self.tcx.dcx().span_delayed_bug(
|
||||
field.ty.span,
|
||||
format!("Unexpected TyKind in FieldUniquenessCheckContext::check_field(): {ty_kind:?}"),
|
||||
);
|
||||
// FIXME: errors during AST validation should abort the compilation before reaching here.
|
||||
self.tcx.dcx().abort_if_errors();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn convert_variant(
|
||||
tcx: TyCtxt<'_>,
|
||||
variant_did: Option<LocalDefId>,
|
||||
|
@ -797,29 +968,26 @@ fn convert_variant(
|
|||
def: &hir::VariantData<'_>,
|
||||
adt_kind: ty::AdtKind,
|
||||
parent_did: LocalDefId,
|
||||
is_anonymous: bool,
|
||||
) -> ty::VariantDef {
|
||||
let mut seen_fields: FxHashMap<Ident, Span> = Default::default();
|
||||
let mut has_unnamed_fields = false;
|
||||
let mut field_uniqueness_check_ctx = FieldUniquenessCheckContext::new(tcx);
|
||||
let fields = def
|
||||
.fields()
|
||||
.iter()
|
||||
.map(|f| {
|
||||
let dup_span = seen_fields.get(&f.ident.normalize_to_macros_2_0()).cloned();
|
||||
if let Some(prev_span) = dup_span {
|
||||
tcx.dcx().emit_err(errors::FieldAlreadyDeclared {
|
||||
field_name: f.ident,
|
||||
span: f.span,
|
||||
prev_span,
|
||||
});
|
||||
} else {
|
||||
seen_fields.insert(f.ident.normalize_to_macros_2_0(), f.span);
|
||||
}
|
||||
|
||||
ty::FieldDef {
|
||||
did: f.def_id.to_def_id(),
|
||||
name: f.ident.name,
|
||||
vis: tcx.visibility(f.def_id),
|
||||
.inspect(|f| {
|
||||
has_unnamed_fields |= f.ident.name == kw::Underscore;
|
||||
// We only check named ADT here because anonymous ADTs are checked inside
|
||||
// the nammed ADT in which they are defined.
|
||||
if !is_anonymous {
|
||||
field_uniqueness_check_ctx.check_field(f);
|
||||
}
|
||||
})
|
||||
.map(|f| ty::FieldDef {
|
||||
did: f.def_id.to_def_id(),
|
||||
name: f.ident.name,
|
||||
vis: tcx.visibility(f.def_id),
|
||||
})
|
||||
.collect();
|
||||
let recovered = match def {
|
||||
hir::VariantData::Struct { recovered, .. } => *recovered,
|
||||
|
@ -837,6 +1005,7 @@ fn convert_variant(
|
|||
adt_kind == AdtKind::Struct && tcx.has_attr(parent_did, sym::non_exhaustive)
|
||||
|| variant_did
|
||||
.is_some_and(|variant_did| tcx.has_attr(variant_did, sym::non_exhaustive)),
|
||||
has_unnamed_fields,
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -847,7 +1016,12 @@ fn adt_def(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::AdtDef<'_> {
|
|||
bug!("expected ADT to be an item");
|
||||
};
|
||||
|
||||
let repr = tcx.repr_options_of_def(def_id.to_def_id());
|
||||
let is_anonymous = item.ident.name == kw::Empty;
|
||||
let repr = if is_anonymous {
|
||||
tcx.adt_def(tcx.local_parent(def_id)).repr()
|
||||
} else {
|
||||
tcx.repr_options_of_def(def_id.to_def_id())
|
||||
};
|
||||
let (kind, variants) = match &item.kind {
|
||||
ItemKind::Enum(def, _) => {
|
||||
let mut distance_from_explicit = 0;
|
||||
|
@ -871,6 +1045,7 @@ fn adt_def(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::AdtDef<'_> {
|
|||
&v.data,
|
||||
AdtKind::Enum,
|
||||
def_id,
|
||||
is_anonymous,
|
||||
)
|
||||
})
|
||||
.collect();
|
||||
|
@ -890,6 +1065,7 @@ fn adt_def(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::AdtDef<'_> {
|
|||
def,
|
||||
adt_kind,
|
||||
def_id,
|
||||
is_anonymous,
|
||||
))
|
||||
.collect();
|
||||
|
||||
|
@ -897,7 +1073,7 @@ fn adt_def(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::AdtDef<'_> {
|
|||
}
|
||||
_ => bug!("{:?} is not an ADT", item.owner_id.def_id),
|
||||
};
|
||||
tcx.mk_adt_def(def_id.to_def_id(), kind, variants, repr)
|
||||
tcx.mk_adt_def(def_id.to_def_id(), kind, variants, repr, is_anonymous)
|
||||
}
|
||||
|
||||
fn trait_def(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::TraitDef {
|
||||
|
|
|
@ -175,14 +175,66 @@ pub struct DropImplOnWrongItem {
|
|||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(hir_analysis_field_already_declared, code = E0124)]
|
||||
pub struct FieldAlreadyDeclared {
|
||||
pub field_name: Ident,
|
||||
pub enum FieldAlreadyDeclared {
|
||||
#[diag(hir_analysis_field_already_declared, code = E0124)]
|
||||
NotNested {
|
||||
field_name: Symbol,
|
||||
#[primary_span]
|
||||
#[label]
|
||||
span: Span,
|
||||
#[label(hir_analysis_previous_decl_label)]
|
||||
prev_span: Span,
|
||||
},
|
||||
#[diag(hir_analysis_field_already_declared_current_nested)]
|
||||
CurrentNested {
|
||||
field_name: Symbol,
|
||||
#[primary_span]
|
||||
#[label]
|
||||
span: Span,
|
||||
#[note(hir_analysis_nested_field_decl_note)]
|
||||
nested_field_span: Span,
|
||||
#[subdiagnostic]
|
||||
help: FieldAlreadyDeclaredNestedHelp,
|
||||
#[label(hir_analysis_previous_decl_label)]
|
||||
prev_span: Span,
|
||||
},
|
||||
#[diag(hir_analysis_field_already_declared_previous_nested)]
|
||||
PreviousNested {
|
||||
field_name: Symbol,
|
||||
#[primary_span]
|
||||
#[label]
|
||||
span: Span,
|
||||
#[label(hir_analysis_previous_decl_label)]
|
||||
prev_span: Span,
|
||||
#[note(hir_analysis_previous_nested_field_decl_note)]
|
||||
prev_nested_field_span: Span,
|
||||
#[subdiagnostic]
|
||||
prev_help: FieldAlreadyDeclaredNestedHelp,
|
||||
},
|
||||
#[diag(hir_analysis_field_already_declared_both_nested)]
|
||||
BothNested {
|
||||
field_name: Symbol,
|
||||
#[primary_span]
|
||||
#[label]
|
||||
span: Span,
|
||||
#[note(hir_analysis_nested_field_decl_note)]
|
||||
nested_field_span: Span,
|
||||
#[subdiagnostic]
|
||||
help: FieldAlreadyDeclaredNestedHelp,
|
||||
#[label(hir_analysis_previous_decl_label)]
|
||||
prev_span: Span,
|
||||
#[note(hir_analysis_previous_nested_field_decl_note)]
|
||||
prev_nested_field_span: Span,
|
||||
#[subdiagnostic]
|
||||
prev_help: FieldAlreadyDeclaredNestedHelp,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Subdiagnostic)]
|
||||
#[help(hir_analysis_field_already_declared_nested_help)]
|
||||
pub struct FieldAlreadyDeclaredNestedHelp {
|
||||
#[primary_span]
|
||||
#[label]
|
||||
pub span: Span,
|
||||
#[label(hir_analysis_previous_decl_label)]
|
||||
pub prev_span: Span,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
|
@ -1534,3 +1586,38 @@ pub(crate) enum UnusedGenericParameterHelp {
|
|||
#[help(hir_analysis_unused_generic_parameter_ty_alias_help)]
|
||||
TyAlias { param_name: Ident },
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
pub enum UnnamedFieldsRepr<'a> {
|
||||
#[diag(hir_analysis_unnamed_fields_repr_missing_repr_c)]
|
||||
MissingReprC {
|
||||
#[primary_span]
|
||||
#[label]
|
||||
span: Span,
|
||||
adt_kind: &'static str,
|
||||
adt_name: Symbol,
|
||||
#[subdiagnostic]
|
||||
unnamed_fields: Vec<UnnamedFieldsReprFieldDefined>,
|
||||
#[suggestion(code = "#[repr(C)]\n")]
|
||||
sugg_span: Span,
|
||||
},
|
||||
#[diag(hir_analysis_unnamed_fields_repr_field_missing_repr_c)]
|
||||
FieldMissingReprC {
|
||||
#[primary_span]
|
||||
#[label]
|
||||
span: Span,
|
||||
#[label(hir_analysis_field_ty_label)]
|
||||
field_ty_span: Span,
|
||||
field_ty: Ty<'a>,
|
||||
field_adt_kind: &'static str,
|
||||
#[suggestion(code = "#[repr(C)]\n")]
|
||||
sugg_span: Span,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Subdiagnostic)]
|
||||
#[note(hir_analysis_unnamed_fields_repr_field_defined)]
|
||||
pub struct UnnamedFieldsReprFieldDefined {
|
||||
#[primary_span]
|
||||
pub span: Span,
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue