[gccrs,COMMIT,2/3] gccrs: Implement compilation of #[repr(transparent)] structs

Message ID 20260708091609.3108-2-gerris.rs@gmail.com
State New
Headers
Series [gccrs,COMMIT,1/3] gccrs: Add support for #[repr(transparent)] attribute |

Checks

Context Check Description
linaro-tcwg-bot/tcwg_simplebootstrap_build--master-arm-bootstrap pending Build did not complete; will be retriggered

Commit Message

gerris.rs@gmail.com July 8, 2026, 9:16 a.m. UTC
  From: Yap Zhi Heng <yapzhhg@gmail.com>

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>
---
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/e85dfa8e6cc46043f027cbf4cec1734b730e966d

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/4650

 gcc/rust/backend/rust-compile-expr.cc         | 53 +++++++++++++-----
 gcc/rust/backend/rust-compile-expr.h          |  3 ++
 gcc/rust/backend/rust-compile-type.cc         | 54 ++++++++++++++-----
 .../rust/compile/c_string_null_byte_check.rs  |  1 +
 .../rust/compile/repr_transparent_fields2.rs  | 21 ++++++++
 .../rust/compile/transparent_struct_deref.rs  | 43 +++++++++++++++
 .../rust/execute/torture/c_string.rs          |  1 +
 .../torture/c_string_ensure_null_term.rs      |  1 +
 8 files changed, 152 insertions(+), 25 deletions(-)
 create mode 100644 gcc/testsuite/rust/compile/repr_transparent_fields2.rs
 create mode 100644 gcc/testsuite/rust/compile/transparent_struct_deref.rs
  

Patch

diff --git a/gcc/rust/backend/rust-compile-expr.cc b/gcc/rust/backend/rust-compile-expr.cc
index 55ff3263a..4bcd11da4 100644
--- a/gcc/rust/backend/rust-compile-expr.cc
+++ b/gcc/rust/backend/rust-compile-expr.cc
@@ -769,10 +769,22 @@  CompileExpr::visit (HIR::StructExprStructFields &struct_expr)
 
   if (!adt->is_enum ())
     {
-      translated
-	= Backend::constructor_expression (compiled_adt_type, adt->is_enum (),
-					   arguments, union_disriminator,
-					   struct_expr.get_locus ());
+      auto repr_kind = adt->get_repr_options ().repr_kind;
+      if (repr_kind == TyTy::ADTType::ReprKind::TRANSPARENT)
+	{
+	  translated
+	    = fold_build1_loc (struct_expr.get_locus (), VIEW_CONVERT_EXPR,
+			       compiled_adt_type, arguments.front ());
+	}
+      else
+	{
+	  translated
+	    = Backend::constructor_expression (compiled_adt_type,
+					       adt->is_enum (), arguments,
+					       union_disriminator,
+					       struct_expr.get_locus ());
+	}
+
       return;
     }
 
@@ -843,6 +855,15 @@  CompileExpr::visit (HIR::FieldAccessExpr &expr)
       bool ok = variant->lookup_field (expr.get_field_name ().as_string (),
 				       nullptr, &field_index);
       rust_assert (ok);
+
+      auto repr_kind = adt->get_repr_options ().repr_kind;
+      if (repr_kind == TyTy::ADTType::ReprKind::TRANSPARENT)
+	{
+	  translated
+	    = compile_transparent_field_access (variant, expr.get_locus (),
+						receiver_ref);
+	  return;
+	}
     }
   else if (receiver->get_kind () == TyTy::TypeKind::REF)
     {
@@ -859,16 +880,12 @@  CompileExpr::visit (HIR::FieldAccessExpr &expr)
 				       nullptr, &field_index);
       rust_assert (ok);
 
-      // TODO this check is only used for CStr, test again when we support
-      // compilation of #[repr(transparent)] structs
-      if (RS_DST_FLAG_P (TREE_TYPE (receiver_ref)))
+      auto repr_kind = adt->get_repr_options ().repr_kind;
+      if (repr_kind == TyTy::ADTType::ReprKind::TRANSPARENT)
 	{
-	  const TyTy::StructFieldType *field
-	    = variant->get_field_at_index (field_index);
-	  tree field_type
-	    = TyTyResolveCompile::compile (ctx, field->get_field_type ());
-	  translated = fold_build1_loc (expr.get_locus (), VIEW_CONVERT_EXPR,
-					field_type, receiver_ref);
+	  translated
+	    = compile_transparent_field_access (variant, expr.get_locus (),
+						receiver_ref);
 	  return;
 	}
       else
