From 633db6feae0ce979d318bf5a371310f68e7f1fc8 Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Tue, 19 May 2026 10:26:01 +0100 Subject: [PATCH 1/5] Don't map user-defined expressions --- cpp2rust/compat/platform_flags.h | 2 +- cpp2rust/converter/converter.cpp | 2 +- cpp2rust/converter/converter_lib.cpp | 17 ++++++++++++++++- cpp2rust/converter/converter_lib.h | 4 +++- cpp2rust/converter/mapper.cpp | 6 +++++- 5 files changed, 26 insertions(+), 5 deletions(-) diff --git a/cpp2rust/compat/platform_flags.h b/cpp2rust/compat/platform_flags.h index 09ceb590..0f563870 100644 --- a/cpp2rust/compat/platform_flags.h +++ b/cpp2rust/compat/platform_flags.h @@ -9,7 +9,7 @@ static inline std::vector getPlatformClangBeginFlags() { std::vector flags = { "-resource-dir=" CLANG_RESOURCE_DIR, - "-I" COMPAT_INCLUDE_DIR, + "-isystem" COMPAT_INCLUDE_DIR, "-D_FORTIFY_SOURCE=0", }; #ifdef MACOS_SDK_PATH diff --git a/cpp2rust/converter/converter.cpp b/cpp2rust/converter/converter.cpp index 703343d6..cab732b0 100644 --- a/cpp2rust/converter/converter.cpp +++ b/cpp2rust/converter/converter.cpp @@ -276,7 +276,7 @@ bool Converter::Convert(clang::Decl *decl) { return TraverseDecl(decl); } bool Converter::VisitTranslationUnitDecl(clang::TranslationUnitDecl *decl) { for (auto *child : decl->decls()) { - if (IsConvertibleDecl(child) && + if (IsUserDefinedDecl(child) && (IsInMainFile(child) || !decl_ids_.contains(GetID(child)))) { Convert(child); } diff --git a/cpp2rust/converter/converter_lib.cpp b/cpp2rust/converter/converter_lib.cpp index 4f60cf64..051aed2d 100644 --- a/cpp2rust/converter/converter_lib.cpp +++ b/cpp2rust/converter/converter_lib.cpp @@ -129,7 +129,7 @@ bool IsInMainFile(const clang::Decl *decl) { return src_mgr.isInMainFile(src_mgr.getExpansionLoc(loc)); } -bool IsConvertibleDecl(const clang::Decl *decl) { +bool IsUserDefinedDecl(const clang::Decl *decl) { const auto &ctx = decl->getASTContext(); const auto &src_mgr = ctx.getSourceManager(); const auto src_loc = decl->getLocation(); @@ -138,6 +138,21 @@ bool IsConvertibleDecl(const clang::Decl *decl) { !src_mgr.isInSystemMacro(src_loc); } +bool RefersToUserDefinedDecl(const clang::Expr *expr) { + expr = expr->IgnoreParenImpCasts(); + const clang::Decl *decl = nullptr; + if (const auto *call = llvm::dyn_cast(expr)) { + decl = call->getDirectCallee(); + } else if (const auto *ref = llvm::dyn_cast(expr)) { + decl = ref->getDecl(); + } else if (const auto *member = llvm::dyn_cast(expr)) { + decl = member->getMemberDecl(); + } else if (const auto *ctor = llvm::dyn_cast(expr)) { + decl = ctor->getConstructor(); + } + return decl && IsUserDefinedDecl(decl); +} + bool IsUnsignedArithOp(const clang::BinaryOperator *expr) { clang::QualType lhs_type; clang::QualType rhs_type; diff --git a/cpp2rust/converter/converter_lib.h b/cpp2rust/converter/converter_lib.h index aa5d0c2b..c208ea1c 100644 --- a/cpp2rust/converter/converter_lib.h +++ b/cpp2rust/converter/converter_lib.h @@ -39,7 +39,9 @@ bool IsComparisonWithNullOp(const clang::BinaryOperator *expr); bool IsInMainFile(const clang::Decl *decl); -bool IsConvertibleDecl(const clang::Decl *decl); +bool IsUserDefinedDecl(const clang::Decl *decl); + +bool RefersToUserDefinedDecl(const clang::Expr *expr); bool IsUnsignedArithOp(const clang::BinaryOperator *expr); diff --git a/cpp2rust/converter/mapper.cpp b/cpp2rust/converter/mapper.cpp index e2b444f4..c86f903a 100644 --- a/cpp2rust/converter/mapper.cpp +++ b/cpp2rust/converter/mapper.cpp @@ -385,6 +385,9 @@ search(std::unordered_multimap &map, const std::string &txt, } TranslationRule::ExprRule *search(const clang::Expr *expr) { + if (RefersToUserDefinedDecl(expr)) { + return nullptr; + } auto qualified_name = ToString(expr); auto [rule, subs] = search(exprs_, qualified_name, GetExprMapKey(qualified_name)); @@ -598,7 +601,8 @@ const TranslationRule::ExprRule *GetExprRule(const clang::Expr *expr) { std::string MapFunctionName(const clang::FunctionDecl *decl) { assert(decl); - if (exprs_.contains(GetExprMapKey(ToString(decl)))) { + if (!IsUserDefinedDecl(decl) && + exprs_.contains(GetExprMapKey(ToString(decl)))) { return std::format("libcc2rs::{}_{}", decl->getNameAsString(), model_ == Model::kRefCount ? "refcount" : "unsafe"); } From 20563e5645fd251eac59d07fcbdcc71d040e0d5b Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Tue, 19 May 2026 10:40:42 +0100 Subject: [PATCH 2/5] Add user_defined_same_as_libc test --- .../out/refcount/user_defined_same_as_libc.rs | 27 +++++++++++++++++++ .../out/unsafe/user_defined_same_as_libc.rs | 26 ++++++++++++++++++ tests/unit/user_defined_same_as_libc.c | 10 +++++++ 3 files changed, 63 insertions(+) create mode 100644 tests/unit/out/refcount/user_defined_same_as_libc.rs create mode 100644 tests/unit/out/unsafe/user_defined_same_as_libc.rs create mode 100644 tests/unit/user_defined_same_as_libc.c diff --git a/tests/unit/out/refcount/user_defined_same_as_libc.rs b/tests/unit/out/refcount/user_defined_same_as_libc.rs new file mode 100644 index 00000000..e072e117 --- /dev/null +++ b/tests/unit/out/refcount/user_defined_same_as_libc.rs @@ -0,0 +1,27 @@ +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}; +pub fn strchr_0(s: Ptr, c: i32) -> Ptr { + let s: Value> = Rc::new(RefCell::new(s)); + let c: Value = Rc::new(RefCell::new(c)); + return Ptr::::null(); +} +pub fn main() { + std::process::exit(main_0()); +} +fn main_0() -> i32 { + let p: Value> = Rc::new(RefCell::new( + ({ + let _s: Ptr = Ptr::from_string_literal("hello"); + let _c: i32 = ('l' as i32); + strchr_0(_s, _c) + }), + )); + assert!(((((*p.borrow()).is_null()) as i32) != 0)); + return 0; +} diff --git a/tests/unit/out/unsafe/user_defined_same_as_libc.rs b/tests/unit/out/unsafe/user_defined_same_as_libc.rs new file mode 100644 index 00000000..8a34abfe --- /dev/null +++ b/tests/unit/out/unsafe/user_defined_same_as_libc.rs @@ -0,0 +1,26 @@ +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; +pub unsafe fn strchr_0(mut s: *const u8, mut c: i32) -> *mut u8 { + return std::ptr::null_mut(); +} +pub fn main() { + unsafe { + std::process::exit(main_0() as i32); + } +} +unsafe fn main_0() -> i32 { + let mut p: *const u8 = (unsafe { + let _s: *const u8 = b"hello\0".as_ptr().cast_mut().cast_const(); + let _c: i32 = ('l' as i32); + strchr_0(_s, _c) + }) + .cast_const(); + assert!(((((p).is_null()) as i32) != 0)); + return 0; +} diff --git a/tests/unit/user_defined_same_as_libc.c b/tests/unit/user_defined_same_as_libc.c new file mode 100644 index 00000000..ee5d7e32 --- /dev/null +++ b/tests/unit/user_defined_same_as_libc.c @@ -0,0 +1,10 @@ +#include +#include + +char *strchr(const char *s, int c) { return NULL; } + +int main() { + const char *p = strchr("hello", 'l'); + assert(p == NULL); + return 0; +} From d30493fa3d8cbbc8e4b3dc6c458e61d127a86de7 Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Tue, 19 May 2026 14:35:54 +0100 Subject: [PATCH 3/5] Replace strchr with a function that the compiler won't replace with a builitn --- .../out/refcount/user_defined_same_as_libc.rs | 20 ++++++++++--------- .../out/unsafe/user_defined_same_as_libc.rs | 17 ++++++++-------- tests/unit/user_defined_same_as_libc.c | 12 +++++++---- 3 files changed, 28 insertions(+), 21 deletions(-) diff --git a/tests/unit/out/refcount/user_defined_same_as_libc.rs b/tests/unit/out/refcount/user_defined_same_as_libc.rs index e072e117..8e294032 100644 --- a/tests/unit/out/refcount/user_defined_same_as_libc.rs +++ b/tests/unit/out/refcount/user_defined_same_as_libc.rs @@ -6,22 +6,24 @@ use std::io::prelude::*; use std::io::{Read, Seek, Write}; use std::os::fd::AsFd; use std::rc::{Rc, Weak}; -pub fn strchr_0(s: Ptr, c: i32) -> Ptr { - let s: Value> = Rc::new(RefCell::new(s)); - let c: Value = Rc::new(RefCell::new(c)); - return Ptr::::null(); +pub fn fopen_0(path: Ptr, mode: Ptr) -> Ptr<::std::fs::File> { + let path: Value> = Rc::new(RefCell::new(path)); + let mode: Value> = Rc::new(RefCell::new(mode)); + (*path.borrow()).clone(); + (*mode.borrow()).clone(); + return Ptr::null(); } pub fn main() { std::process::exit(main_0()); } fn main_0() -> i32 { - let p: Value> = Rc::new(RefCell::new( + let fp: Value> = Rc::new(RefCell::new( ({ - let _s: Ptr = Ptr::from_string_literal("hello"); - let _c: i32 = ('l' as i32); - strchr_0(_s, _c) + let _path: Ptr = Ptr::from_string_literal("/etc/passwd"); + let _mode: Ptr = Ptr::from_string_literal("r"); + fopen_0(_path, _mode) }), )); - assert!(((((*p.borrow()).is_null()) as i32) != 0)); + assert!(((((*fp.borrow()).is_null()) as i32) != 0)); return 0; } diff --git a/tests/unit/out/unsafe/user_defined_same_as_libc.rs b/tests/unit/out/unsafe/user_defined_same_as_libc.rs index 8a34abfe..0fbe1a05 100644 --- a/tests/unit/out/unsafe/user_defined_same_as_libc.rs +++ b/tests/unit/out/unsafe/user_defined_same_as_libc.rs @@ -6,7 +6,9 @@ use std::collections::BTreeMap; use std::io::{Read, Seek, Write}; use std::os::fd::{AsFd, FromRawFd, IntoRawFd}; use std::rc::Rc; -pub unsafe fn strchr_0(mut s: *const u8, mut c: i32) -> *mut u8 { +pub unsafe fn fopen_0(mut path: *const u8, mut mode: *const u8) -> *mut ::std::fs::File { + &(path); + &(mode); return std::ptr::null_mut(); } pub fn main() { @@ -15,12 +17,11 @@ pub fn main() { } } unsafe fn main_0() -> i32 { - let mut p: *const u8 = (unsafe { - let _s: *const u8 = b"hello\0".as_ptr().cast_mut().cast_const(); - let _c: i32 = ('l' as i32); - strchr_0(_s, _c) - }) - .cast_const(); - assert!(((((p).is_null()) as i32) != 0)); + let mut fp: *mut ::std::fs::File = (unsafe { + let _path: *const u8 = b"/etc/passwd\0".as_ptr().cast_mut().cast_const(); + let _mode: *const u8 = b"r\0".as_ptr().cast_mut().cast_const(); + fopen_0(_path, _mode) + }); + assert!(((((fp).is_null()) as i32) != 0)); return 0; } diff --git a/tests/unit/user_defined_same_as_libc.c b/tests/unit/user_defined_same_as_libc.c index ee5d7e32..82f081a4 100644 --- a/tests/unit/user_defined_same_as_libc.c +++ b/tests/unit/user_defined_same_as_libc.c @@ -1,10 +1,14 @@ #include -#include +#include -char *strchr(const char *s, int c) { return NULL; } +FILE *fopen(const char *path, const char *mode) { + (void)path; + (void)mode; + return NULL; +} int main() { - const char *p = strchr("hello", 'l'); - assert(p == NULL); + FILE *fp = fopen("/etc/passwd", "r"); + assert(fp == NULL); return 0; } From 867d8a8555d86739b2c488afd8c4fbc3ff20f628 Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Tue, 19 May 2026 14:43:34 +0100 Subject: [PATCH 4/5] Update tests --- tests/unit/out/unsafe/user_defined_same_as_libc.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/unit/out/unsafe/user_defined_same_as_libc.rs b/tests/unit/out/unsafe/user_defined_same_as_libc.rs index 0fbe1a05..2e1a7366 100644 --- a/tests/unit/out/unsafe/user_defined_same_as_libc.rs +++ b/tests/unit/out/unsafe/user_defined_same_as_libc.rs @@ -18,8 +18,8 @@ pub fn main() { } unsafe fn main_0() -> i32 { let mut fp: *mut ::std::fs::File = (unsafe { - let _path: *const u8 = b"/etc/passwd\0".as_ptr().cast_mut().cast_const(); - let _mode: *const u8 = b"r\0".as_ptr().cast_mut().cast_const(); + let _path: *const u8 = (b"/etc/passwd\0".as_ptr().cast_mut()).cast_const(); + let _mode: *const u8 = (b"r\0".as_ptr().cast_mut()).cast_const(); fopen_0(_path, _mode) }); assert!(((((fp).is_null()) as i32) != 0)); From d13a1fe77f039be8a7f63de41ff1c5538106272b Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Tue, 19 May 2026 16:04:50 +0100 Subject: [PATCH 5/5] Don't read passwd in test --- tests/unit/out/refcount/user_defined_same_as_libc.rs | 2 +- tests/unit/out/unsafe/user_defined_same_as_libc.rs | 2 +- tests/unit/user_defined_same_as_libc.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/unit/out/refcount/user_defined_same_as_libc.rs b/tests/unit/out/refcount/user_defined_same_as_libc.rs index 8e294032..599f7bb3 100644 --- a/tests/unit/out/refcount/user_defined_same_as_libc.rs +++ b/tests/unit/out/refcount/user_defined_same_as_libc.rs @@ -19,7 +19,7 @@ pub fn main() { fn main_0() -> i32 { let fp: Value> = Rc::new(RefCell::new( ({ - let _path: Ptr = Ptr::from_string_literal("/etc/passwd"); + let _path: Ptr = Ptr::from_string_literal("/tmp/irrelevant-file"); let _mode: Ptr = Ptr::from_string_literal("r"); fopen_0(_path, _mode) }), diff --git a/tests/unit/out/unsafe/user_defined_same_as_libc.rs b/tests/unit/out/unsafe/user_defined_same_as_libc.rs index 2e1a7366..2b284523 100644 --- a/tests/unit/out/unsafe/user_defined_same_as_libc.rs +++ b/tests/unit/out/unsafe/user_defined_same_as_libc.rs @@ -18,7 +18,7 @@ pub fn main() { } unsafe fn main_0() -> i32 { let mut fp: *mut ::std::fs::File = (unsafe { - let _path: *const u8 = (b"/etc/passwd\0".as_ptr().cast_mut()).cast_const(); + let _path: *const u8 = (b"/tmp/irrelevant-file\0".as_ptr().cast_mut()).cast_const(); let _mode: *const u8 = (b"r\0".as_ptr().cast_mut()).cast_const(); fopen_0(_path, _mode) }); diff --git a/tests/unit/user_defined_same_as_libc.c b/tests/unit/user_defined_same_as_libc.c index 82f081a4..57863b8a 100644 --- a/tests/unit/user_defined_same_as_libc.c +++ b/tests/unit/user_defined_same_as_libc.c @@ -8,7 +8,7 @@ FILE *fopen(const char *path, const char *mode) { } int main() { - FILE *fp = fopen("/etc/passwd", "r"); + FILE *fp = fopen("/tmp/irrelevant-file", "r"); assert(fp == NULL); return 0; }