From 49eeab43817f781086a352f68cd32e0d0d569345 Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Fri, 17 Apr 2026 09:50:40 +0100 Subject: [PATCH 01/17] Add FnPtr in libcc2rs --- libcc2rs/src/fn_ptr.rs | 172 +++++++++++++++++++++++++++++++++++++++++ libcc2rs/src/lib.rs | 3 + 2 files changed, 175 insertions(+) create mode 100644 libcc2rs/src/fn_ptr.rs diff --git a/libcc2rs/src/fn_ptr.rs b/libcc2rs/src/fn_ptr.rs new file mode 100644 index 00000000..07a5f0d5 --- /dev/null +++ b/libcc2rs/src/fn_ptr.rs @@ -0,0 +1,172 @@ +// Copyright (c) 2022-present INESC-ID. +// Distributed under the MIT license that can be found in the LICENSE file. + +#[macro_export] +macro_rules! fn_ptr { + ($f:expr, $ty:ty) => { + $crate::FnPtr::new($f as $ty, $f as *const () as usize) + }; +} + +use std::any::{Any, TypeId}; +use std::marker::PhantomData; +use std::ops::Deref; +use std::rc::Rc; + +use crate::rc::{AnyPtr, ErasedPtr}; +use crate::reinterpret::ByteRepr; + +#[derive(Clone)] +pub(crate) struct FnState { + addr: usize, + cast_history: Vec>>, +} + +pub struct FnPtr { + state: Option>, + // FnPtr does not use T, hence wrap in PhantomData + _marker: PhantomData, +} + +impl FnPtr { + #[inline] + pub fn null() -> Self { + FnPtr { + state: None, + _marker: PhantomData, + } + } + + #[inline] + pub fn is_null(&self) -> bool { + self.state.is_none() + } +} + +impl FnPtr { + pub fn new(f: T, addr: usize) -> Self { + FnPtr { + state: Some(Rc::new(FnState { + addr, + cast_history: vec![Some(Rc::new(f))], + })), + _marker: PhantomData, + } + } + + pub fn cast(&self, adapter: Option) -> FnPtr { + let state = self.state.as_ref().expect("ub: null fn pointer cast"); + + for (i, entry) in state.cast_history.iter().enumerate() { + if let Some(ref rc) = entry { + if (*rc).as_ref().type_id() == TypeId::of::() { + return FnPtr { + state: Some(Rc::new(FnState { + addr: state.addr, + cast_history: state.cast_history[..=i].to_vec(), + })), + _marker: PhantomData, + }; + } + } + } + + let mut new_stack = state.cast_history.clone(); + new_stack.push(adapter.map(|a| Rc::new(a) as Rc)); + + FnPtr { + state: Some(Rc::new(FnState { + addr: state.addr, + cast_history: new_stack, + })), + _marker: PhantomData, + } + } +} + +impl Deref for FnPtr { + type Target = T; + fn deref(&self) -> &T { + let state = self.state.as_ref().expect("ub: null fn pointer call"); + let entry = state + .cast_history + .last() + .expect("empty fn pointer cast_history"); + match entry { + Some(rc) => rc + .downcast_ref::() + .expect("ub: fn pointer type mismatch"), + None => panic!("ub: calling through incompatible fn pointer type"), + } + } +} + +impl Clone for FnPtr { + fn clone(&self) -> Self { + FnPtr { + state: self.state.clone(), + _marker: PhantomData, + } + } +} + +impl Default for FnPtr { + fn default() -> Self { + Self::null() + } +} + +impl PartialEq for FnPtr { + fn eq(&self, other: &Self) -> bool { + match (&self.state, &other.state) { + (None, None) => true, + (Some(a), Some(b)) => a.addr == b.addr, + _ => false, + } + } +} + +impl Eq for FnPtr {} + +impl std::fmt::Debug for FnPtr { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match &self.state { + None => write!(f, "FnPtr(null)"), + Some(s) => write!(f, "FnPtr(0x{:x})", s.addr), + } + } +} + +impl ByteRepr for FnPtr {} + +impl ErasedPtr for FnPtr { + fn pointee_type_id(&self) -> TypeId { + TypeId::of::() + } + fn memcpy(&self, _src: &dyn ErasedPtr, _len: usize) { + panic!("memcpy not supported on fn pointer"); + } + fn as_any(&self) -> &dyn Any { + self + } + fn equals(&self, other: &dyn ErasedPtr) -> Option { + if self.pointee_type_id() != other.pointee_type_id() { + return None; + } + other.as_any().downcast_ref::>().map(|o| self == o) + } +} + +impl FnPtr { + pub fn to_any(&self) -> AnyPtr { + AnyPtr { + ptr: Rc::new(self.clone()), + } + } +} + +impl AnyPtr { + pub fn cast_fn(&self) -> Option> { + self.ptr.as_any().downcast_ref::>().cloned() + } +} diff --git a/libcc2rs/src/lib.rs b/libcc2rs/src/lib.rs index 42eb4117..1271b941 100644 --- a/libcc2rs/src/lib.rs +++ b/libcc2rs/src/lib.rs @@ -7,6 +7,9 @@ pub use reinterpret::ByteRepr; mod rc; pub use rc::*; +mod fn_ptr; +pub use fn_ptr::FnPtr; + mod inc; pub use inc::*; From 87f2a87d967748a878f26fd6cd0ea09eafd1c3ef Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Fri, 17 Apr 2026 10:10:55 +0100 Subject: [PATCH 02/17] Delete debug trait on FnPtr --- libcc2rs/src/fn_ptr.rs | 9 --------- 1 file changed, 9 deletions(-) diff --git a/libcc2rs/src/fn_ptr.rs b/libcc2rs/src/fn_ptr.rs index 07a5f0d5..e6fafbf2 100644 --- a/libcc2rs/src/fn_ptr.rs +++ b/libcc2rs/src/fn_ptr.rs @@ -128,15 +128,6 @@ impl PartialEq for FnPtr { impl Eq for FnPtr {} -impl std::fmt::Debug for FnPtr { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match &self.state { - None => write!(f, "FnPtr(null)"), - Some(s) => write!(f, "FnPtr(0x{:x})", s.addr), - } - } -} - impl ByteRepr for FnPtr {} impl ErasedPtr for FnPtr { From 6078450a52ce158e810392ae8a8c2092f43e0716 Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Fri, 17 Apr 2026 10:12:31 +0100 Subject: [PATCH 03/17] Add fn ptr tests --- tests/unit/fn_ptr.cpp | 21 +++++++++++ tests/unit/fn_ptr_array.cpp | 21 +++++++++++ tests/unit/fn_ptr_as_condition.cpp | 33 ++++++++++++++++++ tests/unit/fn_ptr_cast.cpp | 56 ++++++++++++++++++++++++++++++ tests/unit/fn_ptr_conditional.cpp | 25 +++++++++++++ tests/unit/fn_ptr_default_arg.cpp | 23 ++++++++++++ tests/unit/fn_ptr_global.cpp | 36 +++++++++++++++++++ tests/unit/fn_ptr_reassign.cpp | 27 ++++++++++++++ tests/unit/fn_ptr_return.cpp | 27 ++++++++++++++ tests/unit/fn_ptr_stable_sort.cpp | 25 +++++++++++++ tests/unit/fn_ptr_struct.cpp | 26 ++++++++++++++ tests/unit/fn_ptr_void_return.cpp | 25 +++++++++++++ tests/unit/fn_ptr_vtable.cpp | 41 ++++++++++++++++++++++ 13 files changed, 386 insertions(+) create mode 100644 tests/unit/fn_ptr.cpp create mode 100644 tests/unit/fn_ptr_array.cpp create mode 100644 tests/unit/fn_ptr_as_condition.cpp create mode 100644 tests/unit/fn_ptr_cast.cpp create mode 100644 tests/unit/fn_ptr_conditional.cpp create mode 100644 tests/unit/fn_ptr_default_arg.cpp create mode 100644 tests/unit/fn_ptr_global.cpp create mode 100644 tests/unit/fn_ptr_reassign.cpp create mode 100644 tests/unit/fn_ptr_return.cpp create mode 100644 tests/unit/fn_ptr_stable_sort.cpp create mode 100644 tests/unit/fn_ptr_struct.cpp create mode 100644 tests/unit/fn_ptr_void_return.cpp create mode 100644 tests/unit/fn_ptr_vtable.cpp diff --git a/tests/unit/fn_ptr.cpp b/tests/unit/fn_ptr.cpp new file mode 100644 index 00000000..c7c9c7e4 --- /dev/null +++ b/tests/unit/fn_ptr.cpp @@ -0,0 +1,21 @@ +#include + +typedef int (*foo_t)(void *); + +int my_foo(void *p) { return *static_cast(p); } + +int foo(foo_t fn, int *pi) { return fn(pi); } + +int main() { + foo_t fn = nullptr; + assert(fn == nullptr); + assert(fn != my_foo); + + fn = my_foo; + assert(fn != nullptr); + assert(fn == my_foo); + + int a = 10; + assert(foo(fn, &a) == a); + return 0; +} diff --git a/tests/unit/fn_ptr_array.cpp b/tests/unit/fn_ptr_array.cpp new file mode 100644 index 00000000..f7521faa --- /dev/null +++ b/tests/unit/fn_ptr_array.cpp @@ -0,0 +1,21 @@ +#include + +typedef int (*op_t)(int, int); + +int add(int a, int b) { return a + b; } +int sub(int a, int b) { return a - b; } +int mul(int a, int b) { return a * b; } + +int main() { + op_t ops[3] = {add, sub, mul}; + + assert(ops[0](2, 3) == 5); + assert(ops[1](7, 4) == 3); + assert(ops[2](6, 5) == 30); + + assert(ops[0] != nullptr); + assert(ops[0] == add); + assert(ops[0] != sub); + + return 0; +} diff --git a/tests/unit/fn_ptr_as_condition.cpp b/tests/unit/fn_ptr_as_condition.cpp new file mode 100644 index 00000000..5817f7e4 --- /dev/null +++ b/tests/unit/fn_ptr_as_condition.cpp @@ -0,0 +1,33 @@ +#include + +typedef void (*callback_t)(int *); + +void double_it(int *x) { *x *= 2; } + +void maybe_call(callback_t cb, int *x) { + if (cb) { + cb(x); + } +} + +int main() { + int a = 5; + maybe_call(double_it, &a); + assert(a == 10); + + int b = 5; + maybe_call(nullptr, &b); + assert(b == 5); + + callback_t fn = nullptr; + if (!fn) { + fn = double_it; + } + int c = 3; + if (fn) { + fn(&c); + } + assert(c == 6); + + return 0; +} diff --git a/tests/unit/fn_ptr_cast.cpp b/tests/unit/fn_ptr_cast.cpp new file mode 100644 index 00000000..d825b050 --- /dev/null +++ b/tests/unit/fn_ptr_cast.cpp @@ -0,0 +1,56 @@ +#include + +typedef void (*generic_fn)(void); +typedef int (*int_fn)(int); + +int double_it(int x) { return x * 2; } + +void test_roundtrip() { + int_fn fn = double_it; + assert(fn(5) == 10); + + generic_fn gfn = (generic_fn)fn; + assert(gfn != nullptr); + + int_fn fn2 = (int_fn)gfn; + assert(fn2(5) == 10); + assert(fn2 == fn); +} + +void test_double_cast() { + int_fn fn = double_it; + int_fn fn2 = (int_fn)(generic_fn)fn; + assert(fn2(5) == 10); + assert(fn2 == fn); +} + +struct Command { + void *data; +}; + +void test_void_ptr_to_fn() { + Command cmd; + cmd.data = (void *)double_it; + + int_fn fn = (int_fn)cmd.data; + assert(fn(5) == 10); +} + +typedef int (*generic_int_fn)(void *, int); + +int add_offset(int *base, int offset) { return *base + offset; } + +void test_call_through_cast() { + generic_int_fn gfn = (generic_int_fn)add_offset; + int val = 100; + int result = gfn(&val, 42); + assert(result == 142); +} + +int main() { + test_roundtrip(); + test_double_cast(); + test_void_ptr_to_fn(); + test_call_through_cast(); + return 0; +} diff --git a/tests/unit/fn_ptr_conditional.cpp b/tests/unit/fn_ptr_conditional.cpp new file mode 100644 index 00000000..d1b4c832 --- /dev/null +++ b/tests/unit/fn_ptr_conditional.cpp @@ -0,0 +1,25 @@ +#include + +typedef int (*op_t)(int); + +int inc(int x) { return x + 1; } +int dec(int x) { return x - 1; } +int identity(int x) { return x; } + +op_t pick(int mode) { return mode > 0 ? inc : mode < 0 ? dec : identity; } + +int apply(op_t fn, int x) { + op_t actual = fn ? fn : identity; + return actual(x); +} + +int main() { + assert(pick(1)(10) == 11); + assert(pick(-1)(10) == 9); + assert(pick(0)(10) == 10); + + assert(apply(inc, 5) == 6); + assert(apply(nullptr, 5) == 5); + + return 0; +} diff --git a/tests/unit/fn_ptr_default_arg.cpp b/tests/unit/fn_ptr_default_arg.cpp new file mode 100644 index 00000000..c5e0f55b --- /dev/null +++ b/tests/unit/fn_ptr_default_arg.cpp @@ -0,0 +1,23 @@ +#include + +typedef int (*transform_t)(int); + +int identity(int x) { return x; } + +int apply(int x, transform_t fn = nullptr) { + if (fn) { + return fn(x); + } + return x; +} + +int main() { + assert(apply(5) == 5); + assert(apply(5, nullptr) == 5); + assert(apply(5, identity) == 5); + + transform_t negate = [](int x) { return -x; }; + assert(apply(5, negate) == -5); + + return 0; +} diff --git a/tests/unit/fn_ptr_global.cpp b/tests/unit/fn_ptr_global.cpp new file mode 100644 index 00000000..5ff3082f --- /dev/null +++ b/tests/unit/fn_ptr_global.cpp @@ -0,0 +1,36 @@ +#include + +typedef int (*op_t)(int); + +int double_it(int x) { return x * 2; } +int triple_it(int x) { return x * 3; } + +static op_t g_op = nullptr; + +void set_op(op_t fn) { g_op = fn; } + +int call_op(int x) { + if (g_op) { + return g_op(x); + } + return x; +} + +int main() { + assert(call_op(5) == 5); + + set_op(double_it); + assert(g_op != nullptr); + assert(g_op == double_it); + assert(call_op(5) == 10); + + set_op(triple_it); + assert(g_op == triple_it); + assert(call_op(5) == 15); + + set_op(nullptr); + assert(g_op == nullptr); + assert(call_op(5) == 5); + + return 0; +} diff --git a/tests/unit/fn_ptr_reassign.cpp b/tests/unit/fn_ptr_reassign.cpp new file mode 100644 index 00000000..b8dc48f8 --- /dev/null +++ b/tests/unit/fn_ptr_reassign.cpp @@ -0,0 +1,27 @@ +#include + +typedef int (*op_t)(int, int); + +int add(int a, int b) { return a + b; } +int sub(int a, int b) { return a - b; } +int mul(int a, int b) { return a * b; } + +int main() { + op_t fn = add; + assert(fn(3, 4) == 7); + + fn = sub; + assert(fn(10, 3) == 7); + + fn = mul; + assert(fn(6, 7) == 42); + + fn = nullptr; + assert(fn == nullptr); + + fn = add; + assert(fn != nullptr); + assert(fn(1, 1) == 2); + + return 0; +} diff --git a/tests/unit/fn_ptr_return.cpp b/tests/unit/fn_ptr_return.cpp new file mode 100644 index 00000000..6e570931 --- /dev/null +++ b/tests/unit/fn_ptr_return.cpp @@ -0,0 +1,27 @@ +#include + +typedef int (*op_t)(int); + +int inc(int x) { return x + 1; } +int dec(int x) { return x - 1; } + +op_t pick(int choose_inc) { + if (choose_inc) { + return inc; + } + return dec; +} + +int main() { + op_t f = pick(1); + assert(f != nullptr); + assert(f == inc); + assert(f(10) == 11); + + op_t g = pick(0); + assert(g == dec); + assert(g(10) == 9); + + assert(f != g); + return 0; +} diff --git a/tests/unit/fn_ptr_stable_sort.cpp b/tests/unit/fn_ptr_stable_sort.cpp new file mode 100644 index 00000000..8361b5a6 --- /dev/null +++ b/tests/unit/fn_ptr_stable_sort.cpp @@ -0,0 +1,25 @@ +#include +#include +#include + +struct Item { + int key; + int value; +}; + +static bool Compare(const Item &a, const Item &b) { return a.key < b.key; } + +int main() { + std::vector v; + v.push_back({3, 30}); + v.push_back({1, 10}); + v.push_back({2, 20}); + + std::stable_sort(v.begin(), v.end(), Compare); + + assert(v[0].key == 1); + assert(v[1].key == 2); + assert(v[2].key == 3); + + return 0; +} diff --git a/tests/unit/fn_ptr_struct.cpp b/tests/unit/fn_ptr_struct.cpp new file mode 100644 index 00000000..55216e7f --- /dev/null +++ b/tests/unit/fn_ptr_struct.cpp @@ -0,0 +1,26 @@ +#include + +typedef int (*handler_t)(int); + +struct Handler { + int tag; + handler_t cb; +}; + +int double_it(int x) { return x * 2; } +int negate(int x) { return -x; } + +int main() { + Handler h1 = {1, double_it}; + Handler h2 = {2, negate}; + + assert(h1.cb != nullptr); + assert(h1.cb(5) == 10); + assert(h2.cb(7) == -7); + + h1.cb = negate; + assert(h1.cb(3) == -3); + assert(h1.cb == h2.cb); + + return 0; +} diff --git a/tests/unit/fn_ptr_void_return.cpp b/tests/unit/fn_ptr_void_return.cpp new file mode 100644 index 00000000..3ae221a9 --- /dev/null +++ b/tests/unit/fn_ptr_void_return.cpp @@ -0,0 +1,25 @@ +#include + +typedef void (*action_t)(int *); + +void negate(int *x) { *x = -*x; } +void zero_out(int *x) { *x = 0; } + +void run(action_t fn, int *x) { fn(x); } + +int main() { + int a = 42; + run(negate, &a); + assert(a == -42); + + run(zero_out, &a); + assert(a == 0); + + action_t fn = negate; + assert(fn != nullptr); + int b = 10; + fn(&b); + assert(b == -10); + + return 0; +} diff --git a/tests/unit/fn_ptr_vtable.cpp b/tests/unit/fn_ptr_vtable.cpp new file mode 100644 index 00000000..8d318a5c --- /dev/null +++ b/tests/unit/fn_ptr_vtable.cpp @@ -0,0 +1,41 @@ +#include + +typedef void *(*create_fn)(int); +typedef int (*get_fn)(void *); +typedef void (*destroy_fn)(void *); + +struct Vtable { + create_fn create; + get_fn get; + destroy_fn destroy; +}; + +static int storage; + +void *int_create(int val) { + storage = val; + return &storage; +} + +int int_get(void *p) { return *(int *)p; } + +void int_destroy(void *p) { *(int *)p = 0; } + +int main() { + Vtable vt = {int_create, int_get, int_destroy}; + + assert(vt.create != nullptr); + assert(vt.get != nullptr); + assert(vt.destroy != nullptr); + + void *obj = vt.create(42); + assert(vt.get(obj) == 42); + + vt.destroy(obj); + assert(storage == 0); + + vt.get = nullptr; + assert(vt.get == nullptr); + + return 0; +} From b2834bc5bb0c7acc0f6f4a89e6f8e66e0ad8a831 Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Fri, 17 Apr 2026 11:12:48 +0100 Subject: [PATCH 04/17] Make ErasedPtr and AnyPtr accessible to FnPtr --- cpp2rust/converter/converter.cpp | 137 +++++++++------ cpp2rust/converter/converter.h | 7 +- cpp2rust/converter/converter_lib.cpp | 31 ++-- cpp2rust/converter/converter_lib.h | 2 + .../converter/models/converter_refcount.cpp | 166 +++++++++++++++++- .../converter/models/converter_refcount.h | 16 ++ libcc2rs/src/rc.rs | 4 +- 7 files changed, 292 insertions(+), 71 deletions(-) diff --git a/cpp2rust/converter/converter.cpp b/cpp2rust/converter/converter.cpp index 5fe0269a..b56301a6 100644 --- a/cpp2rust/converter/converter.cpp +++ b/cpp2rust/converter/converter.cpp @@ -130,17 +130,20 @@ bool Converter::VisitRecordType(clang::RecordType *type) { auto *decl = type->getDecl(); if (auto lambda = clang::dyn_cast(decl)) { if (lambda->isLambda()) { - auto call_op = lambda->getLambdaCallOperator(); - StrCat("Rcparameters()) { - StrCat(std::format("{},", ToStringBase(p->getType()))); - } - StrCat(")"); - if (!call_op->getReturnType()->isVoidType()) { - StrCat("->"); - StrCat(ToStringBase(call_op->getReturnType())); + if (in_function_formals_) { + auto call_op = lambda->getLambdaCallOperator(); + StrCat("impl Fn("); + for (auto p : call_op->parameters()) { + StrCat(std::format("{},", ToStringBase(p->getType()))); + } + StrCat(")"); + if (!call_op->getReturnType()->isVoidType()) { + StrCat("->"); + StrCat(ToStringBase(call_op->getReturnType())); + } + } else { + StrCat("_"); } - StrCat(">"); return false; } } @@ -226,7 +229,7 @@ void Converter::ConvertFunctionPointerType(clang::PointerType *type) { auto proto = type->getPointeeType()->getAs(); assert(proto && "Type should be a function prototype"); - StrCat("Rcparam_types()) { StrCat(std::format("{},", ToString(p_ty))); } @@ -423,6 +426,9 @@ bool Converter::ConvertVarDeclSkipInit(clang::VarDecl *decl) { } bool Converter::ConvertLambdaVarDecl(clang::VarDecl *decl) { + if (decl->getType()->isFunctionPointerType()) { + return false; + } if (decl->hasInit()) { if (clang::isa( decl->getInit()->IgnoreUnlessSpelledInSource())) { @@ -1377,6 +1383,16 @@ bool Converter::VisitCallExpr(clang::CallExpr *expr) { return false; } +void Converter::EmitFnPtrCall(clang::Expr *callee) { + StrCat(token::kOpenParen); + Convert(callee); + StrCat(").unwrap()"); +} + +void Converter::EmitFnAsValue(const clang::FunctionDecl *fn_decl) { + StrCat(std::format("Some({})", Mapper::GetFnRefName(fn_decl))); +} + void Converter::ConvertGenericCallExpr(clang::CallExpr *expr) { clang::Expr *callee = expr->getCallee(); auto convert_param_ty = [&](clang::QualType param_type, clang::Expr *expr) { @@ -1399,7 +1415,8 @@ void Converter::ConvertGenericCallExpr(clang::CallExpr *expr) { StrCat(token::kOpenParen); StrCat(keyword_unsafe_); StrCat(token::kOpenCurlyBracket); - const auto *function = expr->getCalleeDecl()->getAsFunction(); + const auto *function = + expr->getCalleeDecl() ? expr->getCalleeDecl()->getAsFunction() : nullptr; const clang::FunctionProtoType *proto = nullptr; if (!function) { @@ -1447,7 +1464,12 @@ void Converter::ConvertGenericCallExpr(clang::CallExpr *expr) { } } - Convert(callee); + if (proto && !function) { + EmitFnPtrCall(callee); + } else { + PushExprKind push(*this, ExprKind::RValue); + Convert(StripFunctionPointerDecay(callee)); + } StrCat(token::kOpenParen); for (unsigned i = 0; i < num_named_params && i < num_args; ++i) { auto *arg = expr->getArg(i + arg_begin); @@ -1668,7 +1690,11 @@ bool Converter::VisitImplicitCastExpr(clang::ImplicitCastExpr *expr) { break; } case clang::CastKind::CK_FunctionToPointerDecay: - case clang::CastKind::CK_BuiltinFnToFnPtr: + case clang::CastKind::CK_BuiltinFnToFnPtr: { + PushExprKind push(*this, ExprKind::AddrOf); + Convert(sub_expr); + break; + } case clang::CastKind::CK_ConstructorConversion: case clang::CastKind::CK_DerivedToBase: Convert(sub_expr); @@ -1692,7 +1718,11 @@ bool Converter::VisitImplicitCastExpr(clang::ImplicitCastExpr *expr) { ConvertEqualsNullPtr(sub_expr); break; case clang::CastKind::CK_NullToPointer: - StrCat(keyword_default_); + if (type->isFunctionPointerType()) { + StrCat("None"); + } else { + StrCat(keyword_default_); + } computed_expr_type_ = ComputedExprType::FreshPointer; break; default: @@ -1737,6 +1767,17 @@ bool Converter::VisitExplicitCastExpr(clang::ExplicitCastExpr *expr) { if (expr->getType() == sub_expr->getType()) { return Convert(sub_expr); } + if (type->isFunctionPointerType() || + sub_expr->getType()->isFunctionPointerType()) { + StrCat("std::mem::transmute::<"); + Convert(sub_expr->getType()); + StrCat(","); + Convert(type); + StrCat(">("); + Convert(sub_expr); + StrCat(")"); + return false; + } StrCat(token::kOpenParen); Convert(sub_expr); if (auto *unary_oper = clang::dyn_cast(sub_expr); @@ -1963,12 +2004,12 @@ bool Converter::VisitConditionalOperator(clang::ConditionalOperator *expr) { StrCat(keyword::kIf); Convert(expr->getCond()); StrCat(token::kOpenCurlyBracket); - if (expr->isLValue() && !isRValue()) { + if (expr->isLValue() && !isRValue() && !expr->getType()->isFunctionType()) { StrCat(token::kRef, keyword_mut_); } Convert(expr->getTrueExpr()); StrCat(token::kCloseCurlyBracket, keyword::kElse, token::kOpenCurlyBracket); - if (expr->isLValue() && !isRValue()) { + if (expr->isLValue() && !isRValue() && !expr->getType()->isFunctionType()) { StrCat(token::kRef, keyword_mut_); } Convert(expr->getFalseExpr()); @@ -2022,35 +2063,23 @@ bool Converter::VisitDeclRefExpr(clang::DeclRefExpr *expr) { return false; } - if (auto function = clang::dyn_cast(decl)) { + if (auto *fn_decl = clang::dyn_cast(decl)) { if (isAddrOf()) { - // Wrap unsafe function in safe closure because the Fn trait only accepts - // safe functions - std::string arguments; - for (unsigned i = 0; i < function->getNumParams(); ++i) { - arguments += (i ? ", a" : "a") + std::to_string(i); - } - StrCat("Rc::new", token::kOpenParen); - StrCat(std::format("|{}|", arguments)); - StrCat(keyword_unsafe_, token::kOpenCurlyBracket); - StrCat(str); - StrCat(token::kOpenParen); - StrCat(arguments); - StrCat(token::kCloseParen); - StrCat(token::kCloseCurlyBracket); - StrCat(token::kCloseParen); + EmitFnAsValue(fn_decl); return false; } } if (auto var_decl = clang::dyn_cast(decl)) { - if (auto init = var_decl->getInit()) { - if (auto lambda = clang::dyn_cast( - init->IgnoreUnlessSpelledInSource())) { - StrCat(token::kOpenParen); - VisitLambdaExpr(lambda); - StrCat(token::kCloseParen); - return false; + if (!var_decl->getType()->isFunctionPointerType()) { + if (auto init = var_decl->getInit()) { + if (auto lambda = clang::dyn_cast( + init->IgnoreUnlessSpelledInSource())) { + StrCat(token::kOpenParen); + VisitLambdaExpr(lambda); + StrCat(token::kCloseParen); + return false; + } } } } @@ -2509,6 +2538,9 @@ bool Converter::VisitCXXDefaultArgExpr(clang::CXXDefaultArgExpr *expr) { } bool Converter::VisitLambdaExpr(clang::LambdaExpr *expr) { + if (isAddrOf() && expr->capture_size() == 0) { + StrCat("Some"); + } StrCat(token::kOpenParen); StrCat("|"); for (auto p : expr->getLambdaClass()->getLambdaCallOperator()->parameters()) { @@ -2638,15 +2670,7 @@ bool Converter::VisitCXXStdInitializerListExpr( std::string Converter::GetFunctionPointerDefaultAsString(clang::QualType qual_type) { - std::string ret; - auto proto = qual_type->getPointeeType()->getAs(); - assert(proto); - ret = "Rc::new(|"; - for (unsigned i = 0; i < proto->getNumParams(); ++i) { - ret += "_,"; - } - ret += R"(| { panic!("ub: uninit function pointer") }))"; - return ret; + return "None"; } std::string Converter::GetDefaultAsString(clang::QualType qual_type) { @@ -2800,6 +2824,16 @@ void Converter::ConvertVarInit(clang::QualType qual_type, clang::Expr *expr) { StrCat(keyword_mut_); } } + if (qual_type->isFunctionPointerType()) { + if (auto *lambda = clang::dyn_cast( + expr->IgnoreUnlessSpelledInSource())) { + PushExprKind push(*this, ExprKind::AddrOf); + curr_init_type_.push(qual_type); + VisitLambdaExpr(lambda); + curr_init_type_.pop(); + return; + } + } auto *ignore_casts = expr->IgnoreCasts(); // FIXME: this looks very complicated if (auto *ctor = clang::dyn_cast(ignore_casts); @@ -2845,7 +2879,8 @@ void Converter::ConvertUnsignedArithOperand(clang::Expr *expr, void Converter::ConvertEqualsNullPtr(clang::Expr *expr) { StrCat("("); Convert(expr); - if (IsUniquePtr(expr->getType())) { + if (IsUniquePtr(expr->getType()) || + expr->getType()->isFunctionPointerType()) { StrCat(").is_none()"); } else { StrCat(").is_null()"); @@ -3229,6 +3264,8 @@ void Converter::PlaceholderCtx::dump() const { std::string Converter::ConvertPlaceholder(clang::Expr *expr, clang::Expr *arg, const PlaceholderCtx &ph_ctx) { + arg = StripFunctionPointerDecay(arg); + if (ph_ctx.needs_materialization()) { auto materialized = ph_ctx.materialize_ctx->GetOrMaterialize( static_cast(ph_ctx.materialize_idx), diff --git a/cpp2rust/converter/converter.h b/cpp2rust/converter/converter.h index b2908fd0..26f29499 100644 --- a/cpp2rust/converter/converter.h +++ b/cpp2rust/converter/converter.h @@ -201,6 +201,10 @@ class Converter : public clang::RecursiveASTVisitor { void ConvertGenericCallExpr(clang::CallExpr *expr); + virtual void EmitFnPtrCall(clang::Expr *callee); + + virtual void EmitFnAsValue(const clang::FunctionDecl *fn_decl); + virtual void ConvertPrintf(clang::CallExpr *expr); void ConvertVAArgCall(clang::CallExpr *expr); @@ -334,7 +338,8 @@ class Converter : public clang::RecursiveASTVisitor { virtual bool Convert(clang::Stmt *stmt); virtual bool Convert(clang::Expr *expr); - std::string GetFunctionPointerDefaultAsString(clang::QualType qual_type); + virtual std::string + GetFunctionPointerDefaultAsString(clang::QualType qual_type); virtual std::string GetDefaultAsString(clang::QualType qual_type); diff --git a/cpp2rust/converter/converter_lib.cpp b/cpp2rust/converter/converter_lib.cpp index 3f515788..ff770ae2 100644 --- a/cpp2rust/converter/converter_lib.cpp +++ b/cpp2rust/converter/converter_lib.cpp @@ -362,24 +362,35 @@ const char *AccessSpecifierAsString(clang::AccessSpecifier spec) { } clang::QualType GetReturnTypeOfFunction(const clang::CallExpr *expr) { - auto decl = expr->getCalleeDecl(); - if (decl->getAsFunction()) { - return decl->getAsFunction()->getReturnType().getCanonicalType(); + if (auto *decl = expr->getCalleeDecl()) { + if (auto *fn = decl->getAsFunction()) { + return fn->getReturnType().getCanonicalType(); + } } - auto callee_ty = - expr->getCallee()->getType().getDesugaredType(decl->getASTContext()); - if (auto ptr_ty = callee_ty->getAs()) { - return ptr_ty->getPointeeType() - ->getAs() - ->getReturnType() - .getCanonicalType(); + auto callee_ty = expr->getCallee()->getType(); + if (auto *ptr_ty = callee_ty->getAs()) { + if (auto *fn_ty = + ptr_ty->getPointeeType()->getAs()) { + return fn_ty->getReturnType().getCanonicalType(); + } } assert(0 && "Unhandled function prototype"); return {}; } +clang::Expr *StripFunctionPointerDecay(clang::Expr *expr) { + if (auto *ice = clang::dyn_cast(expr)) { + auto ck = ice->getCastKind(); + if (ck == clang::CK_FunctionToPointerDecay || + ck == clang::CK_BuiltinFnToFnPtr) { + return ice->getSubExpr(); + } + } + return expr; +} + std::string GetOverloadedOperator(const clang::FunctionDecl *decl) { switch (decl->getOverloadedOperator()) { case clang::OO_Less: diff --git a/cpp2rust/converter/converter_lib.h b/cpp2rust/converter/converter_lib.h index 3696d680..a2f25342 100644 --- a/cpp2rust/converter/converter_lib.h +++ b/cpp2rust/converter/converter_lib.h @@ -91,6 +91,8 @@ template llvm::SmallString<16> GetNumAsString(const T &num) { clang::QualType GetReturnTypeOfFunction(const clang::CallExpr *expr); +clang::Expr *StripFunctionPointerDecay(clang::Expr *expr); + std::string GetOverloadedOperator(const clang::FunctionDecl *decl); bool IsOverloadedComparisonOperator(const clang::CXXMethodDecl *decl); diff --git a/cpp2rust/converter/models/converter_refcount.cpp b/cpp2rust/converter/models/converter_refcount.cpp index e9f397ca..094ecf38 100644 --- a/cpp2rust/converter/models/converter_refcount.cpp +++ b/cpp2rust/converter/models/converter_refcount.cpp @@ -165,10 +165,77 @@ bool ConverterRefCount::VisitLValueReferenceType( return false; } +std::string ConverterRefCount::BuildFnAdapter( + const clang::FunctionDecl *src_fn, + const clang::FunctionProtoType *src_proto, + const clang::FunctionProtoType *target_proto) { + + // UB: Incompatible arity + if (src_proto->getNumParams() != target_proto->getNumParams()) { + return "None"; + } + + PushConversionKind push(*this, ConversionKind::Unboxed); + + // Build adapter signature: |a0: T0, a1: T1, ...| -> Tr + std::string closure = "(|"; + for (unsigned i = 0; i < target_proto->getNumParams(); ++i) { + closure += + std::format("a{}: {},", i, ToString(target_proto->getParamType(i))); + } + closure += "|"; + if (!target_proto->getReturnType()->isVoidType()) { + closure += std::format(" -> {} ", ToString(target_proto->getReturnType())); + } + closure += "{ "; + + // Build adapter body: src_fn(convert(a0), convert(a1), ...) + closure += Mapper::GetFnRefName(src_fn) + "("; + for (unsigned i = 0; i < src_proto->getNumParams(); ++i) { + auto src_pty = src_proto->getParamType(i); + auto tgt_pty = target_proto->getParamType(i); + if (ToString(src_pty) == ToString(tgt_pty)) { + closure += std::format("a{}", i); + } else if (src_pty->isPointerType() && tgt_pty->isPointerType()) { + if (tgt_pty->isVoidPointerType()) { + closure += std::format("a{}.cast::<{}>().unwrap()", i, + GetPointeeRustType(src_pty)); + } else if (src_pty->isVoidPointerType()) { + closure += std::format("a{}.to_any()", i); + } else if (tgt_pty->getPointeeType()->isCharType()) { + closure += std::format("a{}.reinterpret_cast::<{}>()", i, + GetPointeeRustType(src_pty)); + } else if (src_pty->getPointeeType()->isCharType()) { + closure += std::format("a{}.reinterpret_cast::()", i); + } + } else { + // UB: Incompatible types + return "None"; + } + closure += ", "; + } + closure += ") })"; + + return std::format("Some({} as {})", closure, GetFnTypeString(target_proto)); +} + +std::string +ConverterRefCount::GetFnTypeString(const clang::FunctionProtoType *proto) { + PushConversionKind push(*this, ConversionKind::Unboxed); + std::string result = "fn("; + for (auto p_ty : proto->param_types()) { + result += ToString(p_ty) + ","; + } + result += ")"; + if (!proto->getReturnType()->isVoidType()) { + result += std::format(" -> {}", ToString(proto->getReturnType())); + } + return result; +} + bool ConverterRefCount::VisitPointerType(clang::PointerType *type) { - if (type->getPointeeType()->getAs()) { - PushConversionKind push(*this, ConversionKind::Unboxed); - ConvertFunctionPointerType(type); + if (auto proto = type->getPointeeType()->getAs()) { + StrCat(std::format("FnPtr<{}>", GetFnTypeString(proto))); return false; } @@ -570,7 +637,7 @@ bool ConverterRefCount::VisitDeclRefExpr(clang::DeclRefExpr *expr) { if (clang::isa(decl)) { if (isAddrOf()) { - StrCat(std::format("Rc::new({})", str)); + EmitFnAsValue(fn_decl); } else { StrCat(str); } @@ -951,9 +1018,86 @@ bool ConverterRefCount::VisitImplicitCastExpr(clang::ImplicitCastExpr *expr) { } } + if (expr->getCastKind() == clang::CastKind::CK_NullToPointer && + expr->getType()->isFunctionPointerType()) { + StrCat("FnPtr::null()"); + computed_expr_type_ = ComputedExprType::FreshPointer; + return false; + } + return Converter::VisitImplicitCastExpr(expr); } +void ConverterRefCount::EmitFnPtrCall(clang::Expr *callee) { + StrCat("(*"); + Convert(callee); + StrCat(")"); +} + +void ConverterRefCount::EmitFnAsValue(const clang::FunctionDecl *fn_decl) { + StrCat(std::format( + "fn_ptr!({}, {})", Mapper::GetFnRefName(fn_decl), + GetFnTypeString(fn_decl->getType()->getAs()))); +} + +std::string ConverterRefCount::GetFunctionPointerDefaultAsString( + clang::QualType qual_type) { + return "FnPtr::null()"; +} + +void ConverterRefCount::ConvertEqualsNullPtr(clang::Expr *expr) { + StrCat("("); + Convert(expr); + StrCat(").is_null()"); +} + +bool ConverterRefCount::VisitFunctionPointerCast( + clang::ExplicitCastExpr *expr) { + if (expr->getType()->isFunctionPointerType() || + expr->getSubExpr()->getType()->isFunctionPointerType()) { + if (expr->getSubExpr()->getType()->isFunctionPointerType() && + expr->getType()->isFunctionPointerType()) { + auto target_proto = + expr->getType()->getPointeeType()->getAs(); + auto src_proto = expr->getSubExpr() + ->getType() + ->getPointeeType() + ->getAs(); + auto fn_type = GetFnTypeString(target_proto); + + std::string adapter = "None"; + // Only accept direct references to the casted function. Otherwise the + // closure would be capturing and would not coerce into a fn pointer. + if (auto *decl_ref = clang::dyn_cast( + expr->getSubExpr()->IgnoreImplicit())) { + if (auto *fn_decl = + clang::dyn_cast(decl_ref->getDecl())) { + adapter = BuildFnAdapter(fn_decl, src_proto, target_proto); + } + } + + StrCat(std::format("{}.cast::<{}>({})", ToString(expr->getSubExpr()), + fn_type, adapter)); + } else if (expr->getSubExpr()->getType()->isFunctionPointerType() || + expr->getType()->isVoidPointerType()) { + Convert(expr->getSubExpr()); + StrCat(".to_any()"); + } else if (expr->getSubExpr()->getType()->isVoidPointerType() || + expr->getType()->isFunctionPointerType()) { + auto target_proto = + expr->getType()->getPointeeType()->getAs(); + auto fn_type = GetFnTypeString(target_proto); + StrCat(std::format("{}.cast_fn::<{}>().expect(\"ub:wrong fn type\")", + ToString(expr->getSubExpr()), fn_type)); + } else { + assert(0 && "Unhandled function pointer cast"); + } + return false; + } + + return true; +} + bool ConverterRefCount::VisitExplicitCastExpr(clang::ExplicitCastExpr *expr) { if (expr->getTypeAsWritten()->isVoidType()) { return false; @@ -968,7 +1112,9 @@ bool ConverterRefCount::VisitExplicitCastExpr(clang::ExplicitCastExpr *expr) { return false; case clang::Stmt::CStyleCastExprClass: case clang::Stmt::CXXStaticCastExprClass: - if (expr->getSubExpr()->getType()->isVoidPointerType()) { + if (!VisitFunctionPointerCast(expr)) { + return false; + } else if (expr->getSubExpr()->getType()->isVoidPointerType()) { Convert(expr->getSubExpr()); PushConversionKind push(*this, ConversionKind::Unboxed); StrCat(std::format(".cast::<{}>().expect(\"ub:wrong type\")", @@ -1477,9 +1623,13 @@ void ConverterRefCount::ConvertVarInit(clang::QualType qual_type, { Buffer buf(*this); PushConversionKind push(*this, ConversionKind::Unboxed); - StrCat("Rc::new("); - VisitLambdaExpr(lambda); - StrCat(")"); + if (qual_type->isFunctionPointerType() && lambda->capture_size() == 0) { + StrCat("FnPtr::new(("); + VisitLambdaExpr(lambda); + StrCat("), 0)"); + } else { + VisitLambdaExpr(lambda); + } str = std::move(buf).str(); } StrCat(BoxValue(std::move(str))); diff --git a/cpp2rust/converter/models/converter_refcount.h b/cpp2rust/converter/models/converter_refcount.h index 81025901..cb9f3d82 100644 --- a/cpp2rust/converter/models/converter_refcount.h +++ b/cpp2rust/converter/models/converter_refcount.h @@ -59,12 +59,18 @@ class ConverterRefCount final : public Converter { void ConvertPrintf(clang::CallExpr *expr) override; + void EmitFnPtrCall(clang::Expr *callee) override; + + void EmitFnAsValue(const clang::FunctionDecl *fn_decl) override; + bool VisitCallExpr(clang::CallExpr *expr) override; bool VisitStringLiteral(clang::StringLiteral *expr) override; bool VisitImplicitCastExpr(clang::ImplicitCastExpr *expr) override; + bool VisitFunctionPointerCast(clang::ExplicitCastExpr *expr); + bool VisitExplicitCastExpr(clang::ExplicitCastExpr *expr) override; bool VisitBinaryOperator(clang::BinaryOperator *expr) override; @@ -103,6 +109,11 @@ class ConverterRefCount final : public Converter { std::string GetDefaultAsString(clang::QualType qual_type) override; + std::string + GetFunctionPointerDefaultAsString(clang::QualType qual_type) override; + + void ConvertEqualsNullPtr(clang::Expr *expr) override; + std::string GetDefaultAsStringFallback(clang::QualType qual_type) override; std::string ConvertVarDefaultInit(clang::QualType qual_type) override; @@ -171,6 +182,11 @@ class ConverterRefCount final : public Converter { const char *GetPointerDerefSuffix(clang::QualType pointee_type); const char *GetPointerDerefPrefix(clang::QualType pointee_type) override; + std::string GetFnTypeString(const clang::FunctionProtoType *proto); + std::string BuildFnAdapter(const clang::FunctionDecl *src_fn, + const clang::FunctionProtoType *src_proto, + const clang::FunctionProtoType *target_proto); + void EmitSetOrAssign(clang::Expr *lhs, std::string_view rhs); // Wraps a pointer expression with deref prefix/suffix: e.g. diff --git a/libcc2rs/src/rc.rs b/libcc2rs/src/rc.rs index e02e9626..a6242080 100644 --- a/libcc2rs/src/rc.rs +++ b/libcc2rs/src/rc.rs @@ -1004,7 +1004,7 @@ impl Ptr { } } -trait ErasedPtr: std::any::Any { +pub(crate) trait ErasedPtr: std::any::Any { fn pointee_type_id(&self) -> std::any::TypeId; fn memcpy(&self, src: &dyn ErasedPtr, len: usize); fn as_any(&self) -> &dyn std::any::Any; @@ -1057,7 +1057,7 @@ where #[derive(Clone)] pub struct AnyPtr { - ptr: Rc, + pub(crate) ptr: Rc, } impl Ptr { From 00da5231cf2dc50a53f45398ebfac5910de25460 Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Fri, 17 Apr 2026 11:29:37 +0100 Subject: [PATCH 05/17] Translate functoin pointers in refcount and unsafe --- cpp2rust/converter/converter.cpp | 28 ++++++------ cpp2rust/converter/converter.h | 6 ++- .../converter/models/converter_refcount.cpp | 43 ++++++++----------- .../converter/models/converter_refcount.h | 7 ++- 4 files changed, 42 insertions(+), 42 deletions(-) diff --git a/cpp2rust/converter/converter.cpp b/cpp2rust/converter/converter.cpp index b56301a6..42fa3083 100644 --- a/cpp2rust/converter/converter.cpp +++ b/cpp2rust/converter/converter.cpp @@ -225,24 +225,23 @@ bool Converter::VisitLValueReferenceType(clang::LValueReferenceType *type) { return Convert(pointee_type); } -void Converter::ConvertFunctionPointerType(clang::PointerType *type) { - auto proto = type->getPointeeType()->getAs(); - assert(proto && "Type should be a function prototype"); - - StrCat("Option<", keyword_unsafe_, " fn("); +std::string +Converter::ConvertFunctionPointerType(const clang::FunctionProtoType *proto) { + std::string result = "fn("; for (auto p_ty : proto->param_types()) { - StrCat(std::format("{},", ToString(p_ty))); + result += ToString(p_ty) + ","; } - StrCat(")"); + result += ")"; if (!proto->getReturnType()->isVoidType()) { - StrCat(std::format("-> {}", ToString(proto->getReturnType()))); + result += std::format(" -> {}", ToString(proto->getReturnType())); } - StrCat(">"); + return result; } bool Converter::VisitPointerType(clang::PointerType *type) { - if (type->getPointeeType()->getAs()) { - ConvertFunctionPointerType(type); + if (auto proto = type->getPointeeType()->getAs()) { + StrCat(std::format("Option<{} {}>", keyword_unsafe_, + ConvertFunctionPointerType(proto))); return false; } @@ -1389,8 +1388,9 @@ void Converter::EmitFnPtrCall(clang::Expr *callee) { StrCat(").unwrap()"); } -void Converter::EmitFnAsValue(const clang::FunctionDecl *fn_decl) { - StrCat(std::format("Some({})", Mapper::GetFnRefName(fn_decl))); +void Converter::ConvertFunctionToFunctionPointer( + const clang::FunctionDecl *fn_decl) { + StrCat(std::format("Some({})", GetNamedDeclAsString(fn_decl))); } void Converter::ConvertGenericCallExpr(clang::CallExpr *expr) { @@ -2065,7 +2065,7 @@ bool Converter::VisitDeclRefExpr(clang::DeclRefExpr *expr) { if (auto *fn_decl = clang::dyn_cast(decl)) { if (isAddrOf()) { - EmitFnAsValue(fn_decl); + ConvertFunctionToFunctionPointer(fn_decl); return false; } } diff --git a/cpp2rust/converter/converter.h b/cpp2rust/converter/converter.h index 26f29499..b3601fc5 100644 --- a/cpp2rust/converter/converter.h +++ b/cpp2rust/converter/converter.h @@ -61,7 +61,8 @@ class Converter : public clang::RecursiveASTVisitor { virtual bool VisitPointerType(clang::PointerType *type); - void ConvertFunctionPointerType(clang::PointerType *type); + virtual std::string + ConvertFunctionPointerType(const clang::FunctionProtoType *proto); virtual bool VisitDecayedType(clang::DecayedType *type); @@ -203,7 +204,8 @@ class Converter : public clang::RecursiveASTVisitor { virtual void EmitFnPtrCall(clang::Expr *callee); - virtual void EmitFnAsValue(const clang::FunctionDecl *fn_decl); + virtual void + ConvertFunctionToFunctionPointer(const clang::FunctionDecl *fn_decl); virtual void ConvertPrintf(clang::CallExpr *expr); diff --git a/cpp2rust/converter/models/converter_refcount.cpp b/cpp2rust/converter/models/converter_refcount.cpp index 094ecf38..416918af 100644 --- a/cpp2rust/converter/models/converter_refcount.cpp +++ b/cpp2rust/converter/models/converter_refcount.cpp @@ -190,7 +190,7 @@ std::string ConverterRefCount::BuildFnAdapter( closure += "{ "; // Build adapter body: src_fn(convert(a0), convert(a1), ...) - closure += Mapper::GetFnRefName(src_fn) + "("; + closure += GetNamedDeclAsString(src_fn) + "("; for (unsigned i = 0; i < src_proto->getNumParams(); ++i) { auto src_pty = src_proto->getParamType(i); auto tgt_pty = target_proto->getParamType(i); @@ -199,12 +199,12 @@ std::string ConverterRefCount::BuildFnAdapter( } else if (src_pty->isPointerType() && tgt_pty->isPointerType()) { if (tgt_pty->isVoidPointerType()) { closure += std::format("a{}.cast::<{}>().unwrap()", i, - GetPointeeRustType(src_pty)); + ToString(src_pty->getPointeeType())); } else if (src_pty->isVoidPointerType()) { closure += std::format("a{}.to_any()", i); } else if (tgt_pty->getPointeeType()->isCharType()) { closure += std::format("a{}.reinterpret_cast::<{}>()", i, - GetPointeeRustType(src_pty)); + ToString(src_pty->getPointeeType())); } else if (src_pty->getPointeeType()->isCharType()) { closure += std::format("a{}.reinterpret_cast::()", i); } @@ -216,26 +216,19 @@ std::string ConverterRefCount::BuildFnAdapter( } closure += ") })"; - return std::format("Some({} as {})", closure, GetFnTypeString(target_proto)); + return std::format("Some({} as {})", closure, + ConvertFunctionPointerType(target_proto)); } -std::string -ConverterRefCount::GetFnTypeString(const clang::FunctionProtoType *proto) { +std::string ConverterRefCount::ConvertFunctionPointerType( + const clang::FunctionProtoType *proto) { PushConversionKind push(*this, ConversionKind::Unboxed); - std::string result = "fn("; - for (auto p_ty : proto->param_types()) { - result += ToString(p_ty) + ","; - } - result += ")"; - if (!proto->getReturnType()->isVoidType()) { - result += std::format(" -> {}", ToString(proto->getReturnType())); - } - return result; + return Converter::ConvertFunctionPointerType(proto); } bool ConverterRefCount::VisitPointerType(clang::PointerType *type) { if (auto proto = type->getPointeeType()->getAs()) { - StrCat(std::format("FnPtr<{}>", GetFnTypeString(proto))); + StrCat(std::format("FnPtr<{}>", ConvertFunctionPointerType(proto))); return false; } @@ -635,9 +628,9 @@ bool ConverterRefCount::VisitDeclRefExpr(clang::DeclRefExpr *expr) { auto str = ConvertDeclRefExpr(expr); auto decl = expr->getDecl(); - if (clang::isa(decl)) { + if (auto fn_decl = clang::dyn_cast(decl)) { if (isAddrOf()) { - EmitFnAsValue(fn_decl); + ConvertFunctionToFunctionPointer(fn_decl); } else { StrCat(str); } @@ -1034,10 +1027,12 @@ void ConverterRefCount::EmitFnPtrCall(clang::Expr *callee) { StrCat(")"); } -void ConverterRefCount::EmitFnAsValue(const clang::FunctionDecl *fn_decl) { - StrCat(std::format( - "fn_ptr!({}, {})", Mapper::GetFnRefName(fn_decl), - GetFnTypeString(fn_decl->getType()->getAs()))); +void ConverterRefCount::ConvertFunctionToFunctionPointer( + const clang::FunctionDecl *fn_decl) { + StrCat( + std::format("fn_ptr!({}, {})", GetNamedDeclAsString(fn_decl), + ConvertFunctionPointerType( + fn_decl->getType()->getAs()))); } std::string ConverterRefCount::GetFunctionPointerDefaultAsString( @@ -1063,7 +1058,7 @@ bool ConverterRefCount::VisitFunctionPointerCast( ->getType() ->getPointeeType() ->getAs(); - auto fn_type = GetFnTypeString(target_proto); + auto fn_type = ConvertFunctionPointerType(target_proto); std::string adapter = "None"; // Only accept direct references to the casted function. Otherwise the @@ -1086,7 +1081,7 @@ bool ConverterRefCount::VisitFunctionPointerCast( expr->getType()->isFunctionPointerType()) { auto target_proto = expr->getType()->getPointeeType()->getAs(); - auto fn_type = GetFnTypeString(target_proto); + auto fn_type = ConvertFunctionPointerType(target_proto); StrCat(std::format("{}.cast_fn::<{}>().expect(\"ub:wrong fn type\")", ToString(expr->getSubExpr()), fn_type)); } else { diff --git a/cpp2rust/converter/models/converter_refcount.h b/cpp2rust/converter/models/converter_refcount.h index cb9f3d82..45eefd0a 100644 --- a/cpp2rust/converter/models/converter_refcount.h +++ b/cpp2rust/converter/models/converter_refcount.h @@ -22,6 +22,9 @@ class ConverterRefCount final : public Converter { bool VisitPointerType(clang::PointerType *type) override; + std::string + ConvertFunctionPointerType(const clang::FunctionProtoType *proto) override; + bool VisitCXXRecordDecl(clang::CXXRecordDecl *decl) override; void ConvertOrdAndPartialOrdTraits(const clang::CXXRecordDecl *decl, @@ -61,7 +64,8 @@ class ConverterRefCount final : public Converter { void EmitFnPtrCall(clang::Expr *callee) override; - void EmitFnAsValue(const clang::FunctionDecl *fn_decl) override; + void + ConvertFunctionToFunctionPointer(const clang::FunctionDecl *fn_decl) override; bool VisitCallExpr(clang::CallExpr *expr) override; @@ -182,7 +186,6 @@ class ConverterRefCount final : public Converter { const char *GetPointerDerefSuffix(clang::QualType pointee_type); const char *GetPointerDerefPrefix(clang::QualType pointee_type) override; - std::string GetFnTypeString(const clang::FunctionProtoType *proto); std::string BuildFnAdapter(const clang::FunctionDecl *src_fn, const clang::FunctionProtoType *src_proto, const clang::FunctionProtoType *target_proto); From 24656ca4205cdb32189e06744df62e7be6a4b1e3 Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Fri, 17 Apr 2026 11:29:59 +0100 Subject: [PATCH 06/17] Update existing tests --- .../ub/out/refcount/dangling-prvalue-as-lvalue.rs | 2 +- tests/ub/out/refcount/ub1.rs | 2 +- tests/ub/out/unsafe/dangling-prvalue-as-lvalue.rs | 2 +- tests/ub/out/unsafe/ub1.rs | 2 +- tests/unit/out/refcount/complex_function.rs | 14 +++++++------- tests/unit/out/refcount/no_direct_callee.rs | 8 ++++---- tests/unit/out/refcount/printfs.rs | 4 ++-- tests/unit/out/refcount/prvalue-as-lvalue.rs | 2 +- tests/unit/out/refcount/ref_calls.rs | 2 +- tests/unit/out/unsafe/complex_function.rs | 8 ++++---- tests/unit/out/unsafe/no_direct_callee.rs | 6 +++--- tests/unit/out/unsafe/prvalue-as-lvalue.rs | 2 +- tests/unit/out/unsafe/ref_calls.rs | 2 +- 13 files changed, 28 insertions(+), 28 deletions(-) diff --git a/tests/ub/out/refcount/dangling-prvalue-as-lvalue.rs b/tests/ub/out/refcount/dangling-prvalue-as-lvalue.rs index c5ffd859..fd03296b 100644 --- a/tests/ub/out/refcount/dangling-prvalue-as-lvalue.rs +++ b/tests/ub/out/refcount/dangling-prvalue-as-lvalue.rs @@ -17,7 +17,7 @@ fn main_0() -> i32 { let v: Value> = Rc::new(RefCell::new(vec![1, 2])); let b: Ptr = ({ let _a: Ptr = (v.as_pointer() as Ptr); - Rc::new(foo_0)(_a) + foo_0(_a) }); (*v.borrow_mut()).clear(); return (b.read()); diff --git a/tests/ub/out/refcount/ub1.rs b/tests/ub/out/refcount/ub1.rs index 0b4feefa..e525fce8 100644 --- a/tests/ub/out/refcount/ub1.rs +++ b/tests/ub/out/refcount/ub1.rs @@ -16,6 +16,6 @@ pub fn main() { std::process::exit(main_0()); } fn main_0() -> i32 { - let x: Ptr = ({ Rc::new(dangling_0)() }); + let x: Ptr = ({ dangling_0() }); return (x.read()); } diff --git a/tests/ub/out/unsafe/dangling-prvalue-as-lvalue.rs b/tests/ub/out/unsafe/dangling-prvalue-as-lvalue.rs index 11ddb63f..eeaa1d9b 100644 --- a/tests/ub/out/unsafe/dangling-prvalue-as-lvalue.rs +++ b/tests/ub/out/unsafe/dangling-prvalue-as-lvalue.rs @@ -19,7 +19,7 @@ unsafe fn main_0() -> i32 { let mut v: Vec = vec![1, 2]; let b: *const i32 = (unsafe { let _a: *const i32 = &(*v.as_mut_ptr()) as *const i32; - Rc::new(|a0| unsafe { foo_0(a0) })(_a) + foo_0(_a) }); v.clear(); return (*b); diff --git a/tests/ub/out/unsafe/ub1.rs b/tests/ub/out/unsafe/ub1.rs index bc49a90c..002d9c72 100644 --- a/tests/ub/out/unsafe/ub1.rs +++ b/tests/ub/out/unsafe/ub1.rs @@ -18,6 +18,6 @@ pub fn main() { } } unsafe fn main_0() -> i32 { - let x: *mut i32 = (unsafe { Rc::new(|| unsafe { dangling_0() })() }); + let x: *mut i32 = (unsafe { dangling_0() }); return (*x); } diff --git a/tests/unit/out/refcount/complex_function.rs b/tests/unit/out/refcount/complex_function.rs index 7a7c2038..2d778ae7 100644 --- a/tests/unit/out/refcount/complex_function.rs +++ b/tests/unit/out/refcount/complex_function.rs @@ -133,11 +133,11 @@ fn main_0() -> i32 { let r1: Ptr = x1.as_pointer(); let r2: Ptr = ({ let _x: Ptr = x1.as_pointer(); - Rc::new(bar_2)(_x) + bar_2(_x) }); let r3: Ptr = ({ let _x: Ptr = (r1).clone(); - Rc::new(bar_2)(_x) + bar_2(_x) }); let __rhs = (*x1.borrow()); { @@ -403,7 +403,7 @@ fn main_0() -> i32 { .deref()) .v .as_pointer()); - Rc::new(ptr_1)(_x) + ptr_1(_x) }) .clone(); let __tmp = __ptr.read() + 1; @@ -436,7 +436,7 @@ fn main_0() -> i32 { .deref()) .v .as_pointer()); - Rc::new(ptr_1)(_x) + ptr_1(_x) }); let ptr3: Value> = Rc::new(RefCell::new( ({ @@ -450,7 +450,7 @@ fn main_0() -> i32 { .deref()) .v .as_pointer()); - Rc::new(ptr_1)(_x) + ptr_1(_x) }), )); let vptr: Value = Rc::new(RefCell::new( @@ -481,7 +481,7 @@ fn main_0() -> i32 { .deref()) .v .as_pointer(); - Rc::new(bar_2)(_x) + bar_2(_x) }), )); ({ @@ -495,7 +495,7 @@ fn main_0() -> i32 { .deref()) .v .as_pointer(); - Rc::new(bar_2)(_x) + bar_2(_x) }) .with_mut(|__v| __v.postfix_inc()); return (((({ diff --git a/tests/unit/out/refcount/no_direct_callee.rs b/tests/unit/out/refcount/no_direct_callee.rs index a01b00dc..6b81f983 100644 --- a/tests/unit/out/refcount/no_direct_callee.rs +++ b/tests/unit/out/refcount/no_direct_callee.rs @@ -10,9 +10,9 @@ use std::rc::{Rc, Weak}; pub fn test1_0() -> bool { return false; } -pub fn test_1(fn_: Rc bool>) -> i32 { - let fn_: Value bool>> = Rc::new(RefCell::new(fn_)); - if !({ (*fn_.borrow())() }) { +pub fn test_1(fn_: FnPtr bool>) -> i32 { + let fn_: Value bool>> = Rc::new(RefCell::new(fn_)); + if !({ (*(*fn_.borrow()))() }) { return 1; } return 0; @@ -22,7 +22,7 @@ pub fn main() { } fn main_0() -> i32 { return ({ - let _fn: Rc bool> = Rc::new(test1_0); + let _fn: FnPtr bool> = fn_ptr!(test1_0, fn() -> bool); test_1(_fn) }); } diff --git a/tests/unit/out/refcount/printfs.rs b/tests/unit/out/refcount/printfs.rs index df5ffa6c..a661f372 100644 --- a/tests/unit/out/refcount/printfs.rs +++ b/tests/unit/out/refcount/printfs.rs @@ -44,7 +44,7 @@ fn main_0() -> i32 { .to_c_string_iterator() .chain(std::iter::once(0)) .collect::>(); - Rc::new(fn_0)(_v) + fn_0(_v) }) )) .as_pointer() as Ptr) @@ -53,7 +53,7 @@ fn main_0() -> i32 { "{}", (({ let _v: Ptr> = s.as_pointer(); - Rc::new(fn2_1)(_v) + fn2_1(_v) }) .to_strong() .as_pointer() as Ptr) diff --git a/tests/unit/out/refcount/prvalue-as-lvalue.rs b/tests/unit/out/refcount/prvalue-as-lvalue.rs index a8926cca..610c6ba5 100644 --- a/tests/unit/out/refcount/prvalue-as-lvalue.rs +++ b/tests/unit/out/refcount/prvalue-as-lvalue.rs @@ -18,7 +18,7 @@ fn main_0() -> i32 { let pa: Value> = Rc::new(RefCell::new((a.as_pointer()))); let b: Ptr = ({ let _a: Ptr = (*pa.borrow()).clone(); - Rc::new(foo_0)(_a) + foo_0(_a) }); return (b.read()); } diff --git a/tests/unit/out/refcount/ref_calls.rs b/tests/unit/out/refcount/ref_calls.rs index 41dbbfdb..f9e604f8 100644 --- a/tests/unit/out/refcount/ref_calls.rs +++ b/tests/unit/out/refcount/ref_calls.rs @@ -27,7 +27,7 @@ fn main_0() -> i32 { )); let z: Ptr = ({ let _x: Ptr = x.as_pointer(); - Rc::new(foo_1)(_x) + foo_1(_x) }); return { let _lhs = { diff --git a/tests/unit/out/unsafe/complex_function.rs b/tests/unit/out/unsafe/complex_function.rs index 5d741ea3..5707ff99 100644 --- a/tests/unit/out/unsafe/complex_function.rs +++ b/tests/unit/out/unsafe/complex_function.rs @@ -90,11 +90,11 @@ unsafe fn main_0() -> i32 { let r1: *mut i32 = &mut x1 as *mut i32; let r2: *mut i32 = (unsafe { let _x: *mut i32 = &mut x1 as *mut i32; - Rc::new(|a0| unsafe { bar_2(a0) })(_x) + bar_2(_x) }); let r3: *mut i32 = (unsafe { let _x: *mut i32 = r1; - Rc::new(|a0| unsafe { bar_2(a0) })(_x) + bar_2(_x) }); (*r2) += x1; (*r3) += (*r1); @@ -230,12 +230,12 @@ unsafe fn main_0() -> i32 { let mut pref: *mut i32 = (unsafe { let _x: *mut i32 = &mut (*(unsafe { (*(unsafe { (*(unsafe { d.get() })).get() })).get() })).v as *mut i32; - Rc::new(|a0| unsafe { bar_2(a0) })(_x) + bar_2(_x) }); (*(unsafe { let _x: *mut i32 = &mut (*(unsafe { (*(unsafe { (*(unsafe { d.get() })).get() })).get() })).v as *mut i32; - Rc::new(|a0| unsafe { bar_2(a0) })(_x) + bar_2(_x) })) .postfix_inc(); return (((*(unsafe { diff --git a/tests/unit/out/unsafe/no_direct_callee.rs b/tests/unit/out/unsafe/no_direct_callee.rs index 5c962fe7..a30f6a2b 100644 --- a/tests/unit/out/unsafe/no_direct_callee.rs +++ b/tests/unit/out/unsafe/no_direct_callee.rs @@ -10,8 +10,8 @@ use std::rc::Rc; pub unsafe fn test1_0() -> bool { return false; } -pub unsafe fn test_1(mut fn_: Rc bool>) -> i32 { - if !(unsafe { fn_() }) { +pub unsafe fn test_1(mut fn_: Option bool>) -> i32 { + if !(unsafe { (fn_).unwrap()() }) { return 1; } return 0; @@ -23,7 +23,7 @@ pub fn main() { } unsafe fn main_0() -> i32 { return (unsafe { - let _fn: Rc bool> = Rc::new(|| unsafe { test1_0() }); + let _fn: Option bool> = Some(test1_0); test_1(_fn) }); } diff --git a/tests/unit/out/unsafe/prvalue-as-lvalue.rs b/tests/unit/out/unsafe/prvalue-as-lvalue.rs index 515e0986..4d1040db 100644 --- a/tests/unit/out/unsafe/prvalue-as-lvalue.rs +++ b/tests/unit/out/unsafe/prvalue-as-lvalue.rs @@ -20,7 +20,7 @@ unsafe fn main_0() -> i32 { let mut pa: *mut i32 = (&mut a as *mut i32); let b: *const i32 = (unsafe { let _a: *const i32 = &(*pa) as *const i32; - Rc::new(|a0| unsafe { foo_0(a0) })(_a) + foo_0(_a) }); return (*b); } diff --git a/tests/unit/out/unsafe/ref_calls.rs b/tests/unit/out/unsafe/ref_calls.rs index 0c24b4a9..b32c2b28 100644 --- a/tests/unit/out/unsafe/ref_calls.rs +++ b/tests/unit/out/unsafe/ref_calls.rs @@ -26,7 +26,7 @@ unsafe fn main_0() -> i32 { })); let z: *mut i32 = (unsafe { let _x: *mut i32 = &mut x as *mut i32; - Rc::new(|a0| unsafe { foo_1(a0) })(_x) + foo_1(_x) }); return ((((*(unsafe { let _x: *mut i32 = &mut x as *mut i32; From 0757728d5e0f7c25c93cfa50cb1f7047faed0c59 Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Fri, 17 Apr 2026 11:30:29 +0100 Subject: [PATCH 07/17] Add expected output for fn_ptr tests --- tests/unit/out/refcount/fn_ptr.rs | 48 +++++++ tests/unit/out/refcount/fn_ptr_array.rs | 59 +++++++++ .../unit/out/refcount/fn_ptr_as_condition.rs | 59 +++++++++ tests/unit/out/refcount/fn_ptr_cast.rs | 123 ++++++++++++++++++ tests/unit/out/refcount/fn_ptr_conditional.rs | 94 +++++++++++++ tests/unit/out/refcount/fn_ptr_default_arg.rs | 65 +++++++++ tests/unit/out/refcount/fn_ptr_global.rs | 86 ++++++++++++ tests/unit/out/refcount/fn_ptr_reassign.rs | 66 ++++++++++ tests/unit/out/refcount/fn_ptr_return.rs | 67 ++++++++++ tests/unit/out/refcount/fn_ptr_stable_sort.rs | 80 ++++++++++++ tests/unit/out/refcount/fn_ptr_struct.rs | 78 +++++++++++ tests/unit/out/refcount/fn_ptr_void_return.rs | 53 ++++++++ tests/unit/out/refcount/fn_ptr_vtable.rs | 86 ++++++++++++ tests/unit/out/unsafe/fn_ptr.rs | 43 ++++++ tests/unit/out/unsafe/fn_ptr_array.rs | 51 ++++++++ tests/unit/out/unsafe/fn_ptr_as_condition.rs | 54 ++++++++ tests/unit/out/unsafe/fn_ptr_cast.rs | 94 +++++++++++++ tests/unit/out/unsafe/fn_ptr_conditional.rs | 92 +++++++++++++ tests/unit/out/unsafe/fn_ptr_default_arg.rs | 61 +++++++++ tests/unit/out/unsafe/fn_ptr_global.rs | 76 +++++++++++ tests/unit/out/unsafe/fn_ptr_reassign.rs | 61 +++++++++ tests/unit/out/unsafe/fn_ptr_return.rs | 53 ++++++++ tests/unit/out/unsafe/fn_ptr_stable_sort.rs | 44 +++++++ tests/unit/out/unsafe/fn_ptr_struct.rs | 65 +++++++++ tests/unit/out/unsafe/fn_ptr_void_return.rs | 50 +++++++ tests/unit/out/unsafe/fn_ptr_vtable.rs | 68 ++++++++++ 26 files changed, 1776 insertions(+) create mode 100644 tests/unit/out/refcount/fn_ptr.rs create mode 100644 tests/unit/out/refcount/fn_ptr_array.rs create mode 100644 tests/unit/out/refcount/fn_ptr_as_condition.rs create mode 100644 tests/unit/out/refcount/fn_ptr_cast.rs create mode 100644 tests/unit/out/refcount/fn_ptr_conditional.rs create mode 100644 tests/unit/out/refcount/fn_ptr_default_arg.rs create mode 100644 tests/unit/out/refcount/fn_ptr_global.rs create mode 100644 tests/unit/out/refcount/fn_ptr_reassign.rs create mode 100644 tests/unit/out/refcount/fn_ptr_return.rs create mode 100644 tests/unit/out/refcount/fn_ptr_stable_sort.rs create mode 100644 tests/unit/out/refcount/fn_ptr_struct.rs create mode 100644 tests/unit/out/refcount/fn_ptr_void_return.rs create mode 100644 tests/unit/out/refcount/fn_ptr_vtable.rs create mode 100644 tests/unit/out/unsafe/fn_ptr.rs create mode 100644 tests/unit/out/unsafe/fn_ptr_array.rs create mode 100644 tests/unit/out/unsafe/fn_ptr_as_condition.rs create mode 100644 tests/unit/out/unsafe/fn_ptr_cast.rs create mode 100644 tests/unit/out/unsafe/fn_ptr_conditional.rs create mode 100644 tests/unit/out/unsafe/fn_ptr_default_arg.rs create mode 100644 tests/unit/out/unsafe/fn_ptr_global.rs create mode 100644 tests/unit/out/unsafe/fn_ptr_reassign.rs create mode 100644 tests/unit/out/unsafe/fn_ptr_return.rs create mode 100644 tests/unit/out/unsafe/fn_ptr_stable_sort.rs create mode 100644 tests/unit/out/unsafe/fn_ptr_struct.rs create mode 100644 tests/unit/out/unsafe/fn_ptr_void_return.rs create mode 100644 tests/unit/out/unsafe/fn_ptr_vtable.rs diff --git a/tests/unit/out/refcount/fn_ptr.rs b/tests/unit/out/refcount/fn_ptr.rs new file mode 100644 index 00000000..34753e3f --- /dev/null +++ b/tests/unit/out/refcount/fn_ptr.rs @@ -0,0 +1,48 @@ +extern crate libcc2rs; +use libcc2rs::*; +use std::cell::RefCell; +use std::collections::BTreeMap; +use std::io::prelude::*; +use std::io::Seek; +use std::io::{Read, Write}; +use std::os::fd::AsFd; +use std::rc::{Rc, Weak}; +pub fn my_foo_0(p: AnyPtr) -> i32 { + let p: Value = Rc::new(RefCell::new(p)); + return ((*p.borrow()).cast::().expect("ub:wrong type").read()); +} +pub fn foo_1(fn_: FnPtr i32>, pi: Ptr) -> i32 { + let fn_: Value i32>> = Rc::new(RefCell::new(fn_)); + let pi: Value> = Rc::new(RefCell::new(pi)); + return ({ + let _arg0: AnyPtr = ((*pi.borrow()).clone() as Ptr).to_any(); + (*(*fn_.borrow()))(_arg0) + }); +} +pub fn main() { + std::process::exit(main_0()); +} +fn main_0() -> i32 { + let fn_: Value i32>> = Rc::new(RefCell::new(FnPtr::null())); + assert!((*fn_.borrow()).is_null()); + assert!({ + let _lhs = (*fn_.borrow()).clone(); + _lhs != fn_ptr!(my_foo_0, fn(AnyPtr) -> i32) + }); + (*fn_.borrow_mut()) = fn_ptr!(my_foo_0, fn(AnyPtr) -> i32); + assert!(!((*fn_.borrow()).is_null())); + assert!({ + let _lhs = (*fn_.borrow()).clone(); + _lhs == fn_ptr!(my_foo_0, fn(AnyPtr) -> i32) + }); + let a: Value = Rc::new(RefCell::new(10)); + assert!({ + let _lhs = ({ + let _fn: FnPtr i32> = (*fn_.borrow()).clone(); + let _pi: Ptr = (a.as_pointer()); + foo_1(_fn, _pi) + }); + _lhs == (*a.borrow()) + }); + return 0; +} diff --git a/tests/unit/out/refcount/fn_ptr_array.rs b/tests/unit/out/refcount/fn_ptr_array.rs new file mode 100644 index 00000000..e524cc73 --- /dev/null +++ b/tests/unit/out/refcount/fn_ptr_array.rs @@ -0,0 +1,59 @@ +extern crate libcc2rs; +use libcc2rs::*; +use std::cell::RefCell; +use std::collections::BTreeMap; +use std::io::prelude::*; +use std::io::Seek; +use std::io::{Read, Write}; +use std::os::fd::AsFd; +use std::rc::{Rc, Weak}; +pub fn add_0(a: i32, b: i32) -> i32 { + let a: Value = Rc::new(RefCell::new(a)); + let b: Value = Rc::new(RefCell::new(b)); + return ((*a.borrow()) + (*b.borrow())); +} +pub fn sub_1(a: i32, b: i32) -> i32 { + let a: Value = Rc::new(RefCell::new(a)); + let b: Value = Rc::new(RefCell::new(b)); + return ((*a.borrow()) - (*b.borrow())); +} +pub fn mul_2(a: i32, b: i32) -> i32 { + let a: Value = Rc::new(RefCell::new(a)); + let b: Value = Rc::new(RefCell::new(b)); + return ((*a.borrow()) * (*b.borrow())); +} +pub fn main() { + std::process::exit(main_0()); +} +fn main_0() -> i32 { + let ops: Value i32>]>> = Rc::new(RefCell::new(Box::new([ + fn_ptr!(add_0, fn(i32, i32) -> i32), + fn_ptr!(sub_1, fn(i32, i32) -> i32), + fn_ptr!(mul_2, fn(i32, i32) -> i32), + ]))); + assert!( + (({ + let _arg0: i32 = 2; + let _arg1: i32 = 3; + (*(*ops.borrow())[(0) as usize])(_arg0, _arg1) + }) == 5) + ); + assert!( + (({ + let _arg0: i32 = 7; + let _arg1: i32 = 4; + (*(*ops.borrow())[(1) as usize])(_arg0, _arg1) + }) == 3) + ); + assert!( + (({ + let _arg0: i32 = 6; + let _arg1: i32 = 5; + (*(*ops.borrow())[(2) as usize])(_arg0, _arg1) + }) == 30) + ); + assert!(!(((*ops.borrow())[(0) as usize]).is_null())); + assert!(((*ops.borrow())[(0) as usize] == fn_ptr!(add_0, fn(i32, i32) -> i32))); + assert!(((*ops.borrow())[(0) as usize] != fn_ptr!(sub_1, fn(i32, i32) -> i32))); + return 0; +} diff --git a/tests/unit/out/refcount/fn_ptr_as_condition.rs b/tests/unit/out/refcount/fn_ptr_as_condition.rs new file mode 100644 index 00000000..5afb76cf --- /dev/null +++ b/tests/unit/out/refcount/fn_ptr_as_condition.rs @@ -0,0 +1,59 @@ +extern crate libcc2rs; +use libcc2rs::*; +use std::cell::RefCell; +use std::collections::BTreeMap; +use std::io::prelude::*; +use std::io::Seek; +use std::io::{Read, Write}; +use std::os::fd::AsFd; +use std::rc::{Rc, Weak}; +pub fn double_it_0(x: Ptr) { + let x: Value> = Rc::new(RefCell::new(x)); + { + let __ptr = (*x.borrow()).clone(); + let __tmp = __ptr.read() * 2; + __ptr.write(__tmp) + }; +} +pub fn maybe_call_1(cb: FnPtr)>, x: Ptr) { + let cb: Value)>> = Rc::new(RefCell::new(cb)); + let x: Value> = Rc::new(RefCell::new(x)); + if !(*cb.borrow()).is_null() { + ({ + let _arg0: Ptr = (*x.borrow()).clone(); + (*(*cb.borrow()))(_arg0) + }); + } +} +pub fn main() { + std::process::exit(main_0()); +} +fn main_0() -> i32 { + let a: Value = Rc::new(RefCell::new(5)); + ({ + let _cb: FnPtr)> = fn_ptr!(double_it_0, fn(Ptr::)); + let _x: Ptr = (a.as_pointer()); + maybe_call_1(_cb, _x) + }); + assert!(((*a.borrow()) == 10)); + let b: Value = Rc::new(RefCell::new(5)); + ({ + let _cb: FnPtr)> = FnPtr::null(); + let _x: Ptr = (b.as_pointer()); + maybe_call_1(_cb, _x) + }); + assert!(((*b.borrow()) == 5)); + let fn_: Value)>> = Rc::new(RefCell::new(FnPtr::null())); + if !!(*fn_.borrow()).is_null() { + (*fn_.borrow_mut()) = (fn_ptr!(double_it_0, fn(Ptr::))).clone(); + } + let c: Value = Rc::new(RefCell::new(3)); + if !(*fn_.borrow()).is_null() { + ({ + let _arg0: Ptr = (c.as_pointer()); + (*(*fn_.borrow()))(_arg0) + }); + } + assert!(((*c.borrow()) == 6)); + return 0; +} diff --git a/tests/unit/out/refcount/fn_ptr_cast.rs b/tests/unit/out/refcount/fn_ptr_cast.rs new file mode 100644 index 00000000..400cf447 --- /dev/null +++ b/tests/unit/out/refcount/fn_ptr_cast.rs @@ -0,0 +1,123 @@ +extern crate libcc2rs; +use libcc2rs::*; +use std::cell::RefCell; +use std::collections::BTreeMap; +use std::io::prelude::*; +use std::io::Seek; +use std::io::{Read, Write}; +use std::os::fd::AsFd; +use std::rc::{Rc, Weak}; +pub fn double_it_0(x: i32) -> i32 { + let x: Value = Rc::new(RefCell::new(x)); + return ((*x.borrow()) * 2); +} +pub fn test_roundtrip_1() { + let fn_: Value i32>> = + Rc::new(RefCell::new(fn_ptr!(double_it_0, fn(i32) -> i32))); + assert!( + (({ + let _arg0: i32 = 5; + (*(*fn_.borrow()))(_arg0) + }) == 10) + ); + let gfn: Value> = + Rc::new(RefCell::new(((*fn_.borrow()).cast::(None)).clone())); + assert!(!((*gfn.borrow()).is_null())); + let fn2: Value i32>> = Rc::new(RefCell::new( + ((*gfn.borrow()).cast:: i32>(None)).clone(), + )); + assert!( + (({ + let _arg0: i32 = 5; + (*(*fn2.borrow()))(_arg0) + }) == 10) + ); + assert!({ + let _lhs = (*fn2.borrow()).clone(); + _lhs == (*fn_.borrow()).clone() + }); +} +pub fn test_double_cast_2() { + let fn_: Value i32>> = + Rc::new(RefCell::new(fn_ptr!(double_it_0, fn(i32) -> i32))); + let fn2: Value i32>> = Rc::new(RefCell::new( + ((*fn_.borrow()) + .cast::(None) + .cast:: i32>(None)) + .clone(), + )); + assert!( + (({ + let _arg0: i32 = 5; + (*(*fn2.borrow()))(_arg0) + }) == 10) + ); + assert!({ + let _lhs = (*fn2.borrow()).clone(); + _lhs == (*fn_.borrow()).clone() + }); +} +#[derive(Default)] +pub struct Command { + pub data: Value, +} +impl Clone for Command { + fn clone(&self) -> Self { + let mut this = Self { + data: Rc::new(RefCell::new((*self.data.borrow()).clone())), + }; + this + } +} +impl ByteRepr for Command {} +pub fn test_void_ptr_to_fn_3() { + let cmd: Value = Rc::new(RefCell::new(::default())); + (*(*cmd.borrow()).data.borrow_mut()) = fn_ptr!(double_it_0, fn(i32) -> i32).to_any(); + let fn_: Value i32>> = Rc::new(RefCell::new( + ((*(*cmd.borrow()).data.borrow()) + .cast_fn:: i32>() + .expect("ub:wrong fn type")) + .clone(), + )); + assert!( + (({ + let _arg0: i32 = 5; + (*(*fn_.borrow()))(_arg0) + }) == 10) + ); +} +pub fn add_offset_4(base: Ptr, offset: i32) -> i32 { + let base: Value> = Rc::new(RefCell::new(base)); + let offset: Value = Rc::new(RefCell::new(offset)); + return { + let _lhs = ((*base.borrow()).read()); + _lhs + (*offset.borrow()) + }; +} +pub fn test_call_through_cast_5() { + let gfn: Value i32>> = Rc::new(RefCell::new( + fn_ptr!(add_offset_4, fn(Ptr::, i32) -> i32).cast:: i32>(Some( + (|a0: AnyPtr, a1: i32| -> i32 { add_offset_4(a0.cast::().unwrap(), a1) }) + as fn(AnyPtr, i32) -> i32, + )), + )); + let val: Value = Rc::new(RefCell::new(100)); + let result: Value = Rc::new(RefCell::new( + ({ + let _arg0: AnyPtr = ((val.as_pointer()) as Ptr).to_any(); + let _arg1: i32 = 42; + (*(*gfn.borrow()))(_arg0, _arg1) + }), + )); + assert!(((*result.borrow()) == 142)); +} +pub fn main() { + std::process::exit(main_0()); +} +fn main_0() -> i32 { + ({ test_roundtrip_1() }); + ({ test_double_cast_2() }); + ({ test_void_ptr_to_fn_3() }); + ({ test_call_through_cast_5() }); + return 0; +} diff --git a/tests/unit/out/refcount/fn_ptr_conditional.rs b/tests/unit/out/refcount/fn_ptr_conditional.rs new file mode 100644 index 00000000..88684794 --- /dev/null +++ b/tests/unit/out/refcount/fn_ptr_conditional.rs @@ -0,0 +1,94 @@ +extern crate libcc2rs; +use libcc2rs::*; +use std::cell::RefCell; +use std::collections::BTreeMap; +use std::io::prelude::*; +use std::io::Seek; +use std::io::{Read, Write}; +use std::os::fd::AsFd; +use std::rc::{Rc, Weak}; +pub fn inc_0(x: i32) -> i32 { + let x: Value = Rc::new(RefCell::new(x)); + return ((*x.borrow()) + 1); +} +pub fn dec_1(x: i32) -> i32 { + let x: Value = Rc::new(RefCell::new(x)); + return ((*x.borrow()) - 1); +} +pub fn identity_2(x: i32) -> i32 { + let x: Value = Rc::new(RefCell::new(x)); + return (*x.borrow()); +} +pub fn pick_3(mode: i32) -> FnPtr i32> { + let mode: Value = Rc::new(RefCell::new(mode)); + return if ((*mode.borrow()) > 0) { + fn_ptr!(inc_0, fn(i32) -> i32) + } else { + if ((*mode.borrow()) < 0) { + fn_ptr!(dec_1, fn(i32) -> i32) + } else { + fn_ptr!(identity_2, fn(i32) -> i32) + } + }; +} +pub fn apply_4(fn_: FnPtr i32>, x: i32) -> i32 { + let fn_: Value i32>> = Rc::new(RefCell::new(fn_)); + let x: Value = Rc::new(RefCell::new(x)); + let actual: Value i32>> = + Rc::new(RefCell::new(if !(*fn_.borrow()).is_null() { + (*fn_.borrow()).clone() + } else { + fn_ptr!(identity_2, fn(i32) -> i32) + })); + return ({ + let _arg0: i32 = (*x.borrow()); + (*(*actual.borrow()))(_arg0) + }); +} +pub fn main() { + std::process::exit(main_0()); +} +fn main_0() -> i32 { + assert!( + (({ + let _arg0: i32 = 10; + (*({ + let _mode: i32 = 1; + pick_3(_mode) + }))(_arg0) + }) == 11) + ); + assert!( + (({ + let _arg0: i32 = 10; + (*({ + let _mode: i32 = -1_i32; + pick_3(_mode) + }))(_arg0) + }) == 9) + ); + assert!( + (({ + let _arg0: i32 = 10; + (*({ + let _mode: i32 = 0; + pick_3(_mode) + }))(_arg0) + }) == 10) + ); + assert!( + (({ + let _fn: FnPtr i32> = fn_ptr!(inc_0, fn(i32) -> i32); + let _x: i32 = 5; + apply_4(_fn, _x) + }) == 6) + ); + assert!( + (({ + let _fn: FnPtr i32> = FnPtr::null(); + let _x: i32 = 5; + apply_4(_fn, _x) + }) == 5) + ); + return 0; +} diff --git a/tests/unit/out/refcount/fn_ptr_default_arg.rs b/tests/unit/out/refcount/fn_ptr_default_arg.rs new file mode 100644 index 00000000..0c598b62 --- /dev/null +++ b/tests/unit/out/refcount/fn_ptr_default_arg.rs @@ -0,0 +1,65 @@ +extern crate libcc2rs; +use libcc2rs::*; +use std::cell::RefCell; +use std::collections::BTreeMap; +use std::io::prelude::*; +use std::io::Seek; +use std::io::{Read, Write}; +use std::os::fd::AsFd; +use std::rc::{Rc, Weak}; +pub fn identity_0(x: i32) -> i32 { + let x: Value = Rc::new(RefCell::new(x)); + return (*x.borrow()); +} +pub fn apply_1(x: i32, fn_: Option i32>>) -> i32 { + let x: Value = Rc::new(RefCell::new(x)); + let fn_: Value i32>> = Rc::new(RefCell::new(fn_.unwrap_or(FnPtr::null()))); + if !(*fn_.borrow()).is_null() { + return ({ + let _arg0: i32 = (*x.borrow()); + (*(*fn_.borrow()))(_arg0) + }); + } + return (*x.borrow()); +} +pub fn main() { + std::process::exit(main_0()); +} +fn main_0() -> i32 { + assert!( + (({ + let _x: i32 = 5; + let _fn: FnPtr i32> = Default::default(); + apply_1(_x, Some(_fn)) + }) == 5) + ); + assert!( + (({ + let _x: i32 = 5; + let _fn: FnPtr i32> = FnPtr::null(); + apply_1(_x, Some(_fn)) + }) == 5) + ); + assert!( + (({ + let _x: i32 = 5; + let _fn: FnPtr i32> = fn_ptr!(identity_0, fn(i32) -> i32); + apply_1(_x, Some(_fn)) + }) == 5) + ); + let negate: Value i32>> = Rc::new(RefCell::new(FnPtr::new( + (|x: i32| { + let x: Value = Rc::new(RefCell::new(x)); + return -(*x.borrow()); + }), + 0, + ))); + assert!( + (({ + let _x: i32 = 5; + let _fn: FnPtr i32> = (*negate.borrow()).clone(); + apply_1(_x, Some(_fn)) + }) == -5_i32) + ); + return 0; +} diff --git a/tests/unit/out/refcount/fn_ptr_global.rs b/tests/unit/out/refcount/fn_ptr_global.rs new file mode 100644 index 00000000..94b7d347 --- /dev/null +++ b/tests/unit/out/refcount/fn_ptr_global.rs @@ -0,0 +1,86 @@ +extern crate libcc2rs; +use libcc2rs::*; +use std::cell::RefCell; +use std::collections::BTreeMap; +use std::io::prelude::*; +use std::io::Seek; +use std::io::{Read, Write}; +use std::os::fd::AsFd; +use std::rc::{Rc, Weak}; +pub fn double_it_0(x: i32) -> i32 { + let x: Value = Rc::new(RefCell::new(x)); + return ((*x.borrow()) * 2); +} +pub fn triple_it_1(x: i32) -> i32 { + let x: Value = Rc::new(RefCell::new(x)); + return ((*x.borrow()) * 3); +} +thread_local!( + pub static g_op: Value i32>> = Rc::new(RefCell::new(FnPtr::null())); +); +pub fn set_op_2(fn_: FnPtr i32>) { + let fn_: Value i32>> = Rc::new(RefCell::new(fn_)); + (*g_op.with(Value::clone).borrow_mut()) = (*fn_.borrow()).clone(); +} +pub fn call_op_3(x: i32) -> i32 { + let x: Value = Rc::new(RefCell::new(x)); + if !(*g_op.with(Value::clone).borrow()).is_null() { + return ({ + let _arg0: i32 = (*x.borrow()); + (*(*g_op.with(Value::clone).borrow()))(_arg0) + }); + } + return (*x.borrow()); +} +pub fn main() { + std::process::exit(main_0()); +} +fn main_0() -> i32 { + assert!( + (({ + let _x: i32 = 5; + call_op_3(_x) + }) == 5) + ); + ({ + let _fn: FnPtr i32> = fn_ptr!(double_it_0, fn(i32) -> i32); + set_op_2(_fn) + }); + assert!(!((*g_op.with(Value::clone).borrow()).is_null())); + assert!({ + let _lhs = (*g_op.with(Value::clone).borrow()).clone(); + _lhs == fn_ptr!(double_it_0, fn(i32) -> i32) + }); + assert!( + (({ + let _x: i32 = 5; + call_op_3(_x) + }) == 10) + ); + ({ + let _fn: FnPtr i32> = fn_ptr!(triple_it_1, fn(i32) -> i32); + set_op_2(_fn) + }); + assert!({ + let _lhs = (*g_op.with(Value::clone).borrow()).clone(); + _lhs == fn_ptr!(triple_it_1, fn(i32) -> i32) + }); + assert!( + (({ + let _x: i32 = 5; + call_op_3(_x) + }) == 15) + ); + ({ + let _fn: FnPtr i32> = FnPtr::null(); + set_op_2(_fn) + }); + assert!((*g_op.with(Value::clone).borrow()).is_null()); + assert!( + (({ + let _x: i32 = 5; + call_op_3(_x) + }) == 5) + ); + return 0; +} diff --git a/tests/unit/out/refcount/fn_ptr_reassign.rs b/tests/unit/out/refcount/fn_ptr_reassign.rs new file mode 100644 index 00000000..c8c47f6d --- /dev/null +++ b/tests/unit/out/refcount/fn_ptr_reassign.rs @@ -0,0 +1,66 @@ +extern crate libcc2rs; +use libcc2rs::*; +use std::cell::RefCell; +use std::collections::BTreeMap; +use std::io::prelude::*; +use std::io::Seek; +use std::io::{Read, Write}; +use std::os::fd::AsFd; +use std::rc::{Rc, Weak}; +pub fn add_0(a: i32, b: i32) -> i32 { + let a: Value = Rc::new(RefCell::new(a)); + let b: Value = Rc::new(RefCell::new(b)); + return ((*a.borrow()) + (*b.borrow())); +} +pub fn sub_1(a: i32, b: i32) -> i32 { + let a: Value = Rc::new(RefCell::new(a)); + let b: Value = Rc::new(RefCell::new(b)); + return ((*a.borrow()) - (*b.borrow())); +} +pub fn mul_2(a: i32, b: i32) -> i32 { + let a: Value = Rc::new(RefCell::new(a)); + let b: Value = Rc::new(RefCell::new(b)); + return ((*a.borrow()) * (*b.borrow())); +} +pub fn main() { + std::process::exit(main_0()); +} +fn main_0() -> i32 { + let fn_: Value i32>> = + Rc::new(RefCell::new(fn_ptr!(add_0, fn(i32, i32) -> i32))); + assert!( + (({ + let _arg0: i32 = 3; + let _arg1: i32 = 4; + (*(*fn_.borrow()))(_arg0, _arg1) + }) == 7) + ); + (*fn_.borrow_mut()) = fn_ptr!(sub_1, fn(i32, i32) -> i32); + assert!( + (({ + let _arg0: i32 = 10; + let _arg1: i32 = 3; + (*(*fn_.borrow()))(_arg0, _arg1) + }) == 7) + ); + (*fn_.borrow_mut()) = fn_ptr!(mul_2, fn(i32, i32) -> i32); + assert!( + (({ + let _arg0: i32 = 6; + let _arg1: i32 = 7; + (*(*fn_.borrow()))(_arg0, _arg1) + }) == 42) + ); + (*fn_.borrow_mut()) = FnPtr::null(); + assert!((*fn_.borrow()).is_null()); + (*fn_.borrow_mut()) = fn_ptr!(add_0, fn(i32, i32) -> i32); + assert!(!((*fn_.borrow()).is_null())); + assert!( + (({ + let _arg0: i32 = 1; + let _arg1: i32 = 1; + (*(*fn_.borrow()))(_arg0, _arg1) + }) == 2) + ); + return 0; +} diff --git a/tests/unit/out/refcount/fn_ptr_return.rs b/tests/unit/out/refcount/fn_ptr_return.rs new file mode 100644 index 00000000..b80f7c1b --- /dev/null +++ b/tests/unit/out/refcount/fn_ptr_return.rs @@ -0,0 +1,67 @@ +extern crate libcc2rs; +use libcc2rs::*; +use std::cell::RefCell; +use std::collections::BTreeMap; +use std::io::prelude::*; +use std::io::Seek; +use std::io::{Read, Write}; +use std::os::fd::AsFd; +use std::rc::{Rc, Weak}; +pub fn inc_0(x: i32) -> i32 { + let x: Value = Rc::new(RefCell::new(x)); + return ((*x.borrow()) + 1); +} +pub fn dec_1(x: i32) -> i32 { + let x: Value = Rc::new(RefCell::new(x)); + return ((*x.borrow()) - 1); +} +pub fn pick_2(choose_inc: i32) -> FnPtr i32> { + let choose_inc: Value = Rc::new(RefCell::new(choose_inc)); + if ((*choose_inc.borrow()) != 0) { + return fn_ptr!(inc_0, fn(i32) -> i32); + } + return fn_ptr!(dec_1, fn(i32) -> i32); +} +pub fn main() { + std::process::exit(main_0()); +} +fn main_0() -> i32 { + let f: Value i32>> = Rc::new(RefCell::new( + ({ + let _choose_inc: i32 = 1; + pick_2(_choose_inc) + }), + )); + assert!(!((*f.borrow()).is_null())); + assert!({ + let _lhs = (*f.borrow()).clone(); + _lhs == fn_ptr!(inc_0, fn(i32) -> i32) + }); + assert!( + (({ + let _arg0: i32 = 10; + (*(*f.borrow()))(_arg0) + }) == 11) + ); + let g: Value i32>> = Rc::new(RefCell::new( + ({ + let _choose_inc: i32 = 0; + pick_2(_choose_inc) + }), + )); + assert!({ + let _lhs = (*g.borrow()).clone(); + _lhs == fn_ptr!(dec_1, fn(i32) -> i32) + }); + assert!( + (({ + let _arg0: i32 = 10; + (*(*g.borrow()))(_arg0) + }) == 9) + ); + assert!({ + let _lhs = (*f.borrow()).clone(); + _lhs != (*g.borrow()).clone() + }); + return 0; +} diff --git a/tests/unit/out/refcount/fn_ptr_stable_sort.rs b/tests/unit/out/refcount/fn_ptr_stable_sort.rs new file mode 100644 index 00000000..2770c956 --- /dev/null +++ b/tests/unit/out/refcount/fn_ptr_stable_sort.rs @@ -0,0 +1,80 @@ +extern crate libcc2rs; +use libcc2rs::*; +use std::cell::RefCell; +use std::collections::BTreeMap; +use std::io::prelude::*; +use std::io::Seek; +use std::io::{Read, Write}; +use std::os::fd::AsFd; +use std::rc::{Rc, Weak}; +#[derive(Default)] +pub struct Item { + pub key: Value, + pub value: Value, +} +impl Clone for Item { + fn clone(&self) -> Self { + let mut this = Self { + key: Rc::new(RefCell::new((*self.key.borrow()))), + value: Rc::new(RefCell::new((*self.value.borrow()))), + }; + this + } +} +impl ByteRepr for Item {} +pub fn Compare_0(a: Ptr, b: Ptr) -> bool { + return { + let _lhs = (*(*a.upgrade().deref()).key.borrow()); + _lhs < (*(*b.upgrade().deref()).key.borrow()) + }; +} +pub fn main() { + std::process::exit(main_0()); +} +fn main_0() -> i32 { + let v: Value> = Rc::new(RefCell::new(Vec::new())); + (*v.borrow_mut()).push(Item { + key: Rc::new(RefCell::new(3)), + value: Rc::new(RefCell::new(30)), + }); + (*v.borrow_mut()).push(Item { + key: Rc::new(RefCell::new(1)), + value: Rc::new(RefCell::new(10)), + }); + (*v.borrow_mut()).push(Item { + key: Rc::new(RefCell::new(2)), + value: Rc::new(RefCell::new(20)), + }); + (v.as_pointer() as Ptr).sort_with_cmp( + (v.as_pointer() as Ptr).to_end().get_offset(), + Compare_0, + ); + assert!( + ((*(*(v.as_pointer() as Ptr) + .offset(0_u64 as isize) + .upgrade() + .deref()) + .key + .borrow()) + == 1) + ); + assert!( + ((*(*(v.as_pointer() as Ptr) + .offset(1_u64 as isize) + .upgrade() + .deref()) + .key + .borrow()) + == 2) + ); + assert!( + ((*(*(v.as_pointer() as Ptr) + .offset(2_u64 as isize) + .upgrade() + .deref()) + .key + .borrow()) + == 3) + ); + return 0; +} diff --git a/tests/unit/out/refcount/fn_ptr_struct.rs b/tests/unit/out/refcount/fn_ptr_struct.rs new file mode 100644 index 00000000..0d34f1d4 --- /dev/null +++ b/tests/unit/out/refcount/fn_ptr_struct.rs @@ -0,0 +1,78 @@ +extern crate libcc2rs; +use libcc2rs::*; +use std::cell::RefCell; +use std::collections::BTreeMap; +use std::io::prelude::*; +use std::io::Seek; +use std::io::{Read, Write}; +use std::os::fd::AsFd; +use std::rc::{Rc, Weak}; +#[derive()] +pub struct Handler { + pub tag: Value, + pub cb: Value i32>>, +} +impl Clone for Handler { + fn clone(&self) -> Self { + let mut this = Self { + tag: Rc::new(RefCell::new((*self.tag.borrow()))), + cb: Rc::new(RefCell::new((*self.cb.borrow()).clone())), + }; + this + } +} +impl Default for Handler { + fn default() -> Self { + Handler { + tag: >::default(), + cb: Rc::new(RefCell::new(FnPtr::null())), + } + } +} +impl ByteRepr for Handler {} +pub fn double_it_0(x: i32) -> i32 { + let x: Value = Rc::new(RefCell::new(x)); + return ((*x.borrow()) * 2); +} +pub fn negate_1(x: i32) -> i32 { + let x: Value = Rc::new(RefCell::new(x)); + return -(*x.borrow()); +} +pub fn main() { + std::process::exit(main_0()); +} +fn main_0() -> i32 { + let h1: Value = Rc::new(RefCell::new(Handler { + tag: Rc::new(RefCell::new(1)), + cb: Rc::new(RefCell::new(fn_ptr!(double_it_0, fn(i32) -> i32))), + })); + let h2: Value = Rc::new(RefCell::new(Handler { + tag: Rc::new(RefCell::new(2)), + cb: Rc::new(RefCell::new(fn_ptr!(negate_1, fn(i32) -> i32))), + })); + assert!(!((*(*h1.borrow()).cb.borrow()).is_null())); + assert!( + (({ + let _arg0: i32 = 5; + (*(*(*h1.borrow()).cb.borrow()))(_arg0) + }) == 10) + ); + assert!( + (({ + let _arg0: i32 = 7; + (*(*(*h2.borrow()).cb.borrow()))(_arg0) + }) == -7_i32) + ); + (*(*h1.borrow()).cb.borrow_mut()) = fn_ptr!(negate_1, fn(i32) -> i32); + assert!( + (({ + let _arg0: i32 = 3; + (*(*(*h1.borrow()).cb.borrow()))(_arg0) + }) == -3_i32) + ); + assert!({ + let _lhs = (*(*h1.borrow()).cb.borrow()).clone(); + _lhs == (*(*h2.borrow()).cb.borrow()).clone() + }); + return 0; +} diff --git a/tests/unit/out/refcount/fn_ptr_void_return.rs b/tests/unit/out/refcount/fn_ptr_void_return.rs new file mode 100644 index 00000000..dc49639c --- /dev/null +++ b/tests/unit/out/refcount/fn_ptr_void_return.rs @@ -0,0 +1,53 @@ +extern crate libcc2rs; +use libcc2rs::*; +use std::cell::RefCell; +use std::collections::BTreeMap; +use std::io::prelude::*; +use std::io::Seek; +use std::io::{Read, Write}; +use std::os::fd::AsFd; +use std::rc::{Rc, Weak}; +pub fn negate_0(x: Ptr) { + let x: Value> = Rc::new(RefCell::new(x)); + let __rhs = -((*x.borrow()).read()); + (*x.borrow()).write(__rhs); +} +pub fn zero_out_1(x: Ptr) { + let x: Value> = Rc::new(RefCell::new(x)); + (*x.borrow()).write(0); +} +pub fn run_2(fn_: FnPtr)>, x: Ptr) { + let fn_: Value)>> = Rc::new(RefCell::new(fn_)); + let x: Value> = Rc::new(RefCell::new(x)); + ({ + let _arg0: Ptr = (*x.borrow()).clone(); + (*(*fn_.borrow()))(_arg0) + }); +} +pub fn main() { + std::process::exit(main_0()); +} +fn main_0() -> i32 { + let a: Value = Rc::new(RefCell::new(42)); + ({ + let _fn: FnPtr)> = fn_ptr!(negate_0, fn(Ptr::)); + let _x: Ptr = (a.as_pointer()); + run_2(_fn, _x) + }); + assert!(((*a.borrow()) == -42_i32)); + ({ + let _fn: FnPtr)> = fn_ptr!(zero_out_1, fn(Ptr::)); + let _x: Ptr = (a.as_pointer()); + run_2(_fn, _x) + }); + assert!(((*a.borrow()) == 0)); + let fn_: Value)>> = Rc::new(RefCell::new(fn_ptr!(negate_0, fn(Ptr::)))); + assert!(!((*fn_.borrow()).is_null())); + let b: Value = Rc::new(RefCell::new(10)); + ({ + let _arg0: Ptr = (b.as_pointer()); + (*(*fn_.borrow()))(_arg0) + }); + assert!(((*b.borrow()) == -10_i32)); + return 0; +} diff --git a/tests/unit/out/refcount/fn_ptr_vtable.rs b/tests/unit/out/refcount/fn_ptr_vtable.rs new file mode 100644 index 00000000..b8e80be5 --- /dev/null +++ b/tests/unit/out/refcount/fn_ptr_vtable.rs @@ -0,0 +1,86 @@ +extern crate libcc2rs; +use libcc2rs::*; +use std::cell::RefCell; +use std::collections::BTreeMap; +use std::io::prelude::*; +use std::io::Seek; +use std::io::{Read, Write}; +use std::os::fd::AsFd; +use std::rc::{Rc, Weak}; +#[derive()] +pub struct Vtable { + pub create: Value AnyPtr>>, + pub get: Value i32>>, + pub destroy: Value>, +} +impl Clone for Vtable { + fn clone(&self) -> Self { + let mut this = Self { + create: Rc::new(RefCell::new((*self.create.borrow()).clone())), + get: Rc::new(RefCell::new((*self.get.borrow()).clone())), + destroy: Rc::new(RefCell::new((*self.destroy.borrow()).clone())), + }; + this + } +} +impl Default for Vtable { + fn default() -> Self { + Vtable { + create: Rc::new(RefCell::new(FnPtr::null())), + get: Rc::new(RefCell::new(FnPtr::null())), + destroy: Rc::new(RefCell::new(FnPtr::null())), + } + } +} +impl ByteRepr for Vtable {} +thread_local!( + pub static storage: Value = >::default(); +); +pub fn int_create_0(val: i32) -> AnyPtr { + let val: Value = Rc::new(RefCell::new(val)); + (*storage.with(Value::clone).borrow_mut()) = (*val.borrow()); + return ((storage.with(Value::clone).as_pointer()) as Ptr).to_any(); +} +pub fn int_get_1(p: AnyPtr) -> i32 { + let p: Value = Rc::new(RefCell::new(p)); + return ((*p.borrow()).cast::().expect("ub:wrong type").read()); +} +pub fn int_destroy_2(p: AnyPtr) { + let p: Value = Rc::new(RefCell::new(p)); + (*p.borrow()).cast::().expect("ub:wrong type").write(0); +} +pub fn main() { + std::process::exit(main_0()); +} +fn main_0() -> i32 { + let vt: Value = Rc::new(RefCell::new(Vtable { + create: Rc::new(RefCell::new( + (fn_ptr!(int_create_0, fn(i32) -> AnyPtr)).clone(), + )), + get: Rc::new(RefCell::new(fn_ptr!(int_get_1, fn(AnyPtr) -> i32))), + destroy: Rc::new(RefCell::new(fn_ptr!(int_destroy_2, fn(AnyPtr)))), + })); + assert!(!((*(*vt.borrow()).create.borrow()).is_null())); + assert!(!((*(*vt.borrow()).get.borrow()).is_null())); + assert!(!((*(*vt.borrow()).destroy.borrow()).is_null())); + let obj: Value = Rc::new(RefCell::new( + ({ + let _arg0: i32 = 42; + (*(*(*vt.borrow()).create.borrow()))(_arg0) + }), + )); + assert!( + (({ + let _arg0: AnyPtr = (*obj.borrow()).clone(); + (*(*(*vt.borrow()).get.borrow()))(_arg0) + }) == 42) + ); + ({ + let _arg0: AnyPtr = (*obj.borrow()).clone(); + (*(*(*vt.borrow()).destroy.borrow()))(_arg0) + }); + assert!(((*storage.with(Value::clone).borrow()) == 0)); + (*(*vt.borrow()).get.borrow_mut()) = FnPtr::null(); + assert!((*(*vt.borrow()).get.borrow()).is_null()); + return 0; +} diff --git a/tests/unit/out/unsafe/fn_ptr.rs b/tests/unit/out/unsafe/fn_ptr.rs new file mode 100644 index 00000000..60ce3036 --- /dev/null +++ b/tests/unit/out/unsafe/fn_ptr.rs @@ -0,0 +1,43 @@ +extern crate libc; +use libc::*; +extern crate libcc2rs; +use libcc2rs::*; +use std::collections::BTreeMap; +use std::io::Seek; +use std::io::{Read, Write}; +use std::os::fd::{AsFd, FromRawFd, IntoRawFd}; +use std::rc::Rc; +pub unsafe fn my_foo_0(mut p: *mut ::libc::c_void) -> i32 { + return (*(p as *mut i32)); +} +pub unsafe fn foo_1( + mut fn_: Option i32>, + mut pi: *mut i32, +) -> i32 { + return (unsafe { + let _arg0: *mut ::libc::c_void = (pi as *mut i32 as *mut ::libc::c_void); + (fn_).unwrap()(_arg0) + }); +} +pub fn main() { + unsafe { + std::process::exit(main_0() as i32); + } +} +unsafe fn main_0() -> i32 { + let mut fn_: Option i32> = None; + assert!((fn_).is_none()); + assert!(((fn_) != (Some(my_foo_0)))); + fn_ = Some(my_foo_0); + assert!(!((fn_).is_none())); + assert!(((fn_) == (Some(my_foo_0)))); + let mut a: i32 = 10; + assert!( + ((unsafe { + let _fn: Option i32> = fn_; + let _pi: *mut i32 = (&mut a as *mut i32); + foo_1(_fn, _pi) + }) == (a)) + ); + return 0; +} diff --git a/tests/unit/out/unsafe/fn_ptr_array.rs b/tests/unit/out/unsafe/fn_ptr_array.rs new file mode 100644 index 00000000..1e224706 --- /dev/null +++ b/tests/unit/out/unsafe/fn_ptr_array.rs @@ -0,0 +1,51 @@ +extern crate libc; +use libc::*; +extern crate libcc2rs; +use libcc2rs::*; +use std::collections::BTreeMap; +use std::io::Seek; +use std::io::{Read, Write}; +use std::os::fd::{AsFd, FromRawFd, IntoRawFd}; +use std::rc::Rc; +pub unsafe fn add_0(mut a: i32, mut b: i32) -> i32 { + return ((a) + (b)); +} +pub unsafe fn sub_1(mut a: i32, mut b: i32) -> i32 { + return ((a) - (b)); +} +pub unsafe fn mul_2(mut a: i32, mut b: i32) -> i32 { + return ((a) * (b)); +} +pub fn main() { + unsafe { + std::process::exit(main_0() as i32); + } +} +unsafe fn main_0() -> i32 { + let mut ops: [Option i32>; 3] = [Some(add_0), Some(sub_1), Some(mul_2)]; + assert!( + ((unsafe { + let _arg0: i32 = 2; + let _arg1: i32 = 3; + (ops[(0) as usize]).unwrap()(_arg0, _arg1) + }) == (5)) + ); + assert!( + ((unsafe { + let _arg0: i32 = 7; + let _arg1: i32 = 4; + (ops[(1) as usize]).unwrap()(_arg0, _arg1) + }) == (3)) + ); + assert!( + ((unsafe { + let _arg0: i32 = 6; + let _arg1: i32 = 5; + (ops[(2) as usize]).unwrap()(_arg0, _arg1) + }) == (30)) + ); + assert!(!((ops[(0) as usize]).is_none())); + assert!(((ops[(0) as usize]) == (Some(add_0)))); + assert!(((ops[(0) as usize]) != (Some(sub_1)))); + return 0; +} diff --git a/tests/unit/out/unsafe/fn_ptr_as_condition.rs b/tests/unit/out/unsafe/fn_ptr_as_condition.rs new file mode 100644 index 00000000..7669fb31 --- /dev/null +++ b/tests/unit/out/unsafe/fn_ptr_as_condition.rs @@ -0,0 +1,54 @@ +extern crate libc; +use libc::*; +extern crate libcc2rs; +use libcc2rs::*; +use std::collections::BTreeMap; +use std::io::Seek; +use std::io::{Read, Write}; +use std::os::fd::{AsFd, FromRawFd, IntoRawFd}; +use std::rc::Rc; +pub unsafe fn double_it_0(mut x: *mut i32) { + (*x) *= 2; +} +pub unsafe fn maybe_call_1(mut cb: Option, mut x: *mut i32) { + if !(cb).is_none() { + (unsafe { + let _arg0: *mut i32 = x; + (cb).unwrap()(_arg0) + }); + } +} +pub fn main() { + unsafe { + std::process::exit(main_0() as i32); + } +} +unsafe fn main_0() -> i32 { + let mut a: i32 = 5; + (unsafe { + let _cb: Option = Some(double_it_0); + let _x: *mut i32 = (&mut a as *mut i32); + maybe_call_1(_cb, _x) + }); + assert!(((a) == (10))); + let mut b: i32 = 5; + (unsafe { + let _cb: Option = None; + let _x: *mut i32 = (&mut b as *mut i32); + maybe_call_1(_cb, _x) + }); + assert!(((b) == (5))); + let mut fn_: Option = None; + if !!(fn_).is_none() { + fn_ = Some(double_it_0); + } + let mut c: i32 = 3; + if !(fn_).is_none() { + (unsafe { + let _arg0: *mut i32 = (&mut c as *mut i32); + (fn_).unwrap()(_arg0) + }); + } + assert!(((c) == (6))); + return 0; +} diff --git a/tests/unit/out/unsafe/fn_ptr_cast.rs b/tests/unit/out/unsafe/fn_ptr_cast.rs new file mode 100644 index 00000000..9fce4e7c --- /dev/null +++ b/tests/unit/out/unsafe/fn_ptr_cast.rs @@ -0,0 +1,94 @@ +extern crate libc; +use libc::*; +extern crate libcc2rs; +use libcc2rs::*; +use std::collections::BTreeMap; +use std::io::Seek; +use std::io::{Read, Write}; +use std::os::fd::{AsFd, FromRawFd, IntoRawFd}; +use std::rc::Rc; +pub unsafe fn double_it_0(mut x: i32) -> i32 { + return ((x) * (2)); +} +pub unsafe fn test_roundtrip_1() { + let mut fn_: Option i32> = Some(double_it_0); + assert!( + ((unsafe { + let _arg0: i32 = 5; + (fn_).unwrap()(_arg0) + }) == (10)) + ); + let mut gfn: Option = + std::mem::transmute:: i32>, Option>(fn_); + assert!(!((gfn).is_none())); + let mut fn2: Option i32> = + std::mem::transmute::, Option i32>>(gfn); + assert!( + ((unsafe { + let _arg0: i32 = 5; + (fn2).unwrap()(_arg0) + }) == (10)) + ); + assert!(((fn2) == (fn_))); +} +pub unsafe fn test_double_cast_2() { + let mut fn_: Option i32> = Some(double_it_0); + let mut fn2: Option i32> = + std::mem::transmute::, Option i32>>( + std::mem::transmute:: i32>, Option>(fn_), + ); + assert!( + ((unsafe { + let _arg0: i32 = 5; + (fn2).unwrap()(_arg0) + }) == (10)) + ); + assert!(((fn2) == (fn_))); +} +#[derive(Copy, Clone, Default)] +pub struct Command { + pub data: *mut ::libc::c_void, +} +pub unsafe fn test_void_ptr_to_fn_3() { + let mut cmd: Command = ::default(); + cmd.data = std::mem::transmute:: i32>, *mut ::libc::c_void>(Some( + double_it_0, + )); + let mut fn_: Option i32> = + std::mem::transmute::<*mut ::libc::c_void, Option i32>>(cmd.data); + assert!( + ((unsafe { + let _arg0: i32 = 5; + (fn_).unwrap()(_arg0) + }) == (10)) + ); +} +pub unsafe fn add_offset_4(mut base: *mut i32, mut offset: i32) -> i32 { + return ((*base) + (offset)); +} +pub unsafe fn test_call_through_cast_5() { + let mut gfn: Option i32> = std::mem::transmute::< + Option i32>, + Option i32>, + >(Some(add_offset_4)); + let mut val: i32 = 100; + let mut result: i32 = (unsafe { + let _arg0: *mut ::libc::c_void = + ((&mut val as *mut i32) as *mut i32 as *mut ::libc::c_void); + let _arg1: i32 = 42; + (gfn).unwrap()(_arg0, _arg1) + }); + assert!(((result) == (142))); +} +pub fn main() { + unsafe { + std::process::exit(main_0() as i32); + } +} +unsafe fn main_0() -> i32 { + (unsafe { test_roundtrip_1() }); + (unsafe { test_double_cast_2() }); + (unsafe { test_void_ptr_to_fn_3() }); + (unsafe { test_call_through_cast_5() }); + return 0; +} diff --git a/tests/unit/out/unsafe/fn_ptr_conditional.rs b/tests/unit/out/unsafe/fn_ptr_conditional.rs new file mode 100644 index 00000000..0694a0b1 --- /dev/null +++ b/tests/unit/out/unsafe/fn_ptr_conditional.rs @@ -0,0 +1,92 @@ +extern crate libc; +use libc::*; +extern crate libcc2rs; +use libcc2rs::*; +use std::collections::BTreeMap; +use std::io::Seek; +use std::io::{Read, Write}; +use std::os::fd::{AsFd, FromRawFd, IntoRawFd}; +use std::rc::Rc; +pub unsafe fn inc_0(mut x: i32) -> i32 { + return ((x) + (1)); +} +pub unsafe fn dec_1(mut x: i32) -> i32 { + return ((x) - (1)); +} +pub unsafe fn identity_2(mut x: i32) -> i32 { + return x; +} +pub unsafe fn pick_3(mut mode: i32) -> Option i32> { + return if ((mode) > (0)) { + Some(inc_0) + } else { + if ((mode) < (0)) { + Some(dec_1) + } else { + Some(identity_2) + } + }; +} +pub unsafe fn apply_4(mut fn_: Option i32>, mut x: i32) -> i32 { + let mut actual: Option i32> = if !(fn_).is_none() { + fn_ + } else { + Some(identity_2) + }; + return (unsafe { + let _arg0: i32 = x; + (actual).unwrap()(_arg0) + }); +} +pub fn main() { + unsafe { + std::process::exit(main_0() as i32); + } +} +unsafe fn main_0() -> i32 { + assert!( + ((unsafe { + let _arg0: i32 = 10; + (unsafe { + let _mode: i32 = 1; + pick_3(_mode) + }) + .unwrap()(_arg0) + }) == (11)) + ); + assert!( + ((unsafe { + let _arg0: i32 = 10; + (unsafe { + let _mode: i32 = -1_i32; + pick_3(_mode) + }) + .unwrap()(_arg0) + }) == (9)) + ); + assert!( + ((unsafe { + let _arg0: i32 = 10; + (unsafe { + let _mode: i32 = 0; + pick_3(_mode) + }) + .unwrap()(_arg0) + }) == (10)) + ); + assert!( + ((unsafe { + let _fn: Option i32> = Some(inc_0); + let _x: i32 = 5; + apply_4(_fn, _x) + }) == (6)) + ); + assert!( + ((unsafe { + let _fn: Option i32> = None; + let _x: i32 = 5; + apply_4(_fn, _x) + }) == (5)) + ); + return 0; +} diff --git a/tests/unit/out/unsafe/fn_ptr_default_arg.rs b/tests/unit/out/unsafe/fn_ptr_default_arg.rs new file mode 100644 index 00000000..86287ca6 --- /dev/null +++ b/tests/unit/out/unsafe/fn_ptr_default_arg.rs @@ -0,0 +1,61 @@ +extern crate libc; +use libc::*; +extern crate libcc2rs; +use libcc2rs::*; +use std::collections::BTreeMap; +use std::io::Seek; +use std::io::{Read, Write}; +use std::os::fd::{AsFd, FromRawFd, IntoRawFd}; +use std::rc::Rc; +pub unsafe fn identity_0(mut x: i32) -> i32 { + return x; +} +pub unsafe fn apply_1(mut x: i32, mut fn_: Option i32>>) -> i32 { + let mut fn_: Option i32> = fn_.unwrap_or(None); + if !(fn_).is_none() { + return (unsafe { + let _arg0: i32 = x; + (fn_).unwrap()(_arg0) + }); + } + return x; +} +pub fn main() { + unsafe { + std::process::exit(main_0() as i32); + } +} +unsafe fn main_0() -> i32 { + assert!( + ((unsafe { + let _x: i32 = 5; + let _fn: Option i32> = Default::default(); + apply_1(_x, Some(_fn)) + }) == (5)) + ); + assert!( + ((unsafe { + let _x: i32 = 5; + let _fn: Option i32> = None; + apply_1(_x, Some(_fn)) + }) == (5)) + ); + assert!( + ((unsafe { + let _x: i32 = 5; + let _fn: Option i32> = Some(identity_0); + apply_1(_x, Some(_fn)) + }) == (5)) + ); + let mut negate: Option i32> = Some(|x: i32| { + return -x; + }); + assert!( + ((unsafe { + let _x: i32 = 5; + let _fn: Option i32> = negate; + apply_1(_x, Some(_fn)) + }) == (-5_i32)) + ); + return 0; +} diff --git a/tests/unit/out/unsafe/fn_ptr_global.rs b/tests/unit/out/unsafe/fn_ptr_global.rs new file mode 100644 index 00000000..21dc35b7 --- /dev/null +++ b/tests/unit/out/unsafe/fn_ptr_global.rs @@ -0,0 +1,76 @@ +extern crate libc; +use libc::*; +extern crate libcc2rs; +use libcc2rs::*; +use std::collections::BTreeMap; +use std::io::Seek; +use std::io::{Read, Write}; +use std::os::fd::{AsFd, FromRawFd, IntoRawFd}; +use std::rc::Rc; +pub unsafe fn double_it_0(mut x: i32) -> i32 { + return ((x) * (2)); +} +pub unsafe fn triple_it_1(mut x: i32) -> i32 { + return ((x) * (3)); +} +pub static mut g_op: Option i32> = None; +pub unsafe fn set_op_2(mut fn_: Option i32>) { + g_op = fn_; +} +pub unsafe fn call_op_3(mut x: i32) -> i32 { + if !(g_op).is_none() { + return (unsafe { + let _arg0: i32 = x; + (g_op).unwrap()(_arg0) + }); + } + return x; +} +pub fn main() { + unsafe { + std::process::exit(main_0() as i32); + } +} +unsafe fn main_0() -> i32 { + assert!( + ((unsafe { + let _x: i32 = 5; + call_op_3(_x) + }) == (5)) + ); + (unsafe { + let _fn: Option i32> = Some(double_it_0); + set_op_2(_fn) + }); + assert!(!((g_op).is_none())); + assert!(((g_op) == (Some(double_it_0)))); + assert!( + ((unsafe { + let _x: i32 = 5; + call_op_3(_x) + }) == (10)) + ); + (unsafe { + let _fn: Option i32> = Some(triple_it_1); + set_op_2(_fn) + }); + assert!(((g_op) == (Some(triple_it_1)))); + assert!( + ((unsafe { + let _x: i32 = 5; + call_op_3(_x) + }) == (15)) + ); + (unsafe { + let _fn: Option i32> = None; + set_op_2(_fn) + }); + assert!((g_op).is_none()); + assert!( + ((unsafe { + let _x: i32 = 5; + call_op_3(_x) + }) == (5)) + ); + return 0; +} diff --git a/tests/unit/out/unsafe/fn_ptr_reassign.rs b/tests/unit/out/unsafe/fn_ptr_reassign.rs new file mode 100644 index 00000000..08eed4bb --- /dev/null +++ b/tests/unit/out/unsafe/fn_ptr_reassign.rs @@ -0,0 +1,61 @@ +extern crate libc; +use libc::*; +extern crate libcc2rs; +use libcc2rs::*; +use std::collections::BTreeMap; +use std::io::Seek; +use std::io::{Read, Write}; +use std::os::fd::{AsFd, FromRawFd, IntoRawFd}; +use std::rc::Rc; +pub unsafe fn add_0(mut a: i32, mut b: i32) -> i32 { + return ((a) + (b)); +} +pub unsafe fn sub_1(mut a: i32, mut b: i32) -> i32 { + return ((a) - (b)); +} +pub unsafe fn mul_2(mut a: i32, mut b: i32) -> i32 { + return ((a) * (b)); +} +pub fn main() { + unsafe { + std::process::exit(main_0() as i32); + } +} +unsafe fn main_0() -> i32 { + let mut fn_: Option i32> = Some(add_0); + assert!( + ((unsafe { + let _arg0: i32 = 3; + let _arg1: i32 = 4; + (fn_).unwrap()(_arg0, _arg1) + }) == (7)) + ); + fn_ = Some(sub_1); + assert!( + ((unsafe { + let _arg0: i32 = 10; + let _arg1: i32 = 3; + (fn_).unwrap()(_arg0, _arg1) + }) == (7)) + ); + fn_ = Some(mul_2); + assert!( + ((unsafe { + let _arg0: i32 = 6; + let _arg1: i32 = 7; + (fn_).unwrap()(_arg0, _arg1) + }) == (42)) + ); + fn_ = None; + assert!((fn_).is_none()); + fn_ = Some(add_0); + assert!(!((fn_).is_none())); + assert!( + ((unsafe { + let _arg0: i32 = 1; + let _arg1: i32 = 1; + (fn_).unwrap()(_arg0, _arg1) + }) == (2)) + ); + return 0; +} diff --git a/tests/unit/out/unsafe/fn_ptr_return.rs b/tests/unit/out/unsafe/fn_ptr_return.rs new file mode 100644 index 00000000..1cda2828 --- /dev/null +++ b/tests/unit/out/unsafe/fn_ptr_return.rs @@ -0,0 +1,53 @@ +extern crate libc; +use libc::*; +extern crate libcc2rs; +use libcc2rs::*; +use std::collections::BTreeMap; +use std::io::Seek; +use std::io::{Read, Write}; +use std::os::fd::{AsFd, FromRawFd, IntoRawFd}; +use std::rc::Rc; +pub unsafe fn inc_0(mut x: i32) -> i32 { + return ((x) + (1)); +} +pub unsafe fn dec_1(mut x: i32) -> i32 { + return ((x) - (1)); +} +pub unsafe fn pick_2(mut choose_inc: i32) -> Option i32> { + if (choose_inc != 0) { + return Some(inc_0); + } + return Some(dec_1); +} +pub fn main() { + unsafe { + std::process::exit(main_0() as i32); + } +} +unsafe fn main_0() -> i32 { + let mut f: Option i32> = (unsafe { + let _choose_inc: i32 = 1; + pick_2(_choose_inc) + }); + assert!(!((f).is_none())); + assert!(((f) == (Some(inc_0)))); + assert!( + ((unsafe { + let _arg0: i32 = 10; + (f).unwrap()(_arg0) + }) == (11)) + ); + let mut g: Option i32> = (unsafe { + let _choose_inc: i32 = 0; + pick_2(_choose_inc) + }); + assert!(((g) == (Some(dec_1)))); + assert!( + ((unsafe { + let _arg0: i32 = 10; + (g).unwrap()(_arg0) + }) == (9)) + ); + assert!(((f) != (g))); + return 0; +} diff --git a/tests/unit/out/unsafe/fn_ptr_stable_sort.rs b/tests/unit/out/unsafe/fn_ptr_stable_sort.rs new file mode 100644 index 00000000..123a880c --- /dev/null +++ b/tests/unit/out/unsafe/fn_ptr_stable_sort.rs @@ -0,0 +1,44 @@ +extern crate libc; +use libc::*; +extern crate libcc2rs; +use libcc2rs::*; +use std::collections::BTreeMap; +use std::io::Seek; +use std::io::{Read, Write}; +use std::os::fd::{AsFd, FromRawFd, IntoRawFd}; +use std::rc::Rc; +#[derive(Copy, Clone, Default)] +pub struct Item { + pub key: i32, + pub value: i32, +} +pub unsafe fn Compare_0(a: *const Item, b: *const Item) -> bool { + return (((*a).key) < ((*b).key)); +} +pub fn main() { + unsafe { + std::process::exit(main_0() as i32); + } +} +unsafe fn main_0() -> i32 { + let mut v: Vec = Vec::new(); + v.push(Item { key: 3, value: 30 }); + v.push(Item { key: 1, value: 10 }); + v.push(Item { key: 2, value: 20 }); + { + let len = v.as_mut_ptr().add(v.len()).offset_from(v.as_mut_ptr()) as usize; + ::std::slice::from_raw_parts_mut(v.as_mut_ptr(), len).sort_by(|x, y| { + if (Compare_0)(x, y) { + std::cmp::Ordering::Less + } else if (Compare_0)(y, x) { + std::cmp::Ordering::Greater + } else { + std::cmp::Ordering::Equal + } + }) + }; + assert!(((v[(0_u64) as usize].key) == (1))); + assert!(((v[(1_u64) as usize].key) == (2))); + assert!(((v[(2_u64) as usize].key) == (3))); + return 0; +} diff --git a/tests/unit/out/unsafe/fn_ptr_struct.rs b/tests/unit/out/unsafe/fn_ptr_struct.rs new file mode 100644 index 00000000..ae9e3a7f --- /dev/null +++ b/tests/unit/out/unsafe/fn_ptr_struct.rs @@ -0,0 +1,65 @@ +extern crate libc; +use libc::*; +extern crate libcc2rs; +use libcc2rs::*; +use std::collections::BTreeMap; +use std::io::Seek; +use std::io::{Read, Write}; +use std::os::fd::{AsFd, FromRawFd, IntoRawFd}; +use std::rc::Rc; +#[derive(Clone)] +pub struct Handler { + pub tag: i32, + pub cb: Option i32>, +} +impl Default for Handler { + fn default() -> Self { + Handler { + tag: 0_i32, + cb: None, + } + } +} +pub unsafe fn double_it_0(mut x: i32) -> i32 { + return ((x) * (2)); +} +pub unsafe fn negate_1(mut x: i32) -> i32 { + return -x; +} +pub fn main() { + unsafe { + std::process::exit(main_0() as i32); + } +} +unsafe fn main_0() -> i32 { + let mut h1: Handler = Handler { + tag: 1, + cb: Some(double_it_0), + }; + let mut h2: Handler = Handler { + tag: 2, + cb: Some(negate_1), + }; + assert!(!((h1.cb).is_none())); + assert!( + ((unsafe { + let _arg0: i32 = 5; + (h1.cb).unwrap()(_arg0) + }) == (10)) + ); + assert!( + ((unsafe { + let _arg0: i32 = 7; + (h2.cb).unwrap()(_arg0) + }) == (-7_i32)) + ); + (h1.cb) = Some(negate_1); + assert!( + ((unsafe { + let _arg0: i32 = 3; + (h1.cb).unwrap()(_arg0) + }) == (-3_i32)) + ); + assert!(((h1.cb) == (h2.cb))); + return 0; +} diff --git a/tests/unit/out/unsafe/fn_ptr_void_return.rs b/tests/unit/out/unsafe/fn_ptr_void_return.rs new file mode 100644 index 00000000..72ab341a --- /dev/null +++ b/tests/unit/out/unsafe/fn_ptr_void_return.rs @@ -0,0 +1,50 @@ +extern crate libc; +use libc::*; +extern crate libcc2rs; +use libcc2rs::*; +use std::collections::BTreeMap; +use std::io::Seek; +use std::io::{Read, Write}; +use std::os::fd::{AsFd, FromRawFd, IntoRawFd}; +use std::rc::Rc; +pub unsafe fn negate_0(mut x: *mut i32) { + (*x) = -(*x); +} +pub unsafe fn zero_out_1(mut x: *mut i32) { + (*x) = 0; +} +pub unsafe fn run_2(mut fn_: Option, mut x: *mut i32) { + (unsafe { + let _arg0: *mut i32 = x; + (fn_).unwrap()(_arg0) + }); +} +pub fn main() { + unsafe { + std::process::exit(main_0() as i32); + } +} +unsafe fn main_0() -> i32 { + let mut a: i32 = 42; + (unsafe { + let _fn: Option = Some(negate_0); + let _x: *mut i32 = (&mut a as *mut i32); + run_2(_fn, _x) + }); + assert!(((a) == (-42_i32))); + (unsafe { + let _fn: Option = Some(zero_out_1); + let _x: *mut i32 = (&mut a as *mut i32); + run_2(_fn, _x) + }); + assert!(((a) == (0))); + let mut fn_: Option = Some(negate_0); + assert!(!((fn_).is_none())); + let mut b: i32 = 10; + (unsafe { + let _arg0: *mut i32 = (&mut b as *mut i32); + (fn_).unwrap()(_arg0) + }); + assert!(((b) == (-10_i32))); + return 0; +} diff --git a/tests/unit/out/unsafe/fn_ptr_vtable.rs b/tests/unit/out/unsafe/fn_ptr_vtable.rs new file mode 100644 index 00000000..48e820e6 --- /dev/null +++ b/tests/unit/out/unsafe/fn_ptr_vtable.rs @@ -0,0 +1,68 @@ +extern crate libc; +use libc::*; +extern crate libcc2rs; +use libcc2rs::*; +use std::collections::BTreeMap; +use std::io::Seek; +use std::io::{Read, Write}; +use std::os::fd::{AsFd, FromRawFd, IntoRawFd}; +use std::rc::Rc; +#[derive(Clone)] +pub struct Vtable { + pub create: Option *mut ::libc::c_void>, + pub get: Option i32>, + pub destroy: Option, +} +impl Default for Vtable { + fn default() -> Self { + Vtable { + create: None, + get: None, + destroy: None, + } + } +} +pub static mut storage: i32 = 0_i32; +pub unsafe fn int_create_0(mut val: i32) -> *mut ::libc::c_void { + storage = val; + return ((&mut storage as *mut i32) as *mut i32 as *mut ::libc::c_void); +} +pub unsafe fn int_get_1(mut p: *mut ::libc::c_void) -> i32 { + return (*(p as *mut i32)); +} +pub unsafe fn int_destroy_2(mut p: *mut ::libc::c_void) { + (*(p as *mut i32)) = 0; +} +pub fn main() { + unsafe { + std::process::exit(main_0() as i32); + } +} +unsafe fn main_0() -> i32 { + let mut vt: Vtable = Vtable { + create: Some(int_create_0), + get: Some(int_get_1), + destroy: Some(int_destroy_2), + }; + assert!(!((vt.create).is_none())); + assert!(!((vt.get).is_none())); + assert!(!((vt.destroy).is_none())); + let mut obj: *mut ::libc::c_void = (unsafe { + let _arg0: i32 = 42; + (vt.create).unwrap()(_arg0) + }); + assert!( + ((unsafe { + let _arg0: *mut ::libc::c_void = obj; + (vt.get).unwrap()(_arg0) + }) == (42)) + ); + (unsafe { + let _arg0: *mut ::libc::c_void = obj; + (vt.destroy).unwrap()(_arg0) + }); + assert!(((storage) == (0))); + (vt.get) = None; + assert!((vt.get).is_none()); + return 0; +} From 1e03056a9ebf1464e10c44fcec42b4ac80c30c29 Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Fri, 17 Apr 2026 11:39:14 +0100 Subject: [PATCH 08/17] Delete GetFunctionPointerDefaultAsString --- cpp2rust/converter/converter.cpp | 7 +------ cpp2rust/converter/converter.h | 3 --- cpp2rust/converter/models/converter_refcount.cpp | 7 +------ cpp2rust/converter/models/converter_refcount.h | 3 --- 4 files changed, 2 insertions(+), 18 deletions(-) diff --git a/cpp2rust/converter/converter.cpp b/cpp2rust/converter/converter.cpp index 42fa3083..a834e34f 100644 --- a/cpp2rust/converter/converter.cpp +++ b/cpp2rust/converter/converter.cpp @@ -2668,15 +2668,10 @@ bool Converter::VisitCXXStdInitializerListExpr( return false; } -std::string -Converter::GetFunctionPointerDefaultAsString(clang::QualType qual_type) { - return "None"; -} - std::string Converter::GetDefaultAsString(clang::QualType qual_type) { if (qual_type->isPointerType()) { if (qual_type->getPointeeType()->isFunctionType()) { - return GetFunctionPointerDefaultAsString(qual_type); + return "None"; } else { computed_expr_type_ = ComputedExprType::FreshPointer; return keyword_default_; diff --git a/cpp2rust/converter/converter.h b/cpp2rust/converter/converter.h index b3601fc5..9d368294 100644 --- a/cpp2rust/converter/converter.h +++ b/cpp2rust/converter/converter.h @@ -340,9 +340,6 @@ class Converter : public clang::RecursiveASTVisitor { virtual bool Convert(clang::Stmt *stmt); virtual bool Convert(clang::Expr *expr); - virtual std::string - GetFunctionPointerDefaultAsString(clang::QualType qual_type); - virtual std::string GetDefaultAsString(clang::QualType qual_type); virtual std::string GetDefaultAsStringFallback(clang::QualType qual_type); diff --git a/cpp2rust/converter/models/converter_refcount.cpp b/cpp2rust/converter/models/converter_refcount.cpp index 416918af..ed10a068 100644 --- a/cpp2rust/converter/models/converter_refcount.cpp +++ b/cpp2rust/converter/models/converter_refcount.cpp @@ -1035,11 +1035,6 @@ void ConverterRefCount::ConvertFunctionToFunctionPointer( fn_decl->getType()->getAs()))); } -std::string ConverterRefCount::GetFunctionPointerDefaultAsString( - clang::QualType qual_type) { - return "FnPtr::null()"; -} - void ConverterRefCount::ConvertEqualsNullPtr(clang::Expr *expr) { StrCat("("); Convert(expr); @@ -1553,7 +1548,7 @@ std::string ConverterRefCount::GetDefaultAsString(clang::QualType qual_type) { if (qual_type->isPointerType()) { auto pointee_type = qual_type->getPointeeType(); if (pointee_type->isFunctionType()) { - ret = GetFunctionPointerDefaultAsString(qual_type); + ret = "FnPtr::null()"; } else { if (pointee_type->isVoidType()) { ret = "AnyPtr::default()"; diff --git a/cpp2rust/converter/models/converter_refcount.h b/cpp2rust/converter/models/converter_refcount.h index 45eefd0a..b32130d2 100644 --- a/cpp2rust/converter/models/converter_refcount.h +++ b/cpp2rust/converter/models/converter_refcount.h @@ -113,9 +113,6 @@ class ConverterRefCount final : public Converter { std::string GetDefaultAsString(clang::QualType qual_type) override; - std::string - GetFunctionPointerDefaultAsString(clang::QualType qual_type) override; - void ConvertEqualsNullPtr(clang::Expr *expr) override; std::string GetDefaultAsStringFallback(clang::QualType qual_type) override; From 0aa07eeb0f5f4d08c84cb4316b3cb053b3174a48 Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Fri, 17 Apr 2026 11:51:17 +0100 Subject: [PATCH 09/17] Use ConvertFunctionPointerType for lambda operator() --- cpp2rust/converter/converter.cpp | 21 ++++++++----------- cpp2rust/converter/converter.h | 5 ++++- .../converter/models/converter_refcount.cpp | 4 ++-- .../converter/models/converter_refcount.h | 3 ++- tests/unit/lambda_capture_pass.cpp | 19 +++++++++++++++++ tests/unit/lambda_nested.cpp | 17 +++++++++++++++ 6 files changed, 53 insertions(+), 16 deletions(-) create mode 100644 tests/unit/lambda_capture_pass.cpp create mode 100644 tests/unit/lambda_nested.cpp diff --git a/cpp2rust/converter/converter.cpp b/cpp2rust/converter/converter.cpp index a834e34f..96e7ae67 100644 --- a/cpp2rust/converter/converter.cpp +++ b/cpp2rust/converter/converter.cpp @@ -131,16 +131,11 @@ bool Converter::VisitRecordType(clang::RecordType *type) { if (auto lambda = clang::dyn_cast(decl)) { if (lambda->isLambda()) { if (in_function_formals_) { - auto call_op = lambda->getLambdaCallOperator(); - StrCat("impl Fn("); - for (auto p : call_op->parameters()) { - StrCat(std::format("{},", ToStringBase(p->getType()))); - } - StrCat(")"); - if (!call_op->getReturnType()->isVoidType()) { - StrCat("->"); - StrCat(ToStringBase(call_op->getReturnType())); - } + StrCat( + ConvertFunctionPointerType(lambda->getLambdaCallOperator() + ->getType() + ->getAs(), + FnProtoType::LambdaCallOperator)); } else { StrCat("_"); } @@ -226,8 +221,10 @@ bool Converter::VisitLValueReferenceType(clang::LValueReferenceType *type) { } std::string -Converter::ConvertFunctionPointerType(const clang::FunctionProtoType *proto) { - std::string result = "fn("; +Converter::ConvertFunctionPointerType(const clang::FunctionProtoType *proto, + FnProtoType kind) { + std::string result = + (kind == FnProtoType::LambdaCallOperator ? "impl Fn(" : "fn("); for (auto p_ty : proto->param_types()) { result += ToString(p_ty) + ","; } diff --git a/cpp2rust/converter/converter.h b/cpp2rust/converter/converter.h index 9d368294..308a3cfe 100644 --- a/cpp2rust/converter/converter.h +++ b/cpp2rust/converter/converter.h @@ -61,8 +61,11 @@ class Converter : public clang::RecursiveASTVisitor { virtual bool VisitPointerType(clang::PointerType *type); + enum class FnProtoType { LambdaCallOperator, FnPtr }; + virtual std::string - ConvertFunctionPointerType(const clang::FunctionProtoType *proto); + ConvertFunctionPointerType(const clang::FunctionProtoType *proto, + FnProtoType kind = FnProtoType::FnPtr); virtual bool VisitDecayedType(clang::DecayedType *type); diff --git a/cpp2rust/converter/models/converter_refcount.cpp b/cpp2rust/converter/models/converter_refcount.cpp index ed10a068..6ebac25f 100644 --- a/cpp2rust/converter/models/converter_refcount.cpp +++ b/cpp2rust/converter/models/converter_refcount.cpp @@ -221,9 +221,9 @@ std::string ConverterRefCount::BuildFnAdapter( } std::string ConverterRefCount::ConvertFunctionPointerType( - const clang::FunctionProtoType *proto) { + const clang::FunctionProtoType *proto, FnProtoType kind) { PushConversionKind push(*this, ConversionKind::Unboxed); - return Converter::ConvertFunctionPointerType(proto); + return Converter::ConvertFunctionPointerType(proto, kind); } bool ConverterRefCount::VisitPointerType(clang::PointerType *type) { diff --git a/cpp2rust/converter/models/converter_refcount.h b/cpp2rust/converter/models/converter_refcount.h index b32130d2..e986a266 100644 --- a/cpp2rust/converter/models/converter_refcount.h +++ b/cpp2rust/converter/models/converter_refcount.h @@ -23,7 +23,8 @@ class ConverterRefCount final : public Converter { bool VisitPointerType(clang::PointerType *type) override; std::string - ConvertFunctionPointerType(const clang::FunctionProtoType *proto) override; + ConvertFunctionPointerType(const clang::FunctionProtoType *proto, + FnProtoType kind = FnProtoType::FnPtr) override; bool VisitCXXRecordDecl(clang::CXXRecordDecl *decl) override; diff --git a/tests/unit/lambda_capture_pass.cpp b/tests/unit/lambda_capture_pass.cpp new file mode 100644 index 00000000..439b128c --- /dev/null +++ b/tests/unit/lambda_capture_pass.cpp @@ -0,0 +1,19 @@ +#include + +template int apply(F fn, int x) { return fn(x); } + +int main() { + int base = 10; + + auto add_base = [&base](int x) { return x + base; }; + assert(apply(add_base, 5) == 15); + + base = 100; + assert(apply(add_base, 5) == 105); + + int factor = 3; + auto scale = [factor](int x) { return x * factor; }; + assert(apply(scale, 4) == 12); + + return 0; +} diff --git a/tests/unit/lambda_nested.cpp b/tests/unit/lambda_nested.cpp new file mode 100644 index 00000000..f7f26e93 --- /dev/null +++ b/tests/unit/lambda_nested.cpp @@ -0,0 +1,17 @@ +#include + +int main() { + int x = 10; + + auto outer = [&x](int y) { + auto inner = [&x, y](int z) { return x + y + z; }; + return inner(1); + }; + + assert(outer(20) == 31); + + x = 100; + assert(outer(20) == 121); + + return 0; +} From d69603a81d6c1a9641a817d72c240c0b22759f57 Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Fri, 17 Apr 2026 11:51:47 +0100 Subject: [PATCH 10/17] Add lambda tests --- .../unit/out/refcount/lambda_capture_pass.rs | 69 +++++++++++++++++++ tests/unit/out/refcount/lambda_nested.rs | 45 ++++++++++++ tests/unit/out/unsafe/lambda_capture_pass.rs | 62 +++++++++++++++++ tests/unit/out/unsafe/lambda_nested.rs | 45 ++++++++++++ 4 files changed, 221 insertions(+) create mode 100644 tests/unit/out/refcount/lambda_capture_pass.rs create mode 100644 tests/unit/out/refcount/lambda_nested.rs create mode 100644 tests/unit/out/unsafe/lambda_capture_pass.rs create mode 100644 tests/unit/out/unsafe/lambda_nested.rs diff --git a/tests/unit/out/refcount/lambda_capture_pass.rs b/tests/unit/out/refcount/lambda_capture_pass.rs new file mode 100644 index 00000000..d7e08650 --- /dev/null +++ b/tests/unit/out/refcount/lambda_capture_pass.rs @@ -0,0 +1,69 @@ +extern crate libcc2rs; +use libcc2rs::*; +use std::cell::RefCell; +use std::collections::BTreeMap; +use std::io::prelude::*; +use std::io::Seek; +use std::io::{Read, Write}; +use std::os::fd::AsFd; +use std::rc::{Rc, Weak}; +pub fn apply_0(fn_: impl Fn(i32) -> i32, x: i32) -> i32 { + let fn_: Value<_> = Rc::new(RefCell::new(fn_)); + let x: Value = Rc::new(RefCell::new(x)); + return ({ + let _x: i32 = (*x.borrow()); + (*fn_.borrow())(_x) + }) + .clone(); +} +pub fn apply_1(fn_: impl Fn(i32) -> i32, x: i32) -> i32 { + let fn_: Value<_> = Rc::new(RefCell::new(fn_)); + let x: Value = Rc::new(RefCell::new(x)); + return ({ + let _x: i32 = (*x.borrow()); + (*fn_.borrow())(_x) + }) + .clone(); +} +pub fn main() { + std::process::exit(main_0()); +} +fn main_0() -> i32 { + let base: Value = Rc::new(RefCell::new(10)); + let add_base: Value<_> = Rc::new(RefCell::new( + (|x: i32| { + let x: Value = Rc::new(RefCell::new(x)); + return ((*x.borrow()) + (*base.borrow())); + }), + )); + assert!( + (({ + let _fn: _ = (*add_base.borrow()).clone(); + let _x: i32 = 5; + apply_0(_fn, _x) + }) == 15) + ); + (*base.borrow_mut()) = 100; + assert!( + (({ + let _fn: _ = (*add_base.borrow()).clone(); + let _x: i32 = 5; + apply_0(_fn, _x) + }) == 105) + ); + let factor: Value = Rc::new(RefCell::new(3)); + let scale: Value<_> = Rc::new(RefCell::new( + (|x: i32| { + let x: Value = Rc::new(RefCell::new(x)); + return ((*x.borrow()) * (*factor.borrow())); + }), + )); + assert!( + (({ + let _fn: _ = (*scale.borrow()).clone(); + let _x: i32 = 4; + apply_1(_fn, _x) + }) == 12) + ); + return 0; +} diff --git a/tests/unit/out/refcount/lambda_nested.rs b/tests/unit/out/refcount/lambda_nested.rs new file mode 100644 index 00000000..d562d806 --- /dev/null +++ b/tests/unit/out/refcount/lambda_nested.rs @@ -0,0 +1,45 @@ +extern crate libcc2rs; +use libcc2rs::*; +use std::cell::RefCell; +use std::collections::BTreeMap; +use std::io::prelude::*; +use std::io::Seek; +use std::io::{Read, Write}; +use std::os::fd::AsFd; +use std::rc::{Rc, Weak}; +pub fn main() { + std::process::exit(main_0()); +} +fn main_0() -> i32 { + let x: Value = Rc::new(RefCell::new(10)); + let outer: Value<_> = Rc::new(RefCell::new( + (|y: i32| { + let y: Value = Rc::new(RefCell::new(y)); + let inner: Value<_> = Rc::new(RefCell::new( + (|z: i32| { + let z: Value = Rc::new(RefCell::new(z)); + return (((*x.borrow()) + (*y.borrow())) + (*z.borrow())); + }), + )); + return ({ + let _z: i32 = 1; + (*inner.borrow())(_z) + }) + .clone(); + }), + )); + assert!( + (({ + let _y: i32 = 20; + (*outer.borrow())(_y) + }) == 31) + ); + (*x.borrow_mut()) = 100; + assert!( + (({ + let _y: i32 = 20; + (*outer.borrow())(_y) + }) == 121) + ); + return 0; +} diff --git a/tests/unit/out/unsafe/lambda_capture_pass.rs b/tests/unit/out/unsafe/lambda_capture_pass.rs new file mode 100644 index 00000000..bf1f4fa3 --- /dev/null +++ b/tests/unit/out/unsafe/lambda_capture_pass.rs @@ -0,0 +1,62 @@ +extern crate libc; +use libc::*; +extern crate libcc2rs; +use libcc2rs::*; +use std::collections::BTreeMap; +use std::io::Seek; +use std::io::{Read, Write}; +use std::os::fd::{AsFd, FromRawFd, IntoRawFd}; +use std::rc::Rc; +pub unsafe fn apply_0(mut fn_: impl Fn(i32) -> i32, mut x: i32) -> i32 { + return (unsafe { + let _x: i32 = x; + fn_(_x) + }); +} +pub unsafe fn apply_1(mut fn_: impl Fn(i32) -> i32, mut x: i32) -> i32 { + return (unsafe { + let _x: i32 = x; + fn_(_x) + }); +} +pub fn main() { + unsafe { + std::process::exit(main_0() as i32); + } +} +unsafe fn main_0() -> i32 { + let mut base: i32 = 10; + assert!( + ((unsafe { + let _fn: _ = (|x: i32| { + return ((x) + (base)); + }) + .clone(); + let _x: i32 = 5; + apply_0(_fn, _x) + }) == (15)) + ); + base = 100; + assert!( + ((unsafe { + let _fn: _ = (|x: i32| { + return ((x) + (base)); + }) + .clone(); + let _x: i32 = 5; + apply_0(_fn, _x) + }) == (105)) + ); + let mut factor: i32 = 3; + assert!( + ((unsafe { + let _fn: _ = (|x: i32| { + return ((x) * (factor)); + }) + .clone(); + let _x: i32 = 4; + apply_1(_fn, _x) + }) == (12)) + ); + return 0; +} diff --git a/tests/unit/out/unsafe/lambda_nested.rs b/tests/unit/out/unsafe/lambda_nested.rs new file mode 100644 index 00000000..542a59db --- /dev/null +++ b/tests/unit/out/unsafe/lambda_nested.rs @@ -0,0 +1,45 @@ +extern crate libc; +use libc::*; +extern crate libcc2rs; +use libcc2rs::*; +use std::collections::BTreeMap; +use std::io::Seek; +use std::io::{Read, Write}; +use std::os::fd::{AsFd, FromRawFd, IntoRawFd}; +use std::rc::Rc; +pub fn main() { + unsafe { + std::process::exit(main_0() as i32); + } +} +unsafe fn main_0() -> i32 { + let mut x: i32 = 10; + assert!( + ((unsafe { + let _y: i32 = 20; + (|y: i32| { + return (unsafe { + let _z: i32 = 1; + (|z: i32| { + return (((x) + (y)) + (z)); + })(_z) + }); + })(_y) + }) == (31)) + ); + x = 100; + assert!( + ((unsafe { + let _y: i32 = 20; + (|y: i32| { + return (unsafe { + let _z: i32 = 1; + (|z: i32| { + return (((x) + (y)) + (z)); + })(_z) + }); + })(_y) + }) == (121)) + ); + return 0; +} From 4cee1010aa692d476a28a70463960534924827d3 Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Fri, 17 Apr 2026 14:01:15 +0100 Subject: [PATCH 11/17] Delete StripFunctionPointerDecay --- cpp2rust/converter/converter.cpp | 23 +++++++++++++++---- cpp2rust/converter/converter.h | 4 ++++ cpp2rust/converter/converter_lib.cpp | 11 --------- cpp2rust/converter/converter_lib.h | 2 -- .../unit/out/refcount/lambda_capture_pass.rs | 4 ++-- tests/unit/out/refcount/lambda_nested.rs | 6 ++--- 6 files changed, 27 insertions(+), 23 deletions(-) diff --git a/cpp2rust/converter/converter.cpp b/cpp2rust/converter/converter.cpp index 96e7ae67..ec547615 100644 --- a/cpp2rust/converter/converter.cpp +++ b/cpp2rust/converter/converter.cpp @@ -1464,8 +1464,8 @@ void Converter::ConvertGenericCallExpr(clang::CallExpr *expr) { if (proto && !function) { EmitFnPtrCall(callee); } else { - PushExprKind push(*this, ExprKind::RValue); - Convert(StripFunctionPointerDecay(callee)); + PushExprKind push(*this, ExprKind::Callee); + Convert(callee); } StrCat(token::kOpenParen); for (unsigned i = 0; i < num_named_params && i < num_args; ++i) { @@ -1688,8 +1688,12 @@ bool Converter::VisitImplicitCastExpr(clang::ImplicitCastExpr *expr) { } case clang::CastKind::CK_FunctionToPointerDecay: case clang::CastKind::CK_BuiltinFnToFnPtr: { - PushExprKind push(*this, ExprKind::AddrOf); - Convert(sub_expr); + if (isCallee()) { + Convert(sub_expr); + } else { + PushExprKind push(*this, ExprKind::AddrOf); + Convert(sub_expr); + } break; } case clang::CastKind::CK_ConstructorConversion: @@ -3256,7 +3260,12 @@ void Converter::PlaceholderCtx::dump() const { std::string Converter::ConvertPlaceholder(clang::Expr *expr, clang::Expr *arg, const PlaceholderCtx &ph_ctx) { - arg = StripFunctionPointerDecay(arg); + if (arg->getType()->isFunctionPointerType()) { + PushExprKind push(*this, ExprKind::Callee); + Buffer buf(*this); + Convert(arg); + return std::move(buf).str(); + } if (ph_ctx.needs_materialization()) { auto materialized = ph_ctx.materialize_ctx->GetOrMaterialize( @@ -3405,6 +3414,10 @@ bool Converter::isVoid() const { return curr_expr_kind_.empty() || curr_expr_kind_.back() == ExprKind::Void; } +bool Converter::isCallee() const { + return !curr_expr_kind_.empty() && curr_expr_kind_.back() == ExprKind::Callee; +} + void Converter::SetFresh() { switch (computed_expr_type_) { case ComputedExprType::Value: diff --git a/cpp2rust/converter/converter.h b/cpp2rust/converter/converter.h index 308a3cfe..6f2c3719 100644 --- a/cpp2rust/converter/converter.h +++ b/cpp2rust/converter/converter.h @@ -479,6 +479,7 @@ class Converter : public clang::RecursiveASTVisitor { static std::unordered_set abstract_structs_; enum class ExprKind : uint8_t { + Callee, LValue, RValue, XValue, @@ -489,6 +490,8 @@ class Converter : public clang::RecursiveASTVisitor { static const char *expr_kind_to_string(ExprKind kind) { switch (kind) { + case ExprKind::Callee: + return "Callee"; case ExprKind::LValue: return "LValue"; case ExprKind::RValue: @@ -512,6 +515,7 @@ class Converter : public clang::RecursiveASTVisitor { bool isAddrOf() const; bool isObject() const; bool isVoid() const; + bool isCallee() const; void dump_expr_kinds(); diff --git a/cpp2rust/converter/converter_lib.cpp b/cpp2rust/converter/converter_lib.cpp index ff770ae2..4afa3140 100644 --- a/cpp2rust/converter/converter_lib.cpp +++ b/cpp2rust/converter/converter_lib.cpp @@ -380,17 +380,6 @@ clang::QualType GetReturnTypeOfFunction(const clang::CallExpr *expr) { return {}; } -clang::Expr *StripFunctionPointerDecay(clang::Expr *expr) { - if (auto *ice = clang::dyn_cast(expr)) { - auto ck = ice->getCastKind(); - if (ck == clang::CK_FunctionToPointerDecay || - ck == clang::CK_BuiltinFnToFnPtr) { - return ice->getSubExpr(); - } - } - return expr; -} - std::string GetOverloadedOperator(const clang::FunctionDecl *decl) { switch (decl->getOverloadedOperator()) { case clang::OO_Less: diff --git a/cpp2rust/converter/converter_lib.h b/cpp2rust/converter/converter_lib.h index a2f25342..3696d680 100644 --- a/cpp2rust/converter/converter_lib.h +++ b/cpp2rust/converter/converter_lib.h @@ -91,8 +91,6 @@ template llvm::SmallString<16> GetNumAsString(const T &num) { clang::QualType GetReturnTypeOfFunction(const clang::CallExpr *expr); -clang::Expr *StripFunctionPointerDecay(clang::Expr *expr); - std::string GetOverloadedOperator(const clang::FunctionDecl *decl); bool IsOverloadedComparisonOperator(const clang::CXXMethodDecl *decl); diff --git a/tests/unit/out/refcount/lambda_capture_pass.rs b/tests/unit/out/refcount/lambda_capture_pass.rs index d7e08650..757476fa 100644 --- a/tests/unit/out/refcount/lambda_capture_pass.rs +++ b/tests/unit/out/refcount/lambda_capture_pass.rs @@ -12,7 +12,7 @@ pub fn apply_0(fn_: impl Fn(i32) -> i32, x: i32) -> i32 { let x: Value = Rc::new(RefCell::new(x)); return ({ let _x: i32 = (*x.borrow()); - (*fn_.borrow())(_x) + (*fn_.borrow_mut())(_x) }) .clone(); } @@ -21,7 +21,7 @@ pub fn apply_1(fn_: impl Fn(i32) -> i32, x: i32) -> i32 { let x: Value = Rc::new(RefCell::new(x)); return ({ let _x: i32 = (*x.borrow()); - (*fn_.borrow())(_x) + (*fn_.borrow_mut())(_x) }) .clone(); } diff --git a/tests/unit/out/refcount/lambda_nested.rs b/tests/unit/out/refcount/lambda_nested.rs index d562d806..9b26d593 100644 --- a/tests/unit/out/refcount/lambda_nested.rs +++ b/tests/unit/out/refcount/lambda_nested.rs @@ -23,7 +23,7 @@ fn main_0() -> i32 { )); return ({ let _z: i32 = 1; - (*inner.borrow())(_z) + (*inner.borrow_mut())(_z) }) .clone(); }), @@ -31,14 +31,14 @@ fn main_0() -> i32 { assert!( (({ let _y: i32 = 20; - (*outer.borrow())(_y) + (*outer.borrow_mut())(_y) }) == 31) ); (*x.borrow_mut()) = 100; assert!( (({ let _y: i32 = 20; - (*outer.borrow())(_y) + (*outer.borrow_mut())(_y) }) == 121) ); return 0; From 7c7fa8865888c8f7c40d6cc78956c73e3bcbd8c0 Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Fri, 17 Apr 2026 14:03:28 +0100 Subject: [PATCH 12/17] Add copyright banner to tests --- tests/unit/fn_ptr.cpp | 3 +++ tests/unit/fn_ptr_array.cpp | 3 +++ tests/unit/fn_ptr_as_condition.cpp | 3 +++ tests/unit/fn_ptr_cast.cpp | 3 +++ tests/unit/fn_ptr_conditional.cpp | 3 +++ tests/unit/fn_ptr_default_arg.cpp | 3 +++ tests/unit/fn_ptr_global.cpp | 3 +++ tests/unit/fn_ptr_reassign.cpp | 3 +++ tests/unit/fn_ptr_return.cpp | 3 +++ tests/unit/fn_ptr_stable_sort.cpp | 3 +++ tests/unit/fn_ptr_struct.cpp | 3 +++ tests/unit/fn_ptr_void_return.cpp | 3 +++ tests/unit/fn_ptr_vtable.cpp | 3 +++ tests/unit/lambda_capture_pass.cpp | 3 +++ tests/unit/lambda_nested.cpp | 3 +++ 15 files changed, 45 insertions(+) diff --git a/tests/unit/fn_ptr.cpp b/tests/unit/fn_ptr.cpp index c7c9c7e4..0706e7d8 100644 --- a/tests/unit/fn_ptr.cpp +++ b/tests/unit/fn_ptr.cpp @@ -1,3 +1,6 @@ +// Copyright (c) 2022-present INESC-ID. +// Distributed under the MIT license that can be found in the LICENSE file. + #include typedef int (*foo_t)(void *); diff --git a/tests/unit/fn_ptr_array.cpp b/tests/unit/fn_ptr_array.cpp index f7521faa..ef6f335d 100644 --- a/tests/unit/fn_ptr_array.cpp +++ b/tests/unit/fn_ptr_array.cpp @@ -1,3 +1,6 @@ +// Copyright (c) 2022-present INESC-ID. +// Distributed under the MIT license that can be found in the LICENSE file. + #include typedef int (*op_t)(int, int); diff --git a/tests/unit/fn_ptr_as_condition.cpp b/tests/unit/fn_ptr_as_condition.cpp index 5817f7e4..be74365d 100644 --- a/tests/unit/fn_ptr_as_condition.cpp +++ b/tests/unit/fn_ptr_as_condition.cpp @@ -1,3 +1,6 @@ +// Copyright (c) 2022-present INESC-ID. +// Distributed under the MIT license that can be found in the LICENSE file. + #include typedef void (*callback_t)(int *); diff --git a/tests/unit/fn_ptr_cast.cpp b/tests/unit/fn_ptr_cast.cpp index d825b050..eb5c5abf 100644 --- a/tests/unit/fn_ptr_cast.cpp +++ b/tests/unit/fn_ptr_cast.cpp @@ -1,3 +1,6 @@ +// Copyright (c) 2022-present INESC-ID. +// Distributed under the MIT license that can be found in the LICENSE file. + #include typedef void (*generic_fn)(void); diff --git a/tests/unit/fn_ptr_conditional.cpp b/tests/unit/fn_ptr_conditional.cpp index d1b4c832..343eaeda 100644 --- a/tests/unit/fn_ptr_conditional.cpp +++ b/tests/unit/fn_ptr_conditional.cpp @@ -1,3 +1,6 @@ +// Copyright (c) 2022-present INESC-ID. +// Distributed under the MIT license that can be found in the LICENSE file. + #include typedef int (*op_t)(int); diff --git a/tests/unit/fn_ptr_default_arg.cpp b/tests/unit/fn_ptr_default_arg.cpp index c5e0f55b..c5d659eb 100644 --- a/tests/unit/fn_ptr_default_arg.cpp +++ b/tests/unit/fn_ptr_default_arg.cpp @@ -1,3 +1,6 @@ +// Copyright (c) 2022-present INESC-ID. +// Distributed under the MIT license that can be found in the LICENSE file. + #include typedef int (*transform_t)(int); diff --git a/tests/unit/fn_ptr_global.cpp b/tests/unit/fn_ptr_global.cpp index 5ff3082f..034e2c2b 100644 --- a/tests/unit/fn_ptr_global.cpp +++ b/tests/unit/fn_ptr_global.cpp @@ -1,3 +1,6 @@ +// Copyright (c) 2022-present INESC-ID. +// Distributed under the MIT license that can be found in the LICENSE file. + #include typedef int (*op_t)(int); diff --git a/tests/unit/fn_ptr_reassign.cpp b/tests/unit/fn_ptr_reassign.cpp index b8dc48f8..ecb1af3e 100644 --- a/tests/unit/fn_ptr_reassign.cpp +++ b/tests/unit/fn_ptr_reassign.cpp @@ -1,3 +1,6 @@ +// Copyright (c) 2022-present INESC-ID. +// Distributed under the MIT license that can be found in the LICENSE file. + #include typedef int (*op_t)(int, int); diff --git a/tests/unit/fn_ptr_return.cpp b/tests/unit/fn_ptr_return.cpp index 6e570931..4112b6b0 100644 --- a/tests/unit/fn_ptr_return.cpp +++ b/tests/unit/fn_ptr_return.cpp @@ -1,3 +1,6 @@ +// Copyright (c) 2022-present INESC-ID. +// Distributed under the MIT license that can be found in the LICENSE file. + #include typedef int (*op_t)(int); diff --git a/tests/unit/fn_ptr_stable_sort.cpp b/tests/unit/fn_ptr_stable_sort.cpp index 8361b5a6..16b0f31f 100644 --- a/tests/unit/fn_ptr_stable_sort.cpp +++ b/tests/unit/fn_ptr_stable_sort.cpp @@ -1,3 +1,6 @@ +// Copyright (c) 2022-present INESC-ID. +// Distributed under the MIT license that can be found in the LICENSE file. + #include #include #include diff --git a/tests/unit/fn_ptr_struct.cpp b/tests/unit/fn_ptr_struct.cpp index 55216e7f..ccfa106d 100644 --- a/tests/unit/fn_ptr_struct.cpp +++ b/tests/unit/fn_ptr_struct.cpp @@ -1,3 +1,6 @@ +// Copyright (c) 2022-present INESC-ID. +// Distributed under the MIT license that can be found in the LICENSE file. + #include typedef int (*handler_t)(int); diff --git a/tests/unit/fn_ptr_void_return.cpp b/tests/unit/fn_ptr_void_return.cpp index 3ae221a9..bcd3fed4 100644 --- a/tests/unit/fn_ptr_void_return.cpp +++ b/tests/unit/fn_ptr_void_return.cpp @@ -1,3 +1,6 @@ +// Copyright (c) 2022-present INESC-ID. +// Distributed under the MIT license that can be found in the LICENSE file. + #include typedef void (*action_t)(int *); diff --git a/tests/unit/fn_ptr_vtable.cpp b/tests/unit/fn_ptr_vtable.cpp index 8d318a5c..6a75da96 100644 --- a/tests/unit/fn_ptr_vtable.cpp +++ b/tests/unit/fn_ptr_vtable.cpp @@ -1,3 +1,6 @@ +// Copyright (c) 2022-present INESC-ID. +// Distributed under the MIT license that can be found in the LICENSE file. + #include typedef void *(*create_fn)(int); diff --git a/tests/unit/lambda_capture_pass.cpp b/tests/unit/lambda_capture_pass.cpp index 439b128c..d9b2a05e 100644 --- a/tests/unit/lambda_capture_pass.cpp +++ b/tests/unit/lambda_capture_pass.cpp @@ -1,3 +1,6 @@ +// Copyright (c) 2022-present INESC-ID. +// Distributed under the MIT license that can be found in the LICENSE file. + #include template int apply(F fn, int x) { return fn(x); } diff --git a/tests/unit/lambda_nested.cpp b/tests/unit/lambda_nested.cpp index f7f26e93..14b0287b 100644 --- a/tests/unit/lambda_nested.cpp +++ b/tests/unit/lambda_nested.cpp @@ -1,3 +1,6 @@ +// Copyright (c) 2022-present INESC-ID. +// Distributed under the MIT license that can be found in the LICENSE file. + #include int main() { From 292902c83188726892ad98a0d9ecc09022ef3752 Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Fri, 17 Apr 2026 14:17:53 +0100 Subject: [PATCH 13/17] Don't pass addr to FnPtr::new --- .../converter/models/converter_refcount.cpp | 12 +++--- libcc2rs/src/fn_ptr.rs | 37 ++++++++++++++----- tests/unit/out/refcount/fn_ptr.rs | 6 +-- tests/unit/out/refcount/fn_ptr_array.rs | 10 ++--- .../unit/out/refcount/fn_ptr_as_condition.rs | 4 +- tests/unit/out/refcount/fn_ptr_cast.rs | 8 ++-- tests/unit/out/refcount/fn_ptr_conditional.rs | 10 ++--- tests/unit/out/refcount/fn_ptr_default_arg.rs | 3 +- tests/unit/out/refcount/fn_ptr_global.rs | 8 ++-- tests/unit/out/refcount/fn_ptr_reassign.rs | 8 ++-- tests/unit/out/refcount/fn_ptr_return.rs | 8 ++-- tests/unit/out/refcount/fn_ptr_struct.rs | 6 +-- tests/unit/out/refcount/fn_ptr_void_return.rs | 7 ++-- tests/unit/out/refcount/fn_ptr_vtable.rs | 6 +-- 14 files changed, 75 insertions(+), 58 deletions(-) diff --git a/cpp2rust/converter/models/converter_refcount.cpp b/cpp2rust/converter/models/converter_refcount.cpp index 6ebac25f..1a31c84e 100644 --- a/cpp2rust/converter/models/converter_refcount.cpp +++ b/cpp2rust/converter/models/converter_refcount.cpp @@ -1029,10 +1029,10 @@ void ConverterRefCount::EmitFnPtrCall(clang::Expr *callee) { void ConverterRefCount::ConvertFunctionToFunctionPointer( const clang::FunctionDecl *fn_decl) { - StrCat( - std::format("fn_ptr!({}, {})", GetNamedDeclAsString(fn_decl), - ConvertFunctionPointerType( - fn_decl->getType()->getAs()))); + StrCat(std::format("FnPtr::<{}>::new({})", + ConvertFunctionPointerType( + fn_decl->getType()->getAs()), + GetNamedDeclAsString(fn_decl))); } void ConverterRefCount::ConvertEqualsNullPtr(clang::Expr *expr) { @@ -1614,9 +1614,9 @@ void ConverterRefCount::ConvertVarInit(clang::QualType qual_type, Buffer buf(*this); PushConversionKind push(*this, ConversionKind::Unboxed); if (qual_type->isFunctionPointerType() && lambda->capture_size() == 0) { - StrCat("FnPtr::new(("); + StrCat("FnPtr::new("); VisitLambdaExpr(lambda); - StrCat("), 0)"); + StrCat(")"); } else { VisitLambdaExpr(lambda); } diff --git a/libcc2rs/src/fn_ptr.rs b/libcc2rs/src/fn_ptr.rs index e6fafbf2..6e84fccf 100644 --- a/libcc2rs/src/fn_ptr.rs +++ b/libcc2rs/src/fn_ptr.rs @@ -1,13 +1,6 @@ // Copyright (c) 2022-present INESC-ID. // Distributed under the MIT license that can be found in the LICENSE file. -#[macro_export] -macro_rules! fn_ptr { - ($f:expr, $ty:ty) => { - $crate::FnPtr::new($f as $ty, $f as *const () as usize) - }; -} - use std::any::{Any, TypeId}; use std::marker::PhantomData; use std::ops::Deref; @@ -16,6 +9,28 @@ use std::rc::Rc; use crate::rc::{AnyPtr, ErasedPtr}; use crate::reinterpret::ByteRepr; +pub trait FnAddr { + fn fn_addr(&self) -> usize; +} + +macro_rules! impl_fn_addr { + () => { + impl_fn_addr!(@gen A B C D E F G H I J); + }; + (@gen $($a:ident)*) => { + impl FnAddr for fn($($a,)*) -> R { + #[inline] + fn fn_addr(&self) -> usize { *self as *const () as usize } + } + impl_fn_addr!(@peel $($a)*); + }; + (@peel) => {}; + (@peel $head:ident $($tail:ident)*) => { + impl_fn_addr!(@gen $($tail)*); + }; +} +impl_fn_addr!(); + #[derive(Clone)] pub(crate) struct FnState { addr: usize, @@ -43,17 +58,19 @@ impl FnPtr { } } -impl FnPtr { - pub fn new(f: T, addr: usize) -> Self { +impl FnPtr { + pub fn new(f: T) -> Self { FnPtr { state: Some(Rc::new(FnState { - addr, + addr: f.fn_addr(), cast_history: vec![Some(Rc::new(f))], })), _marker: PhantomData, } } +} +impl FnPtr { pub fn cast(&self, adapter: Option) -> FnPtr { let state = self.state.as_ref().expect("ub: null fn pointer cast"); diff --git a/tests/unit/out/refcount/fn_ptr.rs b/tests/unit/out/refcount/fn_ptr.rs index 34753e3f..ee8e5002 100644 --- a/tests/unit/out/refcount/fn_ptr.rs +++ b/tests/unit/out/refcount/fn_ptr.rs @@ -27,13 +27,13 @@ fn main_0() -> i32 { assert!((*fn_.borrow()).is_null()); assert!({ let _lhs = (*fn_.borrow()).clone(); - _lhs != fn_ptr!(my_foo_0, fn(AnyPtr) -> i32) + _lhs != FnPtr:: i32>::new(my_foo_0) }); - (*fn_.borrow_mut()) = fn_ptr!(my_foo_0, fn(AnyPtr) -> i32); + (*fn_.borrow_mut()) = FnPtr:: i32>::new(my_foo_0); assert!(!((*fn_.borrow()).is_null())); assert!({ let _lhs = (*fn_.borrow()).clone(); - _lhs == fn_ptr!(my_foo_0, fn(AnyPtr) -> i32) + _lhs == FnPtr:: i32>::new(my_foo_0) }); let a: Value = Rc::new(RefCell::new(10)); assert!({ diff --git a/tests/unit/out/refcount/fn_ptr_array.rs b/tests/unit/out/refcount/fn_ptr_array.rs index e524cc73..2c3817c3 100644 --- a/tests/unit/out/refcount/fn_ptr_array.rs +++ b/tests/unit/out/refcount/fn_ptr_array.rs @@ -27,9 +27,9 @@ pub fn main() { } fn main_0() -> i32 { let ops: Value i32>]>> = Rc::new(RefCell::new(Box::new([ - fn_ptr!(add_0, fn(i32, i32) -> i32), - fn_ptr!(sub_1, fn(i32, i32) -> i32), - fn_ptr!(mul_2, fn(i32, i32) -> i32), + FnPtr:: i32>::new(add_0), + FnPtr:: i32>::new(sub_1), + FnPtr:: i32>::new(mul_2), ]))); assert!( (({ @@ -53,7 +53,7 @@ fn main_0() -> i32 { }) == 30) ); assert!(!(((*ops.borrow())[(0) as usize]).is_null())); - assert!(((*ops.borrow())[(0) as usize] == fn_ptr!(add_0, fn(i32, i32) -> i32))); - assert!(((*ops.borrow())[(0) as usize] != fn_ptr!(sub_1, fn(i32, i32) -> i32))); + assert!(((*ops.borrow())[(0) as usize] == FnPtr:: i32>::new(add_0))); + assert!(((*ops.borrow())[(0) as usize] != FnPtr:: i32>::new(sub_1))); return 0; } diff --git a/tests/unit/out/refcount/fn_ptr_as_condition.rs b/tests/unit/out/refcount/fn_ptr_as_condition.rs index 5afb76cf..880d2671 100644 --- a/tests/unit/out/refcount/fn_ptr_as_condition.rs +++ b/tests/unit/out/refcount/fn_ptr_as_condition.rs @@ -31,7 +31,7 @@ pub fn main() { fn main_0() -> i32 { let a: Value = Rc::new(RefCell::new(5)); ({ - let _cb: FnPtr)> = fn_ptr!(double_it_0, fn(Ptr::)); + let _cb: FnPtr)> = FnPtr::)>::new(double_it_0); let _x: Ptr = (a.as_pointer()); maybe_call_1(_cb, _x) }); @@ -45,7 +45,7 @@ fn main_0() -> i32 { assert!(((*b.borrow()) == 5)); let fn_: Value)>> = Rc::new(RefCell::new(FnPtr::null())); if !!(*fn_.borrow()).is_null() { - (*fn_.borrow_mut()) = (fn_ptr!(double_it_0, fn(Ptr::))).clone(); + (*fn_.borrow_mut()) = (FnPtr::)>::new(double_it_0)).clone(); } let c: Value = Rc::new(RefCell::new(3)); if !(*fn_.borrow()).is_null() { diff --git a/tests/unit/out/refcount/fn_ptr_cast.rs b/tests/unit/out/refcount/fn_ptr_cast.rs index 400cf447..0d0d8204 100644 --- a/tests/unit/out/refcount/fn_ptr_cast.rs +++ b/tests/unit/out/refcount/fn_ptr_cast.rs @@ -13,7 +13,7 @@ pub fn double_it_0(x: i32) -> i32 { } pub fn test_roundtrip_1() { let fn_: Value i32>> = - Rc::new(RefCell::new(fn_ptr!(double_it_0, fn(i32) -> i32))); + Rc::new(RefCell::new(FnPtr:: i32>::new(double_it_0))); assert!( (({ let _arg0: i32 = 5; @@ -39,7 +39,7 @@ pub fn test_roundtrip_1() { } pub fn test_double_cast_2() { let fn_: Value i32>> = - Rc::new(RefCell::new(fn_ptr!(double_it_0, fn(i32) -> i32))); + Rc::new(RefCell::new(FnPtr:: i32>::new(double_it_0))); let fn2: Value i32>> = Rc::new(RefCell::new( ((*fn_.borrow()) .cast::(None) @@ -72,7 +72,7 @@ impl Clone for Command { impl ByteRepr for Command {} pub fn test_void_ptr_to_fn_3() { let cmd: Value = Rc::new(RefCell::new(::default())); - (*(*cmd.borrow()).data.borrow_mut()) = fn_ptr!(double_it_0, fn(i32) -> i32).to_any(); + (*(*cmd.borrow()).data.borrow_mut()) = FnPtr:: i32>::new(double_it_0).to_any(); let fn_: Value i32>> = Rc::new(RefCell::new( ((*(*cmd.borrow()).data.borrow()) .cast_fn:: i32>() @@ -96,7 +96,7 @@ pub fn add_offset_4(base: Ptr, offset: i32) -> i32 { } pub fn test_call_through_cast_5() { let gfn: Value i32>> = Rc::new(RefCell::new( - fn_ptr!(add_offset_4, fn(Ptr::, i32) -> i32).cast:: i32>(Some( + FnPtr::, i32) -> i32>::new(add_offset_4).cast:: i32>(Some( (|a0: AnyPtr, a1: i32| -> i32 { add_offset_4(a0.cast::().unwrap(), a1) }) as fn(AnyPtr, i32) -> i32, )), diff --git a/tests/unit/out/refcount/fn_ptr_conditional.rs b/tests/unit/out/refcount/fn_ptr_conditional.rs index 88684794..4d277c65 100644 --- a/tests/unit/out/refcount/fn_ptr_conditional.rs +++ b/tests/unit/out/refcount/fn_ptr_conditional.rs @@ -22,12 +22,12 @@ pub fn identity_2(x: i32) -> i32 { pub fn pick_3(mode: i32) -> FnPtr i32> { let mode: Value = Rc::new(RefCell::new(mode)); return if ((*mode.borrow()) > 0) { - fn_ptr!(inc_0, fn(i32) -> i32) + FnPtr:: i32>::new(inc_0) } else { if ((*mode.borrow()) < 0) { - fn_ptr!(dec_1, fn(i32) -> i32) + FnPtr:: i32>::new(dec_1) } else { - fn_ptr!(identity_2, fn(i32) -> i32) + FnPtr:: i32>::new(identity_2) } }; } @@ -38,7 +38,7 @@ pub fn apply_4(fn_: FnPtr i32>, x: i32) -> i32 { Rc::new(RefCell::new(if !(*fn_.borrow()).is_null() { (*fn_.borrow()).clone() } else { - fn_ptr!(identity_2, fn(i32) -> i32) + FnPtr:: i32>::new(identity_2) })); return ({ let _arg0: i32 = (*x.borrow()); @@ -78,7 +78,7 @@ fn main_0() -> i32 { ); assert!( (({ - let _fn: FnPtr i32> = fn_ptr!(inc_0, fn(i32) -> i32); + let _fn: FnPtr i32> = FnPtr:: i32>::new(inc_0); let _x: i32 = 5; apply_4(_fn, _x) }) == 6) diff --git a/tests/unit/out/refcount/fn_ptr_default_arg.rs b/tests/unit/out/refcount/fn_ptr_default_arg.rs index 0c598b62..35f843d6 100644 --- a/tests/unit/out/refcount/fn_ptr_default_arg.rs +++ b/tests/unit/out/refcount/fn_ptr_default_arg.rs @@ -43,7 +43,7 @@ fn main_0() -> i32 { assert!( (({ let _x: i32 = 5; - let _fn: FnPtr i32> = fn_ptr!(identity_0, fn(i32) -> i32); + let _fn: FnPtr i32> = FnPtr:: i32>::new(identity_0); apply_1(_x, Some(_fn)) }) == 5) ); @@ -52,7 +52,6 @@ fn main_0() -> i32 { let x: Value = Rc::new(RefCell::new(x)); return -(*x.borrow()); }), - 0, ))); assert!( (({ diff --git a/tests/unit/out/refcount/fn_ptr_global.rs b/tests/unit/out/refcount/fn_ptr_global.rs index 94b7d347..3218860a 100644 --- a/tests/unit/out/refcount/fn_ptr_global.rs +++ b/tests/unit/out/refcount/fn_ptr_global.rs @@ -43,13 +43,13 @@ fn main_0() -> i32 { }) == 5) ); ({ - let _fn: FnPtr i32> = fn_ptr!(double_it_0, fn(i32) -> i32); + let _fn: FnPtr i32> = FnPtr:: i32>::new(double_it_0); set_op_2(_fn) }); assert!(!((*g_op.with(Value::clone).borrow()).is_null())); assert!({ let _lhs = (*g_op.with(Value::clone).borrow()).clone(); - _lhs == fn_ptr!(double_it_0, fn(i32) -> i32) + _lhs == FnPtr:: i32>::new(double_it_0) }); assert!( (({ @@ -58,12 +58,12 @@ fn main_0() -> i32 { }) == 10) ); ({ - let _fn: FnPtr i32> = fn_ptr!(triple_it_1, fn(i32) -> i32); + let _fn: FnPtr i32> = FnPtr:: i32>::new(triple_it_1); set_op_2(_fn) }); assert!({ let _lhs = (*g_op.with(Value::clone).borrow()).clone(); - _lhs == fn_ptr!(triple_it_1, fn(i32) -> i32) + _lhs == FnPtr:: i32>::new(triple_it_1) }); assert!( (({ diff --git a/tests/unit/out/refcount/fn_ptr_reassign.rs b/tests/unit/out/refcount/fn_ptr_reassign.rs index c8c47f6d..da316260 100644 --- a/tests/unit/out/refcount/fn_ptr_reassign.rs +++ b/tests/unit/out/refcount/fn_ptr_reassign.rs @@ -27,7 +27,7 @@ pub fn main() { } fn main_0() -> i32 { let fn_: Value i32>> = - Rc::new(RefCell::new(fn_ptr!(add_0, fn(i32, i32) -> i32))); + Rc::new(RefCell::new(FnPtr:: i32>::new(add_0))); assert!( (({ let _arg0: i32 = 3; @@ -35,7 +35,7 @@ fn main_0() -> i32 { (*(*fn_.borrow()))(_arg0, _arg1) }) == 7) ); - (*fn_.borrow_mut()) = fn_ptr!(sub_1, fn(i32, i32) -> i32); + (*fn_.borrow_mut()) = FnPtr:: i32>::new(sub_1); assert!( (({ let _arg0: i32 = 10; @@ -43,7 +43,7 @@ fn main_0() -> i32 { (*(*fn_.borrow()))(_arg0, _arg1) }) == 7) ); - (*fn_.borrow_mut()) = fn_ptr!(mul_2, fn(i32, i32) -> i32); + (*fn_.borrow_mut()) = FnPtr:: i32>::new(mul_2); assert!( (({ let _arg0: i32 = 6; @@ -53,7 +53,7 @@ fn main_0() -> i32 { ); (*fn_.borrow_mut()) = FnPtr::null(); assert!((*fn_.borrow()).is_null()); - (*fn_.borrow_mut()) = fn_ptr!(add_0, fn(i32, i32) -> i32); + (*fn_.borrow_mut()) = FnPtr:: i32>::new(add_0); assert!(!((*fn_.borrow()).is_null())); assert!( (({ diff --git a/tests/unit/out/refcount/fn_ptr_return.rs b/tests/unit/out/refcount/fn_ptr_return.rs index b80f7c1b..3a6ab413 100644 --- a/tests/unit/out/refcount/fn_ptr_return.rs +++ b/tests/unit/out/refcount/fn_ptr_return.rs @@ -18,9 +18,9 @@ pub fn dec_1(x: i32) -> i32 { pub fn pick_2(choose_inc: i32) -> FnPtr i32> { let choose_inc: Value = Rc::new(RefCell::new(choose_inc)); if ((*choose_inc.borrow()) != 0) { - return fn_ptr!(inc_0, fn(i32) -> i32); + return FnPtr:: i32>::new(inc_0); } - return fn_ptr!(dec_1, fn(i32) -> i32); + return FnPtr:: i32>::new(dec_1); } pub fn main() { std::process::exit(main_0()); @@ -35,7 +35,7 @@ fn main_0() -> i32 { assert!(!((*f.borrow()).is_null())); assert!({ let _lhs = (*f.borrow()).clone(); - _lhs == fn_ptr!(inc_0, fn(i32) -> i32) + _lhs == FnPtr:: i32>::new(inc_0) }); assert!( (({ @@ -51,7 +51,7 @@ fn main_0() -> i32 { )); assert!({ let _lhs = (*g.borrow()).clone(); - _lhs == fn_ptr!(dec_1, fn(i32) -> i32) + _lhs == FnPtr:: i32>::new(dec_1) }); assert!( (({ diff --git a/tests/unit/out/refcount/fn_ptr_struct.rs b/tests/unit/out/refcount/fn_ptr_struct.rs index 0d34f1d4..506edd3f 100644 --- a/tests/unit/out/refcount/fn_ptr_struct.rs +++ b/tests/unit/out/refcount/fn_ptr_struct.rs @@ -44,11 +44,11 @@ pub fn main() { fn main_0() -> i32 { let h1: Value = Rc::new(RefCell::new(Handler { tag: Rc::new(RefCell::new(1)), - cb: Rc::new(RefCell::new(fn_ptr!(double_it_0, fn(i32) -> i32))), + cb: Rc::new(RefCell::new(FnPtr:: i32>::new(double_it_0))), })); let h2: Value = Rc::new(RefCell::new(Handler { tag: Rc::new(RefCell::new(2)), - cb: Rc::new(RefCell::new(fn_ptr!(negate_1, fn(i32) -> i32))), + cb: Rc::new(RefCell::new(FnPtr:: i32>::new(negate_1))), })); assert!(!((*(*h1.borrow()).cb.borrow()).is_null())); assert!( @@ -63,7 +63,7 @@ fn main_0() -> i32 { (*(*(*h2.borrow()).cb.borrow()))(_arg0) }) == -7_i32) ); - (*(*h1.borrow()).cb.borrow_mut()) = fn_ptr!(negate_1, fn(i32) -> i32); + (*(*h1.borrow()).cb.borrow_mut()) = FnPtr:: i32>::new(negate_1); assert!( (({ let _arg0: i32 = 3; diff --git a/tests/unit/out/refcount/fn_ptr_void_return.rs b/tests/unit/out/refcount/fn_ptr_void_return.rs index dc49639c..d095d4e0 100644 --- a/tests/unit/out/refcount/fn_ptr_void_return.rs +++ b/tests/unit/out/refcount/fn_ptr_void_return.rs @@ -30,18 +30,19 @@ pub fn main() { fn main_0() -> i32 { let a: Value = Rc::new(RefCell::new(42)); ({ - let _fn: FnPtr)> = fn_ptr!(negate_0, fn(Ptr::)); + let _fn: FnPtr)> = FnPtr::)>::new(negate_0); let _x: Ptr = (a.as_pointer()); run_2(_fn, _x) }); assert!(((*a.borrow()) == -42_i32)); ({ - let _fn: FnPtr)> = fn_ptr!(zero_out_1, fn(Ptr::)); + let _fn: FnPtr)> = FnPtr::)>::new(zero_out_1); let _x: Ptr = (a.as_pointer()); run_2(_fn, _x) }); assert!(((*a.borrow()) == 0)); - let fn_: Value)>> = Rc::new(RefCell::new(fn_ptr!(negate_0, fn(Ptr::)))); + let fn_: Value)>> = + Rc::new(RefCell::new(FnPtr::)>::new(negate_0))); assert!(!((*fn_.borrow()).is_null())); let b: Value = Rc::new(RefCell::new(10)); ({ diff --git a/tests/unit/out/refcount/fn_ptr_vtable.rs b/tests/unit/out/refcount/fn_ptr_vtable.rs index b8e80be5..cb12c7e5 100644 --- a/tests/unit/out/refcount/fn_ptr_vtable.rs +++ b/tests/unit/out/refcount/fn_ptr_vtable.rs @@ -55,10 +55,10 @@ pub fn main() { fn main_0() -> i32 { let vt: Value = Rc::new(RefCell::new(Vtable { create: Rc::new(RefCell::new( - (fn_ptr!(int_create_0, fn(i32) -> AnyPtr)).clone(), + (FnPtr:: AnyPtr>::new(int_create_0)).clone(), )), - get: Rc::new(RefCell::new(fn_ptr!(int_get_1, fn(AnyPtr) -> i32))), - destroy: Rc::new(RefCell::new(fn_ptr!(int_destroy_2, fn(AnyPtr)))), + get: Rc::new(RefCell::new(FnPtr:: i32>::new(int_get_1))), + destroy: Rc::new(RefCell::new(FnPtr::::new(int_destroy_2))), })); assert!(!((*(*vt.borrow()).create.borrow()).is_null())); assert!(!((*(*vt.borrow()).get.borrow()).is_null())); From 2e865ea83c3b5bcb4fc76f4d3de91f92d9bf6542 Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Fri, 17 Apr 2026 14:35:15 +0100 Subject: [PATCH 14/17] Replace cast_history with original + current_cast --- libcc2rs/src/fn_ptr.rs | 46 +++++++++++++++++++----------------------- 1 file changed, 21 insertions(+), 25 deletions(-) diff --git a/libcc2rs/src/fn_ptr.rs b/libcc2rs/src/fn_ptr.rs index 6e84fccf..60db555a 100644 --- a/libcc2rs/src/fn_ptr.rs +++ b/libcc2rs/src/fn_ptr.rs @@ -34,7 +34,8 @@ impl_fn_addr!(); #[derive(Clone)] pub(crate) struct FnState { addr: usize, - cast_history: Vec>>, + original: Rc, + current_cast: Option>, } pub struct FnPtr { @@ -60,10 +61,13 @@ impl FnPtr { impl FnPtr { pub fn new(f: T) -> Self { + let addr = f.fn_addr(); + let rc: Rc = Rc::new(f); FnPtr { state: Some(Rc::new(FnState { - addr: f.fn_addr(), - cast_history: vec![Some(Rc::new(f))], + addr, + original: rc.clone(), + current_cast: Some(rc), })), _marker: PhantomData, } @@ -74,27 +78,23 @@ impl FnPtr { pub fn cast(&self, adapter: Option) -> FnPtr { let state = self.state.as_ref().expect("ub: null fn pointer cast"); - for (i, entry) in state.cast_history.iter().enumerate() { - if let Some(ref rc) = entry { - if (*rc).as_ref().type_id() == TypeId::of::() { - return FnPtr { - state: Some(Rc::new(FnState { - addr: state.addr, - cast_history: state.cast_history[..=i].to_vec(), - })), - _marker: PhantomData, - }; - } - } - } - - let mut new_stack = state.cast_history.clone(); - new_stack.push(adapter.map(|a| Rc::new(a) as Rc)); + let current_cast = if state + .current_cast + .as_ref() + .is_some_and(|rc| (**rc).type_id() == TypeId::of::()) + { + state.current_cast.clone() + } else if (*state.original).type_id() == TypeId::of::() { + Some(state.original.clone()) + } else { + adapter.map(|a| Rc::new(a) as Rc) + }; FnPtr { state: Some(Rc::new(FnState { addr: state.addr, - cast_history: new_stack, + original: state.original.clone(), + current_cast, })), _marker: PhantomData, } @@ -105,11 +105,7 @@ impl Deref for FnPtr { type Target = T; fn deref(&self) -> &T { let state = self.state.as_ref().expect("ub: null fn pointer call"); - let entry = state - .cast_history - .last() - .expect("empty fn pointer cast_history"); - match entry { + match &state.current_cast { Some(rc) => rc .downcast_ref::() .expect("ub: fn pointer type mismatch"), From acbc43bb461e94699d3e385b6bacdd454694cd53 Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Fri, 17 Apr 2026 14:35:42 +0100 Subject: [PATCH 15/17] Update tests --- tests/unit/out/refcount/no_direct_callee.rs | 2 +- tests/unit/out/unsafe/no_direct_callee.rs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/unit/out/refcount/no_direct_callee.rs b/tests/unit/out/refcount/no_direct_callee.rs index 6b81f983..4cd3f05c 100644 --- a/tests/unit/out/refcount/no_direct_callee.rs +++ b/tests/unit/out/refcount/no_direct_callee.rs @@ -22,7 +22,7 @@ pub fn main() { } fn main_0() -> i32 { return ({ - let _fn: FnPtr bool> = fn_ptr!(test1_0, fn() -> bool); + let _fn: FnPtr bool> = FnPtr:: bool>::new(test1_0); test_1(_fn) }); } diff --git a/tests/unit/out/unsafe/no_direct_callee.rs b/tests/unit/out/unsafe/no_direct_callee.rs index a30f6a2b..b4eac215 100644 --- a/tests/unit/out/unsafe/no_direct_callee.rs +++ b/tests/unit/out/unsafe/no_direct_callee.rs @@ -10,7 +10,7 @@ use std::rc::Rc; pub unsafe fn test1_0() -> bool { return false; } -pub unsafe fn test_1(mut fn_: Option bool>) -> i32 { +pub unsafe fn test_1(mut fn_: Option bool>) -> i32 { if !(unsafe { (fn_).unwrap()() }) { return 1; } @@ -23,7 +23,7 @@ pub fn main() { } unsafe fn main_0() -> i32 { return (unsafe { - let _fn: Option bool> = Some(test1_0); + let _fn: Option bool> = Some(test1_0); test_1(_fn) }); } From 407bd8b4326120c31ef3d01170e5daff8f2bbfb6 Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Mon, 20 Apr 2026 10:19:00 +0100 Subject: [PATCH 16/17] Add is_null for ErasedPtr for FnPtr --- libcc2rs/src/fn_ptr.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/libcc2rs/src/fn_ptr.rs b/libcc2rs/src/fn_ptr.rs index 60db555a..a07c8056 100644 --- a/libcc2rs/src/fn_ptr.rs +++ b/libcc2rs/src/fn_ptr.rs @@ -159,6 +159,9 @@ impl ErasedPtr for FnPtr { } other.as_any().downcast_ref::>().map(|o| self == o) } + fn is_null(&self) -> bool { + FnPtr::is_null(self) + } } impl FnPtr { From c460a1c54f0465fa9dbe3f68c8c494c17e82421c Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Mon, 20 Apr 2026 12:22:33 +0100 Subject: [PATCH 17/17] Delete FnState and add ErasedFn trait --- libcc2rs/src/fn_ptr.rs | 76 ++++++++++++++++++++++-------------------- 1 file changed, 39 insertions(+), 37 deletions(-) diff --git a/libcc2rs/src/fn_ptr.rs b/libcc2rs/src/fn_ptr.rs index a07c8056..2ccc150e 100644 --- a/libcc2rs/src/fn_ptr.rs +++ b/libcc2rs/src/fn_ptr.rs @@ -31,15 +31,19 @@ macro_rules! impl_fn_addr { } impl_fn_addr!(); -#[derive(Clone)] -pub(crate) struct FnState { - addr: usize, - original: Rc, - current_cast: Option>, +trait ErasedFn: Any { + fn addr(&self) -> usize; +} + +impl ErasedFn for T { + fn addr(&self) -> usize { + self.fn_addr() + } } pub struct FnPtr { - state: Option>, + original: Option>, + current_cast: Option>, // FnPtr does not use T, hence wrap in PhantomData _marker: PhantomData, } @@ -48,54 +52,48 @@ impl FnPtr { #[inline] pub fn null() -> Self { FnPtr { - state: None, + original: None, + current_cast: None, _marker: PhantomData, } } #[inline] pub fn is_null(&self) -> bool { - self.state.is_none() + self.original.is_none() } } impl FnPtr { pub fn new(f: T) -> Self { - let addr = f.fn_addr(); - let rc: Rc = Rc::new(f); + let rc: Rc = Rc::new(f); FnPtr { - state: Some(Rc::new(FnState { - addr, - original: rc.clone(), - current_cast: Some(rc), - })), + original: Some(rc.clone()), + current_cast: Some(rc), _marker: PhantomData, } } } impl FnPtr { - pub fn cast(&self, adapter: Option) -> FnPtr { - let state = self.state.as_ref().expect("ub: null fn pointer cast"); + pub fn cast(&self, adapter: Option) -> FnPtr { + let original = self.original.as_ref().expect("ub: null fn pointer cast"); - let current_cast = if state + let current_cast = if self .current_cast .as_ref() - .is_some_and(|rc| (**rc).type_id() == TypeId::of::()) + .is_some_and(|rc| Any::type_id(&**rc) == TypeId::of::()) { - state.current_cast.clone() - } else if (*state.original).type_id() == TypeId::of::() { - Some(state.original.clone()) + self.current_cast.clone() + } else if Any::type_id(&**original) == TypeId::of::() { + Some(original.clone()) } else { - adapter.map(|a| Rc::new(a) as Rc) + adapter.map(|a| Rc::new(a) as Rc) }; FnPtr { - state: Some(Rc::new(FnState { - addr: state.addr, - original: state.original.clone(), - current_cast, - })), + original: Some(original.clone()), + current_cast, _marker: PhantomData, } } @@ -104,20 +102,24 @@ impl FnPtr { impl Deref for FnPtr { type Target = T; fn deref(&self) -> &T { - let state = self.state.as_ref().expect("ub: null fn pointer call"); - match &state.current_cast { - Some(rc) => rc - .downcast_ref::() - .expect("ub: fn pointer type mismatch"), - None => panic!("ub: calling through incompatible fn pointer type"), + if self.original.is_none() { + panic!("ub: null fn pointer call"); } + let rc = self + .current_cast + .as_ref() + .expect("ub: calling through incompatible fn pointer type"); + let any: &dyn Any = &**rc; + any.downcast_ref::() + .expect("ub: fn pointer type mismatch") } } impl Clone for FnPtr { fn clone(&self) -> Self { FnPtr { - state: self.state.clone(), + original: self.original.clone(), + current_cast: self.current_cast.clone(), _marker: PhantomData, } } @@ -131,9 +133,9 @@ impl Default for FnPtr { impl PartialEq for FnPtr { fn eq(&self, other: &Self) -> bool { - match (&self.state, &other.state) { + match (&self.original, &other.original) { (None, None) => true, - (Some(a), Some(b)) => a.addr == b.addr, + (Some(a), Some(b)) => a.addr() == b.addr(), _ => false, } }