@@ -2114,6 +2131,16 @@  CompileExpr::compile_c_string_literal (const HIR::LiteralExpr &expr,
 					  expr.get_locus ());
 }
 
+tree
+CompileExpr::compile_transparent_field_access (TyTy::VariantDef *variant,
+					       location_t locus,
+					       tree source_expr)
+{
+  const TyTy::StructFieldType *field = variant->get_field_at_index (0);
+  tree field_type = TyTyResolveCompile::compile (ctx, field->get_field_type ());
+  return fold_build1_loc (locus, VIEW_CONVERT_EXPR, field_type, source_expr);
+}
+
 tree
 CompileExpr::type_cast_expression (tree type_to_cast_to, tree expr_tree,
 				   location_t location)
diff --git a/gcc/rust/backend/rust-compile-expr.h b/gcc/rust/backend/rust-compile-expr.h
index 90e1985c9..ad5c48322 100644
--- a/gcc/rust/backend/rust-compile-expr.h
+++ b/gcc/rust/backend/rust-compile-expr.h
@@ -145,6 +145,9 @@  protected:
 			  const TyTy::ArrayType &array_tyty, tree array_type,
 			  HIR::ArrayElemsCopied &elems);
 
+  tree compile_transparent_field_access (TyTy::VariantDef *variant,
+					 location_t locus, tree source_expr);
+
 protected:
   tree generate_closure_function (HIR::ClosureExpr &expr,
 				  TyTy::ClosureType &closure_tyty,
diff --git a/gcc/rust/backend/rust-compile-type.cc b/gcc/rust/backend/rust-compile-type.cc
index e71d404f1..01f80da52 100644
--- a/gcc/rust/backend/rust-compile-type.cc
+++ b/gcc/rust/backend/rust-compile-type.cc
@@ -295,7 +295,35 @@  void
 TyTyResolveCompile::visit (const TyTy::ADTType &type)
 {
   tree type_record = error_mark_node;
-  if (!type.is_enum ())
+
+  TyTy::ADTType::ReprOptions repr = type.get_repr_options ();
+  if (repr.repr_kind == TyTy::ADTType::ReprKind::TRANSPARENT)
+    {
+      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
+	  // Rustonomicon states that transparent structs should have a single
+	  // non-zero-sized field, but rustc compiles one with 0 fields happily
+	  // without errors, so not sure what's the correct treatment.
+	  //
+	  // For now, treat it as a unit struct
+	  type_record = Backend::struct_type ({});
+	}
+      else
+	{
+	  // single field transparent repr
+	  const TyTy::StructFieldType *field = variant.get_field_at_index (0);
+	  type_record
+	    = TyTyResolveCompile::compile (ctx, field->get_field_type ());
+	}
+    }
+
+  // compilation of non-transparent ADTs below
+  else if (!type.is_enum ())
     {
       rust_assert (type.number_of_variants () == 1);
 
@@ -442,22 +470,24 @@  TyTyResolveCompile::visit (const TyTy::ADTType &type)
   // TODO: "packed" should only narrow type alignment and "align" should only
   // widen it. Do we need to check and enforce this here, or is it taken care of
   // later on in the gcc middle-end?
-  TyTy::ADTType::ReprOptions repr = type.get_repr_options ();
-  if (repr.pack)
+  if (repr.repr_kind != TyTy::ADTType::ReprKind::TRANSPARENT)
     {
-      TYPE_PACKED (type_record) = 1;
-      if (repr.pack > 1)
+      if (repr.pack)
+	{
+	  TYPE_PACKED (type_record) = 1;
+	  if (repr.pack > 1)
+	    {
+	      SET_TYPE_ALIGN (type_record, repr.pack * 8);
+	      TYPE_USER_ALIGN (type_record) = 1;
+	    }
+	}
+      else if (repr.align)
 	{
-	  SET_TYPE_ALIGN (type_record, repr.pack * 8);
+	  SET_TYPE_ALIGN (type_record, repr.align * 8);
 	  TYPE_USER_ALIGN (type_record) = 1;
 	}
+      layout_type (type_record);
     }
-  else if (repr.align)
-    {
-      SET_TYPE_ALIGN (type_record, repr.align * 8);
-      TYPE_USER_ALIGN (type_record) = 1;
-    }
-  layout_type (type_record);
 
   std::string named_struct_str
     = type.get_ident ().path.get () + type.subst_as_string ();
diff --git a/gcc/testsuite/rust/compile/c_string_null_byte_check.rs b/gcc/testsuite/rust/compile/c_string_null_byte_check.rs
index 040ba9ad4..89a4bcdca 100644
--- a/gcc/testsuite/rust/compile/c_string_null_byte_check.rs
+++ b/gcc/testsuite/rust/compile/c_string_null_byte_check.rs
@@ -5,6 +5,7 @@ 
 type c_char = u8;
 
 #[lang = "CStr"]
+#[repr(transparent)]
 pub struct CStr {
     inner: [c_char]
 }
diff --git a/gcc/testsuite/rust/compile/repr_transparent_fields2.rs b/gcc/testsuite/rust/compile/repr_transparent_fields2.rs
new file mode 100644
index 000000000..611cd851c
--- /dev/null
+++ b/gcc/testsuite/rust/compile/repr_transparent_fields2.rs
@@ -0,0 +1,21 @@ 
+// { dg-additional-options "-fdump-tree-gimple" }
+#![feature(no_core)]
+#![no_core]
+
+struct NonTransparent {
+    foo: i32
+}
+
+#[repr(transparent)]
+struct Transparent {
+    foo: i32
+}
+
+fn main () -> i32 {
+    // { dg-final { scan-tree-dump-times {(?n)my_obj . 42;$} 1 gimple } }
+    let mut my_obj = Transparent { foo: 42 };
+    // { dg-final { scan-tree-dump-times {(?n)my_obj2.foo . 40;$} 1 gimple } }
+    let my_obj2 = NonTransparent { foo: 40 };
+    my_obj.foo -= 2;
+    my_obj.foo - my_obj2.foo
+}
\ No newline at end of file
diff --git a/gcc/testsuite/rust/compile/transparent_struct_deref.rs b/gcc/testsuite/rust/compile/transparent_struct_deref.rs
new file mode 100644
index 000000000..97ca93017
--- /dev/null
+++ b/gcc/testsuite/rust/compile/transparent_struct_deref.rs
@@ -0,0 +1,43 @@ 
+#![feature(no_core, intrinsics, staged_api, lang_items)]
+#![no_core]
+
+#[lang = "sized"]
+pub trait Sized {}
+
+// below's helper code copied from issue-1232.rs
+extern "rust-intrinsic" {
+    #[rustc_const_stable(feature = "const_ptr_offset", since = "1.61.0")]
+    fn offset<T>(dst: *const T, offset: isize) -> *const T;
+}
+
+#[lang = "const_ptr"]
+impl<T> *const T {
+    pub const unsafe fn offset(self, count: isize) -> *const T {
+        unsafe { offset(self, count) }
+    }
+
+    pub const unsafe fn add(self, count: usize) -> Self {
+        unsafe { self.offset(count as isize) }
+    }
+
+    pub const fn as_ptr(self) -> *const T {
+        self as *const T
+    }
+}
+
+#[repr(transparent)]
+pub struct Foo {
+    inner: i32
+}
+
+impl Foo {
+    pub const fn to_ptr(&self) -> *const i32 {
+        &self.inner as *const i32
+    }
+}
+
+pub fn main() -> i32 {
+    let a = Foo { inner: 67 };
+    let val = unsafe { a.to_ptr() };
+    unsafe { *val - 67 }
+}
diff --git a/gcc/testsuite/rust/execute/torture/c_string.rs b/gcc/testsuite/rust/execute/torture/c_string.rs
index 5aef4db0c..9f4cd5c03 100644
--- a/gcc/testsuite/rust/execute/torture/c_string.rs
+++ b/gcc/testsuite/rust/execute/torture/c_string.rs
@@ -10,6 +10,7 @@  extern "C" {
 type c_char = u8;
 
 #[lang = "CStr"]
+#[repr(transparent)]
 pub struct CStr {
     inner: [c_char]
 }
diff --git a/gcc/testsuite/rust/execute/torture/c_string_ensure_null_term.rs b/gcc/testsuite/rust/execute/torture/c_string_ensure_null_term.rs
index 26e0fed21..60da8a1dd 100644
--- a/gcc/testsuite/rust/execute/torture/c_string_ensure_null_term.rs
+++ b/gcc/testsuite/rust/execute/torture/c_string_ensure_null_term.rs
@@ -33,6 +33,7 @@  extern "C" {
 type c_char = u8;
 
 #[lang = "CStr"]
+#[repr(transparent)]
 pub struct CStr {
     inner: [c_char]
 }