From: Yap Zhi Heng <[email protected]>
gcc/rust/ChangeLog:
* typecheck/rust-tyty.h (BaseType::is_zero_sized): New function.
(TupleType::is_zero_sized): New function.
(ArrayType::is_zero_sized): New function.
(ADTType::is_zero_sized): New function.
* typecheck/rust-tyty.cc (BaseType::is_zero_sized): Implement checking
of
whether a type is zero-sized.
(TupleType::is_zero_sized): Ditto.
(ArrayType::is_zero_sized): Ditto.
(ADTType::is_zero_sized): Ditto.
* typecheck/rust-hir-type-check-item.cc (TypeCheckItem::visit
(StructStruct)): Update
transparent repr type-checking to check for zero-sized members.
* backend/rust-compile-type.cc (TyTyResolveCompile::visit
(TyTy::ADTType)): Support
compilation of transparent repr structs with more than 1 fields.
gcc/testsuite/ChangeLog:
* rust/compile/repr_transparent_fields.rs: Add new test case with
PhantomData.
Signed-Off-By: Yap Zhi Heng <[email protected]>
---
This change was merged into the gccrs repository and is posted here for
upstream visibility and potential drive-by review, as requested by GCC
release managers.
Each commit email contains a link to its details on github from where you can
find the Pull-Request and associated discussions.
Commit on github:
https://github.com/Rust-GCC/gccrs/commit/941f9359546ea10c548c8f379bb0edfd9df3c52a
The commit has NOT been mentioned in any issue.
The commit has been mentioned in the following pull-request(s):
- https://github.com/Rust-GCC/gccrs/pull/4698
gcc/rust/backend/rust-compile-type.cc | 17 ++-
.../typecheck/rust-hir-type-check-item.cc | 26 +++--
gcc/rust/typecheck/rust-tyty.cc | 106 ++++++++++++++++++
gcc/rust/typecheck/rust-tyty.h | 10 ++
.../rust/compile/repr_transparent_fields.rs | 14 ++-
5 files changed, 162 insertions(+), 11 deletions(-)
diff --git a/gcc/rust/backend/rust-compile-type.cc
b/gcc/rust/backend/rust-compile-type.cc
index 01f80da52..f0c2d1bf1 100644
--- a/gcc/rust/backend/rust-compile-type.cc
+++ b/gcc/rust/backend/rust-compile-type.cc
@@ -302,7 +302,6 @@ TyTyResolveCompile::visit (const TyTy::ADTType &type)
rust_assert (type.number_of_variants () == 1);
TyTy::VariantDef &variant = *type.get_variants ().at (0);
- rust_assert (variant.num_fields () <= 1);
if (variant.num_fields () == 0)
{
// 0-field transparent repr
@@ -313,13 +312,27 @@ TyTyResolveCompile::visit (const TyTy::ADTType &type)
// For now, treat it as a unit struct
type_record = Backend::struct_type ({});
}
- else
+ else if (variant.num_fields () == 1)
{
// single field transparent repr
const TyTy::StructFieldType *field = variant.get_field_at_index (0);
type_record
= TyTyResolveCompile::compile (ctx, field->get_field_type ());
}
+ else
+ {
+ // more than one field - typechecking already ensures there's only one
+ // non-zero-sized field, just compile accessor for that
+ // non-zero-sized.
+ for (size_t i = 0; i < variant.num_fields (); i++)
+ {
+ auto field_ty = variant.get_field_at_index (i)->get_field_type ();
+ if (!field_ty->is_zero_sized ())
+ {
+ type_record = TyTyResolveCompile::compile (ctx, field_ty);
+ }
+ }
+ }
}
// compilation of non-transparent ADTs below
diff --git a/gcc/rust/typecheck/rust-hir-type-check-item.cc
b/gcc/rust/typecheck/rust-hir-type-check-item.cc
index ac0eeaf27..6c1d91132 100644
--- a/gcc/rust/typecheck/rust-hir-type-check-item.cc
+++ b/gcc/rust/typecheck/rust-hir-type-check-item.cc
@@ -343,14 +343,6 @@ TypeCheckItem::visit (HIR::StructStruct &struct_decl)
const AST::AttrVec &attrs = struct_decl.get_outer_attrs ();
TyTy::ADTType::ReprOptions repr
= parse_repr_options (attrs, struct_decl.get_locus ());
- if (repr.repr_kind == TyTy::ADTType::ReprKind::TRANSPARENT
- && struct_decl.get_fields ().size () > 1)
- {
- rust_error_at (struct_decl.get_locus (), ErrorCode::E0690,
- "transparent struct needs at most one field with "
- "non-trivial size or alignment, but has %lu",
- (unsigned long) struct_decl.get_fields ().size ());
- }
std::vector<TyTy::StructFieldType *> fields;
for (auto &field : struct_decl.get_fields ())
@@ -373,6 +365,24 @@ TypeCheckItem::visit (HIR::StructStruct &struct_decl)
context->insert_type (field.get_mappings (), ty_field->get_field_type
());
}
+ if (repr.repr_kind == TyTy::ADTType::ReprKind::TRANSPARENT)
+ {
+ size_t num_non_zst = 0;
+ for (auto &field : fields)
+ {
+ if (!field->get_field_type ()->is_zero_sized ())
+ num_non_zst++;
+ }
+ if (num_non_zst > 1)
+ {
+ rust_error_at (struct_decl.get_locus (), ErrorCode::E0690,
+ "transparent struct needs at most one field with "
+ "non-trivial size or alignment, but has %lu",
+ (unsigned long) struct_decl.get_fields ().size ());
+ return;
+ }
+ }
+
auto &nr_ctx = Resolver2_0::FinalizedNameResolutionContext::get ();
CanonicalPath path
diff --git a/gcc/rust/typecheck/rust-tyty.cc b/gcc/rust/typecheck/rust-tyty.cc
index d5ef3b063..825febe6f 100644
--- a/gcc/rust/typecheck/rust-tyty.cc
+++ b/gcc/rust/typecheck/rust-tyty.cc
@@ -926,6 +926,58 @@ BaseType::is_concrete () const
return false;
}
+bool
+BaseType::is_zero_sized () const
+{
+ const TyTy::BaseType *x = destructure ();
+ switch (x->get_kind ())
+ {
+ // primitives that are always non-zero size
+ case FNPTR:
+ case FNDEF:
+ case SLICE:
+ case POINTER:
+ case REF:
+ case CLOSURE:
+ case INFER:
+ case BOOL:
+ case CHAR:
+ case INT:
+ case UINT:
+ case FLOAT:
+ case USIZE:
+ case ISIZE:
+ case OPAQUE:
+ case STR:
+ case DYNAMIC:
+ case CONST:
+ case PARAM:
+ case PROJECTION:
+ case PLACEHOLDER:
+ case ERROR:
+ return false;
+
+ case NEVER:
+ return true;
+ case TUPLE:
+ {
+ const TupleType *tuple_ty = static_cast<const TupleType *> (x);
+ return tuple_ty->is_zero_sized ();
+ }
+ case ARRAY:
+ {
+ const ArrayType *array_ty = static_cast<const ArrayType *> (x);
+ return array_ty->is_zero_sized ();
+ }
+ case ADT:
+ {
+ const ADTType *adt_ty = static_cast<const ADTType *> (x);
+ return adt_ty->is_zero_sized ();
+ }
+ }
+ return false;
+}
+
bool
BaseType::has_substitutions_defined () const
{
@@ -1881,6 +1933,27 @@ ADTType::is_equal (const BaseType &other) const
return true;
}
+bool
+ADTType::is_zero_sized () const
+{
+ auto phantom = Analysis::Mappings::get ().lookup_lang_item (
+ LangItem::Kind::PHANTOM_DATA);
+ if (phantom.has_value () && phantom.value () == get_id ())
+ return true;
+
+ for (auto *variant : get_variants ())
+ {
+ for (size_t i = 0; i < variant->num_fields (); i++)
+ {
+ if (!variant->get_field_at_index (i)
+ ->get_field_type ()
+ ->is_zero_sized ())
+ return false;
+ }
+ }
+ return true;
+}
+
DefId
ADTType::get_id () const
{
@@ -2084,6 +2157,19 @@ TupleType::is_equal (const BaseType &other) const
return true;
}
+bool
+TupleType::is_zero_sized () const
+{
+ if (num_fields () == 0)
+ return true;
+ for (size_t i = 0; i < num_fields (); i++)
+ {
+ if (!get_field (i)->is_zero_sized ())
+ return false;
+ }
+ return true;
+}
+
BaseType *
TupleType::clone () const
{
@@ -2513,6 +2599,26 @@ ArrayType::is_equal (const BaseType &other) const
return this_element_type->is_equal (*other_element_type);
}
+bool
+ArrayType::is_zero_sized () const
+{
+ auto *capacity_ty = get_capacity ();
+ if (capacity_ty != nullptr
+ && capacity_ty->get_kind () == TyTy::TypeKind::CONST)
+ {
+ auto *capacity_const = capacity_ty->as_const_type ();
+ auto &capacity_value
+ = *static_cast<TyTy::ConstValueType *> (capacity_const);
+ auto cap_tree = capacity_value.get_value ();
+ size_t cap_wi = (size_t) wi::to_wide (cap_tree).to_uhwi ();
+ if (cap_wi == 0)
+ {
+ return true;
+ }
+ }
+ return false;
+}
+
BaseType *
ArrayType::get_element_type () const
{
diff --git a/gcc/rust/typecheck/rust-tyty.h b/gcc/rust/typecheck/rust-tyty.h
index f645f673f..ec9b71123 100644
--- a/gcc/rust/typecheck/rust-tyty.h
+++ b/gcc/rust/typecheck/rust-tyty.h
@@ -256,6 +256,10 @@ public:
// primitives
bool is_concrete () const;
+ // returns if the type is a zero-sized type, which is a type that occupies no
+ // space in memory
+ bool is_zero_sized () const;
+
// return the type-kind
TypeKind get_kind () const;
@@ -775,6 +779,8 @@ public:
bool is_equal (const BaseType &other) const override;
+ bool is_zero_sized () const;
+
size_t num_fields () const;
BaseType *get_field (size_t index) const;
@@ -974,6 +980,8 @@ public:
bool is_equal (const BaseType &other) const override;
+ bool is_zero_sized () const;
+
std::string get_identifier () const { return identifier; }
std::string get_name () const override final
@@ -1375,6 +1383,8 @@ public:
bool is_equal (const BaseType &other) const override;
+ bool is_zero_sized () const;
+
BaseType *get_element_type () const;
const TyVar &get_var_element_type () const;
diff --git a/gcc/testsuite/rust/compile/repr_transparent_fields.rs
b/gcc/testsuite/rust/compile/repr_transparent_fields.rs
index 190021386..72219e486 100644
--- a/gcc/testsuite/rust/compile/repr_transparent_fields.rs
+++ b/gcc/testsuite/rust/compile/repr_transparent_fields.rs
@@ -1,6 +1,12 @@
-#![feature(no_core)]
+#![feature(no_core, lang_items)]
#![no_core]
+#[lang = "sized"]
+trait Sized {}
+
+#[lang = "phantom_data"]
+pub struct PhantomData<T: ?Sized>;
+
#[repr(transparent)]
struct Foo { // { dg-error "transparent struct needs at most one field with
non-trivial size or alignment, but has 2" }
foo: i32,
@@ -14,3 +20,9 @@ struct Bar {}
struct Baz {
foo: i32
}
+
+#[repr(transparent)]
+struct Qux {
+ foo: i32,
+ phantom: PhantomData<i32>,
+}
base-commit: ef5bf0e0183fc81701b17d93ad3911a172facdc8
--
2.54.0