From 393ee2f27b42f62b61059ba77904a6a161cb3b44 Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Wed, 21 Oct 2026 09:51:54 +0100 Subject: [PATCH 1/7] Key rules through explicit and implicit casts --- cpp2rust/converter/rules/rules_loader.cpp | 8 +++++++- rules/chrono/src_cxx20.cpp | 10 ++++++++++ rules/chrono/tgt_unsafe.rs | 14 ++++++++++++++ rules/fs_path/src_cxx17.cpp | 13 +++++++++++++ rules/fs_path/tgt_refcount.rs | 19 +++++++++++++++++++ rules/fs_path/tgt_unsafe.rs | 20 ++++++++++++++++++++ tests/unit/chrono_day_conversion.cpp | 10 ++++++++++ tests/unit/fs_path_string_conversion.cpp | 13 +++++++++++++ 8 files changed, 106 insertions(+), 1 deletion(-) create mode 100644 rules/chrono/src_cxx20.cpp create mode 100644 rules/chrono/tgt_unsafe.rs create mode 100644 rules/fs_path/src_cxx17.cpp create mode 100644 rules/fs_path/tgt_refcount.rs create mode 100644 rules/fs_path/tgt_unsafe.rs create mode 100644 tests/unit/chrono_day_conversion.cpp create mode 100644 tests/unit/fs_path_string_conversion.cpp diff --git a/cpp2rust/converter/rules/rules_loader.cpp b/cpp2rust/converter/rules/rules_loader.cpp index d1b69f724c..a4bd45c859 100644 --- a/cpp2rust/converter/rules/rules_loader.cpp +++ b/cpp2rust/converter/rules/rules_loader.cpp @@ -473,7 +473,13 @@ std::string DeclKey(const clang::NamedDecl *decl) { } std::string ExprKey(clang::ASTContext &ctx, const clang::Expr *expr) { - expr = expr->IgnoreParenImpCasts(); + for (auto inner = expr->IgnoreParens()->IgnoreImplicit(); inner != expr; + inner = expr->IgnoreParens()->IgnoreImplicit()) { + expr = inner; + } + if (auto cast = llvm::dyn_cast(expr)) { + return ExprKey(ctx, cast->getSubExpr()); + } if (llvm::isa(expr) && expr->getBeginLoc().isMacroID()) { return clang::Lexer::getImmediateMacroName( diff --git a/rules/chrono/src_cxx20.cpp b/rules/chrono/src_cxx20.cpp new file mode 100644 index 0000000000..8a2cc49822 --- /dev/null +++ b/rules/chrono/src_cxx20.cpp @@ -0,0 +1,10 @@ +// Copyright (c) 2022-present INESC-ID. +// Distributed under the MIT license that can be found in the LICENSE file. + +#include + +using t1 = std::chrono::day; + +std::chrono::day f1(unsigned a0) { return std::chrono::day(a0); } + +unsigned f2(const std::chrono::day &a0) { return static_cast(a0); } diff --git a/rules/chrono/tgt_unsafe.rs b/rules/chrono/tgt_unsafe.rs new file mode 100644 index 0000000000..e8d446da18 --- /dev/null +++ b/rules/chrono/tgt_unsafe.rs @@ -0,0 +1,14 @@ +// Copyright (c) 2022-present INESC-ID. +// Distributed under the MIT license that can be found in the LICENSE file. + +fn t1() -> u32 { + 0_u32 +} + +unsafe fn f1(a0: u32) -> u32 { + a0 +} + +unsafe fn f2(a0: u32) -> u32 { + a0 +} diff --git a/rules/fs_path/src_cxx17.cpp b/rules/fs_path/src_cxx17.cpp new file mode 100644 index 0000000000..8d8e5b1927 --- /dev/null +++ b/rules/fs_path/src_cxx17.cpp @@ -0,0 +1,13 @@ +// Copyright (c) 2022-present INESC-ID. +// Distributed under the MIT license that can be found in the LICENSE file. + +#include +#include + +using t1 = std::filesystem::path; + +std::filesystem::path f1(const char *a0) { return std::filesystem::path(a0); } + +std::string f2(const std::filesystem::path &a0) { + return static_cast(a0); +} diff --git a/rules/fs_path/tgt_refcount.rs b/rules/fs_path/tgt_refcount.rs new file mode 100644 index 0000000000..0c0aa15ac7 --- /dev/null +++ b/rules/fs_path/tgt_refcount.rs @@ -0,0 +1,19 @@ +// Copyright (c) 2022-present INESC-ID. +// Distributed under the MIT license that can be found in the LICENSE file. + +use libcc2rs::*; + +fn t1() -> Vec { + vec![0] +} + +fn f1(a0: Ptr) -> Vec { + let mut __v = Vec::new(); + a0.with_c_str(|__s| __v.extend_from_slice(__s)); + __v.push(0); + __v +} + +fn f2(a0: Vec) -> Vec { + a0 +} diff --git a/rules/fs_path/tgt_unsafe.rs b/rules/fs_path/tgt_unsafe.rs new file mode 100644 index 0000000000..a76d55f60b --- /dev/null +++ b/rules/fs_path/tgt_unsafe.rs @@ -0,0 +1,20 @@ +// Copyright (c) 2022-present INESC-ID. +// Distributed under the MIT license that can be found in the LICENSE file. + +fn t1() -> Vec { + vec![0] +} + +unsafe fn f1(a0: *const libc::c_char) -> Vec { + let mut __v = ::std::ffi::CStr::from_ptr(a0) + .to_bytes() + .iter() + .map(|&__b| __b as libc::c_char) + .collect::>(); + __v.push(0); + __v +} + +unsafe fn f2(a0: Vec) -> Vec { + a0 +} diff --git a/tests/unit/chrono_day_conversion.cpp b/tests/unit/chrono_day_conversion.cpp new file mode 100644 index 0000000000..b84075b4a3 --- /dev/null +++ b/tests/unit/chrono_day_conversion.cpp @@ -0,0 +1,10 @@ +// ADDITIONAL_COMPILE_FLAGS: -std=c++20 +#include +#include + +int main() { + unsigned n = 17; + std::chrono::day d(n); + assert(static_cast(d) == 17); + return 0; +} diff --git a/tests/unit/fs_path_string_conversion.cpp b/tests/unit/fs_path_string_conversion.cpp new file mode 100644 index 0000000000..bed78035c3 --- /dev/null +++ b/tests/unit/fs_path_string_conversion.cpp @@ -0,0 +1,13 @@ +// ADDITIONAL_COMPILE_FLAGS: -std=c++17 +#include +#include +#include + +int main() { + const char *name = "dir/file.txt"; + std::filesystem::path p(name); + std::string s = static_cast(p); + assert(s.size() == 12); + assert(s == "dir/file.txt"); + return 0; +} From 56198bf1ff27750cda27058f8f29520ebbc9ffc8 Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Wed, 21 Oct 2026 09:57:01 +0100 Subject: [PATCH 2/7] Extend existing tests --- tests/unit/chrono_day_conversion.cpp | 10 ---------- ...ing_conversion.cpp => stl_conversion_operators.cpp} | 7 ++++++- 2 files changed, 6 insertions(+), 11 deletions(-) delete mode 100644 tests/unit/chrono_day_conversion.cpp rename tests/unit/{fs_path_string_conversion.cpp => stl_conversion_operators.cpp} (64%) diff --git a/tests/unit/chrono_day_conversion.cpp b/tests/unit/chrono_day_conversion.cpp deleted file mode 100644 index b84075b4a3..0000000000 --- a/tests/unit/chrono_day_conversion.cpp +++ /dev/null @@ -1,10 +0,0 @@ -// ADDITIONAL_COMPILE_FLAGS: -std=c++20 -#include -#include - -int main() { - unsigned n = 17; - std::chrono::day d(n); - assert(static_cast(d) == 17); - return 0; -} diff --git a/tests/unit/fs_path_string_conversion.cpp b/tests/unit/stl_conversion_operators.cpp similarity index 64% rename from tests/unit/fs_path_string_conversion.cpp rename to tests/unit/stl_conversion_operators.cpp index bed78035c3..7bb5c0a523 100644 --- a/tests/unit/fs_path_string_conversion.cpp +++ b/tests/unit/stl_conversion_operators.cpp @@ -1,9 +1,14 @@ -// ADDITIONAL_COMPILE_FLAGS: -std=c++17 +// ADDITIONAL_COMPILE_FLAGS: -std=c++20 #include +#include #include #include int main() { + unsigned n = 17; + std::chrono::day d(n); + assert(static_cast(d) == 17); + const char *name = "dir/file.txt"; std::filesystem::path p(name); std::string s = static_cast(p); From f6bfec7805371a31cc93e950e657c4b7b6670299 Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Wed, 21 Oct 2026 10:02:15 +0100 Subject: [PATCH 3/7] Use SkipImplicit in ExprKey --- cpp2rust/converter/rules/rules_loader.cpp | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/cpp2rust/converter/rules/rules_loader.cpp b/cpp2rust/converter/rules/rules_loader.cpp index a4bd45c859..f1d19a35f0 100644 --- a/cpp2rust/converter/rules/rules_loader.cpp +++ b/cpp2rust/converter/rules/rules_loader.cpp @@ -473,10 +473,7 @@ std::string DeclKey(const clang::NamedDecl *decl) { } std::string ExprKey(clang::ASTContext &ctx, const clang::Expr *expr) { - for (auto inner = expr->IgnoreParens()->IgnoreImplicit(); inner != expr; - inner = expr->IgnoreParens()->IgnoreImplicit()) { - expr = inner; - } + expr = RuleDecls::SkipImplicit(expr); if (auto cast = llvm::dyn_cast(expr)) { return ExprKey(ctx, cast->getSubExpr()); } From 29b0c71c6dcaa230a912f76a4a75aa172ababd8a Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Tue, 20 Oct 2026 20:51:42 +0100 Subject: [PATCH 4/7] Only analyse user code in address taken visitor --- cpp2rust/converter/models/boxed_vars.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/cpp2rust/converter/models/boxed_vars.cpp b/cpp2rust/converter/models/boxed_vars.cpp index d0ef168c25..e38209cfa4 100644 --- a/cpp2rust/converter/models/boxed_vars.cpp +++ b/cpp2rust/converter/models/boxed_vars.cpp @@ -4,6 +4,7 @@ #include "converter/models/boxed_vars.h" #include +#include #include #include @@ -110,6 +111,14 @@ class AddressTakenVisitor bool shouldVisitTemplateInstantiations() const { return true; } + bool TraverseDecl(clang::Decl *decl) { + if (decl && !llvm::isa(decl) && + ctx_.getSourceManager().isInSystemHeader(decl->getLocation())) { + return true; + } + return RecursiveASTVisitor::TraverseDecl(decl); + } + // Only the semantic form of an initializer list is converted, in which the // values of variables are read through lvalue-to-rvalue conversions. bool TraverseInitListExpr(clang::InitListExpr *expr, From 3f1c7fe3cdc4ffbb2999a8d22ac8947a7319ea59 Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Wed, 21 Oct 2026 10:25:11 +0100 Subject: [PATCH 5/7] Clone path in refcount conversion rule --- rules/fs_path/tgt_refcount.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rules/fs_path/tgt_refcount.rs b/rules/fs_path/tgt_refcount.rs index 0c0aa15ac7..36c5141332 100644 --- a/rules/fs_path/tgt_refcount.rs +++ b/rules/fs_path/tgt_refcount.rs @@ -15,5 +15,5 @@ fn f1(a0: Ptr) -> Vec { } fn f2(a0: Vec) -> Vec { - a0 + a0.clone() } From 7cf43028a8a0a2da277a04bb4bbbb2fda75562df Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Wed, 21 Oct 2026 10:47:56 +0100 Subject: [PATCH 6/7] Pass through no-op casts to records --- cpp2rust/converter/converter.cpp | 3 ++- rules/fs_path/tgt_refcount.rs | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/cpp2rust/converter/converter.cpp b/cpp2rust/converter/converter.cpp index 7184503565..d53648c21c 100644 --- a/cpp2rust/converter/converter.cpp +++ b/cpp2rust/converter/converter.cpp @@ -2736,7 +2736,8 @@ bool Converter::VisitExplicitCastExpr(clang::ExplicitCastExpr *expr) { case clang::Stmt::CXXReinterpretCastExprClass: case clang::Stmt::CXXStaticCastExprClass: case clang::Stmt::CStyleCastExprClass: - if (expr->getType() == sub_expr->getType()) { + if (expr->getType() == sub_expr->getType() || + (expr->getCastKind() == clang::CK_NoOp && type->isRecordType())) { return Convert(sub_expr); } if (type->isFunctionPointerType() || diff --git a/rules/fs_path/tgt_refcount.rs b/rules/fs_path/tgt_refcount.rs index 36c5141332..0c0aa15ac7 100644 --- a/rules/fs_path/tgt_refcount.rs +++ b/rules/fs_path/tgt_refcount.rs @@ -15,5 +15,5 @@ fn f1(a0: Ptr) -> Vec { } fn f2(a0: Vec) -> Vec { - a0.clone() + a0 } From 1ee44801809e094c03ca3f75068e40b52fba7345 Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Wed, 21 Oct 2026 10:51:22 +0100 Subject: [PATCH 7/7] Update tests --- .../out/refcount/stl_conversion_operators.rs | 32 +++++++++++++++ .../out/unsafe/stl_conversion_operators.rs | 40 +++++++++++++++++++ 2 files changed, 72 insertions(+) create mode 100644 tests/unit/out/refcount/stl_conversion_operators.rs create mode 100644 tests/unit/out/unsafe/stl_conversion_operators.rs diff --git a/tests/unit/out/refcount/stl_conversion_operators.rs b/tests/unit/out/refcount/stl_conversion_operators.rs new file mode 100644 index 0000000000..2e2399a8ec --- /dev/null +++ b/tests/unit/out/refcount/stl_conversion_operators.rs @@ -0,0 +1,32 @@ +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 main() { + __cpp2rust_init_globals(); + std::process::exit(main_0()); +} +fn main_0() -> i32 { + let mut n: u32 = 17_u32; + let d: Value = Rc::new(RefCell::new(n)); + assert!(((*d.borrow()) == 17_u32)); + let mut name: Ptr = Ptr::::from_string_literal(b"dir/file.txt"); + let p: Value> = Rc::new(RefCell::new({ + let mut __v = Vec::new(); + name.with_c_str(|__s| __v.extend_from_slice(__s)); + __v.push(0); + __v + })); + let mut s: Vec = (*p.borrow()).clone(); + assert!(((s.len() - 1) == 12_usize)); + assert!( + Ptr::::from_string_literal(b"dir/file.txt") + .with_c_str(|__s| s[..s.len().saturating_sub(1)] == *__s) + ); + return 0; +} +pub fn __cpp2rust_init_globals() {} diff --git a/tests/unit/out/unsafe/stl_conversion_operators.rs b/tests/unit/out/unsafe/stl_conversion_operators.rs new file mode 100644 index 0000000000..6ed9e4c5d8 --- /dev/null +++ b/tests/unit/out/unsafe/stl_conversion_operators.rs @@ -0,0 +1,40 @@ +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 fn main() { + unsafe { + __cpp2rust_init_globals(); + std::process::exit(main_0() as i32); + } +} +unsafe fn main_0() -> i32 { + let mut n: u32 = 17_u32; + let mut d: u32 = n; + assert!(((d) == (17_u32))); + let mut name: *const libc::c_char = c"dir/file.txt".as_ptr(); + let mut p: Vec = { + let mut __v = ::std::ffi::CStr::from_ptr(name) + .to_bytes() + .iter() + .map(|&__b| __b as libc::c_char) + .collect::>(); + __v.push(0); + __v + }; + let mut s: Vec = p; + assert!(((s.len() - 1) == (12_usize))); + assert!( + s == { + let s = c"dir/file.txt".as_ptr(); + std::slice::from_raw_parts(s, (0..).take_while(|&i| *s.add(i) != 0).count() + 1) + .to_vec() + } + ); + return 0; +} +pub unsafe fn __cpp2rust_init_globals() {}