Skip to content

Commit 8dfcc27

Browse files
committed
gccrs: Implement compilation of #[repr(transparent)] structs
gcc/rust/ChangeLog: * backend/rust-compile-expr.h (CompileExpr::compile_transparent_field_access): New helper function. * backend/rust-compile-expr.cc (CompileExpr::compile_transparent_field_access): Implement helper function for accessing the field of #[repr(transparent)] ADTs. (CompileExpr::visit (HIR::StructExprStructFields)): Support struct field construction properly for #[repr(transparent)] structs. (CompileExpr::visit (HIR::FieldAccessExpr)): Implement proper compilation of field access for #[repr(transparent)] ADTs. * backend/rust-compile-type.cc (TyTyResolveCompile::visit (TyTy::ADTType)): Implement proper compilation of typing for #[repr(transparent)] ADTs. gcc/testsuite/ChangeLog: * rust/execute/torture/c_string.rs: Fix missing #[repr(transparent)] * rust/execute/torture/c_string_ensure_null_term.rs: Ditto. * rust/compile/c_string_null_byte_check.rs: Ditto. Signed-Off-By: Yap Zhi Heng <yapzhhg@gmail.com>
1 parent 41545c8 commit 8dfcc27

7 files changed

Lines changed: 144 additions & 26 deletions

File tree

gcc/rust/backend/rust-compile-expr.cc

Lines changed: 53 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -769,10 +769,22 @@ CompileExpr::visit (HIR::StructExprStructFields &struct_expr)
769769

770770
if (!adt->is_enum ())
771771
{
772-
translated
773-
= Backend::constructor_expression (compiled_adt_type, adt->is_enum (),
774-
arguments, union_disriminator,
775-
struct_expr.get_locus ());
772+
auto repr_kind = adt->get_repr_options ().repr_kind;
773+
if (repr_kind == TyTy::ADTType::ReprKind::TRANSPARENT)
774+
{
775+
translated
776+
= fold_build1_loc (struct_expr.get_locus (), VIEW_CONVERT_EXPR,
777+
compiled_adt_type, arguments.front ());
778+
}
779+
else
780+
{
781+
translated
782+
= Backend::constructor_expression (compiled_adt_type,
783+
adt->is_enum (), arguments,
784+
union_disriminator,
785+
struct_expr.get_locus ());
786+
}
787+
776788
return;
777789
}
778790

@@ -843,6 +855,15 @@ CompileExpr::visit (HIR::FieldAccessExpr &expr)
843855
bool ok = variant->lookup_field (expr.get_field_name ().as_string (),
844856
nullptr, &field_index);
845857
rust_assert (ok);
858+
859+
auto repr_kind = adt->get_repr_options ().repr_kind;
860+
if (repr_kind == TyTy::ADTType::ReprKind::TRANSPARENT)
861+
{
862+
translated
863+
= compile_transparent_field_access (variant, expr.get_locus (),
864+
receiver_ref);
865+
return;
866+
}
846867
}
847868
else if (receiver->get_kind () == TyTy::TypeKind::REF)
848869
{
@@ -859,22 +880,30 @@ CompileExpr::visit (HIR::FieldAccessExpr &expr)
859880
nullptr, &field_index);
860881
rust_assert (ok);
861882

862-
// TODO this check is only used for CStr, test again when we support
863-
// compilation of #[repr(transparent)] structs
864-
if (RS_DST_FLAG_P (TREE_TYPE (receiver_ref)))
883+
auto repr_kind = adt->get_repr_options ().repr_kind;
884+
if (repr_kind == TyTy::ADTType::ReprKind::TRANSPARENT)
865885
{
866-
const TyTy::StructFieldType *field
867-
= variant->get_field_at_index (field_index);
868-
tree field_type
869-
= TyTyResolveCompile::compile (ctx, field->get_field_type ());
870-
translated = fold_build1_loc (expr.get_locus (), VIEW_CONVERT_EXPR,
871-
field_type, receiver_ref);
886+
translated
887+
= compile_transparent_field_access (variant, expr.get_locus (),
888+
receiver_ref);
872889
return;
873890
}
874891
else
875892
{
876893
tree indirect = indirect_expression (receiver_ref, expr.get_locus ());
877-
receiver_ref = indirect;
894+
895+
auto repr_kind = adt->get_repr_options ().repr_kind;
896+
if (repr_kind == TyTy::ADTType::ReprKind::TRANSPARENT)
897+
{
898+
translated
899+
= compile_transparent_field_access (variant, expr.get_locus (),
900+
indirect);
901+
return;
902+
}
903+
else
904+
{
905+
receiver_ref = indirect;
906+
}
878907
}
879908
}
880909

@@ -2114,6 +2143,16 @@ CompileExpr::compile_c_string_literal (const HIR::LiteralExpr &expr,
21142143
expr.get_locus ());
21152144
}
21162145

2146+
tree
2147+
CompileExpr::compile_transparent_field_access (TyTy::VariantDef *variant,
2148+
location_t locus,
2149+
tree source_expr)
2150+
{
2151+
const TyTy::StructFieldType *field = variant->get_field_at_index (0);
2152+
tree field_type = TyTyResolveCompile::compile (ctx, field->get_field_type ());
2153+
return fold_build1_loc (locus, VIEW_CONVERT_EXPR, field_type, source_expr);
2154+
}
2155+
21172156
tree
21182157
CompileExpr::type_cast_expression (tree type_to_cast_to, tree expr_tree,
21192158
location_t location)

gcc/rust/backend/rust-compile-expr.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,9 @@ class CompileExpr : private HIRCompileBase, protected HIR::HIRExpressionVisitor
145145
const TyTy::ArrayType &array_tyty, tree array_type,
146146
HIR::ArrayElemsCopied &elems);
147147

