From ab378151f7ec5eea4f15d2c12775c7677d76d7c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Gouveia?= Date: Fri, 3 Jul 2026 00:24:47 +0000 Subject: [PATCH] Add support for dependent declaration names in rules --- cpp2rust/cpp_rule_preprocessor.cpp | 59 +++++++----- rules/functional/src.cpp | 18 ++++ rules/functional/tgt_refcount.rs | 20 ++++ rules/functional/tgt_unsafe.rs | 18 ++++ rules/src/modules.rs | 4 + tests/unit/out/refcount/reference_wrapper.rs | 98 ++++++++++++++++++++ tests/unit/out/unsafe/reference_wrapper.rs | 91 ++++++++++++++++++ tests/unit/reference_wrapper.cpp | 44 +++++++++ 8 files changed, 328 insertions(+), 24 deletions(-) create mode 100644 rules/functional/src.cpp create mode 100644 rules/functional/tgt_refcount.rs create mode 100644 rules/functional/tgt_unsafe.rs create mode 100644 tests/unit/out/refcount/reference_wrapper.rs create mode 100644 tests/unit/out/unsafe/reference_wrapper.rs create mode 100644 tests/unit/reference_wrapper.cpp diff --git a/cpp2rust/cpp_rule_preprocessor.cpp b/cpp2rust/cpp_rule_preprocessor.cpp index 463b7423..81bc3c1e 100644 --- a/cpp2rust/cpp_rule_preprocessor.cpp +++ b/cpp2rust/cpp_rule_preprocessor.cpp @@ -469,21 +469,13 @@ class Callback : public clang::ast_matchers::MatchFinder::MatchCallback { return ctx.getCanonicalType(spec); } - clang::QualType - getDefaultArg(clang::TemplateDecl *decl, - const clang::TemplateTypeParmDecl *parm, - llvm::ArrayRef currentArgs) { - clang::QualType type = parm->getDefaultArgument().getArgument().getAsType(); - if (!type->isDependentType()) { - return type; - } - - clang::Sema::InstantiatingTemplate Inst(*sema_, loc_, decl); + clang::QualType getSubstType(const clang::Sema::InstantiatingTemplate &Inst, + clang::QualType type, + llvm::ArrayRef args) { assert(!Inst.isInvalid() && "Invalid instantiation context"); - clang::MultiLevelTemplateArgumentList mtal; mtal.setKind(clang::TemplateSubstitutionKind::Rewrite); - mtal.addOuterTemplateArguments(currentArgs); + mtal.addOuterTemplateArguments(args); clang::TypeSourceInfo *tsi = sema_->SubstType(sema_->Context.getTrivialTypeSourceInfo(type), mtal, @@ -492,6 +484,19 @@ class Callback : public clang::ast_matchers::MatchFinder::MatchCallback { return tsi->getType(); } + clang::QualType + getDefaultArg(clang::TemplateDecl *decl, + const clang::TemplateTypeParmDecl *parm, + llvm::ArrayRef currentArgs) { + clang::QualType type = parm->getDefaultArgument().getArgument().getAsType(); + if (!type->isDependentType()) { + return type; + } + + const clang::Sema::InstantiatingTemplate Inst(*sema_, loc_, decl); + return getSubstType(Inst, type, currentArgs); + } + clang::VarDecl *createVarDecl(clang::QualType type, llvm::StringRef name, clang::StorageClass sclass = clang::SC_None) { clang::ASTContext &ctx = sema_->Context; @@ -553,6 +558,10 @@ class Callback : public clang::ast_matchers::MatchFinder::MatchCallback { } else if (const auto *nttp = llvm::dyn_cast(param)) { clang::QualType type = nttp->getType(); + if (type->isDependentType()) { + const clang::Sema::InstantiatingTemplate Inst(*sema_, loc_, decl); + type = getSubstType(Inst, type, out); + } clang::DeclRefExpr *var = createConstexprDeclRefExpr(type, param->getName()); out.emplace_back(var, true); @@ -713,7 +722,7 @@ class Callback : public clang::ast_matchers::MatchFinder::MatchCallback { clang::TemplateArgumentListInfo explicitTArgs; { - clang::Sema::InstantiatingTemplate Inst(*sema_, loc_, decl); + const clang::Sema::InstantiatingTemplate Inst(*sema_, loc_, decl); assert(!Inst.isInvalid() && "Invalid instantiation context"); for (const auto &argloc : lookup.explicitArgs) { const auto &arg = argloc.getArgument(); @@ -724,16 +733,8 @@ class Callback : public clang::ast_matchers::MatchFinder::MatchCallback { clang::TemplateArgument inst; if (arg.getKind() == clang::TemplateArgument::Type) { - clang::QualType type = arg.getAsType(); - clang::MultiLevelTemplateArgumentList mtal; - mtal.setKind(clang::TemplateSubstitutionKind::Rewrite); - mtal.addOuterTemplateArguments(ruleTArgs); - - clang::TypeSourceInfo *tsi = - sema_->SubstType(sema_->Context.getTrivialTypeSourceInfo(type), - mtal, loc_, clang::DeclarationName()); - assert(tsi && "Template argument type instantiation failed"); - inst = clang::TemplateArgument(tsi->getType()); + inst = clang::TemplateArgument( + getSubstType(Inst, arg.getAsType(), ruleTArgs)); } else if (arg.getKind() == clang::TemplateArgument::Expression) { if (auto *expr = llvm::dyn_cast(arg.getAsExpr())) { @@ -753,7 +754,17 @@ class Callback : public clang::ast_matchers::MatchFinder::MatchCallback { } } - clang::DeclarationName &name = lookup.name; + clang::DeclarationName name = lookup.name; + if (clang::QualType nameType = name.getCXXNameType(); + !nameType.isNull() && nameType->isDependentType()) { + const clang::Sema::InstantiatingTemplate Inst(*sema_, loc_, decl); + assert(!Inst.isInvalid() && "Invalid instantiation context"); + clang::MultiLevelTemplateArgumentList mtal; + mtal.setKind(clang::TemplateSubstitutionKind::Rewrite); + mtal.addOuterTemplateArguments(ruleTArgs); + name = sema_->SubstDeclarationNameInfo({name, loc_}, mtal).getName(); + } + clang::OverloadCandidateSet candidates(loc_, csk); switch (lookup.kind) { case LookupKind::RegularName: diff --git a/rules/functional/src.cpp b/rules/functional/src.cpp new file mode 100644 index 00000000..f788ba08 --- /dev/null +++ b/rules/functional/src.cpp @@ -0,0 +1,18 @@ +// Copyright (c) 2022-present INESC-ID. +// Distributed under the MIT license that can be found in the LICENSE file. + +#include + +template using t1 = std::reference_wrapper; + +template std::reference_wrapper f1(T1 &a0) { + return std::reference_wrapper(a0); +} + +template T1 &f2(const std::reference_wrapper &a0) { + return a0.operator T1 &(); +} + +template T1 &f3(const std::reference_wrapper &a0) { + return a0.get(); +} diff --git a/rules/functional/tgt_refcount.rs b/rules/functional/tgt_refcount.rs new file mode 100644 index 00000000..49193bcf --- /dev/null +++ b/rules/functional/tgt_refcount.rs @@ -0,0 +1,20 @@ +// Copyright (c) 2022-present INESC-ID. +// Distributed under the MIT license that can be found in the LICENSE file. + +use libcc2rs::*; + +fn t1() -> Ptr { + Ptr::null() +} + +fn f1(a0: Ptr) -> Ptr { + a0 +} + +fn f2(a0: Ptr) -> Ptr { + a0.clone() +} + +fn f3(a0: Ptr) -> Ptr { + a0.clone() +} diff --git a/rules/functional/tgt_unsafe.rs b/rules/functional/tgt_unsafe.rs new file mode 100644 index 00000000..973000c4 --- /dev/null +++ b/rules/functional/tgt_unsafe.rs @@ -0,0 +1,18 @@ +// Copyright (c) 2022-present INESC-ID. +// Distributed under the MIT license that can be found in the LICENSE file. + +fn t1() -> *mut T1 { + Default::default() +} + +unsafe fn f1(a0: *mut T1) -> *mut T1 { + a0 +} + +unsafe fn f2(a0: *mut T1) -> *mut T1 { + a0 +} + +unsafe fn f3(a0: *mut T1) -> *mut T1 { + a0 +} diff --git a/rules/src/modules.rs b/rules/src/modules.rs index 43f66902..b05d690c 100644 --- a/rules/src/modules.rs +++ b/rules/src/modules.rs @@ -48,6 +48,10 @@ pub mod fnmatch_tgt_unsafe; pub mod fstream_tgt_refcount; #[path = r#"../fstream/tgt_unsafe.rs"#] pub mod fstream_tgt_unsafe; +#[path = r#"../functional/tgt_refcount.rs"#] +pub mod functional_tgt_refcount; +#[path = r#"../functional/tgt_unsafe.rs"#] +pub mod functional_tgt_unsafe; #[path = r#"../ifaddrs/tgt_unsafe.rs"#] pub mod ifaddrs_tgt_unsafe; #[path = r#"../initializer_list/tgt_unsafe.rs"#] diff --git a/tests/unit/out/refcount/reference_wrapper.rs b/tests/unit/out/refcount/reference_wrapper.rs new file mode 100644 index 00000000..2a1d94ae --- /dev/null +++ b/tests/unit/out/refcount/reference_wrapper.rs @@ -0,0 +1,98 @@ +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(Default)] +pub struct Point { + pub x: Value, + pub y: Value, +} +impl Clone for Point { + fn clone(&self) -> Self { + let mut this = Self { + x: Rc::new(RefCell::new((*self.x.borrow()))), + y: Rc::new(RefCell::new((*self.y.borrow()))), + }; + this + } +} +impl ByteRepr for Point { + fn byte_size() -> usize { + 8 + } + fn to_bytes(&self, buf: &mut [u8]) { + (*self.x.borrow()).to_bytes(&mut buf[0..4]); + (*self.y.borrow()).to_bytes(&mut buf[4..8]); + } + fn from_bytes(buf: &[u8]) -> Self { + Self { + x: Rc::new(RefCell::new(::from_bytes(&buf[0..4]))), + y: Rc::new(RefCell::new(::from_bytes(&buf[4..8]))), + } + } +} +pub fn set_0(ref_: Ptr, val: i32) { + let ref_: Value> = Rc::new(RefCell::new(ref_)); + let val: Value = Rc::new(RefCell::new(val)); + (*ref_.borrow()).clone().write((*val.borrow())); +} +pub fn read_1(ref_: Ptr) -> i32 { + let ref_: Value> = Rc::new(RefCell::new(ref_)); + let r: Ptr = (*ref_.borrow()).clone(); + return (r.read()); +} +pub fn main() { + std::process::exit(main_0()); +} +fn main_0() -> i32 { + let i1: Value = Rc::new(RefCell::new(10)); + let ref_1: Value> = Rc::new(RefCell::new(i1.as_pointer())); + (*ref_1.borrow()).clone().write(20); + let i2: Ptr = (*ref_1.borrow()).clone(); + { + let _ptr = i2.clone(); + _ptr.write(_ptr.read() + 5) + }; + write!(libcc2rs::cout(), "{:}\n", (*i1.borrow()),); + let i3: Value = Rc::new(RefCell::new(1)); + let i4: Value = Rc::new(RefCell::new(2)); + let ref_3: Value> = Rc::new(RefCell::new(i3.as_pointer())); + let ref_4: Value> = Rc::new(RefCell::new(i4.as_pointer())); + let __rhs = ((*ref_4.borrow()).clone().read()); + (*ref_3.borrow()).clone().write(__rhs); + write!( + libcc2rs::cout(), + "{:} {:}\n", + (*i3.borrow()), + (*i4.borrow()), + ); + ({ set_0((*ref_1.borrow()).clone(), 99) }); + write!( + libcc2rs::cout(), + "{:} {:}\n", + (*i1.borrow()), + ({ read_1((*ref_1.borrow()).clone(),) }), + ); + let point: Value = Rc::new(RefCell::new(Point { + x: Rc::new(RefCell::new(3)), + y: Rc::new(RefCell::new(4)), + })); + let point_ref: Value> = Rc::new(RefCell::new(point.as_pointer())); + (*(*(*point_ref.borrow()).clone().upgrade().deref()) + .x + .borrow_mut()) = 30; + (*(*(*point_ref.borrow()).clone().upgrade().deref()) + .y + .borrow_mut()) = 40; + write!( + libcc2rs::cout(), + "{:} {:}\n", + (*(*point.borrow()).x.borrow()), + (*(*point.borrow()).y.borrow()), + ); + return 0; +} diff --git a/tests/unit/out/unsafe/reference_wrapper.rs b/tests/unit/out/unsafe/reference_wrapper.rs new file mode 100644 index 00000000..3fb76647 --- /dev/null +++ b/tests/unit/out/unsafe/reference_wrapper.rs @@ -0,0 +1,91 @@ +extern crate libc; +use libc::*; +extern crate libcc2rs; +use libcc2rs::*; +use std::collections::BTreeMap; +use std::io::{Read, Seek, Write}; +use std::os::fd::{AsFd, FromRawFd, IntoRawFd}; +use std::rc::Rc; +#[repr(C)] +#[derive(Copy, Clone, Default)] +pub struct Point { + pub x: i32, + pub y: i32, +} +pub unsafe fn set_0(mut ref_: *mut i32, mut val: i32) { + (*ref_) = val; +} +pub unsafe fn read_1(mut ref_: *mut i32) -> i32 { + let r: *mut i32 = ref_; + return (*r); +} +pub fn main() { + unsafe { + std::process::exit(main_0() as i32); + } +} +unsafe fn main_0() -> i32 { + let mut i1: i32 = 10; + let mut ref_1: *mut i32 = &mut i1; + (*ref_1) = 20; + let i2: *mut i32 = ref_1; + (*i2) += 5; + write!( + std::fs::File::from_raw_fd( + std::io::stdout() + .as_fd() + .try_clone_to_owned() + .unwrap() + .into_raw_fd(), + ), + "{:}\n", + i1, + ); + let mut i3: i32 = 1; + let mut i4: i32 = 2; + let mut ref_3: *mut i32 = &mut i3; + let mut ref_4: *mut i32 = &mut i4; + (*ref_3) = (*ref_4); + write!( + std::fs::File::from_raw_fd( + std::io::stdout() + .as_fd() + .try_clone_to_owned() + .unwrap() + .into_raw_fd(), + ), + "{:} {:}\n", + i3, + i4, + ); + (unsafe { set_0(ref_1.clone(), 99) }); + write!( + std::fs::File::from_raw_fd( + std::io::stdout() + .as_fd() + .try_clone_to_owned() + .unwrap() + .into_raw_fd(), + ), + "{:} {:}\n", + i1, + (unsafe { read_1(ref_1.clone(),) }), + ); + let mut point: Point = Point { x: 3, y: 4 }; + let mut point_ref: *mut Point = &mut point; + (*point_ref).x = 30; + (*point_ref).y = 40; + write!( + std::fs::File::from_raw_fd( + std::io::stdout() + .as_fd() + .try_clone_to_owned() + .unwrap() + .into_raw_fd(), + ), + "{:} {:}\n", + point.x, + point.y, + ); + return 0; +} diff --git a/tests/unit/reference_wrapper.cpp b/tests/unit/reference_wrapper.cpp new file mode 100644 index 00000000..77cfabb5 --- /dev/null +++ b/tests/unit/reference_wrapper.cpp @@ -0,0 +1,44 @@ +// Copyright (c) 2022-present INESC-ID. +// Distributed under the MIT license that can be found in the LICENSE file. + +#include +#include + +struct Point { + int x; + int y; +}; + +void set(std::reference_wrapper ref, int val) { ref.get() = val; } + +int read(std::reference_wrapper ref) { + int &r = ref.operator int &(); + return r; +} + +int main() { + int i1 = 10; + std::reference_wrapper ref_1(i1); + ref_1.get() = 20; + int &i2 = ref_1.operator int &(); + i2 += 5; + std::cout << i1 << '\n'; + + int i3 = 1; + int i4 = 2; + std::reference_wrapper ref_3(i3); + std::reference_wrapper ref_4(i4); + ref_3.get() = ref_4.get(); + std::cout << i3 << ' ' << i4 << '\n'; + + set(ref_1, 99); + std::cout << i1 << ' ' << read(ref_1) << '\n'; + + Point point{3, 4}; + std::reference_wrapper point_ref(point); + point_ref.get().x = 30; + point_ref.operator Point &().y = 40; + std::cout << point.x << ' ' << point.y << '\n'; + + return 0; +}