[COMMITTED 28/77] gccrs: Implement compilation of #[repr(transparent)] structs

[email protected]
Newsgroups gmane.comp.gcc.rust,gmane.comp.gcc.patches
Message-ID <[email protected]>
From: Yap Zhi Heng <[email protected]>

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 <[email protected]>
---
 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

diff --git a/gcc/rust/backend/rust-compile-expr.cc b/gcc/rust/backend/rust-compile-expr.cc
index 55ff3263a11..4bcd11da416 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 90e1985c993..ad5c483221a 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 e71d404f1fa..01f80da52a4 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 040ba9ad468..89a4bcdcaef 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 00000000000..611cd851c40
--- /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 00000000000..97ca9301781
--- /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 5aef4db0c21..9f4cd5c036a 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 26e0fed2173..60da8a1dd9e 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]
 }
-- 
2.50.1
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.