diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index 77044df9a4092..fcb15925f6a7b 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -2998,6 +2998,12 @@ impl<'hir> Item<'hir> { ItemId { owner_id: self.owner_id } } + /// Check if this is an [`ItemKind::Enum`], [`ItemKind::Struct`] or + /// [`ItemKind::Union`]. + pub fn is_adt(&self) -> bool { + matches!(self.kind, ItemKind::Enum(..) | ItemKind::Struct(..) | ItemKind::Union(..)) + } + expect_methods_self_kind! { expect_extern_crate, Option, ItemKind::ExternCrate(s), *s; diff --git a/compiler/rustc_hir_analysis/messages.ftl b/compiler/rustc_hir_analysis/messages.ftl index a61cfd0e4ce9a..f32b14aca23fd 100644 --- a/compiler/rustc_hir_analysis/messages.ftl +++ b/compiler/rustc_hir_analysis/messages.ftl @@ -198,6 +198,8 @@ 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 diff --git a/compiler/rustc_hir_analysis/src/check/check.rs b/compiler/rustc_hir_analysis/src/check/check.rs index 1410273e3bc9e..2c367b15df2ab 100644 --- a/compiler/rustc_hir_analysis/src/check/check.rs +++ b/compiler/rustc_hir_analysis/src/check/check.rs @@ -129,17 +129,20 @@ fn check_unnamed_fields(tcx: TyCtxt<'_>, def: ty::AdtDef<'_>) { 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() + && !adt.is_enum() { - 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(), - }); + 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) }); } } } diff --git a/compiler/rustc_hir_analysis/src/collect.rs b/compiler/rustc_hir_analysis/src/collect.rs index 43f0af5bd1da7..a5ef1490bce97 100644 --- a/compiler/rustc_hir_analysis/src/collect.rs +++ b/compiler/rustc_hir_analysis/src/collect.rs @@ -943,7 +943,15 @@ impl<'tcx> FieldUniquenessCheckContext<'tcx> { } } hir::TyKind::Path(hir::QPath::Resolved(_, hir::Path { res, .. })) => { - self.check_field_in_nested_adt(self.tcx.adt_def(res.def_id()), field.span); + // If this is a direct path to an ADT, we can check it + // If this is a type alias or non-ADT, `check_unnamed_fields` should verify it + if let Some(def_id) = res.opt_def_id() + && let Some(local) = def_id.as_local() + && let Node::Item(item) = self.tcx.hir_node_by_def_id(local) + && item.is_adt() + { + self.check_field_in_nested_adt(self.tcx.adt_def(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) diff --git a/compiler/rustc_hir_analysis/src/errors.rs b/compiler/rustc_hir_analysis/src/errors.rs index 6a505b961974f..3bd7687a54425 100644 --- a/compiler/rustc_hir_analysis/src/errors.rs +++ b/compiler/rustc_hir_analysis/src/errors.rs @@ -661,6 +661,13 @@ pub(crate) struct InvalidUnionField { pub note: (), } +#[derive(Diagnostic)] +#[diag(hir_analysis_invalid_unnamed_field_ty)] +pub struct InvalidUnnamedFieldTy { + #[primary_span] + pub span: Span, +} + #[derive(Diagnostic)] #[diag(hir_analysis_return_type_notation_on_non_rpitit)] pub(crate) struct ReturnTypeNotationOnNonRpitit<'tcx> { diff --git a/tests/ui/union/unnamed-fields/auxiliary/dep.rs b/tests/ui/union/unnamed-fields/auxiliary/dep.rs new file mode 100644 index 0000000000000..a11f3e18f52a9 --- /dev/null +++ b/tests/ui/union/unnamed-fields/auxiliary/dep.rs @@ -0,0 +1,18 @@ +#[repr(C)] +pub struct GoodStruct(()); + +pub struct BadStruct(()); + +pub enum BadEnum { + A, + B, +} + +#[repr(C)] +pub enum BadEnum2 { + A, + B, +} + +pub type GoodAlias = GoodStruct; +pub type BadAlias = i32; diff --git a/tests/ui/union/unnamed-fields/restrict_type_hir.rs b/tests/ui/union/unnamed-fields/restrict_type_hir.rs new file mode 100644 index 0000000000000..80e4608be82e6 --- /dev/null +++ b/tests/ui/union/unnamed-fields/restrict_type_hir.rs @@ -0,0 +1,44 @@ +//@ aux-build:dep.rs + +// test for #121151 + +#![allow(incomplete_features)] +#![feature(unnamed_fields)] + +extern crate dep; + +#[repr(C)] +struct A { + a: u8, +} + +enum BadEnum { + A, + B, +} + +#[repr(C)] +enum BadEnum2 { + A, + B, +} + +type MyStruct = A; +type MyI32 = i32; + +#[repr(C)] +struct L { + _: i32, //~ ERROR unnamed fields can only have struct or union types + _: MyI32, //~ ERROR unnamed fields can only have struct or union types + _: BadEnum, //~ ERROR unnamed fields can only have struct or union types + _: BadEnum2, //~ ERROR unnamed fields can only have struct or union types + _: MyStruct, + _: dep::BadStruct, //~ ERROR named type of unnamed field must have `#[repr(C)]` representation + _: dep::BadEnum, //~ ERROR unnamed fields can only have struct or union types + _: dep::BadEnum2, //~ ERROR unnamed fields can only have struct or union types + _: dep::BadAlias, //~ ERROR unnamed fields can only have struct or union types + _: dep::GoodAlias, + _: dep::GoodStruct, +} + +fn main() {} diff --git a/tests/ui/union/unnamed-fields/restrict_type_hir.stderr b/tests/ui/union/unnamed-fields/restrict_type_hir.stderr new file mode 100644 index 0000000000000..3e80d7fbf5c0e --- /dev/null +++ b/tests/ui/union/unnamed-fields/restrict_type_hir.stderr @@ -0,0 +1,62 @@ +error: unnamed fields can only have struct or union types + --> $DIR/restrict_type_hir.rs:31:5 + | +LL | _: i32, + | ^^^^^^ + +error: unnamed fields can only have struct or union types + --> $DIR/restrict_type_hir.rs:32:5 + | +LL | _: MyI32, + | ^^^^^^^^ + +error: unnamed fields can only have struct or union types + --> $DIR/restrict_type_hir.rs:33:5 + | +LL | _: BadEnum, + | ^^^^^^^^^^ + +error: unnamed fields can only have struct or union types + --> $DIR/restrict_type_hir.rs:34:5 + | +LL | _: BadEnum2, + | ^^^^^^^^^^^ + +error: named type of unnamed field must have `#[repr(C)]` representation + --> $DIR/restrict_type_hir.rs:36:5 + | +LL | _: dep::BadStruct, + | ^^^^^^^^^^^^^^^^^ unnamed field defined here + | + ::: $DIR/auxiliary/dep.rs:4:1 + | +LL | pub struct BadStruct(()); + | -------------------- `BadStruct` defined here + | +help: add `#[repr(C)]` to this struct + --> $DIR/auxiliary/dep.rs:4:1 + | +LL + #[repr(C)] +LL | pub struct BadStruct(()); + | + +error: unnamed fields can only have struct or union types + --> $DIR/restrict_type_hir.rs:37:5 + | +LL | _: dep::BadEnum, + | ^^^^^^^^^^^^^^^ + +error: unnamed fields can only have struct or union types + --> $DIR/restrict_type_hir.rs:38:5 + | +LL | _: dep::BadEnum2, + | ^^^^^^^^^^^^^^^^ + +error: unnamed fields can only have struct or union types + --> $DIR/restrict_type_hir.rs:39:5 + | +LL | _: dep::BadAlias, + | ^^^^^^^^^^^^^^^^ + +error: aborting due to 8 previous errors +