From: Yap Zhi Heng <[email protected]>

gcc/rust/ChangeLog:
        * util/rust-lang-item.h (LangItem::Kind): New CSTR kind.
        * util/rust-lang-item.cc (LangItem::lang_items): Ditto.
        * backend/rust-compile-type.cc (visit(TyTy::ReferenceType)): Add 
specific record
        type for CStr.
        * typecheck/rust-tyty.h (ReferenceType): Add function definition for 
is_dyn_cstr_type.
        * typecheck/rust-tyty.cc (ReferenceType::is_dyn_object): Update to 
include
        is_dyn_cstr_type.
        (ReferenceType::is_dyn_cstr_type): Add implementation to check whether 
the
        ReferenceType is of type CStr.
        * typecheck/rust-hir-type-check-base.cc(resolve_literal): Update 
C_STRING case to
        resolve to the CStr language item instead.

gcc/testsuite/ChangeLog:
        * rust/execute/torture/c_string.rs: Add CStr language item definition.
        * rust/compile/c_string_null_byte_check.rs: Ditto.
        * rust/execute/torture/c_string_ensure_null_term.rs: New test.

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/0a1e7a4435e772cf76cdb023f94afb8f905097d3

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

 gcc/rust/backend/rust-compile-type.cc         | 23 +++++++++
 .../typecheck/rust-hir-type-check-base.cc     | 32 ++++++------
 gcc/rust/typecheck/rust-tyty.cc               | 24 ++++++++-
 gcc/rust/typecheck/rust-tyty.h                |  1 +
 gcc/rust/util/rust-lang-item.cc               |  3 ++
 gcc/rust/util/rust-lang-item.h                |  3 ++
 .../rust/compile/c_string_null_byte_check.rs  | 15 +++++-
 .../rust/execute/torture/c_string.rs          | 24 +++++++--
 .../torture/c_string_ensure_null_term.rs      | 50 +++++++++++++++++++
 9 files changed, 154 insertions(+), 21 deletions(-)
 create mode 100644 
gcc/testsuite/rust/execute/torture/c_string_ensure_null_term.rs

diff --git a/gcc/rust/backend/rust-compile-type.cc 
b/gcc/rust/backend/rust-compile-type.cc
index 35d56cc47..da6a8ee4a 100644
--- a/gcc/rust/backend/rust-compile-type.cc
+++ b/gcc/rust/backend/rust-compile-type.cc
@@ -664,6 +664,7 @@ TyTyResolveCompile::visit (const TyTy::ReferenceType &type)
   const TyTy::SliceType *slice = nullptr;
   const TyTy::StrType *str = nullptr;
   const TyTy::DynamicObjectType *dyn = nullptr;
+  const TyTy::ADTType *adt = nullptr;
   if (type.is_dyn_slice_type (&slice))
     {
       tree type_record = create_slice_type_record (*slice);
@@ -698,6 +699,28 @@ TyTyResolveCompile::visit (const TyTy::ReferenceType &type)
 
       return;
     }
+  // Check for CStr, create a specific record for it
+  else if (type.is_dyn_cstr_type (&adt))
+    {
+      // CStr in core crate is defined as the following:
+      //
+      // #[repr(transparent)]
+      // pub struct CStr {
+      //    inner: [u8]
+      // }
+      //
+      // Reuse the c_char (u8) slice fat-pointer layout
+      TyTy::BaseType *u8 = nullptr;
+      ctx->get_tyctx ()->lookup_builtin ("u8", &u8);
+      // Create a synthetic SliceType over u8 and use that record layout
+      TyTy::SliceType synthetic_slice (adt->get_ref (), adt->get_ident 
().locus,
+                                      TyTy::TyVar (u8->get_ref ()));
+      tree type_record = create_slice_type_record (synthetic_slice);
+      translated
+       = Backend::named_type ("&CStr", type_record, adt->get_ident ().locus);
+
+      return;
+    }
 
   tree base_compiled_type
     = TyTyResolveCompile::compile (ctx, type.get_base (), trait_object_mode);
diff --git a/gcc/rust/typecheck/rust-hir-type-check-base.cc 
b/gcc/rust/typecheck/rust-hir-type-check-base.cc
index 9cded2dbc..ade16808e 100644
--- a/gcc/rust/typecheck/rust-hir-type-check-base.cc
+++ b/gcc/rust/typecheck/rust-hir-type-check-base.cc
@@ -408,24 +408,28 @@ TypeCheckBase::resolve_literal (const 
Analysis::NodeMapping &expr_mappings,
            break;
          }
 
