From 236144b7be13c710df556c54b6aedda0a4e59ffd Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Fri, 30 Oct 2026 18:22:01 +0000 Subject: [PATCH 01/10] Add rules for vector bool reference --- rules/vector/src.cpp | 25 +++++++++++++++++++++++++ rules/vector/tgt_refcount.rs | 27 +++++++++++++++++++++++++++ rules/vector/tgt_unsafe.rs | 29 ++++++++++++++++++++++++----- 3 files changed, 76 insertions(+), 5 deletions(-) diff --git a/rules/vector/src.cpp b/rules/vector/src.cpp index b825de4088..772b5a99e0 100644 --- a/rules/vector/src.cpp +++ b/rules/vector/src.cpp @@ -260,3 +260,28 @@ typename std::vector::reference f117(const typename std::vector::reference &a0) { return typename std::vector::reference(a0); } + +template +typename std::vector::reference f118(std::vector &a0, + std::size_t a1) { + return a0.operator[](a1); +} + +template +typename std::vector::reference & +f119(typename std::vector::reference &a0, bool a1) { + return a0.operator=(a1); +} + +template +const typename std::vector::reference & +f120(const typename std::vector::reference &a0, bool a1) { + return a0.operator=(a1); +} + +template +typename std::vector::reference & +f121(typename std::vector::reference &a0, + const typename std::vector::reference &a1) { + return a0.operator=(a1); +} diff --git a/rules/vector/tgt_refcount.rs b/rules/vector/tgt_refcount.rs index 5036c81e62..d0be38c91d 100644 --- a/rules/vector/tgt_refcount.rs +++ b/rules/vector/tgt_refcount.rs @@ -5,6 +5,10 @@ use libcc2rs::*; use std::cell::RefCell; use std::rc::Rc; +fn t12() -> Ptr { + Ptr::null() +} + fn t13() -> Ptr { Ptr::null() } @@ -17,6 +21,10 @@ fn f40(a0: Ptr) -> Ptr { a0.to_end() } +fn f47(a0: Ptr) -> bool { + a0.read() +} + fn f60(a0: Ptr>, a1: RandomAccessIterator) -> RandomAccessIterator { let __it = a1; let __off = __it.index(); @@ -142,3 +150,22 @@ fn f116(a0: &mut Vec, a1: RandomAccessIterator, a2: Vec(a0: Ptr, a1: usize) -> Ptr { + a0.offset(a1 as isize) +} + +fn f119(a0: Ptr, a1: bool) -> Ptr { + a0.write(a1); + a0 +} + +fn f120(a0: Ptr, a1: bool) -> Ptr { + a0.write(a1); + a0 +} + +fn f121(a0: Ptr, a1: Ptr) -> Ptr { + a0.write(a1.read()); + a0 +} diff --git a/rules/vector/tgt_unsafe.rs b/rules/vector/tgt_unsafe.rs index 4fe004b704..43267dec1d 100644 --- a/rules/vector/tgt_unsafe.rs +++ b/rules/vector/tgt_unsafe.rs @@ -19,8 +19,8 @@ fn t11() -> Vec { Vec::new() } -fn t12() -> bool { - false +fn t12() -> *mut bool { + ::std::ptr::null_mut() } fn t13() -> *mut T1 { @@ -39,8 +39,8 @@ unsafe fn f40(a0: Vec) -> *const T1 { a0.as_ptr().add(a0.len()) } -unsafe fn f47(a0: Vec) -> Vec { - a0 +unsafe fn f47(a0: *mut bool) -> bool { + *a0 } unsafe fn f60(a0: &mut Vec, a1: UnsafeRandomAccessIterator) -> UnsafeRandomAccessIterator { @@ -250,6 +250,25 @@ unsafe fn f116(a0: &mut Vec, a1: UnsafeRandomAccessIterator, a2: Vec UnsafeRandomAccessIterator::Vec(a0.as_mut_ptr().add(__off)) } -unsafe fn f117(a0: bool) -> bool { +unsafe fn f117(a0: *mut bool) -> *mut bool { + a0 +} + +unsafe fn f118(a0: &mut Vec, a1: usize) -> *mut bool { + (&mut (a0)[a1 as usize] as *mut bool) +} + +unsafe fn f119(a0: *mut bool, a1: bool) -> *mut bool { + *a0 = a1; + a0 +} + +unsafe fn f120(a0: *mut bool, a1: bool) -> *mut bool { + *a0 = a1; + a0 +} + +unsafe fn f121(a0: *mut bool, a1: *mut bool) -> *mut bool { + *a0 = *a1; a0 } From ff75a0577211f9f3dd5a0e5cd5d3d794fc968838 Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Fri, 30 Oct 2026 18:23:19 +0000 Subject: [PATCH 02/10] Update tests --- tests/unit/out/refcount/simple_index.rs | 8 +++--- .../out/refcount/vector_bool_reference.rs | 24 +++++++++++++++++ tests/unit/out/unsafe/simple_index.rs | 8 +++--- .../unit/out/unsafe/vector_bool_reference.rs | 26 +++++++++++++++++++ tests/unit/vector_bool_reference.cpp | 11 ++++++++ 5 files changed, 69 insertions(+), 8 deletions(-) create mode 100644 tests/unit/out/refcount/vector_bool_reference.rs create mode 100644 tests/unit/out/unsafe/vector_bool_reference.rs create mode 100644 tests/unit/vector_bool_reference.cpp diff --git a/tests/unit/out/refcount/simple_index.rs b/tests/unit/out/refcount/simple_index.rs index 8a6a81e0eb..511f72aac7 100644 --- a/tests/unit/out/refcount/simple_index.rs +++ b/tests/unit/out/refcount/simple_index.rs @@ -12,10 +12,10 @@ pub fn main() { } fn main_0() -> i32 { let v: Value> = Rc::new(RefCell::new(Vec::from([true]))); - assert!(({ (*v.borrow())[0_usize] } as bool)); - let r: Value = Rc::new(RefCell::new({ (*v.borrow())[0_usize] })); - let copy: Value = Rc::new(RefCell::new((*r.borrow()))); - assert!(((*copy.borrow()) as bool)); + assert!(v.decay().offset(0_usize as isize).read()); + let mut r: Ptr = v.decay().offset(0_usize as isize); + let mut copy: Ptr = (r).clone(); + assert!(copy.read()); return 0; } pub fn __cpp2rust_init_globals() {} diff --git a/tests/unit/out/refcount/vector_bool_reference.rs b/tests/unit/out/refcount/vector_bool_reference.rs new file mode 100644 index 0000000000..cf789364a7 --- /dev/null +++ b/tests/unit/out/refcount/vector_bool_reference.rs @@ -0,0 +1,24 @@ +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 v1: Value> = Rc::new(RefCell::new(Vec::from([true]))); + let mut r1: Ptr = v1.decay().offset(0_usize as isize); + assert!((v1.decay().offset(0_usize as isize).read()) && (r1.read())); + { + r1.write(false); + (r1).clone() + }; + assert!(!(v1.decay().offset(0_usize as isize).read())); + return 0; +} +pub fn __cpp2rust_init_globals() {} diff --git a/tests/unit/out/unsafe/simple_index.rs b/tests/unit/out/unsafe/simple_index.rs index cfa91a74df..6f50b42395 100644 --- a/tests/unit/out/unsafe/simple_index.rs +++ b/tests/unit/out/unsafe/simple_index.rs @@ -14,10 +14,10 @@ pub fn main() { } unsafe fn main_0() -> i32 { let mut v: Vec = Vec::from([true]); - assert!((v[(0_usize)] as bool)); - let mut r: bool = v[(0_usize)]; - let mut copy: bool = r; - assert!((copy as bool)); + assert!(*(&mut (v)[0_usize as usize] as *mut bool)); + let mut r: *mut bool = (&mut (v)[0_usize as usize] as *mut bool); + let mut copy: *mut bool = r; + assert!(*copy); return 0; } pub unsafe fn __cpp2rust_init_globals() {} diff --git a/tests/unit/out/unsafe/vector_bool_reference.rs b/tests/unit/out/unsafe/vector_bool_reference.rs new file mode 100644 index 0000000000..e099f3b5e7 --- /dev/null +++ b/tests/unit/out/unsafe/vector_bool_reference.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 fn main() { + unsafe { + __cpp2rust_init_globals(); + std::process::exit(main_0() as i32); + } +} +unsafe fn main_0() -> i32 { + let mut v1: Vec = Vec::from([true]); + let mut r1: *mut bool = (&mut (v1)[0_usize as usize] as *mut bool); + assert!((*(&mut (v1)[0_usize as usize] as *mut bool)) && (*r1)); + { + *r1 = false; + r1 + }; + assert!(!(*(&mut (v1)[0_usize as usize] as *mut bool))); + return 0; +} +pub unsafe fn __cpp2rust_init_globals() {} diff --git a/tests/unit/vector_bool_reference.cpp b/tests/unit/vector_bool_reference.cpp new file mode 100644 index 0000000000..3d5443c5d9 --- /dev/null +++ b/tests/unit/vector_bool_reference.cpp @@ -0,0 +1,11 @@ +#include +#include + +int main() { + std::vector v1 = {true}; + std::vector::reference r1 = v1[0]; + assert(v1[0] && r1); + r1 = false; + assert(!v1[0]); + return 0; +} From 5a08c878070360784297b71d55f96484fec59f6f Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Fri, 30 Oct 2026 18:46:08 +0000 Subject: [PATCH 03/10] Look through noop casts --- cpp2rust/converter/converter.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/cpp2rust/converter/converter.cpp b/cpp2rust/converter/converter.cpp index a5bae5c706..055db2e8ff 100644 --- a/cpp2rust/converter/converter.cpp +++ b/cpp2rust/converter/converter.cpp @@ -5163,7 +5163,9 @@ std::string Converter::ConvertIRFragment( .is_cpp_ptr = is_cpp_ptr, // Whatever its spelling, a pointer in C++ is converted as one. .maps_to_rust_ptr = - !is_cpp_ptr && RuleRegistry::MapsToPointer(ctx_, arg->getType()), + !is_cpp_ptr && + RuleRegistry::MapsToPointer( + ctx_, arg->IgnoreParenNoopCasts(ctx_)->getType()), .declared_in_rule_as_rust_ptr = RuleRegistry::ParamIsPointer(ctx_, expr, arg_idx), .is_index_base = ph->is_index_base, From df68ef365f7186cd22d6a675854f87af66e82cb1 Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Fri, 30 Oct 2026 19:02:57 +0000 Subject: [PATCH 04/10] Compare src declared return type against rust declared return type --- .../converter/models/converter_refcount.cpp | 13 +++++++------ cpp2rust/converter/rules/registry.cpp | 17 +++++++++++++++++ cpp2rust/converter/rules/registry.h | 2 ++ 3 files changed, 26 insertions(+), 6 deletions(-) diff --git a/cpp2rust/converter/models/converter_refcount.cpp b/cpp2rust/converter/models/converter_refcount.cpp index 58bd0793a4..104f2d6fb7 100644 --- a/cpp2rust/converter/models/converter_refcount.cpp +++ b/cpp2rust/converter/models/converter_refcount.cpp @@ -444,12 +444,13 @@ void ConverterRefCount::CheckRuleReturnType(clang::Expr *expr) { !RuleRegistry::GetExprRule(ctx_, expr)->return_type.is_refcount_pointer) { return; } - // A reference is returned as a pointer. The library may spell the pointer - // or reference type with a typedef of its own, e.g., iterator::pointer, - // which is not what the pointee is spelled with. - auto type = GetReturnTypeOfFunction(call).getDesugaredType(ctx_); - if (auto *ref = type->getAs()) { - type = ctx_.getPointerType(ref->getPointeeType()); + auto type = RuleRegistry::GetDeclaredReturnType(expr); + if (auto ref = type->getAs()) { + auto pointee = ref->getPointeeType(); + type = pointee->isRecordType() && + RuleRegistry::MapsToRefcountPointer(ctx_, pointee) + ? pointee + : ctx_.getPointerType(pointee); } auto ret = NormalizeType(Mapper::GetReturnType(*this, expr)); auto expected = NormalizeType(ToString(type)); diff --git a/cpp2rust/converter/rules/registry.cpp b/cpp2rust/converter/rules/registry.cpp index f0e21f7812..2e4dfe65d2 100644 --- a/cpp2rust/converter/rules/registry.cpp +++ b/cpp2rust/converter/rules/registry.cpp @@ -458,6 +458,23 @@ clang::QualType GetInitType(const clang::Expr *expr) { clang::DeclarationName()); } +clang::QualType GetDeclaredReturnType(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(); + assert(function && "rule is not a function"); + auto tmpl = function->getDescribedFunctionTemplate(); + if (!tmpl) { + return function->getReturnType(); + } + clang::MultiLevelTemplateArgumentList args(tmpl, match.args, + /*Final=*/false); + clang::Sema::InstantiatingTemplate inst( + *sema_, expr->getBeginLoc(), const_cast(function)); + return sema_->SubstType(function->getReturnType(), args, expr->getBeginLoc(), + clang::DeclarationName()); +} + bool HasExprRule(const clang::FunctionDecl *decl) { auto key = RuleKeys::GetDeclKey(decl); if (!key) { diff --git a/cpp2rust/converter/rules/registry.h b/cpp2rust/converter/rules/registry.h index a84fb45611..cbe9905635 100644 --- a/cpp2rust/converter/rules/registry.h +++ b/cpp2rust/converter/rules/registry.h @@ -45,6 +45,8 @@ clang::QualType GetGenericType(clang::ASTContext &ctx, const clang::Expr *expr, clang::QualType GetConversionSourceType(clang::ASTContext &ctx, const clang::Expr *expr, unsigned to); +clang::QualType GetDeclaredReturnType(const clang::Expr *expr); + bool HasExprRule(const clang::FunctionDecl *decl); bool IsLibcPassthrough(clang::ASTContext &ctx, const clang::Expr *expr); bool ReturnsPointer(clang::ASTContext &ctx, const clang::Expr *expr); From 3862e3d14e3c610272b761c495509bce150691ea Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Fri, 30 Oct 2026 19:20:01 +0000 Subject: [PATCH 05/10] Add iterator reference rules --- rules/vector/src.cpp | 6 ++++++ rules/vector/tgt_refcount.rs | 4 ++++ rules/vector/tgt_unsafe.rs | 8 ++++++++ 3 files changed, 18 insertions(+) diff --git a/rules/vector/src.cpp b/rules/vector/src.cpp index 772b5a99e0..44826bdef1 100644 --- a/rules/vector/src.cpp +++ b/rules/vector/src.cpp @@ -30,6 +30,12 @@ using t13 = typename std::vector::reference; template > using t14 = typename std::vector::const_reference; +template > +using t15 = typename std::vector::iterator::reference; + +template > +using t16 = typename std::vector::const_iterator::reference; + std::vector f38(std::size_t n, const bool &value) { return std::vector(n, value); } diff --git a/rules/vector/tgt_refcount.rs b/rules/vector/tgt_refcount.rs index 8b5b2e8cd8..c9a4e0d7b6 100644 --- a/rules/vector/tgt_refcount.rs +++ b/rules/vector/tgt_refcount.rs @@ -9,6 +9,10 @@ fn t12() -> Ptr { Ptr::null() } +fn t15() -> Ptr { + Ptr::null() +} + fn t13() -> Ptr { Ptr::null() } diff --git a/rules/vector/tgt_unsafe.rs b/rules/vector/tgt_unsafe.rs index 69a460784c..ec80872e1e 100644 --- a/rules/vector/tgt_unsafe.rs +++ b/rules/vector/tgt_unsafe.rs @@ -27,6 +27,14 @@ fn t13() -> *mut T1 { ::std::ptr::null_mut() } +fn t15() -> *mut bool { + ::std::ptr::null_mut() +} + +fn t16() -> bool { + false +} + fn t14() -> *const T1 { ::std::ptr::null() } From 4f0a9b88dd3c6859de4ce242c580a1247d42d67d Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Fri, 30 Oct 2026 19:34:46 +0000 Subject: [PATCH 06/10] Update tests --- tests/unit/out/refcount/vector_bool_reference.rs | 7 +++++++ tests/unit/out/unsafe/vector_bool_reference.rs | 7 +++++++ tests/unit/vector_bool_reference.cpp | 4 ++++ 3 files changed, 18 insertions(+) diff --git a/tests/unit/out/refcount/vector_bool_reference.rs b/tests/unit/out/refcount/vector_bool_reference.rs index cf789364a7..0931b964cc 100644 --- a/tests/unit/out/refcount/vector_bool_reference.rs +++ b/tests/unit/out/refcount/vector_bool_reference.rs @@ -19,6 +19,13 @@ fn main_0() -> i32 { (r1).clone() }; assert!(!(v1.decay().offset(0_usize as isize).read())); + let it: Value> = Rc::new(RefCell::new(v1.decay().begin())); + let mut r2: Ptr = (*it.borrow()).iter_deref(); + { + r2.write(true); + (r2).clone() + }; + assert!(v1.decay().offset(0_usize as isize).read()); return 0; } pub fn __cpp2rust_init_globals() {} diff --git a/tests/unit/out/unsafe/vector_bool_reference.rs b/tests/unit/out/unsafe/vector_bool_reference.rs index e099f3b5e7..0819a7302b 100644 --- a/tests/unit/out/unsafe/vector_bool_reference.rs +++ b/tests/unit/out/unsafe/vector_bool_reference.rs @@ -21,6 +21,13 @@ unsafe fn main_0() -> i32 { r1 }; assert!(!(*(&mut (v1)[0_usize as usize] as *mut bool))); + let mut it: UnsafeRandomAccessIterator = v1.begin(); + let mut r2: *mut bool = it.iter_deref(); + { + *r2 = true; + r2 + }; + assert!(*(&mut (v1)[0_usize as usize] as *mut bool)); return 0; } pub unsafe fn __cpp2rust_init_globals() {} diff --git a/tests/unit/vector_bool_reference.cpp b/tests/unit/vector_bool_reference.cpp index 3d5443c5d9..8b3af32d88 100644 --- a/tests/unit/vector_bool_reference.cpp +++ b/tests/unit/vector_bool_reference.cpp @@ -7,5 +7,9 @@ int main() { assert(v1[0] && r1); r1 = false; assert(!v1[0]); + auto it = v1.begin(); + auto r2 = *it; + r2 = true; + assert(v1[0]); return 0; } From 70f1fdd4b50496c43104c485dce616107f55f613 Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Fri, 30 Oct 2026 19:36:37 +0000 Subject: [PATCH 07/10] Update rules --- rules/vector/src.cpp | 6 +++--- rules/vector/tgt_refcount.rs | 4 ++-- rules/vector/tgt_unsafe.rs | 6 +++--- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/rules/vector/src.cpp b/rules/vector/src.cpp index 44826bdef1..b132451153 100644 --- a/rules/vector/src.cpp +++ b/rules/vector/src.cpp @@ -22,7 +22,7 @@ using t10 = typename std::vector::value_type; template using t11 = typename std::vector>::value_type; -using t12 = std::vector::reference; +template using t6 = typename std::vector::reference; template > using t13 = typename std::vector::reference; @@ -30,10 +30,10 @@ using t13 = typename std::vector::reference; template > using t14 = typename std::vector::const_reference; -template > +template using t15 = typename std::vector::iterator::reference; -template > +template using t16 = typename std::vector::const_iterator::reference; std::vector f38(std::size_t n, const bool &value) { diff --git a/rules/vector/tgt_refcount.rs b/rules/vector/tgt_refcount.rs index c9a4e0d7b6..2bb7fce9d8 100644 --- a/rules/vector/tgt_refcount.rs +++ b/rules/vector/tgt_refcount.rs @@ -5,11 +5,11 @@ use libcc2rs::*; use std::cell::RefCell; use std::rc::Rc; -fn t12() -> Ptr { +fn t6() -> Ptr { Ptr::null() } -fn t15() -> Ptr { +fn t15() -> Ptr { Ptr::null() } diff --git a/rules/vector/tgt_unsafe.rs b/rules/vector/tgt_unsafe.rs index ec80872e1e..2b92694f4d 100644 --- a/rules/vector/tgt_unsafe.rs +++ b/rules/vector/tgt_unsafe.rs @@ -19,7 +19,7 @@ fn t11() -> Vec { Vec::new() } -fn t12() -> *mut bool { +fn t6() -> *mut bool { ::std::ptr::null_mut() } @@ -27,11 +27,11 @@ fn t13() -> *mut T1 { ::std::ptr::null_mut() } -fn t15() -> *mut bool { +fn t15() -> *mut bool { ::std::ptr::null_mut() } -fn t16() -> bool { +fn t16() -> bool { false } From ae27d9289cee554e794a926c481e30b633326804 Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Fri, 30 Oct 2026 20:08:37 +0000 Subject: [PATCH 08/10] Deduce template arg from operands or from default arguments --- cpp2rust/converter/rules/decls.cpp | 110 ++++++++++++++++++++++ cpp2rust/converter/rules/decls.h | 4 + cpp2rust/converter/rules/expr_matcher.cpp | 43 ++++++--- 3 files changed, 146 insertions(+), 11 deletions(-) diff --git a/cpp2rust/converter/rules/decls.cpp b/cpp2rust/converter/rules/decls.cpp index 9de6b42e78..c2b9a51529 100644 --- a/cpp2rust/converter/rules/decls.cpp +++ b/cpp2rust/converter/rules/decls.cpp @@ -7,6 +7,7 @@ #include #include #include +#include #include #include @@ -105,6 +106,115 @@ DeduceFromType(clang::Sema &sema, clang::TemplateParameterList *params, return std::vector(deduced.begin(), deduced.end()); } +namespace { + +const clang::TemplateSpecializationType * +getMemberParentClass(clang::QualType pattern, clang::ClassTemplateDecl *&tmpl) { + auto member = pattern->getAs(); + if (!member || member->getQualifier().getKind() != + clang::NestedNameSpecifier::Kind::Type) { + return nullptr; + } + auto spec = member->getQualifier() + .getAsType() + ->getAs(); + tmpl = spec ? llvm::dyn_cast_or_null( + spec->getTemplateName().getAsTemplateDecl()) + : nullptr; + return tmpl && !tmpl->getTemplateParameters()->hasParameterPack() ? spec + : nullptr; +} + +std::optional> +getDefaultedArgumentsOfRule(clang::Sema &sema, const clang::TemplateDecl *rule, + clang::ClassTemplateDecl *tmpl, + const clang::TemplateSpecializationType *spec) { + auto params = rule->getTemplateParameters(); + auto loc = params->getTemplateLoc(); + clang::TemplateArgumentListInfo written(loc, loc); + llvm::SmallVector, 4> positions(params->size()); + for (auto [i, arg] : llvm::enumerate(spec->template_arguments())) { + auto parm = arg.getKind() == clang::TemplateArgument::Type + ? arg.getAsType()->getAs() + : nullptr; + if (parm && parm->getDepth() == params->getDepth() && + !parm->isParameterPack()) { + if (positions[parm->getIndex()]) { + return std::nullopt; + } + positions[parm->getIndex()] = i; + continue; + } + if (arg.isDependent() || written.size() != i) { + return std::nullopt; + } + written.addArgument(sema.getTrivialTemplateArgumentLoc( + arg, arg.getNonTypeTemplateArgumentType(), loc)); + } + if (!llvm::all_of(positions, [](const std::optional &position) { + return position.has_value(); + })) { + return std::nullopt; + } + Trial trial(sema); + clang::Sema::CheckTemplateArgumentInfo info; + if (sema.CheckTemplateArgumentList(tmpl, loc, written, /*DefaultArgs=*/{}, + /*PartialTemplateArgs=*/false, info) || + trial.failed()) { + return std::nullopt; + } + std::vector args(params->size()); + for (unsigned i = 0; i < params->size(); ++i) { + args[i] = info.CanonicalConverted[*positions[i]]; + } + return args; +} + +bool resolvesTo(clang::Sema &sema, const clang::TemplateDecl *rule, + llvm::ArrayRef args, + clang::QualType pattern, clang::QualType actual) { + Trial trial(sema); + clang::MultiLevelTemplateArgumentList bound( + const_cast(rule), args, /*Final=*/true); + auto resolved = sema.SubstType(pattern, bound, + rule->getTemplateParameters()->getTemplateLoc(), + clang::DeclarationName()); + return !resolved.isNull() && !trial.failed() && + sema.Context.hasSameUnqualifiedType(resolved, actual); +} + +} // namespace + +std::optional> +DeduceMemberType(clang::Sema &sema, const clang::TemplateDecl *rule, + clang::QualType pattern, clang::QualType use) { + auto &ctx = sema.Context; + clang::ClassTemplateDecl *tmpl = nullptr; + auto spec = getMemberParentClass(pattern, tmpl); + if (!spec) { + return std::nullopt; + } + auto member = pattern->getAs(); + auto params = rule->getTemplateParameters(); + if (auto alias = SpelledTypedef(ctx, use)) { + if (alias->getIdentifier() != member->getIdentifier()) { + return std::nullopt; + } + auto owner = llvm::dyn_cast(alias->getDeclContext()); + if (auto args = owner ? DeduceFromType(sema, params, + clang::QualType(spec, 0), + ctx.getCanonicalTagType(owner)) + : std::nullopt) { + return args; + } + } + auto args = getDefaultedArgumentsOfRule(sema, rule, tmpl, spec); + if (!args || !resolvesTo(sema, rule, *args, pattern, use)) { + return std::nullopt; + } + return args; +} + bool IsInternal(const clang::NamedDecl *decl) { if (decl->getAccess() == clang::AS_private || decl->getAccess() == clang::AS_protected) { diff --git a/cpp2rust/converter/rules/decls.h b/cpp2rust/converter/rules/decls.h index 934200e5bc..018aa069ff 100644 --- a/cpp2rust/converter/rules/decls.h +++ b/cpp2rust/converter/rules/decls.h @@ -46,6 +46,10 @@ std::optional> DeduceFromType(clang::Sema &sema, clang::TemplateParameterList *params, clang::QualType pattern, clang::QualType actual); +std::optional> +DeduceMemberType(clang::Sema &sema, const clang::TemplateDecl *rule, + clang::QualType pattern, clang::QualType use); + bool IsInternal(const clang::NamedDecl *decl); clang::QualType PeelSugar(clang::ASTContext &ctx, clang::QualType type); diff --git a/cpp2rust/converter/rules/expr_matcher.cpp b/cpp2rust/converter/rules/expr_matcher.cpp index 603f99c246..46ffcc8576 100644 --- a/cpp2rust/converter/rules/expr_matcher.cpp +++ b/cpp2rust/converter/rules/expr_matcher.cpp @@ -252,6 +252,27 @@ substituteSpecializationInRuleTmpl(clang::Sema &sema, return result.isInvalid() || trial.failed() ? nullptr : result.get(); } +void deduceMemberTypesFromOperands(clang::Sema &sema, + clang::FunctionTemplateDecl *rule, + const clang::Expr *use, + std::vector &deduced) { + auto pattern = rule->getTemplatedDecl(); + auto operands = getOperands(sema.Context, use, pattern->getNumParams()); + for (unsigned i = 0; i < operands.size() && i < pattern->getNumParams(); + ++i) { + auto param = pattern->getParamDecl(i)->getType().getNonReferenceType(); + auto operand = operands[i]->getType().getNonReferenceType(); + auto args = param->getAs() + ? RuleDecls::DeduceMemberType(sema, rule, param, operand) + : std::nullopt; + 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) { @@ -266,19 +287,19 @@ clang::TemplateArgumentListInfo getResultArgs(clang::Sema &sema, return explicit_args; } auto params = rule->getTemplateParameters(); - auto deduced = RuleDecls::DeduceFromType( - sema, params, rule->getTemplatedDecl()->getReturnType(), - RuleDecls::SkipImplicit(use) - ->getType() - .getCanonicalType() - .getUnqualifiedType()); - if (!deduced) { - return explicit_args; - } + auto deduced = + RuleDecls::DeduceFromType(sema, params, + rule->getTemplatedDecl()->getReturnType(), + RuleDecls::SkipImplicit(use) + ->getType() + .getCanonicalType() + .getUnqualifiedType()) + .value_or(std::vector(params->size())); + deduceMemberTypesFromOperands(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); - if (param->isParameterPack() || (*deduced)[i].isNull()) { + if (param->isParameterPack() || deduced[i].isNull()) { break; } clang::QualType nttp_type; @@ -286,7 +307,7 @@ clang::TemplateArgumentListInfo getResultArgs(clang::Sema &sema, nttp_type = nttp->getType(); } explicit_args.addArgument( - sema.getTrivialTemplateArgumentLoc((*deduced)[i], nttp_type, loc)); + sema.getTrivialTemplateArgumentLoc(deduced[i], nttp_type, loc)); } return explicit_args; } From 34c4d4ddc057d1f9a41c99e1257568c56c223cd7 Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Fri, 30 Oct 2026 20:10:59 +0000 Subject: [PATCH 09/10] Fix indirection hops for dependent names --- cpp2rust/converter/rules/registry.cpp | 5 +++- cpp2rust/converter/rules/rule_keys.cpp | 31 ++++++++++++++++++++--- cpp2rust/converter/rules/rule_keys.h | 3 ++- cpp2rust/converter/rules/type_matcher.cpp | 18 ++----------- 4 files changed, 35 insertions(+), 22 deletions(-) diff --git a/cpp2rust/converter/rules/registry.cpp b/cpp2rust/converter/rules/registry.cpp index 2e4dfe65d2..5b98e5e03e 100644 --- a/cpp2rust/converter/rules/registry.cpp +++ b/cpp2rust/converter/rules/registry.cpp @@ -182,7 +182,7 @@ void addRule(clang::ASTContext &ctx, const std::string &dir, } assert(rule && "rule has no target"); if (!name.starts_with('f')) { - auto keys = RuleKeys::GetTypeKeys(decl); + auto keys = RuleKeys::GetTypeKeys(*sema_, decl); assert(!keys.empty() && "type rule has no key"); for (auto key : keys) { types_.emplace( @@ -302,6 +302,9 @@ TypeRuleMatch findType(clang::QualType use) { if (!templates && llvm::isa(it->second.decl)) { continue; } + if (best.rule_decl && best.rule_decl->decl == it->second.decl) { + continue; + } auto match = AstMatcher::MatchType(*sema_, type, it->second.decl); if (!match) { continue; diff --git a/cpp2rust/converter/rules/rule_keys.cpp b/cpp2rust/converter/rules/rule_keys.cpp index 9c71050d51..1e3ae2f78a 100644 --- a/cpp2rust/converter/rules/rule_keys.cpp +++ b/cpp2rust/converter/rules/rule_keys.cpp @@ -94,7 +94,9 @@ std::vector resolveMemberType(clang::Sema &sema, auto tmpl = spec ? llvm::dyn_cast_or_null( spec->getTemplateName().getAsTemplateDecl()) : nullptr; - assert(tmpl && "member type is not qualified by a class template"); + if (!tmpl) { + continue; + } for (auto record : matchingDefinitions(sema, tmpl, spec)) { if (!record->hasDefinition()) { continue; @@ -266,7 +268,8 @@ std::optional GetTypeKey(clang::QualType type) { return type.getTypePtr(); } -std::vector GetTypeKeys(const clang::NamedDecl *rule) { +std::vector GetTypeKeys(clang::Sema &sema, + const clang::NamedDecl *rule) { auto alias = llvm::dyn_cast(rule); if (auto tmpl = llvm::dyn_cast(rule)) { alias = tmpl->getTemplatedDecl(); @@ -279,9 +282,29 @@ std::vector GetTypeKeys(const clang::NamedDecl *rule) { if (auto member = pattern->getAs()) { auto qualifier = member->getQualifier().getAsType(); assert(qualifier && "member type is not qualified by a type"); - auto parent = getParent(clang::QualType(qualifier, 0)); + auto outermost = qualifier; + while (auto outer = outermost->getAs()) { + outermost = outer->getQualifier().getAsType(); + assert(outermost && "member type is not qualified by a type"); + } + auto parent = getParent(clang::QualType(outermost, 0)); assert(parent && "type rule has no key"); - return {parent}; + std::vector keys{parent}; + auto spec = qualifier->getAs(); + if (outermost == qualifier && spec && + llvm::isa_and_nonnull( + spec->getTemplateName().getAsTemplateDecl())) { + for (auto resolved : resolveMemberType(sema, pattern)) { + auto key = GetTypeKey(resolved); + auto decl = key ? std::get_if(&*key) : nullptr; + if (decl && *decl && + RuleDecls::IsInternal(llvm::cast(*decl)) && + !llvm::is_contained(keys, *key)) { + keys.push_back(*key); + } + } + } + return keys; } auto key = GetTypeKey(pattern); assert(key && "type rule has no key"); diff --git a/cpp2rust/converter/rules/rule_keys.h b/cpp2rust/converter/rules/rule_keys.h index a29d8fe7a3..bc8cb36315 100644 --- a/cpp2rust/converter/rules/rule_keys.h +++ b/cpp2rust/converter/rules/rule_keys.h @@ -54,7 +54,8 @@ std::optional GetDeclKey(const clang::NamedDecl *decl); std::optional GetTypeKey(clang::QualType type); -std::vector GetTypeKeys(const clang::NamedDecl *rule); +std::vector GetTypeKeys(clang::Sema &sema, + const clang::NamedDecl *rule); std::vector GetTypeKeys(clang::ASTContext &ctx, clang::QualType use); } // namespace cpp2rust::RuleKeys diff --git a/cpp2rust/converter/rules/type_matcher.cpp b/cpp2rust/converter/rules/type_matcher.cpp index 84b6ba7a87..98cf75ca71 100644 --- a/cpp2rust/converter/rules/type_matcher.cpp +++ b/cpp2rust/converter/rules/type_matcher.cpp @@ -80,22 +80,8 @@ std::optional MatchType(clang::Sema &sema, clang::QualType use, return TypeMatch{}; } auto params = tmpl->getTemplateParameters(); - if (auto member = pattern->getAs()) { - auto qualifier = member->getQualifier().getAsType(); - auto typedef_type = - llvm::dyn_cast(spelled.getTypePtr()); - if (!qualifier || !typedef_type || - typedef_type->getDecl()->getIdentifier() != member->getIdentifier()) { - return std::nullopt; - } - auto owner = llvm::dyn_cast( - typedef_type->getDecl()->getDeclContext()); - if (!owner) { - return std::nullopt; - } - auto args = - RuleDecls::DeduceFromType(sema, params, clang::QualType(qualifier, 0), - ctx.getCanonicalTagType(owner)); + if (pattern->getAs()) { + auto args = RuleDecls::DeduceMemberType(sema, tmpl, pattern, spelled); if (args && (args = completeArgs(sema, tmpl, *std::move(args)))) { return TypeMatch{*std::move(args)}; } From c5accfe809e9cb6c190f1e1ec7c86ea52d04af8d Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Fri, 30 Oct 2026 20:54:09 +0000 Subject: [PATCH 10/10] format --- cpp2rust/converter/rules/decls.cpp | 18 +++++++++--------- cpp2rust/converter/rules/expr_matcher.cpp | 7 +++---- 2 files changed, 12 insertions(+), 13 deletions(-) diff --git a/cpp2rust/converter/rules/decls.cpp b/cpp2rust/converter/rules/decls.cpp index c2b9a51529..0622645711 100644 --- a/cpp2rust/converter/rules/decls.cpp +++ b/cpp2rust/converter/rules/decls.cpp @@ -127,8 +127,8 @@ getMemberParentClass(clang::QualType pattern, clang::ClassTemplateDecl *&tmpl) { std::optional> getDefaultedArgumentsOfRule(clang::Sema &sema, const clang::TemplateDecl *rule, - clang::ClassTemplateDecl *tmpl, - const clang::TemplateSpecializationType *spec) { + clang::ClassTemplateDecl *tmpl, + const clang::TemplateSpecializationType *spec) { auto params = rule->getTemplateParameters(); auto loc = params->getTemplateLoc(); clang::TemplateArgumentListInfo written(loc, loc); @@ -176,9 +176,9 @@ bool resolvesTo(clang::Sema &sema, const clang::TemplateDecl *rule, Trial trial(sema); clang::MultiLevelTemplateArgumentList bound( const_cast(rule), args, /*Final=*/true); - auto resolved = sema.SubstType(pattern, bound, - rule->getTemplateParameters()->getTemplateLoc(), - clang::DeclarationName()); + auto resolved = sema.SubstType( + pattern, bound, rule->getTemplateParameters()->getTemplateLoc(), + clang::DeclarationName()); return !resolved.isNull() && !trial.failed() && sema.Context.hasSameUnqualifiedType(resolved, actual); } @@ -201,10 +201,10 @@ DeduceMemberType(clang::Sema &sema, const clang::TemplateDecl *rule, return std::nullopt; } auto owner = llvm::dyn_cast(alias->getDeclContext()); - if (auto args = owner ? DeduceFromType(sema, params, - clang::QualType(spec, 0), - ctx.getCanonicalTagType(owner)) - : std::nullopt) { + if (auto args = owner + ? DeduceFromType(sema, params, clang::QualType(spec, 0), + ctx.getCanonicalTagType(owner)) + : std::nullopt) { return args; } } diff --git a/cpp2rust/converter/rules/expr_matcher.cpp b/cpp2rust/converter/rules/expr_matcher.cpp index 46ffcc8576..e37369771e 100644 --- a/cpp2rust/converter/rules/expr_matcher.cpp +++ b/cpp2rust/converter/rules/expr_matcher.cpp @@ -252,10 +252,9 @@ substituteSpecializationInRuleTmpl(clang::Sema &sema, return result.isInvalid() || trial.failed() ? nullptr : result.get(); } -void deduceMemberTypesFromOperands(clang::Sema &sema, - clang::FunctionTemplateDecl *rule, - const clang::Expr *use, - std::vector &deduced) { +void deduceMemberTypesFromOperands( + clang::Sema &sema, clang::FunctionTemplateDecl *rule, + const clang::Expr *use, std::vector &deduced) { auto pattern = rule->getTemplatedDecl(); auto operands = getOperands(sema.Context, use, pattern->getNumParams()); for (unsigned i = 0; i < operands.size() && i < pattern->getNumParams();