148+
tree compile_transparent_field_access (TyTy::VariantDef *variant,
149+
location_t locus, tree source_expr);
150+
148151
protected:
149152
tree generate_closure_function (HIR::ClosureExpr &expr,
150153
TyTy::ClosureType &closure_tyty,

gcc/rust/backend/rust-compile-type.cc

Lines changed: 42 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -295,7 +295,35 @@ void
295295
TyTyResolveCompile::visit (const TyTy::ADTType &type)
296296
{
297297
tree type_record = error_mark_node;
298-
if (!type.is_enum ())
298+
299+
TyTy::ADTType::ReprOptions repr = type.get_repr_options ();
300+
if (repr.repr_kind == TyTy::ADTType::ReprKind::TRANSPARENT)
301+
{
302+
rust_assert (type.number_of_variants () == 1);
303+
TyTy::VariantDef &variant = *type.get_variants ().at (0);
304+
305+
rust_assert (variant.num_fields () <= 1);
306+
if (variant.num_fields () == 0)
307+
{
308+
// 0-field transparent repr
309+
// Rustonomicon states that transparent structs should have a single
310+
// non-zero-sized field, but rustc compiles one with 0 fields happily
311+
// without errors, so not sure what's the correct treatment.
312+
//
313+
// For now, treat it as a unit struct
314+
type_record = Backend::struct_type ({});
315+
}
316+
else
317+
{
318+
// single field transparent repr
319+
const TyTy::StructFieldType *field = variant.get_field_at_index (0);
320+
type_record
321+
= TyTyResolveCompile::compile (ctx, field->get_field_type ());
322+
}
323+
}
324+
325+
// compilation of non-transparent ADTs below
326+
else if (!type.is_enum ())
299327
{
300328
rust_assert (type.number_of_variants () == 1);
301329

@@ -442,22 +470,24 @@ TyTyResolveCompile::visit (const TyTy::ADTType &type)
442470
// TODO: "packed" should only narrow type alignment and "align" should only
443471
// widen it. Do we need to check and enforce this here, or is it taken care of
444472
// later on in the gcc middle-end?
445-
TyTy::ADTType::ReprOptions repr = type.get_repr_options ();
446-
if (repr.pack)
473+
if (repr.repr_kind != TyTy::ADTType::ReprKind::TRANSPARENT)
447474
{
448-
TYPE_PACKED (type_record) = 1;
449-
if (repr.pack > 1)
475+
if (repr.pack)
476+
{
477+
TYPE_PACKED (type_record) = 1;
478+
if (repr.pack > 1)
479+
{
480+
SET_TYPE_ALIGN (type_record, repr.pack * 8);
481+
TYPE_USER_ALIGN (type_record) = 1;
482+
}
483+
}
484+
else if (repr.align)
450485
{
451-
SET_TYPE_ALIGN (type_record, repr.pack * 8);
486+
SET_TYPE_ALIGN (type_record, repr.align * 8);
452487
TYPE_USER_ALIGN (type_record) = 1;
453488
}
489+
layout_type (type_record);
454490
}
455-
else if (repr.align)
456-
{
457-
SET_TYPE_ALIGN (type_record, repr.align * 8);
458-
TYPE_USER_ALIGN (type_record) = 1;
459-
}
460-
layout_type (type_record);
461491

462492
std::string named_struct_str
463493
= type.get_ident ().path.get () + type.subst_as_string ();

gcc/testsuite/rust/compile/c_string_null_byte_check.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
type c_char = u8;
66

77
#[lang = "CStr"]
8+
#[repr(transparent)]
89
pub struct CStr {
910
inner: [c_char]
1011
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
#![feature(no_core, intrinsics, staged_api, lang_items)]
2+
#![no_core]
3+
4+
#[lang = "sized"]
5+
pub trait Sized {}
6+
7+
// below's helper code copied from issue-1232.rs
8+
extern "rust-intrinsic" {
9+
#[rustc_const_stable(feature = "const_ptr_offset", since = "1.61.0")]
10+
fn offset<T>(dst: *const T, offset: isize) -> *const T;
11+
}
12+
13+
#[lang = "const_ptr"]
14+
impl<T> *const T {
15+
pub const unsafe fn offset(self, count: isize) -> *const T {
16+
unsafe { offset(self, count) }
17+
}
18+
19+
pub const unsafe fn add(self, count: usize) -> Self {
20+
unsafe { self.offset(count as isize) }
21+
}
22+
23+
pub const fn as_ptr(self) -> *const T {
24+
self as *const T
25+
}
26+
}
27+
28+
#[repr(transparent)]
29+
pub struct Foo {
30+
inner: i32
31+
}
32+
33+
impl Foo {
34+
pub const fn to_ptr(&self) -> *const i32 {
35+
&self.inner as *const i32
36+
}
37+
}
38+
39+
pub fn main() -> i32 {
40+
let a = Foo { inner: 67 };
41+
let val = unsafe { a.to_ptr() };
42+
unsafe { *val - 67 }
43+
}

gcc/testsuite/rust/execute/torture/c_string.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ extern "C" {
1010
type c_char = u8;
1111

1212
#[lang = "CStr"]
13+
#[repr(transparent)]
1314
pub struct CStr {
1415
inner: [c_char]
1516
}

gcc/testsuite/rust/execute/torture/c_string_ensure_null_term.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ extern "C" {
3333
type c_char = u8;
3434

3535
#[lang = "CStr"]
36+
#[repr(transparent)]
3637
pub struct CStr {
3738
inner: [c_char]
3839
}

0 commit comments

Comments
 (0)