From 62e8318db2541ccea4e8eff94f9aaf79e51724a7 Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Tue, 7 Jul 2026 14:09:33 +0100 Subject: [PATCH] Fix implicit cast of string literals --- cpp2rust/converter/converter.cpp | 3 +- cpp2rust/converter/converter_lib.cpp | 6 + cpp2rust/converter/converter_lib.h | 2 + .../converter/models/converter_refcount.cpp | 22 ++- .../out/refcount/string_literal_ptr_init.rs | 173 ++++++++++++++++++ .../out/unsafe/string_literal_ptr_init.rs | 91 +++++++++ tests/unit/string_literal_ptr_init.c | 41 +++++ 7 files changed, 329 insertions(+), 9 deletions(-) create mode 100644 tests/unit/out/refcount/string_literal_ptr_init.rs create mode 100644 tests/unit/out/unsafe/string_literal_ptr_init.rs create mode 100644 tests/unit/string_literal_ptr_init.c diff --git a/cpp2rust/converter/converter.cpp b/cpp2rust/converter/converter.cpp index 8cb26e93..267e25ba 100644 --- a/cpp2rust/converter/converter.cpp +++ b/cpp2rust/converter/converter.cpp @@ -2084,8 +2084,7 @@ bool Converter::VisitImplicitCastExpr(clang::ImplicitCastExpr *expr) { bool dest_pointee_const = expr->getType()->getPointeeType().isConstQualified(); Convert(sub_expr); - if (clang::isa(sub_expr) || - clang::isa(sub_expr)) { + if (IsStringLiteralExpr(sub_expr)) { StrCat(".as_ptr()"); if (!dest_pointee_const) { StrCat(".cast_mut()"); diff --git a/cpp2rust/converter/converter_lib.cpp b/cpp2rust/converter/converter_lib.cpp index 1927e591..22b6f072 100644 --- a/cpp2rust/converter/converter_lib.cpp +++ b/cpp2rust/converter/converter_lib.cpp @@ -140,6 +140,12 @@ bool IsUnionArrayMember(const clang::Expr *base) { return false; } +bool IsStringLiteralExpr(const clang::Expr *expr) { + const auto *stripped = expr->IgnoreParens()->IgnoreImplicit(); + return clang::isa(stripped) || + clang::isa(stripped); +} + bool IsUserDefinedDecl(const clang::Decl *decl) { const auto &ctx = decl->getASTContext(); const auto &src_mgr = ctx.getSourceManager(); diff --git a/cpp2rust/converter/converter_lib.h b/cpp2rust/converter/converter_lib.h index 291873cc..75009288 100644 --- a/cpp2rust/converter/converter_lib.h +++ b/cpp2rust/converter/converter_lib.h @@ -42,6 +42,8 @@ bool IsInMainFile(const clang::Decl *decl); bool IsUnionArrayMember(const clang::Expr *base); +bool IsStringLiteralExpr(const clang::Expr *expr); + bool IsUserDefinedDecl(const clang::Decl *decl); bool RefersToUserDefinedDecl(const clang::Expr *expr); diff --git a/cpp2rust/converter/models/converter_refcount.cpp b/cpp2rust/converter/models/converter_refcount.cpp index 483c7fdb..d8b93087 100644 --- a/cpp2rust/converter/models/converter_refcount.cpp +++ b/cpp2rust/converter/models/converter_refcount.cpp @@ -1170,6 +1170,8 @@ bool ConverterRefCount::VisitImplicitCastExpr(clang::ImplicitCastExpr *expr) { ->getPointeeType() ->getAsArrayTypeUnsafe() ->getElementType()))); + } else if (IsStringLiteralExpr(sub_expr)) { + StrCat(std::format("{}.to_any()", ConvertFreshPointer(sub_expr))); } else { StrCat(std::format("({} as {}).to_any()", ConvertFreshPointer(sub_expr), ToString(sub_expr->getType()))); @@ -1209,9 +1211,9 @@ bool ConverterRefCount::VisitImplicitCastExpr(clang::ImplicitCastExpr *expr) { Convert(sub_expr); return false; } - if (clang::isa(sub_expr) || - clang::isa(sub_expr)) { - StrCat(std::format("Ptr::from_string_literal({})", ToString(sub_expr))); + if (IsStringLiteralExpr(sub_expr)) { + StrCat(std::format("Ptr::from_string_literal({})", + ToString(sub_expr->IgnoreParens()))); return false; } else { // we need to write (var.as_pointer as Ptr) because Rust isn't @@ -2222,10 +2224,16 @@ void ConverterRefCount::ConvertArraySubscript(clang::Expr *base, { PushParen paren(*this, is_inner_boxed); - StrCat(std::format("({} as {}).offset({})", - ToString(base->IgnoreImplicit()), - ConvertPtrType(base->IgnoreImplicit()->getType()), - ConvertRValue(idx))); + if (IsStringLiteralExpr(base)) { + StrCat(std::format("Ptr::from_string_literal({}).offset({})", + ToString(base->IgnoreParens()->IgnoreImplicit()), + ConvertRValue(idx))); + } else { + StrCat(std::format("({} as {}).offset({})", + ToString(base->IgnoreImplicit()), + ConvertPtrType(base->IgnoreImplicit()->getType()), + ConvertRValue(idx))); + } if (is_inner_boxed) { StrCat(GetPointerDerefSuffix(type), ".as_pointer()"); diff --git a/tests/unit/out/refcount/string_literal_ptr_init.rs b/tests/unit/out/refcount/string_literal_ptr_init.rs new file mode 100644 index 00000000..730980fe --- /dev/null +++ b/tests/unit/out/refcount/string_literal_ptr_init.rs @@ -0,0 +1,173 @@ +extern crate libcc2rs; +use libcc2rs::*; +use std::cell::RefCell; +use std::collections::BTreeMap; +use std::io::prelude::*; +use std::io::{Read, Seek, Write}; +use std::os::fd::AsFd; +use std::rc::{Rc, Weak}; +#[derive()] +pub struct label { + pub name: Value>, + pub probe: Value i32>>, + pub mask: Value, +} +impl Clone for label { + fn clone(&self) -> Self { + Self { + name: Rc::new(RefCell::new((*self.name.borrow()).clone())), + probe: Rc::new(RefCell::new((*self.probe.borrow()).clone())), + mask: Rc::new(RefCell::new((*self.mask.borrow()).clone())), + } + } +} +impl Default for label { + fn default() -> Self { + label { + name: Rc::new(RefCell::new(Ptr::::null())), + probe: Rc::new(RefCell::new(FnPtr::null())), + mask: >::default(), + } + } +} +impl ByteRepr for label { + fn byte_size() -> usize { + 24 + } + fn to_bytes(&self, buf: &mut [u8]) { + (*self.name.borrow()).to_bytes(&mut buf[0..8]); + (*self.probe.borrow()).to_bytes(&mut buf[8..16]); + (*self.mask.borrow()).to_bytes(&mut buf[16..20]); + } + fn from_bytes(buf: &[u8]) -> Self { + Self { + name: Rc::new(RefCell::new(>::from_bytes(&buf[0..8]))), + probe: Rc::new(RefCell::new( i32>>::from_bytes(&buf[8..16]))), + mask: Rc::new(RefCell::new(::from_bytes(&buf[16..20]))), + } + } +} +pub fn probe_two_0() -> i32 { + return 1; +} +thread_local!( + pub static table_1: Value> = Rc::new(RefCell::new(Box::new([ + label { + name: Rc::new(RefCell::new(Ptr::from_string_literal(b"first"))), + probe: Rc::new(RefCell::new(FnPtr::null())), + mask: Rc::new(RefCell::new((1 << 4))), + }, + label { + name: Rc::new(RefCell::new(Ptr::from_string_literal(b"second"))), + probe: Rc::new(RefCell::new((FnPtr:: i32>::new(probe_two_0)))), + mask: Rc::new(RefCell::new((1 << 5))), + }, + ]))); +); +pub fn main() { + std::process::exit(main_0()); +} +fn main_0() -> i32 { + assert!( + ((((((*(*table_1.with(Value::clone).borrow())[(0) as usize] + .name + .borrow()) + .offset((0) as isize) + .read()) as i32) + == ('f' as i32)) as i32) + != 0) + ); + assert!( + ((((((*(*table_1.with(Value::clone).borrow())[(0) as usize] + .name + .borrow()) + .offset((4) as isize) + .read()) as i32) + == ('t' as i32)) as i32) + != 0) + ); + assert!( + ((((*(*table_1.with(Value::clone).borrow())[(0) as usize] + .probe + .borrow()) + .is_null()) as i32) + != 0) + ); + assert!( + ((((*(*table_1.with(Value::clone).borrow())[(0) as usize] + .mask + .borrow()) + == 16) as i32) + != 0) + ); + assert!( + ((((((*(*table_1.with(Value::clone).borrow())[(1) as usize] + .name + .borrow()) + .offset((0) as isize) + .read()) as i32) + == ('s' as i32)) as i32) + != 0) + ); + assert!( + (((({ + (*(*(*table_1.with(Value::clone).borrow())[(1) as usize] + .probe + .borrow()))() + }) == 1) as i32) + != 0) + ); + assert!( + ((((*(*table_1.with(Value::clone).borrow())[(1) as usize] + .mask + .borrow()) + == 32) as i32) + != 0) + ); + let tail: Value> = + Rc::new(RefCell::new((Ptr::from_string_literal(b"ab.cd").offset(2)))); + assert!( + ((((((*tail.borrow()).offset((0) as isize).read()) as i32) == ('.' as i32)) as i32) != 0) + ); + assert!( + ((((((*tail.borrow()).offset((1) as isize).read()) as i32) == ('c' as i32)) as i32) != 0) + ); + assert!( + ((((((*tail.borrow()).offset((2) as isize).read()) as i32) == ('d' as i32)) as i32) != 0) + ); + let have: Value = Rc::new(RefCell::new(0)); + let p: Value = Rc::new(RefCell::new(if ((*have.borrow()) != 0) { + (*(*table_1.with(Value::clone).borrow())[(0) as usize] + .name + .borrow()) + .clone() + .to_any() + } else { + Ptr::from_string_literal(b"").to_any() + })); + assert!( + (((((((*p.borrow()).reinterpret_cast::()) + .offset((0) as isize) + .read()) as i32) + == ('\0' as i32)) as i32) + != 0) + ); + (*have.borrow_mut()) = 1; + (*p.borrow_mut()) = if ((*have.borrow()) != 0) { + (*(*table_1.with(Value::clone).borrow())[(0) as usize] + .name + .borrow()) + .clone() + .to_any() + } else { + Ptr::from_string_literal(b"").to_any() + }; + assert!( + (((((((*p.borrow()).reinterpret_cast::()) + .offset((0) as isize) + .read()) as i32) + == ('f' as i32)) as i32) + != 0) + ); + return 0; +} diff --git a/tests/unit/out/unsafe/string_literal_ptr_init.rs b/tests/unit/out/unsafe/string_literal_ptr_init.rs new file mode 100644 index 00000000..c6395ccf --- /dev/null +++ b/tests/unit/out/unsafe/string_literal_ptr_init.rs @@ -0,0 +1,91 @@ +extern crate libc; +use libc::*; +extern crate libcc2rs; +use libcc2rs::*; +use std::collections::BTreeMap; +use std::io::{Read, Seek, Write}; +use std::os::fd::{AsFd, FromRawFd, IntoRawFd}; +use std::rc::Rc; +#[repr(C)] +#[derive(Copy, Clone)] +pub struct label { + pub name: *const libc::c_char, + pub probe: Option i32>, + pub mask: i32, +} +impl Default for label { + fn default() -> Self { + label { + name: std::ptr::null(), + probe: None, + mask: 0_i32, + } + } +} +pub unsafe fn probe_two_0() -> i32 { + return 1; +} +pub static mut table_1: [label; 2] = unsafe { + [ + label { + name: ((c"first").as_ptr().cast_mut()).cast_const(), + probe: None, + mask: ((1) << (4)), + }, + label { + name: ((c"second").as_ptr().cast_mut()).cast_const(), + probe: (Some(probe_two_0)), + mask: ((1) << (5)), + }, + ] +}; +pub fn main() { + unsafe { + std::process::exit(main_0() as i32); + } +} +unsafe fn main_0() -> i32 { + assert!( + (((((*table_1[(0) as usize].name.offset((0) as isize)) as i32) == ('f' as i32)) as i32) + != 0) + ); + assert!( + (((((*table_1[(0) as usize].name.offset((4) as isize)) as i32) == ('t' as i32)) as i32) + != 0) + ); + assert!(((((table_1[(0) as usize].probe).is_none()) as i32) != 0)); + assert!(((((table_1[(0) as usize].mask) == (16)) as i32) != 0)); + assert!( + (((((*table_1[(1) as usize].name.offset((0) as isize)) as i32) == ('s' as i32)) as i32) + != 0) + ); + assert!(((((unsafe { (table_1[(1) as usize].probe).unwrap()() }) == (1)) as i32) != 0)); + assert!(((((table_1[(1) as usize].mask) == (32)) as i32) != 0)); + let mut tail: *const libc::c_char = (&mut (*c"ab.cd".as_ptr().cast_mut().offset((2) as isize)) + as *mut libc::c_char) + .cast_const(); + assert!((((((*tail.offset((0) as isize)) as i32) == ('.' as i32)) as i32) != 0)); + assert!((((((*tail.offset((1) as isize)) as i32) == ('c' as i32)) as i32) != 0)); + assert!((((((*tail.offset((2) as isize)) as i32) == ('d' as i32)) as i32) != 0)); + let mut have: i32 = 0; + let mut p: *mut ::libc::c_void = if (have != 0) { + (table_1[(0) as usize].name as *mut ::libc::c_void) + } else { + (c"".as_ptr().cast_mut() as *mut libc::c_char as *mut ::libc::c_void) + }; + assert!( + (((((*(p as *const libc::c_char).offset((0) as isize)) as i32) == ('\0' as i32)) as i32) + != 0) + ); + have = 1; + p = if (have != 0) { + (table_1[(0) as usize].name as *mut ::libc::c_void) + } else { + (c"".as_ptr().cast_mut() as *mut libc::c_char as *mut ::libc::c_void) + }; + assert!( + (((((*(p as *const libc::c_char).offset((0) as isize)) as i32) == ('f' as i32)) as i32) + != 0) + ); + return 0; +} diff --git a/tests/unit/string_literal_ptr_init.c b/tests/unit/string_literal_ptr_init.c new file mode 100644 index 00000000..a6091b1d --- /dev/null +++ b/tests/unit/string_literal_ptr_init.c @@ -0,0 +1,41 @@ +#include + +#define END_MARK "ab.cd" + +struct label { + const char *name; + int (*probe)(void); + int mask; +}; + +static int probe_two(void) { return 1; } + +#define ENTRY(name, probe, mask) {(name), (probe), (mask)} + +static const struct label table[] = { + ENTRY("first", 0, 1 << 4), + ENTRY("second", probe_two, 1 << 5), +}; + +int main() { + assert(table[0].name[0] == 'f'); + assert(table[0].name[4] == 't'); + assert(table[0].probe == 0); + assert(table[0].mask == 16); + assert(table[1].name[0] == 's'); + assert(table[1].probe() == 1); + assert(table[1].mask == 32); + + const char *tail = &END_MARK[2]; + assert(tail[0] == '.'); + assert(tail[1] == 'c'); + assert(tail[2] == 'd'); + + int have = 0; + void *p = have ? (void *)table[0].name : ""; + assert(((const char *)p)[0] == '\0'); + have = 1; + p = have ? (void *)table[0].name : ""; + assert(((const char *)p)[0] == 'f'); + return 0; +}