[gcc r17-2240] gccrs: Introduce CStr language item

Arthur Cohen via Gcc-cvs <[email protected]>
Newsgroups gmane.comp.gcc.cvs
Message-ID <[email protected]>
https://gcc.gnu.org/g:ecb843c77de84add5fa315ff06ea99494aa27374

commit r17-2240-gecb843c77de84add5fa315ff06ea99494aa27374
Author: Yap Zhi Heng <[email protected]>
Date:   Sun Jun 7 22:42:53 2026 +0800

    gccrs: Introduce CStr language item
    
    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]>

Diff:
---
 gcc/rust/backend/rust-compile-type.cc              | 23 ++++++++++
 gcc/rust/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 ++++++-
 gcc/testsuite/rust/execute/torture/c_string.rs     | 24 ++++++++---
 .../execute/torture/c_string_ensure_null_term.rs   | 50 ++++++++++++++++++++++
 9 files changed, 154 insertions(+), 21 deletions(-)

diff --git a/gcc/rust/backend/rust-compile-type.cc b/gcc/rust/backend/rust-compile-type.cc
index 35d56cc47908..da6a8ee4a000 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 9cded2dbca95..ade16808e2e7 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 144cca0e9383..9ad09a323d06 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 1079314a429a..b27de7422c81 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 21e2c81e9e69..2f7704546575 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 5a765282b8cd..75f4090b97de 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 ac464bb2828c..040ba9ad468f 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 af96d05915a0..5aef4db0c21a 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 000000000000..26e0fed2173c
--- /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 }
+}
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.