diff options
Diffstat (limited to 'compiler/rustc_hir_analysis/src')
| -rw-r--r-- | compiler/rustc_hir_analysis/src/astconv/mod.rs | 13 | ||||
| -rw-r--r-- | compiler/rustc_hir_analysis/src/check/check.rs | 53 | ||||
| -rw-r--r-- | compiler/rustc_hir_analysis/src/collect.rs | 214 | ||||
| -rw-r--r-- | compiler/rustc_hir_analysis/src/errors.rs | 99 |
4 files changed, 354 insertions, 25 deletions
diff --git a/compiler/rustc_hir_analysis/src/astconv/mod.rs b/compiler/rustc_hir_analysis/src/astconv/mod.rs index 1ae3ebaebbb..a643614d33d 100644 --- a/compiler/rustc_hir_analysis/src/astconv/mod.rs +++ b/compiler/rustc_hir_analysis/src/astconv/mod.rs @@ -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); diff --git a/compiler/rustc_hir_analysis/src/check/check.rs b/compiler/rustc_hir_analysis/src/check/check.rs index 7250dc81faf..f55cba2707c 100644 --- a/compiler/rustc_hir_analysis/src/check/check.rs +++ b/compiler/rustc_hir_analysis/src/check/check.rs @@ -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. diff --git a/compiler/rustc_hir_analysis/src/collect.rs b/compiler/rustc_hir_analysis/src/collect.rs index f458ff01c10..d92e1a14151 100644 --- a/compiler/rustc_hir_analysis/src/collect.rs +++ b/compiler/rustc_hir_analysis/src/collect.rs @@ -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 { diff --git a/compiler/rustc_hir_analysis/src/errors.rs b/compiler/rustc_hir_analysis/src/errors.rs index bec53693d6c..6e163cff7ed 100644 --- a/compiler/rustc_hir_analysis/src/errors.rs +++ b/compiler/rustc_hir_analysis/src/errors.rs @@ -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, +} |
