about summary refs log tree commit diff
diff options
context:
space:
mode:
authorLeón Orell Valerian Liehr <me@fmease.dev>2024-02-18 05:10:17 +0100
committerGitHub <noreply@github.com>2024-02-18 05:10:17 +0100
commit68cf5372b3398f856936ae5b381c34bb452c93d2 (patch)
tree317792f429412abd612ef6e799e0a49bbdd1e3e7
parent28c0fa87bbb34ba5ff6b21e69b9bf33fb528d612 (diff)
parent62b789fba444476eeddb9ae575d69d66a53a23c6 (diff)
downloadrust-68cf5372b3398f856936ae5b381c34bb452c93d2.tar.gz
rust-68cf5372b3398f856936ae5b381c34bb452c93d2.zip
Rollup merge of #121198 - clubby789:unnamed-fields-hir-checks, r=compiler-errors
Add more checks for `unnamed_fields` during HIR analysis

Fixes #121151

I also found that we don't prevent enums here so
```rs
#[repr(C)]
#[derive(Debug)]
enum A {
    #[default]
    B,
    C,
}

#[repr(C)]
#[derive(Debug)]
struct D {
    _: A,
}
```
leads to an ICE on an `self.is_struct() || self.is_union()` assertion, so fixed that too.
-rw-r--r--compiler/rustc_hir/src/hir.rs6
-rw-r--r--compiler/rustc_hir_analysis/messages.ftl2
-rw-r--r--compiler/rustc_hir_analysis/src/check/check.rs23
-rw-r--r--compiler/rustc_hir_analysis/src/collect.rs10
-rw-r--r--compiler/rustc_hir_analysis/src/errors.rs7
-rw-r--r--tests/ui/union/unnamed-fields/auxiliary/dep.rs18
-rw-r--r--tests/ui/union/unnamed-fields/restrict_type_hir.rs44
-rw-r--r--tests/ui/union/unnamed-fields/restrict_type_hir.stderr62
8 files changed, 161 insertions, 11 deletions
diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs
index 77044df9a40..fcb15925f6a 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<Symbol>, ItemKind::ExternCrate(s), *s;
 
diff --git a/compiler/rustc_hir_analysis/messages.ftl b/compiler/rustc_hir_analysis/messages.ftl
index a61cfd0e4ce..f32b14aca23 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 1410273e3bc..2c367b15df2 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 43f0af5bd1d..a5ef1490bce 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 6a505b96197..3bd7687a544 100644
--- a/compiler/rustc_hir_analysis/src/errors.rs
+++ b/compiler/rustc_hir_analysis/src/errors.rs
@@ -662,6 +662,13 @@ pub(crate) struct InvalidUnionField {
 }
 
 #[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> {
     #[primary_span]
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 00000000000..a11f3e18f52
--- /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 00000000000..80e4608be82
--- /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 00000000000..3e80d7fbf5c
--- /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
+