From 117afb3ce52f18cb996a3d63518b2342d46c7281 Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Mon, 13 Apr 2026 20:18:07 +0100 Subject: [PATCH 01/56] Translate free function ptr as Option --- cpp2rust/converter/converter.cpp | 33 ++++--------------- .../converter/models/converter_refcount.cpp | 2 +- 2 files changed, 7 insertions(+), 28 deletions(-) diff --git a/cpp2rust/converter/converter.cpp b/cpp2rust/converter/converter.cpp index 0f6f48d3..64f8ee47 100644 --- a/cpp2rust/converter/converter.cpp +++ b/cpp2rust/converter/converter.cpp @@ -226,7 +226,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))); } @@ -1993,23 +1993,9 @@ bool Converter::VisitDeclRefExpr(clang::DeclRefExpr *expr) { return false; } - if (auto function = clang::dyn_cast(decl)) { + if (clang::isa(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); + StrCat(std::format("Some({})", str)); return false; } } @@ -2609,15 +2595,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) { @@ -2816,7 +2794,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()"); diff --git a/cpp2rust/converter/models/converter_refcount.cpp b/cpp2rust/converter/models/converter_refcount.cpp index 6c589876..a31945d4 100644 --- a/cpp2rust/converter/models/converter_refcount.cpp +++ b/cpp2rust/converter/models/converter_refcount.cpp @@ -570,7 +570,7 @@ bool ConverterRefCount::VisitDeclRefExpr(clang::DeclRefExpr *expr) { if (clang::isa(decl)) { if (isAddrOf()) { - StrCat(std::format("Rc::new({})", str)); + StrCat(std::format("Some({})", str)); } else { StrCat(str); } From 562978b764b56bf37161e7d15573305a2e3b22ae Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Mon, 13 Apr 2026 20:18:45 +0100 Subject: [PATCH 02/56] Fix null ptr deref --- cpp2rust/converter/converter.cpp | 2 +- cpp2rust/converter/converter_lib.cpp | 24 ++++++++++++------------ 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/cpp2rust/converter/converter.cpp b/cpp2rust/converter/converter.cpp index 64f8ee47..23b052e3 100644 --- a/cpp2rust/converter/converter.cpp +++ b/cpp2rust/converter/converter.cpp @@ -1384,7 +1384,7 @@ 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) { diff --git a/cpp2rust/converter/converter_lib.cpp b/cpp2rust/converter/converter_lib.cpp index ba3899a4..b299bcb6 100644 --- a/cpp2rust/converter/converter_lib.cpp +++ b/cpp2rust/converter/converter_lib.cpp @@ -320,18 +320,18 @@ 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(); - } - - auto callee_ty = - expr->getCallee()->getType().getDesugaredType(decl->getASTContext()); - if (auto ptr_ty = callee_ty->getAs()) { - return ptr_ty->getPointeeType() - ->getAs() - ->getReturnType() - .getCanonicalType(); + if (auto *decl = expr->getCalleeDecl()) { + if (auto *fn = decl->getAsFunction()) { + return fn->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"); From 32ae03cf4d008c92b7346a6f5c5f59b8259f3dc9 Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Mon, 13 Apr 2026 20:24:41 +0100 Subject: [PATCH 03/56] Fix translation of lambdas Non-capturing lambdas are translated as Option, same as free functions, because they can be decayed to free functions. Capturing lambdas remain Rc. --- cpp2rust/converter/converter.cpp | 49 +++++++++++++++---- cpp2rust/converter/converter_lib.cpp | 11 +++++ cpp2rust/converter/converter_lib.h | 2 + .../converter/models/converter_refcount.cpp | 11 +++-- 4 files changed, 61 insertions(+), 12 deletions(-) diff --git a/cpp2rust/converter/converter.cpp b/cpp2rust/converter/converter.cpp index 23b052e3..92f351e3 100644 --- a/cpp2rust/converter/converter.cpp +++ b/cpp2rust/converter/converter.cpp @@ -417,6 +417,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())) { @@ -1432,7 +1435,14 @@ void Converter::ConvertGenericCallExpr(clang::CallExpr *expr) { } } - Convert(callee); + if (proto && !function) { + StrCat(token::kOpenParen); + Convert(callee); + StrCat(").unwrap()"); + } 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); @@ -1648,7 +1658,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); @@ -2001,13 +2015,15 @@ bool Converter::VisitDeclRefExpr(clang::DeclRefExpr *expr) { } 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; + } } } } @@ -2466,6 +2482,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()) { @@ -2749,6 +2768,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); @@ -3180,6 +3209,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_lib.cpp b/cpp2rust/converter/converter_lib.cpp index b299bcb6..26624a03 100644 --- a/cpp2rust/converter/converter_lib.cpp +++ b/cpp2rust/converter/converter_lib.cpp @@ -338,6 +338,17 @@ 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 c0d2a65d..68c27049 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 a31945d4..630287c1 100644 --- a/cpp2rust/converter/models/converter_refcount.cpp +++ b/cpp2rust/converter/models/converter_refcount.cpp @@ -1477,9 +1477,14 @@ 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) { + PushExprKind addr_of(*this, ExprKind::AddrOf); + VisitLambdaExpr(lambda); + } else { + StrCat("Rc::new("); + VisitLambdaExpr(lambda); + StrCat(")"); + } str = std::move(buf).str(); } StrCat(BoxValue(std::move(str))); From c431b2953fb78665e3185b6cbd1e7e7efc69b2ce Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Mon, 13 Apr 2026 21:30:05 +0100 Subject: [PATCH 04/56] Translate type of lambda as _ instead of Rc --- cpp2rust/converter/converter.cpp | 24 +++++++++++-------- .../converter/models/converter_refcount.cpp | 2 -- 2 files changed, 14 insertions(+), 12 deletions(-) diff --git a/cpp2rust/converter/converter.cpp b/cpp2rust/converter/converter.cpp index 92f351e3..3ecee285 100644 --- a/cpp2rust/converter/converter.cpp +++ b/cpp2rust/converter/converter.cpp @@ -130,17 +130,21 @@ 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_) { + // Function parameters can't use `_`. Emit `impl Fn(args) -> ret`. + 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; } } diff --git a/cpp2rust/converter/models/converter_refcount.cpp b/cpp2rust/converter/models/converter_refcount.cpp index 630287c1..aea26e47 100644 --- a/cpp2rust/converter/models/converter_refcount.cpp +++ b/cpp2rust/converter/models/converter_refcount.cpp @@ -1481,9 +1481,7 @@ void ConverterRefCount::ConvertVarInit(clang::QualType qual_type, PushExprKind addr_of(*this, ExprKind::AddrOf); VisitLambdaExpr(lambda); } else { - StrCat("Rc::new("); VisitLambdaExpr(lambda); - StrCat(")"); } str = std::move(buf).str(); } From de9631532363467302be125e2ed3a2fbd772e8f3 Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Mon, 13 Apr 2026 21:30:42 +0100 Subject: [PATCH 05/56] Translate non-const globals as static mut --- cpp2rust/converter/converter.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/cpp2rust/converter/converter.cpp b/cpp2rust/converter/converter.cpp index 3ecee285..f8542c8a 100644 --- a/cpp2rust/converter/converter.cpp +++ b/cpp2rust/converter/converter.cpp @@ -387,9 +387,15 @@ bool Converter::ConvertVarDeclSkipInit(clang::VarDecl *decl) { return false; } StrCat(AccessSpecifierAsString(decl->getAccess()), keyword::kStatic); + if (!qual_type.isConstQualified()) { + StrCat(keyword_mut_); + } ENSURE(decl_ids_.insert(GetID(decl)).second); } else if (decl->isStaticLocal()) { StrCat(keyword::kStatic); + if (!qual_type.isConstQualified()) { + StrCat(keyword_mut_); + } } else if (decl->isLocalVarDecl()) { StrCat(keyword::kLet); } From 212d22bb67c1f993ac7bc7f4eb64aa788952440c Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Mon, 13 Apr 2026 21:31:32 +0100 Subject: [PATCH 06/56] Use concerete default val for initializing globals --- cpp2rust/converter/converter.cpp | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/cpp2rust/converter/converter.cpp b/cpp2rust/converter/converter.cpp index f8542c8a..a17e27ac 100644 --- a/cpp2rust/converter/converter.cpp +++ b/cpp2rust/converter/converter.cpp @@ -1687,7 +1687,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: @@ -2686,18 +2690,18 @@ std::string Converter::GetDefaultAsString(clang::QualType qual_type) { } std::string Converter::GetDefaultAsStringFallback(clang::QualType qual_type) { - static llvm::DenseMap default_for_type = { - {clang::BuiltinType::Char_U, "0_u8"}, - {clang::BuiltinType::SChar, "0_i8"}, - {clang::BuiltinType::UChar, "0_u8"}, - }; - qual_type = qual_type.getUnqualifiedType().getCanonicalType(); - if (auto builtin = qual_type->getAs()) { - auto it = default_for_type.find(builtin->getKind()); - if (it != default_for_type.end()) { - return it->second; - } + + if (qual_type->isBooleanType()) { + return "false"; + } + + if (qual_type->isIntegerType()) { + return std::format("0 as {}", ToString(qual_type)); + } + + if (qual_type->isFloatingType()) { + return std::format("0.0 as {}", ToString(qual_type)); } return std::format("<{}>::default()", ToString(qual_type)); From 58c39b9ef0f144e8ff7b212720ee049062d2fbcb Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Mon, 13 Apr 2026 21:31:53 +0100 Subject: [PATCH 07/56] Help the compiler deduce the type of a function --- cpp2rust/converter/converter.cpp | 2 +- cpp2rust/converter/models/converter_refcount.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/cpp2rust/converter/converter.cpp b/cpp2rust/converter/converter.cpp index a17e27ac..95ae9d38 100644 --- a/cpp2rust/converter/converter.cpp +++ b/cpp2rust/converter/converter.cpp @@ -2023,7 +2023,7 @@ bool Converter::VisitDeclRefExpr(clang::DeclRefExpr *expr) { if (clang::isa(decl)) { if (isAddrOf()) { - StrCat(std::format("Some({})", str)); + StrCat(std::format("Some({} as _)", str)); return false; } } diff --git a/cpp2rust/converter/models/converter_refcount.cpp b/cpp2rust/converter/models/converter_refcount.cpp index aea26e47..8234a2c0 100644 --- a/cpp2rust/converter/models/converter_refcount.cpp +++ b/cpp2rust/converter/models/converter_refcount.cpp @@ -570,7 +570,7 @@ bool ConverterRefCount::VisitDeclRefExpr(clang::DeclRefExpr *expr) { if (clang::isa(decl)) { if (isAddrOf()) { - StrCat(std::format("Some({})", str)); + StrCat(std::format("Some({} as _)", str)); } else { StrCat(str); } From 12a44335ca2493122601fa68d9993cf8e23a6c98 Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Mon, 13 Apr 2026 21:32:10 +0100 Subject: [PATCH 08/56] Don't add &mut in function ptr conditionals --- cpp2rust/converter/converter.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cpp2rust/converter/converter.cpp b/cpp2rust/converter/converter.cpp index 95ae9d38..747a002f 100644 --- a/cpp2rust/converter/converter.cpp +++ b/cpp2rust/converter/converter.cpp @@ -1962,12 +1962,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()); From 7ab067db8637f5ed22319e8f352d7d6718564345 Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Mon, 13 Apr 2026 21:32:49 +0100 Subject: [PATCH 09/56] Add fn_ptr and lambda 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 | 44 +++++++++ tests/unit/fn_ptr_conditional.cpp | 27 ++++++ 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_stdlib_compare.cpp | 32 ++++++ tests/unit/fn_ptr_struct.cpp | 26 +++++ tests/unit/fn_ptr_void_return.cpp | 27 ++++++ tests/unit/fn_ptr_vtable.cpp | 41 ++++++++ tests/unit/lambda_capture_pass.cpp | 20 ++++ tests/unit/lambda_nested.cpp | 17 ++++ 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 | 97 +++++++++++++++++++ tests/unit/out/refcount/fn_ptr_conditional.rs | 97 +++++++++++++++++++ tests/unit/out/refcount/fn_ptr_default_arg.rs | 62 ++++++++++++ tests/unit/out/refcount/fn_ptr_global.rs | 86 ++++++++++++++++ tests/unit/out/refcount/fn_ptr_reassign.rs | 65 +++++++++++++ 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 | 84 ++++++++++++++++ .../unit/out/refcount/lambda_capture_local.rs | 74 ++++++++++++++ .../unit/out/refcount/lambda_capture_pass.rs | 69 +++++++++++++ tests/unit/out/refcount/lambda_nested.rs | 45 +++++++++ tests/unit/out/refcount/lambda_nocapture.rs | 74 ++++++++++++++ tests/unit/out/unsafe/fn_ptr.rs | 43 ++++++++ tests/unit/out/unsafe/fn_ptr_array.rs | 52 ++++++++++ tests/unit/out/unsafe/fn_ptr_as_condition.rs | 54 +++++++++++ tests/unit/out/unsafe/fn_ptr_cast.rs | 69 +++++++++++++ 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_mapped.rs | 45 +++++++++ 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 +++++++++++++ tests/unit/out/unsafe/lambda_capture_local.rs | 72 ++++++++++++++ tests/unit/out/unsafe/lambda_capture_pass.rs | 62 ++++++++++++ tests/unit/out/unsafe/lambda_nested.rs | 45 +++++++++ tests/unit/out/unsafe/lambda_nocapture.rs | 64 ++++++++++++ 51 files changed, 2720 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_stdlib_compare.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 create mode 100644 tests/unit/lambda_capture_pass.cpp create mode 100644 tests/unit/lambda_nested.cpp 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/refcount/lambda_capture_local.rs 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/refcount/lambda_nocapture.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_mapped.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 create mode 100644 tests/unit/out/unsafe/lambda_capture_local.rs create mode 100644 tests/unit/out/unsafe/lambda_capture_pass.rs create mode 100644 tests/unit/out/unsafe/lambda_nested.rs create mode 100644 tests/unit/out/unsafe/lambda_nocapture.rs 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..d9fc7ba9 --- /dev/null +++ b/tests/unit/fn_ptr_cast.cpp @@ -0,0 +1,44 @@ +#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); +} + +int main() { + test_roundtrip(); + test_double_cast(); + test_void_ptr_to_fn(); + return 0; +} diff --git a/tests/unit/fn_ptr_conditional.cpp b/tests/unit/fn_ptr_conditional.cpp new file mode 100644 index 00000000..ad39614e --- /dev/null +++ b/tests/unit/fn_ptr_conditional.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; } +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_stdlib_compare.cpp b/tests/unit/fn_ptr_stdlib_compare.cpp new file mode 100644 index 00000000..e2ca1c3e --- /dev/null +++ b/tests/unit/fn_ptr_stdlib_compare.cpp @@ -0,0 +1,32 @@ +#include +#include +#include + +typedef size_t (*read_fn)(void *, size_t, size_t, FILE *); +typedef void (*free_fn)(void *); +typedef void *(*malloc_fn)(size_t); + +int main() { + read_fn rfn = fread; + assert(rfn == fread); + assert(rfn != nullptr); + + free_fn ffn = free; + assert(ffn == free); + assert(ffn != nullptr); + + malloc_fn mfn = malloc; + assert(mfn == malloc); + assert(mfn != nullptr); + + // Reassign and compare + read_fn rfn2 = fread; + assert(rfn == rfn2); + + free_fn ffn2 = nullptr; + assert(ffn != ffn2); + ffn2 = free; + assert(ffn == ffn2); + + 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..ebf7b39f --- /dev/null +++ b/tests/unit/fn_ptr_void_return.cpp @@ -0,0 +1,27 @@ +#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; +} diff --git a/tests/unit/lambda_capture_pass.cpp b/tests/unit/lambda_capture_pass.cpp new file mode 100644 index 00000000..1a7637f4 --- /dev/null +++ b/tests/unit/lambda_capture_pass.cpp @@ -0,0 +1,20 @@ +#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; +} diff --git a/tests/unit/out/refcount/fn_ptr.rs b/tests/unit/out/refcount/fn_ptr.rs new file mode 100644 index 00000000..77a3f29b --- /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_: Option 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()).unwrap()(_arg0) + }); +} +pub fn main() { + std::process::exit(main_0()); +} +fn main_0() -> i32 { + let fn_: Value i32>> = Rc::new(RefCell::new(None)); + assert!((*fn_.borrow()).is_none()); + assert!({ + let _lhs = (*fn_.borrow()).clone(); + _lhs != Some(my_foo_0 as _) + }); + (*fn_.borrow_mut()) = Some(my_foo_0 as _); + assert!(!((*fn_.borrow()).is_none())); + assert!({ + let _lhs = (*fn_.borrow()).clone(); + _lhs == Some(my_foo_0 as _) + }); + let a: Value = Rc::new(RefCell::new(10)); + assert!({ + let _lhs = ({ + let _fn: Option 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..3323c920 --- /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([ + Some(add_0 as _), + Some(sub_1 as _), + Some(mul_2 as _), + ]))); + assert!( + (({ + let _arg0: i32 = 2; + let _arg1: i32 = 3; + ((*ops.borrow())[(0) as usize]).unwrap()(_arg0, _arg1) + }) == 5) + ); + assert!( + (({ + let _arg0: i32 = 7; + let _arg1: i32 = 4; + ((*ops.borrow())[(1) as usize]).unwrap()(_arg0, _arg1) + }) == 3) + ); + assert!( + (({ + let _arg0: i32 = 6; + let _arg1: i32 = 5; + ((*ops.borrow())[(2) as usize]).unwrap()(_arg0, _arg1) + }) == 30) + ); + assert!(!(((*ops.borrow())[(0) as usize]).is_none())); + assert!(((*ops.borrow())[(0) as usize] == Some(add_0 as _))); + assert!(((*ops.borrow())[(0) as usize] != Some(sub_1 as _))); + 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..55471851 --- /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: Option)>, x: Ptr) { + let cb: Value)>> = Rc::new(RefCell::new(cb)); + let x: Value> = Rc::new(RefCell::new(x)); + if !(*cb.borrow()).is_none() { + ({ + let _arg0: Ptr = (*x.borrow()).clone(); + (*cb.borrow()).unwrap()(_arg0) + }); + } +} +pub fn main() { + std::process::exit(main_0()); +} +fn main_0() -> i32 { + let a: Value = Rc::new(RefCell::new(5)); + ({ + let _cb: Option)> = Some(double_it_0 as _); + 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: Option)> = None; + let _x: Ptr = (b.as_pointer()); + maybe_call_1(_cb, _x) + }); + assert!(((*b.borrow()) == 5)); + let fn_: Value)>> = Rc::new(RefCell::new(None)); + if !!(*fn_.borrow()).is_none() { + (*fn_.borrow_mut()) = (Some(double_it_0 as _)).clone(); + } + let c: Value = Rc::new(RefCell::new(3)); + if !(*fn_.borrow()).is_none() { + ({ + let _arg0: Ptr = (c.as_pointer()); + (*fn_.borrow()).unwrap()(_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..cb0f77d9 --- /dev/null +++ b/tests/unit/out/refcount/fn_ptr_cast.rs @@ -0,0 +1,97 @@ +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(Some(double_it_0 as _))); + assert!( + (({ + let _arg0: i32 = 5; + (*fn_.borrow()).unwrap()(_arg0) + }) == 10) + ); + let gfn: Value> = Rc::new(RefCell::new( + ((*fn_.borrow()).to_strong().as_pointer() as Value>).clone(), + )); + assert!(!((*gfn.borrow()).is_none())); + let fn2: Value i32>> = Rc::new(RefCell::new( + ((*gfn.borrow()).to_strong().as_pointer() as Value i32>>).clone(), + )); + assert!( + (({ + let _arg0: i32 = 5; + (*fn2.borrow()).unwrap()(_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(Some(double_it_0 as _))); + let fn2: Value i32>> = Rc::new(RefCell::new( + (((*fn_.borrow()).to_strong().as_pointer() as Value>) + .to_strong() + .as_pointer() as Value i32>>) + .clone(), + )); + assert!( + (({ + let _arg0: i32 = 5; + (*fn2.borrow()).unwrap()(_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()) = + (Some(double_it_0 as _).to_strong().as_pointer() as AnyPtr); + let fn_: Value i32>> = Rc::new(RefCell::new( + ((*(*cmd.borrow()).data.borrow()) + .cast::() + .expect("ub:wrong type")) + .clone(), + )); + assert!( + (({ + let _arg0: i32 = 5; + (*fn_.borrow()).unwrap()(_arg0) + }) == 10) + ); +} +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() }); + 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..6cc940a3 --- /dev/null +++ b/tests/unit/out/refcount/fn_ptr_conditional.rs @@ -0,0 +1,97 @@ +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) -> Option i32> { + let mode: Value = Rc::new(RefCell::new(mode)); + return if ((*mode.borrow()) > 0) { + Some(inc_0 as _) + } else { + if ((*mode.borrow()) < 0) { + Some(dec_1 as _) + } else { + Some(identity_2 as _) + } + }; +} +pub fn apply_4(fn_: Option 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_none() { + (*fn_.borrow()).clone() + } else { + Some(identity_2 as _) + })); + return ({ + let _arg0: i32 = (*x.borrow()); + (*actual.borrow()).unwrap()(_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) + }) + .unwrap()(_arg0) + }) == 11) + ); + assert!( + (({ + let _arg0: i32 = 10; + ({ + let _mode: i32 = -1_i32; + pick_3(_mode) + }) + .unwrap()(_arg0) + }) == 9) + ); + assert!( + (({ + let _arg0: i32 = 10; + ({ + let _mode: i32 = 0; + pick_3(_mode) + }) + .unwrap()(_arg0) + }) == 10) + ); + assert!( + (({ + let _fn: Option i32> = Some(inc_0 as _); + let _x: i32 = 5; + apply_4(_fn, _x) + }) == 6) + ); + assert!( + (({ + let _fn: Option i32> = None; + 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..68af5d97 --- /dev/null +++ b/tests/unit/out/refcount/fn_ptr_default_arg.rs @@ -0,0 +1,62 @@ +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(None))); + if !(*fn_.borrow()).is_none() { + return ({ + let _arg0: i32 = (*x.borrow()); + (*fn_.borrow()).unwrap()(_arg0) + }); + } + return (*x.borrow()); +} +pub fn main() { + std::process::exit(main_0()); +} +fn main_0() -> i32 { + assert!( + (({ + let _x: i32 = 5; + let _fn: Option i32> = Default::default(); + apply_1(_x, Some(_fn)) + }) == 5) + ); + assert!( + (({ + let _x: i32 = 5; + let _fn: Option i32> = None; + apply_1(_x, Some(_fn)) + }) == 5) + ); + assert!( + (({ + let _x: i32 = 5; + let _fn: Option i32> = Some(identity_0 as _); + apply_1(_x, Some(_fn)) + }) == 5) + ); + let negate: Value i32>> = Rc::new(RefCell::new(Some(|x: i32| { + let x: Value = Rc::new(RefCell::new(x)); + return -(*x.borrow()); + }))); + assert!( + (({ + let _x: i32 = 5; + let _fn: Option 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..c7bf0561 --- /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(None)); +); +pub fn set_op_2(fn_: Option 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_none() { + return ({ + let _arg0: i32 = (*x.borrow()); + (*g_op.with(Value::clone).borrow()).unwrap()(_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: Option i32> = Some(double_it_0 as _); + set_op_2(_fn) + }); + assert!(!((*g_op.with(Value::clone).borrow()).is_none())); + assert!({ + let _lhs = (*g_op.with(Value::clone).borrow()).clone(); + _lhs == Some(double_it_0 as _) + }); + assert!( + (({ + let _x: i32 = 5; + call_op_3(_x) + }) == 10) + ); + ({ + let _fn: Option i32> = Some(triple_it_1 as _); + set_op_2(_fn) + }); + assert!({ + let _lhs = (*g_op.with(Value::clone).borrow()).clone(); + _lhs == Some(triple_it_1 as _) + }); + assert!( + (({ + let _x: i32 = 5; + call_op_3(_x) + }) == 15) + ); + ({ + let _fn: Option i32> = None; + set_op_2(_fn) + }); + assert!((*g_op.with(Value::clone).borrow()).is_none()); + 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..98f6def1 --- /dev/null +++ b/tests/unit/out/refcount/fn_ptr_reassign.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 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(Some(add_0 as _))); + assert!( + (({ + let _arg0: i32 = 3; + let _arg1: i32 = 4; + (*fn_.borrow()).unwrap()(_arg0, _arg1) + }) == 7) + ); + (*fn_.borrow_mut()) = Some(sub_1 as _); + assert!( + (({ + let _arg0: i32 = 10; + let _arg1: i32 = 3; + (*fn_.borrow()).unwrap()(_arg0, _arg1) + }) == 7) + ); + (*fn_.borrow_mut()) = Some(mul_2 as _); + assert!( + (({ + let _arg0: i32 = 6; + let _arg1: i32 = 7; + (*fn_.borrow()).unwrap()(_arg0, _arg1) + }) == 42) + ); + (*fn_.borrow_mut()) = None; + assert!((*fn_.borrow()).is_none()); + (*fn_.borrow_mut()) = Some(add_0 as _); + assert!(!((*fn_.borrow()).is_none())); + assert!( + (({ + let _arg0: i32 = 1; + let _arg1: i32 = 1; + (*fn_.borrow()).unwrap()(_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..9dead5c8 --- /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) -> Option i32> { + let choose_inc: Value = Rc::new(RefCell::new(choose_inc)); + if ((*choose_inc.borrow()) != 0) { + return Some(inc_0 as _); + } + return Some(dec_1 as _); +} +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_none())); + assert!({ + let _lhs = (*f.borrow()).clone(); + _lhs == Some(inc_0 as _) + }); + assert!( + (({ + let _arg0: i32 = 10; + (*f.borrow()).unwrap()(_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 == Some(dec_1 as _) + }); + assert!( + (({ + let _arg0: i32 = 10; + (*g.borrow()).unwrap()(_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..433b3f47 --- /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(None)), + } + } +} +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(Some(double_it_0 as _))), + })); + let h2: Value = Rc::new(RefCell::new(Handler { + tag: Rc::new(RefCell::new(2)), + cb: Rc::new(RefCell::new(Some(negate_1 as _))), + })); + assert!(!((*(*h1.borrow()).cb.borrow()).is_none())); + assert!( + (({ + let _arg0: i32 = 5; + (*(*h1.borrow()).cb.borrow()).unwrap()(_arg0) + }) == 10) + ); + assert!( + (({ + let _arg0: i32 = 7; + (*(*h2.borrow()).cb.borrow()).unwrap()(_arg0) + }) == -7_i32) + ); + (*(*h1.borrow()).cb.borrow_mut()) = Some(negate_1 as _); + assert!( + (({ + let _arg0: i32 = 3; + (*(*h1.borrow()).cb.borrow()).unwrap()(_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..9c2011b0 --- /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_: Option)>, 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()).unwrap()(_arg0) + }); +} +pub fn main() { + std::process::exit(main_0()); +} +fn main_0() -> i32 { + let a: Value = Rc::new(RefCell::new(42)); + ({ + let _fn: Option)> = Some(negate_0 as _); + let _x: Ptr = (a.as_pointer()); + run_2(_fn, _x) + }); + assert!(((*a.borrow()) == -42_i32)); + ({ + let _fn: Option)> = Some(zero_out_1 as _); + let _x: Ptr = (a.as_pointer()); + run_2(_fn, _x) + }); + assert!(((*a.borrow()) == 0)); + let fn_: Value)>> = Rc::new(RefCell::new(Some(negate_0 as _))); + assert!(!((*fn_.borrow()).is_none())); + let b: Value = Rc::new(RefCell::new(10)); + ({ + let _arg0: Ptr = (b.as_pointer()); + (*fn_.borrow()).unwrap()(_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..a9128c94 --- /dev/null +++ b/tests/unit/out/refcount/fn_ptr_vtable.rs @@ -0,0 +1,84 @@ +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(None)), + get: Rc::new(RefCell::new(None)), + destroy: Rc::new(RefCell::new(None)), + } + } +} +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((Some(int_create_0 as _)).clone())), + get: Rc::new(RefCell::new(Some(int_get_1 as _))), + destroy: Rc::new(RefCell::new(Some(int_destroy_2 as _))), + })); + assert!(!((*(*vt.borrow()).create.borrow()).is_none())); + assert!(!((*(*vt.borrow()).get.borrow()).is_none())); + assert!(!((*(*vt.borrow()).destroy.borrow()).is_none())); + let obj: Value = Rc::new(RefCell::new( + ({ + let _arg0: i32 = 42; + (*(*vt.borrow()).create.borrow()).unwrap()(_arg0) + }), + )); + assert!( + (({ + let _arg0: AnyPtr = (*obj.borrow()).clone(); + (*(*vt.borrow()).get.borrow()).unwrap()(_arg0) + }) == 42) + ); + ({ + let _arg0: AnyPtr = (*obj.borrow()).clone(); + (*(*vt.borrow()).destroy.borrow()).unwrap()(_arg0) + }); + assert!(((*storage.with(Value::clone).borrow()) == 0)); + (*(*vt.borrow()).get.borrow_mut()) = None; + assert!((*(*vt.borrow()).get.borrow()).is_none()); + return 0; +} diff --git a/tests/unit/out/refcount/lambda_capture_local.rs b/tests/unit/out/refcount/lambda_capture_local.rs new file mode 100644 index 00000000..5b4a5250 --- /dev/null +++ b/tests/unit/out/refcount/lambda_capture_local.rs @@ -0,0 +1,74 @@ +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 base: Value = Rc::new(RefCell::new(10)); + let factor: Value = Rc::new(RefCell::new(3)); + let add_base: Value i32>> = Rc::new(RefCell::new(Rc::new( + (|x: i32| { + let x: Value = Rc::new(RefCell::new(x)); + return ((*x.borrow()) + (*base.borrow())); + }), + ))); + let scale: Value i32>> = Rc::new(RefCell::new(Rc::new( + (|x: i32| { + let x: Value = Rc::new(RefCell::new(x)); + return ((*x.borrow()) * (*factor.borrow())); + }), + ))); + assert!( + (({ + let _x: i32 = 5; + (*add_base.borrow())(_x) + }) == 15) + ); + assert!( + (({ + let _x: i32 = 4; + (*scale.borrow())(_x) + }) == 12) + ); + (*base.borrow_mut()) = 100; + assert!( + (({ + let _x: i32 = 5; + (*add_base.borrow())(_x) + }) == 105) + ); + assert!( + (({ + let _x: i32 = 4; + (*scale.borrow())(_x) + }) == 12) + ); + let sum: Value = Rc::new(RefCell::new(0)); + let accumulate: Value> = Rc::new(RefCell::new(Rc::new( + (|x: i32| { + let x: Value = Rc::new(RefCell::new(x)); + (*sum.borrow_mut()) += (*x.borrow()); + }), + ))); + ({ + let _x: i32 = 1; + (*accumulate.borrow())(_x) + }); + ({ + let _x: i32 = 2; + (*accumulate.borrow())(_x) + }); + ({ + let _x: i32 = 3; + (*accumulate.borrow())(_x) + }); + assert!(((*sum.borrow()) == 6)); + return 0; +} 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/refcount/lambda_nocapture.rs b/tests/unit/out/refcount/lambda_nocapture.rs new file mode 100644 index 00000000..6936dc8a --- /dev/null +++ b/tests/unit/out/refcount/lambda_nocapture.rs @@ -0,0 +1,74 @@ +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 call_0(f: Option i32>, a: i32, b: i32) -> i32 { + let f: Value i32>> = Rc::new(RefCell::new(f)); + let a: Value = Rc::new(RefCell::new(a)); + let b: Value = Rc::new(RefCell::new(b)); + return ({ + let _arg0: i32 = (*a.borrow()); + let _arg1: i32 = (*b.borrow()); + (*f.borrow()).unwrap()(_arg0, _arg1) + }); +} +pub fn main() { + std::process::exit(main_0()); +} +fn main_0() -> i32 { + let add: Value i32>> = Rc::new(RefCell::new(Some(|a: i32, b: i32| { + let a: Value = Rc::new(RefCell::new(a)); + let b: Value = Rc::new(RefCell::new(b)); + return ((*a.borrow()) + (*b.borrow())); + }))); + let sub: Value i32>> = Rc::new(RefCell::new(Some(|a: i32, b: i32| { + let a: Value = Rc::new(RefCell::new(a)); + let b: Value = Rc::new(RefCell::new(b)); + return ((*a.borrow()) - (*b.borrow())); + }))); + assert!(!((*add.borrow()).is_none())); + assert!({ + let _lhs = (*add.borrow()).clone(); + _lhs != (*sub.borrow()).clone() + }); + assert!( + (({ + let _arg0: i32 = 2; + let _arg1: i32 = 3; + (*add.borrow()).unwrap()(_arg0, _arg1) + }) == 5) + ); + assert!( + (({ + let _arg0: i32 = 10; + let _arg1: i32 = 4; + (*sub.borrow()).unwrap()(_arg0, _arg1) + }) == 6) + ); + assert!( + (({ + let _f: Option i32> = (*add.borrow()).clone(); + let _a: i32 = 7; + let _b: i32 = 8; + call_0(_f, _a, _b) + }) == 15) + ); + assert!( + (({ + let _f: Option i32> = Some(|a: i32, b: i32| { + let a: Value = Rc::new(RefCell::new(a)); + let b: Value = Rc::new(RefCell::new(b)); + return ((*a.borrow()) * (*b.borrow())); + }); + let _a: i32 = 6; + let _b: i32 = 7; + call_0(_f, _a, _b) + }) == 42) + ); + 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..cc9d4ae3 --- /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 as _)))); + fn_ = Some(my_foo_0 as _); + assert!(!((fn_).is_none())); + assert!(((fn_) == (Some(my_foo_0 as _)))); + 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..f69829eb --- /dev/null +++ b/tests/unit/out/unsafe/fn_ptr_array.rs @@ -0,0 +1,52 @@ +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 as _), Some(sub_1 as _), Some(mul_2 as _)]; + 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 as _)))); + assert!(((ops[(0) as usize]) != (Some(sub_1 as _)))); + 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..ce851f5b --- /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 as _); + 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 as _); + } + 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..caf6a62e --- /dev/null +++ b/tests/unit/out/unsafe/fn_ptr_cast.rs @@ -0,0 +1,69 @@ +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 as _); + assert!( + ((unsafe { + let _arg0: i32 = 5; + (fn_).unwrap()(_arg0) + }) == (10)) + ); + let mut gfn: Option = (fn_ as Option); + assert!(!((gfn).is_none())); + let mut fn2: Option i32> = (gfn as Option i32>); + 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 as _); + let mut fn2: Option i32> = + ((fn_ as Option) as Option i32>); + 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 = (Some(double_it_0 as _) as *mut ::libc::c_void); + let mut fn_: Option i32> = (cmd.data as Option i32>); + assert!( + ((unsafe { + let _arg0: i32 = 5; + (fn_).unwrap()(_arg0) + }) == (10)) + ); +} +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() }); + 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..adfad96b --- /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 as _) + } else { + if ((mode) < (0)) { + Some(dec_1 as _) + } else { + Some(identity_2 as _) + } + }; +} +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 as _) + }; + 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 as _); + 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..5decf93f --- /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 as _); + 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..d4ce4c20 --- /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 as _); + set_op_2(_fn) + }); + assert!(!((g_op).is_none())); + assert!(((g_op) == (Some(double_it_0 as _)))); + assert!( + ((unsafe { + let _x: i32 = 5; + call_op_3(_x) + }) == (10)) + ); + (unsafe { + let _fn: Option i32> = Some(triple_it_1 as _); + set_op_2(_fn) + }); + assert!(((g_op) == (Some(triple_it_1 as _)))); + 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_mapped.rs b/tests/unit/out/unsafe/fn_ptr_mapped.rs new file mode 100644 index 00000000..e19a1f5d --- /dev/null +++ b/tests/unit/out/unsafe/fn_ptr_mapped.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 unsafe fn foo_0( + mut fn_: Rc u64>, + mut p: *mut ::libc::c_void, + mut size: u64, + mut nmemb: u64, + mut f: *mut ::std::fs::File, +) -> u64 { + return (unsafe { + let _arg0: *mut ::libc::c_void = p; + let _arg1: u64 = size; + let _arg2: u64 = nmemb; + let _arg3: *mut ::std::fs::File = f; + fn_(_arg0, _arg1, _arg2, _arg3) + }); +} +pub fn main() { + unsafe { + std::process::exit(main_0() as i32); + } +} +unsafe fn main_0() -> i32 { + let mut fn_: Rc u64> = + (Rc::new(|a0, a1, a2, a3| unsafe { fread_1(a0, a1, a2, a3) }) + as Rc u64>); + (unsafe { + let _fn: Rc u64> = + Rc::new(|a0, a1, a2, a3| unsafe { fread_1(a0, a1, a2, a3) }); + let _p: *mut ::libc::c_void = Default::default(); + let _size: u64 = 0_u64; + let _nmemb: u64 = 0_u64; + let _f: *mut ::std::fs::File = Default::default(); + foo_0(_fn, _p, _size, _nmemb, _f) + }); + assert!(((fn_) != (Default::default()))); + 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..19ade021 --- /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 as _); + assert!( + ((unsafe { + let _arg0: i32 = 3; + let _arg1: i32 = 4; + (fn_).unwrap()(_arg0, _arg1) + }) == (7)) + ); + fn_ = Some(sub_1 as _); + assert!( + ((unsafe { + let _arg0: i32 = 10; + let _arg1: i32 = 3; + (fn_).unwrap()(_arg0, _arg1) + }) == (7)) + ); + fn_ = Some(mul_2 as _); + 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 as _); + 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..7e2e9270 --- /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 as _); + } + return Some(dec_1 as _); +} +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 as _)))); + 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 as _)))); + 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..fc5a1070 --- /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 as 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 as _), + }; + let mut h2: Handler = Handler { + tag: 2, + cb: Some(negate_1 as _), + }; + 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 as _); + 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..00817f01 --- /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 as _); + 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 as _); + let _x: *mut i32 = (&mut a as *mut i32); + run_2(_fn, _x) + }); + assert!(((a) == (0))); + let mut fn_: Option = Some(negate_0 as _); + 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..677cceb0 --- /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 as 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 as _), + get: Some(int_get_1 as _), + destroy: Some(int_destroy_2 as _), + }; + 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; +} diff --git a/tests/unit/out/unsafe/lambda_capture_local.rs b/tests/unit/out/unsafe/lambda_capture_local.rs new file mode 100644 index 00000000..9e449d6d --- /dev/null +++ b/tests/unit/out/unsafe/lambda_capture_local.rs @@ -0,0 +1,72 @@ +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 base: i32 = 10; + let mut factor: i32 = 3; + assert!( + ((unsafe { + let _x: i32 = 5; + (|x: i32| { + return ((x) + (base)); + })(_x) + }) == (15)) + ); + assert!( + ((unsafe { + let _x: i32 = 4; + (|x: i32| { + return ((x) * (factor)); + })(_x) + }) == (12)) + ); + base = 100; + assert!( + ((unsafe { + let _x: i32 = 5; + (|x: i32| { + return ((x) + (base)); + })(_x) + }) == (105)) + ); + assert!( + ((unsafe { + let _x: i32 = 4; + (|x: i32| { + return ((x) * (factor)); + })(_x) + }) == (12)) + ); + let mut sum: i32 = 0; + (unsafe { + let _x: i32 = 1; + (|x: i32| { + sum += x; + })(_x) + }); + (unsafe { + let _x: i32 = 2; + (|x: i32| { + sum += x; + })(_x) + }); + (unsafe { + let _x: i32 = 3; + (|x: i32| { + sum += x; + })(_x) + }); + assert!(((sum) == (6))); + 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; +} diff --git a/tests/unit/out/unsafe/lambda_nocapture.rs b/tests/unit/out/unsafe/lambda_nocapture.rs new file mode 100644 index 00000000..d71ab107 --- /dev/null +++ b/tests/unit/out/unsafe/lambda_nocapture.rs @@ -0,0 +1,64 @@ +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 call_0(mut f: Option i32>, mut a: i32, mut b: i32) -> i32 { + return (unsafe { + let _arg0: i32 = a; + let _arg1: i32 = b; + (f).unwrap()(_arg0, _arg1) + }); +} +pub fn main() { + unsafe { + std::process::exit(main_0() as i32); + } +} +unsafe fn main_0() -> i32 { + let mut add: Option i32> = Some(|a: i32, b: i32| { + return ((a) + (b)); + }); + let mut sub: Option i32> = Some(|a: i32, b: i32| { + return ((a) - (b)); + }); + assert!(!((add).is_none())); + assert!(((add) != (sub))); + assert!( + ((unsafe { + let _arg0: i32 = 2; + let _arg1: i32 = 3; + (add).unwrap()(_arg0, _arg1) + }) == (5)) + ); + assert!( + ((unsafe { + let _arg0: i32 = 10; + let _arg1: i32 = 4; + (sub).unwrap()(_arg0, _arg1) + }) == (6)) + ); + assert!( + ((unsafe { + let _f: Option i32> = add; + let _a: i32 = 7; + let _b: i32 = 8; + call_0(_f, _a, _b) + }) == (15)) + ); + assert!( + ((unsafe { + let _f: Option i32> = Some(|a: i32, b: i32| { + return ((a) * (b)); + }); + let _a: i32 = 6; + let _b: i32 = 7; + call_0(_f, _a, _b) + }) == (42)) + ); + return 0; +} From ddd13a8f6c7f7e9241c2558d5a5a670871c9c890 Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Mon, 13 Apr 2026 21:36:20 +0100 Subject: [PATCH 10/56] clang-format --- cpp2rust/converter/converter.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cpp2rust/converter/converter.cpp b/cpp2rust/converter/converter.cpp index 747a002f..8b117c74 100644 --- a/cpp2rust/converter/converter.cpp +++ b/cpp2rust/converter/converter.cpp @@ -1397,7 +1397,8 @@ void Converter::ConvertGenericCallExpr(clang::CallExpr *expr) { StrCat(token::kOpenParen); StrCat(keyword_unsafe_); StrCat(token::kOpenCurlyBracket); - const auto *function = expr->getCalleeDecl() ? expr->getCalleeDecl()->getAsFunction() : nullptr; + const auto *function = + expr->getCalleeDecl() ? expr->getCalleeDecl()->getAsFunction() : nullptr; const clang::FunctionProtoType *proto = nullptr; if (!function) { From 4d93cd4bbac5948be8d8a83cd228f0e644e60f40 Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Mon, 13 Apr 2026 21:45:45 +0100 Subject: [PATCH 11/56] Update 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/ub/out/unsafe/ub10.rs | 8 ++------ tests/ub/out/unsafe/ub14.rs | 8 ++------ tests/ub/out/unsafe/ub15.rs | 8 ++------ tests/ub/out/unsafe/ub16.rs | 8 ++------ tests/ub/out/unsafe/ub20.rs | 8 ++------ tests/ub/out/unsafe/ub9.rs | 8 ++------ 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/06_new_array.rs | 2 +- tests/unit/out/unsafe/clone_vs_move.rs | 4 ++-- tests/unit/out/unsafe/complex_function.rs | 8 ++++---- tests/unit/out/unsafe/default.rs | 2 +- tests/unit/out/unsafe/init.rs | 2 +- tests/unit/out/unsafe/init_list.rs | 4 ++-- tests/unit/out/unsafe/memset.rs | 8 ++------ tests/unit/out/unsafe/new_alloc_array.rs | 8 ++------ tests/unit/out/unsafe/new_array.rs | 8 ++------ tests/unit/out/unsafe/new_array_var_size.rs | 10 +++------- tests/unit/out/unsafe/no_direct_callee.rs | 6 +++--- tests/unit/out/unsafe/pointer_call_offset.rs | 8 ++------ tests/unit/out/unsafe/prvalue-as-lvalue.rs | 2 +- tests/unit/out/unsafe/random.rs | 6 +++--- tests/unit/out/unsafe/ref_calls.rs | 2 +- .../out/unsafe/reinterpret_cast_large_array.rs | 8 ++------ .../unit/out/unsafe/reinterpret_cast_new_array.rs | 2 +- tests/unit/out/unsafe/static_local.rs | 2 +- tests/unit/out/unsafe/stdcopy.rs | 2 +- tests/unit/out/unsafe/swap_extended.rs | 2 +- tests/unit/out/unsafe/unique_ptr.rs | 8 ++------ tests/unit/out/unsafe/vector2.rs | 2 +- tests/unit/static_local.cpp | 1 - 38 files changed, 70 insertions(+), 123 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/ub/out/unsafe/ub10.rs b/tests/ub/out/unsafe/ub10.rs index 14ba0eaf..e60c673d 100644 --- a/tests/ub/out/unsafe/ub10.rs +++ b/tests/ub/out/unsafe/ub10.rs @@ -13,12 +13,8 @@ pub fn main() { } } unsafe fn main_0() -> i32 { - let mut arr: *mut i32 = Box::leak( - (0..10_u64) - .map(|_| ::default()) - .collect::>(), - ) - .as_mut_ptr(); + let mut arr: *mut i32 = + Box::leak((0..10_u64).map(|_| 0 as i32).collect::>()).as_mut_ptr(); let mut ptr: *mut i32 = arr.offset((1) as isize); let mut out: i32 = (*ptr); diff --git a/tests/ub/out/unsafe/ub14.rs b/tests/ub/out/unsafe/ub14.rs index 491de599..7a213611 100644 --- a/tests/ub/out/unsafe/ub14.rs +++ b/tests/ub/out/unsafe/ub14.rs @@ -13,12 +13,8 @@ pub fn main() { } } unsafe fn main_0() -> i32 { - let mut arr1: *mut i32 = Box::leak( - (0..100_u64) - .map(|_| ::default()) - .collect::>(), - ) - .as_mut_ptr(); + let mut arr1: *mut i32 = + Box::leak((0..100_u64).map(|_| 0 as i32).collect::>()).as_mut_ptr(); (*arr1.offset((100) as isize)) = 1; ::std::mem::drop(Box::from_raw(::std::slice::from_raw_parts_mut( diff --git a/tests/ub/out/unsafe/ub15.rs b/tests/ub/out/unsafe/ub15.rs index dc08aa16..b7ed8446 100644 --- a/tests/ub/out/unsafe/ub15.rs +++ b/tests/ub/out/unsafe/ub15.rs @@ -13,12 +13,8 @@ pub fn main() { } } unsafe fn main_0() -> i32 { - let mut arr: *mut i32 = Box::leak( - (0..15_u64) - .map(|_| ::default()) - .collect::>(), - ) - .as_mut_ptr(); + let mut arr: *mut i32 = + Box::leak((0..15_u64).map(|_| 0 as i32).collect::>()).as_mut_ptr(); let mut ptr: *mut i32 = arr.offset((15) as isize); let mut out: i32 = (*ptr); diff --git a/tests/ub/out/unsafe/ub16.rs b/tests/ub/out/unsafe/ub16.rs index a53d8844..b558d411 100644 --- a/tests/ub/out/unsafe/ub16.rs +++ b/tests/ub/out/unsafe/ub16.rs @@ -16,12 +16,8 @@ pub fn main() { } } unsafe fn main_0() -> i32 { - let mut p1: *mut i32 = Box::leak( - (0..10_u64) - .map(|_| ::default()) - .collect::>(), - ) - .as_mut_ptr(); + let mut p1: *mut i32 = + Box::leak((0..10_u64).map(|_| 0 as i32).collect::>()).as_mut_ptr(); let mut out: i32 = (*(unsafe { let _a: *mut i32 = (&mut (*p1.offset((1) as isize)) as *mut i32); foo_0(_a) diff --git a/tests/ub/out/unsafe/ub20.rs b/tests/ub/out/unsafe/ub20.rs index 8517a699..b57376fa 100644 --- a/tests/ub/out/unsafe/ub20.rs +++ b/tests/ub/out/unsafe/ub20.rs @@ -16,12 +16,8 @@ pub fn main() { } } unsafe fn main_0() -> i32 { - let mut x: *mut i32 = Box::leak( - (0..10_u64) - .map(|_| ::default()) - .collect::>(), - ) - .as_mut_ptr(); + let mut x: *mut i32 = + Box::leak((0..10_u64).map(|_| 0 as i32).collect::>()).as_mut_ptr(); (unsafe { let _single: *mut i32 = x; foo_0(_single) diff --git a/tests/ub/out/unsafe/ub9.rs b/tests/ub/out/unsafe/ub9.rs index f2136e71..f8206b71 100644 --- a/tests/ub/out/unsafe/ub9.rs +++ b/tests/ub/out/unsafe/ub9.rs @@ -13,12 +13,8 @@ pub fn main() { } } unsafe fn main_0() -> i32 { - let mut arr: *mut i32 = Box::leak( - (0..10_u64) - .map(|_| ::default()) - .collect::>(), - ) - .as_mut_ptr(); + let mut arr: *mut i32 = + Box::leak((0..10_u64).map(|_| 0 as i32).collect::>()).as_mut_ptr(); let mut out: i32 = (*arr.offset((10) as isize)); ::std::mem::drop(Box::from_raw(::std::slice::from_raw_parts_mut( 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..33d09c6e 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_: Option bool>) -> i32 { + let fn_: Value bool>> = Rc::new(RefCell::new(fn_)); + if !({ (*fn_.borrow()).unwrap()() }) { 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: Option bool> = Some(test1_0 as _); 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/06_new_array.rs b/tests/unit/out/unsafe/06_new_array.rs index cdb76f92..fdc11d3e 100644 --- a/tests/unit/out/unsafe/06_new_array.rs +++ b/tests/unit/out/unsafe/06_new_array.rs @@ -14,7 +14,7 @@ pub fn main() { } unsafe fn main_0() -> i32 { let mut e: *mut i32 = - Box::leak((0..2_u64).map(|_| ::default()).collect::>()).as_mut_ptr(); + Box::leak((0..2_u64).map(|_| 0 as i32).collect::>()).as_mut_ptr(); (*e.offset((0) as isize)) = 6; (*e.offset((1) as isize)) = 7; diff --git a/tests/unit/out/unsafe/clone_vs_move.rs b/tests/unit/out/unsafe/clone_vs_move.rs index c19f9ec1..065ba07c 100644 --- a/tests/unit/out/unsafe/clone_vs_move.rs +++ b/tests/unit/out/unsafe/clone_vs_move.rs @@ -22,10 +22,10 @@ pub struct Foo { impl Default for Foo { fn default() -> Self { Foo { - x: ::default(), + x: 0 as i32, y: <*mut i32>::default(), z: Default::default(), - a: [::default(); 3], + a: [0 as i32; 3], bar: ::default(), } } 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/default.rs b/tests/unit/out/unsafe/default.rs index 68683773..990bf336 100644 --- a/tests/unit/out/unsafe/default.rs +++ b/tests/unit/out/unsafe/default.rs @@ -22,7 +22,7 @@ impl Default for Pointers { x2: Default::default(), x3: [Default::default(); 5], x4: [Default::default(); 10], - x5: ::default(), + x5: 0 as i32, } } } diff --git a/tests/unit/out/unsafe/init.rs b/tests/unit/out/unsafe/init.rs index 894070b2..e7675385 100644 --- a/tests/unit/out/unsafe/init.rs +++ b/tests/unit/out/unsafe/init.rs @@ -20,7 +20,7 @@ pub fn main() { } } unsafe fn main_0() -> i32 { - let mut x: i32 = ::default(); + let mut x: i32 = 0 as i32; let mut p: *mut i32 = Default::default(); let g: *mut i32 = &mut x as *mut i32; let mut q: *mut i32 = (&mut x as *mut i32); diff --git a/tests/unit/out/unsafe/init_list.rs b/tests/unit/out/unsafe/init_list.rs index d8e2166f..1d5e27e2 100644 --- a/tests/unit/out/unsafe/init_list.rs +++ b/tests/unit/out/unsafe/init_list.rs @@ -15,9 +15,9 @@ pub fn main() { } unsafe fn main_0() -> i32 { let mut i1: i32 = 3; - let mut i2: i32 = ::default(); + let mut i2: i32 = 0 as i32; let mut carr1: [i32; 2] = [1, 2]; - let mut carr2: [i32; 3] = [1, ::default(), ::default()]; + let mut carr2: [i32; 3] = [1, 0 as i32, 0 as i32]; let mut arr: Vec = vec![1, 2, 3]; let mut vec_: Vec = vec![1, 2, 3]; (unsafe { diff --git a/tests/unit/out/unsafe/memset.rs b/tests/unit/out/unsafe/memset.rs index ec8fd428..26eb5e6c 100644 --- a/tests/unit/out/unsafe/memset.rs +++ b/tests/unit/out/unsafe/memset.rs @@ -14,12 +14,8 @@ pub fn main() { } unsafe fn main_0() -> i32 { let N: i32 = 3; - let mut arr: *mut i32 = Box::leak( - (0..(N as u64)) - .map(|_| ::default()) - .collect::>(), - ) - .as_mut_ptr(); + let mut arr: *mut i32 = + Box::leak((0..(N as u64)).map(|_| 0 as i32).collect::>()).as_mut_ptr(); { let byte_0 = (arr as *mut i32 as *mut ::libc::c_void) as *mut u8; for offset in 0..(::std::mem::size_of::() as u64 as u64).wrapping_mul((N as u64)) { diff --git a/tests/unit/out/unsafe/new_alloc_array.rs b/tests/unit/out/unsafe/new_alloc_array.rs index 697fe0a1..5a83f057 100644 --- a/tests/unit/out/unsafe/new_alloc_array.rs +++ b/tests/unit/out/unsafe/new_alloc_array.rs @@ -13,12 +13,8 @@ pub fn main() { } } unsafe fn main_0() -> i32 { - let mut array: *mut i32 = Box::leak( - (0..100_u64) - .map(|_| ::default()) - .collect::>(), - ) - .as_mut_ptr(); + let mut array: *mut i32 = + Box::leak((0..100_u64).map(|_| 0 as i32).collect::>()).as_mut_ptr(); { let byte_0 = (array as *mut i32 as *mut ::libc::c_void) as *mut u8; for offset in 0..(::std::mem::size_of::() as u64 as u64).wrapping_mul(100_u64) { diff --git a/tests/unit/out/unsafe/new_array.rs b/tests/unit/out/unsafe/new_array.rs index e2243eea..b73aea86 100644 --- a/tests/unit/out/unsafe/new_array.rs +++ b/tests/unit/out/unsafe/new_array.rs @@ -13,12 +13,8 @@ pub fn main() { } } unsafe fn main_0() -> i32 { - let mut array: *mut i32 = Box::leak( - (0..100_u64) - .map(|_| ::default()) - .collect::>(), - ) - .as_mut_ptr(); + let mut array: *mut i32 = + Box::leak((0..100_u64).map(|_| 0 as i32).collect::>()).as_mut_ptr(); ::std::mem::drop(Box::from_raw(::std::slice::from_raw_parts_mut( array, diff --git a/tests/unit/out/unsafe/new_array_var_size.rs b/tests/unit/out/unsafe/new_array_var_size.rs index cfdf0c19..ff403a2c 100644 --- a/tests/unit/out/unsafe/new_array_var_size.rs +++ b/tests/unit/out/unsafe/new_array_var_size.rs @@ -14,12 +14,8 @@ pub fn main() { } unsafe fn main_0() -> i32 { let mut N: i32 = 5; - let mut A: *mut i32 = Box::leak( - (0..(N as u64)) - .map(|_| ::default()) - .collect::>(), - ) - .as_mut_ptr(); + let mut A: *mut i32 = + Box::leak((0..(N as u64)).map(|_| 0 as i32).collect::>()).as_mut_ptr(); ::std::mem::drop(Box::from_raw(::std::slice::from_raw_parts_mut( A, @@ -28,7 +24,7 @@ unsafe fn main_0() -> i32 { let N2: *mut i32 = &mut N as *mut i32; let mut A2: *mut i32 = Box::leak( (0..((*N2) as u64)) - .map(|_| ::default()) + .map(|_| 0 as i32) .collect::>(), ) .as_mut_ptr(); diff --git a/tests/unit/out/unsafe/no_direct_callee.rs b/tests/unit/out/unsafe/no_direct_callee.rs index 5c962fe7..1136da61 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 as _); test_1(_fn) }); } diff --git a/tests/unit/out/unsafe/pointer_call_offset.rs b/tests/unit/out/unsafe/pointer_call_offset.rs index fdbca43a..8d9472e4 100644 --- a/tests/unit/out/unsafe/pointer_call_offset.rs +++ b/tests/unit/out/unsafe/pointer_call_offset.rs @@ -16,12 +16,8 @@ pub fn main() { } } unsafe fn main_0() -> i32 { - let mut p1: *mut i32 = Box::leak( - (0..10_u64) - .map(|_| ::default()) - .collect::>(), - ) - .as_mut_ptr(); + let mut p1: *mut i32 = + Box::leak((0..10_u64).map(|_| 0 as i32).collect::>()).as_mut_ptr(); let mut i: u32 = 0_u32; 'loop_: while ((i) < (10_u32)) { (*p1.offset((i) as isize)) = (i as i32); 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/random.rs b/tests/unit/out/unsafe/random.rs index 87397236..82133af1 100644 --- a/tests/unit/out/unsafe/random.rs +++ b/tests/unit/out/unsafe/random.rs @@ -40,9 +40,9 @@ impl Pair { impl Default for Pair { fn default() -> Self { Pair { - x: ::default(), - y: ::default(), - a: [::default(); 5], + x: 0 as i32, + y: 0 as i32, + a: [0 as i32; 5], r: <*mut i32>::default(), p: Default::default(), pair: Default::default(), 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; diff --git a/tests/unit/out/unsafe/reinterpret_cast_large_array.rs b/tests/unit/out/unsafe/reinterpret_cast_large_array.rs index 6d85d331..5344c2be 100644 --- a/tests/unit/out/unsafe/reinterpret_cast_large_array.rs +++ b/tests/unit/out/unsafe/reinterpret_cast_large_array.rs @@ -14,12 +14,8 @@ pub fn main() { } unsafe fn main_0() -> i32 { let N: i32 = 10000; - let mut arr: *mut u32 = Box::leak( - (0..(N as u64)) - .map(|_| ::default()) - .collect::>(), - ) - .as_mut_ptr(); + let mut arr: *mut u32 = + Box::leak((0..(N as u64)).map(|_| 0 as u32).collect::>()).as_mut_ptr(); let mut i: i32 = 0; 'loop_: while ((i) < (N)) { (*arr.offset((i) as isize)) = 0_u32; diff --git a/tests/unit/out/unsafe/reinterpret_cast_new_array.rs b/tests/unit/out/unsafe/reinterpret_cast_new_array.rs index 87840ed1..f4e567b8 100644 --- a/tests/unit/out/unsafe/reinterpret_cast_new_array.rs +++ b/tests/unit/out/unsafe/reinterpret_cast_new_array.rs @@ -14,7 +14,7 @@ pub fn main() { } unsafe fn main_0() -> i32 { let mut arr: *mut u32 = - Box::leak((0..2_u64).map(|_| ::default()).collect::>()).as_mut_ptr(); + Box::leak((0..2_u64).map(|_| 0 as u32).collect::>()).as_mut_ptr(); (*arr.offset((0) as isize)) = 67305985_u32; (*arr.offset((1) as isize)) = 134678021_u32; let mut bytes: *mut u8 = (arr as *mut u8); diff --git a/tests/unit/out/unsafe/static_local.rs b/tests/unit/out/unsafe/static_local.rs index c6fe9391..042a7008 100644 --- a/tests/unit/out/unsafe/static_local.rs +++ b/tests/unit/out/unsafe/static_local.rs @@ -8,7 +8,7 @@ use std::io::{Read, Write}; use std::os::fd::{AsFd, FromRawFd, IntoRawFd}; use std::rc::Rc; pub unsafe fn foo_0() -> i32 { - static kX1: i32 = 1;; + static mut kX1: i32 = 1;; static kX2: i32 = 2;; kX1 += 1; return ((kX1) + (kX2)); diff --git a/tests/unit/out/unsafe/stdcopy.rs b/tests/unit/out/unsafe/stdcopy.rs index 4c813830..706e5fb6 100644 --- a/tests/unit/out/unsafe/stdcopy.rs +++ b/tests/unit/out/unsafe/stdcopy.rs @@ -14,7 +14,7 @@ pub fn main() { } unsafe fn main_0() -> i32 { let mut input: [i32; 3] = [1, 2, 3]; - let mut output: [i32; 3] = [::default(); 3]; + let mut output: [i32; 3] = [0 as i32; 3]; { let mut outptr = output.as_mut_ptr().clone(); let mut curr = input.as_mut_ptr().clone(); diff --git a/tests/unit/out/unsafe/swap_extended.rs b/tests/unit/out/unsafe/swap_extended.rs index adc9da12..8aae96cc 100644 --- a/tests/unit/out/unsafe/swap_extended.rs +++ b/tests/unit/out/unsafe/swap_extended.rs @@ -94,7 +94,7 @@ unsafe fn main_0() -> i32 { (*h), ); ::std::mem::drop(Box::from_raw(h)); - let mut i: *mut i32 = Box::leak(Box::new([7, 8, ::default()])).as_mut_ptr(); + let mut i: *mut i32 = Box::leak(Box::new([7, 8, 0 as i32])).as_mut_ptr(); write!( std::fs::File::from_raw_fd( std::io::stdout() diff --git a/tests/unit/out/unsafe/unique_ptr.rs b/tests/unit/out/unsafe/unique_ptr.rs index 1db51390..a94bb752 100644 --- a/tests/unit/out/unsafe/unique_ptr.rs +++ b/tests/unit/out/unsafe/unique_ptr.rs @@ -72,9 +72,7 @@ pub unsafe fn Consume_1(mut safe_ptr: Option>) -> i32 { pub unsafe fn RndStuff_2() { let mut x1: Option> = None; let mut x2: Option> = Some(Box::from_raw(Box::leak( - (0..100_u64) - .map(|_| ::default()) - .collect::>(), + (0..100_u64).map(|_| 0 as i32).collect::>(), ))); let mut i: i32 = 0; 'loop_: while ((i) < (100)) { @@ -82,9 +80,7 @@ pub unsafe fn RndStuff_2() { i.prefix_inc(); } x2 = Some(Box::from_raw(Box::leak( - (0..200_u64) - .map(|_| ::default()) - .collect::>(), + (0..200_u64).map(|_| 0 as i32).collect::>(), ))); let mut i: i32 = 0; 'loop_: while ((i) < (200)) { diff --git a/tests/unit/out/unsafe/vector2.rs b/tests/unit/out/unsafe/vector2.rs index a62467e9..640b7624 100644 --- a/tests/unit/out/unsafe/vector2.rs +++ b/tests/unit/out/unsafe/vector2.rs @@ -9,7 +9,7 @@ use std::os::fd::{AsFd, FromRawFd, IntoRawFd}; use std::rc::Rc; pub unsafe fn fn_0(v: *mut Vec, mut v3: Vec) { (*v).push(20); - let mut x: i32 = ::default(); + let mut x: i32 = 0 as i32; let mut v2: Vec = Vec::new(); let mut v4: *mut Vec = (&mut v3 as *mut Vec); v2.push(0); diff --git a/tests/unit/static_local.cpp b/tests/unit/static_local.cpp index 4378e997..acb9117d 100644 --- a/tests/unit/static_local.cpp +++ b/tests/unit/static_local.cpp @@ -1,7 +1,6 @@ // Copyright (c) 2022-present INESC-ID. // Distributed under the MIT license that can be found in the LICENSE file. -// no-compile: unsafe int foo() { static int kX1 = 1; static const int kX2 = 2; From 691a4355481d3b90c0ccfb73ce493e9337d5a807 Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Mon, 13 Apr 2026 21:47:18 +0100 Subject: [PATCH 12/56] clang-format --- tests/unit/fn_ptr_conditional.cpp | 4 +--- tests/unit/fn_ptr_void_return.cpp | 4 +--- tests/unit/lambda_capture_pass.cpp | 3 +-- 3 files changed, 3 insertions(+), 8 deletions(-) diff --git a/tests/unit/fn_ptr_conditional.cpp b/tests/unit/fn_ptr_conditional.cpp index ad39614e..d1b4c832 100644 --- a/tests/unit/fn_ptr_conditional.cpp +++ b/tests/unit/fn_ptr_conditional.cpp @@ -6,9 +6,7 @@ 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; -} +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; diff --git a/tests/unit/fn_ptr_void_return.cpp b/tests/unit/fn_ptr_void_return.cpp index ebf7b39f..3ae221a9 100644 --- a/tests/unit/fn_ptr_void_return.cpp +++ b/tests/unit/fn_ptr_void_return.cpp @@ -5,9 +5,7 @@ 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); -} +void run(action_t fn, int *x) { fn(x); } int main() { int a = 42; diff --git a/tests/unit/lambda_capture_pass.cpp b/tests/unit/lambda_capture_pass.cpp index 1a7637f4..439b128c 100644 --- a/tests/unit/lambda_capture_pass.cpp +++ b/tests/unit/lambda_capture_pass.cpp @@ -1,7 +1,6 @@ #include -template -int apply(F fn, int x) { return fn(x); } +template int apply(F fn, int x) { return fn(x); } int main() { int base = 10; From f0267287286be389560790bf18fe2935c260fbad Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Mon, 13 Apr 2026 22:16:44 +0100 Subject: [PATCH 13/56] Translate fn ptr cast using mem::transmute --- cpp2rust/converter/converter.cpp | 11 +++++++++++ tests/unit/out/unsafe/fn_ptr_cast.rs | 17 ++++++++++++----- 2 files changed, 23 insertions(+), 5 deletions(-) diff --git a/cpp2rust/converter/converter.cpp b/cpp2rust/converter/converter.cpp index 8b117c74..64ccd1a1 100644 --- a/cpp2rust/converter/converter.cpp +++ b/cpp2rust/converter/converter.cpp @@ -1737,6 +1737,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); diff --git a/tests/unit/out/unsafe/fn_ptr_cast.rs b/tests/unit/out/unsafe/fn_ptr_cast.rs index caf6a62e..c4eee5ea 100644 --- a/tests/unit/out/unsafe/fn_ptr_cast.rs +++ b/tests/unit/out/unsafe/fn_ptr_cast.rs @@ -18,9 +18,11 @@ pub unsafe fn test_roundtrip_1() { (fn_).unwrap()(_arg0) }) == (10)) ); - let mut gfn: Option = (fn_ as Option); + let mut gfn: Option = + std::mem::transmute:: i32>, Option>(fn_); assert!(!((gfn).is_none())); - let mut fn2: Option i32> = (gfn as Option i32>); + let mut fn2: Option i32> = + std::mem::transmute::, Option i32>>(gfn); assert!( ((unsafe { let _arg0: i32 = 5; @@ -32,7 +34,9 @@ pub unsafe fn test_roundtrip_1() { pub unsafe fn test_double_cast_2() { let mut fn_: Option i32> = Some(double_it_0 as _); let mut fn2: Option i32> = - ((fn_ as Option) as Option i32>); + std::mem::transmute::, Option i32>>( + std::mem::transmute:: i32>, Option>(fn_), + ); assert!( ((unsafe { let _arg0: i32 = 5; @@ -47,8 +51,11 @@ pub struct Command { } pub unsafe fn test_void_ptr_to_fn_3() { let mut cmd: Command = ::default(); - cmd.data = (Some(double_it_0 as _) as *mut ::libc::c_void); - let mut fn_: Option i32> = (cmd.data as Option i32>); + cmd.data = std::mem::transmute:: i32>, *mut ::libc::c_void>(Some( + double_it_0 as _, + )); + let mut fn_: Option i32> = + std::mem::transmute::<*mut ::libc::c_void, Option i32>>(cmd.data); assert!( ((unsafe { let _arg0: i32 = 5; From e320bdbb620530e2b653c24fe31cb9c171173022 Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Tue, 14 Apr 2026 15:32:28 +0100 Subject: [PATCH 14/56] Add support for function pointers in PtrKind Previously function pointers were modeled as Option, but this is problematic for function pointer casts. Option works well in unsafe with std::mem::transmute, however there is no safe way to achieve the same operation in refcount. This is solved using the new Ptr. To allow casting between different function types, use type erased Rc inside the new PtrKind::Fn. Equality of function pointers is achieved through implementing the OriginalAlloc::address method. The C standard allows converting function pointers between incompatible function types. UB is triggered only when the incompatible pointer is called. For this reason the new FnState implements 2 new concepts: 1. casting adaptors (to allow argument casting between ABI compatible types) 2. provenance stack (to allow round-trip function pointer casts) For 1., consider the following cast: int fn_taking_int_ptr(int *p); int (*fn_taking_void_ptr)(void*) = (int (*)(void*))fn_taking_int_ptr; Calling fn_taking_int_ptr with an int* argument works because both int* and void* have the same size. To support this in Rust we need to create an int* -> void* adapter when casting from fn_taking_int_ptr to fn_taking_void_ptr: fn_taking_int_ptr.cast_fn:: i32>(Some( (|a0: AnyPtr| -> i32 { fn_taking_int_ptr(a0.cast::().unwrap()) }) as fn(AnyPtr) -> i32 )) The job of the adapter is to convert from AnyPtr to Ptr. Ptr::cast_fn is a new function that takes as type argument the type of the target function pointer and an optional adaptor. If cast_fn receives None, then there is no valid adaptor from source to target, matching the UB semantics of calling a function through an incompatible function pointer: int add(int a, int b) { return a + b; } void (*wrong)(void) = (void (*)(void))add; wrong() For 2., the provenance stack contains all casts performed on the pointer in the past. Compared to PtrKind::Reinterpreted, PtrKind::Fn has no backing byte storage through OriginalAlloc, so each cast must know its history in order to allow round-trip casts, such as: int (*)(int, int) -> void (*)(void) -> int (*)(int, int) (1) (2) For this specific case, where both (1) and (2) create non-compatible adaptors (because of non-compatible arguments), we cannot recover a call to the original function after (1) is performed. For this to work, save a stack of provenance, and when (2) is perfomed, cast_fn recovers the original function pointer. See test_roundtrip in fn_ptr_cast.cpp. A current limitation of this approach is that it only allows function pointer casts where the source is a direct declaration of a function. Accessing a function pointer through a member field for example, would create a capturing adapter which does not coerce in a fn inside Ptr. --- cpp2rust/converter/converter.cpp | 10 +- cpp2rust/converter/converter.h | 5 +- .../converter/models/converter_refcount.cpp | 152 +++++++++++++++++- .../converter/models/converter_refcount.h | 14 ++ libcc2rs/src/lib.rs | 7 + libcc2rs/src/rc.rs | 106 ++++++++++++ libcc2rs/src/reinterpret.rs | 18 +++ tests/unit/fn_ptr_cast.cpp | 12 ++ tests/unit/out/refcount/fn_ptr.rs | 20 +-- tests/unit/out/refcount/fn_ptr_array.rs | 20 +-- .../unit/out/refcount/fn_ptr_as_condition.rs | 22 +-- tests/unit/out/refcount/fn_ptr_cast.rs | 69 +++++--- tests/unit/out/refcount/fn_ptr_conditional.rs | 35 ++-- tests/unit/out/refcount/fn_ptr_default_arg.rs | 27 ++-- tests/unit/out/refcount/fn_ptr_global.rs | 24 +-- tests/unit/out/refcount/fn_ptr_reassign.rs | 23 +-- tests/unit/out/refcount/fn_ptr_return.rs | 20 +-- tests/unit/out/refcount/fn_ptr_struct.rs | 18 +-- tests/unit/out/refcount/fn_ptr_void_return.rs | 16 +- tests/unit/out/refcount/fn_ptr_vtable.rs | 36 +++-- tests/unit/out/refcount/no_direct_callee.rs | 8 +- tests/unit/out/unsafe/fn_ptr_cast.rs | 19 +++ 22 files changed, 518 insertions(+), 163 deletions(-) diff --git a/cpp2rust/converter/converter.cpp b/cpp2rust/converter/converter.cpp index 64ccd1a1..1fdc402d 100644 --- a/cpp2rust/converter/converter.cpp +++ b/cpp2rust/converter/converter.cpp @@ -1375,6 +1375,12 @@ bool Converter::VisitCallExpr(clang::CallExpr *expr) { return false; } +void Converter::EmitFnPtrCall(clang::Expr *callee) { + StrCat(token::kOpenParen); + Convert(callee); + StrCat(").unwrap()"); +} + void Converter::ConvertGenericCallExpr(clang::CallExpr *expr) { clang::Expr *callee = expr->getCallee(); auto convert_param_ty = [&](clang::QualType param_type, clang::Expr *expr) { @@ -1447,9 +1453,7 @@ void Converter::ConvertGenericCallExpr(clang::CallExpr *expr) { } if (proto && !function) { - StrCat(token::kOpenParen); - Convert(callee); - StrCat(").unwrap()"); + EmitFnPtrCall(callee); } else { PushExprKind push(*this, ExprKind::RValue); Convert(StripFunctionPointerDecay(callee)); diff --git a/cpp2rust/converter/converter.h b/cpp2rust/converter/converter.h index 899cc965..faf340c2 100644 --- a/cpp2rust/converter/converter.h +++ b/cpp2rust/converter/converter.h @@ -199,6 +199,8 @@ class Converter : public clang::RecursiveASTVisitor { void ConvertGenericCallExpr(clang::CallExpr *expr); + virtual void EmitFnPtrCall(clang::Expr *callee); + virtual void ConvertPrintf(clang::CallExpr *expr); void ConvertVAArgCall(clang::CallExpr *expr); @@ -332,7 +334,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/models/converter_refcount.cpp b/cpp2rust/converter/models/converter_refcount.cpp index 8234a2c0..d4637eb7 100644 --- a/cpp2rust/converter/models/converter_refcount.cpp +++ b/cpp2rust/converter/models/converter_refcount.cpp @@ -165,10 +165,72 @@ 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) { + if (i > 0) + closure += ", "; + 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 += GetNamedDeclAsString(src_fn->getCanonicalDecl()) + "("; + 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->isVoidPointerType()) { + closure += std::format("a{}.cast::<{}>().unwrap()", i, + ToString(src_pty->getPointeeType())); + } else if (src_pty->isVoidPointerType() && tgt_pty->isPointerType()) { + closure += std::format("a{}.to_any()", 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("Ptr<{}>", GetFnTypeString(proto))); return false; } @@ -570,7 +632,9 @@ bool ConverterRefCount::VisitDeclRefExpr(clang::DeclRefExpr *expr) { if (clang::isa(decl)) { if (isAddrOf()) { - StrCat(std::format("Some({} as _)", str)); + auto proto = decl->getType()->getAs(); + auto fn_type = GetFnTypeString(proto); + StrCat(std::format("fn_ptr!({}, {})", str, fn_type)); } else { StrCat(str); } @@ -951,9 +1015,79 @@ bool ConverterRefCount::VisitImplicitCastExpr(clang::ImplicitCastExpr *expr) { } } + if (expr->getCastKind() == clang::CastKind::CK_NullToPointer && + expr->getType()->isFunctionPointerType()) { + StrCat("Ptr::null()"); + computed_expr_type_ = ComputedExprType::FreshPointer; + return false; + } + return Converter::VisitImplicitCastExpr(expr); } +void ConverterRefCount::EmitFnPtrCall(clang::Expr *callee) { + Convert(callee); + StrCat(".call_fn()"); +} + +std::string ConverterRefCount::GetFunctionPointerDefaultAsString( + clang::QualType qual_type) { + return "Ptr::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_fn::<{}>({})", 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::<{}>().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 +1102,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\")", @@ -1478,8 +1614,12 @@ void ConverterRefCount::ConvertVarInit(clang::QualType qual_type, Buffer buf(*this); PushConversionKind push(*this, ConversionKind::Unboxed); if (qual_type->isFunctionPointerType() && lambda->capture_size() == 0) { - PushExprKind addr_of(*this, ExprKind::AddrOf); + StrCat(std::format("Ptr::from_fn((")); VisitLambdaExpr(lambda); + StrCat(std::format( + ") as {}, 0)", + GetFnTypeString(qual_type->getPointeeType() + ->getAs()))); } else { VisitLambdaExpr(lambda); } diff --git a/cpp2rust/converter/models/converter_refcount.h b/cpp2rust/converter/models/converter_refcount.h index 81025901..cc9cc6e1 100644 --- a/cpp2rust/converter/models/converter_refcount.h +++ b/cpp2rust/converter/models/converter_refcount.h @@ -59,12 +59,16 @@ class ConverterRefCount final : public Converter { void ConvertPrintf(clang::CallExpr *expr) override; + void EmitFnPtrCall(clang::Expr *callee) 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 +107,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 +180,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/lib.rs b/libcc2rs/src/lib.rs index 42eb4117..9b7b96ef 100644 --- a/libcc2rs/src/lib.rs +++ b/libcc2rs/src/lib.rs @@ -4,6 +4,13 @@ mod reinterpret; pub use reinterpret::ByteRepr; +#[macro_export] +macro_rules! fn_ptr { + ($f:expr, $ty:ty) => { + $crate::Ptr::from_fn($f as $ty, $f as *const () as usize) + }; +} + mod rc; pub use rc::*; diff --git a/libcc2rs/src/rc.rs b/libcc2rs/src/rc.rs index eb197ee2..a9b87eb7 100644 --- a/libcc2rs/src/rc.rs +++ b/libcc2rs/src/rc.rs @@ -26,6 +26,32 @@ enum PtrKind { HeapArray(Weak>>), Vec(Weak>>), Reinterpreted(Rc), + Fn(Rc), +} + +struct FnPtrAlloc { + addr: usize, +} + +impl OriginalAlloc for FnPtrAlloc { + fn address(&self) -> usize { + self.addr + } + fn read_bytes(&self, _: usize, _: &mut [u8]) { + panic!("fn pointer"); + } + fn write_bytes(&self, _: usize, _: &[u8]) { + panic!("fn pointer"); + } + fn total_byte_len(&self) -> usize { + panic!("fn pointer"); + } +} + +#[derive(Clone)] +pub struct FnState { + alloc: Rc, + stack: Vec>>, } pub enum StrongPtr { @@ -80,6 +106,7 @@ impl fmt::Debug for PtrKind { PtrKind::Reinterpreted(ref data) => { write!(f, "Reinterpreted(0x{:x})", data.address()) } + PtrKind::Fn(ref state) => write!(f, "Fn(0x{:x})", state.alloc.address()), } } } @@ -94,6 +121,7 @@ impl Clone for PtrKind { PtrKind::StackArray(ref weak) => PtrKind::StackArray(weak.clone()), PtrKind::HeapArray(ref weak) => PtrKind::HeapArray(weak.clone()), PtrKind::Reinterpreted(ref data) => PtrKind::Reinterpreted(Rc::clone(data)), + PtrKind::Fn(ref state) => PtrKind::Fn(Rc::clone(state)), } } } @@ -106,6 +134,7 @@ impl PtrKind { PtrKind::Vec(w) => w.as_ptr() as usize, PtrKind::StackArray(w) | PtrKind::HeapArray(w) => w.as_ptr() as usize, PtrKind::Reinterpreted(ref data) => data.address(), + PtrKind::Fn(ref state) => state.alloc.address(), } } } @@ -269,6 +298,7 @@ impl Ptr { let step = std::mem::size_of::(); (data.total_byte_len() - self.offset % step) / step } + PtrKind::Fn(_) => 1, } } @@ -332,6 +362,7 @@ impl Ptr { byte_offset: self.offset, cell: RefCell::new(None), }, + PtrKind::Fn(_) => panic!("fn pointer"), } } @@ -358,6 +389,7 @@ impl Ptr { value.to_bytes(&mut buf); data.write_bytes(self.offset, &buf); } + PtrKind::Fn(_) => panic!("fn pointer"), } } @@ -398,6 +430,7 @@ impl Ptr { self.byte_offset(), ), PtrKind::Reinterpreted(ref data) => (Rc::clone(data), self.offset), + PtrKind::Fn(_) => panic!("fn pointer: use cast_fn instead"), }; Ptr { @@ -438,6 +471,7 @@ impl Ptr { data.write_bytes(self.offset, &buf); ret } + PtrKind::Fn(_) => panic!("fn pointer"), } } @@ -471,6 +505,7 @@ impl Ptr { data.write_bytes(self.offset, &buf); ret } + PtrKind::Fn(_) => panic!("fn pointer"), } } } @@ -497,6 +532,7 @@ impl Ptr { data.read_bytes(self.offset, &mut buf); T::from_bytes(&buf) } + PtrKind::Fn(_) => panic!("fn pointer"), } } } @@ -529,6 +565,7 @@ impl Ptr { PtrKind::Reinterpreted(..) => { panic!("sorting not supported for reinterpreted pointers") } + PtrKind::Fn(_) => panic!("fn pointer"), } } } @@ -582,6 +619,7 @@ impl Ptr { PtrKind::Reinterpreted(..) => { panic!("sorting not supported for reinterpreted pointers") } + PtrKind::Fn(_) => panic!("fn pointer"), } } } @@ -830,6 +868,7 @@ impl ToOwnedOption for Ptr { PtrKind::Vec(_) => panic!("Can't own a vector"), PtrKind::HeapArray(_) => panic!("Can't own an array variable as single"), PtrKind::Reinterpreted(..) => panic!("Can't own a reinterpreted pointer"), + PtrKind::Fn(_) => panic!("fn pointer"), } } } @@ -856,6 +895,7 @@ impl ToOwnedOption> for Ptr { PtrKind::Vec(_) => panic!("Can't own a vector"), PtrKind::HeapSingle(_) => panic!("Can't own a single variable as an array"), PtrKind::Reinterpreted(..) => panic!("Can't own a reinterpreted pointer"), + PtrKind::Fn(_) => panic!("fn pointer"), } } } @@ -871,6 +911,7 @@ impl fmt::Debug for Ptr { PtrKind::Vec(w) => (Weak::as_ptr(w) as usize) .wrapping_add(self.offset.wrapping_mul(std::mem::size_of::())), PtrKind::Reinterpreted(ref data) => data.address().wrapping_add(self.offset), + PtrKind::Fn(ref state) => state.alloc.address(), }; write!(f, "0x{:x}", addr) } @@ -967,6 +1008,7 @@ impl Ptr { data.read_bytes(start, &mut buf); buf } + PtrKind::Fn(_) => panic!("fn pointer"), } } @@ -1004,6 +1046,70 @@ impl Ptr { } } +impl Ptr { + pub fn from_fn(f: T, addr: usize) -> Self { + Ptr { + offset: 0, + kind: PtrKind::Fn(Rc::new(FnState { + alloc: Rc::new(FnPtrAlloc { addr }), + stack: vec![Some(Rc::new(f))], + })), + } + } + + pub fn cast_fn(&self, adapter: Option) -> Ptr { + let state = match &self.kind { + PtrKind::Fn(ref state) => state, + _ => panic!("not a fn pointer"), + }; + + let target_id = TypeId::of::(); + + for (i, entry) in state.stack.iter().enumerate() { + if let Some(ref rc) = entry { + if (*rc).as_ref().type_id() == target_id { + return Ptr { + offset: 0, + kind: PtrKind::Fn(Rc::new(FnState { + alloc: Rc::clone(&state.alloc), + stack: state.stack[..=i].to_vec(), + })), + }; + } + } + } + + let mut new_stack = state.stack.clone(); + new_stack.push(adapter.map(|a| Rc::new(a) as Rc)); + + Ptr { + offset: 0, + kind: PtrKind::Fn(Rc::new(FnState { + alloc: Rc::clone(&state.alloc), + stack: new_stack, + })), + } + } + + pub fn call_fn(&self) -> T + where + T: Copy + 'static, + { + let state = match &self.kind { + PtrKind::Fn(ref state) => state, + _ => panic!("not a fn pointer"), + }; + + let entry = state.stack.last().expect("empty fn pointer stack"); + match entry { + Some(ref rc) => *rc + .downcast_ref::() + .expect("ub: fn pointer type mismatch"), + None => panic!("ub: calling through incompatible fn pointer type"), + } + } +} + trait ErasedPtr: std::any::Any { fn pointee_type_id(&self) -> std::any::TypeId; fn memcpy(&self, src: &dyn ErasedPtr, len: usize); diff --git a/libcc2rs/src/reinterpret.rs b/libcc2rs/src/reinterpret.rs index 7bef8167..19495011 100644 --- a/libcc2rs/src/reinterpret.rs +++ b/libcc2rs/src/reinterpret.rs @@ -64,6 +64,24 @@ impl ByteRepr for std::rc::Rc {} impl ByteRepr for std::cell::RefCell {} impl ByteRepr for Box<[T]> {} impl ByteRepr for Box {} + +macro_rules! impl_byterepr_fn { + ($($name:ident),*) => { + impl ByteRepr for fn($($name),*) -> R {} + }; +} + +impl_byterepr_fn!(); +impl_byterepr_fn!(A); +impl_byterepr_fn!(A, B); +impl_byterepr_fn!(A, B, C); +impl_byterepr_fn!(A, B, C, D); +impl_byterepr_fn!(A, B, C, D, E); +impl_byterepr_fn!(A, B, C, D, E, F); +impl_byterepr_fn!(A, B, C, D, E, F, G); +impl_byterepr_fn!(A, B, C, D, E, F, G, H); +impl_byterepr_fn!(A, B, C, D, E, F, G, H, I); +impl_byterepr_fn!(A, B, C, D, E, F, G, H, I, J); impl ByteRepr for *const T {} impl ByteRepr for *mut T {} impl ByteRepr for (A, B) {} diff --git a/tests/unit/fn_ptr_cast.cpp b/tests/unit/fn_ptr_cast.cpp index d9fc7ba9..d825b050 100644 --- a/tests/unit/fn_ptr_cast.cpp +++ b/tests/unit/fn_ptr_cast.cpp @@ -36,9 +36,21 @@ void test_void_ptr_to_fn() { 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/out/refcount/fn_ptr.rs b/tests/unit/out/refcount/fn_ptr.rs index 77a3f29b..f63e1eed 100644 --- a/tests/unit/out/refcount/fn_ptr.rs +++ b/tests/unit/out/refcount/fn_ptr.rs @@ -11,34 +11,34 @@ 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_: Option i32>, pi: Ptr) -> i32 { - let fn_: Value i32>> = Rc::new(RefCell::new(fn_)); +pub fn foo_1(fn_: Ptr 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()).unwrap()(_arg0) + (*fn_.borrow()).call_fn()(_arg0) }); } pub fn main() { std::process::exit(main_0()); } fn main_0() -> i32 { - let fn_: Value i32>> = Rc::new(RefCell::new(None)); - assert!((*fn_.borrow()).is_none()); + let fn_: Value i32>> = Rc::new(RefCell::new(Ptr::null())); + assert!((*fn_.borrow()).is_null()); assert!({ let _lhs = (*fn_.borrow()).clone(); - _lhs != Some(my_foo_0 as _) + _lhs != fn_ptr!(my_foo_0, fn(AnyPtr) -> i32) }); - (*fn_.borrow_mut()) = Some(my_foo_0 as _); - assert!(!((*fn_.borrow()).is_none())); + (*fn_.borrow_mut()) = fn_ptr!(my_foo_0, fn(AnyPtr) -> i32); + assert!(!((*fn_.borrow()).is_null())); assert!({ let _lhs = (*fn_.borrow()).clone(); - _lhs == Some(my_foo_0 as _) + _lhs == fn_ptr!(my_foo_0, fn(AnyPtr) -> i32) }); let a: Value = Rc::new(RefCell::new(10)); assert!({ let _lhs = ({ - let _fn: Option i32> = (*fn_.borrow()).clone(); + let _fn: Ptr i32> = (*fn_.borrow()).clone(); let _pi: Ptr = (a.as_pointer()); foo_1(_fn, _pi) }); diff --git a/tests/unit/out/refcount/fn_ptr_array.rs b/tests/unit/out/refcount/fn_ptr_array.rs index 3323c920..da29926d 100644 --- a/tests/unit/out/refcount/fn_ptr_array.rs +++ b/tests/unit/out/refcount/fn_ptr_array.rs @@ -26,34 +26,34 @@ pub fn main() { std::process::exit(main_0()); } fn main_0() -> i32 { - let ops: Value i32>]>> = Rc::new(RefCell::new(Box::new([ - Some(add_0 as _), - Some(sub_1 as _), - Some(mul_2 as _), + 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]).unwrap()(_arg0, _arg1) + (*ops.borrow())[(0) as usize].call_fn()(_arg0, _arg1) }) == 5) ); assert!( (({ let _arg0: i32 = 7; let _arg1: i32 = 4; - ((*ops.borrow())[(1) as usize]).unwrap()(_arg0, _arg1) + (*ops.borrow())[(1) as usize].call_fn()(_arg0, _arg1) }) == 3) ); assert!( (({ let _arg0: i32 = 6; let _arg1: i32 = 5; - ((*ops.borrow())[(2) as usize]).unwrap()(_arg0, _arg1) + (*ops.borrow())[(2) as usize].call_fn()(_arg0, _arg1) }) == 30) ); - assert!(!(((*ops.borrow())[(0) as usize]).is_none())); - assert!(((*ops.borrow())[(0) as usize] == Some(add_0 as _))); - assert!(((*ops.borrow())[(0) as usize] != Some(sub_1 as _))); + 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 index 55471851..af8e24c9 100644 --- a/tests/unit/out/refcount/fn_ptr_as_condition.rs +++ b/tests/unit/out/refcount/fn_ptr_as_condition.rs @@ -15,13 +15,13 @@ pub fn double_it_0(x: Ptr) { __ptr.write(__tmp) }; } -pub fn maybe_call_1(cb: Option)>, x: Ptr) { - let cb: Value)>> = Rc::new(RefCell::new(cb)); +pub fn maybe_call_1(cb: Ptr)>, x: Ptr) { + let cb: Value)>> = Rc::new(RefCell::new(cb)); let x: Value> = Rc::new(RefCell::new(x)); - if !(*cb.borrow()).is_none() { + if !(*cb.borrow()).is_null() { ({ let _arg0: Ptr = (*x.borrow()).clone(); - (*cb.borrow()).unwrap()(_arg0) + (*cb.borrow()).call_fn()(_arg0) }); } } @@ -31,27 +31,27 @@ pub fn main() { fn main_0() -> i32 { let a: Value = Rc::new(RefCell::new(5)); ({ - let _cb: Option)> = Some(double_it_0 as _); + let _cb: Ptr)> = 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: Option)> = None; + let _cb: Ptr)> = Ptr::null(); let _x: Ptr = (b.as_pointer()); maybe_call_1(_cb, _x) }); assert!(((*b.borrow()) == 5)); - let fn_: Value)>> = Rc::new(RefCell::new(None)); - if !!(*fn_.borrow()).is_none() { - (*fn_.borrow_mut()) = (Some(double_it_0 as _)).clone(); + let fn_: Value)>> = Rc::new(RefCell::new(Ptr::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_none() { + if !(*fn_.borrow()).is_null() { ({ let _arg0: Ptr = (c.as_pointer()); - (*fn_.borrow()).unwrap()(_arg0) + (*fn_.borrow()).call_fn()(_arg0) }); } assert!(((*c.borrow()) == 6)); diff --git a/tests/unit/out/refcount/fn_ptr_cast.rs b/tests/unit/out/refcount/fn_ptr_cast.rs index cb0f77d9..c3893d73 100644 --- a/tests/unit/out/refcount/fn_ptr_cast.rs +++ b/tests/unit/out/refcount/fn_ptr_cast.rs @@ -12,24 +12,25 @@ pub fn double_it_0(x: i32) -> i32 { return ((*x.borrow()) * 2); } pub fn test_roundtrip_1() { - let fn_: Value i32>> = Rc::new(RefCell::new(Some(double_it_0 as _))); + let fn_: Value i32>> = + Rc::new(RefCell::new(fn_ptr!(double_it_0, fn(i32) -> i32))); assert!( (({ let _arg0: i32 = 5; - (*fn_.borrow()).unwrap()(_arg0) + (*fn_.borrow()).call_fn()(_arg0) }) == 10) ); - let gfn: Value> = Rc::new(RefCell::new( - ((*fn_.borrow()).to_strong().as_pointer() as Value>).clone(), + let gfn: Value> = Rc::new(RefCell::new( + ((*fn_.borrow()).cast_fn::(None)).clone(), )); - assert!(!((*gfn.borrow()).is_none())); - let fn2: Value i32>> = Rc::new(RefCell::new( - ((*gfn.borrow()).to_strong().as_pointer() as Value i32>>).clone(), + assert!(!((*gfn.borrow()).is_null())); + let fn2: Value i32>> = Rc::new(RefCell::new( + ((*gfn.borrow()).cast_fn:: i32>(None)).clone(), )); assert!( (({ let _arg0: i32 = 5; - (*fn2.borrow()).unwrap()(_arg0) + (*fn2.borrow()).call_fn()(_arg0) }) == 10) ); assert!({ @@ -38,17 +39,18 @@ pub fn test_roundtrip_1() { }); } pub fn test_double_cast_2() { - let fn_: Value i32>> = Rc::new(RefCell::new(Some(double_it_0 as _))); - let fn2: Value i32>> = Rc::new(RefCell::new( - (((*fn_.borrow()).to_strong().as_pointer() as Value>) - .to_strong() - .as_pointer() as Value i32>>) - .clone(), + 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_fn::(None) + .cast_fn:: i32>(None)) + .clone(), )); assert!( (({ let _arg0: i32 = 5; - (*fn2.borrow()).unwrap()(_arg0) + (*fn2.borrow()).call_fn()(_arg0) }) == 10) ); assert!({ @@ -71,21 +73,45 @@ 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()) = - (Some(double_it_0 as _).to_strong().as_pointer() as AnyPtr); - let fn_: Value i32>> = Rc::new(RefCell::new( + (*(*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::() - .expect("ub:wrong type")) + .cast:: i32>() + .expect("ub:wrong fn type")) .clone(), )); assert!( (({ let _arg0: i32 = 5; - (*fn_.borrow()).unwrap()(_arg0) + (*fn_.borrow()).call_fn()(_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_fn:: 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()).call_fn()(_arg0, _arg1) + }), + )); + assert!(((*result.borrow()) == 142)); +} pub fn main() { std::process::exit(main_0()); } @@ -93,5 +119,6 @@ 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 index 6cc940a3..fa323e1f 100644 --- a/tests/unit/out/refcount/fn_ptr_conditional.rs +++ b/tests/unit/out/refcount/fn_ptr_conditional.rs @@ -19,30 +19,29 @@ pub fn identity_2(x: i32) -> i32 { let x: Value = Rc::new(RefCell::new(x)); return (*x.borrow()); } -pub fn pick_3(mode: i32) -> Option i32> { +pub fn pick_3(mode: i32) -> Ptr i32> { let mode: Value = Rc::new(RefCell::new(mode)); return if ((*mode.borrow()) > 0) { - Some(inc_0 as _) + fn_ptr!(inc_0, fn(i32) -> i32) } else { if ((*mode.borrow()) < 0) { - Some(dec_1 as _) + fn_ptr!(dec_1, fn(i32) -> i32) } else { - Some(identity_2 as _) + fn_ptr!(identity_2, fn(i32) -> i32) } }; } -pub fn apply_4(fn_: Option i32>, x: i32) -> i32 { - let fn_: Value i32>> = Rc::new(RefCell::new(fn_)); +pub fn apply_4(fn_: Ptr 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_none() { - (*fn_.borrow()).clone() - } else { - Some(identity_2 as _) - })); + 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()).unwrap()(_arg0) + (*actual.borrow()).call_fn()(_arg0) }); } pub fn main() { @@ -56,7 +55,7 @@ fn main_0() -> i32 { let _mode: i32 = 1; pick_3(_mode) }) - .unwrap()(_arg0) + .call_fn()(_arg0) }) == 11) ); assert!( @@ -66,7 +65,7 @@ fn main_0() -> i32 { let _mode: i32 = -1_i32; pick_3(_mode) }) - .unwrap()(_arg0) + .call_fn()(_arg0) }) == 9) ); assert!( @@ -76,19 +75,19 @@ fn main_0() -> i32 { let _mode: i32 = 0; pick_3(_mode) }) - .unwrap()(_arg0) + .call_fn()(_arg0) }) == 10) ); assert!( (({ - let _fn: Option i32> = Some(inc_0 as _); + let _fn: Ptr i32> = fn_ptr!(inc_0, fn(i32) -> i32); let _x: i32 = 5; apply_4(_fn, _x) }) == 6) ); assert!( (({ - let _fn: Option i32> = None; + let _fn: Ptr i32> = Ptr::null(); let _x: i32 = 5; apply_4(_fn, _x) }) == 5) diff --git a/tests/unit/out/refcount/fn_ptr_default_arg.rs b/tests/unit/out/refcount/fn_ptr_default_arg.rs index 68af5d97..41961ea5 100644 --- a/tests/unit/out/refcount/fn_ptr_default_arg.rs +++ b/tests/unit/out/refcount/fn_ptr_default_arg.rs @@ -11,13 +11,13 @@ 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 { +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(None))); - if !(*fn_.borrow()).is_none() { + let fn_: Value i32>> = Rc::new(RefCell::new(fn_.unwrap_or(Ptr::null()))); + if !(*fn_.borrow()).is_null() { return ({ let _arg0: i32 = (*x.borrow()); - (*fn_.borrow()).unwrap()(_arg0) + (*fn_.borrow()).call_fn()(_arg0) }); } return (*x.borrow()); @@ -29,32 +29,35 @@ fn main_0() -> i32 { assert!( (({ let _x: i32 = 5; - let _fn: Option i32> = Default::default(); + let _fn: Ptr i32> = Default::default(); apply_1(_x, Some(_fn)) }) == 5) ); assert!( (({ let _x: i32 = 5; - let _fn: Option i32> = None; + let _fn: Ptr i32> = Ptr::null(); apply_1(_x, Some(_fn)) }) == 5) ); assert!( (({ let _x: i32 = 5; - let _fn: Option i32> = Some(identity_0 as _); + let _fn: Ptr i32> = fn_ptr!(identity_0, fn(i32) -> i32); apply_1(_x, Some(_fn)) }) == 5) ); - let negate: Value i32>> = Rc::new(RefCell::new(Some(|x: i32| { - let x: Value = Rc::new(RefCell::new(x)); - return -(*x.borrow()); - }))); + let negate: Value i32>> = Rc::new(RefCell::new(Ptr::from_fn( + (|x: i32| { + let x: Value = Rc::new(RefCell::new(x)); + return -(*x.borrow()); + }) as fn(i32) -> i32, + 0, + ))); assert!( (({ let _x: i32 = 5; - let _fn: Option i32> = (*negate.borrow()).clone(); + let _fn: Ptr i32> = (*negate.borrow()).clone(); apply_1(_x, Some(_fn)) }) == -5_i32) ); diff --git a/tests/unit/out/refcount/fn_ptr_global.rs b/tests/unit/out/refcount/fn_ptr_global.rs index c7bf0561..38303602 100644 --- a/tests/unit/out/refcount/fn_ptr_global.rs +++ b/tests/unit/out/refcount/fn_ptr_global.rs @@ -16,18 +16,18 @@ pub fn triple_it_1(x: i32) -> i32 { return ((*x.borrow()) * 3); } thread_local!( - pub static g_op: Value i32>> = Rc::new(RefCell::new(None)); + pub static g_op: Value i32>> = Rc::new(RefCell::new(Ptr::null())); ); -pub fn set_op_2(fn_: Option i32>) { - let fn_: Value i32>> = Rc::new(RefCell::new(fn_)); +pub fn set_op_2(fn_: Ptr 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_none() { + if !(*g_op.with(Value::clone).borrow()).is_null() { return ({ let _arg0: i32 = (*x.borrow()); - (*g_op.with(Value::clone).borrow()).unwrap()(_arg0) + (*g_op.with(Value::clone).borrow()).call_fn()(_arg0) }); } return (*x.borrow()); @@ -43,13 +43,13 @@ fn main_0() -> i32 { }) == 5) ); ({ - let _fn: Option i32> = Some(double_it_0 as _); + let _fn: Ptr i32> = fn_ptr!(double_it_0, fn(i32) -> i32); set_op_2(_fn) }); - assert!(!((*g_op.with(Value::clone).borrow()).is_none())); + assert!(!((*g_op.with(Value::clone).borrow()).is_null())); assert!({ let _lhs = (*g_op.with(Value::clone).borrow()).clone(); - _lhs == Some(double_it_0 as _) + _lhs == fn_ptr!(double_it_0, fn(i32) -> i32) }); assert!( (({ @@ -58,12 +58,12 @@ fn main_0() -> i32 { }) == 10) ); ({ - let _fn: Option i32> = Some(triple_it_1 as _); + let _fn: Ptr i32> = fn_ptr!(triple_it_1, fn(i32) -> i32); set_op_2(_fn) }); assert!({ let _lhs = (*g_op.with(Value::clone).borrow()).clone(); - _lhs == Some(triple_it_1 as _) + _lhs == fn_ptr!(triple_it_1, fn(i32) -> i32) }); assert!( (({ @@ -72,10 +72,10 @@ fn main_0() -> i32 { }) == 15) ); ({ - let _fn: Option i32> = None; + let _fn: Ptr i32> = Ptr::null(); set_op_2(_fn) }); - assert!((*g_op.with(Value::clone).borrow()).is_none()); + assert!((*g_op.with(Value::clone).borrow()).is_null()); assert!( (({ let _x: i32 = 5; diff --git a/tests/unit/out/refcount/fn_ptr_reassign.rs b/tests/unit/out/refcount/fn_ptr_reassign.rs index 98f6def1..7675e66a 100644 --- a/tests/unit/out/refcount/fn_ptr_reassign.rs +++ b/tests/unit/out/refcount/fn_ptr_reassign.rs @@ -26,39 +26,40 @@ pub fn main() { std::process::exit(main_0()); } fn main_0() -> i32 { - let fn_: Value i32>> = Rc::new(RefCell::new(Some(add_0 as _))); + 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()).unwrap()(_arg0, _arg1) + (*fn_.borrow()).call_fn()(_arg0, _arg1) }) == 7) ); - (*fn_.borrow_mut()) = Some(sub_1 as _); + (*fn_.borrow_mut()) = fn_ptr!(sub_1, fn(i32, i32) -> i32); assert!( (({ let _arg0: i32 = 10; let _arg1: i32 = 3; - (*fn_.borrow()).unwrap()(_arg0, _arg1) + (*fn_.borrow()).call_fn()(_arg0, _arg1) }) == 7) ); - (*fn_.borrow_mut()) = Some(mul_2 as _); + (*fn_.borrow_mut()) = fn_ptr!(mul_2, fn(i32, i32) -> i32); assert!( (({ let _arg0: i32 = 6; let _arg1: i32 = 7; - (*fn_.borrow()).unwrap()(_arg0, _arg1) + (*fn_.borrow()).call_fn()(_arg0, _arg1) }) == 42) ); - (*fn_.borrow_mut()) = None; - assert!((*fn_.borrow()).is_none()); - (*fn_.borrow_mut()) = Some(add_0 as _); - assert!(!((*fn_.borrow()).is_none())); + (*fn_.borrow_mut()) = Ptr::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()).unwrap()(_arg0, _arg1) + (*fn_.borrow()).call_fn()(_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 index 9dead5c8..2b9621fb 100644 --- a/tests/unit/out/refcount/fn_ptr_return.rs +++ b/tests/unit/out/refcount/fn_ptr_return.rs @@ -15,35 +15,35 @@ 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) -> Option i32> { +pub fn pick_2(choose_inc: i32) -> Ptr i32> { let choose_inc: Value = Rc::new(RefCell::new(choose_inc)); if ((*choose_inc.borrow()) != 0) { - return Some(inc_0 as _); + return fn_ptr!(inc_0, fn(i32) -> i32); } - return Some(dec_1 as _); + 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 f: Value i32>> = Rc::new(RefCell::new( ({ let _choose_inc: i32 = 1; pick_2(_choose_inc) }), )); - assert!(!((*f.borrow()).is_none())); + assert!(!((*f.borrow()).is_null())); assert!({ let _lhs = (*f.borrow()).clone(); - _lhs == Some(inc_0 as _) + _lhs == fn_ptr!(inc_0, fn(i32) -> i32) }); assert!( (({ let _arg0: i32 = 10; - (*f.borrow()).unwrap()(_arg0) + (*f.borrow()).call_fn()(_arg0) }) == 11) ); - let g: Value i32>> = Rc::new(RefCell::new( + let g: Value i32>> = Rc::new(RefCell::new( ({ let _choose_inc: i32 = 0; pick_2(_choose_inc) @@ -51,12 +51,12 @@ fn main_0() -> i32 { )); assert!({ let _lhs = (*g.borrow()).clone(); - _lhs == Some(dec_1 as _) + _lhs == fn_ptr!(dec_1, fn(i32) -> i32) }); assert!( (({ let _arg0: i32 = 10; - (*g.borrow()).unwrap()(_arg0) + (*g.borrow()).call_fn()(_arg0) }) == 9) ); assert!({ diff --git a/tests/unit/out/refcount/fn_ptr_struct.rs b/tests/unit/out/refcount/fn_ptr_struct.rs index 433b3f47..74d0c631 100644 --- a/tests/unit/out/refcount/fn_ptr_struct.rs +++ b/tests/unit/out/refcount/fn_ptr_struct.rs @@ -10,7 +10,7 @@ use std::rc::{Rc, Weak}; #[derive()] pub struct Handler { pub tag: Value, - pub cb: Value i32>>, + pub cb: Value i32>>, } impl Clone for Handler { fn clone(&self) -> Self { @@ -25,7 +25,7 @@ impl Default for Handler { fn default() -> Self { Handler { tag: >::default(), - cb: Rc::new(RefCell::new(None)), + cb: Rc::new(RefCell::new(Ptr::null())), } } } @@ -44,30 +44,30 @@ 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(Some(double_it_0 as _))), + 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(Some(negate_1 as _))), + cb: Rc::new(RefCell::new(fn_ptr!(negate_1, fn(i32) -> i32))), })); - assert!(!((*(*h1.borrow()).cb.borrow()).is_none())); + assert!(!((*(*h1.borrow()).cb.borrow()).is_null())); assert!( (({ let _arg0: i32 = 5; - (*(*h1.borrow()).cb.borrow()).unwrap()(_arg0) + (*(*h1.borrow()).cb.borrow()).call_fn()(_arg0) }) == 10) ); assert!( (({ let _arg0: i32 = 7; - (*(*h2.borrow()).cb.borrow()).unwrap()(_arg0) + (*(*h2.borrow()).cb.borrow()).call_fn()(_arg0) }) == -7_i32) ); - (*(*h1.borrow()).cb.borrow_mut()) = Some(negate_1 as _); + (*(*h1.borrow()).cb.borrow_mut()) = fn_ptr!(negate_1, fn(i32) -> i32); assert!( (({ let _arg0: i32 = 3; - (*(*h1.borrow()).cb.borrow()).unwrap()(_arg0) + (*(*h1.borrow()).cb.borrow()).call_fn()(_arg0) }) == -3_i32) ); assert!({ diff --git a/tests/unit/out/refcount/fn_ptr_void_return.rs b/tests/unit/out/refcount/fn_ptr_void_return.rs index 9c2011b0..316439a3 100644 --- a/tests/unit/out/refcount/fn_ptr_void_return.rs +++ b/tests/unit/out/refcount/fn_ptr_void_return.rs @@ -16,12 +16,12 @@ pub fn zero_out_1(x: Ptr) { let x: Value> = Rc::new(RefCell::new(x)); (*x.borrow()).write(0); } -pub fn run_2(fn_: Option)>, x: Ptr) { - let fn_: Value)>> = Rc::new(RefCell::new(fn_)); +pub fn run_2(fn_: Ptr)>, 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()).unwrap()(_arg0) + (*fn_.borrow()).call_fn()(_arg0) }); } pub fn main() { @@ -30,23 +30,23 @@ pub fn main() { fn main_0() -> i32 { let a: Value = Rc::new(RefCell::new(42)); ({ - let _fn: Option)> = Some(negate_0 as _); + let _fn: Ptr)> = fn_ptr!(negate_0, fn(Ptr::)); let _x: Ptr = (a.as_pointer()); run_2(_fn, _x) }); assert!(((*a.borrow()) == -42_i32)); ({ - let _fn: Option)> = Some(zero_out_1 as _); + let _fn: Ptr)> = 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(Some(negate_0 as _))); - assert!(!((*fn_.borrow()).is_none())); + 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()).unwrap()(_arg0) + (*fn_.borrow()).call_fn()(_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 index a9128c94..3ec0683a 100644 --- a/tests/unit/out/refcount/fn_ptr_vtable.rs +++ b/tests/unit/out/refcount/fn_ptr_vtable.rs @@ -9,9 +9,9 @@ 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>, + pub create: Value AnyPtr>>, + pub get: Value i32>>, + pub destroy: Value>, } impl Clone for Vtable { fn clone(&self) -> Self { @@ -26,9 +26,9 @@ impl Clone for Vtable { impl Default for Vtable { fn default() -> Self { Vtable { - create: Rc::new(RefCell::new(None)), - get: Rc::new(RefCell::new(None)), - destroy: Rc::new(RefCell::new(None)), + create: Rc::new(RefCell::new(Ptr::null())), + get: Rc::new(RefCell::new(Ptr::null())), + destroy: Rc::new(RefCell::new(Ptr::null())), } } } @@ -54,31 +54,33 @@ pub fn main() { } fn main_0() -> i32 { let vt: Value = Rc::new(RefCell::new(Vtable { - create: Rc::new(RefCell::new((Some(int_create_0 as _)).clone())), - get: Rc::new(RefCell::new(Some(int_get_1 as _))), - destroy: Rc::new(RefCell::new(Some(int_destroy_2 as _))), + 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_none())); - assert!(!((*(*vt.borrow()).get.borrow()).is_none())); - assert!(!((*(*vt.borrow()).destroy.borrow()).is_none())); + 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()).unwrap()(_arg0) + (*(*vt.borrow()).create.borrow()).call_fn()(_arg0) }), )); assert!( (({ let _arg0: AnyPtr = (*obj.borrow()).clone(); - (*(*vt.borrow()).get.borrow()).unwrap()(_arg0) + (*(*vt.borrow()).get.borrow()).call_fn()(_arg0) }) == 42) ); ({ let _arg0: AnyPtr = (*obj.borrow()).clone(); - (*(*vt.borrow()).destroy.borrow()).unwrap()(_arg0) + (*(*vt.borrow()).destroy.borrow()).call_fn()(_arg0) }); assert!(((*storage.with(Value::clone).borrow()) == 0)); - (*(*vt.borrow()).get.borrow_mut()) = None; - assert!((*(*vt.borrow()).get.borrow()).is_none()); + (*(*vt.borrow()).get.borrow_mut()) = Ptr::null(); + assert!((*(*vt.borrow()).get.borrow()).is_null()); return 0; } diff --git a/tests/unit/out/refcount/no_direct_callee.rs b/tests/unit/out/refcount/no_direct_callee.rs index 33d09c6e..5bd650b7 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_: Option bool>) -> i32 { - let fn_: Value bool>> = Rc::new(RefCell::new(fn_)); - if !({ (*fn_.borrow()).unwrap()() }) { +pub fn test_1(fn_: Ptr bool>) -> i32 { + let fn_: Value bool>> = Rc::new(RefCell::new(fn_)); + if !({ (*fn_.borrow()).call_fn()() }) { return 1; } return 0; @@ -22,7 +22,7 @@ pub fn main() { } fn main_0() -> i32 { return ({ - let _fn: Option bool> = Some(test1_0 as _); + let _fn: Ptr bool> = fn_ptr!(test1_0, fn() -> bool); test_1(_fn) }); } diff --git a/tests/unit/out/unsafe/fn_ptr_cast.rs b/tests/unit/out/unsafe/fn_ptr_cast.rs index c4eee5ea..e5ebf489 100644 --- a/tests/unit/out/unsafe/fn_ptr_cast.rs +++ b/tests/unit/out/unsafe/fn_ptr_cast.rs @@ -63,6 +63,24 @@ pub unsafe fn test_void_ptr_to_fn_3() { }) == (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 as _)); + 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); @@ -72,5 +90,6 @@ 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; } From a5d65f6e5d6ac40081bcb337cd8717e2fc66f122 Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Tue, 14 Apr 2026 20:18:09 +0100 Subject: [PATCH 15/56] Move PtrKind::Fn into FnPtr --- .../converter/models/converter_refcount.cpp | 16 +- libcc2rs/src/fn_ptr.rs | 164 ++++++++++++++++++ libcc2rs/src/lib.rs | 14 +- libcc2rs/src/rc.rs | 110 +----------- tests/unit/out/refcount/fn_ptr.rs | 10 +- tests/unit/out/refcount/fn_ptr_array.rs | 8 +- .../unit/out/refcount/fn_ptr_as_condition.rs | 14 +- tests/unit/out/refcount/fn_ptr_cast.rs | 37 ++-- tests/unit/out/refcount/fn_ptr_conditional.rs | 29 ++-- tests/unit/out/refcount/fn_ptr_default_arg.rs | 20 +-- tests/unit/out/refcount/fn_ptr_global.rs | 14 +- tests/unit/out/refcount/fn_ptr_reassign.rs | 12 +- tests/unit/out/refcount/fn_ptr_return.rs | 10 +- tests/unit/out/refcount/fn_ptr_struct.rs | 10 +- tests/unit/out/refcount/fn_ptr_void_return.rs | 14 +- tests/unit/out/refcount/fn_ptr_vtable.rs | 20 +-- tests/unit/out/refcount/no_direct_callee.rs | 8 +- 17 files changed, 290 insertions(+), 220 deletions(-) create mode 100644 libcc2rs/src/fn_ptr.rs diff --git a/cpp2rust/converter/models/converter_refcount.cpp b/cpp2rust/converter/models/converter_refcount.cpp index d4637eb7..19b2fdf2 100644 --- a/cpp2rust/converter/models/converter_refcount.cpp +++ b/cpp2rust/converter/models/converter_refcount.cpp @@ -230,7 +230,7 @@ ConverterRefCount::GetFnTypeString(const clang::FunctionProtoType *proto) { bool ConverterRefCount::VisitPointerType(clang::PointerType *type) { if (auto proto = type->getPointeeType()->getAs()) { - StrCat(std::format("Ptr<{}>", GetFnTypeString(proto))); + StrCat(std::format("FnPtr<{}>", GetFnTypeString(proto))); return false; } @@ -1017,7 +1017,7 @@ bool ConverterRefCount::VisitImplicitCastExpr(clang::ImplicitCastExpr *expr) { if (expr->getCastKind() == clang::CastKind::CK_NullToPointer && expr->getType()->isFunctionPointerType()) { - StrCat("Ptr::null()"); + StrCat("FnPtr::null()"); computed_expr_type_ = ComputedExprType::FreshPointer; return false; } @@ -1027,12 +1027,12 @@ bool ConverterRefCount::VisitImplicitCastExpr(clang::ImplicitCastExpr *expr) { void ConverterRefCount::EmitFnPtrCall(clang::Expr *callee) { Convert(callee); - StrCat(".call_fn()"); + StrCat(".call()"); } std::string ConverterRefCount::GetFunctionPointerDefaultAsString( clang::QualType qual_type) { - return "Ptr::null()"; + return "FnPtr::null()"; } void ConverterRefCount::ConvertEqualsNullPtr(clang::Expr *expr) { @@ -1066,7 +1066,7 @@ bool ConverterRefCount::VisitFunctionPointerCast( } } - StrCat(std::format("{}.cast_fn::<{}>({})", ToString(expr->getSubExpr()), + StrCat(std::format("{}.cast::<{}>({})", ToString(expr->getSubExpr()), fn_type, adapter)); } else if (expr->getSubExpr()->getType()->isFunctionPointerType() || expr->getType()->isVoidPointerType()) { @@ -1077,7 +1077,7 @@ bool ConverterRefCount::VisitFunctionPointerCast( auto target_proto = expr->getType()->getPointeeType()->getAs(); auto fn_type = GetFnTypeString(target_proto); - StrCat(std::format("{}.cast::<{}>().expect(\"ub:wrong fn type\")", + StrCat(std::format("{}.cast_fn::<{}>().expect(\"ub:wrong fn type\")", ToString(expr->getSubExpr()), fn_type)); } else { assert(0 && "Unhandled function pointer cast"); @@ -1614,10 +1614,10 @@ void ConverterRefCount::ConvertVarInit(clang::QualType qual_type, Buffer buf(*this); PushConversionKind push(*this, ConversionKind::Unboxed); if (qual_type->isFunctionPointerType() && lambda->capture_size() == 0) { - StrCat(std::format("Ptr::from_fn((")); + StrCat("fn_ptr_anon!(("); VisitLambdaExpr(lambda); StrCat(std::format( - ") as {}, 0)", + "), {})", GetFnTypeString(qual_type->getPointeeType() ->getAs()))); } else { diff --git a/libcc2rs/src/fn_ptr.rs b/libcc2rs/src/fn_ptr.rs new file mode 100644 index 00000000..e2d9f6a4 --- /dev/null +++ b/libcc2rs/src/fn_ptr.rs @@ -0,0 +1,164 @@ +// Copyright (c) 2022-present INESC-ID. +// Distributed under the MIT license that can be found in the LICENSE file. + +use std::any::{Any, TypeId}; +use std::marker::PhantomData; +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, + } + } + + pub fn call(&self) -> T + where + T: Copy, + { + 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 9b7b96ef..998280fe 100644 --- a/libcc2rs/src/lib.rs +++ b/libcc2rs/src/lib.rs @@ -7,13 +7,25 @@ pub use reinterpret::ByteRepr; #[macro_export] macro_rules! fn_ptr { ($f:expr, $ty:ty) => { - $crate::Ptr::from_fn($f as $ty, $f as *const () as usize) + $crate::FnPtr::new($f as $ty, $f as *const () as usize) + }; +} + +// Lambda: no stable address, use 0. TODO: assign unique addr per lambda site so distinct +// lambdas don't compare equal. +#[macro_export] +macro_rules! fn_ptr_anon { + ($f:expr, $ty:ty) => { + $crate::FnPtr::new($f as $ty, 0) }; } mod rc; pub use rc::*; +mod fn_ptr; +pub use fn_ptr::FnPtr; + mod inc; pub use inc::*; diff --git a/libcc2rs/src/rc.rs b/libcc2rs/src/rc.rs index a9b87eb7..3b8fbe1f 100644 --- a/libcc2rs/src/rc.rs +++ b/libcc2rs/src/rc.rs @@ -26,32 +26,6 @@ enum PtrKind { HeapArray(Weak>>), Vec(Weak>>), Reinterpreted(Rc), - Fn(Rc), -} - -struct FnPtrAlloc { - addr: usize, -} - -impl OriginalAlloc for FnPtrAlloc { - fn address(&self) -> usize { - self.addr - } - fn read_bytes(&self, _: usize, _: &mut [u8]) { - panic!("fn pointer"); - } - fn write_bytes(&self, _: usize, _: &[u8]) { - panic!("fn pointer"); - } - fn total_byte_len(&self) -> usize { - panic!("fn pointer"); - } -} - -#[derive(Clone)] -pub struct FnState { - alloc: Rc, - stack: Vec>>, } pub enum StrongPtr { @@ -106,7 +80,6 @@ impl fmt::Debug for PtrKind { PtrKind::Reinterpreted(ref data) => { write!(f, "Reinterpreted(0x{:x})", data.address()) } - PtrKind::Fn(ref state) => write!(f, "Fn(0x{:x})", state.alloc.address()), } } } @@ -121,7 +94,6 @@ impl Clone for PtrKind { PtrKind::StackArray(ref weak) => PtrKind::StackArray(weak.clone()), PtrKind::HeapArray(ref weak) => PtrKind::HeapArray(weak.clone()), PtrKind::Reinterpreted(ref data) => PtrKind::Reinterpreted(Rc::clone(data)), - PtrKind::Fn(ref state) => PtrKind::Fn(Rc::clone(state)), } } } @@ -134,7 +106,6 @@ impl PtrKind { PtrKind::Vec(w) => w.as_ptr() as usize, PtrKind::StackArray(w) | PtrKind::HeapArray(w) => w.as_ptr() as usize, PtrKind::Reinterpreted(ref data) => data.address(), - PtrKind::Fn(ref state) => state.alloc.address(), } } } @@ -298,7 +269,6 @@ impl Ptr { let step = std::mem::size_of::(); (data.total_byte_len() - self.offset % step) / step } - PtrKind::Fn(_) => 1, } } @@ -362,7 +332,6 @@ impl Ptr { byte_offset: self.offset, cell: RefCell::new(None), }, - PtrKind::Fn(_) => panic!("fn pointer"), } } @@ -389,7 +358,6 @@ impl Ptr { value.to_bytes(&mut buf); data.write_bytes(self.offset, &buf); } - PtrKind::Fn(_) => panic!("fn pointer"), } } @@ -430,7 +398,6 @@ impl Ptr { self.byte_offset(), ), PtrKind::Reinterpreted(ref data) => (Rc::clone(data), self.offset), - PtrKind::Fn(_) => panic!("fn pointer: use cast_fn instead"), }; Ptr { @@ -471,7 +438,6 @@ impl Ptr { data.write_bytes(self.offset, &buf); ret } - PtrKind::Fn(_) => panic!("fn pointer"), } } @@ -505,7 +471,6 @@ impl Ptr { data.write_bytes(self.offset, &buf); ret } - PtrKind::Fn(_) => panic!("fn pointer"), } } } @@ -532,7 +497,6 @@ impl Ptr { data.read_bytes(self.offset, &mut buf); T::from_bytes(&buf) } - PtrKind::Fn(_) => panic!("fn pointer"), } } } @@ -565,7 +529,6 @@ impl Ptr { PtrKind::Reinterpreted(..) => { panic!("sorting not supported for reinterpreted pointers") } - PtrKind::Fn(_) => panic!("fn pointer"), } } } @@ -619,7 +582,6 @@ impl Ptr { PtrKind::Reinterpreted(..) => { panic!("sorting not supported for reinterpreted pointers") } - PtrKind::Fn(_) => panic!("fn pointer"), } } } @@ -868,7 +830,6 @@ impl ToOwnedOption for Ptr { PtrKind::Vec(_) => panic!("Can't own a vector"), PtrKind::HeapArray(_) => panic!("Can't own an array variable as single"), PtrKind::Reinterpreted(..) => panic!("Can't own a reinterpreted pointer"), - PtrKind::Fn(_) => panic!("fn pointer"), } } } @@ -895,7 +856,6 @@ impl ToOwnedOption> for Ptr { PtrKind::Vec(_) => panic!("Can't own a vector"), PtrKind::HeapSingle(_) => panic!("Can't own a single variable as an array"), PtrKind::Reinterpreted(..) => panic!("Can't own a reinterpreted pointer"), - PtrKind::Fn(_) => panic!("fn pointer"), } } } @@ -911,7 +871,6 @@ impl fmt::Debug for Ptr { PtrKind::Vec(w) => (Weak::as_ptr(w) as usize) .wrapping_add(self.offset.wrapping_mul(std::mem::size_of::())), PtrKind::Reinterpreted(ref data) => data.address().wrapping_add(self.offset), - PtrKind::Fn(ref state) => state.alloc.address(), }; write!(f, "0x{:x}", addr) } @@ -1008,7 +967,6 @@ impl Ptr { data.read_bytes(start, &mut buf); buf } - PtrKind::Fn(_) => panic!("fn pointer"), } } @@ -1046,71 +1004,7 @@ impl Ptr { } } -impl Ptr { - pub fn from_fn(f: T, addr: usize) -> Self { - Ptr { - offset: 0, - kind: PtrKind::Fn(Rc::new(FnState { - alloc: Rc::new(FnPtrAlloc { addr }), - stack: vec![Some(Rc::new(f))], - })), - } - } - - pub fn cast_fn(&self, adapter: Option) -> Ptr { - let state = match &self.kind { - PtrKind::Fn(ref state) => state, - _ => panic!("not a fn pointer"), - }; - - let target_id = TypeId::of::(); - - for (i, entry) in state.stack.iter().enumerate() { - if let Some(ref rc) = entry { - if (*rc).as_ref().type_id() == target_id { - return Ptr { - offset: 0, - kind: PtrKind::Fn(Rc::new(FnState { - alloc: Rc::clone(&state.alloc), - stack: state.stack[..=i].to_vec(), - })), - }; - } - } - } - - let mut new_stack = state.stack.clone(); - new_stack.push(adapter.map(|a| Rc::new(a) as Rc)); - - Ptr { - offset: 0, - kind: PtrKind::Fn(Rc::new(FnState { - alloc: Rc::clone(&state.alloc), - stack: new_stack, - })), - } - } - - pub fn call_fn(&self) -> T - where - T: Copy + 'static, - { - let state = match &self.kind { - PtrKind::Fn(ref state) => state, - _ => panic!("not a fn pointer"), - }; - - let entry = state.stack.last().expect("empty fn pointer stack"); - match entry { - Some(ref rc) => *rc - .downcast_ref::() - .expect("ub: fn pointer type mismatch"), - None => panic!("ub: calling through incompatible fn pointer type"), - } - } -} - -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; @@ -1180,7 +1074,7 @@ where #[derive(Clone)] pub struct AnyPtr { - ptr: Rc, + pub(crate) ptr: Rc, } impl Ptr { diff --git a/tests/unit/out/refcount/fn_ptr.rs b/tests/unit/out/refcount/fn_ptr.rs index f63e1eed..0ef56254 100644 --- a/tests/unit/out/refcount/fn_ptr.rs +++ b/tests/unit/out/refcount/fn_ptr.rs @@ -11,19 +11,19 @@ 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_: Ptr i32>, pi: Ptr) -> i32 { - let fn_: Value i32>> = Rc::new(RefCell::new(fn_)); +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()).call_fn()(_arg0) + (*fn_.borrow()).call()(_arg0) }); } pub fn main() { std::process::exit(main_0()); } fn main_0() -> i32 { - let fn_: Value i32>> = Rc::new(RefCell::new(Ptr::null())); + let fn_: Value i32>> = Rc::new(RefCell::new(FnPtr::null())); assert!((*fn_.borrow()).is_null()); assert!({ let _lhs = (*fn_.borrow()).clone(); @@ -38,7 +38,7 @@ fn main_0() -> i32 { let a: Value = Rc::new(RefCell::new(10)); assert!({ let _lhs = ({ - let _fn: Ptr i32> = (*fn_.borrow()).clone(); + let _fn: FnPtr i32> = (*fn_.borrow()).clone(); let _pi: Ptr = (a.as_pointer()); foo_1(_fn, _pi) }); diff --git a/tests/unit/out/refcount/fn_ptr_array.rs b/tests/unit/out/refcount/fn_ptr_array.rs index da29926d..8e1d5fca 100644 --- a/tests/unit/out/refcount/fn_ptr_array.rs +++ b/tests/unit/out/refcount/fn_ptr_array.rs @@ -26,7 +26,7 @@ pub fn main() { std::process::exit(main_0()); } fn main_0() -> i32 { - let ops: Value i32>]>> = Rc::new(RefCell::new(Box::new([ + 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), @@ -35,21 +35,21 @@ fn main_0() -> i32 { (({ let _arg0: i32 = 2; let _arg1: i32 = 3; - (*ops.borrow())[(0) as usize].call_fn()(_arg0, _arg1) + (*ops.borrow())[(0) as usize].call()(_arg0, _arg1) }) == 5) ); assert!( (({ let _arg0: i32 = 7; let _arg1: i32 = 4; - (*ops.borrow())[(1) as usize].call_fn()(_arg0, _arg1) + (*ops.borrow())[(1) as usize].call()(_arg0, _arg1) }) == 3) ); assert!( (({ let _arg0: i32 = 6; let _arg1: i32 = 5; - (*ops.borrow())[(2) as usize].call_fn()(_arg0, _arg1) + (*ops.borrow())[(2) as usize].call()(_arg0, _arg1) }) == 30) ); assert!(!(((*ops.borrow())[(0) as usize]).is_null())); diff --git a/tests/unit/out/refcount/fn_ptr_as_condition.rs b/tests/unit/out/refcount/fn_ptr_as_condition.rs index af8e24c9..c6ffacfd 100644 --- a/tests/unit/out/refcount/fn_ptr_as_condition.rs +++ b/tests/unit/out/refcount/fn_ptr_as_condition.rs @@ -15,13 +15,13 @@ pub fn double_it_0(x: Ptr) { __ptr.write(__tmp) }; } -pub fn maybe_call_1(cb: Ptr)>, x: Ptr) { - let cb: Value)>> = Rc::new(RefCell::new(cb)); +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()).call_fn()(_arg0) + (*cb.borrow()).call()(_arg0) }); } } @@ -31,19 +31,19 @@ pub fn main() { fn main_0() -> i32 { let a: Value = Rc::new(RefCell::new(5)); ({ - let _cb: Ptr)> = fn_ptr!(double_it_0, fn(Ptr::)); + 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: Ptr)> = Ptr::null(); + 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(Ptr::null())); + 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(); } @@ -51,7 +51,7 @@ fn main_0() -> i32 { if !(*fn_.borrow()).is_null() { ({ let _arg0: Ptr = (c.as_pointer()); - (*fn_.borrow()).call_fn()(_arg0) + (*fn_.borrow()).call()(_arg0) }); } assert!(((*c.borrow()) == 6)); diff --git a/tests/unit/out/refcount/fn_ptr_cast.rs b/tests/unit/out/refcount/fn_ptr_cast.rs index c3893d73..c33b2bd3 100644 --- a/tests/unit/out/refcount/fn_ptr_cast.rs +++ b/tests/unit/out/refcount/fn_ptr_cast.rs @@ -12,25 +12,24 @@ pub fn double_it_0(x: i32) -> i32 { return ((*x.borrow()) * 2); } pub fn test_roundtrip_1() { - let fn_: Value i32>> = + let fn_: Value i32>> = Rc::new(RefCell::new(fn_ptr!(double_it_0, fn(i32) -> i32))); assert!( (({ let _arg0: i32 = 5; - (*fn_.borrow()).call_fn()(_arg0) + (*fn_.borrow()).call()(_arg0) }) == 10) ); - let gfn: Value> = Rc::new(RefCell::new( - ((*fn_.borrow()).cast_fn::(None)).clone(), - )); + 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_fn:: i32>(None)).clone(), + let fn2: Value i32>> = Rc::new(RefCell::new( + ((*gfn.borrow()).cast:: i32>(None)).clone(), )); assert!( (({ let _arg0: i32 = 5; - (*fn2.borrow()).call_fn()(_arg0) + (*fn2.borrow()).call()(_arg0) }) == 10) ); assert!({ @@ -39,18 +38,18 @@ pub fn test_roundtrip_1() { }); } pub fn test_double_cast_2() { - let fn_: Value i32>> = + let fn_: Value i32>> = Rc::new(RefCell::new(fn_ptr!(double_it_0, fn(i32) -> i32))); - let fn2: Value i32>> = Rc::new(RefCell::new( + let fn2: Value i32>> = Rc::new(RefCell::new( ((*fn_.borrow()) - .cast_fn::(None) - .cast_fn:: i32>(None)) + .cast::(None) + .cast:: i32>(None)) .clone(), )); assert!( (({ let _arg0: i32 = 5; - (*fn2.borrow()).call_fn()(_arg0) + (*fn2.borrow()).call()(_arg0) }) == 10) ); assert!({ @@ -74,16 +73,16 @@ 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( + let fn_: Value i32>> = Rc::new(RefCell::new( ((*(*cmd.borrow()).data.borrow()) - .cast:: i32>() + .cast_fn:: i32>() .expect("ub:wrong fn type")) .clone(), )); assert!( (({ let _arg0: i32 = 5; - (*fn_.borrow()).call_fn()(_arg0) + (*fn_.borrow()).call()(_arg0) }) == 10) ); } @@ -96,8 +95,8 @@ 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_fn:: i32>(Some( + 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, )), @@ -107,7 +106,7 @@ pub fn test_call_through_cast_5() { ({ let _arg0: AnyPtr = ((val.as_pointer()) as Ptr).to_any(); let _arg1: i32 = 42; - (*gfn.borrow()).call_fn()(_arg0, _arg1) + (*gfn.borrow()).call()(_arg0, _arg1) }), )); assert!(((*result.borrow()) == 142)); diff --git a/tests/unit/out/refcount/fn_ptr_conditional.rs b/tests/unit/out/refcount/fn_ptr_conditional.rs index fa323e1f..c6e577aa 100644 --- a/tests/unit/out/refcount/fn_ptr_conditional.rs +++ b/tests/unit/out/refcount/fn_ptr_conditional.rs @@ -19,7 +19,7 @@ pub fn identity_2(x: i32) -> i32 { let x: Value = Rc::new(RefCell::new(x)); return (*x.borrow()); } -pub fn pick_3(mode: i32) -> Ptr 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) @@ -31,17 +31,18 @@ pub fn pick_3(mode: i32) -> Ptr i32> { } }; } -pub fn apply_4(fn_: Ptr i32>, x: i32) -> i32 { - let fn_: Value i32>> = Rc::new(RefCell::new(fn_)); +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) - })); + 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()).call_fn()(_arg0) + (*actual.borrow()).call()(_arg0) }); } pub fn main() { @@ -55,7 +56,7 @@ fn main_0() -> i32 { let _mode: i32 = 1; pick_3(_mode) }) - .call_fn()(_arg0) + .call()(_arg0) }) == 11) ); assert!( @@ -65,7 +66,7 @@ fn main_0() -> i32 { let _mode: i32 = -1_i32; pick_3(_mode) }) - .call_fn()(_arg0) + .call()(_arg0) }) == 9) ); assert!( @@ -75,19 +76,19 @@ fn main_0() -> i32 { let _mode: i32 = 0; pick_3(_mode) }) - .call_fn()(_arg0) + .call()(_arg0) }) == 10) ); assert!( (({ - let _fn: Ptr i32> = fn_ptr!(inc_0, fn(i32) -> i32); + let _fn: FnPtr i32> = fn_ptr!(inc_0, fn(i32) -> i32); let _x: i32 = 5; apply_4(_fn, _x) }) == 6) ); assert!( (({ - let _fn: Ptr i32> = Ptr::null(); + let _fn: FnPtr i32> = FnPtr::null(); let _x: i32 = 5; apply_4(_fn, _x) }) == 5) diff --git a/tests/unit/out/refcount/fn_ptr_default_arg.rs b/tests/unit/out/refcount/fn_ptr_default_arg.rs index 41961ea5..6c6ab799 100644 --- a/tests/unit/out/refcount/fn_ptr_default_arg.rs +++ b/tests/unit/out/refcount/fn_ptr_default_arg.rs @@ -11,13 +11,13 @@ 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 { +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(Ptr::null()))); + 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()).call_fn()(_arg0) + (*fn_.borrow()).call()(_arg0) }); } return (*x.borrow()); @@ -29,35 +29,35 @@ fn main_0() -> i32 { assert!( (({ let _x: i32 = 5; - let _fn: Ptr i32> = Default::default(); + let _fn: FnPtr i32> = Default::default(); apply_1(_x, Some(_fn)) }) == 5) ); assert!( (({ let _x: i32 = 5; - let _fn: Ptr i32> = Ptr::null(); + let _fn: FnPtr i32> = FnPtr::null(); apply_1(_x, Some(_fn)) }) == 5) ); assert!( (({ let _x: i32 = 5; - let _fn: Ptr i32> = fn_ptr!(identity_0, fn(i32) -> i32); + 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(Ptr::from_fn( + let negate: Value i32>> = Rc::new(RefCell::new(fn_ptr_anon!( (|x: i32| { let x: Value = Rc::new(RefCell::new(x)); return -(*x.borrow()); - }) as fn(i32) -> i32, - 0, + }), + fn(i32) -> i32 ))); assert!( (({ let _x: i32 = 5; - let _fn: Ptr i32> = (*negate.borrow()).clone(); + let _fn: FnPtr i32> = (*negate.borrow()).clone(); apply_1(_x, Some(_fn)) }) == -5_i32) ); diff --git a/tests/unit/out/refcount/fn_ptr_global.rs b/tests/unit/out/refcount/fn_ptr_global.rs index 38303602..af0dca94 100644 --- a/tests/unit/out/refcount/fn_ptr_global.rs +++ b/tests/unit/out/refcount/fn_ptr_global.rs @@ -16,10 +16,10 @@ pub fn triple_it_1(x: i32) -> i32 { return ((*x.borrow()) * 3); } thread_local!( - pub static g_op: Value i32>> = Rc::new(RefCell::new(Ptr::null())); + pub static g_op: Value i32>> = Rc::new(RefCell::new(FnPtr::null())); ); -pub fn set_op_2(fn_: Ptr i32>) { - let fn_: Value i32>> = Rc::new(RefCell::new(fn_)); +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 { @@ -27,7 +27,7 @@ pub fn call_op_3(x: i32) -> i32 { if !(*g_op.with(Value::clone).borrow()).is_null() { return ({ let _arg0: i32 = (*x.borrow()); - (*g_op.with(Value::clone).borrow()).call_fn()(_arg0) + (*g_op.with(Value::clone).borrow()).call()(_arg0) }); } return (*x.borrow()); @@ -43,7 +43,7 @@ fn main_0() -> i32 { }) == 5) ); ({ - let _fn: Ptr i32> = fn_ptr!(double_it_0, fn(i32) -> i32); + 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())); @@ -58,7 +58,7 @@ fn main_0() -> i32 { }) == 10) ); ({ - let _fn: Ptr i32> = fn_ptr!(triple_it_1, fn(i32) -> i32); + let _fn: FnPtr i32> = fn_ptr!(triple_it_1, fn(i32) -> i32); set_op_2(_fn) }); assert!({ @@ -72,7 +72,7 @@ fn main_0() -> i32 { }) == 15) ); ({ - let _fn: Ptr i32> = Ptr::null(); + let _fn: FnPtr i32> = FnPtr::null(); set_op_2(_fn) }); assert!((*g_op.with(Value::clone).borrow()).is_null()); diff --git a/tests/unit/out/refcount/fn_ptr_reassign.rs b/tests/unit/out/refcount/fn_ptr_reassign.rs index 7675e66a..123abbec 100644 --- a/tests/unit/out/refcount/fn_ptr_reassign.rs +++ b/tests/unit/out/refcount/fn_ptr_reassign.rs @@ -26,13 +26,13 @@ pub fn main() { std::process::exit(main_0()); } fn main_0() -> i32 { - let fn_: Value 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()).call_fn()(_arg0, _arg1) + (*fn_.borrow()).call()(_arg0, _arg1) }) == 7) ); (*fn_.borrow_mut()) = fn_ptr!(sub_1, fn(i32, i32) -> i32); @@ -40,7 +40,7 @@ fn main_0() -> i32 { (({ let _arg0: i32 = 10; let _arg1: i32 = 3; - (*fn_.borrow()).call_fn()(_arg0, _arg1) + (*fn_.borrow()).call()(_arg0, _arg1) }) == 7) ); (*fn_.borrow_mut()) = fn_ptr!(mul_2, fn(i32, i32) -> i32); @@ -48,10 +48,10 @@ fn main_0() -> i32 { (({ let _arg0: i32 = 6; let _arg1: i32 = 7; - (*fn_.borrow()).call_fn()(_arg0, _arg1) + (*fn_.borrow()).call()(_arg0, _arg1) }) == 42) ); - (*fn_.borrow_mut()) = Ptr::null(); + (*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())); @@ -59,7 +59,7 @@ fn main_0() -> i32 { (({ let _arg0: i32 = 1; let _arg1: i32 = 1; - (*fn_.borrow()).call_fn()(_arg0, _arg1) + (*fn_.borrow()).call()(_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 index 2b9621fb..43681494 100644 --- a/tests/unit/out/refcount/fn_ptr_return.rs +++ b/tests/unit/out/refcount/fn_ptr_return.rs @@ -15,7 +15,7 @@ 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) -> Ptr 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); @@ -26,7 +26,7 @@ pub fn main() { std::process::exit(main_0()); } fn main_0() -> i32 { - let f: Value i32>> = Rc::new(RefCell::new( + let f: Value i32>> = Rc::new(RefCell::new( ({ let _choose_inc: i32 = 1; pick_2(_choose_inc) @@ -40,10 +40,10 @@ fn main_0() -> i32 { assert!( (({ let _arg0: i32 = 10; - (*f.borrow()).call_fn()(_arg0) + (*f.borrow()).call()(_arg0) }) == 11) ); - let g: Value i32>> = Rc::new(RefCell::new( + let g: Value i32>> = Rc::new(RefCell::new( ({ let _choose_inc: i32 = 0; pick_2(_choose_inc) @@ -56,7 +56,7 @@ fn main_0() -> i32 { assert!( (({ let _arg0: i32 = 10; - (*g.borrow()).call_fn()(_arg0) + (*g.borrow()).call()(_arg0) }) == 9) ); assert!({ diff --git a/tests/unit/out/refcount/fn_ptr_struct.rs b/tests/unit/out/refcount/fn_ptr_struct.rs index 74d0c631..777a60ee 100644 --- a/tests/unit/out/refcount/fn_ptr_struct.rs +++ b/tests/unit/out/refcount/fn_ptr_struct.rs @@ -10,7 +10,7 @@ use std::rc::{Rc, Weak}; #[derive()] pub struct Handler { pub tag: Value, - pub cb: Value i32>>, + pub cb: Value i32>>, } impl Clone for Handler { fn clone(&self) -> Self { @@ -25,7 +25,7 @@ impl Default for Handler { fn default() -> Self { Handler { tag: >::default(), - cb: Rc::new(RefCell::new(Ptr::null())), + cb: Rc::new(RefCell::new(FnPtr::null())), } } } @@ -54,20 +54,20 @@ fn main_0() -> i32 { assert!( (({ let _arg0: i32 = 5; - (*(*h1.borrow()).cb.borrow()).call_fn()(_arg0) + (*(*h1.borrow()).cb.borrow()).call()(_arg0) }) == 10) ); assert!( (({ let _arg0: i32 = 7; - (*(*h2.borrow()).cb.borrow()).call_fn()(_arg0) + (*(*h2.borrow()).cb.borrow()).call()(_arg0) }) == -7_i32) ); (*(*h1.borrow()).cb.borrow_mut()) = fn_ptr!(negate_1, fn(i32) -> i32); assert!( (({ let _arg0: i32 = 3; - (*(*h1.borrow()).cb.borrow()).call_fn()(_arg0) + (*(*h1.borrow()).cb.borrow()).call()(_arg0) }) == -3_i32) ); assert!({ diff --git a/tests/unit/out/refcount/fn_ptr_void_return.rs b/tests/unit/out/refcount/fn_ptr_void_return.rs index 316439a3..69fd6b15 100644 --- a/tests/unit/out/refcount/fn_ptr_void_return.rs +++ b/tests/unit/out/refcount/fn_ptr_void_return.rs @@ -16,12 +16,12 @@ pub fn zero_out_1(x: Ptr) { let x: Value> = Rc::new(RefCell::new(x)); (*x.borrow()).write(0); } -pub fn run_2(fn_: Ptr)>, x: Ptr) { - let fn_: Value)>> = Rc::new(RefCell::new(fn_)); +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()).call_fn()(_arg0) + (*fn_.borrow()).call()(_arg0) }); } pub fn main() { @@ -30,23 +30,23 @@ pub fn main() { fn main_0() -> i32 { let a: Value = Rc::new(RefCell::new(42)); ({ - let _fn: Ptr)> = fn_ptr!(negate_0, fn(Ptr::)); + 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: Ptr)> = fn_ptr!(zero_out_1, fn(Ptr::)); + 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::)))); + 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()).call_fn()(_arg0) + (*fn_.borrow()).call()(_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 index 3ec0683a..7b94350e 100644 --- a/tests/unit/out/refcount/fn_ptr_vtable.rs +++ b/tests/unit/out/refcount/fn_ptr_vtable.rs @@ -9,9 +9,9 @@ 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>, + pub create: Value AnyPtr>>, + pub get: Value i32>>, + pub destroy: Value>, } impl Clone for Vtable { fn clone(&self) -> Self { @@ -26,9 +26,9 @@ impl Clone for Vtable { impl Default for Vtable { fn default() -> Self { Vtable { - create: Rc::new(RefCell::new(Ptr::null())), - get: Rc::new(RefCell::new(Ptr::null())), - destroy: Rc::new(RefCell::new(Ptr::null())), + create: Rc::new(RefCell::new(FnPtr::null())), + get: Rc::new(RefCell::new(FnPtr::null())), + destroy: Rc::new(RefCell::new(FnPtr::null())), } } } @@ -66,21 +66,21 @@ fn main_0() -> i32 { let obj: Value = Rc::new(RefCell::new( ({ let _arg0: i32 = 42; - (*(*vt.borrow()).create.borrow()).call_fn()(_arg0) + (*(*vt.borrow()).create.borrow()).call()(_arg0) }), )); assert!( (({ let _arg0: AnyPtr = (*obj.borrow()).clone(); - (*(*vt.borrow()).get.borrow()).call_fn()(_arg0) + (*(*vt.borrow()).get.borrow()).call()(_arg0) }) == 42) ); ({ let _arg0: AnyPtr = (*obj.borrow()).clone(); - (*(*vt.borrow()).destroy.borrow()).call_fn()(_arg0) + (*(*vt.borrow()).destroy.borrow()).call()(_arg0) }); assert!(((*storage.with(Value::clone).borrow()) == 0)); - (*(*vt.borrow()).get.borrow_mut()) = Ptr::null(); + (*(*vt.borrow()).get.borrow_mut()) = FnPtr::null(); assert!((*(*vt.borrow()).get.borrow()).is_null()); return 0; } diff --git a/tests/unit/out/refcount/no_direct_callee.rs b/tests/unit/out/refcount/no_direct_callee.rs index 5bd650b7..192ee3e8 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_: Ptr bool>) -> i32 { - let fn_: Value bool>> = Rc::new(RefCell::new(fn_)); - if !({ (*fn_.borrow()).call_fn()() }) { +pub fn test_1(fn_: FnPtr bool>) -> i32 { + let fn_: Value bool>> = Rc::new(RefCell::new(fn_)); + if !({ (*fn_.borrow()).call()() }) { return 1; } return 0; @@ -22,7 +22,7 @@ pub fn main() { } fn main_0() -> i32 { return ({ - let _fn: Ptr bool> = fn_ptr!(test1_0, fn() -> bool); + let _fn: FnPtr bool> = fn_ptr!(test1_0, fn() -> bool); test_1(_fn) }); } From 01e9e9f0dfe2e5f53656f41ce638b24677085d2c Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Wed, 15 Apr 2026 09:55:02 +0100 Subject: [PATCH 16/56] Fix signature of fread and fwrite Also make the rules public so that generated code can reference them. --- rules/stdio/tgt_refcount.rs | 4 ++-- rules/stdio/tgt_unsafe.rs | 14 ++++++++++++-- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/rules/stdio/tgt_refcount.rs b/rules/stdio/tgt_refcount.rs index ea7174fd..a36d31a7 100644 --- a/rules/stdio/tgt_refcount.rs +++ b/rules/stdio/tgt_refcount.rs @@ -55,7 +55,7 @@ fn f4(a0: &mut ::std::fs::File, a1: i64, a2: i32) -> i32 { } } -fn f5(a0: AnyPtr, a1: u64, a2: u64, a3: Ptr<::std::fs::File>) -> u64 { +pub fn f5(a0: AnyPtr, a1: u64, a2: u64, a3: Ptr<::std::fs::File>) -> u64 { let total = a1.saturating_mul(a2) as usize; let mut dst = a0 .cast::() @@ -92,7 +92,7 @@ fn f5(a0: AnyPtr, a1: u64, a2: u64, a3: Ptr<::std::fs::File>) -> u64 { (read_bytes / a1 as usize) as u64 } -fn f6(a0: AnyPtr, a1: u64, a2: u64, a3: Ptr<::std::fs::File>) -> u64 { +pub fn f6(a0: AnyPtr, a1: u64, a2: u64, a3: Ptr<::std::fs::File>) -> u64 { let total = a1.saturating_mul(a2) as usize; let mut src = a0 .cast::() diff --git a/rules/stdio/tgt_unsafe.rs b/rules/stdio/tgt_unsafe.rs index adc05652..ae324d5f 100644 --- a/rules/stdio/tgt_unsafe.rs +++ b/rules/stdio/tgt_unsafe.rs @@ -65,7 +65,12 @@ unsafe fn f4(a0: *mut ::std::fs::File, a1: i64, a2: i32) -> i32 { } } -unsafe fn f5(a0: *mut u8, a1: u64, a2: u64, a3: *mut ::std::fs::File) -> u64 { +pub unsafe fn f5( + a0: *mut ::libc::c_void, + a1: u64, + a2: u64, + a3: *mut ::std::fs::File, +) -> u64 { let total = a1.saturating_mul(a2) as usize; let mut dst = a0 as *mut u8; @@ -97,7 +102,12 @@ unsafe fn f5(a0: *mut u8, a1: u64, a2: u64, a3: *mut ::std::fs::File) -> u64 { (read_bytes / a1 as usize) as u64 } -unsafe fn f6(a0: *mut u8, a1: u64, a2: u64, a3: *mut ::std::fs::File) -> u64 { +pub unsafe fn f6( + a0: *const ::libc::c_void, + a1: u64, + a2: u64, + a3: *mut ::std::fs::File, +) -> u64 { let total = a1.saturating_mul(a2) as usize; let mut src = a0 as *mut u8; From e3a69afb5bad442dfe87fae0c4e9e96c4caef64a Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Wed, 15 Apr 2026 10:03:21 +0100 Subject: [PATCH 17/56] Add name and module in ExprTgt --- cpp2rust/converter/translation_rule.cpp | 4 ++++ cpp2rust/converter/translation_rule.h | 2 ++ 2 files changed, 6 insertions(+) diff --git a/cpp2rust/converter/translation_rule.cpp b/cpp2rust/converter/translation_rule.cpp index 6350a21a..5f124de4 100644 --- a/cpp2rust/converter/translation_rule.cpp +++ b/cpp2rust/converter/translation_rule.cpp @@ -320,6 +320,7 @@ RuleMap LoadTgtFromIR(const std::filesystem::path &json_path) { Rule rule; if (name[0] == 'f') { rule.tgt = ParseExprTgtJSON(*obj); + std::get(rule.tgt).name = name; std::get(rule.tgt).validate(json_path.string() + ":" + name); } else if (name[0] == 't') { rule.tgt = ParseTypeTgtJSON(*obj); @@ -484,6 +485,9 @@ std::vector Load(const std::filesystem::path &path, Model model) { } for (auto &[name, src_rule] : src_rules) { rules.at(name).src = std::move(src_rule.src); + if (auto *expr_tgt = std::get_if(&rules.at(name).tgt)) { + expr_tgt->module = dir.filename().string(); + } } std::vector result; diff --git a/cpp2rust/converter/translation_rule.h b/cpp2rust/converter/translation_rule.h index 566dd900..2d63c8f9 100644 --- a/cpp2rust/converter/translation_rule.h +++ b/cpp2rust/converter/translation_rule.h @@ -61,6 +61,8 @@ struct TypeInfo { }; struct ExprTgt { + std::string name; + std::string module; std::unordered_map params; // "a0" -> TypeInfo TypeInfo return_type; std::unordered_map> From e30fef097e98f6ef6ad1d3527b115c78a9f6f6b4 Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Wed, 15 Apr 2026 10:44:28 +0100 Subject: [PATCH 18/56] Use rule function name in AddrOf context --- cpp2rust/converter/converter.cpp | 26 ++++++++++++++++--- cpp2rust/converter/converter.h | 4 +++ .../converter/models/converter_refcount.cpp | 25 +++++++++++------- .../converter/models/converter_refcount.h | 2 ++ 4 files changed, 44 insertions(+), 13 deletions(-) diff --git a/cpp2rust/converter/converter.cpp b/cpp2rust/converter/converter.cpp index 1fdc402d..112a26e4 100644 --- a/cpp2rust/converter/converter.cpp +++ b/cpp2rust/converter/converter.cpp @@ -1381,6 +1381,25 @@ void Converter::EmitFnPtrCall(clang::Expr *callee) { StrCat(").unwrap()"); } +void Converter::EmitFnAsValue(const clang::FunctionDecl *fn_decl) { + StrCat(std::format("Some({} as _)", Mapper::GetFnRefName(fn_decl))); +} + +std::string Converter::GetPointeeRustType(clang::QualType ptr_type) { + auto pointee = ptr_type->getPointeeType(); + if (pointee->isIntegerType()) { + return ToString(pointee); + } + auto str = ToString(ptr_type); + while (!str.empty() && std::isspace(str.back())) { + str.pop_back(); + } + if (str.starts_with("Ptr<") && str.ends_with(">")) { + return str.substr(4, str.size() - 5); + } + return ToString(pointee); +} + void Converter::ConvertGenericCallExpr(clang::CallExpr *expr) { clang::Expr *callee = expr->getCallee(); auto convert_param_ty = [&](clang::QualType param_type, clang::Expr *expr) { @@ -2000,7 +2019,8 @@ std::string Converter::ConvertDeclRefExpr(clang::DeclRefExpr *expr) { } auto *decl = expr->getDecl(); - if (Mapper::Contains(expr)) { + if (!(clang::isa(decl) && isAddrOf()) && + Mapper::Contains(expr)) { return GetMappedAsString(expr); } else if (auto *function = decl->getAsFunction()) { if (auto method = clang::dyn_cast(function)) { @@ -2037,9 +2057,9 @@ bool Converter::VisitDeclRefExpr(clang::DeclRefExpr *expr) { return false; } - if (clang::isa(decl)) { + if (auto *fn_decl = clang::dyn_cast(decl)) { if (isAddrOf()) { - StrCat(std::format("Some({} as _)", str)); + EmitFnAsValue(fn_decl); return false; } } diff --git a/cpp2rust/converter/converter.h b/cpp2rust/converter/converter.h index faf340c2..7494a996 100644 --- a/cpp2rust/converter/converter.h +++ b/cpp2rust/converter/converter.h @@ -201,6 +201,10 @@ class Converter : public clang::RecursiveASTVisitor { virtual void EmitFnPtrCall(clang::Expr *callee); + virtual void EmitFnAsValue(const clang::FunctionDecl *fn_decl); + + std::string GetPointeeRustType(clang::QualType ptr_type); + virtual void ConvertPrintf(clang::CallExpr *expr); void ConvertVAArgCall(clang::CallExpr *expr); diff --git a/cpp2rust/converter/models/converter_refcount.cpp b/cpp2rust/converter/models/converter_refcount.cpp index 19b2fdf2..b11e1598 100644 --- a/cpp2rust/converter/models/converter_refcount.cpp +++ b/cpp2rust/converter/models/converter_refcount.cpp @@ -192,7 +192,7 @@ std::string ConverterRefCount::BuildFnAdapter( closure += "{ "; // Build adapter body: src_fn(convert(a0), convert(a1), ...) - closure += GetNamedDeclAsString(src_fn->getCanonicalDecl()) + "("; + 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); @@ -200,7 +200,7 @@ std::string ConverterRefCount::BuildFnAdapter( closure += std::format("a{}", i); } else if (src_pty->isPointerType() && tgt_pty->isVoidPointerType()) { closure += std::format("a{}.cast::<{}>().unwrap()", i, - ToString(src_pty->getPointeeType())); + GetPointeeRustType(src_pty)); } else if (src_pty->isVoidPointerType() && tgt_pty->isPointerType()) { closure += std::format("a{}.to_any()", i); } else { @@ -622,19 +622,18 @@ bool ConverterRefCount::VisitDeclRefExpr(clang::DeclRefExpr *expr) { } } - if (Mapper::Contains(expr)) { + auto str = ConvertDeclRefExpr(expr); + auto decl = expr->getDecl(); + + if (!(clang::isa(decl) && isAddrOf()) && + Mapper::Contains(expr)) { StrCat(GetMappedAsString(expr)); return false; } - auto str = ConvertDeclRefExpr(expr); - auto decl = expr->getDecl(); - - if (clang::isa(decl)) { + if (auto *fn_decl = clang::dyn_cast(decl)) { if (isAddrOf()) { - auto proto = decl->getType()->getAs(); - auto fn_type = GetFnTypeString(proto); - StrCat(std::format("fn_ptr!({}, {})", str, fn_type)); + EmitFnAsValue(fn_decl); } else { StrCat(str); } @@ -1030,6 +1029,12 @@ void ConverterRefCount::EmitFnPtrCall(clang::Expr *callee) { StrCat(".call()"); } +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()"; diff --git a/cpp2rust/converter/models/converter_refcount.h b/cpp2rust/converter/models/converter_refcount.h index cc9cc6e1..cb9f3d82 100644 --- a/cpp2rust/converter/models/converter_refcount.h +++ b/cpp2rust/converter/models/converter_refcount.h @@ -61,6 +61,8 @@ class ConverterRefCount final : public Converter { 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; From 133e94f0af66c648bbcf05a3c8138a64d458bb02 Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Wed, 15 Apr 2026 11:07:30 +0100 Subject: [PATCH 19/56] Check name and module are never empty --- cpp2rust/converter/translation_rule.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/cpp2rust/converter/translation_rule.cpp b/cpp2rust/converter/translation_rule.cpp index 5f124de4..2adaa07e 100644 --- a/cpp2rust/converter/translation_rule.cpp +++ b/cpp2rust/converter/translation_rule.cpp @@ -321,7 +321,6 @@ RuleMap LoadTgtFromIR(const std::filesystem::path &json_path) { if (name[0] == 'f') { rule.tgt = ParseExprTgtJSON(*obj); std::get(rule.tgt).name = name; - std::get(rule.tgt).validate(json_path.string() + ":" + name); } else if (name[0] == 't') { rule.tgt = ParseTypeTgtJSON(*obj); } else { @@ -463,6 +462,8 @@ void ExprTgt::validate(const std::string &context) const { ValidateConsecutiveKeys(params, 'a', 0, context + " params"); ValidateConsecutiveKeys(generics, 'T', 1, context + " generics"); assert(!body.empty() && "ExprTgt body must not be empty"); + assert(!name.empty() && "ExprTgt name must not be empty"); + assert(!module.empty() && "ExprTgt module must not be empty"); } std::vector Load(const std::filesystem::path &path, Model model) { @@ -493,6 +494,9 @@ std::vector Load(const std::filesystem::path &path, Model model) { std::vector result; for (auto &[name, rule] : rules) { assert(!rule.src.empty() && "Rule loaded from IR but has no src"); + if (auto *expr_tgt = std::get_if(&rule.tgt)) { + expr_tgt->validate(path.string() + ":" + name); + } result.push_back(std::move(rule)); } return result; From 25b203fcfd6dbef25d8a788d4d8bba35bffe64c6 Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Wed, 15 Apr 2026 11:12:54 +0100 Subject: [PATCH 20/56] Get fully qualified name of translation rule --- cpp2rust/converter/mapper.cpp | 27 +++++++++++++++++++++++++++ cpp2rust/converter/mapper.h | 1 + 2 files changed, 28 insertions(+) diff --git a/cpp2rust/converter/mapper.cpp b/cpp2rust/converter/mapper.cpp index 478e3c51..8ba1d59c 100644 --- a/cpp2rust/converter/mapper.cpp +++ b/cpp2rust/converter/mapper.cpp @@ -8,6 +8,7 @@ #include #include +#include #include #include #include @@ -560,6 +561,17 @@ std::string normalizeTranslationRule(std::string rule) { return rule; } +std::string GetRulePathForFunction(const clang::FunctionDecl *decl, + const std::string &model_suffix) { + assert(decl); + auto it = exprs_.find(ToString(decl)); + if (it == exprs_.end()) { + return ""; + } + auto &tgt = it->second; + return std::format("rules::{}_{}::{}", tgt.module, model_suffix, tgt.name); +} + } // namespace bool Contains(clang::QualType qual_type) { @@ -575,6 +587,21 @@ const TranslationRule::ExprTgt *GetExprTgt(const clang::Expr *expr) { return nullptr; } +std::string GetFnRefName(const clang::FunctionDecl *decl) { + assert(decl); + if (model_ == Model::kRefCount) { + auto refcount_path = GetRulePathForFunction(decl, "tgt_refcount"); + if (!refcount_path.empty()) { + return refcount_path; + } + } + auto unsafe_path = GetRulePathForFunction(decl, "tgt_unsafe"); + if (!unsafe_path.empty()) { + return unsafe_path; + } + return GetNamedDeclAsString(decl->getCanonicalDecl()); +} + std::string InstantiateTemplate(const clang::Expr *expr, const std::string &text) { auto it = search(expr); diff --git a/cpp2rust/converter/mapper.h b/cpp2rust/converter/mapper.h index 14de1808..c8ed73fc 100644 --- a/cpp2rust/converter/mapper.h +++ b/cpp2rust/converter/mapper.h @@ -19,6 +19,7 @@ bool Contains(const clang::Expr *expr); std::string Map(clang::QualType qual_type); const TranslationRule::ExprTgt *GetExprTgt(const clang::Expr *expr); +std::string GetFnRefName(const clang::FunctionDecl *decl); std::string InstantiateTemplate(const clang::Expr *expr, const std::string &text); bool ReturnsPointer(const clang::Expr *expr); From 9cfda12bee003327cb7be379d98a29614e8308ff Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Wed, 15 Apr 2026 11:13:48 +0100 Subject: [PATCH 21/56] Update output of fn_ptr_stdlib_compare --- tests/unit/fn_ptr_stdlib_compare.cpp | 22 ++------- .../out/refcount/fn_ptr_stdlib_compare.rs | 49 +++++++++++++++++++ .../unit/out/unsafe/fn_ptr_stdlib_compare.rs | 33 +++++++++++++ 3 files changed, 86 insertions(+), 18 deletions(-) create mode 100644 tests/unit/out/refcount/fn_ptr_stdlib_compare.rs create mode 100644 tests/unit/out/unsafe/fn_ptr_stdlib_compare.rs diff --git a/tests/unit/fn_ptr_stdlib_compare.cpp b/tests/unit/fn_ptr_stdlib_compare.cpp index e2ca1c3e..a71581c2 100644 --- a/tests/unit/fn_ptr_stdlib_compare.cpp +++ b/tests/unit/fn_ptr_stdlib_compare.cpp @@ -3,30 +3,16 @@ #include typedef size_t (*read_fn)(void *, size_t, size_t, FILE *); -typedef void (*free_fn)(void *); -typedef void *(*malloc_fn)(size_t); + +typedef size_t (*read_alternative_fn)(char *, size_t, size_t, void *); int main() { read_fn rfn = fread; assert(rfn == fread); assert(rfn != nullptr); - free_fn ffn = free; - assert(ffn == free); - assert(ffn != nullptr); - - malloc_fn mfn = malloc; - assert(mfn == malloc); - assert(mfn != nullptr); - - // Reassign and compare - read_fn rfn2 = fread; - assert(rfn == rfn2); - - free_fn ffn2 = nullptr; - assert(ffn != ffn2); - ffn2 = free; - assert(ffn == ffn2); + read_alternative_fn rfn2 = (read_alternative_fn)fread; + assert(rfn == (read_fn)rfn2); return 0; } diff --git a/tests/unit/out/refcount/fn_ptr_stdlib_compare.rs b/tests/unit/out/refcount/fn_ptr_stdlib_compare.rs new file mode 100644 index 00000000..5c10309f --- /dev/null +++ b/tests/unit/out/refcount/fn_ptr_stdlib_compare.rs @@ -0,0 +1,49 @@ +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 rfn: Value) -> u64>> = + Rc::new(RefCell::new(fn_ptr!( + rules::stdio_tgt_refcount::f5, + fn(AnyPtr, u64, u64, Ptr<::std::fs::File>) -> u64 + ))); + assert!({ + let _lhs = (*rfn.borrow()).clone(); + _lhs == fn_ptr!( + rules::stdio_tgt_refcount::f5, + fn(AnyPtr, u64, u64, Ptr<::std::fs::File>) -> u64 + ) + }); + assert!(!((*rfn.borrow()).is_null())); + let rfn2: Value, u64, u64, AnyPtr) -> u64>> = Rc::new(RefCell::new( + fn_ptr!( + rules::stdio_tgt_refcount::f5, + fn(AnyPtr, u64, u64, Ptr<::std::fs::File>) -> u64 + ) + .cast::, u64, u64, AnyPtr) -> u64>(Some( + (|a0: Ptr, a1: u64, a2: u64, a3: AnyPtr| -> u64 { + rules::stdio_tgt_refcount::f5( + a0.to_any(), + a1, + a2, + a3.cast::<::std::fs::File>().unwrap(), + ) + }) as fn(Ptr, u64, u64, AnyPtr) -> u64, + )), + )); + assert!({ + let _lhs = (*rfn.borrow()).clone(); + _lhs == ((*rfn2.borrow()).cast::) -> u64>(None)) + .clone() + }); + return 0; +} diff --git a/tests/unit/out/unsafe/fn_ptr_stdlib_compare.rs b/tests/unit/out/unsafe/fn_ptr_stdlib_compare.rs new file mode 100644 index 00000000..cf09b6b4 --- /dev/null +++ b/tests/unit/out/unsafe/fn_ptr_stdlib_compare.rs @@ -0,0 +1,33 @@ +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 rfn: Option u64> = + Some(rules::stdio_tgt_unsafe::f5 as _); + assert!(((rfn) == (Some(rules::stdio_tgt_unsafe::f5 as _)))); + assert!(!((rfn).is_none())); + let mut rfn2: Option u64> = + std::mem::transmute::< + Option u64>, + Option u64>, + >(Some(rules::stdio_tgt_unsafe::f5 as _)); + assert!( + ((rfn) + == (std::mem::transmute::< + Option u64>, + Option u64>, + >(rfn2))) + ); + return 0; +} From 06b73fdfab9c536df076be2a2074b11bf1eb72ca Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Wed, 15 Apr 2026 11:45:44 +0100 Subject: [PATCH 22/56] Change to deafult with suffix --- cpp2rust/converter/converter.cpp | 4 ++-- tests/benchmarks/out/unsafe/bfs.rs | 6 +++--- tests/ub/out/unsafe/ub10.rs | 2 +- tests/ub/out/unsafe/ub14.rs | 2 +- tests/ub/out/unsafe/ub15.rs | 2 +- tests/ub/out/unsafe/ub16.rs | 2 +- tests/ub/out/unsafe/ub20.rs | 2 +- tests/ub/out/unsafe/ub9.rs | 2 +- tests/unit/out/unsafe/06_new_array.rs | 3 +-- tests/unit/out/unsafe/clone_vs_move.rs | 4 ++-- tests/unit/out/unsafe/default.rs | 2 +- tests/unit/out/unsafe/fn_ptr_struct.rs | 2 +- tests/unit/out/unsafe/fn_ptr_vtable.rs | 2 +- tests/unit/out/unsafe/init.rs | 2 +- tests/unit/out/unsafe/init_list.rs | 4 ++-- tests/unit/out/unsafe/memset.rs | 2 +- tests/unit/out/unsafe/new_alloc_array.rs | 2 +- tests/unit/out/unsafe/new_array.rs | 2 +- tests/unit/out/unsafe/new_array_var_size.rs | 10 +++------- tests/unit/out/unsafe/pointer_call_offset.rs | 2 +- tests/unit/out/unsafe/random.rs | 6 +++--- tests/unit/out/unsafe/reinterpret_cast_large_array.rs | 2 +- tests/unit/out/unsafe/reinterpret_cast_new_array.rs | 2 +- tests/unit/out/unsafe/stdcopy.rs | 2 +- tests/unit/out/unsafe/swap_extended.rs | 2 +- tests/unit/out/unsafe/unique_ptr.rs | 4 ++-- tests/unit/out/unsafe/vector2.rs | 2 +- 27 files changed, 37 insertions(+), 42 deletions(-) diff --git a/cpp2rust/converter/converter.cpp b/cpp2rust/converter/converter.cpp index 112a26e4..8586ffab 100644 --- a/cpp2rust/converter/converter.cpp +++ b/cpp2rust/converter/converter.cpp @@ -2733,11 +2733,11 @@ std::string Converter::GetDefaultAsStringFallback(clang::QualType qual_type) { } if (qual_type->isIntegerType()) { - return std::format("0 as {}", ToString(qual_type)); + return std::format("0_{}", ToString(qual_type)); } if (qual_type->isFloatingType()) { - return std::format("0.0 as {}", ToString(qual_type)); + return std::format("0.0_{}", ToString(qual_type)); } return std::format("<{}>::default()", ToString(qual_type)); diff --git a/tests/benchmarks/out/unsafe/bfs.rs b/tests/benchmarks/out/unsafe/bfs.rs index ae9be6fd..3a114e2b 100644 --- a/tests/benchmarks/out/unsafe/bfs.rs +++ b/tests/benchmarks/out/unsafe/bfs.rs @@ -62,7 +62,7 @@ pub unsafe fn BFS_0(graph: *const Graph, mut start_vertex: u32) -> *mut u32 { let mut Q: Queue = Queue { elems: Box::leak( (0..((*graph).V as u64)) - .map(|_| ::default()) + .map(|_| 0 as u32) .collect::>(), ) .as_mut_ptr(), @@ -72,13 +72,13 @@ pub unsafe fn BFS_0(graph: *const Graph, mut start_vertex: u32) -> *mut u32 { }; let mut visited: *mut bool = Box::leak( (0..((*graph).V as u64)) - .map(|_| ::default()) + .map(|_| false) .collect::>(), ) .as_mut_ptr(); let mut pred: *mut u32 = Box::leak( (0..((*graph).V as u64)) - .map(|_| ::default()) + .map(|_| 0 as u32) .collect::>(), ) .as_mut_ptr(); diff --git a/tests/ub/out/unsafe/ub10.rs b/tests/ub/out/unsafe/ub10.rs index e60c673d..f684d128 100644 --- a/tests/ub/out/unsafe/ub10.rs +++ b/tests/ub/out/unsafe/ub10.rs @@ -14,7 +14,7 @@ pub fn main() { } unsafe fn main_0() -> i32 { let mut arr: *mut i32 = - Box::leak((0..10_u64).map(|_| 0 as i32).collect::>()).as_mut_ptr(); + Box::leak((0..10_u64).map(|_| 0_i32).collect::>()).as_mut_ptr(); let mut ptr: *mut i32 = arr.offset((1) as isize); let mut out: i32 = (*ptr); diff --git a/tests/ub/out/unsafe/ub14.rs b/tests/ub/out/unsafe/ub14.rs index 7a213611..61c89588 100644 --- a/tests/ub/out/unsafe/ub14.rs +++ b/tests/ub/out/unsafe/ub14.rs @@ -14,7 +14,7 @@ pub fn main() { } unsafe fn main_0() -> i32 { let mut arr1: *mut i32 = - Box::leak((0..100_u64).map(|_| 0 as i32).collect::>()).as_mut_ptr(); + Box::leak((0..100_u64).map(|_| 0_i32).collect::>()).as_mut_ptr(); (*arr1.offset((100) as isize)) = 1; ::std::mem::drop(Box::from_raw(::std::slice::from_raw_parts_mut( diff --git a/tests/ub/out/unsafe/ub15.rs b/tests/ub/out/unsafe/ub15.rs index b7ed8446..669d188a 100644 --- a/tests/ub/out/unsafe/ub15.rs +++ b/tests/ub/out/unsafe/ub15.rs @@ -14,7 +14,7 @@ pub fn main() { } unsafe fn main_0() -> i32 { let mut arr: *mut i32 = - Box::leak((0..15_u64).map(|_| 0 as i32).collect::>()).as_mut_ptr(); + Box::leak((0..15_u64).map(|_| 0_i32).collect::>()).as_mut_ptr(); let mut ptr: *mut i32 = arr.offset((15) as isize); let mut out: i32 = (*ptr); diff --git a/tests/ub/out/unsafe/ub16.rs b/tests/ub/out/unsafe/ub16.rs index b558d411..a4c801eb 100644 --- a/tests/ub/out/unsafe/ub16.rs +++ b/tests/ub/out/unsafe/ub16.rs @@ -17,7 +17,7 @@ pub fn main() { } unsafe fn main_0() -> i32 { let mut p1: *mut i32 = - Box::leak((0..10_u64).map(|_| 0 as i32).collect::>()).as_mut_ptr(); + Box::leak((0..10_u64).map(|_| 0_i32).collect::>()).as_mut_ptr(); let mut out: i32 = (*(unsafe { let _a: *mut i32 = (&mut (*p1.offset((1) as isize)) as *mut i32); foo_0(_a) diff --git a/tests/ub/out/unsafe/ub20.rs b/tests/ub/out/unsafe/ub20.rs index b57376fa..053b3532 100644 --- a/tests/ub/out/unsafe/ub20.rs +++ b/tests/ub/out/unsafe/ub20.rs @@ -17,7 +17,7 @@ pub fn main() { } unsafe fn main_0() -> i32 { let mut x: *mut i32 = - Box::leak((0..10_u64).map(|_| 0 as i32).collect::>()).as_mut_ptr(); + Box::leak((0..10_u64).map(|_| 0_i32).collect::>()).as_mut_ptr(); (unsafe { let _single: *mut i32 = x; foo_0(_single) diff --git a/tests/ub/out/unsafe/ub9.rs b/tests/ub/out/unsafe/ub9.rs index f8206b71..8f574181 100644 --- a/tests/ub/out/unsafe/ub9.rs +++ b/tests/ub/out/unsafe/ub9.rs @@ -14,7 +14,7 @@ pub fn main() { } unsafe fn main_0() -> i32 { let mut arr: *mut i32 = - Box::leak((0..10_u64).map(|_| 0 as i32).collect::>()).as_mut_ptr(); + Box::leak((0..10_u64).map(|_| 0_i32).collect::>()).as_mut_ptr(); let mut out: i32 = (*arr.offset((10) as isize)); ::std::mem::drop(Box::from_raw(::std::slice::from_raw_parts_mut( diff --git a/tests/unit/out/unsafe/06_new_array.rs b/tests/unit/out/unsafe/06_new_array.rs index fdc11d3e..d7bb799c 100644 --- a/tests/unit/out/unsafe/06_new_array.rs +++ b/tests/unit/out/unsafe/06_new_array.rs @@ -13,8 +13,7 @@ pub fn main() { } } unsafe fn main_0() -> i32 { - let mut e: *mut i32 = - Box::leak((0..2_u64).map(|_| 0 as i32).collect::>()).as_mut_ptr(); + let mut e: *mut i32 = Box::leak((0..2_u64).map(|_| 0_i32).collect::>()).as_mut_ptr(); (*e.offset((0) as isize)) = 6; (*e.offset((1) as isize)) = 7; diff --git a/tests/unit/out/unsafe/clone_vs_move.rs b/tests/unit/out/unsafe/clone_vs_move.rs index 065ba07c..d0fcde2a 100644 --- a/tests/unit/out/unsafe/clone_vs_move.rs +++ b/tests/unit/out/unsafe/clone_vs_move.rs @@ -22,10 +22,10 @@ pub struct Foo { impl Default for Foo { fn default() -> Self { Foo { - x: 0 as i32, + x: 0_i32, y: <*mut i32>::default(), z: Default::default(), - a: [0 as i32; 3], + a: [0_i32; 3], bar: ::default(), } } diff --git a/tests/unit/out/unsafe/default.rs b/tests/unit/out/unsafe/default.rs index 990bf336..952ccd85 100644 --- a/tests/unit/out/unsafe/default.rs +++ b/tests/unit/out/unsafe/default.rs @@ -22,7 +22,7 @@ impl Default for Pointers { x2: Default::default(), x3: [Default::default(); 5], x4: [Default::default(); 10], - x5: 0 as i32, + x5: 0_i32, } } } diff --git a/tests/unit/out/unsafe/fn_ptr_struct.rs b/tests/unit/out/unsafe/fn_ptr_struct.rs index fc5a1070..277b226d 100644 --- a/tests/unit/out/unsafe/fn_ptr_struct.rs +++ b/tests/unit/out/unsafe/fn_ptr_struct.rs @@ -15,7 +15,7 @@ pub struct Handler { impl Default for Handler { fn default() -> Self { Handler { - tag: 0 as i32, + tag: 0_i32, cb: None, } } diff --git a/tests/unit/out/unsafe/fn_ptr_vtable.rs b/tests/unit/out/unsafe/fn_ptr_vtable.rs index 677cceb0..98298086 100644 --- a/tests/unit/out/unsafe/fn_ptr_vtable.rs +++ b/tests/unit/out/unsafe/fn_ptr_vtable.rs @@ -22,7 +22,7 @@ impl Default for Vtable { } } } -pub static mut storage: i32 = 0 as i32; +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); diff --git a/tests/unit/out/unsafe/init.rs b/tests/unit/out/unsafe/init.rs index e7675385..5e615764 100644 --- a/tests/unit/out/unsafe/init.rs +++ b/tests/unit/out/unsafe/init.rs @@ -20,7 +20,7 @@ pub fn main() { } } unsafe fn main_0() -> i32 { - let mut x: i32 = 0 as i32; + let mut x: i32 = 0_i32; let mut p: *mut i32 = Default::default(); let g: *mut i32 = &mut x as *mut i32; let mut q: *mut i32 = (&mut x as *mut i32); diff --git a/tests/unit/out/unsafe/init_list.rs b/tests/unit/out/unsafe/init_list.rs index 1d5e27e2..1f6a2b52 100644 --- a/tests/unit/out/unsafe/init_list.rs +++ b/tests/unit/out/unsafe/init_list.rs @@ -15,9 +15,9 @@ pub fn main() { } unsafe fn main_0() -> i32 { let mut i1: i32 = 3; - let mut i2: i32 = 0 as i32; + let mut i2: i32 = 0_i32; let mut carr1: [i32; 2] = [1, 2]; - let mut carr2: [i32; 3] = [1, 0 as i32, 0 as i32]; + let mut carr2: [i32; 3] = [1, 0_i32, 0_i32]; let mut arr: Vec = vec![1, 2, 3]; let mut vec_: Vec = vec![1, 2, 3]; (unsafe { diff --git a/tests/unit/out/unsafe/memset.rs b/tests/unit/out/unsafe/memset.rs index 26eb5e6c..284f9765 100644 --- a/tests/unit/out/unsafe/memset.rs +++ b/tests/unit/out/unsafe/memset.rs @@ -15,7 +15,7 @@ pub fn main() { unsafe fn main_0() -> i32 { let N: i32 = 3; let mut arr: *mut i32 = - Box::leak((0..(N as u64)).map(|_| 0 as i32).collect::>()).as_mut_ptr(); + Box::leak((0..(N as u64)).map(|_| 0_i32).collect::>()).as_mut_ptr(); { let byte_0 = (arr as *mut i32 as *mut ::libc::c_void) as *mut u8; for offset in 0..(::std::mem::size_of::() as u64 as u64).wrapping_mul((N as u64)) { diff --git a/tests/unit/out/unsafe/new_alloc_array.rs b/tests/unit/out/unsafe/new_alloc_array.rs index 5a83f057..1814e52a 100644 --- a/tests/unit/out/unsafe/new_alloc_array.rs +++ b/tests/unit/out/unsafe/new_alloc_array.rs @@ -14,7 +14,7 @@ pub fn main() { } unsafe fn main_0() -> i32 { let mut array: *mut i32 = - Box::leak((0..100_u64).map(|_| 0 as i32).collect::>()).as_mut_ptr(); + Box::leak((0..100_u64).map(|_| 0_i32).collect::>()).as_mut_ptr(); { let byte_0 = (array as *mut i32 as *mut ::libc::c_void) as *mut u8; for offset in 0..(::std::mem::size_of::() as u64 as u64).wrapping_mul(100_u64) { diff --git a/tests/unit/out/unsafe/new_array.rs b/tests/unit/out/unsafe/new_array.rs index b73aea86..514fc12c 100644 --- a/tests/unit/out/unsafe/new_array.rs +++ b/tests/unit/out/unsafe/new_array.rs @@ -14,7 +14,7 @@ pub fn main() { } unsafe fn main_0() -> i32 { let mut array: *mut i32 = - Box::leak((0..100_u64).map(|_| 0 as i32).collect::>()).as_mut_ptr(); + Box::leak((0..100_u64).map(|_| 0_i32).collect::>()).as_mut_ptr(); ::std::mem::drop(Box::from_raw(::std::slice::from_raw_parts_mut( array, diff --git a/tests/unit/out/unsafe/new_array_var_size.rs b/tests/unit/out/unsafe/new_array_var_size.rs index ff403a2c..f9be6146 100644 --- a/tests/unit/out/unsafe/new_array_var_size.rs +++ b/tests/unit/out/unsafe/new_array_var_size.rs @@ -15,19 +15,15 @@ pub fn main() { unsafe fn main_0() -> i32 { let mut N: i32 = 5; let mut A: *mut i32 = - Box::leak((0..(N as u64)).map(|_| 0 as i32).collect::>()).as_mut_ptr(); + Box::leak((0..(N as u64)).map(|_| 0_i32).collect::>()).as_mut_ptr(); ::std::mem::drop(Box::from_raw(::std::slice::from_raw_parts_mut( A, libcc2rs::malloc_usable_size(A as *mut ::libc::c_void) / ::std::mem::size_of::(), ))); let N2: *mut i32 = &mut N as *mut i32; - let mut A2: *mut i32 = Box::leak( - (0..((*N2) as u64)) - .map(|_| 0 as i32) - .collect::>(), - ) - .as_mut_ptr(); + let mut A2: *mut i32 = + Box::leak((0..((*N2) as u64)).map(|_| 0_i32).collect::>()).as_mut_ptr(); ::std::mem::drop(Box::from_raw(::std::slice::from_raw_parts_mut( A2, diff --git a/tests/unit/out/unsafe/pointer_call_offset.rs b/tests/unit/out/unsafe/pointer_call_offset.rs index 8d9472e4..76f2a3d8 100644 --- a/tests/unit/out/unsafe/pointer_call_offset.rs +++ b/tests/unit/out/unsafe/pointer_call_offset.rs @@ -17,7 +17,7 @@ pub fn main() { } unsafe fn main_0() -> i32 { let mut p1: *mut i32 = - Box::leak((0..10_u64).map(|_| 0 as i32).collect::>()).as_mut_ptr(); + Box::leak((0..10_u64).map(|_| 0_i32).collect::>()).as_mut_ptr(); let mut i: u32 = 0_u32; 'loop_: while ((i) < (10_u32)) { (*p1.offset((i) as isize)) = (i as i32); diff --git a/tests/unit/out/unsafe/random.rs b/tests/unit/out/unsafe/random.rs index 82133af1..05efa2b1 100644 --- a/tests/unit/out/unsafe/random.rs +++ b/tests/unit/out/unsafe/random.rs @@ -40,9 +40,9 @@ impl Pair { impl Default for Pair { fn default() -> Self { Pair { - x: 0 as i32, - y: 0 as i32, - a: [0 as i32; 5], + x: 0_i32, + y: 0_i32, + a: [0_i32; 5], r: <*mut i32>::default(), p: Default::default(), pair: Default::default(), diff --git a/tests/unit/out/unsafe/reinterpret_cast_large_array.rs b/tests/unit/out/unsafe/reinterpret_cast_large_array.rs index 5344c2be..d3e0fd8e 100644 --- a/tests/unit/out/unsafe/reinterpret_cast_large_array.rs +++ b/tests/unit/out/unsafe/reinterpret_cast_large_array.rs @@ -15,7 +15,7 @@ pub fn main() { unsafe fn main_0() -> i32 { let N: i32 = 10000; let mut arr: *mut u32 = - Box::leak((0..(N as u64)).map(|_| 0 as u32).collect::>()).as_mut_ptr(); + Box::leak((0..(N as u64)).map(|_| 0_u32).collect::>()).as_mut_ptr(); let mut i: i32 = 0; 'loop_: while ((i) < (N)) { (*arr.offset((i) as isize)) = 0_u32; diff --git a/tests/unit/out/unsafe/reinterpret_cast_new_array.rs b/tests/unit/out/unsafe/reinterpret_cast_new_array.rs index f4e567b8..3a71d5be 100644 --- a/tests/unit/out/unsafe/reinterpret_cast_new_array.rs +++ b/tests/unit/out/unsafe/reinterpret_cast_new_array.rs @@ -14,7 +14,7 @@ pub fn main() { } unsafe fn main_0() -> i32 { let mut arr: *mut u32 = - Box::leak((0..2_u64).map(|_| 0 as u32).collect::>()).as_mut_ptr(); + Box::leak((0..2_u64).map(|_| 0_u32).collect::>()).as_mut_ptr(); (*arr.offset((0) as isize)) = 67305985_u32; (*arr.offset((1) as isize)) = 134678021_u32; let mut bytes: *mut u8 = (arr as *mut u8); diff --git a/tests/unit/out/unsafe/stdcopy.rs b/tests/unit/out/unsafe/stdcopy.rs index 706e5fb6..15c58095 100644 --- a/tests/unit/out/unsafe/stdcopy.rs +++ b/tests/unit/out/unsafe/stdcopy.rs @@ -14,7 +14,7 @@ pub fn main() { } unsafe fn main_0() -> i32 { let mut input: [i32; 3] = [1, 2, 3]; - let mut output: [i32; 3] = [0 as i32; 3]; + let mut output: [i32; 3] = [0_i32; 3]; { let mut outptr = output.as_mut_ptr().clone(); let mut curr = input.as_mut_ptr().clone(); diff --git a/tests/unit/out/unsafe/swap_extended.rs b/tests/unit/out/unsafe/swap_extended.rs index 8aae96cc..d1e3a3b4 100644 --- a/tests/unit/out/unsafe/swap_extended.rs +++ b/tests/unit/out/unsafe/swap_extended.rs @@ -94,7 +94,7 @@ unsafe fn main_0() -> i32 { (*h), ); ::std::mem::drop(Box::from_raw(h)); - let mut i: *mut i32 = Box::leak(Box::new([7, 8, 0 as i32])).as_mut_ptr(); + let mut i: *mut i32 = Box::leak(Box::new([7, 8, 0_i32])).as_mut_ptr(); write!( std::fs::File::from_raw_fd( std::io::stdout() diff --git a/tests/unit/out/unsafe/unique_ptr.rs b/tests/unit/out/unsafe/unique_ptr.rs index a94bb752..73a1a4ba 100644 --- a/tests/unit/out/unsafe/unique_ptr.rs +++ b/tests/unit/out/unsafe/unique_ptr.rs @@ -72,7 +72,7 @@ pub unsafe fn Consume_1(mut safe_ptr: Option>) -> i32 { pub unsafe fn RndStuff_2() { let mut x1: Option> = None; let mut x2: Option> = Some(Box::from_raw(Box::leak( - (0..100_u64).map(|_| 0 as i32).collect::>(), + (0..100_u64).map(|_| 0_i32).collect::>(), ))); let mut i: i32 = 0; 'loop_: while ((i) < (100)) { @@ -80,7 +80,7 @@ pub unsafe fn RndStuff_2() { i.prefix_inc(); } x2 = Some(Box::from_raw(Box::leak( - (0..200_u64).map(|_| 0 as i32).collect::>(), + (0..200_u64).map(|_| 0_i32).collect::>(), ))); let mut i: i32 = 0; 'loop_: while ((i) < (200)) { diff --git a/tests/unit/out/unsafe/vector2.rs b/tests/unit/out/unsafe/vector2.rs index 640b7624..dd5b4d4e 100644 --- a/tests/unit/out/unsafe/vector2.rs +++ b/tests/unit/out/unsafe/vector2.rs @@ -9,7 +9,7 @@ use std::os::fd::{AsFd, FromRawFd, IntoRawFd}; use std::rc::Rc; pub unsafe fn fn_0(v: *mut Vec, mut v3: Vec) { (*v).push(20); - let mut x: i32 = 0 as i32; + let mut x: i32 = 0_i32; let mut v2: Vec = Vec::new(); let mut v4: *mut Vec = (&mut v3 as *mut Vec); v2.push(0); From 942bde3faddc1d0a0285bc76983ae833fccc237f Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Wed, 15 Apr 2026 11:45:54 +0100 Subject: [PATCH 23/56] Make rules public --- rules/src/lib.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/rules/src/lib.rs b/rules/src/lib.rs index 9efa0643..44df340b 100644 --- a/rules/src/lib.rs +++ b/rules/src/lib.rs @@ -3,3 +3,4 @@ #![allow(warnings)] mod modules; +pub use modules::*; From 03e7ab77396bad0563c853f245e4f16e1682245c Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Wed, 15 Apr 2026 11:46:04 +0100 Subject: [PATCH 24/56] cargo-fmt --- rules/stdio/tgt_unsafe.rs | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/rules/stdio/tgt_unsafe.rs b/rules/stdio/tgt_unsafe.rs index ae324d5f..d0bf8820 100644 --- a/rules/stdio/tgt_unsafe.rs +++ b/rules/stdio/tgt_unsafe.rs @@ -65,12 +65,7 @@ unsafe fn f4(a0: *mut ::std::fs::File, a1: i64, a2: i32) -> i32 { } } -pub unsafe fn f5( - a0: *mut ::libc::c_void, - a1: u64, - a2: u64, - a3: *mut ::std::fs::File, -) -> u64 { +pub unsafe fn f5(a0: *mut ::libc::c_void, a1: u64, a2: u64, a3: *mut ::std::fs::File) -> u64 { let total = a1.saturating_mul(a2) as usize; let mut dst = a0 as *mut u8; @@ -102,12 +97,7 @@ pub unsafe fn f5( (read_bytes / a1 as usize) as u64 } -pub unsafe fn f6( - a0: *const ::libc::c_void, - a1: u64, - a2: u64, - a3: *mut ::std::fs::File, -) -> u64 { +pub unsafe fn f6(a0: *const ::libc::c_void, a1: u64, a2: u64, a3: *mut ::std::fs::File) -> u64 { let total = a1.saturating_mul(a2) as usize; let mut src = a0 as *mut u8; From a2e1c1a93f05f56fb7fcc12e4a34211db359882d Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Wed, 15 Apr 2026 11:49:58 +0100 Subject: [PATCH 25/56] Delete unused Cargo.toml --- tests/benchmarks/out/unsafe/bfs.rs | 4 ++-- tests/unit/Cargo.toml | 14 -------------- 2 files changed, 2 insertions(+), 16 deletions(-) delete mode 100644 tests/unit/Cargo.toml diff --git a/tests/benchmarks/out/unsafe/bfs.rs b/tests/benchmarks/out/unsafe/bfs.rs index 3a114e2b..956d7541 100644 --- a/tests/benchmarks/out/unsafe/bfs.rs +++ b/tests/benchmarks/out/unsafe/bfs.rs @@ -62,7 +62,7 @@ pub unsafe fn BFS_0(graph: *const Graph, mut start_vertex: u32) -> *mut u32 { let mut Q: Queue = Queue { elems: Box::leak( (0..((*graph).V as u64)) - .map(|_| 0 as u32) + .map(|_| 0_u32) .collect::>(), ) .as_mut_ptr(), @@ -78,7 +78,7 @@ pub unsafe fn BFS_0(graph: *const Graph, mut start_vertex: u32) -> *mut u32 { .as_mut_ptr(); let mut pred: *mut u32 = Box::leak( (0..((*graph).V as u64)) - .map(|_| 0 as u32) + .map(|_| 0_u32) .collect::>(), ) .as_mut_ptr(); diff --git a/tests/unit/Cargo.toml b/tests/unit/Cargo.toml deleted file mode 100644 index a8618fe4..00000000 --- a/tests/unit/Cargo.toml +++ /dev/null @@ -1,14 +0,0 @@ -[package] -name = "rs" -version = "0.1.0" -edition = "2021" - -[dependencies] -libc = "0.2.153" -libcc2rs = { path = "../../../libcc2rs" } - -# Any project that uses rust-analyzer with the rustc_private -# crates must set [package.metadata.rust-analyzer] rustc_private=true to use it. - -[package.metadata.rust-analyzer] -rustc_private=true From 1c1ab7d3a783e248080e3425d2cb6027f4879bc5 Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Wed, 15 Apr 2026 13:28:58 +0100 Subject: [PATCH 26/56] Use prebuilt rules and libcc2rs targets --- tests/lit/lit/formats/Cpp2RustTest.py | 36 +++++++++++++++++++-------- 1 file changed, 26 insertions(+), 10 deletions(-) diff --git a/tests/lit/lit/formats/Cpp2RustTest.py b/tests/lit/lit/formats/Cpp2RustTest.py index 292eedcb..5fafe0cd 100644 --- a/tests/lit/lit/formats/Cpp2RustTest.py +++ b/tests/lit/lit/formats/Cpp2RustTest.py @@ -7,21 +7,29 @@ import difflib, os, re, shutil, random import tomli -def read_rust_version(): - toolchain_path = os.path.join(os.path.dirname(__file__), - '../../../../libcc2rs/rust-toolchain.toml') - with open(toolchain_path, 'rb') as f: - return tomli.load(f)['toolchain']['channel'] - class Cpp2RustTest(TestFormat): def __init__(self): self.regex_xfail = re.compile(r"//\s*XFAIL:\s*(.*)") self.regex_panic = re.compile(r"//\s*panic\s*(?::\s*(.*))?$", re.MULTILINE) self.regex_nocompile = re.compile(r"//\s*no-compile\s*(?::\s*(.*))?$", re.MULTILINE) self.regex_nondet_result = re.compile(r"//\s*nondet-result\s*(?::\s*(.*))?$", re.MULTILINE) - self.rust_version = read_rust_version() + self.rust_version = self.readRustVersion() os.environ['RUSTFLAGS'] = '-Awarnings -A dangerous-implicit-autorefs' + def readRustVersion(self): + toolchain_path = os.path.join(os.path.dirname(__file__), + '../../../../libcc2rs/rust-toolchain.toml') + with open(toolchain_path, 'rb') as f: + return tomli.load(f)['toolchain']['channel'] + + def cargoEnv(self): + return dict(os.environ, CARGO_TARGET_DIR=os.path.abspath(self.sharedTargetDir())) + + def sharedTargetDir(self): + return os.path.abspath(os.path.join( + os.path.dirname(__file__), + '../../../../build/tmp/cargo-target')) + def updateExpected(self, generated, expected_path): os.makedirs(os.path.dirname(expected_path), exist_ok=True) with open(expected_path, 'w') as f: @@ -120,24 +128,32 @@ def fail(str, code = fail_code): fromfile='expected', tofile='generated')) return fail('different output\n' + diff) + pkg_name = "test_" + re.sub(r'[^a-zA-Z0-9_]', '_', + os.path.basename(tmp_dir)) + # Check if we can compile the rust file with open(tmp_dir + "/rust-toolchain.toml", 'w') as f: f.write(f'[toolchain]\nchannel = "{self.rust_version}"\n') with open(tmp_dir + "/Cargo.toml", 'w') as f: f.write(f""" [package] -name = "test" +name = "{pkg_name}" version = "0.1.0" edition = "2021" rust-version = "{self.rust_version}" +[[bin]] +name = "{pkg_name}" +path = "src/main.rs" + [dependencies] libc = "0.2.169" libcc2rs = {{ path = "../../../libcc2rs" }} +rules = {{ path = "../../../rules" }} """) cmd = ['cargo', 'build', '--release', '--quiet'] - _, err, returncode = lit.util.executeCommand(cmd, tmp_dir) + _, err, returncode = lit.util.executeCommand(cmd, tmp_dir, env=self.cargoEnv()) if should_not_compile: if returncode != 0: shutil.rmtree(tmp_dir, True) @@ -146,7 +162,7 @@ def fail(str, code = fail_code): if returncode != 0: return fail('cargo failed\n' + err) - rust_bin = tmp_dir + "/target/release/test" + rust_bin = os.path.join(self.sharedTargetDir(), "release", pkg_name) if not skip_run: if should_panic: From 840863d017b5c03d7fe563e4cd04ec858fe7f485 Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Wed, 15 Apr 2026 13:47:21 +0100 Subject: [PATCH 27/56] Implement Deref for FnPtr --- cpp2rust/converter/converter.cpp | 1 - .../converter/models/converter_refcount.cpp | 7 +++---- libcc2rs/src/fn_ptr.rs | 12 +++++++----- tests/unit/out/refcount/fn_ptr.rs | 2 +- tests/unit/out/refcount/fn_ptr_array.rs | 6 +++--- tests/unit/out/refcount/fn_ptr_as_condition.rs | 4 ++-- tests/unit/out/refcount/fn_ptr_cast.rs | 10 +++++----- tests/unit/out/refcount/fn_ptr_conditional.rs | 17 +++++++---------- tests/unit/out/refcount/fn_ptr_default_arg.rs | 2 +- tests/unit/out/refcount/fn_ptr_global.rs | 2 +- tests/unit/out/refcount/fn_ptr_reassign.rs | 8 ++++---- tests/unit/out/refcount/fn_ptr_return.rs | 4 ++-- tests/unit/out/refcount/fn_ptr_struct.rs | 6 +++--- tests/unit/out/refcount/fn_ptr_void_return.rs | 4 ++-- tests/unit/out/refcount/fn_ptr_vtable.rs | 6 +++--- tests/unit/out/refcount/no_direct_callee.rs | 2 +- 16 files changed, 45 insertions(+), 48 deletions(-) diff --git a/cpp2rust/converter/converter.cpp b/cpp2rust/converter/converter.cpp index 8586ffab..e8e638a2 100644 --- a/cpp2rust/converter/converter.cpp +++ b/cpp2rust/converter/converter.cpp @@ -131,7 +131,6 @@ bool Converter::VisitRecordType(clang::RecordType *type) { if (auto lambda = clang::dyn_cast(decl)) { if (lambda->isLambda()) { if (in_function_formals_) { - // Function parameters can't use `_`. Emit `impl Fn(args) -> ret`. auto call_op = lambda->getLambdaCallOperator(); StrCat("impl Fn("); for (auto p : call_op->parameters()) { diff --git a/cpp2rust/converter/models/converter_refcount.cpp b/cpp2rust/converter/models/converter_refcount.cpp index b11e1598..bf3160ad 100644 --- a/cpp2rust/converter/models/converter_refcount.cpp +++ b/cpp2rust/converter/models/converter_refcount.cpp @@ -180,10 +180,8 @@ std::string ConverterRefCount::BuildFnAdapter( // Build adapter signature: |a0: T0, a1: T1, ...| -> Tr std::string closure = "(|"; for (unsigned i = 0; i < target_proto->getNumParams(); ++i) { - if (i > 0) - closure += ", "; closure += - std::format("a{}: {}", i, ToString(target_proto->getParamType(i))); + std::format("a{}: {},", i, ToString(target_proto->getParamType(i))); } closure += "|"; if (!target_proto->getReturnType()->isVoidType()) { @@ -1025,8 +1023,9 @@ bool ConverterRefCount::VisitImplicitCastExpr(clang::ImplicitCastExpr *expr) { } void ConverterRefCount::EmitFnPtrCall(clang::Expr *callee) { + StrCat("(*"); Convert(callee); - StrCat(".call()"); + StrCat(")"); } void ConverterRefCount::EmitFnAsValue(const clang::FunctionDecl *fn_decl) { diff --git a/libcc2rs/src/fn_ptr.rs b/libcc2rs/src/fn_ptr.rs index e2d9f6a4..63d0c865 100644 --- a/libcc2rs/src/fn_ptr.rs +++ b/libcc2rs/src/fn_ptr.rs @@ -3,6 +3,7 @@ use std::any::{Any, TypeId}; use std::marker::PhantomData; +use std::ops::Deref; use std::rc::Rc; use crate::rc::{AnyPtr, ErasedPtr}; @@ -75,17 +76,18 @@ impl FnPtr { } } - pub fn call(&self) -> T - where - T: Copy, - { +} + +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 + Some(rc) => rc .downcast_ref::() .expect("ub: fn pointer type mismatch"), None => panic!("ub: calling through incompatible fn pointer type"), diff --git a/tests/unit/out/refcount/fn_ptr.rs b/tests/unit/out/refcount/fn_ptr.rs index 0ef56254..34753e3f 100644 --- a/tests/unit/out/refcount/fn_ptr.rs +++ b/tests/unit/out/refcount/fn_ptr.rs @@ -16,7 +16,7 @@ pub fn foo_1(fn_: FnPtr i32>, pi: Ptr) -> i32 { let pi: Value> = Rc::new(RefCell::new(pi)); return ({ let _arg0: AnyPtr = ((*pi.borrow()).clone() as Ptr).to_any(); - (*fn_.borrow()).call()(_arg0) + (*(*fn_.borrow()))(_arg0) }); } pub fn main() { diff --git a/tests/unit/out/refcount/fn_ptr_array.rs b/tests/unit/out/refcount/fn_ptr_array.rs index 8e1d5fca..e524cc73 100644 --- a/tests/unit/out/refcount/fn_ptr_array.rs +++ b/tests/unit/out/refcount/fn_ptr_array.rs @@ -35,21 +35,21 @@ fn main_0() -> i32 { (({ let _arg0: i32 = 2; let _arg1: i32 = 3; - (*ops.borrow())[(0) as usize].call()(_arg0, _arg1) + (*(*ops.borrow())[(0) as usize])(_arg0, _arg1) }) == 5) ); assert!( (({ let _arg0: i32 = 7; let _arg1: i32 = 4; - (*ops.borrow())[(1) as usize].call()(_arg0, _arg1) + (*(*ops.borrow())[(1) as usize])(_arg0, _arg1) }) == 3) ); assert!( (({ let _arg0: i32 = 6; let _arg1: i32 = 5; - (*ops.borrow())[(2) as usize].call()(_arg0, _arg1) + (*(*ops.borrow())[(2) as usize])(_arg0, _arg1) }) == 30) ); assert!(!(((*ops.borrow())[(0) as usize]).is_null())); diff --git a/tests/unit/out/refcount/fn_ptr_as_condition.rs b/tests/unit/out/refcount/fn_ptr_as_condition.rs index c6ffacfd..5afb76cf 100644 --- a/tests/unit/out/refcount/fn_ptr_as_condition.rs +++ b/tests/unit/out/refcount/fn_ptr_as_condition.rs @@ -21,7 +21,7 @@ pub fn maybe_call_1(cb: FnPtr)>, x: Ptr) { if !(*cb.borrow()).is_null() { ({ let _arg0: Ptr = (*x.borrow()).clone(); - (*cb.borrow()).call()(_arg0) + (*(*cb.borrow()))(_arg0) }); } } @@ -51,7 +51,7 @@ fn main_0() -> i32 { if !(*fn_.borrow()).is_null() { ({ let _arg0: Ptr = (c.as_pointer()); - (*fn_.borrow()).call()(_arg0) + (*(*fn_.borrow()))(_arg0) }); } assert!(((*c.borrow()) == 6)); diff --git a/tests/unit/out/refcount/fn_ptr_cast.rs b/tests/unit/out/refcount/fn_ptr_cast.rs index c33b2bd3..400cf447 100644 --- a/tests/unit/out/refcount/fn_ptr_cast.rs +++ b/tests/unit/out/refcount/fn_ptr_cast.rs @@ -17,7 +17,7 @@ pub fn test_roundtrip_1() { assert!( (({ let _arg0: i32 = 5; - (*fn_.borrow()).call()(_arg0) + (*(*fn_.borrow()))(_arg0) }) == 10) ); let gfn: Value> = @@ -29,7 +29,7 @@ pub fn test_roundtrip_1() { assert!( (({ let _arg0: i32 = 5; - (*fn2.borrow()).call()(_arg0) + (*(*fn2.borrow()))(_arg0) }) == 10) ); assert!({ @@ -49,7 +49,7 @@ pub fn test_double_cast_2() { assert!( (({ let _arg0: i32 = 5; - (*fn2.borrow()).call()(_arg0) + (*(*fn2.borrow()))(_arg0) }) == 10) ); assert!({ @@ -82,7 +82,7 @@ pub fn test_void_ptr_to_fn_3() { assert!( (({ let _arg0: i32 = 5; - (*fn_.borrow()).call()(_arg0) + (*(*fn_.borrow()))(_arg0) }) == 10) ); } @@ -106,7 +106,7 @@ pub fn test_call_through_cast_5() { ({ let _arg0: AnyPtr = ((val.as_pointer()) as Ptr).to_any(); let _arg1: i32 = 42; - (*gfn.borrow()).call()(_arg0, _arg1) + (*(*gfn.borrow()))(_arg0, _arg1) }), )); assert!(((*result.borrow()) == 142)); diff --git a/tests/unit/out/refcount/fn_ptr_conditional.rs b/tests/unit/out/refcount/fn_ptr_conditional.rs index c6e577aa..88684794 100644 --- a/tests/unit/out/refcount/fn_ptr_conditional.rs +++ b/tests/unit/out/refcount/fn_ptr_conditional.rs @@ -42,7 +42,7 @@ pub fn apply_4(fn_: FnPtr i32>, x: i32) -> i32 { })); return ({ let _arg0: i32 = (*x.borrow()); - (*actual.borrow()).call()(_arg0) + (*(*actual.borrow()))(_arg0) }); } pub fn main() { @@ -52,31 +52,28 @@ fn main_0() -> i32 { assert!( (({ let _arg0: i32 = 10; - ({ + (*({ let _mode: i32 = 1; pick_3(_mode) - }) - .call()(_arg0) + }))(_arg0) }) == 11) ); assert!( (({ let _arg0: i32 = 10; - ({ + (*({ let _mode: i32 = -1_i32; pick_3(_mode) - }) - .call()(_arg0) + }))(_arg0) }) == 9) ); assert!( (({ let _arg0: i32 = 10; - ({ + (*({ let _mode: i32 = 0; pick_3(_mode) - }) - .call()(_arg0) + }))(_arg0) }) == 10) ); assert!( diff --git a/tests/unit/out/refcount/fn_ptr_default_arg.rs b/tests/unit/out/refcount/fn_ptr_default_arg.rs index 6c6ab799..26b8d9b5 100644 --- a/tests/unit/out/refcount/fn_ptr_default_arg.rs +++ b/tests/unit/out/refcount/fn_ptr_default_arg.rs @@ -17,7 +17,7 @@ pub fn apply_1(x: i32, fn_: Option i32>>) -> i32 { if !(*fn_.borrow()).is_null() { return ({ let _arg0: i32 = (*x.borrow()); - (*fn_.borrow()).call()(_arg0) + (*(*fn_.borrow()))(_arg0) }); } return (*x.borrow()); diff --git a/tests/unit/out/refcount/fn_ptr_global.rs b/tests/unit/out/refcount/fn_ptr_global.rs index af0dca94..94b7d347 100644 --- a/tests/unit/out/refcount/fn_ptr_global.rs +++ b/tests/unit/out/refcount/fn_ptr_global.rs @@ -27,7 +27,7 @@ pub fn call_op_3(x: i32) -> i32 { if !(*g_op.with(Value::clone).borrow()).is_null() { return ({ let _arg0: i32 = (*x.borrow()); - (*g_op.with(Value::clone).borrow()).call()(_arg0) + (*(*g_op.with(Value::clone).borrow()))(_arg0) }); } return (*x.borrow()); diff --git a/tests/unit/out/refcount/fn_ptr_reassign.rs b/tests/unit/out/refcount/fn_ptr_reassign.rs index 123abbec..c8c47f6d 100644 --- a/tests/unit/out/refcount/fn_ptr_reassign.rs +++ b/tests/unit/out/refcount/fn_ptr_reassign.rs @@ -32,7 +32,7 @@ fn main_0() -> i32 { (({ let _arg0: i32 = 3; let _arg1: i32 = 4; - (*fn_.borrow()).call()(_arg0, _arg1) + (*(*fn_.borrow()))(_arg0, _arg1) }) == 7) ); (*fn_.borrow_mut()) = fn_ptr!(sub_1, fn(i32, i32) -> i32); @@ -40,7 +40,7 @@ fn main_0() -> i32 { (({ let _arg0: i32 = 10; let _arg1: i32 = 3; - (*fn_.borrow()).call()(_arg0, _arg1) + (*(*fn_.borrow()))(_arg0, _arg1) }) == 7) ); (*fn_.borrow_mut()) = fn_ptr!(mul_2, fn(i32, i32) -> i32); @@ -48,7 +48,7 @@ fn main_0() -> i32 { (({ let _arg0: i32 = 6; let _arg1: i32 = 7; - (*fn_.borrow()).call()(_arg0, _arg1) + (*(*fn_.borrow()))(_arg0, _arg1) }) == 42) ); (*fn_.borrow_mut()) = FnPtr::null(); @@ -59,7 +59,7 @@ fn main_0() -> i32 { (({ let _arg0: i32 = 1; let _arg1: i32 = 1; - (*fn_.borrow()).call()(_arg0, _arg1) + (*(*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 index 43681494..b80f7c1b 100644 --- a/tests/unit/out/refcount/fn_ptr_return.rs +++ b/tests/unit/out/refcount/fn_ptr_return.rs @@ -40,7 +40,7 @@ fn main_0() -> i32 { assert!( (({ let _arg0: i32 = 10; - (*f.borrow()).call()(_arg0) + (*(*f.borrow()))(_arg0) }) == 11) ); let g: Value i32>> = Rc::new(RefCell::new( @@ -56,7 +56,7 @@ fn main_0() -> i32 { assert!( (({ let _arg0: i32 = 10; - (*g.borrow()).call()(_arg0) + (*(*g.borrow()))(_arg0) }) == 9) ); assert!({ diff --git a/tests/unit/out/refcount/fn_ptr_struct.rs b/tests/unit/out/refcount/fn_ptr_struct.rs index 777a60ee..0d34f1d4 100644 --- a/tests/unit/out/refcount/fn_ptr_struct.rs +++ b/tests/unit/out/refcount/fn_ptr_struct.rs @@ -54,20 +54,20 @@ fn main_0() -> i32 { assert!( (({ let _arg0: i32 = 5; - (*(*h1.borrow()).cb.borrow()).call()(_arg0) + (*(*(*h1.borrow()).cb.borrow()))(_arg0) }) == 10) ); assert!( (({ let _arg0: i32 = 7; - (*(*h2.borrow()).cb.borrow()).call()(_arg0) + (*(*(*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()).call()(_arg0) + (*(*(*h1.borrow()).cb.borrow()))(_arg0) }) == -3_i32) ); assert!({ diff --git a/tests/unit/out/refcount/fn_ptr_void_return.rs b/tests/unit/out/refcount/fn_ptr_void_return.rs index 69fd6b15..dc49639c 100644 --- a/tests/unit/out/refcount/fn_ptr_void_return.rs +++ b/tests/unit/out/refcount/fn_ptr_void_return.rs @@ -21,7 +21,7 @@ pub fn run_2(fn_: FnPtr)>, x: Ptr) { let x: Value> = Rc::new(RefCell::new(x)); ({ let _arg0: Ptr = (*x.borrow()).clone(); - (*fn_.borrow()).call()(_arg0) + (*(*fn_.borrow()))(_arg0) }); } pub fn main() { @@ -46,7 +46,7 @@ fn main_0() -> i32 { let b: Value = Rc::new(RefCell::new(10)); ({ let _arg0: Ptr = (b.as_pointer()); - (*fn_.borrow()).call()(_arg0) + (*(*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 index 7b94350e..b8e80be5 100644 --- a/tests/unit/out/refcount/fn_ptr_vtable.rs +++ b/tests/unit/out/refcount/fn_ptr_vtable.rs @@ -66,18 +66,18 @@ fn main_0() -> i32 { let obj: Value = Rc::new(RefCell::new( ({ let _arg0: i32 = 42; - (*(*vt.borrow()).create.borrow()).call()(_arg0) + (*(*(*vt.borrow()).create.borrow()))(_arg0) }), )); assert!( (({ let _arg0: AnyPtr = (*obj.borrow()).clone(); - (*(*vt.borrow()).get.borrow()).call()(_arg0) + (*(*(*vt.borrow()).get.borrow()))(_arg0) }) == 42) ); ({ let _arg0: AnyPtr = (*obj.borrow()).clone(); - (*(*vt.borrow()).destroy.borrow()).call()(_arg0) + (*(*(*vt.borrow()).destroy.borrow()))(_arg0) }); assert!(((*storage.with(Value::clone).borrow()) == 0)); (*(*vt.borrow()).get.borrow_mut()) = FnPtr::null(); diff --git a/tests/unit/out/refcount/no_direct_callee.rs b/tests/unit/out/refcount/no_direct_callee.rs index 192ee3e8..6b81f983 100644 --- a/tests/unit/out/refcount/no_direct_callee.rs +++ b/tests/unit/out/refcount/no_direct_callee.rs @@ -12,7 +12,7 @@ pub fn test1_0() -> bool { } pub fn test_1(fn_: FnPtr bool>) -> i32 { let fn_: Value bool>> = Rc::new(RefCell::new(fn_)); - if !({ (*fn_.borrow()).call()() }) { + if !({ (*(*fn_.borrow()))() }) { return 1; } return 0; From 3c1b9c7e41c59a815e9001f9a9928d1a6242f7d4 Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Wed, 15 Apr 2026 13:48:37 +0100 Subject: [PATCH 28/56] Update rules IR --- libcc2rs/src/fn_ptr.rs | 1 - rules/stdio/ir_unsafe.json | 4 ++-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/libcc2rs/src/fn_ptr.rs b/libcc2rs/src/fn_ptr.rs index 63d0c865..0cef3106 100644 --- a/libcc2rs/src/fn_ptr.rs +++ b/libcc2rs/src/fn_ptr.rs @@ -75,7 +75,6 @@ impl FnPtr { _marker: PhantomData, } } - } impl Deref for FnPtr { diff --git a/rules/stdio/ir_unsafe.json b/rules/stdio/ir_unsafe.json index 81d8f440..8b55e1ce 100644 --- a/rules/stdio/ir_unsafe.json +++ b/rules/stdio/ir_unsafe.json @@ -618,7 +618,7 @@ "multi_statement": true, "params": { "a0": { - "type": "*mut u8", + "type": "*mut ::libc::c_void", "is_unsafe_pointer": true }, "a1": { @@ -797,7 +797,7 @@ "multi_statement": true, "params": { "a0": { - "type": "*mut u8", + "type": "*const ::libc::c_void", "is_unsafe_pointer": true }, "a1": { From e7fa221ca9c0149af83bd92587b246af081f3a56 Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Wed, 15 Apr 2026 14:21:47 +0100 Subject: [PATCH 29/56] Remove unused fn ptr cast and fn_ptr_anon --- cpp2rust/converter/converter.cpp | 2 +- cpp2rust/converter/models/converter_refcount.cpp | 7 ++----- libcc2rs/src/fn_ptr.rs | 7 +++++++ libcc2rs/src/lib.rs | 16 ---------------- tests/unit/out/refcount/fn_ptr_default_arg.rs | 4 ++-- tests/unit/out/unsafe/fn_ptr.rs | 6 +++--- tests/unit/out/unsafe/fn_ptr_array.rs | 7 +++---- tests/unit/out/unsafe/fn_ptr_as_condition.rs | 4 ++-- tests/unit/out/unsafe/fn_ptr_cast.rs | 15 +++++++-------- tests/unit/out/unsafe/fn_ptr_conditional.rs | 10 +++++----- tests/unit/out/unsafe/fn_ptr_default_arg.rs | 2 +- tests/unit/out/unsafe/fn_ptr_global.rs | 8 ++++---- tests/unit/out/unsafe/fn_ptr_reassign.rs | 8 ++++---- tests/unit/out/unsafe/fn_ptr_return.rs | 8 ++++---- tests/unit/out/unsafe/fn_ptr_stdlib_compare.rs | 6 +++--- tests/unit/out/unsafe/fn_ptr_struct.rs | 6 +++--- tests/unit/out/unsafe/fn_ptr_void_return.rs | 6 +++--- tests/unit/out/unsafe/fn_ptr_vtable.rs | 6 +++--- tests/unit/out/unsafe/no_direct_callee.rs | 2 +- 19 files changed, 58 insertions(+), 72 deletions(-) diff --git a/cpp2rust/converter/converter.cpp b/cpp2rust/converter/converter.cpp index e8e638a2..0df813f4 100644 --- a/cpp2rust/converter/converter.cpp +++ b/cpp2rust/converter/converter.cpp @@ -1381,7 +1381,7 @@ void Converter::EmitFnPtrCall(clang::Expr *callee) { } void Converter::EmitFnAsValue(const clang::FunctionDecl *fn_decl) { - StrCat(std::format("Some({} as _)", Mapper::GetFnRefName(fn_decl))); + StrCat(std::format("Some({})", Mapper::GetFnRefName(fn_decl))); } std::string Converter::GetPointeeRustType(clang::QualType ptr_type) { diff --git a/cpp2rust/converter/models/converter_refcount.cpp b/cpp2rust/converter/models/converter_refcount.cpp index bf3160ad..d8b2b135 100644 --- a/cpp2rust/converter/models/converter_refcount.cpp +++ b/cpp2rust/converter/models/converter_refcount.cpp @@ -1618,12 +1618,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("fn_ptr_anon!(("); + StrCat("FnPtr::new(("); VisitLambdaExpr(lambda); - StrCat(std::format( - "), {})", - GetFnTypeString(qual_type->getPointeeType() - ->getAs()))); + StrCat("), 0)"); } else { VisitLambdaExpr(lambda); } diff --git a/libcc2rs/src/fn_ptr.rs b/libcc2rs/src/fn_ptr.rs index 0cef3106..07a5f0d5 100644 --- a/libcc2rs/src/fn_ptr.rs +++ b/libcc2rs/src/fn_ptr.rs @@ -1,6 +1,13 @@ // 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; diff --git a/libcc2rs/src/lib.rs b/libcc2rs/src/lib.rs index 998280fe..1271b941 100644 --- a/libcc2rs/src/lib.rs +++ b/libcc2rs/src/lib.rs @@ -4,22 +4,6 @@ mod reinterpret; pub use reinterpret::ByteRepr; -#[macro_export] -macro_rules! fn_ptr { - ($f:expr, $ty:ty) => { - $crate::FnPtr::new($f as $ty, $f as *const () as usize) - }; -} - -// Lambda: no stable address, use 0. TODO: assign unique addr per lambda site so distinct -// lambdas don't compare equal. -#[macro_export] -macro_rules! fn_ptr_anon { - ($f:expr, $ty:ty) => { - $crate::FnPtr::new($f as $ty, 0) - }; -} - mod rc; pub use rc::*; diff --git a/tests/unit/out/refcount/fn_ptr_default_arg.rs b/tests/unit/out/refcount/fn_ptr_default_arg.rs index 26b8d9b5..0c598b62 100644 --- a/tests/unit/out/refcount/fn_ptr_default_arg.rs +++ b/tests/unit/out/refcount/fn_ptr_default_arg.rs @@ -47,12 +47,12 @@ fn main_0() -> i32 { apply_1(_x, Some(_fn)) }) == 5) ); - let negate: Value i32>> = Rc::new(RefCell::new(fn_ptr_anon!( + let negate: Value i32>> = Rc::new(RefCell::new(FnPtr::new( (|x: i32| { let x: Value = Rc::new(RefCell::new(x)); return -(*x.borrow()); }), - fn(i32) -> i32 + 0, ))); assert!( (({ diff --git a/tests/unit/out/unsafe/fn_ptr.rs b/tests/unit/out/unsafe/fn_ptr.rs index cc9d4ae3..60ce3036 100644 --- a/tests/unit/out/unsafe/fn_ptr.rs +++ b/tests/unit/out/unsafe/fn_ptr.rs @@ -27,10 +27,10 @@ pub fn main() { unsafe fn main_0() -> i32 { let mut fn_: Option i32> = None; assert!((fn_).is_none()); - assert!(((fn_) != (Some(my_foo_0 as _)))); - fn_ = Some(my_foo_0 as _); + assert!(((fn_) != (Some(my_foo_0)))); + fn_ = Some(my_foo_0); assert!(!((fn_).is_none())); - assert!(((fn_) == (Some(my_foo_0 as _)))); + assert!(((fn_) == (Some(my_foo_0)))); let mut a: i32 = 10; assert!( ((unsafe { diff --git a/tests/unit/out/unsafe/fn_ptr_array.rs b/tests/unit/out/unsafe/fn_ptr_array.rs index f69829eb..1e224706 100644 --- a/tests/unit/out/unsafe/fn_ptr_array.rs +++ b/tests/unit/out/unsafe/fn_ptr_array.rs @@ -22,8 +22,7 @@ pub fn main() { } } unsafe fn main_0() -> i32 { - let mut ops: [Option i32>; 3] = - [Some(add_0 as _), Some(sub_1 as _), Some(mul_2 as _)]; + let mut ops: [Option i32>; 3] = [Some(add_0), Some(sub_1), Some(mul_2)]; assert!( ((unsafe { let _arg0: i32 = 2; @@ -46,7 +45,7 @@ unsafe fn main_0() -> i32 { }) == (30)) ); assert!(!((ops[(0) as usize]).is_none())); - assert!(((ops[(0) as usize]) == (Some(add_0 as _)))); - assert!(((ops[(0) as usize]) != (Some(sub_1 as _)))); + 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 index ce851f5b..7669fb31 100644 --- a/tests/unit/out/unsafe/fn_ptr_as_condition.rs +++ b/tests/unit/out/unsafe/fn_ptr_as_condition.rs @@ -26,7 +26,7 @@ pub fn main() { unsafe fn main_0() -> i32 { let mut a: i32 = 5; (unsafe { - let _cb: Option = Some(double_it_0 as _); + let _cb: Option = Some(double_it_0); let _x: *mut i32 = (&mut a as *mut i32); maybe_call_1(_cb, _x) }); @@ -40,7 +40,7 @@ unsafe fn main_0() -> i32 { assert!(((b) == (5))); let mut fn_: Option = None; if !!(fn_).is_none() { - fn_ = Some(double_it_0 as _); + fn_ = Some(double_it_0); } let mut c: i32 = 3; if !(fn_).is_none() { diff --git a/tests/unit/out/unsafe/fn_ptr_cast.rs b/tests/unit/out/unsafe/fn_ptr_cast.rs index e5ebf489..9fce4e7c 100644 --- a/tests/unit/out/unsafe/fn_ptr_cast.rs +++ b/tests/unit/out/unsafe/fn_ptr_cast.rs @@ -11,7 +11,7 @@ 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 as _); + let mut fn_: Option i32> = Some(double_it_0); assert!( ((unsafe { let _arg0: i32 = 5; @@ -32,7 +32,7 @@ pub unsafe fn test_roundtrip_1() { assert!(((fn2) == (fn_))); } pub unsafe fn test_double_cast_2() { - let mut fn_: Option i32> = Some(double_it_0 as _); + 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_), @@ -52,7 +52,7 @@ pub struct Command { 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 as _, + double_it_0, )); let mut fn_: Option i32> = std::mem::transmute::<*mut ::libc::c_void, Option i32>>(cmd.data); @@ -67,11 +67,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 as _)); + 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 = diff --git a/tests/unit/out/unsafe/fn_ptr_conditional.rs b/tests/unit/out/unsafe/fn_ptr_conditional.rs index adfad96b..0694a0b1 100644 --- a/tests/unit/out/unsafe/fn_ptr_conditional.rs +++ b/tests/unit/out/unsafe/fn_ptr_conditional.rs @@ -18,12 +18,12 @@ pub unsafe fn identity_2(mut x: i32) -> i32 { } pub unsafe fn pick_3(mut mode: i32) -> Option i32> { return if ((mode) > (0)) { - Some(inc_0 as _) + Some(inc_0) } else { if ((mode) < (0)) { - Some(dec_1 as _) + Some(dec_1) } else { - Some(identity_2 as _) + Some(identity_2) } }; } @@ -31,7 +31,7 @@ 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 as _) + Some(identity_2) }; return (unsafe { let _arg0: i32 = x; @@ -76,7 +76,7 @@ unsafe fn main_0() -> i32 { ); assert!( ((unsafe { - let _fn: Option i32> = Some(inc_0 as _); + let _fn: Option i32> = Some(inc_0); let _x: i32 = 5; apply_4(_fn, _x) }) == (6)) diff --git a/tests/unit/out/unsafe/fn_ptr_default_arg.rs b/tests/unit/out/unsafe/fn_ptr_default_arg.rs index 5decf93f..86287ca6 100644 --- a/tests/unit/out/unsafe/fn_ptr_default_arg.rs +++ b/tests/unit/out/unsafe/fn_ptr_default_arg.rs @@ -43,7 +43,7 @@ unsafe fn main_0() -> i32 { assert!( ((unsafe { let _x: i32 = 5; - let _fn: Option i32> = Some(identity_0 as _); + let _fn: Option i32> = Some(identity_0); apply_1(_x, Some(_fn)) }) == (5)) ); diff --git a/tests/unit/out/unsafe/fn_ptr_global.rs b/tests/unit/out/unsafe/fn_ptr_global.rs index d4ce4c20..21dc35b7 100644 --- a/tests/unit/out/unsafe/fn_ptr_global.rs +++ b/tests/unit/out/unsafe/fn_ptr_global.rs @@ -39,11 +39,11 @@ unsafe fn main_0() -> i32 { }) == (5)) ); (unsafe { - let _fn: Option i32> = Some(double_it_0 as _); + let _fn: Option i32> = Some(double_it_0); set_op_2(_fn) }); assert!(!((g_op).is_none())); - assert!(((g_op) == (Some(double_it_0 as _)))); + assert!(((g_op) == (Some(double_it_0)))); assert!( ((unsafe { let _x: i32 = 5; @@ -51,10 +51,10 @@ unsafe fn main_0() -> i32 { }) == (10)) ); (unsafe { - let _fn: Option i32> = Some(triple_it_1 as _); + let _fn: Option i32> = Some(triple_it_1); set_op_2(_fn) }); - assert!(((g_op) == (Some(triple_it_1 as _)))); + assert!(((g_op) == (Some(triple_it_1)))); assert!( ((unsafe { let _x: i32 = 5; diff --git a/tests/unit/out/unsafe/fn_ptr_reassign.rs b/tests/unit/out/unsafe/fn_ptr_reassign.rs index 19ade021..08eed4bb 100644 --- a/tests/unit/out/unsafe/fn_ptr_reassign.rs +++ b/tests/unit/out/unsafe/fn_ptr_reassign.rs @@ -22,7 +22,7 @@ pub fn main() { } } unsafe fn main_0() -> i32 { - let mut fn_: Option i32> = Some(add_0 as _); + let mut fn_: Option i32> = Some(add_0); assert!( ((unsafe { let _arg0: i32 = 3; @@ -30,7 +30,7 @@ unsafe fn main_0() -> i32 { (fn_).unwrap()(_arg0, _arg1) }) == (7)) ); - fn_ = Some(sub_1 as _); + fn_ = Some(sub_1); assert!( ((unsafe { let _arg0: i32 = 10; @@ -38,7 +38,7 @@ unsafe fn main_0() -> i32 { (fn_).unwrap()(_arg0, _arg1) }) == (7)) ); - fn_ = Some(mul_2 as _); + fn_ = Some(mul_2); assert!( ((unsafe { let _arg0: i32 = 6; @@ -48,7 +48,7 @@ unsafe fn main_0() -> i32 { ); fn_ = None; assert!((fn_).is_none()); - fn_ = Some(add_0 as _); + fn_ = Some(add_0); assert!(!((fn_).is_none())); assert!( ((unsafe { diff --git a/tests/unit/out/unsafe/fn_ptr_return.rs b/tests/unit/out/unsafe/fn_ptr_return.rs index 7e2e9270..1cda2828 100644 --- a/tests/unit/out/unsafe/fn_ptr_return.rs +++ b/tests/unit/out/unsafe/fn_ptr_return.rs @@ -15,9 +15,9 @@ pub unsafe fn dec_1(mut x: i32) -> i32 { } pub unsafe fn pick_2(mut choose_inc: i32) -> Option i32> { if (choose_inc != 0) { - return Some(inc_0 as _); + return Some(inc_0); } - return Some(dec_1 as _); + return Some(dec_1); } pub fn main() { unsafe { @@ -30,7 +30,7 @@ unsafe fn main_0() -> i32 { pick_2(_choose_inc) }); assert!(!((f).is_none())); - assert!(((f) == (Some(inc_0 as _)))); + assert!(((f) == (Some(inc_0)))); assert!( ((unsafe { let _arg0: i32 = 10; @@ -41,7 +41,7 @@ unsafe fn main_0() -> i32 { let _choose_inc: i32 = 0; pick_2(_choose_inc) }); - assert!(((g) == (Some(dec_1 as _)))); + assert!(((g) == (Some(dec_1)))); assert!( ((unsafe { let _arg0: i32 = 10; diff --git a/tests/unit/out/unsafe/fn_ptr_stdlib_compare.rs b/tests/unit/out/unsafe/fn_ptr_stdlib_compare.rs index cf09b6b4..432cdf5c 100644 --- a/tests/unit/out/unsafe/fn_ptr_stdlib_compare.rs +++ b/tests/unit/out/unsafe/fn_ptr_stdlib_compare.rs @@ -14,14 +14,14 @@ pub fn main() { } unsafe fn main_0() -> i32 { let mut rfn: Option u64> = - Some(rules::stdio_tgt_unsafe::f5 as _); - assert!(((rfn) == (Some(rules::stdio_tgt_unsafe::f5 as _)))); + Some(rules::stdio_tgt_unsafe::f5); + assert!(((rfn) == (Some(rules::stdio_tgt_unsafe::f5)))); assert!(!((rfn).is_none())); let mut rfn2: Option u64> = std::mem::transmute::< Option u64>, Option u64>, - >(Some(rules::stdio_tgt_unsafe::f5 as _)); + >(Some(rules::stdio_tgt_unsafe::f5)); assert!( ((rfn) == (std::mem::transmute::< diff --git a/tests/unit/out/unsafe/fn_ptr_struct.rs b/tests/unit/out/unsafe/fn_ptr_struct.rs index 277b226d..ae9e3a7f 100644 --- a/tests/unit/out/unsafe/fn_ptr_struct.rs +++ b/tests/unit/out/unsafe/fn_ptr_struct.rs @@ -34,11 +34,11 @@ pub fn main() { unsafe fn main_0() -> i32 { let mut h1: Handler = Handler { tag: 1, - cb: Some(double_it_0 as _), + cb: Some(double_it_0), }; let mut h2: Handler = Handler { tag: 2, - cb: Some(negate_1 as _), + cb: Some(negate_1), }; assert!(!((h1.cb).is_none())); assert!( @@ -53,7 +53,7 @@ unsafe fn main_0() -> i32 { (h2.cb).unwrap()(_arg0) }) == (-7_i32)) ); - (h1.cb) = Some(negate_1 as _); + (h1.cb) = Some(negate_1); assert!( ((unsafe { let _arg0: i32 = 3; diff --git a/tests/unit/out/unsafe/fn_ptr_void_return.rs b/tests/unit/out/unsafe/fn_ptr_void_return.rs index 00817f01..72ab341a 100644 --- a/tests/unit/out/unsafe/fn_ptr_void_return.rs +++ b/tests/unit/out/unsafe/fn_ptr_void_return.rs @@ -27,18 +27,18 @@ pub fn main() { unsafe fn main_0() -> i32 { let mut a: i32 = 42; (unsafe { - let _fn: Option = Some(negate_0 as _); + 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 as _); + 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 as _); + let mut fn_: Option = Some(negate_0); assert!(!((fn_).is_none())); let mut b: i32 = 10; (unsafe { diff --git a/tests/unit/out/unsafe/fn_ptr_vtable.rs b/tests/unit/out/unsafe/fn_ptr_vtable.rs index 98298086..48e820e6 100644 --- a/tests/unit/out/unsafe/fn_ptr_vtable.rs +++ b/tests/unit/out/unsafe/fn_ptr_vtable.rs @@ -40,9 +40,9 @@ pub fn main() { } unsafe fn main_0() -> i32 { let mut vt: Vtable = Vtable { - create: Some(int_create_0 as _), - get: Some(int_get_1 as _), - destroy: Some(int_destroy_2 as _), + 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())); diff --git a/tests/unit/out/unsafe/no_direct_callee.rs b/tests/unit/out/unsafe/no_direct_callee.rs index 1136da61..b4eac215 100644 --- a/tests/unit/out/unsafe/no_direct_callee.rs +++ b/tests/unit/out/unsafe/no_direct_callee.rs @@ -23,7 +23,7 @@ pub fn main() { } unsafe fn main_0() -> i32 { return (unsafe { - let _fn: Option bool> = Some(test1_0 as _); + let _fn: Option bool> = Some(test1_0); test_1(_fn) }); } From 0c1ebf9c65360bcd3028fe55d40d5bdf268e74cd Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Wed, 15 Apr 2026 14:24:44 +0100 Subject: [PATCH 30/56] Delete ByteRepr for fn pointers --- libcc2rs/src/reinterpret.rs | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/libcc2rs/src/reinterpret.rs b/libcc2rs/src/reinterpret.rs index 19495011..7bef8167 100644 --- a/libcc2rs/src/reinterpret.rs +++ b/libcc2rs/src/reinterpret.rs @@ -64,24 +64,6 @@ impl ByteRepr for std::rc::Rc {} impl ByteRepr for std::cell::RefCell {} impl ByteRepr for Box<[T]> {} impl ByteRepr for Box {} - -macro_rules! impl_byterepr_fn { - ($($name:ident),*) => { - impl ByteRepr for fn($($name),*) -> R {} - }; -} - -impl_byterepr_fn!(); -impl_byterepr_fn!(A); -impl_byterepr_fn!(A, B); -impl_byterepr_fn!(A, B, C); -impl_byterepr_fn!(A, B, C, D); -impl_byterepr_fn!(A, B, C, D, E); -impl_byterepr_fn!(A, B, C, D, E, F); -impl_byterepr_fn!(A, B, C, D, E, F, G); -impl_byterepr_fn!(A, B, C, D, E, F, G, H); -impl_byterepr_fn!(A, B, C, D, E, F, G, H, I); -impl_byterepr_fn!(A, B, C, D, E, F, G, H, I, J); impl ByteRepr for *const T {} impl ByteRepr for *mut T {} impl ByteRepr for (A, B) {} From 60fd8c351d70a41d78008781c42a352b735f1f33 Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Wed, 15 Apr 2026 14:51:01 +0100 Subject: [PATCH 31/56] Drop Clone trait from ErasedPtr --- libcc2rs/src/rc.rs | 30 ++++-------------------------- 1 file changed, 4 insertions(+), 26 deletions(-) diff --git a/libcc2rs/src/rc.rs b/libcc2rs/src/rc.rs index 3b8fbe1f..a921407e 100644 --- a/libcc2rs/src/rc.rs +++ b/libcc2rs/src/rc.rs @@ -1013,7 +1013,7 @@ pub(crate) trait ErasedPtr: std::any::Any { impl ErasedPtr for Ptr where - T: Clone + ByteRepr + 'static, + T: ByteRepr + 'static, Ptr: PartialEq, { fn pointee_type_id(&self) -> std::any::TypeId { @@ -1024,35 +1024,13 @@ where if self.pointee_type_id() != src.pointee_type_id() { panic!("memcpy: type mismatch"); } - let src_ptr = src .as_any() .downcast_ref::>() .expect("memcpy: downcast to Ptr failed"); - - let elem = std::mem::size_of::(); - - if len == elem { - self.write(src_ptr.read()); - return; - } - - if elem != 0 && len.is_multiple_of(elem) { - let mut dst = self.clone(); - let mut src_it = src_ptr.clone(); - for _ in 0..(len / elem) { - dst.write(src_it.read()); - dst += 1; - src_it += 1; - } - - return; - } - - panic!( - "memcpy: len {} not compatible with element size {}", - len, elem - ); + let dst_bytes: Ptr = self.reinterpret_cast(); + let src_bytes: Ptr = src_ptr.reinterpret_cast(); + dst_bytes.memcpy(&src_bytes, len); } fn as_any(&self) -> &dyn std::any::Any { From 81581df08296e8bdb681b710b7b0a00edb09afd1 Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Wed, 15 Apr 2026 14:51:16 +0100 Subject: [PATCH 32/56] Allow casting between nullptr's of different types --- libcc2rs/src/rc.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/libcc2rs/src/rc.rs b/libcc2rs/src/rc.rs index a921407e..75c26068 100644 --- a/libcc2rs/src/rc.rs +++ b/libcc2rs/src/rc.rs @@ -1055,7 +1055,7 @@ pub struct AnyPtr { pub(crate) ptr: Rc, } -impl Ptr { +impl Ptr { pub fn to_any(&self) -> AnyPtr { AnyPtr { ptr: Rc::new(self.clone()), @@ -1071,6 +1071,11 @@ impl Default for AnyPtr { impl AnyPtr { pub fn cast(&self) -> Option> { + if let Some(p) = self.ptr.as_any().downcast_ref::>() { + if p.is_null() { + return Some(Ptr::::null()); + } + } self.ptr.as_any().downcast_ref::>().cloned() } From 82b9ed04f28d8d6b02b109c2fec20274b1224395 Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Wed, 15 Apr 2026 14:58:15 +0100 Subject: [PATCH 33/56] Update output of fn_ptr_stdlib_compare --- tests/unit/fn_ptr_stdlib_compare.cpp | 19 +++++---- .../out/refcount/fn_ptr_stdlib_compare.rs | 40 ++++++++++++++++--- .../unit/out/unsafe/fn_ptr_stdlib_compare.rs | 34 +++++++++++++--- 3 files changed, 74 insertions(+), 19 deletions(-) diff --git a/tests/unit/fn_ptr_stdlib_compare.cpp b/tests/unit/fn_ptr_stdlib_compare.cpp index a71581c2..6bb8b55d 100644 --- a/tests/unit/fn_ptr_stdlib_compare.cpp +++ b/tests/unit/fn_ptr_stdlib_compare.cpp @@ -2,17 +2,22 @@ #include #include -typedef size_t (*read_fn)(void *, size_t, size_t, FILE *); +typedef size_t (*fread_t)(void *, size_t, size_t, FILE *); -typedef size_t (*read_alternative_fn)(char *, size_t, size_t, void *); +typedef size_t (*fread_alternative_t)(char *, size_t, size_t, void *); + +size_t my_alternative_fread(char *p, size_t n, size_t m, void *f) { return 22; } int main() { - read_fn rfn = fread; - assert(rfn == fread); - assert(rfn != nullptr); + fread_t fn1 = fread; + assert(fn1 == fread); + assert(fn1 != nullptr); + + fread_alternative_t fn2 = (fread_alternative_t)fread; + assert(fn1 == (fread_t)fn2); - read_alternative_fn rfn2 = (read_alternative_fn)fread; - assert(rfn == (read_fn)rfn2); + fread_t f3 = (fread_t)my_alternative_fread; + assert((*f3)(nullptr, 0, 0, nullptr) == 22); return 0; } diff --git a/tests/unit/out/refcount/fn_ptr_stdlib_compare.rs b/tests/unit/out/refcount/fn_ptr_stdlib_compare.rs index 5c10309f..178f0357 100644 --- a/tests/unit/out/refcount/fn_ptr_stdlib_compare.rs +++ b/tests/unit/out/refcount/fn_ptr_stdlib_compare.rs @@ -7,24 +7,31 @@ use std::io::Seek; use std::io::{Read, Write}; use std::os::fd::AsFd; use std::rc::{Rc, Weak}; +pub fn my_alternative_fread_0(p: Ptr, n: u64, m: u64, f: AnyPtr) -> u64 { + let p: Value> = Rc::new(RefCell::new(p)); + let n: Value = Rc::new(RefCell::new(n)); + let m: Value = Rc::new(RefCell::new(m)); + let f: Value = Rc::new(RefCell::new(f)); + return 22_u64; +} pub fn main() { std::process::exit(main_0()); } fn main_0() -> i32 { - let rfn: Value) -> u64>> = + let fn1: Value) -> u64>> = Rc::new(RefCell::new(fn_ptr!( rules::stdio_tgt_refcount::f5, fn(AnyPtr, u64, u64, Ptr<::std::fs::File>) -> u64 ))); assert!({ - let _lhs = (*rfn.borrow()).clone(); + let _lhs = (*fn1.borrow()).clone(); _lhs == fn_ptr!( rules::stdio_tgt_refcount::f5, fn(AnyPtr, u64, u64, Ptr<::std::fs::File>) -> u64 ) }); - assert!(!((*rfn.borrow()).is_null())); - let rfn2: Value, u64, u64, AnyPtr) -> u64>> = Rc::new(RefCell::new( + assert!(!((*fn1.borrow()).is_null())); + let fn2: Value, u64, u64, AnyPtr) -> u64>> = Rc::new(RefCell::new( fn_ptr!( rules::stdio_tgt_refcount::f5, fn(AnyPtr, u64, u64, Ptr<::std::fs::File>) -> u64 @@ -41,9 +48,30 @@ fn main_0() -> i32 { )), )); assert!({ - let _lhs = (*rfn.borrow()).clone(); - _lhs == ((*rfn2.borrow()).cast::) -> u64>(None)) + let _lhs = (*fn1.borrow()).clone(); + _lhs == ((*fn2.borrow()).cast::) -> u64>(None)) .clone() }); + let f3: Value) -> u64>> = + Rc::new(RefCell::new( + fn_ptr!( + my_alternative_fread_0, + fn(Ptr::, u64, u64, AnyPtr) -> u64 + ) + .cast::) -> u64>(Some( + (|a0: AnyPtr, a1: u64, a2: u64, a3: Ptr<::std::fs::File>| -> u64 { + my_alternative_fread_0(a0.cast::().unwrap(), a1, a2, a3.to_any()) + }) as fn(AnyPtr, u64, u64, Ptr<::std::fs::File>) -> u64, + )), + )); + assert!( + (({ + let _arg0: AnyPtr = Default::default(); + let _arg1: u64 = 0_u64; + let _arg2: u64 = 0_u64; + let _arg3: Ptr<::std::fs::File> = Default::default(); + (*(*f3.borrow()))(_arg0, _arg1, _arg2, _arg3) + }) == 22_u64) + ); return 0; } diff --git a/tests/unit/out/unsafe/fn_ptr_stdlib_compare.rs b/tests/unit/out/unsafe/fn_ptr_stdlib_compare.rs index 432cdf5c..3ff8e110 100644 --- a/tests/unit/out/unsafe/fn_ptr_stdlib_compare.rs +++ b/tests/unit/out/unsafe/fn_ptr_stdlib_compare.rs @@ -7,27 +7,49 @@ use std::io::Seek; use std::io::{Read, Write}; use std::os::fd::{AsFd, FromRawFd, IntoRawFd}; use std::rc::Rc; +pub unsafe fn my_alternative_fread_0( + mut p: *mut u8, + mut n: u64, + mut m: u64, + mut f: *mut ::libc::c_void, +) -> u64 { + return 22_u64; +} pub fn main() { unsafe { std::process::exit(main_0() as i32); } } unsafe fn main_0() -> i32 { - let mut rfn: Option u64> = + let mut fn1: Option u64> = Some(rules::stdio_tgt_unsafe::f5); - assert!(((rfn) == (Some(rules::stdio_tgt_unsafe::f5)))); - assert!(!((rfn).is_none())); - let mut rfn2: Option u64> = + assert!(((fn1) == (Some(rules::stdio_tgt_unsafe::f5)))); + assert!(!((fn1).is_none())); + let mut fn2: Option u64> = std::mem::transmute::< Option u64>, Option u64>, >(Some(rules::stdio_tgt_unsafe::f5)); assert!( - ((rfn) + ((fn1) == (std::mem::transmute::< Option u64>, Option u64>, - >(rfn2))) + >(fn2))) + ); + let mut f3: Option u64> = + std::mem::transmute::< + Option u64>, + Option u64>, + >(Some(my_alternative_fread_0)); + assert!( + ((unsafe { + let _arg0: *mut ::libc::c_void = Default::default(); + let _arg1: u64 = 0_u64; + let _arg2: u64 = 0_u64; + let _arg3: *mut ::std::fs::File = Default::default(); + (f3).unwrap()(_arg0, _arg1, _arg2, _arg3) + }) == (22_u64)) ); return 0; } From 207106c6c5e43a1e902466ce70a796e42f6281a1 Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Wed, 15 Apr 2026 15:01:06 +0100 Subject: [PATCH 34/56] Allow char*/void* <-> T* conversions in adapter --- .../converter/models/converter_refcount.cpp | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/cpp2rust/converter/models/converter_refcount.cpp b/cpp2rust/converter/models/converter_refcount.cpp index d8b2b135..0a57c26c 100644 --- a/cpp2rust/converter/models/converter_refcount.cpp +++ b/cpp2rust/converter/models/converter_refcount.cpp @@ -196,11 +196,18 @@ std::string ConverterRefCount::BuildFnAdapter( 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->isVoidPointerType()) { - closure += std::format("a{}.cast::<{}>().unwrap()", i, - GetPointeeRustType(src_pty)); - } else if (src_pty->isVoidPointerType() && tgt_pty->isPointerType()) { - closure += std::format("a{}.to_any()", 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"; From c500cff87a9252d8ba7556e676754841bccb86e3 Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Wed, 15 Apr 2026 15:23:26 +0100 Subject: [PATCH 35/56] Omit enumeral types on generating default integers --- cpp2rust/converter/converter.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpp2rust/converter/converter.cpp b/cpp2rust/converter/converter.cpp index 0df813f4..6870ce91 100644 --- a/cpp2rust/converter/converter.cpp +++ b/cpp2rust/converter/converter.cpp @@ -2731,7 +2731,7 @@ std::string Converter::GetDefaultAsStringFallback(clang::QualType qual_type) { return "false"; } - if (qual_type->isIntegerType()) { + if (qual_type->isIntegerType() && !qual_type->isEnumeralType()) { return std::format("0_{}", ToString(qual_type)); } From f138af55041baec3f23f7abb94a566d6df59bd1a Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Mon, 20 Apr 2026 16:54:45 +0100 Subject: [PATCH 36/56] Rename GetFnRefName to MapFunctionName --- cpp2rust/converter/converter.cpp | 2 +- cpp2rust/converter/mapper.cpp | 2 +- cpp2rust/converter/mapper.h | 2 +- cpp2rust/converter/models/converter_refcount.cpp | 4 ++-- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/cpp2rust/converter/converter.cpp b/cpp2rust/converter/converter.cpp index 31855b60..3fe5f889 100644 --- a/cpp2rust/converter/converter.cpp +++ b/cpp2rust/converter/converter.cpp @@ -1408,7 +1408,7 @@ std::string Converter::GetPointeeRustType(clang::QualType ptr_type) { void Converter::ConvertFunctionToFunctionPointer( const clang::FunctionDecl *fn_decl) { - StrCat(std::format("Some({})", Mapper::GetFnRefName(fn_decl))); + StrCat(std::format("Some({})", Mapper::MapFunctionName(fn_decl))); } void Converter::ConvertGenericCallExpr(clang::CallExpr *expr) { diff --git a/cpp2rust/converter/mapper.cpp b/cpp2rust/converter/mapper.cpp index 1436cf15..3a34545c 100644 --- a/cpp2rust/converter/mapper.cpp +++ b/cpp2rust/converter/mapper.cpp @@ -588,7 +588,7 @@ const TranslationRule::ExprTgt *GetExprTgt(const clang::Expr *expr) { return nullptr; } -std::string GetFnRefName(const clang::FunctionDecl *decl) { +std::string MapFunctionName(const clang::FunctionDecl *decl) { assert(decl); if (model_ == Model::kRefCount) { auto refcount_path = GetRulePathForFunction(decl, "tgt_refcount"); diff --git a/cpp2rust/converter/mapper.h b/cpp2rust/converter/mapper.h index c8ed73fc..09d822d7 100644 --- a/cpp2rust/converter/mapper.h +++ b/cpp2rust/converter/mapper.h @@ -19,7 +19,7 @@ bool Contains(const clang::Expr *expr); std::string Map(clang::QualType qual_type); const TranslationRule::ExprTgt *GetExprTgt(const clang::Expr *expr); -std::string GetFnRefName(const clang::FunctionDecl *decl); +std::string MapFunctionName(const clang::FunctionDecl *decl); std::string InstantiateTemplate(const clang::Expr *expr, const std::string &text); bool ReturnsPointer(const clang::Expr *expr); diff --git a/cpp2rust/converter/models/converter_refcount.cpp b/cpp2rust/converter/models/converter_refcount.cpp index 42eaf475..bb476576 100644 --- a/cpp2rust/converter/models/converter_refcount.cpp +++ b/cpp2rust/converter/models/converter_refcount.cpp @@ -196,7 +196,7 @@ std::string ConverterRefCount::BuildFnAdapter( closure += "{ "; // Build adapter body: src_fn(convert(a0), convert(a1), ...) - closure += Mapper::GetFnRefName(src_fn) + "("; + closure += Mapper::MapFunctionName(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); @@ -1039,7 +1039,7 @@ void ConverterRefCount::ConvertFunctionToFunctionPointer( StrCat(std::format("FnPtr::<{}>::new({})", ConvertFunctionPointerType( fn_decl->getType()->getAs()), - Mapper::GetFnRefName(fn_decl))); + Mapper::MapFunctionName(fn_decl))); } void ConverterRefCount::ConvertEqualsNullPtr(clang::Expr *expr) { From a929df945143422e4e0a1950a4fbf0b35ec64867 Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Mon, 20 Apr 2026 17:34:48 +0100 Subject: [PATCH 37/56] Add ConvertPointeeType --- cpp2rust/converter/converter.cpp | 15 --------------- cpp2rust/converter/converter.h | 2 -- cpp2rust/converter/converter_lib.cpp | 16 ++++++++++++++++ cpp2rust/converter/converter_lib.h | 3 +++ cpp2rust/converter/models/converter_refcount.cpp | 14 ++++++++++++-- cpp2rust/converter/models/converter_refcount.h | 1 + 6 files changed, 32 insertions(+), 19 deletions(-) diff --git a/cpp2rust/converter/converter.cpp b/cpp2rust/converter/converter.cpp index 3fe5f889..d361276d 100644 --- a/cpp2rust/converter/converter.cpp +++ b/cpp2rust/converter/converter.cpp @@ -1391,21 +1391,6 @@ void Converter::EmitFnPtrCall(clang::Expr *callee) { StrCat(").unwrap()"); } -std::string Converter::GetPointeeRustType(clang::QualType ptr_type) { - auto pointee = ptr_type->getPointeeType(); - if (pointee->isIntegerType()) { - return ToString(pointee); - } - auto str = ToString(ptr_type); - while (!str.empty() && std::isspace(str.back())) { - str.pop_back(); - } - if (str.starts_with("Ptr<") && str.ends_with(">")) { - return str.substr(4, str.size() - 5); - } - return ToString(pointee); -} - void Converter::ConvertFunctionToFunctionPointer( const clang::FunctionDecl *fn_decl) { StrCat(std::format("Some({})", Mapper::MapFunctionName(fn_decl))); diff --git a/cpp2rust/converter/converter.h b/cpp2rust/converter/converter.h index 0d787f62..6f2c3719 100644 --- a/cpp2rust/converter/converter.h +++ b/cpp2rust/converter/converter.h @@ -207,8 +207,6 @@ class Converter : public clang::RecursiveASTVisitor { virtual void EmitFnPtrCall(clang::Expr *callee); - std::string GetPointeeRustType(clang::QualType ptr_type); - virtual void ConvertFunctionToFunctionPointer(const clang::FunctionDecl *fn_decl); diff --git a/cpp2rust/converter/converter_lib.cpp b/cpp2rust/converter/converter_lib.cpp index d493ad74..4b28f0a7 100644 --- a/cpp2rust/converter/converter_lib.cpp +++ b/cpp2rust/converter/converter_lib.cpp @@ -8,7 +8,9 @@ #include #include +#include #include +#include #include #include @@ -660,4 +662,18 @@ clang::Expr *CreateConversionToBool(clang::Expr *expr, clang::ASTContext &ctx) { /*BasePath=*/nullptr, clang::VK_PRValue, clang::FPOptionsOverride()); } +static void Trim(std::string &s) { + auto is_space = [](unsigned char c) { return std::isspace(c); }; + s.erase(s.begin(), std::find_if_not(s.begin(), s.end(), is_space)); + s.erase(std::find_if_not(s.rbegin(), s.rend(), is_space).base(), s.end()); +} + +void Unwrap(std::string &s, std::string_view prefix, std::string_view suffix) { + Trim(s); + if (s.size() >= prefix.size() + suffix.size() && s.starts_with(prefix) && + s.ends_with(suffix)) { + s = s.substr(prefix.size(), s.size() - prefix.size() - suffix.size()); + } +} + } // namespace cpp2rust diff --git a/cpp2rust/converter/converter_lib.h b/cpp2rust/converter/converter_lib.h index 36776336..a5c8d692 100644 --- a/cpp2rust/converter/converter_lib.h +++ b/cpp2rust/converter/converter_lib.h @@ -12,6 +12,7 @@ #include #include #include +#include #include namespace cpp2rust { @@ -154,4 +155,6 @@ bool ContainsVAArgExpr(const clang::Stmt *stmt); clang::Expr *CreateConversionToBool(clang::Expr *expr, clang::ASTContext &ctx); +void Unwrap(std::string &s, std::string_view prefix, std::string_view suffix); + } // namespace cpp2rust diff --git a/cpp2rust/converter/models/converter_refcount.cpp b/cpp2rust/converter/models/converter_refcount.cpp index bb476576..c1f46dd6 100644 --- a/cpp2rust/converter/models/converter_refcount.cpp +++ b/cpp2rust/converter/models/converter_refcount.cpp @@ -205,12 +205,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)); + ConvertPointeeType(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)); + ConvertPointeeType(src_pty)); } else if (src_pty->getPointeeType()->isCharType()) { closure += std::format("a{}.reinterpret_cast::()", i); } @@ -2158,4 +2158,14 @@ std::string ConverterRefCount::ConvertMappedMethodCall( return std::format("{}.with_mut(|__v: {}| __v{})", ptr, param_type, body); } +std::string ConverterRefCount::ConvertPointeeType(clang::QualType ptr_type) { + // Pointee of a pointer to incomplete type is an incomplete type that does + // not have a translation rule. Hence ToString(ptr_type->getPointeeType()) is + // not enough + assert(ptr_type->isPointerType()); + auto str = ToString(ptr_type); + Unwrap(str, "Ptr<", ">"); + return str; +} + } // namespace cpp2rust diff --git a/cpp2rust/converter/models/converter_refcount.h b/cpp2rust/converter/models/converter_refcount.h index a55aad58..5ee5d6d9 100644 --- a/cpp2rust/converter/models/converter_refcount.h +++ b/cpp2rust/converter/models/converter_refcount.h @@ -205,6 +205,7 @@ class ConverterRefCount final : public Converter { std::string ConvertFreshPointer(clang::Expr *expr) override; std::string ConvertPtrType(clang::QualType type); + std::string ConvertPointeeType(clang::QualType ptr_type); /// The kind of conversion that should be performed. enum class ConversionKind : uint8_t { From 1e0794a0ac9d0c13c8f8f599de6107119f96bbbe Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Mon, 20 Apr 2026 17:40:08 +0100 Subject: [PATCH 38/56] Add ShouldReplaceWithMappedBody --- cpp2rust/converter/converter.cpp | 10 ++++++++-- cpp2rust/converter/converter.h | 2 ++ cpp2rust/converter/models/converter_refcount.cpp | 9 ++++----- 3 files changed, 14 insertions(+), 7 deletions(-) diff --git a/cpp2rust/converter/converter.cpp b/cpp2rust/converter/converter.cpp index d361276d..a2ecf842 100644 --- a/cpp2rust/converter/converter.cpp +++ b/cpp2rust/converter/converter.cpp @@ -2033,8 +2033,7 @@ std::string Converter::ConvertDeclRefExpr(clang::DeclRefExpr *expr) { } auto *decl = expr->getDecl(); - if (!(clang::isa(decl) && isAddrOf()) && - Mapper::Contains(expr)) { + if (ShouldReplaceWithMappedBody(expr)) { return GetMappedAsString(expr); } else if (auto *function = decl->getAsFunction()) { if (auto method = clang::dyn_cast(function)) { @@ -3430,6 +3429,13 @@ bool Converter::isCallee() const { return !curr_expr_kind_.empty() && curr_expr_kind_.back() == ExprKind::Callee; } +bool Converter::ShouldReplaceWithMappedBody(clang::DeclRefExpr *expr) const { + if (clang::isa(expr->getDecl()) && isAddrOf()) { + return false; + } + return Mapper::Contains(expr); +} + void Converter::SetFresh() { switch (computed_expr_type_) { case ComputedExprType::Value: diff --git a/cpp2rust/converter/converter.h b/cpp2rust/converter/converter.h index 6f2c3719..b68ecba8 100644 --- a/cpp2rust/converter/converter.h +++ b/cpp2rust/converter/converter.h @@ -455,6 +455,8 @@ class Converter : public clang::RecursiveASTVisitor { virtual bool RecordDerivesDefault(const clang::CXXRecordDecl *decl); + bool ShouldReplaceWithMappedBody(clang::DeclRefExpr *expr) const; + std::string *rs_code_; clang::ASTContext &ctx_; clang::FunctionDecl *curr_function_ = nullptr; diff --git a/cpp2rust/converter/models/converter_refcount.cpp b/cpp2rust/converter/models/converter_refcount.cpp index c1f46dd6..a9c4a07f 100644 --- a/cpp2rust/converter/models/converter_refcount.cpp +++ b/cpp2rust/converter/models/converter_refcount.cpp @@ -626,15 +626,14 @@ bool ConverterRefCount::VisitDeclRefExpr(clang::DeclRefExpr *expr) { } } - auto str = ConvertDeclRefExpr(expr); - auto decl = expr->getDecl(); - - if (!(clang::isa(decl) && isAddrOf()) && - Mapper::Contains(expr)) { + if (ShouldReplaceWithMappedBody(expr)) { StrCat(GetMappedAsString(expr)); return false; } + auto str = ConvertDeclRefExpr(expr); + auto decl = expr->getDecl(); + if (auto fn_decl = clang::dyn_cast(decl)) { if (isAddrOf()) { ConvertFunctionToFunctionPointer(fn_decl); From 747195b4f8fb1d17e60b000135baa29e8a9385e5 Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Mon, 20 Apr 2026 17:47:35 +0100 Subject: [PATCH 39/56] Replace single char strings with char literals --- cpp2rust/converter/models/converter_refcount.cpp | 2 +- cpp2rust/converter/translation_rule.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/cpp2rust/converter/models/converter_refcount.cpp b/cpp2rust/converter/models/converter_refcount.cpp index a9c4a07f..251835ba 100644 --- a/cpp2rust/converter/models/converter_refcount.cpp +++ b/cpp2rust/converter/models/converter_refcount.cpp @@ -196,7 +196,7 @@ std::string ConverterRefCount::BuildFnAdapter( closure += "{ "; // Build adapter body: src_fn(convert(a0), convert(a1), ...) - closure += Mapper::MapFunctionName(src_fn) + "("; + closure += Mapper::MapFunctionName(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); diff --git a/cpp2rust/converter/translation_rule.cpp b/cpp2rust/converter/translation_rule.cpp index 2adaa07e..c82fd18a 100644 --- a/cpp2rust/converter/translation_rule.cpp +++ b/cpp2rust/converter/translation_rule.cpp @@ -495,7 +495,7 @@ std::vector Load(const std::filesystem::path &path, Model model) { for (auto &[name, rule] : rules) { assert(!rule.src.empty() && "Rule loaded from IR but has no src"); if (auto *expr_tgt = std::get_if(&rule.tgt)) { - expr_tgt->validate(path.string() + ":" + name); + expr_tgt->validate(path.string() + ':' + name); } result.push_back(std::move(rule)); } From 1645da14ec9bba7d41fe6b49b08a1196645d51d9 Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Mon, 20 Apr 2026 19:21:59 +0100 Subject: [PATCH 40/56] Short-circuit ConvertPointeeType for integer types --- cpp2rust/converter/models/converter_refcount.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/cpp2rust/converter/models/converter_refcount.cpp b/cpp2rust/converter/models/converter_refcount.cpp index 251835ba..c645bce6 100644 --- a/cpp2rust/converter/models/converter_refcount.cpp +++ b/cpp2rust/converter/models/converter_refcount.cpp @@ -2158,6 +2158,10 @@ std::string ConverterRefCount::ConvertMappedMethodCall( } std::string ConverterRefCount::ConvertPointeeType(clang::QualType ptr_type) { + if (ptr_type->getPointeeType()->isIntegerType()) { + return ToString(ptr_type->getPointeeType()); + } + // Pointee of a pointer to incomplete type is an incomplete type that does // not have a translation rule. Hence ToString(ptr_type->getPointeeType()) is // not enough From 2f7ce1b5fb15a1cf2a5c16baf1ceee3885e360ea Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Tue, 21 Apr 2026 11:19:05 +0100 Subject: [PATCH 41/56] Update tests --- tests/unit/out/refcount/fn_ptr_stdlib_compare.rs | 3 +-- tests/unit/out/unsafe/fn_ptr_stdlib_compare.rs | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/tests/unit/out/refcount/fn_ptr_stdlib_compare.rs b/tests/unit/out/refcount/fn_ptr_stdlib_compare.rs index a0166013..0024b395 100644 --- a/tests/unit/out/refcount/fn_ptr_stdlib_compare.rs +++ b/tests/unit/out/refcount/fn_ptr_stdlib_compare.rs @@ -3,8 +3,7 @@ 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::io::{Read, Seek, Write}; use std::os::fd::AsFd; use std::rc::{Rc, Weak}; pub fn my_alternative_fread_0(p: Ptr, n: u64, m: u64, f: AnyPtr) -> u64 { diff --git a/tests/unit/out/unsafe/fn_ptr_stdlib_compare.rs b/tests/unit/out/unsafe/fn_ptr_stdlib_compare.rs index 3ff8e110..8cb90569 100644 --- a/tests/unit/out/unsafe/fn_ptr_stdlib_compare.rs +++ b/tests/unit/out/unsafe/fn_ptr_stdlib_compare.rs @@ -3,8 +3,7 @@ use libc::*; extern crate libcc2rs; use libcc2rs::*; use std::collections::BTreeMap; -use std::io::Seek; -use std::io::{Read, Write}; +use std::io::{Read, Seek, Write}; use std::os::fd::{AsFd, FromRawFd, IntoRawFd}; use std::rc::Rc; pub unsafe fn my_alternative_fread_0( From 83e88689d277377e20e79f9e07c8b2a85192fe3b Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Fri, 24 Apr 2026 09:25:20 +0100 Subject: [PATCH 42/56] Replace rules::stdio_tgt_unsafe::f5 with rules::fread_unsafe --- cpp2rust/converter/mapper.cpp | 23 +++---------------- rules/src/lib.rs | 3 +++ .../out/refcount/fn_ptr_stdlib_compare.rs | 17 ++++---------- .../unit/out/unsafe/fn_ptr_stdlib_compare.rs | 6 ++--- 4 files changed, 14 insertions(+), 35 deletions(-) diff --git a/cpp2rust/converter/mapper.cpp b/cpp2rust/converter/mapper.cpp index 3a34545c..6fcea241 100644 --- a/cpp2rust/converter/mapper.cpp +++ b/cpp2rust/converter/mapper.cpp @@ -562,17 +562,6 @@ std::string normalizeTranslationRule(std::string rule) { return rule; } -std::string GetRulePathForFunction(const clang::FunctionDecl *decl, - const std::string &model_suffix) { - assert(decl); - auto it = exprs_.find(ToString(decl)); - if (it == exprs_.end()) { - return ""; - } - auto &tgt = it->second; - return std::format("rules::{}_{}::{}", tgt.module, model_suffix, tgt.name); -} - } // namespace bool Contains(clang::QualType qual_type) { @@ -590,15 +579,9 @@ const TranslationRule::ExprTgt *GetExprTgt(const clang::Expr *expr) { std::string MapFunctionName(const clang::FunctionDecl *decl) { assert(decl); - if (model_ == Model::kRefCount) { - auto refcount_path = GetRulePathForFunction(decl, "tgt_refcount"); - if (!refcount_path.empty()) { - return refcount_path; - } - } - auto unsafe_path = GetRulePathForFunction(decl, "tgt_unsafe"); - if (!unsafe_path.empty()) { - return unsafe_path; + if (exprs_.contains(ToString(decl))) { + return std::format("rules::{}_{}", decl->getNameAsString(), + model_ == Model::kRefCount ? "refcount" : "unsafe"); } return GetNamedDeclAsString(decl->getCanonicalDecl()); } diff --git a/rules/src/lib.rs b/rules/src/lib.rs index 44df340b..72c260cd 100644 --- a/rules/src/lib.rs +++ b/rules/src/lib.rs @@ -4,3 +4,6 @@ #![allow(warnings)] mod modules; pub use modules::*; + +pub use modules::stdio_tgt_refcount::f5 as fread_refcount; +pub use modules::stdio_tgt_unsafe::f5 as fread_unsafe; diff --git a/tests/unit/out/refcount/fn_ptr_stdlib_compare.rs b/tests/unit/out/refcount/fn_ptr_stdlib_compare.rs index 0024b395..e58aa082 100644 --- a/tests/unit/out/refcount/fn_ptr_stdlib_compare.rs +++ b/tests/unit/out/refcount/fn_ptr_stdlib_compare.rs @@ -20,26 +20,19 @@ fn main_0() -> i32 { let fn1: Value) -> u64>> = Rc::new(RefCell::new(FnPtr::< fn(AnyPtr, u64, u64, Ptr<::std::fs::File>) -> u64, - >::new(rules::stdio_tgt_refcount::f5))); + >::new(rules::fread_refcount))); assert!({ let _lhs = (*fn1.borrow()).clone(); _lhs == FnPtr::) -> u64>::new( - rules::stdio_tgt_refcount::f5, + rules::fread_refcount, ) }); assert!(!((*fn1.borrow()).is_null())); let fn2: Value, u64, u64, AnyPtr) -> u64>> = Rc::new(RefCell::new( - FnPtr::) -> u64>::new( - rules::stdio_tgt_refcount::f5, - ) - .cast::, u64, u64, AnyPtr) -> u64>(Some( + FnPtr::) -> u64>::new(rules::fread_refcount) + .cast::, u64, u64, AnyPtr) -> u64>(Some( (|a0: Ptr, a1: u64, a2: u64, a3: AnyPtr| -> u64 { - rules::stdio_tgt_refcount::f5( - a0.to_any(), - a1, - a2, - a3.cast::<::std::fs::File>().unwrap(), - ) + rules::fread_refcount(a0.to_any(), a1, a2, a3.cast::<::std::fs::File>().unwrap()) }) as fn(Ptr, u64, u64, AnyPtr) -> u64, )), )); diff --git a/tests/unit/out/unsafe/fn_ptr_stdlib_compare.rs b/tests/unit/out/unsafe/fn_ptr_stdlib_compare.rs index 8cb90569..85ea1fc7 100644 --- a/tests/unit/out/unsafe/fn_ptr_stdlib_compare.rs +++ b/tests/unit/out/unsafe/fn_ptr_stdlib_compare.rs @@ -21,14 +21,14 @@ pub fn main() { } unsafe fn main_0() -> i32 { let mut fn1: Option u64> = - Some(rules::stdio_tgt_unsafe::f5); - assert!(((fn1) == (Some(rules::stdio_tgt_unsafe::f5)))); + Some(rules::fread_unsafe); + assert!(((fn1) == (Some(rules::fread_unsafe)))); assert!(!((fn1).is_none())); let mut fn2: Option u64> = std::mem::transmute::< Option u64>, Option u64>, - >(Some(rules::stdio_tgt_unsafe::f5)); + >(Some(rules::fread_unsafe)); assert!( ((fn1) == (std::mem::transmute::< From 81a2f40c51fee4584140ac3b910a453daccfd7f5 Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Fri, 24 Apr 2026 09:28:21 +0100 Subject: [PATCH 43/56] Revert translation_rule to master --- cpp2rust/converter/translation_rule.cpp | 10 +--------- cpp2rust/converter/translation_rule.h | 2 -- 2 files changed, 1 insertion(+), 11 deletions(-) diff --git a/cpp2rust/converter/translation_rule.cpp b/cpp2rust/converter/translation_rule.cpp index 05dcc9b1..f3433bbd 100644 --- a/cpp2rust/converter/translation_rule.cpp +++ b/cpp2rust/converter/translation_rule.cpp @@ -897,7 +897,7 @@ RuleMap LoadTgtFromIR(const std::filesystem::path &json_path) { Rule rule; if (name[0] == 'f') { rule.tgt = ParseExprTgtJSON(*obj); - std::get(rule.tgt).name = name; + std::get(rule.tgt).validate(json_path.string() + ":" + name); } else if (name[0] == 't') { rule.tgt = ParseTypeTgtJSON(*obj); } else { @@ -1039,8 +1039,6 @@ void ExprTgt::validate(const std::string &context) const { ValidateConsecutiveKeys(params, 'a', 0, context + " params"); ValidateConsecutiveKeys(generics, 'T', 1, context + " generics"); assert(!body.empty() && "ExprTgt body must not be empty"); - assert(!name.empty() && "ExprTgt name must not be empty"); - assert(!module.empty() && "ExprTgt module must not be empty"); } std::vector Load(const std::filesystem::path &path, Model model) { @@ -1063,9 +1061,6 @@ std::vector Load(const std::filesystem::path &path, Model model) { } for (auto &[name, src_rule] : src_rules) { rules.at(name).src = std::move(src_rule.src); - if (auto *expr_tgt = std::get_if(&rules.at(name).tgt)) { - expr_tgt->module = dir.filename().string(); - } } std::vector result; @@ -1080,9 +1075,6 @@ std::vector Load(const std::filesystem::path &path, Model model) { } } assert(!rule.src.empty() && "Rule loaded from IR but has no src"); - if (auto *expr_tgt = std::get_if(&rule.tgt)) { - expr_tgt->validate(path.string() + ':' + name); - } result.push_back(std::move(rule)); } return result; diff --git a/cpp2rust/converter/translation_rule.h b/cpp2rust/converter/translation_rule.h index 2d63c8f9..566dd900 100644 --- a/cpp2rust/converter/translation_rule.h +++ b/cpp2rust/converter/translation_rule.h @@ -61,8 +61,6 @@ struct TypeInfo { }; struct ExprTgt { - std::string name; - std::string module; std::unordered_map params; // "a0" -> TypeInfo TypeInfo return_type; std::unordered_map> From d7709d564bdcb99b8b81d34727d7f37cece97406 Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Fri, 24 Apr 2026 09:38:20 +0100 Subject: [PATCH 44/56] Add fread alias for f5 rule --- rules/src/lib.rs | 19 +++++++++++++++++-- rules/stdio/tgt_refcount.rs | 4 ++-- rules/stdio/tgt_unsafe.rs | 4 ++-- 3 files changed, 21 insertions(+), 6 deletions(-) diff --git a/rules/src/lib.rs b/rules/src/lib.rs index 72c260cd..18df7617 100644 --- a/rules/src/lib.rs +++ b/rules/src/lib.rs @@ -5,5 +5,20 @@ mod modules; pub use modules::*; -pub use modules::stdio_tgt_refcount::f5 as fread_refcount; -pub use modules::stdio_tgt_unsafe::f5 as fread_unsafe; +pub fn fread_refcount( + a0: ::libcc2rs::AnyPtr, + a1: u64, + a2: u64, + a3: ::libcc2rs::Ptr<::std::fs::File>, +) -> u64 { + modules::stdio_tgt_refcount::f5(a0, a1, a2, a3) +} + +pub unsafe fn fread_unsafe( + a0: *mut ::libc::c_void, + a1: u64, + a2: u64, + a3: *mut ::std::fs::File, +) -> u64 { + unsafe { modules::stdio_tgt_unsafe::f5(a0, a1, a2, a3) } +} diff --git a/rules/stdio/tgt_refcount.rs b/rules/stdio/tgt_refcount.rs index a36d31a7..bf4457f9 100644 --- a/rules/stdio/tgt_refcount.rs +++ b/rules/stdio/tgt_refcount.rs @@ -55,7 +55,7 @@ fn f4(a0: &mut ::std::fs::File, a1: i64, a2: i32) -> i32 { } } -pub fn f5(a0: AnyPtr, a1: u64, a2: u64, a3: Ptr<::std::fs::File>) -> u64 { +pub(crate) fn f5(a0: AnyPtr, a1: u64, a2: u64, a3: Ptr<::std::fs::File>) -> u64 { let total = a1.saturating_mul(a2) as usize; let mut dst = a0 .cast::() @@ -92,7 +92,7 @@ pub fn f5(a0: AnyPtr, a1: u64, a2: u64, a3: Ptr<::std::fs::File>) -> u64 { (read_bytes / a1 as usize) as u64 } -pub fn f6(a0: AnyPtr, a1: u64, a2: u64, a3: Ptr<::std::fs::File>) -> u64 { +fn f6(a0: AnyPtr, a1: u64, a2: u64, a3: Ptr<::std::fs::File>) -> u64 { let total = a1.saturating_mul(a2) as usize; let mut src = a0 .cast::() diff --git a/rules/stdio/tgt_unsafe.rs b/rules/stdio/tgt_unsafe.rs index d0bf8820..62302d7d 100644 --- a/rules/stdio/tgt_unsafe.rs +++ b/rules/stdio/tgt_unsafe.rs @@ -65,7 +65,7 @@ unsafe fn f4(a0: *mut ::std::fs::File, a1: i64, a2: i32) -> i32 { } } -pub unsafe fn f5(a0: *mut ::libc::c_void, a1: u64, a2: u64, a3: *mut ::std::fs::File) -> u64 { +pub(crate) unsafe fn f5(a0: *mut ::libc::c_void, a1: u64, a2: u64, a3: *mut ::std::fs::File) -> u64 { let total = a1.saturating_mul(a2) as usize; let mut dst = a0 as *mut u8; @@ -97,7 +97,7 @@ pub unsafe fn f5(a0: *mut ::libc::c_void, a1: u64, a2: u64, a3: *mut ::std::fs:: (read_bytes / a1 as usize) as u64 } -pub unsafe fn f6(a0: *const ::libc::c_void, a1: u64, a2: u64, a3: *mut ::std::fs::File) -> u64 { +unsafe fn f6(a0: *const ::libc::c_void, a1: u64, a2: u64, a3: *mut ::std::fs::File) -> u64 { let total = a1.saturating_mul(a2) as usize; let mut src = a0 as *mut u8; From 44a6d61f7df3fb76fa17367ac467f021f79d8001 Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Fri, 24 Apr 2026 09:42:25 +0100 Subject: [PATCH 45/56] Add call to fread through function pointer --- tests/unit/fn_ptr_stdlib_compare.cpp | 15 +++++ .../out/refcount/fn_ptr_stdlib_compare.rs | 52 +++++++++++++++++ .../unit/out/unsafe/fn_ptr_stdlib_compare.rs | 58 +++++++++++++++++++ 3 files changed, 125 insertions(+) diff --git a/tests/unit/fn_ptr_stdlib_compare.cpp b/tests/unit/fn_ptr_stdlib_compare.cpp index 6bb8b55d..a66e55e4 100644 --- a/tests/unit/fn_ptr_stdlib_compare.cpp +++ b/tests/unit/fn_ptr_stdlib_compare.cpp @@ -1,6 +1,7 @@ #include #include #include +#include typedef size_t (*fread_t)(void *, size_t, size_t, FILE *); @@ -19,5 +20,19 @@ int main() { fread_t f3 = (fread_t)my_alternative_fread; assert((*f3)(nullptr, 0, 0, nullptr) == 22); + FILE *stream = fopen("/dev/zero", "rb"); + assert(stream != nullptr); + char buf[16]; + memset(buf, 'X', sizeof(buf)); + size_t n = (*fn1)(buf, 1, 10, stream); + assert(n == 10); + for (int i = 0; i < 10; ++i) { + assert(buf[i] == 0); + } + for (int i = 10; i < 16; ++i) { + assert(buf[i] == 'X'); + } + fclose(stream); + return 0; } diff --git a/tests/unit/out/refcount/fn_ptr_stdlib_compare.rs b/tests/unit/out/refcount/fn_ptr_stdlib_compare.rs index e58aa082..11cd15cc 100644 --- a/tests/unit/out/refcount/fn_ptr_stdlib_compare.rs +++ b/tests/unit/out/refcount/fn_ptr_stdlib_compare.rs @@ -66,5 +66,57 @@ fn main_0() -> i32 { (*(*f3.borrow()))(_arg0, _arg1, _arg2, _arg3) }) == 22_u64) ); + let stream: Value> = Rc::new(RefCell::new( + match Ptr::from_string_literal("rb").to_rust_string() { + v if v == "rb" => std::fs::OpenOptions::new() + .read(true) + .open(Ptr::from_string_literal("/dev/zero").to_rust_string()) + .ok() + .map_or(Ptr::null(), |f| Ptr::alloc(f)), + v if v == "wb" => std::fs::OpenOptions::new() + .write(true) + .create(true) + .truncate(true) + .open(Ptr::from_string_literal("/dev/zero").to_rust_string()) + .ok() + .map_or(Ptr::null(), |f| Ptr::alloc(f)), + _ => panic!("unsupported mode"), + }, + )); + assert!(!((*stream.borrow()).is_null())); + let buf: Value> = Rc::new(RefCell::new( + (0..16).map(|_| ::default()).collect::>(), + )); + { + ((buf.as_pointer() as Ptr) as Ptr).to_any().memset( + (('X' as u8) as i32) as u8, + ::std::mem::size_of::<[u8; 16]>() as u64 as usize, + ); + ((buf.as_pointer() as Ptr) as Ptr).to_any().clone() + }; + let n: Value = Rc::new(RefCell::new( + ({ + let _arg0: AnyPtr = ((buf.as_pointer() as Ptr) as Ptr).to_any(); + let _arg1: u64 = 1_u64; + let _arg2: u64 = 10_u64; + let _arg3: Ptr<::std::fs::File> = (*stream.borrow()).clone(); + (*(*fn1.borrow()))(_arg0, _arg1, _arg2, _arg3) + }), + )); + assert!(((*n.borrow()) == 10_u64)); + let i: Value = Rc::new(RefCell::new(0)); + 'loop_: while ((*i.borrow()) < 10) { + assert!((((*buf.borrow())[(*i.borrow()) as usize] as i32) == 0)); + (*i.borrow_mut()).prefix_inc(); + } + let i: Value = Rc::new(RefCell::new(10)); + 'loop_: while ((*i.borrow()) < 16) { + assert!((((*buf.borrow())[(*i.borrow()) as usize] as i32) == (('X' as u8) as i32))); + (*i.borrow_mut()).prefix_inc(); + } + { + (*stream.borrow()).delete(); + 0 + }; return 0; } diff --git a/tests/unit/out/unsafe/fn_ptr_stdlib_compare.rs b/tests/unit/out/unsafe/fn_ptr_stdlib_compare.rs index 85ea1fc7..f4eb3ab6 100644 --- a/tests/unit/out/unsafe/fn_ptr_stdlib_compare.rs +++ b/tests/unit/out/unsafe/fn_ptr_stdlib_compare.rs @@ -50,5 +50,63 @@ unsafe fn main_0() -> i32 { (f3).unwrap()(_arg0, _arg1, _arg2, _arg3) }) == (22_u64)) ); + let mut stream: *mut ::std::fs::File = + match std::ffi::CStr::from_ptr(b"rb\0".as_ptr() as *const i8) + .to_str() + .expect("invalid c-string") + { + v if v == "rb" => std::fs::OpenOptions::new() + .read(true) + .open( + std::ffi::CStr::from_ptr(b"/dev/zero\0".as_ptr() as *const i8) + .to_str() + .expect("invalid c-string"), + ) + .ok() + .map_or(std::ptr::null_mut(), |f| Box::into_raw(Box::new(f))), + v if v == "wb" => std::fs::OpenOptions::new() + .write(true) + .create(true) + .truncate(true) + .open( + std::ffi::CStr::from_ptr(b"/dev/zero\0".as_ptr() as *const i8) + .to_str() + .expect("invalid c-string"), + ) + .ok() + .map_or(std::ptr::null_mut(), |f| Box::into_raw(Box::new(f))), + _ => panic!("unsupported mode"), + }; + assert!(!((stream).is_null())); + let mut buf: [u8; 16] = [0_u8; 16]; + { + let byte_0 = (buf.as_mut_ptr() as *mut u8 as *mut ::libc::c_void) as *mut u8; + for offset in 0..::std::mem::size_of::<[u8; 16]>() as u64 { + *byte_0.offset(offset as isize) = (('X' as u8) as i32) as u8; + } + (buf.as_mut_ptr() as *mut u8 as *mut ::libc::c_void) + }; + let mut n: u64 = (unsafe { + let _arg0: *mut ::libc::c_void = (buf.as_mut_ptr() as *mut u8 as *mut ::libc::c_void); + let _arg1: u64 = 1_u64; + let _arg2: u64 = 10_u64; + let _arg3: *mut ::std::fs::File = stream; + (fn1).unwrap()(_arg0, _arg1, _arg2, _arg3) + }); + assert!(((n) == (10_u64))); + let mut i: i32 = 0; + 'loop_: while ((i) < (10)) { + assert!(((buf[(i) as usize] as i32) == (0))); + i.prefix_inc(); + } + let mut i: i32 = 10; + 'loop_: while ((i) < (16)) { + assert!(((buf[(i) as usize] as i32) == (('X' as u8) as i32))); + i.prefix_inc(); + } + { + Box::from_raw(stream); + 0 + }; return 0; } From 9b3b1653b927a7e57065e758485c73f47f419280 Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Fri, 24 Apr 2026 11:50:57 +0100 Subject: [PATCH 46/56] rustfmt --- rules/stdio/tgt_unsafe.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/rules/stdio/tgt_unsafe.rs b/rules/stdio/tgt_unsafe.rs index 62302d7d..ebcd5db8 100644 --- a/rules/stdio/tgt_unsafe.rs +++ b/rules/stdio/tgt_unsafe.rs @@ -65,7 +65,12 @@ unsafe fn f4(a0: *mut ::std::fs::File, a1: i64, a2: i32) -> i32 { } } -pub(crate) unsafe fn f5(a0: *mut ::libc::c_void, a1: u64, a2: u64, a3: *mut ::std::fs::File) -> u64 { +pub(crate) unsafe fn f5( + a0: *mut ::libc::c_void, + a1: u64, + a2: u64, + a3: *mut ::std::fs::File, +) -> u64 { let total = a1.saturating_mul(a2) as usize; let mut dst = a0 as *mut u8; From 06d80a3816fc83f2641fedcc7f19071f67a8a210 Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Fri, 24 Apr 2026 13:28:01 +0100 Subject: [PATCH 47/56] Move implementation of fread inside libcc2rs --- cpp2rust/converter/mapper.cpp | 2 +- libcc2rs/src/io.rs | 79 ++++++++++++++++++- rules/src/lib.rs | 18 ----- rules/stdio/tgt_refcount.rs | 37 +-------- rules/stdio/tgt_unsafe.rs | 37 +-------- .../out/refcount/fn_ptr_stdlib_compare.rs | 8 +- .../unit/out/unsafe/fn_ptr_stdlib_compare.rs | 6 +- 7 files changed, 90 insertions(+), 97 deletions(-) diff --git a/cpp2rust/converter/mapper.cpp b/cpp2rust/converter/mapper.cpp index e910da85..fe36fc16 100644 --- a/cpp2rust/converter/mapper.cpp +++ b/cpp2rust/converter/mapper.cpp @@ -585,7 +585,7 @@ const TranslationRule::ExprTgt *GetExprTgt(const clang::Expr *expr) { std::string MapFunctionName(const clang::FunctionDecl *decl) { assert(decl); if (exprs_.contains(ToString(decl))) { - return std::format("rules::{}_{}", decl->getNameAsString(), + return std::format("libcc2rs::{}_{}", decl->getNameAsString(), model_ == Model::kRefCount ? "refcount" : "unsafe"); } return GetNamedDeclAsString(decl->getCanonicalDecl()); diff --git a/libcc2rs/src/io.rs b/libcc2rs/src/io.rs index 664b3d86..445bdbe3 100644 --- a/libcc2rs/src/io.rs +++ b/libcc2rs/src/io.rs @@ -1,8 +1,9 @@ // Copyright (c) 2022-present INESC-ID. // Distributed under the MIT license that can be found in the LICENSE file. -use crate::{AsPointer, Ptr, Value}; +use crate::{AnyPtr, AsPointer, Ptr, Value}; use std::cell::{RefCell, UnsafeCell}; +use std::ops::Deref; use std::os::fd::{AsFd, FromRawFd, IntoRawFd}; use std::rc::Rc; @@ -58,3 +59,79 @@ pub unsafe fn cout_unsafe() -> *mut std::fs::File { pub unsafe fn cerr_unsafe() -> *mut std::fs::File { UNSAFE_STDERR.with(UnsafeCell::get) } + +pub fn fread_refcount(a0: AnyPtr, a1: u64, a2: u64, a3: Ptr<::std::fs::File>) -> u64 { + let total = a1.saturating_mul(a2) as usize; + let mut dst = a0 + .cast::() + .expect("fread: only supporting u8 pointers") + .clone(); + + let f = (*a3.upgrade().deref()) + .try_clone() + .expect("try_clone failed"); + let mut reader = std::io::BufReader::with_capacity(64 * 1024, f); + + let mut read_bytes: usize = 0; + let mut buffer: [u8; 8192] = [0; 8192]; + + while read_bytes < total { + let remaining = total - read_bytes; + let to_read = std::cmp::min(buffer.len(), remaining); + + let n = match std::io::Read::read(&mut reader, &mut buffer[..to_read]) { + Ok(0) => break, + Ok(n) => n, + Err(ref e) if e.kind() == std::io::ErrorKind::Interrupted => continue, + Err(e) => panic!("Unhandled error in fread: {e}"), + }; + + for i in 0..n { + dst.write(buffer[i]); + dst = dst.offset(1); + } + + read_bytes += n; + } + + (read_bytes / a1 as usize) as u64 +} + +pub unsafe fn fread_unsafe( + a0: *mut ::std::ffi::c_void, + a1: u64, + a2: u64, + a3: *mut ::std::fs::File, +) -> u64 { + let total = a1.saturating_mul(a2) as usize; + let mut dst = a0 as *mut u8; + + let f = unsafe { (*a3).try_clone().expect("try_clone failed") }; + let mut reader = std::io::BufReader::with_capacity(64 * 1024, f); + + let mut read_bytes: usize = 0; + let mut buffer: [u8; 8192] = [0; 8192]; + + while read_bytes < total { + let remaining = total - read_bytes; + let to_read = std::cmp::min(buffer.len(), remaining); + + let n = match std::io::Read::read(&mut reader, &mut buffer[..to_read]) { + Ok(0) => break, + Ok(n) => n, + Err(ref e) if e.kind() == std::io::ErrorKind::Interrupted => continue, + Err(e) => panic!("Unhandled error in fread: {e}"), + }; + + for i in 0..n { + unsafe { + *dst = buffer[i]; + dst = dst.offset(1); + } + } + + read_bytes += n; + } + + (read_bytes / a1 as usize) as u64 +} diff --git a/rules/src/lib.rs b/rules/src/lib.rs index 18df7617..44df340b 100644 --- a/rules/src/lib.rs +++ b/rules/src/lib.rs @@ -4,21 +4,3 @@ #![allow(warnings)] mod modules; pub use modules::*; - -pub fn fread_refcount( - a0: ::libcc2rs::AnyPtr, - a1: u64, - a2: u64, - a3: ::libcc2rs::Ptr<::std::fs::File>, -) -> u64 { - modules::stdio_tgt_refcount::f5(a0, a1, a2, a3) -} - -pub unsafe fn fread_unsafe( - a0: *mut ::libc::c_void, - a1: u64, - a2: u64, - a3: *mut ::std::fs::File, -) -> u64 { - unsafe { modules::stdio_tgt_unsafe::f5(a0, a1, a2, a3) } -} diff --git a/rules/stdio/tgt_refcount.rs b/rules/stdio/tgt_refcount.rs index bf4457f9..5d31bfda 100644 --- a/rules/stdio/tgt_refcount.rs +++ b/rules/stdio/tgt_refcount.rs @@ -55,41 +55,8 @@ fn f4(a0: &mut ::std::fs::File, a1: i64, a2: i32) -> i32 { } } -pub(crate) fn f5(a0: AnyPtr, a1: u64, a2: u64, a3: Ptr<::std::fs::File>) -> u64 { - let total = a1.saturating_mul(a2) as usize; - let mut dst = a0 - .cast::() - .expect("fread: only supporting u8 pointers") - .clone(); - - let mut f = (*a3.upgrade().deref()) - .try_clone() - .expect("try_clone failed"); - let mut reader = std::io::BufReader::with_capacity(64 * 1024, f); - - let mut read_bytes: usize = 0; - let mut buffer: [u8; 8192] = [0; 8192]; - - while read_bytes < total { - let remaining = total - read_bytes; - let to_read = std::cmp::min(buffer.len(), remaining); - - let n = match std::io::Read::read(&mut reader, &mut buffer[..to_read]) { - Ok(0) => break, - Ok(n) => n, - Err(ref e) if e.kind() == std::io::ErrorKind::Interrupted => continue, - Err(e) => panic!("Unhandled error in fread: {e}"), - }; - - for i in 0..n { - dst.write(buffer[i]); - dst = dst.offset(1); - } - - read_bytes += n; - } - - (read_bytes / a1 as usize) as u64 +fn f5(a0: AnyPtr, a1: u64, a2: u64, a3: Ptr<::std::fs::File>) -> u64 { + libcc2rs::fread_refcount(a0, a1, a2, a3) } fn f6(a0: AnyPtr, a1: u64, a2: u64, a3: Ptr<::std::fs::File>) -> u64 { diff --git a/rules/stdio/tgt_unsafe.rs b/rules/stdio/tgt_unsafe.rs index ebcd5db8..1bd1b6e7 100644 --- a/rules/stdio/tgt_unsafe.rs +++ b/rules/stdio/tgt_unsafe.rs @@ -65,41 +65,8 @@ unsafe fn f4(a0: *mut ::std::fs::File, a1: i64, a2: i32) -> i32 { } } -pub(crate) unsafe fn f5( - a0: *mut ::libc::c_void, - a1: u64, - a2: u64, - a3: *mut ::std::fs::File, -) -> u64 { - let total = a1.saturating_mul(a2) as usize; - let mut dst = a0 as *mut u8; - - let mut f = (*a3).try_clone().expect("try_clone failed"); - let mut reader = std::io::BufReader::with_capacity(64 * 1024, f); - - let mut read_bytes: usize = 0; - let mut buffer: [u8; 8192] = [0; 8192]; - - while read_bytes < total { - let remaining = total - read_bytes; - let to_read = std::cmp::min(buffer.len(), remaining); - - let n = match std::io::Read::read(&mut reader, &mut buffer[..to_read]) { - Ok(0) => break, - Ok(n) => n, - Err(ref e) if e.kind() == std::io::ErrorKind::Interrupted => continue, - Err(e) => panic!("Unhandled error in fread: {e}"), - }; - - for i in 0..n { - *dst = buffer[i]; - dst = dst.offset(1); - } - - read_bytes += n; - } - - (read_bytes / a1 as usize) as u64 +unsafe fn f5(a0: *mut ::libc::c_void, a1: u64, a2: u64, a3: *mut ::std::fs::File) -> u64 { + unsafe { libcc2rs::fread_unsafe(a0 as *mut ::std::ffi::c_void, a1, a2, a3) } } unsafe fn f6(a0: *const ::libc::c_void, a1: u64, a2: u64, a3: *mut ::std::fs::File) -> u64 { diff --git a/tests/unit/out/refcount/fn_ptr_stdlib_compare.rs b/tests/unit/out/refcount/fn_ptr_stdlib_compare.rs index 11cd15cc..fd41242a 100644 --- a/tests/unit/out/refcount/fn_ptr_stdlib_compare.rs +++ b/tests/unit/out/refcount/fn_ptr_stdlib_compare.rs @@ -20,19 +20,19 @@ fn main_0() -> i32 { let fn1: Value) -> u64>> = Rc::new(RefCell::new(FnPtr::< fn(AnyPtr, u64, u64, Ptr<::std::fs::File>) -> u64, - >::new(rules::fread_refcount))); + >::new(libcc2rs::fread_refcount))); assert!({ let _lhs = (*fn1.borrow()).clone(); _lhs == FnPtr::) -> u64>::new( - rules::fread_refcount, + libcc2rs::fread_refcount, ) }); assert!(!((*fn1.borrow()).is_null())); let fn2: Value, u64, u64, AnyPtr) -> u64>> = Rc::new(RefCell::new( - FnPtr::) -> u64>::new(rules::fread_refcount) + FnPtr::) -> u64>::new(libcc2rs::fread_refcount) .cast::, u64, u64, AnyPtr) -> u64>(Some( (|a0: Ptr, a1: u64, a2: u64, a3: AnyPtr| -> u64 { - rules::fread_refcount(a0.to_any(), a1, a2, a3.cast::<::std::fs::File>().unwrap()) + libcc2rs::fread_refcount(a0.to_any(), a1, a2, a3.cast::<::std::fs::File>().unwrap()) }) as fn(Ptr, u64, u64, AnyPtr) -> u64, )), )); diff --git a/tests/unit/out/unsafe/fn_ptr_stdlib_compare.rs b/tests/unit/out/unsafe/fn_ptr_stdlib_compare.rs index f4eb3ab6..995ab80b 100644 --- a/tests/unit/out/unsafe/fn_ptr_stdlib_compare.rs +++ b/tests/unit/out/unsafe/fn_ptr_stdlib_compare.rs @@ -21,14 +21,14 @@ pub fn main() { } unsafe fn main_0() -> i32 { let mut fn1: Option u64> = - Some(rules::fread_unsafe); - assert!(((fn1) == (Some(rules::fread_unsafe)))); + Some(libcc2rs::fread_unsafe); + assert!(((fn1) == (Some(libcc2rs::fread_unsafe)))); assert!(!((fn1).is_none())); let mut fn2: Option u64> = std::mem::transmute::< Option u64>, Option u64>, - >(Some(rules::fread_unsafe)); + >(Some(libcc2rs::fread_unsafe)); assert!( ((fn1) == (std::mem::transmute::< From c6ab724587420ba0aa3fc26e4f9ab2dff10ad3eb Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Fri, 24 Apr 2026 13:45:54 +0100 Subject: [PATCH 48/56] Remove unused import --- libcc2rs/src/io.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/libcc2rs/src/io.rs b/libcc2rs/src/io.rs index 445bdbe3..507769b8 100644 --- a/libcc2rs/src/io.rs +++ b/libcc2rs/src/io.rs @@ -3,7 +3,6 @@ use crate::{AnyPtr, AsPointer, Ptr, Value}; use std::cell::{RefCell, UnsafeCell}; -use std::ops::Deref; use std::os::fd::{AsFd, FromRawFd, IntoRawFd}; use std::rc::Rc; From a689a259ce1a30f87ad7214110d6c503bcf4566c Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Fri, 24 Apr 2026 13:46:14 +0100 Subject: [PATCH 49/56] Update the IR --- rules/stdio/ir_refcount.json | 199 ++++------------------------------- rules/stdio/ir_unsafe.json | 124 +++------------------- rules/stdio/tgt_refcount.rs | 2 +- 3 files changed, 32 insertions(+), 293 deletions(-) diff --git a/rules/stdio/ir_refcount.json b/rules/stdio/ir_refcount.json index 4a588a5e..736057e2 100644 --- a/rules/stdio/ir_refcount.json +++ b/rules/stdio/ir_refcount.json @@ -420,217 +420,56 @@ "f5": { "body": [ { - "text": "let total = " - }, - { - "method_call": { - "receiver": [ - { - "placeholder": { - "arg": "a1", - "access": "read" - } - } - ], - "body": [ - { - "text": ".saturating_mul(" - }, - { - "placeholder": { - "arg": "a2", - "access": "read" - } - }, - { - "text": ")" - } - ] - } - }, - { - "text": " as usize;\n let mut dst = " + "text": "libcc2rs::fread_refcount(" }, { - "method_call": { - "receiver": [ - { - "method_call": { - "receiver": [ - { - "method_call": { - "receiver": [ - { - "placeholder": { - "arg": "a0", - "access": "read" - } - } - ], - "body": [ - { - "text": ".cast::()" - } - ] - } - } - ], - "body": [ - { - "text": ".expect(\"fread: only supporting u8 pointers\")" - } - ] - } - } - ], - "body": [ - { - "text": ".clone()" - } - ] - } - }, - { - "text": ";\n\n let mut f = " - }, - { - "method_call": { - "receiver": [ - { - "method_call": { - "receiver": [ - { - "text": "(*" - }, - { - "method_call": { - "receiver": [ - { - "method_call": { - "receiver": [ - { - "placeholder": { - "arg": "a3", - "access": "read" - } - } - ], - "body": [ - { - "text": ".upgrade()" - } - ] - } - } - ], - "body": [ - { - "text": ".deref()" - } - ] - } - }, - { - "text": ")" - } - ], - "body": [ - { - "text": ".try_clone()" - } - ] - } - } - ], - "body": [ - { - "text": ".expect(\"try_clone failed\")" - } - ] - } - }, - { - "text": ";\n let mut reader = std::io::BufReader::with_capacity(64 * 1024, f);\n\n let mut read_bytes: usize = 0;\n let mut buffer: [u8; 8192] = [0; 8192];\n\n while read_bytes < total {\n let remaining = total - read_bytes;\n let to_read = std::cmp::min(" - }, - { - "method_call": { - "receiver": [ - { - "text": "buffer" - } - ], - "body": [ - { - "text": ".len()" - } - ] + "placeholder": { + "arg": "a0", + "access": "read" } }, { - "text": ", remaining);\n\n let n = match std::io::Read::read(&mut reader, &mut buffer[..to_read]) {\n Ok(0) => break,\n Ok(n) => n,\n Err(ref e) if " + "text": ", " }, { - "method_call": { - "receiver": [ - { - "text": "e" - } - ], - "body": [ - { - "text": ".kind()" - } - ] + "placeholder": { + "arg": "a1", + "access": "read" } }, { - "text": " == std::io::ErrorKind::Interrupted => continue,\n Err(e) => panic!(\"Unhandled error in fread: {e}\"),\n };\n\n for i in 0..n {\n " + "text": ", " }, { - "method_call": { - "receiver": [ - { - "text": "dst" - } - ], - "body": [ - { - "text": ".write(buffer[i])" - } - ] + "placeholder": { + "arg": "a2", + "access": "read" } }, { - "text": ";\n dst = " + "text": ", " }, { "method_call": { "receiver": [ { - "text": "dst" + "placeholder": { + "arg": "a3", + "access": "read" + } } ], "body": [ { - "text": ".offset(1)" + "text": ".clone()" } ] } }, { - "text": ";\n }\n\n read_bytes += n;\n }\n\n (read_bytes / " - }, - { - "placeholder": { - "arg": "a1", - "access": "read" - } - }, - { - "text": " as usize) as u64" + "text": ")" } ], - "multi_statement": true, "params": { "a0": { "type": "AnyPtr" diff --git a/rules/stdio/ir_unsafe.json b/rules/stdio/ir_unsafe.json index 8b55e1ce..314394d0 100644 --- a/rules/stdio/ir_unsafe.json +++ b/rules/stdio/ir_unsafe.json @@ -477,36 +477,7 @@ "f5": { "body": [ { - "text": "let total = " - }, - { - "method_call": { - "receiver": [ - { - "placeholder": { - "arg": "a1", - "access": "read" - } - } - ], - "body": [ - { - "text": ".saturating_mul(" - }, - { - "placeholder": { - "arg": "a2", - "access": "read" - } - }, - { - "text": ")" - } - ] - } - }, - { - "text": " as usize;\n let mut dst = " + "text": "unsafe { libcc2rs::fread_unsafe(" }, { "placeholder": { @@ -515,107 +486,36 @@ } }, { - "text": " as *mut u8;\n\n let mut f = " + "text": " as *mut ::std::ffi::c_void, " }, { - "method_call": { - "receiver": [ - { - "method_call": { - "receiver": [ - { - "text": "(*" - }, - { - "placeholder": { - "arg": "a3", - "access": "read" - } - }, - { - "text": ")" - } - ], - "body": [ - { - "text": ".try_clone()" - } - ] - } - } - ], - "body": [ - { - "text": ".expect(\"try_clone failed\")" - } - ] - } - }, - { - "text": ";\n let mut reader = std::io::BufReader::with_capacity(64 * 1024, f);\n\n let mut read_bytes: usize = 0;\n let mut buffer: [u8; 8192] = [0; 8192];\n\n while read_bytes < total {\n let remaining = total - read_bytes;\n let to_read = std::cmp::min(" - }, - { - "method_call": { - "receiver": [ - { - "text": "buffer" - } - ], - "body": [ - { - "text": ".len()" - } - ] - } - }, - { - "text": ", remaining);\n\n let n = match std::io::Read::read(&mut reader, &mut buffer[..to_read]) {\n Ok(0) => break,\n Ok(n) => n,\n Err(ref e) if " - }, - { - "method_call": { - "receiver": [ - { - "text": "e" - } - ], - "body": [ - { - "text": ".kind()" - } - ] + "placeholder": { + "arg": "a1", + "access": "read" } }, { - "text": " == std::io::ErrorKind::Interrupted => continue,\n Err(e) => panic!(\"Unhandled error in fread: {e}\"),\n };\n\n for i in 0..n {\n *dst = buffer[i];\n dst = " + "text": ", " }, { - "method_call": { - "receiver": [ - { - "text": "dst" - } - ], - "body": [ - { - "text": ".offset(1)" - } - ] + "placeholder": { + "arg": "a2", + "access": "read" } }, { - "text": ";\n }\n\n read_bytes += n;\n }\n\n (read_bytes / " + "text": ", " }, { "placeholder": { - "arg": "a1", + "arg": "a3", "access": "read" } }, { - "text": " as usize) as u64" + "text": ") }" } ], - "multi_statement": true, "params": { "a0": { "type": "*mut ::libc::c_void", diff --git a/rules/stdio/tgt_refcount.rs b/rules/stdio/tgt_refcount.rs index 5d31bfda..98659d76 100644 --- a/rules/stdio/tgt_refcount.rs +++ b/rules/stdio/tgt_refcount.rs @@ -56,7 +56,7 @@ fn f4(a0: &mut ::std::fs::File, a1: i64, a2: i32) -> i32 { } fn f5(a0: AnyPtr, a1: u64, a2: u64, a3: Ptr<::std::fs::File>) -> u64 { - libcc2rs::fread_refcount(a0, a1, a2, a3) + libcc2rs::fread_refcount(a0, a1, a2, a3.clone()) } fn f6(a0: AnyPtr, a1: u64, a2: u64, a3: Ptr<::std::fs::File>) -> u64 { From ff7719a827d115e50b97042f89a9dd06d0c2e23b Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Fri, 24 Apr 2026 13:46:41 +0100 Subject: [PATCH 50/56] Call fread both through func ptr and direct func call --- tests/unit/fn_ptr_stdlib_compare.cpp | 32 ++-- .../out/refcount/fn_ptr_stdlib_compare.rs | 159 +++++++++++----- .../unit/out/unsafe/fn_ptr_stdlib_compare.rs | 175 ++++++++++++------ 3 files changed, 250 insertions(+), 116 deletions(-) diff --git a/tests/unit/fn_ptr_stdlib_compare.cpp b/tests/unit/fn_ptr_stdlib_compare.cpp index a66e55e4..38f2e134 100644 --- a/tests/unit/fn_ptr_stdlib_compare.cpp +++ b/tests/unit/fn_ptr_stdlib_compare.cpp @@ -9,6 +9,23 @@ typedef size_t (*fread_alternative_t)(char *, size_t, size_t, void *); size_t my_alternative_fread(char *p, size_t n, size_t m, void *f) { return 22; } +#define CHECK_FREAD(call) \ + do { \ + FILE *stream = fopen("/dev/zero", "rb"); \ + assert(stream != nullptr); \ + char buf[16]; \ + memset(buf, 'X', sizeof(buf)); \ + size_t n = (call)(buf, 1, 10, stream); \ + assert(n == 10); \ + for (int i = 0; i < 10; ++i) { \ + assert(buf[i] == 0); \ + } \ + for (int i = 10; i < 16; ++i) { \ + assert(buf[i] == 'X'); \ + } \ + fclose(stream); \ + } while (0) + int main() { fread_t fn1 = fread; assert(fn1 == fread); @@ -20,19 +37,8 @@ int main() { fread_t f3 = (fread_t)my_alternative_fread; assert((*f3)(nullptr, 0, 0, nullptr) == 22); - FILE *stream = fopen("/dev/zero", "rb"); - assert(stream != nullptr); - char buf[16]; - memset(buf, 'X', sizeof(buf)); - size_t n = (*fn1)(buf, 1, 10, stream); - assert(n == 10); - for (int i = 0; i < 10; ++i) { - assert(buf[i] == 0); - } - for (int i = 10; i < 16; ++i) { - assert(buf[i] == 'X'); - } - fclose(stream); + CHECK_FREAD(fread); + CHECK_FREAD((*fn1)); return 0; } diff --git a/tests/unit/out/refcount/fn_ptr_stdlib_compare.rs b/tests/unit/out/refcount/fn_ptr_stdlib_compare.rs index fd41242a..b71cb285 100644 --- a/tests/unit/out/refcount/fn_ptr_stdlib_compare.rs +++ b/tests/unit/out/refcount/fn_ptr_stdlib_compare.rs @@ -66,57 +66,116 @@ fn main_0() -> i32 { (*(*f3.borrow()))(_arg0, _arg1, _arg2, _arg3) }) == 22_u64) ); - let stream: Value> = Rc::new(RefCell::new( - match Ptr::from_string_literal("rb").to_rust_string() { - v if v == "rb" => std::fs::OpenOptions::new() - .read(true) - .open(Ptr::from_string_literal("/dev/zero").to_rust_string()) - .ok() - .map_or(Ptr::null(), |f| Ptr::alloc(f)), - v if v == "wb" => std::fs::OpenOptions::new() - .write(true) - .create(true) - .truncate(true) - .open(Ptr::from_string_literal("/dev/zero").to_rust_string()) - .ok() - .map_or(Ptr::null(), |f| Ptr::alloc(f)), - _ => panic!("unsupported mode"), - }, - )); - assert!(!((*stream.borrow()).is_null())); - let buf: Value> = Rc::new(RefCell::new( - (0..16).map(|_| ::default()).collect::>(), - )); - { - ((buf.as_pointer() as Ptr) as Ptr).to_any().memset( - (('X' as u8) as i32) as u8, - ::std::mem::size_of::<[u8; 16]>() as u64 as usize, - ); - ((buf.as_pointer() as Ptr) as Ptr).to_any().clone() - }; - let n: Value = Rc::new(RefCell::new( - ({ - let _arg0: AnyPtr = ((buf.as_pointer() as Ptr) as Ptr).to_any(); - let _arg1: u64 = 1_u64; - let _arg2: u64 = 10_u64; - let _arg3: Ptr<::std::fs::File> = (*stream.borrow()).clone(); - (*(*fn1.borrow()))(_arg0, _arg1, _arg2, _arg3) - }), - )); - assert!(((*n.borrow()) == 10_u64)); - let i: Value = Rc::new(RefCell::new(0)); - 'loop_: while ((*i.borrow()) < 10) { - assert!((((*buf.borrow())[(*i.borrow()) as usize] as i32) == 0)); - (*i.borrow_mut()).prefix_inc(); + 'loop_: loop { + let stream: Value> = Rc::new(RefCell::new( + match Ptr::from_string_literal("rb").to_rust_string() { + v if v == "rb" => std::fs::OpenOptions::new() + .read(true) + .open(Ptr::from_string_literal("/dev/zero").to_rust_string()) + .ok() + .map_or(Ptr::null(), |f| Ptr::alloc(f)), + v if v == "wb" => std::fs::OpenOptions::new() + .write(true) + .create(true) + .truncate(true) + .open(Ptr::from_string_literal("/dev/zero").to_rust_string()) + .ok() + .map_or(Ptr::null(), |f| Ptr::alloc(f)), + _ => panic!("unsupported mode"), + }, + )); + assert!(!((*stream.borrow()).is_null())); + let buf: Value> = Rc::new(RefCell::new( + (0..16).map(|_| ::default()).collect::>(), + )); + { + ((buf.as_pointer() as Ptr) as Ptr).to_any().memset( + (('X' as u8) as i32) as u8, + ::std::mem::size_of::<[u8; 16]>() as u64 as usize, + ); + ((buf.as_pointer() as Ptr) as Ptr).to_any().clone() + }; + let n: Value = Rc::new(RefCell::new(libcc2rs::fread_refcount( + ((buf.as_pointer() as Ptr) as Ptr).to_any(), + 1_u64, + 10_u64, + (*stream.borrow()).clone(), + ))); + assert!(((*n.borrow()) == 10_u64)); + let i: Value = Rc::new(RefCell::new(0)); + 'loop_: while ((*i.borrow()) < 10) { + assert!((((*buf.borrow())[(*i.borrow()) as usize] as i32) == 0)); + (*i.borrow_mut()).prefix_inc(); + } + let i: Value = Rc::new(RefCell::new(10)); + 'loop_: while ((*i.borrow()) < 16) { + assert!((((*buf.borrow())[(*i.borrow()) as usize] as i32) == (('X' as u8) as i32))); + (*i.borrow_mut()).prefix_inc(); + } + { + (*stream.borrow()).delete(); + 0 + }; + if !(0 != 0) { + break; + } } - let i: Value = Rc::new(RefCell::new(10)); - 'loop_: while ((*i.borrow()) < 16) { - assert!((((*buf.borrow())[(*i.borrow()) as usize] as i32) == (('X' as u8) as i32))); - (*i.borrow_mut()).prefix_inc(); + 'loop_: loop { + let stream: Value> = Rc::new(RefCell::new( + match Ptr::from_string_literal("rb").to_rust_string() { + v if v == "rb" => std::fs::OpenOptions::new() + .read(true) + .open(Ptr::from_string_literal("/dev/zero").to_rust_string()) + .ok() + .map_or(Ptr::null(), |f| Ptr::alloc(f)), + v if v == "wb" => std::fs::OpenOptions::new() + .write(true) + .create(true) + .truncate(true) + .open(Ptr::from_string_literal("/dev/zero").to_rust_string()) + .ok() + .map_or(Ptr::null(), |f| Ptr::alloc(f)), + _ => panic!("unsupported mode"), + }, + )); + assert!(!((*stream.borrow()).is_null())); + let buf: Value> = Rc::new(RefCell::new( + (0..16).map(|_| ::default()).collect::>(), + )); + { + ((buf.as_pointer() as Ptr) as Ptr).to_any().memset( + (('X' as u8) as i32) as u8, + ::std::mem::size_of::<[u8; 16]>() as u64 as usize, + ); + ((buf.as_pointer() as Ptr) as Ptr).to_any().clone() + }; + let n: Value = Rc::new(RefCell::new( + ({ + let _arg0: AnyPtr = ((buf.as_pointer() as Ptr) as Ptr).to_any(); + let _arg1: u64 = 1_u64; + let _arg2: u64 = 10_u64; + let _arg3: Ptr<::std::fs::File> = (*stream.borrow()).clone(); + (*(*fn1.borrow()))(_arg0, _arg1, _arg2, _arg3) + }), + )); + assert!(((*n.borrow()) == 10_u64)); + let i: Value = Rc::new(RefCell::new(0)); + 'loop_: while ((*i.borrow()) < 10) { + assert!((((*buf.borrow())[(*i.borrow()) as usize] as i32) == 0)); + (*i.borrow_mut()).prefix_inc(); + } + let i: Value = Rc::new(RefCell::new(10)); + 'loop_: while ((*i.borrow()) < 16) { + assert!((((*buf.borrow())[(*i.borrow()) as usize] as i32) == (('X' as u8) as i32))); + (*i.borrow_mut()).prefix_inc(); + } + { + (*stream.borrow()).delete(); + 0 + }; + if !(0 != 0) { + break; + } } - { - (*stream.borrow()).delete(); - 0 - }; return 0; } diff --git a/tests/unit/out/unsafe/fn_ptr_stdlib_compare.rs b/tests/unit/out/unsafe/fn_ptr_stdlib_compare.rs index 995ab80b..c0e91c0f 100644 --- a/tests/unit/out/unsafe/fn_ptr_stdlib_compare.rs +++ b/tests/unit/out/unsafe/fn_ptr_stdlib_compare.rs @@ -50,63 +50,132 @@ unsafe fn main_0() -> i32 { (f3).unwrap()(_arg0, _arg1, _arg2, _arg3) }) == (22_u64)) ); - let mut stream: *mut ::std::fs::File = - match std::ffi::CStr::from_ptr(b"rb\0".as_ptr() as *const i8) - .to_str() - .expect("invalid c-string") + 'loop_: loop { + let mut stream: *mut ::std::fs::File = + match std::ffi::CStr::from_ptr(b"rb\0".as_ptr() as *const i8) + .to_str() + .expect("invalid c-string") + { + v if v == "rb" => std::fs::OpenOptions::new() + .read(true) + .open( + std::ffi::CStr::from_ptr(b"/dev/zero\0".as_ptr() as *const i8) + .to_str() + .expect("invalid c-string"), + ) + .ok() + .map_or(std::ptr::null_mut(), |f| Box::into_raw(Box::new(f))), + v if v == "wb" => std::fs::OpenOptions::new() + .write(true) + .create(true) + .truncate(true) + .open( + std::ffi::CStr::from_ptr(b"/dev/zero\0".as_ptr() as *const i8) + .to_str() + .expect("invalid c-string"), + ) + .ok() + .map_or(std::ptr::null_mut(), |f| Box::into_raw(Box::new(f))), + _ => panic!("unsupported mode"), + }; + assert!(!((stream).is_null())); + let mut buf: [u8; 16] = [0_u8; 16]; { - v if v == "rb" => std::fs::OpenOptions::new() - .read(true) - .open( - std::ffi::CStr::from_ptr(b"/dev/zero\0".as_ptr() as *const i8) - .to_str() - .expect("invalid c-string"), - ) - .ok() - .map_or(std::ptr::null_mut(), |f| Box::into_raw(Box::new(f))), - v if v == "wb" => std::fs::OpenOptions::new() - .write(true) - .create(true) - .truncate(true) - .open( - std::ffi::CStr::from_ptr(b"/dev/zero\0".as_ptr() as *const i8) - .to_str() - .expect("invalid c-string"), - ) - .ok() - .map_or(std::ptr::null_mut(), |f| Box::into_raw(Box::new(f))), - _ => panic!("unsupported mode"), + let byte_0 = (buf.as_mut_ptr() as *mut u8 as *mut ::libc::c_void) as *mut u8; + for offset in 0..::std::mem::size_of::<[u8; 16]>() as u64 { + *byte_0.offset(offset as isize) = (('X' as u8) as i32) as u8; + } + (buf.as_mut_ptr() as *mut u8 as *mut ::libc::c_void) }; - assert!(!((stream).is_null())); - let mut buf: [u8; 16] = [0_u8; 16]; - { - let byte_0 = (buf.as_mut_ptr() as *mut u8 as *mut ::libc::c_void) as *mut u8; - for offset in 0..::std::mem::size_of::<[u8; 16]>() as u64 { - *byte_0.offset(offset as isize) = (('X' as u8) as i32) as u8; + let mut n: u64 = unsafe { + libcc2rs::fread_unsafe( + (buf.as_mut_ptr() as *mut u8 as *mut ::libc::c_void) as *mut ::std::ffi::c_void, + 1_u64, + 10_u64, + stream, + ) + }; + assert!(((n) == (10_u64))); + let mut i: i32 = 0; + 'loop_: while ((i) < (10)) { + assert!(((buf[(i) as usize] as i32) == (0))); + i.prefix_inc(); + } + let mut i: i32 = 10; + 'loop_: while ((i) < (16)) { + assert!(((buf[(i) as usize] as i32) == (('X' as u8) as i32))); + i.prefix_inc(); + } + { + Box::from_raw(stream); + 0 + }; + if !(0 != 0) { + break; } - (buf.as_mut_ptr() as *mut u8 as *mut ::libc::c_void) - }; - let mut n: u64 = (unsafe { - let _arg0: *mut ::libc::c_void = (buf.as_mut_ptr() as *mut u8 as *mut ::libc::c_void); - let _arg1: u64 = 1_u64; - let _arg2: u64 = 10_u64; - let _arg3: *mut ::std::fs::File = stream; - (fn1).unwrap()(_arg0, _arg1, _arg2, _arg3) - }); - assert!(((n) == (10_u64))); - let mut i: i32 = 0; - 'loop_: while ((i) < (10)) { - assert!(((buf[(i) as usize] as i32) == (0))); - i.prefix_inc(); } - let mut i: i32 = 10; - 'loop_: while ((i) < (16)) { - assert!(((buf[(i) as usize] as i32) == (('X' as u8) as i32))); - i.prefix_inc(); + 'loop_: loop { + let mut stream: *mut ::std::fs::File = + match std::ffi::CStr::from_ptr(b"rb\0".as_ptr() as *const i8) + .to_str() + .expect("invalid c-string") + { + v if v == "rb" => std::fs::OpenOptions::new() + .read(true) + .open( + std::ffi::CStr::from_ptr(b"/dev/zero\0".as_ptr() as *const i8) + .to_str() + .expect("invalid c-string"), + ) + .ok() + .map_or(std::ptr::null_mut(), |f| Box::into_raw(Box::new(f))), + v if v == "wb" => std::fs::OpenOptions::new() + .write(true) + .create(true) + .truncate(true) + .open( + std::ffi::CStr::from_ptr(b"/dev/zero\0".as_ptr() as *const i8) + .to_str() + .expect("invalid c-string"), + ) + .ok() + .map_or(std::ptr::null_mut(), |f| Box::into_raw(Box::new(f))), + _ => panic!("unsupported mode"), + }; + assert!(!((stream).is_null())); + let mut buf: [u8; 16] = [0_u8; 16]; + { + let byte_0 = (buf.as_mut_ptr() as *mut u8 as *mut ::libc::c_void) as *mut u8; + for offset in 0..::std::mem::size_of::<[u8; 16]>() as u64 { + *byte_0.offset(offset as isize) = (('X' as u8) as i32) as u8; + } + (buf.as_mut_ptr() as *mut u8 as *mut ::libc::c_void) + }; + let mut n: u64 = (unsafe { + let _arg0: *mut ::libc::c_void = (buf.as_mut_ptr() as *mut u8 as *mut ::libc::c_void); + let _arg1: u64 = 1_u64; + let _arg2: u64 = 10_u64; + let _arg3: *mut ::std::fs::File = stream; + (fn1).unwrap()(_arg0, _arg1, _arg2, _arg3) + }); + assert!(((n) == (10_u64))); + let mut i: i32 = 0; + 'loop_: while ((i) < (10)) { + assert!(((buf[(i) as usize] as i32) == (0))); + i.prefix_inc(); + } + let mut i: i32 = 10; + 'loop_: while ((i) < (16)) { + assert!(((buf[(i) as usize] as i32) == (('X' as u8) as i32))); + i.prefix_inc(); + } + { + Box::from_raw(stream); + 0 + }; + if !(0 != 0) { + break; + } } - { - Box::from_raw(stream); - 0 - }; return 0; } From b993692ae3be179136551f14dfe45cc2056f57bc Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Fri, 24 Apr 2026 13:51:18 +0100 Subject: [PATCH 51/56] Remove unintented generated files --- .../unit/out/refcount/lambda_capture_local.rs | 74 ------------------- tests/unit/out/refcount/lambda_nocapture.rs | 74 ------------------- tests/unit/out/unsafe/lambda_capture_local.rs | 72 ------------------ tests/unit/out/unsafe/lambda_nocapture.rs | 64 ---------------- 4 files changed, 284 deletions(-) delete mode 100644 tests/unit/out/refcount/lambda_capture_local.rs delete mode 100644 tests/unit/out/refcount/lambda_nocapture.rs delete mode 100644 tests/unit/out/unsafe/lambda_capture_local.rs delete mode 100644 tests/unit/out/unsafe/lambda_nocapture.rs diff --git a/tests/unit/out/refcount/lambda_capture_local.rs b/tests/unit/out/refcount/lambda_capture_local.rs deleted file mode 100644 index 5b4a5250..00000000 --- a/tests/unit/out/refcount/lambda_capture_local.rs +++ /dev/null @@ -1,74 +0,0 @@ -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 base: Value = Rc::new(RefCell::new(10)); - let factor: Value = Rc::new(RefCell::new(3)); - let add_base: Value i32>> = Rc::new(RefCell::new(Rc::new( - (|x: i32| { - let x: Value = Rc::new(RefCell::new(x)); - return ((*x.borrow()) + (*base.borrow())); - }), - ))); - let scale: Value i32>> = Rc::new(RefCell::new(Rc::new( - (|x: i32| { - let x: Value = Rc::new(RefCell::new(x)); - return ((*x.borrow()) * (*factor.borrow())); - }), - ))); - assert!( - (({ - let _x: i32 = 5; - (*add_base.borrow())(_x) - }) == 15) - ); - assert!( - (({ - let _x: i32 = 4; - (*scale.borrow())(_x) - }) == 12) - ); - (*base.borrow_mut()) = 100; - assert!( - (({ - let _x: i32 = 5; - (*add_base.borrow())(_x) - }) == 105) - ); - assert!( - (({ - let _x: i32 = 4; - (*scale.borrow())(_x) - }) == 12) - ); - let sum: Value = Rc::new(RefCell::new(0)); - let accumulate: Value> = Rc::new(RefCell::new(Rc::new( - (|x: i32| { - let x: Value = Rc::new(RefCell::new(x)); - (*sum.borrow_mut()) += (*x.borrow()); - }), - ))); - ({ - let _x: i32 = 1; - (*accumulate.borrow())(_x) - }); - ({ - let _x: i32 = 2; - (*accumulate.borrow())(_x) - }); - ({ - let _x: i32 = 3; - (*accumulate.borrow())(_x) - }); - assert!(((*sum.borrow()) == 6)); - return 0; -} diff --git a/tests/unit/out/refcount/lambda_nocapture.rs b/tests/unit/out/refcount/lambda_nocapture.rs deleted file mode 100644 index 6936dc8a..00000000 --- a/tests/unit/out/refcount/lambda_nocapture.rs +++ /dev/null @@ -1,74 +0,0 @@ -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 call_0(f: Option i32>, a: i32, b: i32) -> i32 { - let f: Value i32>> = Rc::new(RefCell::new(f)); - let a: Value = Rc::new(RefCell::new(a)); - let b: Value = Rc::new(RefCell::new(b)); - return ({ - let _arg0: i32 = (*a.borrow()); - let _arg1: i32 = (*b.borrow()); - (*f.borrow()).unwrap()(_arg0, _arg1) - }); -} -pub fn main() { - std::process::exit(main_0()); -} -fn main_0() -> i32 { - let add: Value i32>> = Rc::new(RefCell::new(Some(|a: i32, b: i32| { - let a: Value = Rc::new(RefCell::new(a)); - let b: Value = Rc::new(RefCell::new(b)); - return ((*a.borrow()) + (*b.borrow())); - }))); - let sub: Value i32>> = Rc::new(RefCell::new(Some(|a: i32, b: i32| { - let a: Value = Rc::new(RefCell::new(a)); - let b: Value = Rc::new(RefCell::new(b)); - return ((*a.borrow()) - (*b.borrow())); - }))); - assert!(!((*add.borrow()).is_none())); - assert!({ - let _lhs = (*add.borrow()).clone(); - _lhs != (*sub.borrow()).clone() - }); - assert!( - (({ - let _arg0: i32 = 2; - let _arg1: i32 = 3; - (*add.borrow()).unwrap()(_arg0, _arg1) - }) == 5) - ); - assert!( - (({ - let _arg0: i32 = 10; - let _arg1: i32 = 4; - (*sub.borrow()).unwrap()(_arg0, _arg1) - }) == 6) - ); - assert!( - (({ - let _f: Option i32> = (*add.borrow()).clone(); - let _a: i32 = 7; - let _b: i32 = 8; - call_0(_f, _a, _b) - }) == 15) - ); - assert!( - (({ - let _f: Option i32> = Some(|a: i32, b: i32| { - let a: Value = Rc::new(RefCell::new(a)); - let b: Value = Rc::new(RefCell::new(b)); - return ((*a.borrow()) * (*b.borrow())); - }); - let _a: i32 = 6; - let _b: i32 = 7; - call_0(_f, _a, _b) - }) == 42) - ); - return 0; -} diff --git a/tests/unit/out/unsafe/lambda_capture_local.rs b/tests/unit/out/unsafe/lambda_capture_local.rs deleted file mode 100644 index 9e449d6d..00000000 --- a/tests/unit/out/unsafe/lambda_capture_local.rs +++ /dev/null @@ -1,72 +0,0 @@ -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 base: i32 = 10; - let mut factor: i32 = 3; - assert!( - ((unsafe { - let _x: i32 = 5; - (|x: i32| { - return ((x) + (base)); - })(_x) - }) == (15)) - ); - assert!( - ((unsafe { - let _x: i32 = 4; - (|x: i32| { - return ((x) * (factor)); - })(_x) - }) == (12)) - ); - base = 100; - assert!( - ((unsafe { - let _x: i32 = 5; - (|x: i32| { - return ((x) + (base)); - })(_x) - }) == (105)) - ); - assert!( - ((unsafe { - let _x: i32 = 4; - (|x: i32| { - return ((x) * (factor)); - })(_x) - }) == (12)) - ); - let mut sum: i32 = 0; - (unsafe { - let _x: i32 = 1; - (|x: i32| { - sum += x; - })(_x) - }); - (unsafe { - let _x: i32 = 2; - (|x: i32| { - sum += x; - })(_x) - }); - (unsafe { - let _x: i32 = 3; - (|x: i32| { - sum += x; - })(_x) - }); - assert!(((sum) == (6))); - return 0; -} diff --git a/tests/unit/out/unsafe/lambda_nocapture.rs b/tests/unit/out/unsafe/lambda_nocapture.rs deleted file mode 100644 index d71ab107..00000000 --- a/tests/unit/out/unsafe/lambda_nocapture.rs +++ /dev/null @@ -1,64 +0,0 @@ -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 call_0(mut f: Option i32>, mut a: i32, mut b: i32) -> i32 { - return (unsafe { - let _arg0: i32 = a; - let _arg1: i32 = b; - (f).unwrap()(_arg0, _arg1) - }); -} -pub fn main() { - unsafe { - std::process::exit(main_0() as i32); - } -} -unsafe fn main_0() -> i32 { - let mut add: Option i32> = Some(|a: i32, b: i32| { - return ((a) + (b)); - }); - let mut sub: Option i32> = Some(|a: i32, b: i32| { - return ((a) - (b)); - }); - assert!(!((add).is_none())); - assert!(((add) != (sub))); - assert!( - ((unsafe { - let _arg0: i32 = 2; - let _arg1: i32 = 3; - (add).unwrap()(_arg0, _arg1) - }) == (5)) - ); - assert!( - ((unsafe { - let _arg0: i32 = 10; - let _arg1: i32 = 4; - (sub).unwrap()(_arg0, _arg1) - }) == (6)) - ); - assert!( - ((unsafe { - let _f: Option i32> = add; - let _a: i32 = 7; - let _b: i32 = 8; - call_0(_f, _a, _b) - }) == (15)) - ); - assert!( - ((unsafe { - let _f: Option i32> = Some(|a: i32, b: i32| { - return ((a) * (b)); - }); - let _a: i32 = 6; - let _b: i32 = 7; - call_0(_f, _a, _b) - }) == (42)) - ); - return 0; -} From 6234360c309b46632c8f37455150ddaf711b387c Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Fri, 24 Apr 2026 13:51:35 +0100 Subject: [PATCH 52/56] Revert making rules visible --- rules/src/lib.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/rules/src/lib.rs b/rules/src/lib.rs index 44df340b..9efa0643 100644 --- a/rules/src/lib.rs +++ b/rules/src/lib.rs @@ -3,4 +3,3 @@ #![allow(warnings)] mod modules; -pub use modules::*; From 3890493302f5f784f541074f704200e4c132e828 Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Fri, 24 Apr 2026 13:53:21 +0100 Subject: [PATCH 53/56] Delete unrelated file --- tests/unit/out/unsafe/fn_ptr_mapped.rs | 45 -------------------------- 1 file changed, 45 deletions(-) delete mode 100644 tests/unit/out/unsafe/fn_ptr_mapped.rs diff --git a/tests/unit/out/unsafe/fn_ptr_mapped.rs b/tests/unit/out/unsafe/fn_ptr_mapped.rs deleted file mode 100644 index e19a1f5d..00000000 --- a/tests/unit/out/unsafe/fn_ptr_mapped.rs +++ /dev/null @@ -1,45 +0,0 @@ -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 foo_0( - mut fn_: Rc u64>, - mut p: *mut ::libc::c_void, - mut size: u64, - mut nmemb: u64, - mut f: *mut ::std::fs::File, -) -> u64 { - return (unsafe { - let _arg0: *mut ::libc::c_void = p; - let _arg1: u64 = size; - let _arg2: u64 = nmemb; - let _arg3: *mut ::std::fs::File = f; - fn_(_arg0, _arg1, _arg2, _arg3) - }); -} -pub fn main() { - unsafe { - std::process::exit(main_0() as i32); - } -} -unsafe fn main_0() -> i32 { - let mut fn_: Rc u64> = - (Rc::new(|a0, a1, a2, a3| unsafe { fread_1(a0, a1, a2, a3) }) - as Rc u64>); - (unsafe { - let _fn: Rc u64> = - Rc::new(|a0, a1, a2, a3| unsafe { fread_1(a0, a1, a2, a3) }); - let _p: *mut ::libc::c_void = Default::default(); - let _size: u64 = 0_u64; - let _nmemb: u64 = 0_u64; - let _f: *mut ::std::fs::File = Default::default(); - foo_0(_fn, _p, _size, _nmemb, _f) - }); - assert!(((fn_) != (Default::default()))); - return 0; -} From 49034f3d4c19ec24da6cc76052719d09d075a171 Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Fri, 24 Apr 2026 14:07:07 +0100 Subject: [PATCH 54/56] Use strinv_view in Trim --- cpp2rust/converter/converter_lib.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/cpp2rust/converter/converter_lib.cpp b/cpp2rust/converter/converter_lib.cpp index 4b28f0a7..c257537d 100644 --- a/cpp2rust/converter/converter_lib.cpp +++ b/cpp2rust/converter/converter_lib.cpp @@ -662,10 +662,11 @@ clang::Expr *CreateConversionToBool(clang::Expr *expr, clang::ASTContext &ctx) { /*BasePath=*/nullptr, clang::VK_PRValue, clang::FPOptionsOverride()); } -static void Trim(std::string &s) { +static std::string_view Trim(std::string_view s) { auto is_space = [](unsigned char c) { return std::isspace(c); }; - s.erase(s.begin(), std::find_if_not(s.begin(), s.end(), is_space)); - s.erase(std::find_if_not(s.rbegin(), s.rend(), is_space).base(), s.end()); + auto b = std::find_if_not(s.begin(), s.end(), is_space); + auto e = std::find_if_not(s.rbegin(), s.rend(), is_space).base(); + return {b, e}; } void Unwrap(std::string &s, std::string_view prefix, std::string_view suffix) { From 7aa0fdb67c0aa2ff194ca65d40f3e92b23104747 Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Fri, 24 Apr 2026 14:07:21 +0100 Subject: [PATCH 55/56] Move overlapping check in assert --- cpp2rust/converter/converter_lib.cpp | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/cpp2rust/converter/converter_lib.cpp b/cpp2rust/converter/converter_lib.cpp index c257537d..a7f12c8d 100644 --- a/cpp2rust/converter/converter_lib.cpp +++ b/cpp2rust/converter/converter_lib.cpp @@ -670,10 +670,13 @@ static std::string_view Trim(std::string_view s) { } void Unwrap(std::string &s, std::string_view prefix, std::string_view suffix) { - Trim(s); - if (s.size() >= prefix.size() + suffix.size() && s.starts_with(prefix) && - s.ends_with(suffix)) { - s = s.substr(prefix.size(), s.size() - prefix.size() - suffix.size()); + auto trimmed = Trim(s); + if (trimmed.starts_with(prefix) && trimmed.ends_with(suffix)) { + assert(trimmed.size() >= prefix.size() + suffix.size() && + "prefix and suffix overlap in s"); + trimmed.remove_prefix(prefix.size()); + trimmed.remove_suffix(suffix.size()); + s = std::string(trimmed); } } From c6c7024d2ef8910657674a68ce33021a8f37e393 Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Fri, 24 Apr 2026 14:19:58 +0100 Subject: [PATCH 56/56] Fix clippy errors --- libcc2rs/src/io.rs | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/libcc2rs/src/io.rs b/libcc2rs/src/io.rs index 507769b8..e1f77112 100644 --- a/libcc2rs/src/io.rs +++ b/libcc2rs/src/io.rs @@ -85,8 +85,8 @@ pub fn fread_refcount(a0: AnyPtr, a1: u64, a2: u64, a3: Ptr<::std::fs::File>) -> Err(e) => panic!("Unhandled error in fread: {e}"), }; - for i in 0..n { - dst.write(buffer[i]); + for &byte in &buffer[..n] { + dst.write(byte); dst = dst.offset(1); } @@ -96,6 +96,10 @@ pub fn fread_refcount(a0: AnyPtr, a1: u64, a2: u64, a3: Ptr<::std::fs::File>) -> (read_bytes / a1 as usize) as u64 } +/// # Safety +/// +/// `a0` must point to a writable buffer of at least `a1 * a2` bytes, and `a3` +/// must point to a valid, open `std::fs::File`. pub unsafe fn fread_unsafe( a0: *mut ::std::ffi::c_void, a1: u64, @@ -122,9 +126,9 @@ pub unsafe fn fread_unsafe( Err(e) => panic!("Unhandled error in fread: {e}"), }; - for i in 0..n { + for &byte in &buffer[..n] { unsafe { - *dst = buffer[i]; + *dst = byte; dst = dst.offset(1); } }