-       /* This is a pointer to a null-terminated byte slice (&[u8]). */
-       TyTy::BaseType *u8;
-       auto ok = context->lookup_builtin ("u8", &u8);
-       rust_assert (ok);
+       auto lang_item_defined
+         = mappings.lookup_lang_item (LangItem::Kind::CSTR);
 
-       auto crate_num = mappings.get_current_crate ();
-       Analysis::NodeMapping slice_mapping (crate_num, UNKNOWN_NODEID,
-                                            mappings.get_next_hir_id (
-                                              crate_num),
-                                            UNKNOWN_LOCAL_DEFID);
+       if (!lang_item_defined)
+         {
+           rust_error_at (locus, "unable to find lang item: %<c_str%>");
+           infered = new TyTy::ErrorType (expr_mappings.get_hirid (), locus);
+           break;
+         }
 
-       TyTy::SliceType *slice
-         = new TyTy::SliceType (slice_mapping.get_hirid (), locus,
-                                TyTy::TyVar (u8->get_ref ()));
-       context->insert_type (slice_mapping, slice);
+       DefId cstr_defid = lang_item_defined.value ();
+       HIR::Item *item = mappings.lookup_defid (cstr_defid).value ();
+
+       TyTy::BaseType *item_type = nullptr;
+       bool ok = context->lookup_type (item->get_mappings ().get_hirid (),
+                                       &item_type);
+
+       rust_assert (ok);
+       rust_assert (item_type->get_kind () == TyTy::TypeKind::ADT);
 
        infered = new TyTy::ReferenceType (expr_mappings.get_hirid (),
-                                          TyTy::TyVar (slice->get_ref ()),
+                                          TyTy::TyVar (item_type->get_ref ()),
                                           Mutability::Imm,
                                           TyTy::Region::make_static ());
       }
diff --git a/gcc/rust/typecheck/rust-tyty.cc b/gcc/rust/typecheck/rust-tyty.cc
index 144cca0e9..9ad09a323 100644
--- a/gcc/rust/typecheck/rust-tyty.cc
+++ b/gcc/rust/typecheck/rust-tyty.cc
@@ -3060,7 +3060,8 @@ ReferenceType::get_region () const
 bool
 ReferenceType::is_dyn_object () const
 {
-  return is_dyn_slice_type () || is_dyn_str_type () || is_dyn_obj_type ();
+  return is_dyn_slice_type () || is_dyn_str_type () || is_dyn_obj_type ()
+        || is_dyn_cstr_type ();
 }
 
 static const TyTy::BaseType *
@@ -3122,6 +3123,27 @@ ReferenceType::is_dyn_obj_type (const 
TyTy::DynamicObjectType **dyn) const
   return true;
 }
 
