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 } +}
