From 28103a4fef1ab119073c8299ea5964e80c593015 Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Sat, 31 Oct 2026 12:11:12 +0000 Subject: [PATCH 01/14] Delete init marker --- rules/common/init.h | 4 ---- rules/deque/src.cpp | 4 +--- rules/iterator_category/src.cpp | 2 -- rules/unique_ptr/src.cpp | 2 -- rules/unique_ptr/src_cxx14.cpp | 4 +--- rules/vector/src.cpp | 4 +--- 6 files changed, 3 insertions(+), 17 deletions(-) delete mode 100644 rules/common/init.h diff --git a/rules/common/init.h b/rules/common/init.h deleted file mode 100644 index 4d8d528716..0000000000 --- a/rules/common/init.h +++ /dev/null @@ -1,4 +0,0 @@ -// Copyright (c) 2022-present INESC-ID. -// Distributed under the MIT license that can be found in the LICENSE file. - -template using Init = A; diff --git a/rules/deque/src.cpp b/rules/deque/src.cpp index 6fde4378ed..17dce0ba57 100644 --- a/rules/deque/src.cpp +++ b/rules/deque/src.cpp @@ -4,8 +4,6 @@ #include #include -#include "../common/init.h" - template using t1 = std::deque; template using t2 = typename std::deque::size_type; @@ -51,7 +49,7 @@ std::deque &f11(std::deque &dst, std::deque &&src) { } template -auto f12(std::deque &o, Init &&...args) +auto f12(std::deque &o, Args &&...args) -> decltype(o.emplace_back(std::forward(args)...)) { return o.emplace_back(std::forward(args)...); } diff --git a/rules/iterator_category/src.cpp b/rules/iterator_category/src.cpp index 3895c49cdb..4d87690dcd 100644 --- a/rules/iterator_category/src.cpp +++ b/rules/iterator_category/src.cpp @@ -5,8 +5,6 @@ #include #include -#include "../common/init.h" - template < typename It, typename T1 = typename std::iterator_traits::value_type, typename = typename std::enable_if< diff --git a/rules/unique_ptr/src.cpp b/rules/unique_ptr/src.cpp index 96705680bd..bdcb0a442f 100644 --- a/rules/unique_ptr/src.cpp +++ b/rules/unique_ptr/src.cpp @@ -4,8 +4,6 @@ #include #include -#include "../common/init.h" - template using t1 = std::unique_ptr; template using t2 = std::unique_ptr; template using t3 = typename std::unique_ptr::pointer; diff --git a/rules/unique_ptr/src_cxx14.cpp b/rules/unique_ptr/src_cxx14.cpp index cf18c0b2d1..fb24c3760e 100644 --- a/rules/unique_ptr/src_cxx14.cpp +++ b/rules/unique_ptr/src_cxx14.cpp @@ -5,13 +5,11 @@ #include #include -#include "../common/init.h" - template std::unique_ptr f1(std::size_t n) { return std::make_unique(n); } template -std::unique_ptr f8(Init &&...args) { +std::unique_ptr f8(Args &&...args) { return std::make_unique(std::forward(args)...); } diff --git a/rules/vector/src.cpp b/rules/vector/src.cpp index b132451153..ea4a6b3dcd 100644 --- a/rules/vector/src.cpp +++ b/rules/vector/src.cpp @@ -8,8 +8,6 @@ #include #include -#include "../common/init.h" - template > using t5 = std::vector; @@ -242,7 +240,7 @@ std::vector f110(const std::vector &o) { } template , typename... Args> -auto f114(std::vector &o, Init &&...args) +auto f114(std::vector &o, Args &&...args) -> decltype(o.emplace_back(std::forward(args)...)) { return o.emplace_back(std::forward(args)...); } From bcc3246340e2ca0360e570879b11abd7e680e8cd Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Sat, 31 Oct 2026 12:22:19 +0000 Subject: [PATCH 02/14] Add init argument in params --- cpp2rust/converter/converter.cpp | 2 +- cpp2rust/converter/rules/registry.cpp | 52 ++++++++----------------- cpp2rust/converter/rules/registry.h | 2 +- cpp2rust/converter/translation_rule.cpp | 4 ++ cpp2rust/converter/translation_rule.h | 1 + rule-preprocessor/src/ir.rs | 7 +++- rule-preprocessor/src/syntactic.rs | 2 +- 7 files changed, 31 insertions(+), 39 deletions(-) diff --git a/cpp2rust/converter/converter.cpp b/cpp2rust/converter/converter.cpp index 055db2e8ff..77196e05e4 100644 --- a/cpp2rust/converter/converter.cpp +++ b/cpp2rust/converter/converter.cpp @@ -5279,7 +5279,7 @@ Converter::ConvertInitFragment(clang::Expr *expr, if (!tgt_ir || !tgt_ir->usesInit()) { llvm::report_fatal_error("expression rule does not use Init"); } - auto type = RuleRegistry::GetInitType(expr); + auto type = RuleRegistry::GetInitType(ctx_, expr); Buffer buf(*this); PushExprKind push(*this, ExprKind::RValue); diff --git a/cpp2rust/converter/rules/registry.cpp b/cpp2rust/converter/rules/registry.cpp index 5b98e5e03e..5eba171eb4 100644 --- a/cpp2rust/converter/rules/registry.cpp +++ b/cpp2rust/converter/rules/registry.cpp @@ -65,26 +65,6 @@ bool isRuleName(llvm::StringRef name) { llvm::all_of(name.drop_front(), llvm::isDigit); } -// The T of a rule's trailing `Init &&...args` pack. -clang::QualType getInitPattern(const clang::FunctionDecl *rule) { - if (rule->param_empty()) { - return {}; - } - auto expansion = - rule->parameters().back()->getType()->getAs(); - if (!expansion) { - return {}; - } - auto alias = expansion->getPattern() - .getNonReferenceType() - ->getAs(); - if (!alias || !alias->isTypeAlias() || - alias->getTemplateName().getAsTemplateDecl()->getName() != "Init") { - return {}; - } - return alias->template_arguments()[0].getAsType(); -} - void collectGenerics(const std::vector &body, std::unordered_set &used) { TranslationRule::AnyFragment(body, [&](const TranslationRule::BodyFragment @@ -157,6 +137,7 @@ void checkGenerics(const std::string &dir, const std::string &name, for (const auto ¶m : rule.params) { collectGenerics(param.type, used); } + collectGenerics(rule.init.type, used); for (unsigned i : used) { if (!declared.contains(std::format("T{}", i)) && !isBoundArgument(rule, i)) { @@ -165,10 +146,14 @@ void checkGenerics(const std::string &dir, const std::string &name, llvm::report_fatal_error("Absent generic from src"); } } - if (rule.usesInit() && getInitPattern(decl).isNull()) { + if (rule.usesInit() && + (decl->param_empty() || !decl->parameters() + .back() + ->getType() + ->getAs())) { llvm::errs() << dir << "::" << name << '\n'; - llvm::report_fatal_error("Expr rule uses init but its src pack is not " - "declared as Init"); + llvm::report_fatal_error( + "Expr rule uses init but its src does not end in a pack"); } } @@ -447,18 +432,15 @@ clang::QualType GetConversionSourceType(clang::ASTContext &ctx, llvm::report_fatal_error("converted generic has no CppFrom bound"); } -clang::QualType GetInitType(const clang::Expr *expr) { - auto match = findExpr(expr); - assert(match.rule_decl && "expression must have a translation rule"); - auto function = match.rule_decl->decl->getAsFunction(); - auto pattern = getInitPattern(function); - assert(!pattern.isNull() && "rule has no Init pack"); - clang::MultiLevelTemplateArgumentList args( - function->getDescribedFunctionTemplate(), match.args, /*Final=*/false); - clang::Sema::InstantiatingTemplate inst( - *sema_, expr->getBeginLoc(), const_cast(function)); - return sema_->SubstType(pattern, args, expr->getBeginLoc(), - clang::DeclarationName()); +clang::QualType GetInitType(clang::ASTContext &ctx, const clang::Expr *expr) { + auto rule = Search(ctx, expr).first; + assert(rule && "expression must have a translation rule"); + llvm::StringRef type = rule->init.type; + unsigned n = 0; + [[maybe_unused]] bool bad = + !type.consume_front("T") || type.getAsInteger(10, n); + assert(!bad && "init parameter type must be a bare generic"); + return GetGenericType(ctx, expr, n); } clang::QualType GetDeclaredReturnType(const clang::Expr *expr) { diff --git a/cpp2rust/converter/rules/registry.h b/cpp2rust/converter/rules/registry.h index cbe9905635..22425ec117 100644 --- a/cpp2rust/converter/rules/registry.h +++ b/cpp2rust/converter/rules/registry.h @@ -37,7 +37,7 @@ Matcher::Match Search(clang::ASTContext &ctx, const TranslationRule::ExprRule *GetExprRule(clang::ASTContext &ctx, const clang::Expr *expr); -clang::QualType GetInitType(const clang::Expr *expr); +clang::QualType GetInitType(clang::ASTContext &ctx, const clang::Expr *expr); clang::QualType GetGenericType(clang::ASTContext &ctx, const clang::Expr *expr, unsigned n); diff --git a/cpp2rust/converter/translation_rule.cpp b/cpp2rust/converter/translation_rule.cpp index f634a20aa4..8dc3e33b71 100644 --- a/cpp2rust/converter/translation_rule.cpp +++ b/cpp2rust/converter/translation_rule.cpp @@ -296,6 +296,10 @@ ExprRule ParseExprRuleJSON(const llvm::json::Object &obj) { if (auto *params = obj.getObject("params")) { for (auto &[key, val] : *params) { if (auto *param_obj = val.getAsObject()) { + if (key == "init") { + ir.init = ParseTypeInfoJSON(*param_obj); + continue; + } size_t id = atoi(llvm::StringRef(key).data() + 1); ir.params.resize(std::max(ir.params.size(), id + 1)); ir.params[id] = ParseTypeInfoJSON(*param_obj); diff --git a/cpp2rust/converter/translation_rule.h b/cpp2rust/converter/translation_rule.h index 1bb3ae9d4c..bc13773251 100644 --- a/cpp2rust/converter/translation_rule.h +++ b/cpp2rust/converter/translation_rule.h @@ -102,6 +102,7 @@ struct TypeInfo { struct ExprRule { std::vector params; + TypeInfo init; TypeInfo return_type; std::vector> generics; // "T1" -> [Ord, Clone] std::vector body; diff --git a/rule-preprocessor/src/ir.rs b/rule-preprocessor/src/ir.rs index 09014cd82b..0334ccf0b0 100644 --- a/rule-preprocessor/src/ir.rs +++ b/rule-preprocessor/src/ir.rs @@ -121,7 +121,12 @@ impl FnIr { pub fn validate(&self, name: &str) { validate_consecutive_keys( - self.params.as_ref().map(|p| p.keys()).into_iter().flatten(), + self.params + .as_ref() + .map(|p| p.keys()) + .into_iter() + .flatten() + .filter(|key| *key != "init"), 'a', 0, &format!("Rule {name} params"), diff --git a/rule-preprocessor/src/syntactic.rs b/rule-preprocessor/src/syntactic.rs index 1441fc7ee6..65706413d7 100644 --- a/rule-preprocessor/src/syntactic.rs +++ b/rule-preprocessor/src/syntactic.rs @@ -616,7 +616,7 @@ impl<'a> FnIrBuilder<'a> { let params_map: BTreeMap = params .iter() - .filter(|p| !p.is_va_args && !p.is_init) + .filter(|p| !p.is_va_args) .map(|p| { ( p.name.clone(), From 727ca7446ea2b1a0e77a5ce00dc604c73dfb1e4e Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Sat, 31 Oct 2026 12:28:19 +0000 Subject: [PATCH 03/14] Use Tn/an instead of Args/args --- rules/deque/src.cpp | 8 ++++---- rules/unique_ptr/src_cxx14.cpp | 5 ++--- rules/vector/src.cpp | 8 ++++---- 3 files changed, 10 insertions(+), 11 deletions(-) diff --git a/rules/deque/src.cpp b/rules/deque/src.cpp index 17dce0ba57..f7ffe33b8b 100644 --- a/rules/deque/src.cpp +++ b/rules/deque/src.cpp @@ -48,10 +48,10 @@ std::deque &f11(std::deque &dst, std::deque &&src) { return dst = std::move(src); } -template -auto f12(std::deque &o, Args &&...args) - -> decltype(o.emplace_back(std::forward(args)...)) { - return o.emplace_back(std::forward(args)...); +template +auto f12(std::deque &o, T2 &&...a1) + -> decltype(o.emplace_back(std::forward(a1)...)) { + return o.emplace_back(std::forward(a1)...); } template std::deque f14() { return std::deque(); } diff --git a/rules/unique_ptr/src_cxx14.cpp b/rules/unique_ptr/src_cxx14.cpp index fb24c3760e..31047607d4 100644 --- a/rules/unique_ptr/src_cxx14.cpp +++ b/rules/unique_ptr/src_cxx14.cpp @@ -9,7 +9,6 @@ template std::unique_ptr f1(std::size_t n) { return std::make_unique(n); } -template -std::unique_ptr f8(Args &&...args) { - return std::make_unique(std::forward(args)...); +template std::unique_ptr f8(T2 &&...a0) { + return std::make_unique(std::forward(a0)...); } diff --git a/rules/vector/src.cpp b/rules/vector/src.cpp index ea4a6b3dcd..05980fa9bc 100644 --- a/rules/vector/src.cpp +++ b/rules/vector/src.cpp @@ -239,10 +239,10 @@ std::vector f110(const std::vector &o) { return std::vector(o); } -template , typename... Args> -auto f114(std::vector &o, Args &&...args) - -> decltype(o.emplace_back(std::forward(args)...)) { - return o.emplace_back(std::forward(args)...); +template , typename... T3> +auto f114(std::vector &o, T3 &&...a1) + -> decltype(o.emplace_back(std::forward(a1)...)) { + return o.emplace_back(std::forward(a1)...); } template > From 68ad4d6448fa804872a0549e7e3149004188ca96 Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Sat, 31 Oct 2026 12:34:51 +0000 Subject: [PATCH 04/14] Add rule that builds init as a pair --- rules/map/src.cpp | 6 ++++++ rules/map/tgt_refcount.rs | 13 +++++++++++++ rules/map/tgt_unsafe.rs | 12 ++++++++++++ 3 files changed, 31 insertions(+) diff --git a/rules/map/src.cpp b/rules/map/src.cpp index 5b492cde14..636c97083d 100644 --- a/rules/map/src.cpp +++ b/rules/map/src.cpp @@ -109,3 +109,9 @@ template typename std::map::const_iterator f29(const std::map &o) { return o.begin(); } + +template +std::pair::iterator, bool> +f67(std::map &a0, T5 &&...a1) { + return a0.template emplace(std::forward(a1)...); +} diff --git a/rules/map/tgt_refcount.rs b/rules/map/tgt_refcount.rs index a76ff4c160..2c5d90ec21 100644 --- a/rules/map/tgt_refcount.rs +++ b/rules/map/tgt_refcount.rs @@ -97,3 +97,16 @@ fn f28(a0: Ptr, Value fn f29(a0: Ptr, Value)>>) -> BidirectionalIterator<(Value, Value)> { a0.with(|__m: &Map, Value)>| __m.begin()) } + +fn f67( + a0: Ptr, Value)>>, + init: (Value, Value), +) -> (BidirectionalIterator<(Value, Value)>, bool) { + let __init = init; + let __key = __init.0.as_pointer(); + let __inserted = a0.with(|__m: &Map, Value)>| __m.get(&__key).is_none()); + if __inserted { + Map::subscript(a0.clone(), __key.clone()).write(__init.1.borrow().clone()); + } + (a0.with(|__m: &Map, Value)>| __m.find(&__key)), __inserted) +} diff --git a/rules/map/tgt_unsafe.rs b/rules/map/tgt_unsafe.rs index 25f79b84a3..9c7fe3dd77 100644 --- a/rules/map/tgt_unsafe.rs +++ b/rules/map/tgt_unsafe.rs @@ -97,3 +97,15 @@ unsafe fn f28(a0: &mut UnsafeMap, a1: T1) -> usize { unsafe fn f29(a0: UnsafeMap) -> UnsafeBidirectionalIterator<(T1, T2)> { a0.begin() } + +unsafe fn f67( + a0: &mut UnsafeMap, + init: (T1, T2), +) -> (UnsafeBidirectionalIterator<(T1, T2)>, bool) { + let __init = init; + let __inserted = a0.get(&__init.0).is_none(); + if __inserted { + *a0.subscript(&__init.0) = __init.1; + } + (a0.find(&__init.0), __inserted) +} From 6067dac25deed419877429f9f55630ab7353088f Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Sat, 31 Oct 2026 13:24:16 +0000 Subject: [PATCH 05/14] Update tests --- tests/unit/map.cpp | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/tests/unit/map.cpp b/tests/unit/map.cpp index e696c65a29..159ad7b638 100644 --- a/tests/unit/map.cpp +++ b/tests/unit/map.cpp @@ -106,6 +106,17 @@ int main() { assert(pair.second == pair.first % 2); last = pair.first; } + std::map m3; + auto inserted = m3.emplace(1, 2); + assert(inserted.second); + assert(inserted.first->second == 2); + auto not_inserted = m3.emplace(1, 3); + assert(!not_inserted.second); + assert(not_inserted.first->second == 2); + m3.emplace(std::make_pair(4, 5)); + assert(m3.size() == 2); + assert(m3[1] == 2); + assert(m3[4] == 5); k = 0; const auto &value_0 = m.at(k); assert(m.size() + x1 + x2 + x3 + x4 + x5 + value_0 == 21); From 970805fb5fd539b2b2bd52999831ccaba8bc4534 Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Sat, 31 Oct 2026 13:30:52 +0000 Subject: [PATCH 06/14] Add more rules that construct a different type from the pack --- rules/map/src.cpp | 15 +++++++++++++++ rules/map/tgt_refcount.rs | 29 ++++++++++++++++++++++++++++- rules/map/tgt_unsafe.rs | 28 +++++++++++++++++++++++++++- tests/unit/map.cpp | 27 +++++++++++++++++++++++++++ 4 files changed, 97 insertions(+), 2 deletions(-) diff --git a/rules/map/src.cpp b/rules/map/src.cpp index 636c97083d..53ff394f75 100644 --- a/rules/map/src.cpp +++ b/rules/map/src.cpp @@ -115,3 +115,18 @@ std::pair::iterator, bool> f67(std::map &a0, T5 &&...a1) { return a0.template emplace(std::forward(a1)...); } + +template +typename std::map::iterator +f68(std::map &a0, + typename std::map::const_iterator a1, T5 &&...a2) { + return a0.template emplace_hint( + std::forward::const_iterator>(a1), + std::forward(a2)...); +} + +template +std::pair::iterator, bool> +f73(std::map &a0, const T1 &a1, T5 &&...a2) { + return a0.template try_emplace(a1, std::forward(a2)...); +} diff --git a/rules/map/tgt_refcount.rs b/rules/map/tgt_refcount.rs index 2c5d90ec21..39a31822ce 100644 --- a/rules/map/tgt_refcount.rs +++ b/rules/map/tgt_refcount.rs @@ -100,7 +100,7 @@ fn f29(a0: Ptr, Value)>>) -> Bi fn f67( a0: Ptr, Value)>>, - init: (Value, Value), + init: cpp_type!("typename std::map::value_type", (Value, Value)), ) -> (BidirectionalIterator<(Value, Value)>, bool) { let __init = init; let __key = __init.0.as_pointer(); @@ -110,3 +110,30 @@ fn f67, Value)>| __m.find(&__key)), __inserted) } + +fn f68( + a0: Ptr, Value)>>, + a1: BidirectionalIterator<(Value, Value)>, + init: cpp_type!("typename std::map::value_type", (Value, Value)), +) -> BidirectionalIterator<(Value, Value)> { + let _ = a1; + let __init = init; + let __key = __init.0.as_pointer(); + if a0.with(|__m: &Map, Value)>| __m.get(&__key).is_none()) { + Map::subscript(a0.clone(), __key.clone()).write(__init.1.borrow().clone()); + } + a0.with(|__m: &Map, Value)>| __m.find(&__key)) +} + +fn f73( + a0: Ptr, Value)>>, + a1: Ptr, + init: T2, +) -> (BidirectionalIterator<(Value, Value)>, bool) { + let __key = a1; + let __inserted = a0.with(|__m: &Map, Value)>| __m.get(&__key).is_none()); + if __inserted { + Map::subscript(a0.clone(), __key.clone()).write(init); + } + (a0.with(|__m: &Map, Value)>| __m.find(&__key)), __inserted) +} diff --git a/rules/map/tgt_unsafe.rs b/rules/map/tgt_unsafe.rs index 9c7fe3dd77..a2284bef3f 100644 --- a/rules/map/tgt_unsafe.rs +++ b/rules/map/tgt_unsafe.rs @@ -100,7 +100,7 @@ unsafe fn f29(a0: UnsafeMap) -> UnsafeBidirectionalIterato unsafe fn f67( a0: &mut UnsafeMap, - init: (T1, T2), + init: cpp_type!("typename std::map::value_type", (T1, T2)), ) -> (UnsafeBidirectionalIterator<(T1, T2)>, bool) { let __init = init; let __inserted = a0.get(&__init.0).is_none(); @@ -109,3 +109,29 @@ unsafe fn f67( } (a0.find(&__init.0), __inserted) } + +unsafe fn f68( + a0: &mut UnsafeMap, + a1: UnsafeBidirectionalIterator<(T1, T2)>, + init: cpp_type!("typename std::map::value_type", (T1, T2)), +) -> UnsafeBidirectionalIterator<(T1, T2)> { + let _ = a1; + let __init = init; + if a0.get(&__init.0).is_none() { + *a0.subscript(&__init.0) = __init.1; + } + a0.find(&__init.0) +} + +unsafe fn f73( + a0: &mut UnsafeMap, + a1: T1, + init: T2, +) -> (UnsafeBidirectionalIterator<(T1, T2)>, bool) { + let __key = a1; + let __inserted = a0.get(&__key).is_none(); + if __inserted { + *a0.subscript(&__key) = init; + } + (a0.find(&__key), __inserted) +} diff --git a/tests/unit/map.cpp b/tests/unit/map.cpp index 159ad7b638..caa288ecd8 100644 --- a/tests/unit/map.cpp +++ b/tests/unit/map.cpp @@ -5,6 +5,13 @@ void foo(unsigned x) { x = x + 1; } void bar(unsigned &x) { x = x + 1; } +struct Point { + int x; + int y; + Point() : x(0), y(0) {} + Point(int x, int y) : x(x), y(y) {} +}; + int main() { std::map m; m[0] = 1; @@ -117,6 +124,26 @@ int main() { assert(m3.size() == 2); assert(m3[1] == 2); assert(m3[4] == 5); + std::map::iterator hinted = m3.emplace_hint(m3.end(), 6, 7); + assert(hinted->second == 7); + int key1 = 1; + auto tried = m3.try_emplace(key1, 9); + assert(!tried.second); + assert(tried.first->second == 2); + int key8 = 8; + auto tried_new = m3.try_emplace(key8, 9); + assert(tried_new.second); + assert(m3[8] == 9); + assert(m3.size() == 4); + std::map points; + auto point_new = points.try_emplace(key1, 3, 4); + assert(point_new.second); + assert(points[1].x == 3); + assert(points[1].y == 4); + points.emplace(key8, Point(5, 6)); + assert(points[8].x == 5); + assert(points[8].y == 6); + assert(points.size() == 2); k = 0; const auto &value_0 = m.at(k); assert(m.size() + x1 + x2 + x3 + x4 + x5 + value_0 == 21); From 6f4dcce77202c74bb002625c627e56818a33523d Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Sat, 31 Oct 2026 13:32:57 +0000 Subject: [PATCH 07/14] Add cpp_type --- libcc2rs/src/conversion.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/libcc2rs/src/conversion.rs b/libcc2rs/src/conversion.rs index 946e6aee3d..61c26d57a8 100644 --- a/libcc2rs/src/conversion.rs +++ b/libcc2rs/src/conversion.rs @@ -35,3 +35,10 @@ impl CppFromUnsafe for T { unsafe { (*src).clone() } } } + +#[macro_export] +macro_rules! cpp_type { + ($cpp:literal, $rust:ty) => { + $rust + }; +} From fedf0ab023282fdb0c8bfce511befbf2e596ab0a Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Sat, 31 Oct 2026 13:50:06 +0000 Subject: [PATCH 08/14] Update tests --- tests/unit/any.cpp | 28 ++++ tests/unit/out/refcount/any.rs | 102 ++++++++++++ tests/unit/out/refcount/map.rs | 280 +++++++++++++++++++++++++++++++++ tests/unit/out/unsafe/any.rs | 97 ++++++++++++ tests/unit/out/unsafe/map.rs | 111 +++++++++++++ 5 files changed, 618 insertions(+) create mode 100644 tests/unit/any.cpp create mode 100644 tests/unit/out/refcount/any.rs create mode 100644 tests/unit/out/unsafe/any.rs diff --git a/tests/unit/any.cpp b/tests/unit/any.cpp new file mode 100644 index 0000000000..856fdfe7e8 --- /dev/null +++ b/tests/unit/any.cpp @@ -0,0 +1,28 @@ +// ADDITIONAL_COMPILE_FLAGS: -std=c++23 +#include +#include + +struct Point { + int x; + int y; + Point(int x, int y) : x(x), y(y) {} +}; + +int main() { + std::any a = std::make_any(3, 4); + assert(a.has_value()); + Point *p = std::any_cast(&a); + assert(p != nullptr); + assert(p->x == 3); + assert(p->y == 4); + assert(std::any_cast(&a) == nullptr); + Point &q = a.emplace(5, 6); + assert(q.x == 5); + assert(std::any_cast(&a)->y == 6); + std::any b = std::make_any(7); + assert(*std::any_cast(&b) == 7); + b.reset(); + assert(!b.has_value()); + assert(std::any_cast(&b) == nullptr); + return 0; +} diff --git a/tests/unit/out/refcount/any.rs b/tests/unit/out/refcount/any.rs new file mode 100644 index 0000000000..3341fd287c --- /dev/null +++ b/tests/unit/out/refcount/any.rs @@ -0,0 +1,102 @@ +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(Clone, Record, ByteRepr, VaArg, FnPtrArg, Default)] +#[byte_size(8)] +pub struct Point { + #[offset(0)] + pub x: i32, + #[offset(4)] + pub y: i32, +} +impl Point { + pub fn new(mut x: i32, mut y: i32) -> Self { + Self { x: x, y: y } + } +} +pub fn main() { + __cpp2rust_init_globals(); + std::process::exit(main_0()); +} +fn main_0() -> i32 { + let a: Value>> = Rc::new(RefCell::new( + ({ + let ___args_0: Value = Rc::new(RefCell::new(3)); + let ___args_1: Value = Rc::new(RefCell::new(4)); + make_any_0(___args_0.as_pointer(), ___args_1.as_pointer()) + }), + )); + assert!((*a.borrow()).is_some()); + let mut p: Ptr = (a.as_pointer()).with(|__a: &Option>| match __a + .clone() + .map(|__r| __r.downcast::>()) + { + Some(Ok(__v)) => __v.as_pointer(), + _ => Ptr::null(), + }); + assert!(!((p).is_null())); + assert!((p.with(|__s| __s.x) == 3)); + assert!((p.with(|__s| __s.y) == 4)); + assert!( + ((a.as_pointer()).with(|__a: &Option>| { + match __a.clone().map(|__r| __r.downcast::>()) { + Some(Ok(__v)) => __v.as_pointer(), + _ => Ptr::null(), + } + })) + .is_null() + ); + let q: Ptr = ({ + let ___args_0: Value = Rc::new(RefCell::new(5)); + let ___args_1: Value = Rc::new(RefCell::new(6)); + (*a.borrow()).emplace(___args_0.as_pointer(), ___args_1.as_pointer()) + }); + assert!((q.with(|__s| __s.x) == 5)); + assert!( + ((a.as_pointer()) + .with(|__a: &Option>| { + match __a.clone().map(|__r| __r.downcast::>()) { + Some(Ok(__v)) => __v.as_pointer(), + _ => Ptr::null(), + } + }) + .with(|__s| __s.y) + == 6) + ); + let b: Value>> = Rc::new(RefCell::new( + ({ + let ___args: Value = Rc::new(RefCell::new(7)); + make_any_1(___args.as_pointer()) + }), + )); + assert!( + (((b.as_pointer()) + .with(|__a: &Option>| { + match __a.clone().map(|__r| __r.downcast::>()) { + Some(Ok(__v)) => __v.as_pointer(), + _ => Ptr::null(), + } + }) + .read()) + == 7) + ); + b.as_pointer() + .with_mut(|__a: &mut Option>| *__a = None); + assert!(!((*b.borrow()).is_some())); + assert!( + ((b.as_pointer()).with(|__a: &Option>| { + match __a.clone().map(|__r| __r.downcast::>()) { + Some(Ok(__v)) => __v.as_pointer(), + _ => Ptr::null(), + } + })) + .is_null() + ); + return 0; +} +pub fn __cpp2rust_init_globals() {} diff --git a/tests/unit/out/refcount/map.rs b/tests/unit/out/refcount/map.rs index 7031aac499..be71dc3b3d 100644 --- a/tests/unit/out/refcount/map.rs +++ b/tests/unit/out/refcount/map.rs @@ -12,6 +12,27 @@ pub fn foo_0(mut x: u32) { pub fn bar_1(x: Ptr) { x.write({ (x.read()).wrapping_add(1_u32) }); } +#[derive(Clone, Record, ByteRepr, VaArg, FnPtrArg)] +#[byte_size(8)] +pub struct Point { + #[offset(0)] + pub x: i32, + #[offset(4)] + pub y: i32, +} +impl Point { + pub fn new_1() -> Self { + Self { x: 0, y: 0 } + } + pub fn new_2(mut x: i32, mut y: i32) -> Self { + Self { x: x, y: y } + } +} +impl Default for Point { + fn default() -> Self { + { Point::new_1() } + } +} pub fn main() { __cpp2rust_init_globals(); std::process::exit(main_0()); @@ -486,6 +507,265 @@ fn main_0() -> i32 { ); last = (*(*pair.upgrade().deref()).0.borrow()); } + let m3: Value, Value)>> = Rc::new(RefCell::new(Map::new())); + let inserted: Value<( + Value, Value)>>, + Value, + )> = Rc::new(RefCell::new({ + let __init = (Rc::new(RefCell::new(1)), Rc::new(RefCell::new(2))); + let __key = __init.0.as_pointer(); + let __inserted = m3 + .as_pointer() + .with(|__m: &Map, Value)>| __m.get(&__key).is_none()); + if __inserted { + Map::subscript(m3.as_pointer().clone(), __key.clone()).write(__init.1.borrow().clone()); + } + ( + Rc::new(RefCell::new(m3.as_pointer().with( + |__m: &Map, Value)>| __m.find(&__key), + ))), + Rc::new(RefCell::new(__inserted)), + ) + })); + assert!(((*(*inserted.borrow()).1.borrow()) as bool)); + assert!( + ((*(*(*(*inserted.borrow()).0.borrow()) + .iter_deref() + .upgrade() + .deref()) + .1 + .borrow()) + == 2) + ); + let not_inserted: Value<( + Value, Value)>>, + Value, + )> = Rc::new(RefCell::new({ + let __init = (Rc::new(RefCell::new(1)), Rc::new(RefCell::new(3))); + let __key = __init.0.as_pointer(); + let __inserted = m3 + .as_pointer() + .with(|__m: &Map, Value)>| __m.get(&__key).is_none()); + if __inserted { + Map::subscript(m3.as_pointer().clone(), __key.clone()).write(__init.1.borrow().clone()); + } + ( + Rc::new(RefCell::new(m3.as_pointer().with( + |__m: &Map, Value)>| __m.find(&__key), + ))), + Rc::new(RefCell::new(__inserted)), + ) + })); + assert!(!(*(*not_inserted.borrow()).1.borrow())); + assert!( + ((*(*(*(*not_inserted.borrow()).0.borrow()) + .iter_deref() + .upgrade() + .deref()) + .1 + .borrow()) + == 2) + ); + { + let __init = { + let __a0 = (Rc::new(RefCell::new(4)), Rc::new(RefCell::new(5))); + ( + Rc::new(RefCell::new((__a0.0.as_pointer().read()))), + Rc::new(RefCell::new((__a0.1.as_pointer().read()))), + ) + }; + let __key = __init.0.as_pointer(); + let __inserted = m3 + .as_pointer() + .with(|__m: &Map, Value)>| __m.get(&__key).is_none()); + if __inserted { + Map::subscript(m3.as_pointer().clone(), __key.clone()).write(__init.1.borrow().clone()); + } + ( + Rc::new(RefCell::new(m3.as_pointer().with( + |__m: &Map, Value)>| __m.find(&__key), + ))), + Rc::new(RefCell::new(__inserted)), + ) + }; + assert!(((*m3.borrow()).len() == 2_usize)); + assert!( + ((Map::subscript_move( + m3.as_pointer(), + Rc::new(RefCell::new(1)).as_pointer(), + |__k| cpp_move!(__k) + ) + .read()) + == 2) + ); + assert!( + ((Map::subscript_move( + m3.as_pointer(), + Rc::new(RefCell::new(4)).as_pointer(), + |__k| cpp_move!(__k) + ) + .read()) + == 5) + ); + let hinted: Value, Value)>> = Rc::new(RefCell::new({ + let _ = , Value)>>::from( + m3.as_pointer() + .with(|__m: &Map, Value)>| __m.end()), + ); + let __init = (Rc::new(RefCell::new(6)), Rc::new(RefCell::new(7))); + let __key = __init.0.as_pointer(); + if m3 + .as_pointer() + .with(|__m: &Map, Value)>| __m.get(&__key).is_none()) + { + Map::subscript(m3.as_pointer().clone(), __key.clone()).write(__init.1.borrow().clone()); + } + m3.as_pointer() + .with(|__m: &Map, Value)>| __m.find(&__key)) + })); + assert!( + ((*(*(*hinted.borrow()).iter_deref().upgrade().deref()) + .1 + .borrow()) + == 7) + ); + let key1: Value = Rc::new(RefCell::new(1)); + let tried: Value<( + Value, Value)>>, + Value, + )> = Rc::new(RefCell::new({ + let __key = key1.as_pointer(); + let __inserted = m3 + .as_pointer() + .with(|__m: &Map, Value)>| __m.get(&__key).is_none()); + if __inserted { + Map::subscript(m3.as_pointer().clone(), __key.clone()).write(9); + } + ( + Rc::new(RefCell::new(m3.as_pointer().with( + |__m: &Map, Value)>| __m.find(&__key), + ))), + Rc::new(RefCell::new(__inserted)), + ) + })); + assert!(!(*(*tried.borrow()).1.borrow())); + assert!( + ((*(*(*(*tried.borrow()).0.borrow()) + .iter_deref() + .upgrade() + .deref()) + .1 + .borrow()) + == 2) + ); + let key8: Value = Rc::new(RefCell::new(8)); + let tried_new: Value<( + Value, Value)>>, + Value, + )> = Rc::new(RefCell::new({ + let __key = key8.as_pointer(); + let __inserted = m3 + .as_pointer() + .with(|__m: &Map, Value)>| __m.get(&__key).is_none()); + if __inserted { + Map::subscript(m3.as_pointer().clone(), __key.clone()).write(9); + } + ( + Rc::new(RefCell::new(m3.as_pointer().with( + |__m: &Map, Value)>| __m.find(&__key), + ))), + Rc::new(RefCell::new(__inserted)), + ) + })); + assert!(((*(*tried_new.borrow()).1.borrow()) as bool)); + assert!( + ((Map::subscript_move( + m3.as_pointer(), + Rc::new(RefCell::new(8)).as_pointer(), + |__k| cpp_move!(__k) + ) + .read()) + == 9) + ); + assert!(((*m3.borrow()).len() == 4_usize)); + let points: Value, Value)>> = Rc::new(RefCell::new(Map::new())); + let point_new: Value<( + Value, Value)>>, + Value, + )> = Rc::new(RefCell::new({ + let __key = key1.as_pointer(); + let __inserted = points + .as_pointer() + .with(|__m: &Map, Value)>| __m.get(&__key).is_none()); + if __inserted { + Map::subscript(points.as_pointer().clone(), __key.clone()) + .write(Point::new_2({ 3 }, { 4 })); + } + ( + Rc::new(RefCell::new(points.as_pointer().with( + |__m: &Map, Value)>| __m.find(&__key), + ))), + Rc::new(RefCell::new(__inserted)), + ) + })); + assert!(((*(*point_new.borrow()).1.borrow()) as bool)); + assert!( + (Map::subscript_move( + points.as_pointer(), + Rc::new(RefCell::new(1)).as_pointer(), + |__k| cpp_move!(__k) + ) + .with(|__s| __s.x) + == 3) + ); + assert!( + (Map::subscript_move( + points.as_pointer(), + Rc::new(RefCell::new(1)).as_pointer(), + |__k| cpp_move!(__k) + ) + .with(|__s| __s.y) + == 4) + ); + { + let __init = ( + Rc::new(RefCell::new((*key8.borrow()))), + Rc::new(RefCell::new(Point::new_2({ 5 }, { 6 }))), + ); + let __key = __init.0.as_pointer(); + let __inserted = points + .as_pointer() + .with(|__m: &Map, Value)>| __m.get(&__key).is_none()); + if __inserted { + Map::subscript(points.as_pointer().clone(), __key.clone()) + .write(__init.1.borrow().clone()); + } + ( + Rc::new(RefCell::new(points.as_pointer().with( + |__m: &Map, Value)>| __m.find(&__key), + ))), + Rc::new(RefCell::new(__inserted)), + ) + }; + assert!( + (Map::subscript_move( + points.as_pointer(), + Rc::new(RefCell::new(8)).as_pointer(), + |__k| cpp_move!(__k) + ) + .with(|__s| __s.x) + == 5) + ); + assert!( + (Map::subscript_move( + points.as_pointer(), + Rc::new(RefCell::new(8)).as_pointer(), + |__k| cpp_move!(__k) + ) + .with(|__s| __s.y) + == 6) + ); + assert!(((*points.borrow()).len() == 2_usize)); k = 0; let value_0: Ptr = ({ let __tmp_14: Value = Rc::new(RefCell::new((k as i16))); diff --git a/tests/unit/out/unsafe/any.rs b/tests/unit/out/unsafe/any.rs new file mode 100644 index 0000000000..a29946e741 --- /dev/null +++ b/tests/unit/out/unsafe/any.rs @@ -0,0 +1,97 @@ +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, VaArg, FnPtrArg, Default)] +pub struct Point { + pub x: i32, + pub y: i32, +} +impl Point { + pub unsafe fn new(mut x: i32, mut y: i32) -> Self { + let mut this = Self { x: x, y: y }; + this + } +} +pub fn main() { + unsafe { + __cpp2rust_init_globals(); + std::process::exit(main_0() as i32); + } +} +unsafe fn main_0() -> i32 { + let mut a: Option> = (unsafe { + let mut ___args_0: i32 = 3; + let mut ___args_1: i32 = 4; + make_any_0(&mut ___args_0, &mut ___args_1) + }); + assert!(a.is_some()); + let mut p: *mut Point = match (*(&mut a as *mut Option>)) + .as_mut() + .and_then(|__b| __b.downcast_mut::()) + { + Some(__v) => __v as *mut Point, + None => std::ptr::null_mut(), + }; + assert!(!((p).is_null())); + assert!((((*p).x) == (3))); + assert!((((*p).y) == (4))); + assert!( + (match (*(&mut a as *mut Option>)) + .as_mut() + .and_then(|__b| __b.downcast_mut::()) + { + Some(__v) => __v as *mut i32, + None => std::ptr::null_mut(), + }) + .is_null() + ); + let q: *mut Point = (unsafe { + let mut ___args_0: i32 = 5; + let mut ___args_1: i32 = 6; + a.emplace(&mut ___args_0, &mut ___args_1) + }); + assert!((((*q).x) == (5))); + assert!( + (((*match (*(&mut a as *mut Option>)) + .as_mut() + .and_then(|__b| __b.downcast_mut::()) + { + Some(__v) => __v as *mut Point, + None => std::ptr::null_mut(), + }) + .y) == (6)) + ); + let mut b: Option> = (unsafe { + let mut ___args: i32 = 7; + make_any_1(&mut ___args) + }); + assert!( + ((*match (*(&mut b as *mut Option>)) + .as_mut() + .and_then(|__b| __b.downcast_mut::()) + { + Some(__v) => __v as *mut i32, + None => std::ptr::null_mut(), + }) == (7)) + ); + b = None; + assert!(!(b.is_some())); + assert!( + (match (*(&mut b as *mut Option>)) + .as_mut() + .and_then(|__b| __b.downcast_mut::()) + { + Some(__v) => __v as *mut i32, + None => std::ptr::null_mut(), + }) + .is_null() + ); + return 0; +} +pub unsafe fn __cpp2rust_init_globals() {} diff --git a/tests/unit/out/unsafe/map.rs b/tests/unit/out/unsafe/map.rs index a07f253dda..cb39347da8 100644 --- a/tests/unit/out/unsafe/map.rs +++ b/tests/unit/out/unsafe/map.rs @@ -12,6 +12,27 @@ pub unsafe fn foo_0(mut x: u32) { pub unsafe fn bar_1(x: *mut u32) { (*x) = (*x).wrapping_add(1_u32); } +#[repr(C)] +#[derive(Copy, Clone, VaArg, FnPtrArg)] +pub struct Point { + pub x: i32, + pub y: i32, +} +impl Point { + pub unsafe fn new_1() -> Self { + let mut this = Self { x: 0, y: 0 }; + this + } + pub unsafe fn new_2(mut x: i32, mut y: i32) -> Self { + let mut this = Self { x: x, y: y }; + this + } +} +impl Default for Point { + fn default() -> Self { + unsafe { Point::new_1() } + } +} pub fn main() { unsafe { __cpp2rust_init_globals(); @@ -163,6 +184,96 @@ unsafe fn main_0() -> i32 { assert!((((*pair).1 as i32) == (((*pair).0) % (2)))); last = (*pair).0; } + let mut m3: UnsafeMap = UnsafeMap::new(); + let mut inserted: (UnsafeBidirectionalIterator<(i32, i32)>, bool) = { + let __init = (1, 2); + let __inserted = m3.get(&__init.0).is_none(); + if __inserted { + *m3.subscript(&__init.0) = __init.1; + } + (m3.find(&__init.0), __inserted) + }; + assert!((inserted.1 as bool)); + assert!((((*inserted.0.iter_deref()).1) == (2))); + let mut not_inserted: (UnsafeBidirectionalIterator<(i32, i32)>, bool) = { + let __init = (1, 3); + let __inserted = m3.get(&__init.0).is_none(); + if __inserted { + *m3.subscript(&__init.0) = __init.1; + } + (m3.find(&__init.0), __inserted) + }; + assert!(!(not_inserted.1)); + assert!((((*not_inserted.0.iter_deref()).1) == (2))); + { + let __init = { + let mut __a0 = (4, 5); + ((*&mut __a0.0), (*&mut __a0.1)) + }; + let __inserted = m3.get(&__init.0).is_none(); + if __inserted { + *m3.subscript(&__init.0) = __init.1; + } + (m3.find(&__init.0), __inserted) + }; + assert!(((m3.len()) == (2_usize))); + assert!(((*m3.subscript_move(&mut 1, |__k| cpp_move_unsafe!(__k))) == (2))); + assert!(((*m3.subscript_move(&mut 4, |__k| cpp_move_unsafe!(__k))) == (5))); + let mut hinted: UnsafeBidirectionalIterator<(i32, i32)> = { + let _ = >::from(m3.end()); + let __init = (6, 7); + if m3.get(&__init.0).is_none() { + *m3.subscript(&__init.0) = __init.1; + } + m3.find(&__init.0) + }; + assert!((((*hinted.iter_deref()).1) == (7))); + let mut key1: i32 = 1; + let mut tried: (UnsafeBidirectionalIterator<(i32, i32)>, bool) = { + let __key = key1; + let __inserted = m3.get(&__key).is_none(); + if __inserted { + *m3.subscript(&__key) = 9; + } + (m3.find(&__key), __inserted) + }; + assert!(!(tried.1)); + assert!((((*tried.0.iter_deref()).1) == (2))); + let mut key8: i32 = 8; + let mut tried_new: (UnsafeBidirectionalIterator<(i32, i32)>, bool) = { + let __key = key8; + let __inserted = m3.get(&__key).is_none(); + if __inserted { + *m3.subscript(&__key) = 9; + } + (m3.find(&__key), __inserted) + }; + assert!((tried_new.1 as bool)); + assert!(((*m3.subscript_move(&mut 8, |__k| cpp_move_unsafe!(__k))) == (9))); + assert!(((m3.len()) == (4_usize))); + let mut points: UnsafeMap = UnsafeMap::new(); + let mut point_new: (UnsafeBidirectionalIterator<(i32, Point)>, bool) = { + let __key = key1; + let __inserted = points.get(&__key).is_none(); + if __inserted { + *points.subscript(&__key) = Point::new_2({ 3 }, { 4 }); + } + (points.find(&__key), __inserted) + }; + assert!((point_new.1 as bool)); + assert!((((*points.subscript_move(&mut 1, |__k| cpp_move_unsafe!(__k))).x) == (3))); + assert!((((*points.subscript_move(&mut 1, |__k| cpp_move_unsafe!(__k))).y) == (4))); + { + let __init = (key8, Point::new_2({ 5 }, { 6 })); + let __inserted = points.get(&__init.0).is_none(); + if __inserted { + *points.subscript(&__init.0) = __init.1; + } + (points.find(&__init.0), __inserted) + }; + assert!((((*points.subscript_move(&mut 8, |__k| cpp_move_unsafe!(__k))).x) == (5))); + assert!((((*points.subscript_move(&mut 8, |__k| cpp_move_unsafe!(__k))).y) == (6))); + assert!(((points.len()) == (2_usize))); k = 0; let value_0: *const u32 = &raw const (*m.get(&(k as i16)).expect("out of range!")).1; assert!( From 272c86d4e4418f430323b51e2d4b49de9c8cd586 Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Sat, 31 Oct 2026 13:50:21 +0000 Subject: [PATCH 09/14] Update rules --- rules/any/src_cxx23.cpp | 32 +++++++++++++++++++++++++++++++ rules/any/tgt_refcount.rs | 40 +++++++++++++++++++++++++++++++++++++++ rules/any/tgt_unsafe.rs | 36 +++++++++++++++++++++++++++++++++++ rules/map/tgt_refcount.rs | 20 ++++++++++++++++---- 4 files changed, 124 insertions(+), 4 deletions(-) create mode 100644 rules/any/src_cxx23.cpp create mode 100644 rules/any/tgt_refcount.rs create mode 100644 rules/any/tgt_unsafe.rs diff --git a/rules/any/src_cxx23.cpp b/rules/any/src_cxx23.cpp new file mode 100644 index 0000000000..d1a55836c1 --- /dev/null +++ b/rules/any/src_cxx23.cpp @@ -0,0 +1,32 @@ +#include +#include +#include +#include + +using t1 = std::any; + +std::any f1() { return std::any(); } + +void f6(std::any &a0) { return a0.reset(); } + +bool f8(const std::any &a0) { return a0.has_value(); } + +template +typename std::enable_if< + std::integral_constant< + bool, std::is_copy_constructible>::value && + std::is_constructible, T2...>::value>::value, + std::decay_t &>::type +f14(std::any &a0, T2 &&...a1) { + return a0.template emplace(std::forward(a1)...); +} + +template T1 *f19(std::any *a0) { return std::any_cast(a0); } + +template +std::enable_if_t< + std::is_constructible_v, T2...>, + std::any> +f27(T2 &&...a0) { + return std::make_any(std::forward(a0)...); +} diff --git a/rules/any/tgt_refcount.rs b/rules/any/tgt_refcount.rs new file mode 100644 index 0000000000..fe81112c04 --- /dev/null +++ b/rules/any/tgt_refcount.rs @@ -0,0 +1,40 @@ +// Copyright (c) 2022-present INESC-ID. +// Distributed under the MIT license that can be found in the LICENSE file. + +use libcc2rs::*; +use std::cell::RefCell; +use std::rc::Rc; + +fn t1() -> Option> { + None +} + +fn f1() -> Option> { + None +} + +fn f6(a0: Ptr>>) { + a0.with_mut(|__a: &mut Option>| *__a = None) +} + +fn f8(a0: Option>) -> bool { + a0.is_some() +} + +fn f14(a0: Ptr>>, init: cpp_type!("std::decay_t", T1)) -> Ptr { + let __value: Value = Rc::new(RefCell::new(init)); + let __ptr = __value.as_pointer(); + a0.with_mut(|__a: &mut Option>| *__a = Some(__value)); + __ptr +} + +fn f19(a0: Ptr>>) -> Ptr { + a0.with(|__a: &Option>| match __a.clone().map(|__r| __r.downcast::>()) { + Some(Ok(__v)) => __v.as_pointer(), + _ => Ptr::null(), + }) +} + +fn f27(init: cpp_type!("std::decay_t", T1)) -> Option> { + Some(Rc::new(RefCell::new(init))) +} diff --git a/rules/any/tgt_unsafe.rs b/rules/any/tgt_unsafe.rs new file mode 100644 index 0000000000..dfdd9f82b1 --- /dev/null +++ b/rules/any/tgt_unsafe.rs @@ -0,0 +1,36 @@ +// Copyright (c) 2022-present INESC-ID. +// Distributed under the MIT license that can be found in the LICENSE file. + +use libcc2rs::*; + +fn t1() -> Option> { + None +} + +unsafe fn f1() -> Option> { + None +} + +unsafe fn f6(a0: &mut Option>) { + *a0 = None +} + +unsafe fn f8(a0: Option>) -> bool { + a0.is_some() +} + +unsafe fn f14(a0: &mut Option>, init: cpp_type!("std::decay_t", T1)) -> &mut T1 { + *a0 = Some(Box::new(init)); + a0.as_mut().unwrap().downcast_mut::().unwrap() +} + +unsafe fn f19(a0: *mut Option>) -> *mut T1 { + match (*a0).as_mut().and_then(|__b| __b.downcast_mut::()) { + Some(__v) => __v as *mut T1, + None => std::ptr::null_mut(), + } +} + +unsafe fn f27(init: cpp_type!("std::decay_t", T1)) -> Option> { + Some(Box::new(init)) +} diff --git a/rules/map/tgt_refcount.rs b/rules/map/tgt_refcount.rs index 39a31822ce..e5121ea00c 100644 --- a/rules/map/tgt_refcount.rs +++ b/rules/map/tgt_refcount.rs @@ -2,6 +2,8 @@ // Distributed under the MIT license that can be found in the LICENSE file. use libcc2rs::*; +use std::cell::RefCell; +use std::rc::Rc; fn t1() -> Map, Value)> { Map::new() @@ -101,14 +103,19 @@ fn f29(a0: Ptr, Value)>>) -> Bi fn f67( a0: Ptr, Value)>>, init: cpp_type!("typename std::map::value_type", (Value, Value)), -) -> (BidirectionalIterator<(Value, Value)>, bool) { +) -> (Value, Value)>>, Value) { let __init = init; let __key = __init.0.as_pointer(); let __inserted = a0.with(|__m: &Map, Value)>| __m.get(&__key).is_none()); if __inserted { Map::subscript(a0.clone(), __key.clone()).write(__init.1.borrow().clone()); } - (a0.with(|__m: &Map, Value)>| __m.find(&__key)), __inserted) + ( + Rc::new(RefCell::new( + a0.with(|__m: &Map, Value)>| __m.find(&__key)), + )), + Rc::new(RefCell::new(__inserted)), + ) } fn f68( @@ -129,11 +136,16 @@ fn f73, Value)>>, a1: Ptr, init: T2, -) -> (BidirectionalIterator<(Value, Value)>, bool) { +) -> (Value, Value)>>, Value) { let __key = a1; let __inserted = a0.with(|__m: &Map, Value)>| __m.get(&__key).is_none()); if __inserted { Map::subscript(a0.clone(), __key.clone()).write(init); } - (a0.with(|__m: &Map, Value)>| __m.find(&__key)), __inserted) + ( + Rc::new(RefCell::new( + a0.with(|__m: &Map, Value)>| __m.find(&__key)), + )), + Rc::new(RefCell::new(__inserted)), + ) } From 07009f11eed14c10baa79185d41359a08f2c4b21 Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Sat, 31 Oct 2026 13:51:10 +0000 Subject: [PATCH 10/14] Route cpp_type to converter --- cpp2rust/converter/rules/registry.cpp | 63 ++++++++++++++++--- cpp2rust/converter/rules/registry.h | 5 ++ cpp2rust/converter/rules/rules_loader.cpp | 12 ++++ cpp2rust/converter/translation_rule.cpp | 4 ++ cpp2rust/converter/translation_rule.h | 1 + .../rule_preprocessing/cpp_rule_indexer.cpp | 8 +++ libcc2rs/src/reinterpret.rs | 1 + rule-preprocessor/src/ir.rs | 2 + rule-preprocessor/src/syntactic.rs | 33 +++++++++- 9 files changed, 119 insertions(+), 10 deletions(-) diff --git a/cpp2rust/converter/rules/registry.cpp b/cpp2rust/converter/rules/registry.cpp index 5eba171eb4..d4c2ece230 100644 --- a/cpp2rust/converter/rules/registry.cpp +++ b/cpp2rust/converter/rules/registry.cpp @@ -60,6 +60,8 @@ bool pointsToUserType(clang::QualType type) { return tag && IsUserDefinedDecl(tag); } +constexpr llvm::StringLiteral kInitAliasPrefix = "cpp2rust_init_"; + bool isRuleName(llvm::StringRef name) { return name.size() > 1 && (name[0] == 'f' || name[0] == 't') && llvm::all_of(name.drop_front(), llvm::isDigit); @@ -203,6 +205,10 @@ void addCxxRules(clang::ASTContext &ctx, const clang::NamespaceDecl *rules) { } for (auto decl : ns->decls()) { auto named = llvm::cast(decl); + if (llvm::isa(named) && + named->getName().starts_with(kInitAliasPrefix)) { + continue; + } if (!isRuleName(named->getName())) { llvm::report_fatal_error("declaration is not a rule"); } @@ -433,14 +439,55 @@ clang::QualType GetConversionSourceType(clang::ASTContext &ctx, } clang::QualType GetInitType(clang::ASTContext &ctx, const clang::Expr *expr) { - auto rule = Search(ctx, expr).first; - assert(rule && "expression must have a translation rule"); - llvm::StringRef type = rule->init.type; - unsigned n = 0; - [[maybe_unused]] bool bad = - !type.consume_front("T") || type.getAsInteger(10, n); - assert(!bad && "init parameter type must be a bare generic"); - return GetGenericType(ctx, expr, n); + auto match = findExpr(expr); + assert(match.rule_decl && "expression must have a translation rule"); + const auto &rule = match.rule_decl->rule; + if (rule.init_cpp_type.empty()) { + llvm::StringRef type = rule.init.type; + unsigned n = 0; + [[maybe_unused]] bool bad = + !type.consume_front("T") || type.getAsInteger(10, n); + assert(!bad && "init type must be a bare generic or a cpp_type!"); + return GetGenericType(ctx, expr, n); + } + auto function = match.rule_decl->decl->getAsFunction(); + auto tmpl = function->getDescribedFunctionTemplate(); + assert(tmpl && "cpp_type! init in a non-template rule"); + clang::TypeAliasTemplateDecl *alias = nullptr; + for (auto decl : function->getDeclContext()->lookup(&ctx.Idents.get( + InitAliasName(model_, function->getName().str())))) { + if ((alias = llvm::dyn_cast(decl))) { + break; + } + } + assert(alias && "cpp_type! init has no alias next to its rule"); + clang::MultiLevelTemplateArgumentList args(tmpl, match.args, + /*Final=*/false); + clang::Sema::InstantiatingTemplate inst( + *sema_, expr->getBeginLoc(), const_cast(function)); + auto type = sema_->SubstType(alias->getTemplatedDecl()->getUnderlyingType(), + args, expr->getBeginLoc(), + clang::DeclarationName()); + assert(!type.isNull() && "cpp_type! init does not substitute"); + return type; +} + +std::string InitCppType(Model model, const std::string &dir, + const std::string &name) { + auto rule = findRule(model, dir, name); + if (!rule) { + rule = findRule(Model::kUnsafe, dir, name); + } + if (!rule) { + return {}; + } + auto cpp_type = rule->getString("init_cpp_type"); + return cpp_type ? cpp_type->str() : std::string(); +} + +std::string InitAliasName(Model model, const std::string &name) { + return std::format("{}{}_{}", kInitAliasPrefix.str(), + model == Model::kRefCount ? "refcount" : "unsafe", name); } clang::QualType GetDeclaredReturnType(const clang::Expr *expr) { diff --git a/cpp2rust/converter/rules/registry.h b/cpp2rust/converter/rules/registry.h index 22425ec117..2c8a524be0 100644 --- a/cpp2rust/converter/rules/registry.h +++ b/cpp2rust/converter/rules/registry.h @@ -39,6 +39,11 @@ const TranslationRule::ExprRule *GetExprRule(clang::ASTContext &ctx, const clang::Expr *expr); clang::QualType GetInitType(clang::ASTContext &ctx, const clang::Expr *expr); +std::string InitCppType(Model model, const std::string &dir, + const std::string &name); + +std::string InitAliasName(Model model, const std::string &name); + clang::QualType GetGenericType(clang::ASTContext &ctx, const clang::Expr *expr, unsigned n); diff --git a/cpp2rust/converter/rules/rules_loader.cpp b/cpp2rust/converter/rules/rules_loader.cpp index 8e318b92c3..3619a1448d 100644 --- a/cpp2rust/converter/rules/rules_loader.cpp +++ b/cpp2rust/converter/rules/rules_loader.cpp @@ -27,6 +27,7 @@ #include "converter/converter_lib.h" #include "converter/rules/decls.h" +#include "converter/rules/registry.h" #include "logging.h" namespace cpp2rust::RulesLoader { @@ -314,6 +315,17 @@ const llvm::json::Object &CxxIndex() { std::string WrapRule(const llvm::json::Object &rule, bool is_cxx) { auto dir = rule.getString("dir")->str(); auto text = rule.getString("text")->str(); + if (auto header = rule.getString("template_header")) { + auto name = rule.getString("name")->str(); + for (auto model : {Model::kUnsafe, Model::kRefCount}) { + auto cpp_type = RuleRegistry::InitCppType(model, dir, name); + if (!cpp_type.empty()) { + text += std::format("{} using {} = {};\n", header->str(), + RuleRegistry::InitAliasName(model, name), + cpp_type); + } + } + } if (!is_cxx) { return std::format("#define {0} cpp2rust_rules_{1}_{0}\n{2}#undef {0}\n", rule.getString("name")->str(), dir, text); diff --git a/cpp2rust/converter/translation_rule.cpp b/cpp2rust/converter/translation_rule.cpp index 8dc3e33b71..26623a4ad9 100644 --- a/cpp2rust/converter/translation_rule.cpp +++ b/cpp2rust/converter/translation_rule.cpp @@ -355,6 +355,10 @@ ExprRule ParseExprRuleJSON(const llvm::json::Object &obj) { ir.body = ParseBodyFragmentsJSON(*body); } + if (auto init_cpp_type = obj.getString("init_cpp_type")) { + ir.init_cpp_type = init_cpp_type->str(); + } + return ir; } diff --git a/cpp2rust/converter/translation_rule.h b/cpp2rust/converter/translation_rule.h index bc13773251..00fe3d317e 100644 --- a/cpp2rust/converter/translation_rule.h +++ b/cpp2rust/converter/translation_rule.h @@ -103,6 +103,7 @@ struct TypeInfo { struct ExprRule { std::vector params; TypeInfo init; + std::string init_cpp_type; TypeInfo return_type; std::vector> generics; // "T1" -> [Ord, Clone] std::vector body; diff --git a/cpp2rust/rule_preprocessing/cpp_rule_indexer.cpp b/cpp2rust/rule_preprocessing/cpp_rule_indexer.cpp index 3ccf982258..a999e2a5a8 100644 --- a/cpp2rust/rule_preprocessing/cpp_rule_indexer.cpp +++ b/cpp2rust/rule_preprocessing/cpp_rule_indexer.cpp @@ -286,6 +286,14 @@ void IndexRuleFile(clang::ASTContext &ctx, const std::string &dir_name, auto range = clang::tooling::getExtendedRange(*decl, clang::tok::semi, ctx); auto text = clang::tooling::getText(range, ctx).str() + '\n'; llvm::json::Object rule{{"dir", dir_name}, {"name", name}, {"text", text}}; + if (auto tmpl = llvm::dyn_cast(decl)) { + rule["template_header"] = + clang::tooling::getText( + clang::CharSourceRange::getTokenRange( + tmpl->getTemplateParameters()->getSourceRange()), + ctx) + .str(); + } if (any_class) { rule["requires"] = llvm::json::Array{RulesLoader::kAnyIteratorKey}; } else if (auto required = RequiredClasses(decl, alias); diff --git a/libcc2rs/src/reinterpret.rs b/libcc2rs/src/reinterpret.rs index 7759621f57..9e0ac16e00 100644 --- a/libcc2rs/src/reinterpret.rs +++ b/libcc2rs/src/reinterpret.rs @@ -175,6 +175,7 @@ impl ByteRepr for *const T {} impl ByteRepr for *mut T {} impl ByteRepr for (A, B) {} impl ByteRepr for std::collections::BTreeMap {} +impl ByteRepr for std::rc::Rc {} // Runs `f` with a zeroed scratch buffer of `len` elements. Small buffers live // on the stack so that accessing memory through a reinterpreted pointer does diff --git a/rule-preprocessor/src/ir.rs b/rule-preprocessor/src/ir.rs index 0334ccf0b0..7311fbdac6 100644 --- a/rule-preprocessor/src/ir.rs +++ b/rule-preprocessor/src/ir.rs @@ -85,6 +85,8 @@ pub struct FnIr { pub return_type: Option, #[serde(skip_serializing_if = "Option::is_none")] pub is_extern: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub init_cpp_type: Option, } impl FnIr { diff --git a/rule-preprocessor/src/syntactic.rs b/rule-preprocessor/src/syntactic.rs index 65706413d7..5046bf0273 100644 --- a/rule-preprocessor/src/syntactic.rs +++ b/rule-preprocessor/src/syntactic.rs @@ -4,9 +4,9 @@ use cfg_expr::Expression; use cfg_expr::expr::{Predicate, TargetPredicate}; use ra_ap_syntax::ast::{ - HasArgList, HasAttrs, HasGenericArgs, HasGenericParams, HasName, HasTypeBounds, + HasArgList, HasAttrs, HasGenericArgs, HasGenericParams, HasName, HasTypeBounds, IsString, }; -use ra_ap_syntax::{AstNode, SyntaxKind, ast, match_ast}; +use ra_ap_syntax::{AstNode, AstToken, SyntaxKind, ast, match_ast}; use std::collections::BTreeMap; use std::path::{Path, PathBuf}; @@ -42,6 +42,27 @@ fn pointer_flags(ty: &ast::Type) -> (bool, bool) { flags } +fn cpp_type_of(ty: &ast::Type) -> Option { + let ast::Type::MacroType(macro_type) = ty else { + return None; + }; + let call = macro_type.macro_call()?; + if call.path()?.segment()?.name_ref()?.text() != "cpp_type" { + return None; + } + let token = call + .token_tree()? + .syntax() + .descendants_with_tokens() + .filter_map(|element| element.into_token()) + .find(|token| token.kind() == SyntaxKind::STRING) + .expect("cpp_type! must start with a string literal"); + let string = ast::String::cast(token).expect("cpp_type! literal is not a string"); + let text = string.text_without_quotes(); + assert!(!text.contains('\\'), "cpp_type! literal must not contain escapes"); + Some(text.to_string()) +} + fn is_va_args_type(ty: &ast::Type) -> bool { if let ast::Type::PathType(boxed) = ty && let Some(segment) = boxed.path().and_then(|p| p.segment()) @@ -341,6 +362,7 @@ struct ParamInfo { is_mut_ref: bool, is_va_args: bool, is_init: bool, + cpp_type: Option, } struct FnIrBuilder<'a> { @@ -376,6 +398,11 @@ impl<'a> FnIrBuilder<'a> { "variadic argument parameter must be named `va`, found `{name}`" ); let is_init = name == "init"; + let cpp_type = cpp_type_of(&ty); + assert!( + cpp_type.is_none() || is_init, + "cpp_type! is only allowed as the type of `init`, found on `{name}`" + ); params.push(ParamInfo { name, ty: ty.syntax().text().to_string(), @@ -384,6 +411,7 @@ impl<'a> FnIrBuilder<'a> { is_mut_ref: matches!(&ty, ast::Type::RefType(r) if r.mut_token().is_some()), is_va_args, is_init, + cpp_type, }); } assert!( @@ -661,6 +689,7 @@ impl<'a> FnIrBuilder<'a> { multi_statement, body, is_extern: self.is_extern().then_some(true), + init_cpp_type: params.iter().find_map(|p| p.cpp_type.clone()), }; ir.validate(&format!("{}:{}", path.display(), fn_name)); ir From adb23e7ee8d424afa3f8ad484b0f0d1b7d9006b3 Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Sat, 31 Oct 2026 13:54:26 +0000 Subject: [PATCH 11/14] Deduce template arguments from explicit args --- cpp2rust/converter/rules/expr_matcher.cpp | 52 +++++++++++++++++++++++ cpp2rust/converter/rules/registry.cpp | 10 ++--- cpp2rust/converter/rules/rules_loader.cpp | 3 +- rule-preprocessor/src/syntactic.rs | 5 ++- tests/unit/out/refcount/any.rs | 30 ++++++------- tests/unit/out/unsafe/any.rs | 20 +++------ 6 files changed, 80 insertions(+), 40 deletions(-) diff --git a/cpp2rust/converter/rules/expr_matcher.cpp b/cpp2rust/converter/rules/expr_matcher.cpp index e37369771e..81acb9b861 100644 --- a/cpp2rust/converter/rules/expr_matcher.cpp +++ b/cpp2rust/converter/rules/expr_matcher.cpp @@ -272,6 +272,57 @@ void deduceMemberTypesFromOperands( } } +llvm::ArrayRef +getExplicitTemplateArgs(const clang::Expr *expr) { + auto call = llvm::dyn_cast(RuleDecls::SkipImplicit(expr)); + if (!call) { + return {}; + } + auto callee = call->getCallee()->IgnoreParenImpCasts(); + if (auto ref = llvm::dyn_cast(callee)) { + return ref->template_arguments(); + } + if (auto member = llvm::dyn_cast(callee)) { + return member->template_arguments(); + } + if (auto overload = llvm::dyn_cast(callee)) { + return overload->template_arguments(); + } + if (auto member = + llvm::dyn_cast(callee)) { + return member->template_arguments(); + } + return {}; +} + +void deduceFromExplicitArgs(clang::Sema &sema, + clang::FunctionTemplateDecl *rule, + const clang::Expr *use, + std::vector &deduced) { + auto rule_args = + getExplicitTemplateArgs(RuleDecls::GetReturned(rule->getTemplatedDecl())); + auto use_args = getExplicitTemplateArgs(use); + for (unsigned i = 0; i < rule_args.size() && i < use_args.size(); ++i) { + const auto &rule_arg = rule_args[i].getArgument(); + const auto &use_arg = use_args[i].getArgument(); + if (rule_arg.getKind() != clang::TemplateArgument::Type || + use_arg.getKind() != clang::TemplateArgument::Type) { + continue; + } + if (rule_arg.getAsType()->getAs()) { + break; + } + auto args = RuleDecls::DeduceFromType( + sema, rule->getTemplateParameters(), rule_arg.getAsType(), + use_arg.getAsType().getCanonicalType()); + for (unsigned j = 0; args && j < deduced.size(); ++j) { + if (deduced[j].isNull()) { + deduced[j] = (*args)[j]; + } + } + } +} + clang::TemplateArgumentListInfo getResultArgs(clang::Sema &sema, clang::FunctionTemplateDecl *rule, const clang::Expr *use) { @@ -295,6 +346,7 @@ clang::TemplateArgumentListInfo getResultArgs(clang::Sema &sema, .getUnqualifiedType()) .value_or(std::vector(params->size())); deduceMemberTypesFromOperands(sema, rule, use, deduced); + deduceFromExplicitArgs(sema, rule, use, deduced); auto last = static_cast(deducible.find_last_unset()); for (unsigned i = 0; i < params->size() && i <= last; ++i) { auto param = params->getParam(i); diff --git a/cpp2rust/converter/rules/registry.cpp b/cpp2rust/converter/rules/registry.cpp index d4c2ece230..62956dabc5 100644 --- a/cpp2rust/converter/rules/registry.cpp +++ b/cpp2rust/converter/rules/registry.cpp @@ -454,8 +454,8 @@ clang::QualType GetInitType(clang::ASTContext &ctx, const clang::Expr *expr) { auto tmpl = function->getDescribedFunctionTemplate(); assert(tmpl && "cpp_type! init in a non-template rule"); clang::TypeAliasTemplateDecl *alias = nullptr; - for (auto decl : function->getDeclContext()->lookup(&ctx.Idents.get( - InitAliasName(model_, function->getName().str())))) { + for (auto decl : function->getDeclContext()->lookup( + &ctx.Idents.get(InitAliasName(model_, function->getName().str())))) { if ((alias = llvm::dyn_cast(decl))) { break; } @@ -465,9 +465,9 @@ clang::QualType GetInitType(clang::ASTContext &ctx, const clang::Expr *expr) { /*Final=*/false); clang::Sema::InstantiatingTemplate inst( *sema_, expr->getBeginLoc(), const_cast(function)); - auto type = sema_->SubstType(alias->getTemplatedDecl()->getUnderlyingType(), - args, expr->getBeginLoc(), - clang::DeclarationName()); + auto type = + sema_->SubstType(alias->getTemplatedDecl()->getUnderlyingType(), args, + expr->getBeginLoc(), clang::DeclarationName()); assert(!type.isNull() && "cpp_type! init does not substitute"); return type; } diff --git a/cpp2rust/converter/rules/rules_loader.cpp b/cpp2rust/converter/rules/rules_loader.cpp index 3619a1448d..f8e0c96342 100644 --- a/cpp2rust/converter/rules/rules_loader.cpp +++ b/cpp2rust/converter/rules/rules_loader.cpp @@ -321,8 +321,7 @@ std::string WrapRule(const llvm::json::Object &rule, bool is_cxx) { auto cpp_type = RuleRegistry::InitCppType(model, dir, name); if (!cpp_type.empty()) { text += std::format("{} using {} = {};\n", header->str(), - RuleRegistry::InitAliasName(model, name), - cpp_type); + RuleRegistry::InitAliasName(model, name), cpp_type); } } } diff --git a/rule-preprocessor/src/syntactic.rs b/rule-preprocessor/src/syntactic.rs index 5046bf0273..e367bad04b 100644 --- a/rule-preprocessor/src/syntactic.rs +++ b/rule-preprocessor/src/syntactic.rs @@ -59,7 +59,10 @@ fn cpp_type_of(ty: &ast::Type) -> Option { .expect("cpp_type! must start with a string literal"); let string = ast::String::cast(token).expect("cpp_type! literal is not a string"); let text = string.text_without_quotes(); - assert!(!text.contains('\\'), "cpp_type! literal must not contain escapes"); + assert!( + !text.contains('\\'), + "cpp_type! literal must not contain escapes" + ); Some(text.to_string()) } diff --git a/tests/unit/out/refcount/any.rs b/tests/unit/out/refcount/any.rs index 3341fd287c..cfe7dbda18 100644 --- a/tests/unit/out/refcount/any.rs +++ b/tests/unit/out/refcount/any.rs @@ -24,13 +24,9 @@ pub fn main() { std::process::exit(main_0()); } fn main_0() -> i32 { - let a: Value>> = Rc::new(RefCell::new( - ({ - let ___args_0: Value = Rc::new(RefCell::new(3)); - let ___args_1: Value = Rc::new(RefCell::new(4)); - make_any_0(___args_0.as_pointer(), ___args_1.as_pointer()) - }), - )); + let a: Value>> = Rc::new(RefCell::new(Some(Rc::new( + RefCell::new(Point::new({ 3 }, { 4 })), + )))); assert!((*a.borrow()).is_some()); let mut p: Ptr = (a.as_pointer()).with(|__a: &Option>| match __a .clone() @@ -51,11 +47,13 @@ fn main_0() -> i32 { })) .is_null() ); - let q: Ptr = ({ - let ___args_0: Value = Rc::new(RefCell::new(5)); - let ___args_1: Value = Rc::new(RefCell::new(6)); - (*a.borrow()).emplace(___args_0.as_pointer(), ___args_1.as_pointer()) - }); + let q: Ptr = { + let __value: Value = Rc::new(RefCell::new(Point::new({ 5 }, { 6 }))); + let __ptr = __value.as_pointer(); + a.as_pointer() + .with_mut(|__a: &mut Option>| *__a = Some(__value)); + __ptr + }; assert!((q.with(|__s| __s.x) == 5)); assert!( ((a.as_pointer()) @@ -68,12 +66,8 @@ fn main_0() -> i32 { .with(|__s| __s.y) == 6) ); - let b: Value>> = Rc::new(RefCell::new( - ({ - let ___args: Value = Rc::new(RefCell::new(7)); - make_any_1(___args.as_pointer()) - }), - )); + let b: Value>> = + Rc::new(RefCell::new(Some(Rc::new(RefCell::new(7))))); assert!( (((b.as_pointer()) .with(|__a: &Option>| { diff --git a/tests/unit/out/unsafe/any.rs b/tests/unit/out/unsafe/any.rs index a29946e741..abdb000c81 100644 --- a/tests/unit/out/unsafe/any.rs +++ b/tests/unit/out/unsafe/any.rs @@ -25,11 +25,7 @@ pub fn main() { } } unsafe fn main_0() -> i32 { - let mut a: Option> = (unsafe { - let mut ___args_0: i32 = 3; - let mut ___args_1: i32 = 4; - make_any_0(&mut ___args_0, &mut ___args_1) - }); + let mut a: Option> = Some(Box::new(Point::new({ 3 }, { 4 }))); assert!(a.is_some()); let mut p: *mut Point = match (*(&mut a as *mut Option>)) .as_mut() @@ -51,11 +47,10 @@ unsafe fn main_0() -> i32 { }) .is_null() ); - let q: *mut Point = (unsafe { - let mut ___args_0: i32 = 5; - let mut ___args_1: i32 = 6; - a.emplace(&mut ___args_0, &mut ___args_1) - }); + let q: *mut Point = { + a = Some(Box::new(Point::new({ 5 }, { 6 }))); + a.as_mut().unwrap().downcast_mut::().unwrap() + }; assert!((((*q).x) == (5))); assert!( (((*match (*(&mut a as *mut Option>)) @@ -67,10 +62,7 @@ unsafe fn main_0() -> i32 { }) .y) == (6)) ); - let mut b: Option> = (unsafe { - let mut ___args: i32 = 7; - make_any_1(&mut ___args) - }); + let mut b: Option> = Some(Box::new(7)); assert!( ((*match (*(&mut b as *mut Option>)) .as_mut() From 92f7cf4584568ded84e2c0ff52140cc384e7b031 Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Sat, 31 Oct 2026 13:59:57 +0000 Subject: [PATCH 12/14] Move cpp_init_type in TypeInfo --- cpp2rust/converter/rules/registry.cpp | 6 ++++-- cpp2rust/converter/translation_rule.cpp | 6 ++---- cpp2rust/converter/translation_rule.h | 2 +- rule-preprocessor/src/ir.rs | 4 ++-- rule-preprocessor/src/syntactic.rs | 4 +++- 5 files changed, 12 insertions(+), 10 deletions(-) diff --git a/cpp2rust/converter/rules/registry.cpp b/cpp2rust/converter/rules/registry.cpp index 62956dabc5..1bca633b59 100644 --- a/cpp2rust/converter/rules/registry.cpp +++ b/cpp2rust/converter/rules/registry.cpp @@ -442,7 +442,7 @@ clang::QualType GetInitType(clang::ASTContext &ctx, const clang::Expr *expr) { auto match = findExpr(expr); assert(match.rule_decl && "expression must have a translation rule"); const auto &rule = match.rule_decl->rule; - if (rule.init_cpp_type.empty()) { + if (rule.init.cpp_type.empty()) { llvm::StringRef type = rule.init.type; unsigned n = 0; [[maybe_unused]] bool bad = @@ -481,7 +481,9 @@ std::string InitCppType(Model model, const std::string &dir, if (!rule) { return {}; } - auto cpp_type = rule->getString("init_cpp_type"); + auto params = rule->getObject("params"); + auto init = params ? params->getObject("init") : nullptr; + auto cpp_type = init ? init->getString("cpp_type") : std::nullopt; return cpp_type ? cpp_type->str() : std::string(); } diff --git a/cpp2rust/converter/translation_rule.cpp b/cpp2rust/converter/translation_rule.cpp index 26623a4ad9..66b6a6dabf 100644 --- a/cpp2rust/converter/translation_rule.cpp +++ b/cpp2rust/converter/translation_rule.cpp @@ -20,6 +20,8 @@ TypeInfo ParseTypeInfoJSON(const llvm::json::Object &obj) { TypeInfo info; if (auto ty = obj.getString("type")) info.type = ty->str(); + if (auto cpp_type = obj.getString("cpp_type")) + info.cpp_type = cpp_type->str(); if (auto v = obj.getBoolean("is_refcount_pointer")) info.is_refcount_pointer = *v; if (auto v = obj.getBoolean("is_unsafe_pointer")) @@ -355,10 +357,6 @@ ExprRule ParseExprRuleJSON(const llvm::json::Object &obj) { ir.body = ParseBodyFragmentsJSON(*body); } - if (auto init_cpp_type = obj.getString("init_cpp_type")) { - ir.init_cpp_type = init_cpp_type->str(); - } - return ir; } diff --git a/cpp2rust/converter/translation_rule.h b/cpp2rust/converter/translation_rule.h index 00fe3d317e..9f08ea7d6d 100644 --- a/cpp2rust/converter/translation_rule.h +++ b/cpp2rust/converter/translation_rule.h @@ -92,6 +92,7 @@ bool AnyFragment(const std::vector &body, struct TypeInfo { std::vector derives; std::string type; + std::string cpp_type; bool is_refcount_pointer = false; bool is_unsafe_pointer = false; @@ -103,7 +104,6 @@ struct TypeInfo { struct ExprRule { std::vector params; TypeInfo init; - std::string init_cpp_type; TypeInfo return_type; std::vector> generics; // "T1" -> [Ord, Clone] std::vector body; diff --git a/rule-preprocessor/src/ir.rs b/rule-preprocessor/src/ir.rs index 7311fbdac6..cec8fe07c9 100644 --- a/rule-preprocessor/src/ir.rs +++ b/rule-preprocessor/src/ir.rs @@ -70,6 +70,8 @@ pub struct TypeInfo { pub is_unsafe_pointer: bool, #[serde(default, skip_serializing_if = "Vec::is_empty")] pub derives: Vec, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub cpp_type: Option, } #[derive(Debug, Clone, Serialize, Deserialize)] @@ -85,8 +87,6 @@ pub struct FnIr { pub return_type: Option, #[serde(skip_serializing_if = "Option::is_none")] pub is_extern: Option, - #[serde(skip_serializing_if = "Option::is_none")] - pub init_cpp_type: Option, } impl FnIr { diff --git a/rule-preprocessor/src/syntactic.rs b/rule-preprocessor/src/syntactic.rs index e367bad04b..f451e85dcf 100644 --- a/rule-preprocessor/src/syntactic.rs +++ b/rule-preprocessor/src/syntactic.rs @@ -440,6 +440,7 @@ impl<'a> FnIrBuilder<'a> { is_refcount_pointer, is_unsafe_pointer, derives: Vec::new(), + cpp_type: None, }) } } @@ -656,6 +657,7 @@ impl<'a> FnIrBuilder<'a> { is_refcount_pointer: p.is_refcount_pointer, is_unsafe_pointer: p.is_unsafe_pointer, derives: Vec::new(), + cpp_type: p.cpp_type.clone(), }, ) }) @@ -692,7 +694,6 @@ impl<'a> FnIrBuilder<'a> { multi_statement, body, is_extern: self.is_extern().then_some(true), - init_cpp_type: params.iter().find_map(|p| p.cpp_type.clone()), }; ir.validate(&format!("{}:{}", path.display(), fn_name)); ir @@ -767,6 +768,7 @@ impl<'a> TypeIrBuilder<'a> { is_refcount_pointer, is_unsafe_pointer, derives: Vec::new(), + cpp_type: None, }, } } From 11dd0c9620598efd620347f8dc0b86693339eaf1 Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Sat, 31 Oct 2026 14:04:19 +0000 Subject: [PATCH 13/14] Update tests --- rules/any/tgt_refcount.rs | 8 ++++---- tests/unit/out/refcount/any.rs | 6 ++---- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/rules/any/tgt_refcount.rs b/rules/any/tgt_refcount.rs index fe81112c04..8bad9f113c 100644 --- a/rules/any/tgt_refcount.rs +++ b/rules/any/tgt_refcount.rs @@ -13,18 +13,18 @@ fn f1() -> Option> { None } -fn f6(a0: Ptr>>) { - a0.with_mut(|__a: &mut Option>| *__a = None) +fn f6(a0: &mut Option>) { + *a0 = None } fn f8(a0: Option>) -> bool { a0.is_some() } -fn f14(a0: Ptr>>, init: cpp_type!("std::decay_t", T1)) -> Ptr { +fn f14(a0: &mut Option>, init: cpp_type!("std::decay_t", T1)) -> Ptr { let __value: Value = Rc::new(RefCell::new(init)); let __ptr = __value.as_pointer(); - a0.with_mut(|__a: &mut Option>| *__a = Some(__value)); + *a0 = Some(__value); __ptr } diff --git a/tests/unit/out/refcount/any.rs b/tests/unit/out/refcount/any.rs index cfe7dbda18..ab2fec64fe 100644 --- a/tests/unit/out/refcount/any.rs +++ b/tests/unit/out/refcount/any.rs @@ -50,8 +50,7 @@ fn main_0() -> i32 { let q: Ptr = { let __value: Value = Rc::new(RefCell::new(Point::new({ 5 }, { 6 }))); let __ptr = __value.as_pointer(); - a.as_pointer() - .with_mut(|__a: &mut Option>| *__a = Some(__value)); + (*a.borrow_mut()) = Some(__value); __ptr }; assert!((q.with(|__s| __s.x) == 5)); @@ -79,8 +78,7 @@ fn main_0() -> i32 { .read()) == 7) ); - b.as_pointer() - .with_mut(|__a: &mut Option>| *__a = None); + (*b.borrow_mut()) = None; assert!(!((*b.borrow()).is_some())); assert!( ((b.as_pointer()).with(|__a: &Option>| { From 37c4ca84bac5f1ed21b6002cfaf83ab9d0ca0ba1 Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Sat, 31 Oct 2026 14:46:22 +0000 Subject: [PATCH 14/14] Don't require metafunction classes of dependent names --- cpp2rust/rule_preprocessing/cpp_rule_indexer.cpp | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/cpp2rust/rule_preprocessing/cpp_rule_indexer.cpp b/cpp2rust/rule_preprocessing/cpp_rule_indexer.cpp index a999e2a5a8..6a7ba2e58c 100644 --- a/cpp2rust/rule_preprocessing/cpp_rule_indexer.cpp +++ b/cpp2rust/rule_preprocessing/cpp_rule_indexer.cpp @@ -78,7 +78,14 @@ void AddRequiredClasses(clang::QualType type, if (auto name = type->getAs()) { auto qualifier = name->getQualifier(); if (qualifier.getKind() == clang::NestedNameSpecifier::Kind::Type) { - AddRequiredClasses(clang::QualType(qualifier.getAsType(), 0), required); + if (auto spec = qualifier.getAsType() + ->getAs()) { + for (const auto &arg : spec->template_arguments()) { + if (arg.getKind() == clang::TemplateArgument::Type) { + AddRequiredClasses(arg.getAsType(), required); + } + } + } } return; }