+bool
+ReferenceType::is_dyn_cstr_type (const TyTy::ADTType **adt) const
+{
+  if (get_base ()->get_kind () != TyTy::TypeKind::ADT)
+    return false;
+
+  const TyTy::ADTType *adt_ty
+    = static_cast<const TyTy::ADTType *> (get_base ());
+  auto &mappings = Analysis::Mappings::get ();
+  auto cstr_item = mappings.lookup_lang_item (LangItem::Kind::CSTR);
+
+  if (!cstr_item.has_value ())
+    return false;
+
+  if (cstr_item.value () != adt_ty->get_id ())
+    return false;
+
+  *adt = adt_ty;
+  return true;
+}
+
 void
 ReferenceType::accept_vis (TyVisitor &vis)
 {
diff --git a/gcc/rust/typecheck/rust-tyty.h b/gcc/rust/typecheck/rust-tyty.h
index 1079314a4..b27de7422 100644
--- a/gcc/rust/typecheck/rust-tyty.h
+++ b/gcc/rust/typecheck/rust-tyty.h
@@ -1691,6 +1691,7 @@ public:
   bool is_dyn_slice_type (const TyTy::SliceType **slice = nullptr) const;
   bool is_dyn_str_type (const TyTy::StrType **str = nullptr) const;
   bool is_dyn_obj_type (const TyTy::DynamicObjectType **dyn = nullptr) const;
+  bool is_dyn_cstr_type (const TyTy::ADTType **adt = nullptr) const;
 
 private:
   TyVar base;
diff --git a/gcc/rust/util/rust-lang-item.cc b/gcc/rust/util/rust-lang-item.cc
index 21e2c81e9..2f7704546 100644
--- a/gcc/rust/util/rust-lang-item.cc
+++ b/gcc/rust/util/rust-lang-item.cc
@@ -92,6 +92,9 @@ const BiMap<std::string, LangItem::Kind> 
Rust::LangItem::lang_items = {{
   {"slice_u8", Kind::SLICE_U8},
   {"slice", Kind::SLICE},
   {"str", Kind::STR},
+  // NOTE: CStr is not present in Rust 1.49, and is only backported for
+  // compilation of Rust for Linux with a patched core lib.
+  {"CStr", Kind::CSTR},
   {"f32_runtime", Kind::F32_RUNTIME},
   {"f64_runtime", Kind::F64_RUNTIME},
 
diff --git a/gcc/rust/util/rust-lang-item.h b/gcc/rust/util/rust-lang-item.h
index 5a765282b..75f4090b9 100644
--- a/gcc/rust/util/rust-lang-item.h
+++ b/gcc/rust/util/rust-lang-item.h
@@ -125,6 +125,9 @@ public:
     SLICE_U8,
     SLICE,
     STR,
+    // NOTE: CStr is not present in Rust 1.49, and is only backported for
+    // compilation of Rust for Linux with a patched core lib.
+    CSTR,
     F32_RUNTIME,
     F64_RUNTIME,
 
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 ac464bb28..040ba9ad4 100644
--- a/gcc/testsuite/rust/compile/c_string_null_byte_check.rs
+++ b/gcc/testsuite/rust/compile/c_string_null_byte_check.rs
@@ -1,7 +1,20 @@
 // { dg-additional-options "-frust-c-style-string-literals" }
-#![feature(no_core)]
+#![feature(no_core, lang_items)]
 #![no_core]
 
+type c_char = u8;
+
+#[lang = "CStr"]
+pub struct CStr {
+    inner: [c_char]
+}
+
+impl CStr {
+    pub const fn to_ptr(&self) -> *const c_char {
+        &self.inner as *const [c_char] as *const c_char
+    }
+}
+
 pub fn main() {
     let _fail = c"gc\0crs";
     // { dg-error "null characters in C string literals are not supported" "" 
{ target *-*-* } .-1 }
diff --git a/gcc/testsuite/rust/execute/torture/c_string.rs 
b/gcc/testsuite/rust/execute/torture/c_string.rs
index af96d0591..5aef4db0c 100644
--- a/gcc/testsuite/rust/execute/torture/c_string.rs
+++ b/gcc/testsuite/rust/execute/torture/c_string.rs
@@ -1,15 +1,29 @@
 // { dg-additional-options "-frust-c-style-string-literals" }
-// { dg-output "gccrs\n" }
-#![feature(no_core)]
+// { dg-output "gccrs" }
+#![feature(no_core, lang_items)]
 #![no_core]
 
 extern "C" {
-    fn printf(s: *const i8, ...);
+    fn printf(s: *const u8, ...);
 }
 
-pub fn main() {
+type c_char = u8;
+
+#[lang = "CStr"]
+pub struct CStr {
+    inner: [c_char]
+}
+
+impl CStr {
+    pub const fn to_ptr(&self) -> *const c_char {
+        &self.inner as *const [c_char] as *const c_char
+    }
+}
+
+fn main() -> i32 {
     let a = c"gccrs";
     unsafe {
-        printf(a as *const [u8] as *const i8); // TODO change `as *const [u8]` 
to `.as_ptr()` when C strings are compiled to their own CStr type
+        printf(a.to_ptr());
     }
+    0
 }
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
new file mode 100644
index 000000000..26e0fed21
--- /dev/null
+++ b/gcc/testsuite/rust/execute/torture/c_string_ensure_null_term.rs
@@ -0,0 +1,50 @@
+// { dg-additional-options "-frust-c-style-string-literals" }
+#![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
+    }
+}
+
+extern "C" {
+    fn printf(s: *const u8, ...);
+}
+
+type c_char = u8;
+
+#[lang = "CStr"]
+pub struct CStr {
+    inner: [c_char]
+}
+
+impl CStr {
+    pub const fn to_ptr(&self) -> *const c_char {
+        &self.inner as *const [c_char] as *const c_char
+    }
+}
+
+pub fn main() -> u8 {
+    let a = c"gccrs";
+    let val = unsafe { a.to_ptr().add(5) };
+    unsafe { *val }
+}
-- 
2.54.0

Reply via email to