Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add more checks for unnamed_fields during HIR analysis #121198

Merged
merged 1 commit into from
Feb 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions compiler/rustc_hir/src/hir.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down
2 changes: 2 additions & 0 deletions compiler/rustc_hir_analysis/messages.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
23 changes: 13 additions & 10 deletions compiler/rustc_hir_analysis/src/check/check.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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) });
}
}
}
Expand Down
10 changes: 9 additions & 1 deletion compiler/rustc_hir_analysis/src/collect.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
7 changes: 7 additions & 0 deletions compiler/rustc_hir_analysis/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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> {
Expand Down
18 changes: 18 additions & 0 deletions tests/ui/union/unnamed-fields/auxiliary/dep.rs
Original file line number Diff line number Diff line change
@@ -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;
44 changes: 44 additions & 0 deletions tests/ui/union/unnamed-fields/restrict_type_hir.rs
Original file line number Diff line number Diff line change
@@ -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() {}
62 changes: 62 additions & 0 deletions tests/ui/union/unnamed-fields/restrict_type_hir.stderr
Original file line number Diff line number Diff line change
@@ -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

Loading