From 4bfaa37256e4c8e66caf6f75fd4ab9ae1d526878 Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Fri, 10 Apr 2026 13:53:03 +0100 Subject: [PATCH 01/23] Use int to allow C code to compile --- cpp2rust/compat/assert.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpp2rust/compat/assert.h b/cpp2rust/compat/assert.h index a18bec79..09282934 100644 --- a/cpp2rust/compat/assert.h +++ b/cpp2rust/compat/assert.h @@ -5,6 +5,6 @@ #undef assert -void __cpp2rust_assert_fail(bool condition) __attribute__((noreturn)); +void __cpp2rust_assert_fail(int condition) __attribute__((noreturn)); #define assert(expr) __cpp2rust_assert_fail(expr) From 0254d39dfe93aaf9b53787d8b722d7b0fcd74a84 Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Fri, 10 Apr 2026 13:53:43 +0100 Subject: [PATCH 02/23] Use getAsRecordDecl for C compatibility --- cpp2rust/compat/assert.h | 6 +++++- cpp2rust/converter/converter.cpp | 2 +- cpp2rust/converter/converter_lib.cpp | 1 + 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/cpp2rust/compat/assert.h b/cpp2rust/compat/assert.h index 09282934..a06ef790 100644 --- a/cpp2rust/compat/assert.h +++ b/cpp2rust/compat/assert.h @@ -5,6 +5,10 @@ #undef assert -void __cpp2rust_assert_fail(int condition) __attribute__((noreturn)); +#ifndef __cplusplus +#include +#endif + +void __cpp2rust_assert_fail(bool condition) __attribute__((noreturn)); #define assert(expr) __cpp2rust_assert_fail(expr) diff --git a/cpp2rust/converter/converter.cpp b/cpp2rust/converter/converter.cpp index a0736540..7459788d 100644 --- a/cpp2rust/converter/converter.cpp +++ b/cpp2rust/converter/converter.cpp @@ -247,7 +247,7 @@ bool Converter::VisitPointerType(clang::PointerType *type) { auto pointee_type = type->getPointeeType(); StrCat(pointee_type.isConstQualified() ? keyword::kConst : keyword_mut_); if (pointee_type->isRecordType() && - abstract_structs_.contains(GetID(pointee_type->getAsCXXRecordDecl()))) { + abstract_structs_.contains(GetID(pointee_type->getAsRecordDecl()))) { StrCat(keyword::kDyn); } return Convert(pointee_type); diff --git a/cpp2rust/converter/converter_lib.cpp b/cpp2rust/converter/converter_lib.cpp index 08a6bd55..330c7e11 100644 --- a/cpp2rust/converter/converter_lib.cpp +++ b/cpp2rust/converter/converter_lib.cpp @@ -253,6 +253,7 @@ unsigned GetArraySize(clang::QualType array_type) { } std::string GetID(const clang::Decl *decl) { + assert(decl); const auto file_name = GetFileName(decl); const auto line_num = GetLineNumber(decl); const auto column_num = GetColumnNumber(decl); From 2a09ea260d7c5785f90287e6bd52c671d9cbdcdf Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Fri, 10 Apr 2026 16:51:19 +0100 Subject: [PATCH 03/23] Add va_args helpers --- cpp2rust/converter/converter_lib.cpp | 41 ++++++++++++++++++++++++++++ cpp2rust/converter/converter_lib.h | 8 ++++++ 2 files changed, 49 insertions(+) diff --git a/cpp2rust/converter/converter_lib.cpp b/cpp2rust/converter/converter_lib.cpp index 330c7e11..c6b513d9 100644 --- a/cpp2rust/converter/converter_lib.cpp +++ b/cpp2rust/converter/converter_lib.cpp @@ -558,4 +558,45 @@ bool IsRedundantCopyInConversion(clang::ASTContext &ctx, return parent && parent->getConstructor()->isConvertingConstructor(false); } +// va_list is implemented as __va_list_tag[1] and decays to __va_list_tag *. +// That's because va_list must have pointer semantics, but still be passed as +// value by user code. +bool IsVaListType(clang::ASTContext &ctx, clang::QualType type) { + auto canonical = type.getCanonicalType(); + auto va_list = ctx.getBuiltinVaListType().getCanonicalType(); + + // Direct match: va_list itself + if (canonical == va_list) { + return true; + } + + // Decayed match: __va_list_tag[1] decays to __va_list_tag * + if (auto *arr = clang::dyn_cast(va_list)) { + return canonical == ctx.getPointerType(arr->getElementType()).getCanonicalType(); + } + + return false; +} + +bool IsBuiltinVaStart(const clang::CallExpr *expr) { + if (auto *fn = expr->getDirectCallee()) { + return fn->getBuiltinID() == clang::Builtin::BI__builtin_va_start; + } + return false; +} + +bool IsBuiltinVaEnd(const clang::CallExpr *expr) { + if (auto *fn = expr->getDirectCallee()) { + return fn->getBuiltinID() == clang::Builtin::BI__builtin_va_end; + } + return false; +} + +bool IsBuiltinVaCopy(const clang::CallExpr *expr) { + if (auto *fn = expr->getDirectCallee()) { + return fn->getBuiltinID() == clang::Builtin::BI__builtin_va_copy; + } + return false; +} + } // namespace cpp2rust diff --git a/cpp2rust/converter/converter_lib.h b/cpp2rust/converter/converter_lib.h index 7bc50d6f..50fa6a9f 100644 --- a/cpp2rust/converter/converter_lib.h +++ b/cpp2rust/converter/converter_lib.h @@ -142,4 +142,12 @@ std::string GetClassName(clang::QualType type); bool IsRedundantCopyInConversion(clang::ASTContext &ctx, const clang::CXXConstructExpr *expr); +bool IsVaListType(clang::ASTContext &ctx, clang::QualType type); + +bool IsBuiltinVaStart(const clang::CallExpr *expr); + +bool IsBuiltinVaEnd(const clang::CallExpr *expr); + +bool IsBuiltinVaCopy(const clang::CallExpr *expr); + } // namespace cpp2rust From 89f8cd0852631e7088b5f9602f2dbe0e201a53c9 Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Fri, 10 Apr 2026 16:51:52 +0100 Subject: [PATCH 04/23] Handle va_args calls and definitions --- cpp2rust/converter/converter.cpp | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/cpp2rust/converter/converter.cpp b/cpp2rust/converter/converter.cpp index 7459788d..abc05175 100644 --- a/cpp2rust/converter/converter.cpp +++ b/cpp2rust/converter/converter.cpp @@ -1356,11 +1356,16 @@ void Converter::ConvertGenericCallExpr(clang::CallExpr *expr) { "Either function decl or function prototype should be known"); auto num_args = expr->getNumArgs() - arg_begin; + bool is_variadic = + function ? function->isVariadic() : (proto && proto->isVariadic()); + unsigned num_named_params = function + ? function->getNumParams() + : (proto ? proto->getNumParams() : num_args); // Track which args are materialized temps bound to reference params std::vector temp_refs(num_args); - for (unsigned i = 0; i < num_args; ++i) { + for (unsigned i = 0; i < num_named_params && i < num_args; ++i) { auto *arg = expr->getArg(i + arg_begin); std::string param_name = function ? function->getParamDecl(i)->getNameAsString() @@ -1387,7 +1392,7 @@ void Converter::ConvertGenericCallExpr(clang::CallExpr *expr) { Convert(callee); StrCat(token::kOpenParen); - for (unsigned i = 0; i < num_args; ++i) { + for (unsigned i = 0; i < num_named_params && i < num_args; ++i) { auto *arg = expr->getArg(i + arg_begin); std::string param_name = function ? function->getParamDecl(i)->getNameAsString() @@ -1412,6 +1417,18 @@ void Converter::ConvertGenericCallExpr(clang::CallExpr *expr) { } StrCat(token::kComma); } + + // Variadic args: wrap in &[arg.into(), ...] + if (is_variadic) { + StrCat("& ["); + for (unsigned i = num_named_params; i < num_args; ++i) { + auto *arg = expr->getArg(i + arg_begin); + Convert(arg); + StrCat(".into()", token::kComma); + } + StrCat("]"); + } + StrCat(token::kCloseParen); StrCat(token::kCloseCurlyBracket); StrCat(token::kCloseParen); @@ -2807,6 +2824,9 @@ void Converter::ConvertFunctionParameters(clang::FunctionDecl *decl) { ConvertVarDeclSkipInit(parameter); StrCat(token::kComma); } + if (decl->isVariadic()) { + StrCat("args: &[VaArg]", token::kComma); + } in_function_formals_ = false; } From e74570e38dd1193f37a69548f9b7d93db0f339c2 Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Fri, 10 Apr 2026 16:53:46 +0100 Subject: [PATCH 05/23] Translate va_args type --- cpp2rust/converter/converter.cpp | 36 +++++++++++++++++++++++++++++++- cpp2rust/converter/converter.h | 4 ++++ 2 files changed, 39 insertions(+), 1 deletion(-) diff --git a/cpp2rust/converter/converter.cpp b/cpp2rust/converter/converter.cpp index abc05175..b5ab9cfa 100644 --- a/cpp2rust/converter/converter.cpp +++ b/cpp2rust/converter/converter.cpp @@ -243,6 +243,11 @@ bool Converter::VisitPointerType(clang::PointerType *type) { return false; } + if (IsVaListType(ctx_, clang::QualType(type, 0))) { + StrCat("VaList"); + return false; + } + StrCat(token::kStar); auto pointee_type = type->getPointeeType(); StrCat(pointee_type.isConstQualified() ? keyword::kConst : keyword_mut_); @@ -355,6 +360,16 @@ bool Converter::VisitFunctionTemplateDecl(clang::FunctionTemplateDecl *decl) { bool Converter::ConvertVarDeclSkipInit(clang::VarDecl *decl) { auto qual_type = decl->getType(); auto name = GetNamedDeclAsString(decl); + + if (IsVaListType(ctx_, qual_type) && decl->isLocalVarDecl()) { + if (!clang::isa(decl)) { + // va_list local variable: emit "let mut ap: VaList" + StrCat(keyword::kLet); + } // else va_list parameter (decayed to __va_list_tag *): emit "mut ap: VaList" + StrCat(keyword_mut_, name, token::kColon, "VaList"); + return true; + } + if (decl->isFileVarDecl()) { name = std::regex_replace(Mapper::ToString(decl), std::regex("::"), "_"); if ((decl->isExternallyDeclarable() && !decl->hasInit()) || @@ -412,7 +427,9 @@ void Converter::ConvertVarDecl(clang::VarDecl *decl) { return; } auto qual_type = decl->getType(); - if (decl->hasInit()) { + if (IsVaListType(ctx_, qual_type)) { + StrCat(token::kAssign, "VaList::empty()"); + } else if (decl->hasInit()) { StrCat(token::kAssign); ConvertVarInit(qual_type, decl->getInit()); } else if (!clang::isa(decl)) { @@ -1578,6 +1595,11 @@ bool Converter::VisitImplicitCastExpr(clang::ImplicitCastExpr *expr) { clang::isa(sub_expr)) { return Convert(sub_expr); } + // __va_list_tag [1] decays to __va_list_tag *. Just pass through by value + if (IsVaListType(ctx_, sub_expr->getType())) { + Convert(sub_expr); + break; + } Convert(sub_expr); if (sub_expr->getType().isConstQualified()) { StrCat(keyword_ptr_decay_const_); @@ -2226,6 +2248,18 @@ bool Converter::VisitCXXNullPtrLiteralExpr(clang::CXXNullPtrLiteralExpr *expr) { return false; } +bool Converter::VisitVAArgExpr(clang::VAArgExpr *expr) { + auto va_list_expr = expr->getSubExpr(); + if (auto *cast = clang::dyn_cast(va_list_expr)) { + va_list_expr = cast->getSubExpr(); + } + Convert(va_list_expr); + StrCat(".arg::<"); + Convert(expr->getType()); + StrCat(">()"); + return false; +} + bool Converter::VisitGNUNullExpr(clang::GNUNullExpr *expr) { StrCat(keyword_default_); computed_expr_type_ = ComputedExprType::FreshPointer; diff --git a/cpp2rust/converter/converter.h b/cpp2rust/converter/converter.h index b127a423..83828934 100644 --- a/cpp2rust/converter/converter.h +++ b/cpp2rust/converter/converter.h @@ -199,6 +199,8 @@ class Converter : public clang::RecursiveASTVisitor { virtual void ConvertPrintf(clang::CallExpr *expr); + virtual void ConvertVAArgCall(clang::CallExpr *expr); + virtual bool VisitCallExpr(clang::CallExpr *expr); virtual bool VisitIntegerLiteral(clang::IntegerLiteral *expr); @@ -273,6 +275,8 @@ class Converter : public clang::RecursiveASTVisitor { virtual bool VisitSwitchCase(clang::SwitchCase *stmt); + virtual bool VisitVAArgExpr(clang::VAArgExpr *expr); + virtual bool VisitCXXDefaultInitExpr(clang::CXXDefaultInitExpr *expr); virtual bool VisitPredefinedExpr(clang::PredefinedExpr *expr); From 55dcc22c57c09bfd5e468e277f8d3c2e9d8bf206 Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Fri, 10 Apr 2026 16:54:02 +0100 Subject: [PATCH 06/23] Translate va_start, va_end and va_copy --- cpp2rust/converter/converter.cpp | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/cpp2rust/converter/converter.cpp b/cpp2rust/converter/converter.cpp index b5ab9cfa..b43a15ea 100644 --- a/cpp2rust/converter/converter.cpp +++ b/cpp2rust/converter/converter.cpp @@ -128,6 +128,7 @@ bool Converter::VisitBuiltinType(clang::BuiltinType *type) { 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(); @@ -1276,7 +1277,28 @@ std::optional Converter::TryPluginConvert(clang::CallExpr *call) { return std::nullopt; } +void Converter::ConvertVAArgCall(clang::CallExpr *expr) { + if (IsBuiltinVaStart(expr)) { + StrCat(ToString(expr->getArg(0)->IgnoreImpCasts()), "= VaList::new(args)"); + return; + } + if (IsBuiltinVaEnd(expr)) { + // va_end is a no-op + return; + } + if (IsBuiltinVaCopy(expr)) { + StrCat(ToString(expr->getArg(0)->IgnoreImpCasts()), "=", + ToString(expr->getArg(1)->IgnoreImpCasts()), ".clone()"); + return; + } +} + bool Converter::VisitCallExpr(clang::CallExpr *expr) { + if (IsBuiltinVaStart(expr) || IsBuiltinVaEnd(expr) || IsBuiltinVaCopy(expr)) { + ConvertVAArgCall(expr); + return false; + } + if (auto plugin_str = TryPluginConvert(expr)) { StrCat(*plugin_str); return false; From 950f09f49ff1064ab8af254387911a6f9fb06d1d Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Fri, 10 Apr 2026 16:58:26 +0100 Subject: [PATCH 07/23] Add rule for va_list --- rules/src/modules.rs | 2 ++ rules/stdarg/ir_unsafe.json | 6 ++++++ rules/stdarg/src.cpp | 3 +++ rules/stdarg/tgt_unsafe.rs | 5 +++++ 4 files changed, 16 insertions(+) create mode 100644 rules/stdarg/ir_unsafe.json create mode 100644 rules/stdarg/src.cpp create mode 100644 rules/stdarg/tgt_unsafe.rs diff --git a/rules/src/modules.rs b/rules/src/modules.rs index 47770137..f3c5b168 100644 --- a/rules/src/modules.rs +++ b/rules/src/modules.rs @@ -58,6 +58,8 @@ pub mod math_tgt_unsafe; pub mod pair_tgt_refcount; #[path = r#"../pair/tgt_unsafe.rs"#] pub mod pair_tgt_unsafe; +#[path = r#"../stdarg/tgt_unsafe.rs"#] +pub mod stdarg_tgt_unsafe; #[path = r#"../stdio/tgt_refcount.rs"#] pub mod stdio_tgt_refcount; #[path = r#"../stdio/tgt_unsafe.rs"#] diff --git a/rules/stdarg/ir_unsafe.json b/rules/stdarg/ir_unsafe.json new file mode 100644 index 00000000..e45e600f --- /dev/null +++ b/rules/stdarg/ir_unsafe.json @@ -0,0 +1,6 @@ +{ + "t1": { + "init": "VaList::empty()", + "type": "VaList" + } +} diff --git a/rules/stdarg/src.cpp b/rules/stdarg/src.cpp new file mode 100644 index 00000000..bb020abb --- /dev/null +++ b/rules/stdarg/src.cpp @@ -0,0 +1,3 @@ +#include + +va_list t1; diff --git a/rules/stdarg/tgt_unsafe.rs b/rules/stdarg/tgt_unsafe.rs new file mode 100644 index 00000000..ff48596a --- /dev/null +++ b/rules/stdarg/tgt_unsafe.rs @@ -0,0 +1,5 @@ +use libcc2rs::VaList; + +fn types() { + let t1: VaList = VaList::empty(); +} From 9477aad852e1b391ff05ceb110a5fdb0ce55156c Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Fri, 10 Apr 2026 16:59:45 +0100 Subject: [PATCH 08/23] Add VaList --- libcc2rs/src/lib.rs | 3 + libcc2rs/src/var_args.rs | 162 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 165 insertions(+) create mode 100644 libcc2rs/src/var_args.rs diff --git a/libcc2rs/src/lib.rs b/libcc2rs/src/lib.rs index 4e20f35f..c56ca637 100644 --- a/libcc2rs/src/lib.rs +++ b/libcc2rs/src/lib.rs @@ -24,3 +24,6 @@ pub use iterators::*; mod compat; pub use compat::*; + +mod var_args; +pub use var_args::*; diff --git a/libcc2rs/src/var_args.rs b/libcc2rs/src/var_args.rs new file mode 100644 index 00000000..abf486ac --- /dev/null +++ b/libcc2rs/src/var_args.rs @@ -0,0 +1,162 @@ +use std::ffi::c_void; + +use crate::rc::AnyPtr; + +#[derive(Clone)] +pub enum VaArg { + Int(i32), + UInt(u32), + Long(i64), + ULong(u64), + Double(f64), + RawPtr(*mut c_void), + Ptr(AnyPtr), +} + +macro_rules! impl_from { + ($ty:ty, $variant:ident) => { + impl From<$ty> for VaArg { + fn from(v: $ty) -> Self { + VaArg::$variant(v) + } + } + }; + ($ty:ty => $variant:ident as $cast:ty) => { + impl From<$ty> for VaArg { + fn from(v: $ty) -> Self { + VaArg::$variant(v as $cast) + } + } + }; +} + +impl_from!(i32, Int); +impl_from!(u32, UInt); +impl_from!(i64, Long); +impl_from!(u64, ULong); +impl_from!(f64, Double); +impl_from!(*mut c_void, RawPtr); +impl_from!(*const c_void => RawPtr as *mut c_void); +impl_from!(*mut i8 => RawPtr as *mut c_void); +impl_from!(*const i8 => RawPtr as *mut c_void); +impl_from!(*mut u8 => RawPtr as *mut c_void); +impl_from!(*const u8 => RawPtr as *mut c_void); +impl_from!(AnyPtr, Ptr); + +pub struct VaList<'a> { + args: &'a [VaArg], + pos: usize, +} + +impl<'a> VaList<'a> { + pub fn empty() -> Self { + VaList { args: &[], pos: 0 } + } + + pub fn new(args: &'a [VaArg]) -> Self { + VaList { args, pos: 0 } + } + + pub fn arg(&mut self) -> T { + let val = &self.args[self.pos]; + self.pos += 1; + T::get(val) + } +} + +impl Clone for VaList<'_> { + fn clone(&self) -> Self { + VaList { + args: self.args, + pos: self.pos, + } + } +} + +fn get_int(v: &VaArg) -> i64 { + match v { + VaArg::Int(n) => *n as i64, + VaArg::UInt(n) => *n as i64, + VaArg::Long(n) => *n, + VaArg::ULong(n) => *n as i64, + _ => panic!("VaList::arg: expected integer, got different variant"), + } +} + +fn get_uint(v: &VaArg) -> u64 { + match v { + VaArg::Int(n) => *n as u64, + VaArg::UInt(n) => *n as u64, + VaArg::Long(n) => *n as u64, + VaArg::ULong(n) => *n, + _ => panic!("VaList::arg: expected unsigned integer, got different variant"), + } +} + +fn get_float(v: &VaArg) -> f64 { + match v { + VaArg::Double(n) => *n, + VaArg::Int(n) => *n as f64, + VaArg::Long(n) => *n as f64, + _ => panic!("VaList::arg: expected float, got different variant"), + } +} + +fn get_ptr(v: &VaArg) -> *mut c_void { + match v { + VaArg::RawPtr(p) => *p, + _ => panic!("VaList::arg: expected pointer, got different variant"), + } +} + +pub trait VaArgGet { + fn get(v: &VaArg) -> Self; +} + +macro_rules! impl_get_int { + ($($ty:ty),*) => { + $(impl VaArgGet for $ty { + fn get(v: &VaArg) -> Self { get_int(v) as $ty } + })* + }; +} + +macro_rules! impl_get_uint { + ($($ty:ty),*) => { + $(impl VaArgGet for $ty { + fn get(v: &VaArg) -> Self { get_uint(v) as $ty } + })* + }; +} + +macro_rules! impl_get_ptr { + ($($ty:ty),*) => { + $(impl VaArgGet for $ty { + fn get(v: &VaArg) -> Self { get_ptr(v) as $ty } + })* + }; +} + +impl_get_int!(i8, i16, i32, i64); +impl_get_uint!(u8, u16, u32, u64); + +impl VaArgGet for f32 { + fn get(v: &VaArg) -> Self { + get_float(v) as f32 + } +} + +impl VaArgGet for f64 { + fn get(v: &VaArg) -> Self { + get_float(v) + } +} + +impl_get_ptr!( + *mut c_void, + *const c_void, + *mut i8, + *const i8, + *mut u8, + *const u8 +); From d81ef345f14dfd1d7c5d94b0022ea7ea37227016 Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Fri, 10 Apr 2026 17:00:03 +0100 Subject: [PATCH 09/23] clang-format --- cpp2rust/converter/converter.cpp | 3 ++- cpp2rust/converter/converter_lib.cpp | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/cpp2rust/converter/converter.cpp b/cpp2rust/converter/converter.cpp index b43a15ea..4994fb5b 100644 --- a/cpp2rust/converter/converter.cpp +++ b/cpp2rust/converter/converter.cpp @@ -366,7 +366,8 @@ bool Converter::ConvertVarDeclSkipInit(clang::VarDecl *decl) { if (!clang::isa(decl)) { // va_list local variable: emit "let mut ap: VaList" StrCat(keyword::kLet); - } // else va_list parameter (decayed to __va_list_tag *): emit "mut ap: VaList" + } // else va_list parameter (decayed to __va_list_tag *): emit "mut ap: + // VaList" StrCat(keyword_mut_, name, token::kColon, "VaList"); return true; } diff --git a/cpp2rust/converter/converter_lib.cpp b/cpp2rust/converter/converter_lib.cpp index c6b513d9..16e7d32f 100644 --- a/cpp2rust/converter/converter_lib.cpp +++ b/cpp2rust/converter/converter_lib.cpp @@ -572,7 +572,8 @@ bool IsVaListType(clang::ASTContext &ctx, clang::QualType type) { // Decayed match: __va_list_tag[1] decays to __va_list_tag * if (auto *arr = clang::dyn_cast(va_list)) { - return canonical == ctx.getPointerType(arr->getElementType()).getCanonicalType(); + return canonical == + ctx.getPointerType(arr->getElementType()).getCanonicalType(); } return false; From 1305c9786bf22509dfb2fcebab6de985d9052646 Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Fri, 10 Apr 2026 17:48:04 +0100 Subject: [PATCH 10/23] Rename var_args to va_args --- libcc2rs/src/lib.rs | 4 ++-- libcc2rs/src/{var_args.rs => va_args.rs} | 0 2 files changed, 2 insertions(+), 2 deletions(-) rename libcc2rs/src/{var_args.rs => va_args.rs} (100%) diff --git a/libcc2rs/src/lib.rs b/libcc2rs/src/lib.rs index c56ca637..42eb4117 100644 --- a/libcc2rs/src/lib.rs +++ b/libcc2rs/src/lib.rs @@ -25,5 +25,5 @@ pub use iterators::*; mod compat; pub use compat::*; -mod var_args; -pub use var_args::*; +mod va_args; +pub use va_args::*; diff --git a/libcc2rs/src/var_args.rs b/libcc2rs/src/va_args.rs similarity index 100% rename from libcc2rs/src/var_args.rs rename to libcc2rs/src/va_args.rs From 538a21a677a936173192db50a1cb85600767ca56 Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Fri, 10 Apr 2026 17:49:56 +0100 Subject: [PATCH 11/23] Add default impl for VaList --- libcc2rs/src/va_args.rs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/libcc2rs/src/va_args.rs b/libcc2rs/src/va_args.rs index abf486ac..c4b9a5c0 100644 --- a/libcc2rs/src/va_args.rs +++ b/libcc2rs/src/va_args.rs @@ -49,10 +49,6 @@ pub struct VaList<'a> { } impl<'a> VaList<'a> { - pub fn empty() -> Self { - VaList { args: &[], pos: 0 } - } - pub fn new(args: &'a [VaArg]) -> Self { VaList { args, pos: 0 } } @@ -64,6 +60,12 @@ impl<'a> VaList<'a> { } } +impl Default for VaList<'_> { + fn default() -> Self { + VaList { args: &[], pos: 0 } + } +} + impl Clone for VaList<'_> { fn clone(&self) -> Self { VaList { From c6441cb37fa8600fe3f220a44a6965b52d788271 Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Fri, 10 Apr 2026 18:02:07 +0100 Subject: [PATCH 12/23] Add refcount support for va_args --- cpp2rust/converter/converter.cpp | 21 ++++++----- cpp2rust/converter/converter.h | 6 ++- .../converter/models/converter_refcount.cpp | 37 +++++++++++++++++++ .../converter/models/converter_refcount.h | 4 ++ 4 files changed, 57 insertions(+), 11 deletions(-) diff --git a/cpp2rust/converter/converter.cpp b/cpp2rust/converter/converter.cpp index 4994fb5b..7a7930f0 100644 --- a/cpp2rust/converter/converter.cpp +++ b/cpp2rust/converter/converter.cpp @@ -358,17 +358,22 @@ bool Converter::VisitFunctionTemplateDecl(clang::FunctionTemplateDecl *decl) { return false; } +void Converter::ConvertVaListVarDecl(clang::VarDecl *decl) { + if (clang::isa(decl)) { + // va_list parameter (decayed to __va_list_tag *): emit "mut ap: VaList" + } else { + // va_list local variable: emit "let mut ap: VaList" + StrCat(keyword::kLet); + } + StrCat(keyword_mut_, GetNamedDeclAsString(decl), token::kColon, "VaList"); +} + bool Converter::ConvertVarDeclSkipInit(clang::VarDecl *decl) { auto qual_type = decl->getType(); auto name = GetNamedDeclAsString(decl); if (IsVaListType(ctx_, qual_type) && decl->isLocalVarDecl()) { - if (!clang::isa(decl)) { - // va_list local variable: emit "let mut ap: VaList" - StrCat(keyword::kLet); - } // else va_list parameter (decayed to __va_list_tag *): emit "mut ap: - // VaList" - StrCat(keyword_mut_, name, token::kColon, "VaList"); + ConvertVaListVarDecl(decl); return true; } @@ -429,9 +434,7 @@ void Converter::ConvertVarDecl(clang::VarDecl *decl) { return; } auto qual_type = decl->getType(); - if (IsVaListType(ctx_, qual_type)) { - StrCat(token::kAssign, "VaList::empty()"); - } else if (decl->hasInit()) { + if (decl->hasInit()) { StrCat(token::kAssign); ConvertVarInit(qual_type, decl->getInit()); } else if (!clang::isa(decl)) { diff --git a/cpp2rust/converter/converter.h b/cpp2rust/converter/converter.h index 83828934..899cc965 100644 --- a/cpp2rust/converter/converter.h +++ b/cpp2rust/converter/converter.h @@ -85,7 +85,9 @@ class Converter : public clang::RecursiveASTVisitor { virtual void ConvertGlobalVarDecl(clang::VarDecl *decl); - bool ConvertVarDeclSkipInit(clang::VarDecl *decl); + virtual void ConvertVaListVarDecl(clang::VarDecl *decl); + + virtual bool ConvertVarDeclSkipInit(clang::VarDecl *decl); virtual bool ConvertLambdaVarDecl(clang::VarDecl *decl); @@ -199,7 +201,7 @@ class Converter : public clang::RecursiveASTVisitor { virtual void ConvertPrintf(clang::CallExpr *expr); - virtual void ConvertVAArgCall(clang::CallExpr *expr); + void ConvertVAArgCall(clang::CallExpr *expr); virtual bool VisitCallExpr(clang::CallExpr *expr); diff --git a/cpp2rust/converter/models/converter_refcount.cpp b/cpp2rust/converter/models/converter_refcount.cpp index 4a34b951..29474a6c 100644 --- a/cpp2rust/converter/models/converter_refcount.cpp +++ b/cpp2rust/converter/models/converter_refcount.cpp @@ -172,6 +172,11 @@ bool ConverterRefCount::VisitPointerType(clang::PointerType *type) { return false; } + if (IsVaListType(ctx_, clang::QualType(type, 0))) { + StrCat("VaList"); + return false; + } + if (type->isVoidPointerType()) { StrCat("AnyPtr"); return false; @@ -472,6 +477,17 @@ void ConverterRefCount::EmitFunctionPreamble(clang::FunctionDecl *decl) { } } +void ConverterRefCount::ConvertVaListVarDecl(clang::VarDecl *decl) { + if (clang::isa(decl)) { + // va_list parameter (decayed to __va_list_tag *): emit "mut ap: VaList" + } else { + // va_list local variable: emit "let mut ap: VaList" + StrCat(keyword::kLet); + } + + StrCat(GetNamedDeclAsString(decl), token::kColon, "Value"); +} + bool ConverterRefCount::ConvertLambdaVarDecl(clang::VarDecl *decl) { return false; } @@ -775,6 +791,11 @@ void ConverterRefCount::ConvertPrintf(clang::CallExpr *expr) { } bool ConverterRefCount::VisitCallExpr(clang::CallExpr *expr) { + if (IsBuiltinVaStart(expr) || IsBuiltinVaEnd(expr) || IsBuiltinVaCopy(expr)) { + ConvertVAArgCall(expr); + return false; + } + if (expr->isCallToStdMove()) { if (IsUniquePtr(expr->getArg(0)->getType())) { StrCat(std::format("{}.take()", ConvertLValue(expr->getArg(0)))); @@ -912,6 +933,10 @@ bool ConverterRefCount::VisitImplicitCastExpr(clang::ImplicitCastExpr *expr) { } if (expr->getCastKind() == clang::CastKind::CK_ArrayToPointerDecay) { + if (IsVaListType(ctx_, sub_expr->getType())) { + Convert(sub_expr); + return false; + } if (clang::isa(sub_expr) || clang::isa(sub_expr)) { StrCat(std::format("Ptr::from_string_literal({})", ToString(sub_expr))); @@ -1366,6 +1391,18 @@ bool ConverterRefCount::VisitImplicitValueInitExpr( return Converter::VisitImplicitValueInitExpr(expr); } +bool ConverterRefCount::VisitVAArgExpr(clang::VAArgExpr *expr) { + auto va_list_expr = expr->getSubExpr(); + if (auto *cast = clang::dyn_cast(va_list_expr)) { + va_list_expr = cast->getSubExpr(); + } + StrCat(ConvertLValue(va_list_expr)); + StrCat(".arg::<"); + Convert(expr->getType()); + StrCat(">()"); + return false; +} + bool ConverterRefCount::VisitCXXDefaultArgExpr(clang::CXXDefaultArgExpr *expr) { return Converter::VisitCXXDefaultArgExpr(expr); } diff --git a/cpp2rust/converter/models/converter_refcount.h b/cpp2rust/converter/models/converter_refcount.h index 0f55b370..81025901 100644 --- a/cpp2rust/converter/models/converter_refcount.h +++ b/cpp2rust/converter/models/converter_refcount.h @@ -47,6 +47,8 @@ class ConverterRefCount final : public Converter { void ConvertGlobalVarDecl(clang::VarDecl *decl) override; + void ConvertVaListVarDecl(clang::VarDecl *decl) override; + bool ConvertLambdaVarDecl(clang::VarDecl *decl) override; bool VisitDeclRefExpr(clang::DeclRefExpr *expr) override; @@ -93,6 +95,8 @@ class ConverterRefCount final : public Converter { bool VisitImplicitValueInitExpr(clang::ImplicitValueInitExpr *expr) override; + bool VisitVAArgExpr(clang::VAArgExpr *expr) override; + void ConvertArrayCXXConstructExpr(clang::CXXConstructExpr *expr) override; bool VisitCXXDefaultArgExpr(clang::CXXDefaultArgExpr *expr) override; From 3a1a8489191c02084d052d8787d6027582e760b5 Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Fri, 10 Apr 2026 18:02:51 +0100 Subject: [PATCH 13/23] Add basic va_args tests --- tests/unit/out/refcount/va_arg_concat.rs | 34 +++++++++++++++ tests/unit/out/refcount/va_arg_forward.rs | 42 +++++++++++++++++++ tests/unit/out/refcount/va_arg_mixed_types.rs | 31 ++++++++++++++ tests/unit/out/unsafe/va_arg_concat.rs | 35 ++++++++++++++++ tests/unit/out/unsafe/va_arg_forward.rs | 39 +++++++++++++++++ tests/unit/out/unsafe/va_arg_mixed_types.rs | 32 ++++++++++++++ tests/unit/va_arg_concat.c | 19 +++++++++ tests/unit/va_arg_forward.c | 21 ++++++++++ tests/unit/va_arg_mixed_types.c | 16 +++++++ 9 files changed, 269 insertions(+) create mode 100644 tests/unit/out/refcount/va_arg_concat.rs create mode 100644 tests/unit/out/refcount/va_arg_forward.rs create mode 100644 tests/unit/out/refcount/va_arg_mixed_types.rs create mode 100644 tests/unit/out/unsafe/va_arg_concat.rs create mode 100644 tests/unit/out/unsafe/va_arg_forward.rs create mode 100644 tests/unit/out/unsafe/va_arg_mixed_types.rs create mode 100644 tests/unit/va_arg_concat.c create mode 100644 tests/unit/va_arg_forward.c create mode 100644 tests/unit/va_arg_mixed_types.c diff --git a/tests/unit/out/refcount/va_arg_concat.rs b/tests/unit/out/refcount/va_arg_concat.rs new file mode 100644 index 00000000..2207b347 --- /dev/null +++ b/tests/unit/out/refcount/va_arg_concat.rs @@ -0,0 +1,34 @@ +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 sum_ints_0(first: i32, args: &[VaArg]) -> i32 { + let first: Value = Rc::new(RefCell::new(first)); + let ap: Value = Rc::new(RefCell::new(::default())); + let total: Value = Rc::new(RefCell::new((*first.borrow()))); + (*ap.borrow_mut()) = VaList::new(args); + let val: Value = >::default(); + 'loop_: while ((({ + (*val.borrow_mut()) = ((*ap.borrow_mut()).arg::()).clone(); + (*val.borrow()) + }) as i32) + != 0) + { + (*total.borrow_mut()) += (*val.borrow()); + } + return (*total.borrow()); +} +pub fn main() { + std::process::exit(main_0()); +} +fn main_0() -> i32 { + return (({ + let _first: i32 = 1; + sum_ints_0(_first, &[2.into(), 3.into(), 4.into(), 0.into()]) + }) - 10); +} diff --git a/tests/unit/out/refcount/va_arg_forward.rs b/tests/unit/out/refcount/va_arg_forward.rs new file mode 100644 index 00000000..b566876d --- /dev/null +++ b/tests/unit/out/refcount/va_arg_forward.rs @@ -0,0 +1,42 @@ +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 inner_0(count: i32, ap: VaList) -> i32 { + let count: Value = Rc::new(RefCell::new(count)); + let ap: Value = Rc::new(RefCell::new(ap)); + let total: Value = Rc::new(RefCell::new(0)); + let i: Value = Rc::new(RefCell::new(0)); + 'loop_: while ((*i.borrow()) < (*count.borrow())) { + (*total.borrow_mut()) += ((*ap.borrow_mut()).arg::()).clone(); + (*i.borrow_mut()).postfix_inc(); + } + return (*total.borrow()); +} +pub fn outer_1(count: i32, args: &[VaArg]) -> i32 { + let count: Value = Rc::new(RefCell::new(count)); + let ap: Value = Rc::new(RefCell::new(::default())); + (*ap.borrow_mut()) = VaList::new(args); + let result: Value = Rc::new(RefCell::new( + ({ + let _count: i32 = (*count.borrow()); + let _ap: VaList = (*ap.borrow()).clone(); + inner_0(_count, _ap) + }), + )); + return (*result.borrow()); +} +pub fn main() { + std::process::exit(main_0()); +} +fn main_0() -> i32 { + return (({ + let _count: i32 = 3; + outer_1(_count, &[10.into(), 20.into(), 30.into()]) + }) - 60); +} diff --git a/tests/unit/out/refcount/va_arg_mixed_types.rs b/tests/unit/out/refcount/va_arg_mixed_types.rs new file mode 100644 index 00000000..ad7450a1 --- /dev/null +++ b/tests/unit/out/refcount/va_arg_mixed_types.rs @@ -0,0 +1,31 @@ +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 sum_mixed_0(count: i32, args: &[VaArg]) -> f64 { + let count: Value = Rc::new(RefCell::new(count)); + let ap: Value = Rc::new(RefCell::new(::default())); + (*ap.borrow_mut()) = VaList::new(args); + let total: Value = Rc::new(RefCell::new(0_f64)); + let i: Value = Rc::new(RefCell::new(0)); + 'loop_: while ((*i.borrow()) < (*count.borrow())) { + (*total.borrow_mut()) += ((*ap.borrow_mut()).arg::()).clone(); + (*i.borrow_mut()).postfix_inc(); + } + return (*total.borrow()); +} +pub fn main() { + std::process::exit(main_0()); +} +fn main_0() -> i32 { + return ((({ + let _count: i32 = 3; + sum_mixed_0(_count, &[1.5E+0.into(), 2.5E+0.into(), 3.0E+0.into()]) + }) as i32) + - 7); +} diff --git a/tests/unit/out/unsafe/va_arg_concat.rs b/tests/unit/out/unsafe/va_arg_concat.rs new file mode 100644 index 00000000..fef2ff45 --- /dev/null +++ b/tests/unit/out/unsafe/va_arg_concat.rs @@ -0,0 +1,35 @@ +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 sum_ints_0(mut first: i32, args: &[VaArg]) -> i32 { + let mut ap: VaList = ::default(); + let mut total: i32 = first; + ap = VaList::new(args); + let mut val: i32 = ::default(); + 'loop_: while ((({ + val = ap.arg::(); + val + }) as i32) + != (0)) + { + total += val; + } + return total; +} +pub fn main() { + unsafe { + std::process::exit(main_0() as i32); + } +} +unsafe fn main_0() -> i32 { + return ((unsafe { + let _first: i32 = 1; + sum_ints_0(_first, &[2.into(), 3.into(), 4.into(), 0.into()]) + }) - (10)); +} diff --git a/tests/unit/out/unsafe/va_arg_forward.rs b/tests/unit/out/unsafe/va_arg_forward.rs new file mode 100644 index 00000000..425e2e16 --- /dev/null +++ b/tests/unit/out/unsafe/va_arg_forward.rs @@ -0,0 +1,39 @@ +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 inner_0(mut count: i32, mut ap: VaList) -> i32 { + let mut total: i32 = 0; + let mut i: i32 = 0; + 'loop_: while ((i) < (count)) { + total += ap.arg::(); + i.postfix_inc(); + } + return total; +} +pub unsafe fn outer_1(mut count: i32, args: &[VaArg]) -> i32 { + let mut ap: VaList = ::default(); + ap = VaList::new(args); + let mut result: i32 = (unsafe { + let _count: i32 = count; + let _ap: VaList = ap; + inner_0(_count, _ap) + }); + return result; +} +pub fn main() { + unsafe { + std::process::exit(main_0() as i32); + } +} +unsafe fn main_0() -> i32 { + return ((unsafe { + let _count: i32 = 3; + outer_1(_count, &[10.into(), 20.into(), 30.into()]) + }) - (60)); +} diff --git a/tests/unit/out/unsafe/va_arg_mixed_types.rs b/tests/unit/out/unsafe/va_arg_mixed_types.rs new file mode 100644 index 00000000..45360e75 --- /dev/null +++ b/tests/unit/out/unsafe/va_arg_mixed_types.rs @@ -0,0 +1,32 @@ +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 sum_mixed_0(mut count: i32, args: &[VaArg]) -> f64 { + let mut ap: VaList = ::default(); + ap = VaList::new(args); + let mut total: f64 = 0_f64; + let mut i: i32 = 0; + 'loop_: while ((i) < (count)) { + total += ap.arg::(); + i.postfix_inc(); + } + return total; +} +pub fn main() { + unsafe { + std::process::exit(main_0() as i32); + } +} +unsafe fn main_0() -> i32 { + return (((unsafe { + let _count: i32 = 3; + sum_mixed_0(_count, &[1.5E+0.into(), 2.5E+0.into(), 3.0E+0.into()]) + }) as i32) + - (7)); +} diff --git a/tests/unit/va_arg_concat.c b/tests/unit/va_arg_concat.c new file mode 100644 index 00000000..2f1a41bb --- /dev/null +++ b/tests/unit/va_arg_concat.c @@ -0,0 +1,19 @@ +#include + +int sum_ints(int first, ...) { + va_list ap; + int total = first; + + va_start(ap, first); + int val; + while ((val = va_arg(ap, int)) != 0) { + total += val; + } + va_end(ap); + + return total; +} + +int main() { + return sum_ints(1, 2, 3, 4, 0) - 10; +} diff --git a/tests/unit/va_arg_forward.c b/tests/unit/va_arg_forward.c new file mode 100644 index 00000000..f2f03772 --- /dev/null +++ b/tests/unit/va_arg_forward.c @@ -0,0 +1,21 @@ +#include + +int inner(int count, va_list ap) { + int total = 0; + for (int i = 0; i < count; i++) { + total += va_arg(ap, int); + } + return total; +} + +int outer(int count, ...) { + va_list ap; + va_start(ap, count); + int result = inner(count, ap); + va_end(ap); + return result; +} + +int main() { + return outer(3, 10, 20, 30) - 60; +} diff --git a/tests/unit/va_arg_mixed_types.c b/tests/unit/va_arg_mixed_types.c new file mode 100644 index 00000000..4d1badc7 --- /dev/null +++ b/tests/unit/va_arg_mixed_types.c @@ -0,0 +1,16 @@ +#include + +double sum_mixed(int count, ...) { + va_list ap; + va_start(ap, count); + double total = 0; + for (int i = 0; i < count; i++) { + total += va_arg(ap, double); + } + va_end(ap); + return total; +} + +int main() { + return (int)sum_mixed(3, 1.5, 2.5, 3.0) - 7; +} From e2e7caf65f1d463d783655fe33cd6a0a1ec9c3d4 Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Fri, 10 Apr 2026 18:38:19 +0100 Subject: [PATCH 14/23] Add support for int promotion and pointers --- libcc2rs/src/va_args.rs | 61 +++++++++++++++++++++++++++++++---------- 1 file changed, 47 insertions(+), 14 deletions(-) diff --git a/libcc2rs/src/va_args.rs b/libcc2rs/src/va_args.rs index c4b9a5c0..deba532d 100644 --- a/libcc2rs/src/va_args.rs +++ b/libcc2rs/src/va_args.rs @@ -35,14 +35,32 @@ impl_from!(u32, UInt); impl_from!(i64, Long); impl_from!(u64, ULong); impl_from!(f64, Double); -impl_from!(*mut c_void, RawPtr); -impl_from!(*const c_void => RawPtr as *mut c_void); -impl_from!(*mut i8 => RawPtr as *mut c_void); -impl_from!(*const i8 => RawPtr as *mut c_void); -impl_from!(*mut u8 => RawPtr as *mut c_void); -impl_from!(*const u8 => RawPtr as *mut c_void); + +// C promotion: char/short -> int, float -> double +impl_from!(i8 => Int as i32); +impl_from!(i16 => Int as i32); +impl_from!(u8 => UInt as u32); +impl_from!(u16 => UInt as u32); +impl_from!(f32 => Double as f64); impl_from!(AnyPtr, Ptr); +impl From> for VaArg { + fn from(v: crate::rc::Ptr) -> Self { + VaArg::Ptr(v.to_any()) + } +} + +macro_rules! impl_from_ptr { + ($($ty:ty),*) => { + $( + impl_from!(*mut $ty => RawPtr as *mut c_void); + impl_from!(*const $ty => RawPtr as *mut c_void); + )* + }; +} + +impl_from_ptr!(c_void, i8, u8, i16, u16, i32, u32, i64, u64, f32, f64, usize, isize); + pub struct VaList<'a> { args: &'a [VaArg], pos: usize, @@ -154,11 +172,26 @@ impl VaArgGet for f64 { } } -impl_get_ptr!( - *mut c_void, - *const c_void, - *mut i8, - *const i8, - *mut u8, - *const u8 -); +macro_rules! impl_get_ptr { + ($($ty:ty),*) => { + $( + impl VaArgGet for *mut $ty { + fn get(v: &VaArg) -> Self { get_ptr(v) as *mut $ty } + } + impl VaArgGet for *const $ty { + fn get(v: &VaArg) -> Self { get_ptr(v) as *const $ty } + } + )* + }; +} + +impl_get_ptr!(c_void, i8, u8, i16, u16, i32, u32, i64, u64, f32, f64, usize, isize); + +impl VaArgGet for crate::rc::Ptr { + fn get(v: &VaArg) -> Self { + match v { + VaArg::Ptr(any) => any.cast::().expect("VaList::arg: Ptr type mismatch"), + _ => panic!("VaList::arg: expected Ptr, got different variant"), + } + } +} From 4965fe1a0134ae3f3e548e900a57a6819b96ee82 Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Fri, 10 Apr 2026 18:39:25 +0100 Subject: [PATCH 15/23] Add more va_arg tests --- tests/unit/out/refcount/va_arg_chain.rs | 65 +++++++++++++++++++ tests/unit/out/refcount/va_arg_concat.rs | 23 +++++-- tests/unit/out/refcount/va_arg_conditional.rs | 40 ++++++++++++ tests/unit/out/refcount/va_arg_copy.rs | 42 ++++++++++++ tests/unit/out/refcount/va_arg_forward.rs | 23 +++++-- .../unit/out/refcount/va_arg_mixed_int_ptr.rs | 65 +++++++++++++++++++ tests/unit/out/refcount/va_arg_mixed_types.rs | 48 +++++++++++--- tests/unit/out/refcount/va_arg_printf.rs | 48 ++++++++++++++ tests/unit/out/refcount/va_arg_promotion.rs | 43 ++++++++++++ tests/unit/out/refcount/va_arg_snprintf.rs | 47 ++++++++++++++ tests/unit/out/refcount/va_arg_struct_ctx.rs | 56 ++++++++++++++++ tests/unit/out/refcount/va_arg_two_passes.rs | 47 ++++++++++++++ tests/unit/out/unsafe/va_arg_chain.rs | 60 +++++++++++++++++ tests/unit/out/unsafe/va_arg_concat.rs | 23 +++++-- tests/unit/out/unsafe/va_arg_conditional.rs | 40 ++++++++++++ tests/unit/out/unsafe/va_arg_copy.rs | 43 ++++++++++++ tests/unit/out/unsafe/va_arg_forward.rs | 23 +++++-- tests/unit/out/unsafe/va_arg_mixed_int_ptr.rs | 64 ++++++++++++++++++ tests/unit/out/unsafe/va_arg_mixed_types.rs | 48 +++++++++++--- tests/unit/out/unsafe/va_arg_printf.rs | 42 ++++++++++++ tests/unit/out/unsafe/va_arg_promotion.rs | 40 ++++++++++++ tests/unit/out/unsafe/va_arg_snprintf.rs | 48 ++++++++++++++ tests/unit/out/unsafe/va_arg_struct_ctx.rs | 45 +++++++++++++ tests/unit/out/unsafe/va_arg_two_passes.rs | 48 ++++++++++++++ tests/unit/va_arg_chain.c | 30 +++++++++ tests/unit/va_arg_concat.c | 6 +- tests/unit/va_arg_conditional.c | 20 ++++++ tests/unit/va_arg_copy.c | 34 ++++++++++ tests/unit/va_arg_forward.c | 6 +- tests/unit/va_arg_mixed_int_ptr.c | 33 ++++++++++ tests/unit/va_arg_mixed_types.c | 21 ++++-- tests/unit/va_arg_printf.c | 21 ++++++ tests/unit/va_arg_promotion.c | 32 +++++++++ tests/unit/va_arg_snprintf.c | 20 ++++++ tests/unit/va_arg_struct_ctx.c | 32 +++++++++ tests/unit/va_arg_two_passes.c | 32 +++++++++ 36 files changed, 1320 insertions(+), 38 deletions(-) create mode 100644 tests/unit/out/refcount/va_arg_chain.rs create mode 100644 tests/unit/out/refcount/va_arg_conditional.rs create mode 100644 tests/unit/out/refcount/va_arg_copy.rs create mode 100644 tests/unit/out/refcount/va_arg_mixed_int_ptr.rs create mode 100644 tests/unit/out/refcount/va_arg_printf.rs create mode 100644 tests/unit/out/refcount/va_arg_promotion.rs create mode 100644 tests/unit/out/refcount/va_arg_snprintf.rs create mode 100644 tests/unit/out/refcount/va_arg_struct_ctx.rs create mode 100644 tests/unit/out/refcount/va_arg_two_passes.rs create mode 100644 tests/unit/out/unsafe/va_arg_chain.rs create mode 100644 tests/unit/out/unsafe/va_arg_conditional.rs create mode 100644 tests/unit/out/unsafe/va_arg_copy.rs create mode 100644 tests/unit/out/unsafe/va_arg_mixed_int_ptr.rs create mode 100644 tests/unit/out/unsafe/va_arg_printf.rs create mode 100644 tests/unit/out/unsafe/va_arg_promotion.rs create mode 100644 tests/unit/out/unsafe/va_arg_snprintf.rs create mode 100644 tests/unit/out/unsafe/va_arg_struct_ctx.rs create mode 100644 tests/unit/out/unsafe/va_arg_two_passes.rs create mode 100644 tests/unit/va_arg_chain.c create mode 100644 tests/unit/va_arg_conditional.c create mode 100644 tests/unit/va_arg_copy.c create mode 100644 tests/unit/va_arg_mixed_int_ptr.c create mode 100644 tests/unit/va_arg_printf.c create mode 100644 tests/unit/va_arg_promotion.c create mode 100644 tests/unit/va_arg_snprintf.c create mode 100644 tests/unit/va_arg_struct_ctx.c create mode 100644 tests/unit/va_arg_two_passes.c diff --git a/tests/unit/out/refcount/va_arg_chain.rs b/tests/unit/out/refcount/va_arg_chain.rs new file mode 100644 index 00000000..0dbdeef3 --- /dev/null +++ b/tests/unit/out/refcount/va_arg_chain.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 extract_nth_0(n: i32, ap: VaList) -> i32 { + let n: Value = Rc::new(RefCell::new(n)); + let ap: Value = Rc::new(RefCell::new(ap)); + let i: Value = Rc::new(RefCell::new(0)); + 'loop_: while ((*i.borrow()) < (*n.borrow())) { + (*ap.borrow_mut()).arg::(); + (*i.borrow_mut()).postfix_inc(); + } + return ((*ap.borrow_mut()).arg::()).clone(); +} +pub fn middle_layer_1(n: i32, ap: VaList) -> i32 { + let n: Value = Rc::new(RefCell::new(n)); + let ap: Value = Rc::new(RefCell::new(ap)); + return ({ + let _n: i32 = (*n.borrow()); + let _ap: VaList = (*ap.borrow()).clone(); + extract_nth_0(_n, _ap) + }); +} +pub fn top_level_2(n: i32, args: &[VaArg]) -> i32 { + let n: Value = Rc::new(RefCell::new(n)); + let ap: Value = Rc::new(RefCell::new(::default())); + (*ap.borrow_mut()) = VaList::new(args); + let result: Value = Rc::new(RefCell::new( + ({ + let _n: i32 = (*n.borrow()); + let _ap: VaList = (*ap.borrow()).clone(); + middle_layer_1(_n, _ap) + }), + )); + return (*result.borrow()); +} +pub fn main() { + std::process::exit(main_0()); +} +fn main_0() -> i32 { + assert!( + (({ + let _n: i32 = 2; + top_level_2(_n, &[100.into(), 200.into(), 300.into(), 400.into()]) + }) == 300) + ); + assert!( + (({ + let _n: i32 = 0; + top_level_2(_n, &[42.into(), 99.into()]) + }) == 42) + ); + assert!( + (({ + let _n: i32 = 3; + top_level_2(_n, &[1.into(), 2.into(), 3.into(), 4.into()]) + }) == 4) + ); + return 0; +} diff --git a/tests/unit/out/refcount/va_arg_concat.rs b/tests/unit/out/refcount/va_arg_concat.rs index 2207b347..c39321b1 100644 --- a/tests/unit/out/refcount/va_arg_concat.rs +++ b/tests/unit/out/refcount/va_arg_concat.rs @@ -27,8 +27,23 @@ pub fn main() { std::process::exit(main_0()); } fn main_0() -> i32 { - return (({ - let _first: i32 = 1; - sum_ints_0(_first, &[2.into(), 3.into(), 4.into(), 0.into()]) - }) - 10); + assert!( + (({ + let _first: i32 = 1; + sum_ints_0(_first, &[2.into(), 3.into(), 4.into(), 0.into()]) + }) == 10) + ); + assert!( + (({ + let _first: i32 = 100; + sum_ints_0(_first, &[0.into()]) + }) == 100) + ); + assert!( + (({ + let _first: i32 = 5; + sum_ints_0(_first, &[5.into(), 5.into(), 5.into(), 5.into(), 0.into()]) + }) == 25) + ); + return 0; } diff --git a/tests/unit/out/refcount/va_arg_conditional.rs b/tests/unit/out/refcount/va_arg_conditional.rs new file mode 100644 index 00000000..5717a301 --- /dev/null +++ b/tests/unit/out/refcount/va_arg_conditional.rs @@ -0,0 +1,40 @@ +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 conditional_log_0(verbose: i32, fmt: Ptr, args: &[VaArg]) -> i32 { + let verbose: Value = Rc::new(RefCell::new(verbose)); + let fmt: Value> = Rc::new(RefCell::new(fmt)); + if ((*verbose.borrow()) != 0) { + let ap: Value = Rc::new(RefCell::new(::default())); + (*ap.borrow_mut()) = VaList::new(args); + let result: Value = Rc::new(RefCell::new(((*ap.borrow_mut()).arg::()).clone())); + return (*result.borrow()); + } + return -1_i32; +} +pub fn main() { + std::process::exit(main_0()); +} +fn main_0() -> i32 { + assert!( + (({ + let _verbose: i32 = 1; + let _fmt: Ptr = Ptr::from_string_literal("%d"); + conditional_log_0(_verbose, _fmt, &[42.into()]) + }) == 42) + ); + assert!( + (({ + let _verbose: i32 = 0; + let _fmt: Ptr = Ptr::from_string_literal("%d"); + conditional_log_0(_verbose, _fmt, &[99.into()]) + }) == -1_i32) + ); + return 0; +} diff --git a/tests/unit/out/refcount/va_arg_copy.rs b/tests/unit/out/refcount/va_arg_copy.rs new file mode 100644 index 00000000..f5cabeb4 --- /dev/null +++ b/tests/unit/out/refcount/va_arg_copy.rs @@ -0,0 +1,42 @@ +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 sum_with_copy_0(count: i32, args: &[VaArg]) -> i32 { + let count: Value = Rc::new(RefCell::new(count)); + let ap: Value = Rc::new(RefCell::new(::default())); + let aq: Value = Rc::new(RefCell::new(::default())); + (*ap.borrow_mut()) = VaList::new(args); + (*aq.borrow_mut()) = (*ap.borrow_mut()).clone(); + let sum1: Value = Rc::new(RefCell::new(0)); + let i: Value = Rc::new(RefCell::new(0)); + 'loop_: while ((*i.borrow()) < (*count.borrow())) { + (*sum1.borrow_mut()) += ((*ap.borrow_mut()).arg::()).clone(); + (*i.borrow_mut()).postfix_inc(); + } + let sum2: Value = Rc::new(RefCell::new(0)); + let i: Value = Rc::new(RefCell::new(0)); + 'loop_: while ((*i.borrow()) < (*count.borrow())) { + (*sum2.borrow_mut()) += ((*aq.borrow_mut()).arg::()).clone(); + (*i.borrow_mut()).postfix_inc(); + } + assert!(((*sum1.borrow()) == (*sum2.borrow()))); + return ((*sum1.borrow()) + (*sum2.borrow())); +} +pub fn main() { + std::process::exit(main_0()); +} +fn main_0() -> i32 { + assert!( + (({ + let _count: i32 = 3; + sum_with_copy_0(_count, &[10.into(), 20.into(), 30.into()]) + }) == 120) + ); + return 0; +} diff --git a/tests/unit/out/refcount/va_arg_forward.rs b/tests/unit/out/refcount/va_arg_forward.rs index b566876d..25929e68 100644 --- a/tests/unit/out/refcount/va_arg_forward.rs +++ b/tests/unit/out/refcount/va_arg_forward.rs @@ -35,8 +35,23 @@ pub fn main() { std::process::exit(main_0()); } fn main_0() -> i32 { - return (({ - let _count: i32 = 3; - outer_1(_count, &[10.into(), 20.into(), 30.into()]) - }) - 60); + assert!( + (({ + let _count: i32 = 3; + outer_1(_count, &[10.into(), 20.into(), 30.into()]) + }) == 60) + ); + assert!( + (({ + let _count: i32 = 1; + outer_1(_count, &[42.into()]) + }) == 42) + ); + assert!( + (({ + let _count: i32 = 0; + outer_1(_count, &[]) + }) == 0) + ); + return 0; } diff --git a/tests/unit/out/refcount/va_arg_mixed_int_ptr.rs b/tests/unit/out/refcount/va_arg_mixed_int_ptr.rs new file mode 100644 index 00000000..4cfaedc3 --- /dev/null +++ b/tests/unit/out/refcount/va_arg_mixed_int_ptr.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 mixed_args_0(count: i32, args: &[VaArg]) -> i32 { + let count: Value = Rc::new(RefCell::new(count)); + let ap: Value = Rc::new(RefCell::new(::default())); + (*ap.borrow_mut()) = VaList::new(args); + let total: Value = Rc::new(RefCell::new(0)); + let i: Value = Rc::new(RefCell::new(0)); + 'loop_: while ((*i.borrow()) < (*count.borrow())) { + let tag: Value = Rc::new(RefCell::new(((*ap.borrow_mut()).arg::()).clone())); + if ((*tag.borrow()) == 0) { + (*total.borrow_mut()) += ((*ap.borrow_mut()).arg::()).clone(); + } else { + let ptr: Value> = + Rc::new(RefCell::new(((*ap.borrow_mut()).arg::>()).clone())); + let __rhs = ((*ptr.borrow()).read()); + (*total.borrow_mut()) += __rhs; + } + (*i.borrow_mut()).postfix_inc(); + } + return (*total.borrow()); +} +pub fn main() { + std::process::exit(main_0()); +} +fn main_0() -> i32 { + let x: Value = Rc::new(RefCell::new(100)); + assert!( + (({ + let _count: i32 = 3; + mixed_args_0( + _count, + &[ + 0.into(), + 10.into(), + 1.into(), + (x.as_pointer()).into(), + 0.into(), + 20.into(), + ], + ) + }) == 130) + ); + let y: Value = Rc::new(RefCell::new(50)); + assert!( + (({ + let _count: i32 = 1; + mixed_args_0(_count, &[1.into(), (y.as_pointer()).into()]) + }) == 50) + ); + assert!( + (({ + let _count: i32 = 2; + mixed_args_0(_count, &[0.into(), 5.into(), 0.into(), 3.into()]) + }) == 8) + ); + return 0; +} diff --git a/tests/unit/out/refcount/va_arg_mixed_types.rs b/tests/unit/out/refcount/va_arg_mixed_types.rs index ad7450a1..c54047df 100644 --- a/tests/unit/out/refcount/va_arg_mixed_types.rs +++ b/tests/unit/out/refcount/va_arg_mixed_types.rs @@ -7,14 +7,22 @@ use std::io::Seek; use std::io::{Read, Write}; use std::os::fd::AsFd; use std::rc::{Rc, Weak}; -pub fn sum_mixed_0(count: i32, args: &[VaArg]) -> f64 { +pub fn sum_mixed_0(count: i32, args: &[VaArg]) -> i32 { let count: Value = Rc::new(RefCell::new(count)); let ap: Value = Rc::new(RefCell::new(::default())); (*ap.borrow_mut()) = VaList::new(args); - let total: Value = Rc::new(RefCell::new(0_f64)); + let total: Value = Rc::new(RefCell::new(0)); let i: Value = Rc::new(RefCell::new(0)); 'loop_: while ((*i.borrow()) < (*count.borrow())) { - (*total.borrow_mut()) += ((*ap.borrow_mut()).arg::()).clone(); + let tag: Value = Rc::new(RefCell::new(((*ap.borrow_mut()).arg::()).clone())); + if ((*tag.borrow()) == 0) { + (*total.borrow_mut()) += ((*ap.borrow_mut()).arg::()).clone(); + } else if ((*tag.borrow()) == 1) { + (*total.borrow_mut()) += ((*ap.borrow_mut()).arg::() as i32).clone(); + } else { + let val: Value = Rc::new(RefCell::new(((*ap.borrow_mut()).arg::()).clone())); + (*total.borrow_mut()) += ((*val.borrow()) as i32); + } (*i.borrow_mut()).postfix_inc(); } return (*total.borrow()); @@ -23,9 +31,33 @@ pub fn main() { std::process::exit(main_0()); } fn main_0() -> i32 { - return ((({ - let _count: i32 = 3; - sum_mixed_0(_count, &[1.5E+0.into(), 2.5E+0.into(), 3.0E+0.into()]) - }) as i32) - - 7); + assert!( + (({ + let _count: i32 = 3; + sum_mixed_0( + _count, + &[ + 0.into(), + 10.into(), + 1.into(), + 2.05E+1.into(), + 2.into(), + 30_i64.into(), + ], + ) + }) == 60) + ); + assert!( + (({ + let _count: i32 = 1; + sum_mixed_0(_count, &[0.into(), 42.into()]) + }) == 42) + ); + assert!( + (({ + let _count: i32 = 2; + sum_mixed_0(_count, &[1.into(), 3.7E+0.into(), 2.into(), 100_i64.into()]) + }) == 103) + ); + return 0; } diff --git a/tests/unit/out/refcount/va_arg_printf.rs b/tests/unit/out/refcount/va_arg_printf.rs new file mode 100644 index 00000000..5fe7f0d8 --- /dev/null +++ b/tests/unit/out/refcount/va_arg_printf.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 logf_impl_0(fmt: Ptr, ap: VaList) -> i32 { + let fmt: Value> = Rc::new(RefCell::new(fmt)); + let ap: Value = Rc::new(RefCell::new(ap)); + return { + let _lhs = ((*ap.borrow_mut()).arg::()).clone(); + _lhs + ((*ap.borrow_mut()).arg::()).clone() + }; +} +pub fn logf_1(fmt: Ptr, args: &[VaArg]) -> i32 { + let fmt: Value> = Rc::new(RefCell::new(fmt)); + let ap: Value = Rc::new(RefCell::new(::default())); + (*ap.borrow_mut()) = VaList::new(args); + let result: Value = Rc::new(RefCell::new( + ({ + let _fmt: Ptr = (*fmt.borrow()).clone(); + let _ap: VaList = (*ap.borrow()).clone(); + logf_impl_0(_fmt, _ap) + }), + )); + return (*result.borrow()); +} +pub fn main() { + std::process::exit(main_0()); +} +fn main_0() -> i32 { + assert!( + (({ + let _fmt: Ptr = Ptr::from_string_literal("hello %d %d"); + logf_1(_fmt, &[10.into(), 32.into()]) + }) == 42) + ); + assert!( + (({ + let _fmt: Ptr = Ptr::from_string_literal("x %d %d"); + logf_1(_fmt, &[1.into(), 2.into()]) + }) == 3) + ); + return 0; +} diff --git a/tests/unit/out/refcount/va_arg_promotion.rs b/tests/unit/out/refcount/va_arg_promotion.rs new file mode 100644 index 00000000..a7065516 --- /dev/null +++ b/tests/unit/out/refcount/va_arg_promotion.rs @@ -0,0 +1,43 @@ +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 test_promotions_0(count: i32, args: &[VaArg]) -> i32 { + let count: Value = Rc::new(RefCell::new(count)); + let ap: Value = Rc::new(RefCell::new(::default())); + (*ap.borrow_mut()) = VaList::new(args); + let a: Value = Rc::new(RefCell::new(((*ap.borrow_mut()).arg::()).clone())); + let b: Value = Rc::new(RefCell::new(((*ap.borrow_mut()).arg::()).clone())); + let c: Value = Rc::new(RefCell::new(((*ap.borrow_mut()).arg::()).clone())); + assert!(((*a.borrow()) == 65)); + assert!(((*b.borrow()) == 10)); + assert!(((*c.borrow()) == 3.0E+0)); + return (((*a.borrow()) + (*b.borrow())) + ((*c.borrow()) as i32)); +} +pub fn main() { + std::process::exit(main_0()); +} +fn main_0() -> i32 { + let x: Value = Rc::new(RefCell::new(('A' as u8))); + let y: Value = Rc::new(RefCell::new(10_i16)); + let z: Value = Rc::new(RefCell::new(3.0E+0)); + assert!( + (({ + let _count: i32 = 3; + test_promotions_0( + _count, + &[ + ((*x.borrow()) as i32).into(), + ((*y.borrow()) as i32).into(), + ((*z.borrow()) as f64).into(), + ], + ) + }) == 78) + ); + return 0; +} diff --git a/tests/unit/out/refcount/va_arg_snprintf.rs b/tests/unit/out/refcount/va_arg_snprintf.rs new file mode 100644 index 00000000..fcd28cbf --- /dev/null +++ b/tests/unit/out/refcount/va_arg_snprintf.rs @@ -0,0 +1,47 @@ +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 extract_first_0(buf: Ptr, size: i32, fmt: Ptr, args: &[VaArg]) -> i32 { + let buf: Value> = Rc::new(RefCell::new(buf)); + let size: Value = Rc::new(RefCell::new(size)); + let fmt: Value> = Rc::new(RefCell::new(fmt)); + let ap: Value = Rc::new(RefCell::new(::default())); + (*ap.borrow_mut()) = VaList::new(args); + let n: Value = Rc::new(RefCell::new(((*ap.borrow_mut()).arg::()).clone())); + let __rhs = ((*n.borrow()) as u8); + (*buf.borrow()).offset((0) as isize).write(__rhs); + return (*n.borrow()); +} +pub fn main() { + std::process::exit(main_0()); +} +fn main_0() -> i32 { + let buf: Value> = Rc::new(RefCell::new( + (0..64).map(|_| ::default()).collect::>(), + )); + assert!( + (({ + let _buf: Ptr = (buf.as_pointer() as Ptr); + let _size: i32 = 1; + let _fmt: Ptr = Ptr::from_string_literal("%d"); + extract_first_0(_buf, _size, _fmt, &[42.into()]) + }) == 42) + ); + assert!((((*buf.borrow())[(0) as usize] as i32) == 42)); + assert!( + (({ + let _buf: Ptr = (buf.as_pointer() as Ptr); + let _size: i32 = 1; + let _fmt: Ptr = Ptr::from_string_literal("%d"); + extract_first_0(_buf, _size, _fmt, &[65.into()]) + }) == 65) + ); + assert!((((*buf.borrow())[(0) as usize] as i32) == (('A' as u8) as i32))); + return 0; +} diff --git a/tests/unit/out/refcount/va_arg_struct_ctx.rs b/tests/unit/out/refcount/va_arg_struct_ctx.rs new file mode 100644 index 00000000..d8218acf --- /dev/null +++ b/tests/unit/out/refcount/va_arg_struct_ctx.rs @@ -0,0 +1,56 @@ +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 context { + pub verbose: Value, + pub last_error: Value, +} +impl Clone for context { + fn clone(&self) -> Self { + let mut this = Self { + verbose: Rc::new(RefCell::new((*self.verbose.borrow()))), + last_error: Rc::new(RefCell::new((*self.last_error.borrow()))), + }; + this + } +} +impl ByteRepr for context {} +pub fn set_error_0(ctx: Ptr, fmt: Ptr, args: &[VaArg]) { + let ctx: Value> = Rc::new(RefCell::new(ctx)); + let fmt: Value> = Rc::new(RefCell::new(fmt)); + if ((*(*(*ctx.borrow()).upgrade().deref()).verbose.borrow()) != 0) { + let ap: Value = Rc::new(RefCell::new(::default())); + (*ap.borrow_mut()) = VaList::new(args); + (*(*(*ctx.borrow()).upgrade().deref()).last_error.borrow_mut()) = + ((*ap.borrow_mut()).arg::()).clone(); + } +} +pub fn main() { + std::process::exit(main_0()); +} +fn main_0() -> i32 { + let ctx: Value = Rc::new(RefCell::new(::default())); + (*(*ctx.borrow()).verbose.borrow_mut()) = 1; + (*(*ctx.borrow()).last_error.borrow_mut()) = 0; + ({ + let _ctx: Ptr = (ctx.as_pointer()); + let _fmt: Ptr = Ptr::from_string_literal("error %d"); + set_error_0(_ctx, _fmt, &[42.into()]) + }); + assert!(((*(*ctx.borrow()).last_error.borrow()) == 42)); + (*(*ctx.borrow()).verbose.borrow_mut()) = 0; + ({ + let _ctx: Ptr = (ctx.as_pointer()); + let _fmt: Ptr = Ptr::from_string_literal("error %d"); + set_error_0(_ctx, _fmt, &[99.into()]) + }); + assert!(((*(*ctx.borrow()).last_error.borrow()) == 42)); + return 0; +} diff --git a/tests/unit/out/refcount/va_arg_two_passes.rs b/tests/unit/out/refcount/va_arg_two_passes.rs new file mode 100644 index 00000000..d5362b99 --- /dev/null +++ b/tests/unit/out/refcount/va_arg_two_passes.rs @@ -0,0 +1,47 @@ +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 sum_then_product_0(first: i32, args: &[VaArg]) -> i32 { + let first: Value = Rc::new(RefCell::new(first)); + let ap: Value = Rc::new(RefCell::new(::default())); + let sum: Value = Rc::new(RefCell::new((*first.borrow()))); + let product: Value = Rc::new(RefCell::new((*first.borrow()))); + (*ap.borrow_mut()) = VaList::new(args); + let val: Value = >::default(); + 'loop_: while ((({ + (*val.borrow_mut()) = ((*ap.borrow_mut()).arg::()).clone(); + (*val.borrow()) + }) as i32) + != 0) + { + (*sum.borrow_mut()) += (*val.borrow()); + } + (*ap.borrow_mut()) = VaList::new(args); + 'loop_: while ((({ + (*val.borrow_mut()) = ((*ap.borrow_mut()).arg::()).clone(); + (*val.borrow()) + }) as i32) + != 0) + { + (*product.borrow_mut()) *= (*val.borrow()); + } + return ((*sum.borrow()) + (*product.borrow())); +} +pub fn main() { + std::process::exit(main_0()); +} +fn main_0() -> i32 { + assert!( + (({ + let _first: i32 = 2; + sum_then_product_0(_first, &[3.into(), 4.into(), 0.into()]) + }) == 33) + ); + return 0; +} diff --git a/tests/unit/out/unsafe/va_arg_chain.rs b/tests/unit/out/unsafe/va_arg_chain.rs new file mode 100644 index 00000000..cdba8506 --- /dev/null +++ b/tests/unit/out/unsafe/va_arg_chain.rs @@ -0,0 +1,60 @@ +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 extract_nth_0(mut n: i32, mut ap: VaList) -> i32 { + let mut i: i32 = 0; + 'loop_: while ((i) < (n)) { + ap.arg::(); + i.postfix_inc(); + } + return ap.arg::(); +} +pub unsafe fn middle_layer_1(mut n: i32, mut ap: VaList) -> i32 { + return (unsafe { + let _n: i32 = n; + let _ap: VaList = ap; + extract_nth_0(_n, _ap) + }); +} +pub unsafe fn top_level_2(mut n: i32, args: &[VaArg]) -> i32 { + let mut ap: VaList = ::default(); + ap = VaList::new(args); + let mut result: i32 = (unsafe { + let _n: i32 = n; + let _ap: VaList = ap; + middle_layer_1(_n, _ap) + }); + return result; +} +pub fn main() { + unsafe { + std::process::exit(main_0() as i32); + } +} +unsafe fn main_0() -> i32 { + assert!( + ((unsafe { + let _n: i32 = 2; + top_level_2(_n, &[100.into(), 200.into(), 300.into(), 400.into()]) + }) == (300)) + ); + assert!( + ((unsafe { + let _n: i32 = 0; + top_level_2(_n, &[42.into(), 99.into()]) + }) == (42)) + ); + assert!( + ((unsafe { + let _n: i32 = 3; + top_level_2(_n, &[1.into(), 2.into(), 3.into(), 4.into()]) + }) == (4)) + ); + return 0; +} diff --git a/tests/unit/out/unsafe/va_arg_concat.rs b/tests/unit/out/unsafe/va_arg_concat.rs index fef2ff45..cddda762 100644 --- a/tests/unit/out/unsafe/va_arg_concat.rs +++ b/tests/unit/out/unsafe/va_arg_concat.rs @@ -28,8 +28,23 @@ pub fn main() { } } unsafe fn main_0() -> i32 { - return ((unsafe { - let _first: i32 = 1; - sum_ints_0(_first, &[2.into(), 3.into(), 4.into(), 0.into()]) - }) - (10)); + assert!( + ((unsafe { + let _first: i32 = 1; + sum_ints_0(_first, &[2.into(), 3.into(), 4.into(), 0.into()]) + }) == (10)) + ); + assert!( + ((unsafe { + let _first: i32 = 100; + sum_ints_0(_first, &[0.into()]) + }) == (100)) + ); + assert!( + ((unsafe { + let _first: i32 = 5; + sum_ints_0(_first, &[5.into(), 5.into(), 5.into(), 5.into(), 0.into()]) + }) == (25)) + ); + return 0; } diff --git a/tests/unit/out/unsafe/va_arg_conditional.rs b/tests/unit/out/unsafe/va_arg_conditional.rs new file mode 100644 index 00000000..f757e71f --- /dev/null +++ b/tests/unit/out/unsafe/va_arg_conditional.rs @@ -0,0 +1,40 @@ +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 conditional_log_0(mut verbose: i32, mut fmt: *const u8, args: &[VaArg]) -> i32 { + if (verbose != 0) { + let mut ap: VaList = ::default(); + ap = VaList::new(args); + let mut result: i32 = ap.arg::(); + return result; + } + return -1_i32; +} +pub fn main() { + unsafe { + std::process::exit(main_0() as i32); + } +} +unsafe fn main_0() -> i32 { + assert!( + ((unsafe { + let _verbose: i32 = 1; + let _fmt: *const u8 = b"%d\0".as_ptr(); + conditional_log_0(_verbose, _fmt, &[42.into()]) + }) == (42)) + ); + assert!( + ((unsafe { + let _verbose: i32 = 0; + let _fmt: *const u8 = b"%d\0".as_ptr(); + conditional_log_0(_verbose, _fmt, &[99.into()]) + }) == (-1_i32)) + ); + return 0; +} diff --git a/tests/unit/out/unsafe/va_arg_copy.rs b/tests/unit/out/unsafe/va_arg_copy.rs new file mode 100644 index 00000000..ce342ee5 --- /dev/null +++ b/tests/unit/out/unsafe/va_arg_copy.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 sum_with_copy_0(mut count: i32, args: &[VaArg]) -> i32 { + let mut ap: VaList = ::default(); + let mut aq: VaList = ::default(); + ap = VaList::new(args); + aq = ap.clone(); + let mut sum1: i32 = 0; + let mut i: i32 = 0; + 'loop_: while ((i) < (count)) { + sum1 += ap.arg::(); + i.postfix_inc(); + } + let mut sum2: i32 = 0; + let mut i: i32 = 0; + 'loop_: while ((i) < (count)) { + sum2 += aq.arg::(); + i.postfix_inc(); + } + assert!(((sum1) == (sum2))); + return ((sum1) + (sum2)); +} +pub fn main() { + unsafe { + std::process::exit(main_0() as i32); + } +} +unsafe fn main_0() -> i32 { + assert!( + ((unsafe { + let _count: i32 = 3; + sum_with_copy_0(_count, &[10.into(), 20.into(), 30.into()]) + }) == (120)) + ); + return 0; +} diff --git a/tests/unit/out/unsafe/va_arg_forward.rs b/tests/unit/out/unsafe/va_arg_forward.rs index 425e2e16..475fef73 100644 --- a/tests/unit/out/unsafe/va_arg_forward.rs +++ b/tests/unit/out/unsafe/va_arg_forward.rs @@ -32,8 +32,23 @@ pub fn main() { } } unsafe fn main_0() -> i32 { - return ((unsafe { - let _count: i32 = 3; - outer_1(_count, &[10.into(), 20.into(), 30.into()]) - }) - (60)); + assert!( + ((unsafe { + let _count: i32 = 3; + outer_1(_count, &[10.into(), 20.into(), 30.into()]) + }) == (60)) + ); + assert!( + ((unsafe { + let _count: i32 = 1; + outer_1(_count, &[42.into()]) + }) == (42)) + ); + assert!( + ((unsafe { + let _count: i32 = 0; + outer_1(_count, &[]) + }) == (0)) + ); + return 0; } diff --git a/tests/unit/out/unsafe/va_arg_mixed_int_ptr.rs b/tests/unit/out/unsafe/va_arg_mixed_int_ptr.rs new file mode 100644 index 00000000..9460298e --- /dev/null +++ b/tests/unit/out/unsafe/va_arg_mixed_int_ptr.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 mixed_args_0(mut count: i32, args: &[VaArg]) -> i32 { + let mut ap: VaList = ::default(); + ap = VaList::new(args); + let mut total: i32 = 0; + let mut i: i32 = 0; + 'loop_: while ((i) < (count)) { + let mut tag: i32 = ap.arg::(); + if ((tag) == (0)) { + total += ap.arg::(); + } else { + let mut ptr: *mut i32 = ap.arg::<*mut i32>(); + total += (*ptr); + } + i.postfix_inc(); + } + return total; +} +pub fn main() { + unsafe { + std::process::exit(main_0() as i32); + } +} +unsafe fn main_0() -> i32 { + let mut x: i32 = 100; + assert!( + ((unsafe { + let _count: i32 = 3; + mixed_args_0( + _count, + &[ + 0.into(), + 10.into(), + 1.into(), + (&mut x as *mut i32).into(), + 0.into(), + 20.into(), + ], + ) + }) == (130)) + ); + let mut y: i32 = 50; + assert!( + ((unsafe { + let _count: i32 = 1; + mixed_args_0(_count, &[1.into(), (&mut y as *mut i32).into()]) + }) == (50)) + ); + assert!( + ((unsafe { + let _count: i32 = 2; + mixed_args_0(_count, &[0.into(), 5.into(), 0.into(), 3.into()]) + }) == (8)) + ); + return 0; +} diff --git a/tests/unit/out/unsafe/va_arg_mixed_types.rs b/tests/unit/out/unsafe/va_arg_mixed_types.rs index 45360e75..0a8411e5 100644 --- a/tests/unit/out/unsafe/va_arg_mixed_types.rs +++ b/tests/unit/out/unsafe/va_arg_mixed_types.rs @@ -7,13 +7,21 @@ use std::io::Seek; use std::io::{Read, Write}; use std::os::fd::{AsFd, FromRawFd, IntoRawFd}; use std::rc::Rc; -pub unsafe fn sum_mixed_0(mut count: i32, args: &[VaArg]) -> f64 { +pub unsafe fn sum_mixed_0(mut count: i32, args: &[VaArg]) -> i32 { let mut ap: VaList = ::default(); ap = VaList::new(args); - let mut total: f64 = 0_f64; + let mut total: i32 = 0; let mut i: i32 = 0; 'loop_: while ((i) < (count)) { - total += ap.arg::(); + let mut tag: i32 = ap.arg::(); + if ((tag) == (0)) { + total += ap.arg::(); + } else if ((tag) == (1)) { + total += (ap.arg::() as i32); + } else { + let mut val: i64 = ap.arg::(); + total += (val as i32); + } i.postfix_inc(); } return total; @@ -24,9 +32,33 @@ pub fn main() { } } unsafe fn main_0() -> i32 { - return (((unsafe { - let _count: i32 = 3; - sum_mixed_0(_count, &[1.5E+0.into(), 2.5E+0.into(), 3.0E+0.into()]) - }) as i32) - - (7)); + assert!( + ((unsafe { + let _count: i32 = 3; + sum_mixed_0( + _count, + &[ + 0.into(), + 10.into(), + 1.into(), + 2.05E+1.into(), + 2.into(), + 30_i64.into(), + ], + ) + }) == (60)) + ); + assert!( + ((unsafe { + let _count: i32 = 1; + sum_mixed_0(_count, &[0.into(), 42.into()]) + }) == (42)) + ); + assert!( + ((unsafe { + let _count: i32 = 2; + sum_mixed_0(_count, &[1.into(), 3.7E+0.into(), 2.into(), 100_i64.into()]) + }) == (103)) + ); + return 0; } diff --git a/tests/unit/out/unsafe/va_arg_printf.rs b/tests/unit/out/unsafe/va_arg_printf.rs new file mode 100644 index 00000000..a0adbac1 --- /dev/null +++ b/tests/unit/out/unsafe/va_arg_printf.rs @@ -0,0 +1,42 @@ +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 logf_impl_0(mut fmt: *const u8, mut ap: VaList) -> i32 { + return ((ap.arg::()) + (ap.arg::())); +} +pub unsafe fn logf_1(mut fmt: *const u8, args: &[VaArg]) -> i32 { + let mut ap: VaList = ::default(); + ap = VaList::new(args); + let mut result: i32 = (unsafe { + let _fmt: *const u8 = fmt; + let _ap: VaList = ap; + logf_impl_0(_fmt, _ap) + }); + return result; +} +pub fn main() { + unsafe { + std::process::exit(main_0() as i32); + } +} +unsafe fn main_0() -> i32 { + assert!( + ((unsafe { + let _fmt: *const u8 = b"hello %d %d\0".as_ptr(); + logf_1(_fmt, &[10.into(), 32.into()]) + }) == (42)) + ); + assert!( + ((unsafe { + let _fmt: *const u8 = b"x %d %d\0".as_ptr(); + logf_1(_fmt, &[1.into(), 2.into()]) + }) == (3)) + ); + return 0; +} diff --git a/tests/unit/out/unsafe/va_arg_promotion.rs b/tests/unit/out/unsafe/va_arg_promotion.rs new file mode 100644 index 00000000..5cef927b --- /dev/null +++ b/tests/unit/out/unsafe/va_arg_promotion.rs @@ -0,0 +1,40 @@ +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 test_promotions_0(mut count: i32, args: &[VaArg]) -> i32 { + let mut ap: VaList = ::default(); + ap = VaList::new(args); + let mut a: i32 = ap.arg::(); + let mut b: i32 = ap.arg::(); + let mut c: f64 = ap.arg::(); + assert!(((a) == (65))); + assert!(((b) == (10))); + assert!(((c) == (3.0E+0))); + return (((a) + (b)) + (c as i32)); +} +pub fn main() { + unsafe { + std::process::exit(main_0() as i32); + } +} +unsafe fn main_0() -> i32 { + let mut x: u8 = ('A' as u8); + let mut y: i16 = 10_i16; + let mut z: f32 = 3.0E+0; + assert!( + ((unsafe { + let _count: i32 = 3; + test_promotions_0( + _count, + &[(x as i32).into(), (y as i32).into(), (z as f64).into()], + ) + }) == (78)) + ); + return 0; +} diff --git a/tests/unit/out/unsafe/va_arg_snprintf.rs b/tests/unit/out/unsafe/va_arg_snprintf.rs new file mode 100644 index 00000000..38ccf04e --- /dev/null +++ b/tests/unit/out/unsafe/va_arg_snprintf.rs @@ -0,0 +1,48 @@ +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 extract_first_0( + mut buf: *mut u8, + mut size: i32, + mut fmt: *const u8, + args: &[VaArg], +) -> i32 { + let mut ap: VaList = ::default(); + ap = VaList::new(args); + let mut n: i32 = ap.arg::(); + (*buf.offset((0) as isize)) = (n as u8); + return n; +} +pub fn main() { + unsafe { + std::process::exit(main_0() as i32); + } +} +unsafe fn main_0() -> i32 { + let mut buf: [u8; 64] = [::default(); 64]; + assert!( + ((unsafe { + let _buf: *mut u8 = buf.as_mut_ptr(); + let _size: i32 = 1; + let _fmt: *const u8 = b"%d\0".as_ptr(); + extract_first_0(_buf, _size, _fmt, &[42.into()]) + }) == (42)) + ); + assert!(((buf[(0) as usize] as i32) == (42))); + assert!( + ((unsafe { + let _buf: *mut u8 = buf.as_mut_ptr(); + let _size: i32 = 1; + let _fmt: *const u8 = b"%d\0".as_ptr(); + extract_first_0(_buf, _size, _fmt, &[65.into()]) + }) == (65)) + ); + assert!(((buf[(0) as usize] as i32) == (('A' as u8) as i32))); + return 0; +} diff --git a/tests/unit/out/unsafe/va_arg_struct_ctx.rs b/tests/unit/out/unsafe/va_arg_struct_ctx.rs new file mode 100644 index 00000000..8ce93a92 --- /dev/null +++ b/tests/unit/out/unsafe/va_arg_struct_ctx.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; +#[derive(Copy, Clone, Default)] +pub struct context { + pub verbose: i32, + pub last_error: i32, +} +pub unsafe fn set_error_0(mut ctx: *mut context, mut fmt: *const u8, args: &[VaArg]) { + if ((*ctx).verbose != 0) { + let mut ap: VaList = ::default(); + ap = VaList::new(args); + (*ctx).last_error = (ap.arg::()).clone(); + } +} +pub fn main() { + unsafe { + std::process::exit(main_0() as i32); + } +} +unsafe fn main_0() -> i32 { + let mut ctx: context = ::default(); + ctx.verbose = 1; + ctx.last_error = 0; + (unsafe { + let _ctx: *mut context = (&mut ctx as *mut context); + let _fmt: *const u8 = b"error %d\0".as_ptr(); + set_error_0(_ctx, _fmt, &[42.into()]) + }); + assert!(((ctx.last_error) == (42))); + ctx.verbose = 0; + (unsafe { + let _ctx: *mut context = (&mut ctx as *mut context); + let _fmt: *const u8 = b"error %d\0".as_ptr(); + set_error_0(_ctx, _fmt, &[99.into()]) + }); + assert!(((ctx.last_error) == (42))); + return 0; +} diff --git a/tests/unit/out/unsafe/va_arg_two_passes.rs b/tests/unit/out/unsafe/va_arg_two_passes.rs new file mode 100644 index 00000000..0ebe33ca --- /dev/null +++ b/tests/unit/out/unsafe/va_arg_two_passes.rs @@ -0,0 +1,48 @@ +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 sum_then_product_0(mut first: i32, args: &[VaArg]) -> i32 { + let mut ap: VaList = ::default(); + let mut sum: i32 = first; + let mut product: i32 = first; + ap = VaList::new(args); + let mut val: i32 = ::default(); + 'loop_: while ((({ + val = ap.arg::(); + val + }) as i32) + != (0)) + { + sum += val; + } + ap = VaList::new(args); + 'loop_: while ((({ + val = ap.arg::(); + val + }) as i32) + != (0)) + { + product *= val; + } + return ((sum) + (product)); +} +pub fn main() { + unsafe { + std::process::exit(main_0() as i32); + } +} +unsafe fn main_0() -> i32 { + assert!( + ((unsafe { + let _first: i32 = 2; + sum_then_product_0(_first, &[3.into(), 4.into(), 0.into()]) + }) == (33)) + ); + return 0; +} diff --git a/tests/unit/va_arg_chain.c b/tests/unit/va_arg_chain.c new file mode 100644 index 00000000..87b315c2 --- /dev/null +++ b/tests/unit/va_arg_chain.c @@ -0,0 +1,30 @@ +// Pattern from curl's trc_write chain: variadic -> va_list -> va_list forwarding +#include +#include + +int extract_nth(int n, va_list ap) { + for (int i = 0; i < n; i++) { + va_arg(ap, int); + } + return va_arg(ap, int); +} + +int middle_layer(int n, va_list ap) { + return extract_nth(n, ap); +} + +int top_level(int n, ...) { + va_list ap; + va_start(ap, n); + int result = middle_layer(n, ap); + va_end(ap); + return result; +} + +int main() { + // Extract the 3rd arg (0-indexed): 100, 200, 300, 400 -> 300 + assert(top_level(2, 100, 200, 300, 400) == 300); + assert(top_level(0, 42, 99) == 42); + assert(top_level(3, 1, 2, 3, 4) == 4); + return 0; +} diff --git a/tests/unit/va_arg_concat.c b/tests/unit/va_arg_concat.c index 2f1a41bb..19f77ae7 100644 --- a/tests/unit/va_arg_concat.c +++ b/tests/unit/va_arg_concat.c @@ -1,3 +1,4 @@ +#include #include int sum_ints(int first, ...) { @@ -15,5 +16,8 @@ int sum_ints(int first, ...) { } int main() { - return sum_ints(1, 2, 3, 4, 0) - 10; + assert(sum_ints(1, 2, 3, 4, 0) == 10); + assert(sum_ints(100, 0) == 100); + assert(sum_ints(5, 5, 5, 5, 5, 0) == 25); + return 0; } diff --git a/tests/unit/va_arg_conditional.c b/tests/unit/va_arg_conditional.c new file mode 100644 index 00000000..06c1faec --- /dev/null +++ b/tests/unit/va_arg_conditional.c @@ -0,0 +1,20 @@ +// Pattern from Curl_failf: va_start only under a condition +#include +#include + +int conditional_log(int verbose, const char *fmt, ...) { + if (verbose) { + va_list ap; + va_start(ap, fmt); + int result = va_arg(ap, int); + va_end(ap); + return result; + } + return -1; +} + +int main() { + assert(conditional_log(1, "%d", 42) == 42); + assert(conditional_log(0, "%d", 99) == -1); + return 0; +} diff --git a/tests/unit/va_arg_copy.c b/tests/unit/va_arg_copy.c new file mode 100644 index 00000000..3e4e34b8 --- /dev/null +++ b/tests/unit/va_arg_copy.c @@ -0,0 +1,34 @@ +// va_copy: save position, consume from copy, original still usable +#include +#include + +int sum_with_copy(int count, ...) { + va_list ap, aq; + va_start(ap, count); + + // Copy before consuming + va_copy(aq, ap); + + // Consume from original + int sum1 = 0; + for (int i = 0; i < count; i++) { + sum1 += va_arg(ap, int); + } + va_end(ap); + + // Consume from copy (should start from same position) + int sum2 = 0; + for (int i = 0; i < count; i++) { + sum2 += va_arg(aq, int); + } + va_end(aq); + + assert(sum1 == sum2); + return sum1 + sum2; +} + +int main() { + // sum1 = 10+20+30 = 60, sum2 = 10+20+30 = 60, total = 120 + assert(sum_with_copy(3, 10, 20, 30) == 120); + return 0; +} diff --git a/tests/unit/va_arg_forward.c b/tests/unit/va_arg_forward.c index f2f03772..33d7e287 100644 --- a/tests/unit/va_arg_forward.c +++ b/tests/unit/va_arg_forward.c @@ -1,3 +1,4 @@ +#include #include int inner(int count, va_list ap) { @@ -17,5 +18,8 @@ int outer(int count, ...) { } int main() { - return outer(3, 10, 20, 30) - 60; + assert(outer(3, 10, 20, 30) == 60); + assert(outer(1, 42) == 42); + assert(outer(0) == 0); + return 0; } diff --git a/tests/unit/va_arg_mixed_int_ptr.c b/tests/unit/va_arg_mixed_int_ptr.c new file mode 100644 index 00000000..efd9833f --- /dev/null +++ b/tests/unit/va_arg_mixed_int_ptr.c @@ -0,0 +1,33 @@ +// va_arg with mixed int and pointer types in the same call +#include +#include + +int mixed_args(int count, ...) { + va_list ap; + va_start(ap, count); + + int total = 0; + for (int i = 0; i < count; i++) { + int tag = va_arg(ap, int); + if (tag == 0) { + total += va_arg(ap, int); + } else { + int *ptr = va_arg(ap, int *); + total += *ptr; + } + } + + va_end(ap); + return total; +} + +int main() { + int x = 100; + // tag=0 val=10, tag=1 ptr=&x(100), tag=0 val=20 + assert(mixed_args(3, 0, 10, 1, &x, 0, 20) == 130); + + int y = 50; + assert(mixed_args(1, 1, &y) == 50); + assert(mixed_args(2, 0, 5, 0, 3) == 8); + return 0; +} diff --git a/tests/unit/va_arg_mixed_types.c b/tests/unit/va_arg_mixed_types.c index 4d1badc7..2c49c06b 100644 --- a/tests/unit/va_arg_mixed_types.c +++ b/tests/unit/va_arg_mixed_types.c @@ -1,16 +1,29 @@ +#include #include -double sum_mixed(int count, ...) { +int sum_mixed(int count, ...) { va_list ap; va_start(ap, count); - double total = 0; + int total = 0; for (int i = 0; i < count; i++) { - total += va_arg(ap, double); + int tag = va_arg(ap, int); + if (tag == 0) { + total += va_arg(ap, int); + } else if (tag == 1) { + total += (int)va_arg(ap, double); + } else { + long val = va_arg(ap, long); + total += (int)val; + } } va_end(ap); return total; } int main() { - return (int)sum_mixed(3, 1.5, 2.5, 3.0) - 7; + // tag=0 int=10, tag=1 double=20.5, tag=2 long=30 + assert(sum_mixed(3, 0, 10, 1, 20.5, 2, 30L) == 60); + assert(sum_mixed(1, 0, 42) == 42); + assert(sum_mixed(2, 1, 3.7, 2, 100L) == 103); + return 0; } diff --git a/tests/unit/va_arg_printf.c b/tests/unit/va_arg_printf.c new file mode 100644 index 00000000..7073e46a --- /dev/null +++ b/tests/unit/va_arg_printf.c @@ -0,0 +1,21 @@ +#include +#include + +int logf_impl(const char *fmt, va_list ap) { + (void)fmt; + return va_arg(ap, int) + va_arg(ap, int); +} + +int logf(const char *fmt, ...) { + va_list ap; + va_start(ap, fmt); + int result = logf_impl(fmt, ap); + va_end(ap); + return result; +} + +int main() { + assert(logf("hello %d %d", 10, 32) == 42); + assert(logf("x %d %d", 1, 2) == 3); + return 0; +} diff --git a/tests/unit/va_arg_promotion.c b/tests/unit/va_arg_promotion.c new file mode 100644 index 00000000..e938625c --- /dev/null +++ b/tests/unit/va_arg_promotion.c @@ -0,0 +1,32 @@ +// C promotion rules: char/short promote to int, float promotes to double +#include +#include + +int test_promotions(int count, ...) { + va_list ap; + va_start(ap, count); + + // char and short are promoted to int by the caller + int a = va_arg(ap, int); // was passed as char 'A' (65) + int b = va_arg(ap, int); // was passed as short 10 + + // float is promoted to double by the caller + double c = va_arg(ap, double); // was passed as float 3.0 + + va_end(ap); + + assert(a == 65); + assert(b == 10); + assert(c == 3.0); + + return a + b + (int)c; +} + +int main() { + char x = 'A'; // 65 + short y = 10; + float z = 3.0f; + // 65 + 10 + 3 = 78 + assert(test_promotions(3, x, y, z) == 78); + return 0; +} diff --git a/tests/unit/va_arg_snprintf.c b/tests/unit/va_arg_snprintf.c new file mode 100644 index 00000000..75a5e7c3 --- /dev/null +++ b/tests/unit/va_arg_snprintf.c @@ -0,0 +1,20 @@ +#include +#include + +int extract_first(char *buf, int size, const char *fmt, ...) { + va_list ap; + va_start(ap, fmt); + int n = va_arg(ap, int); + buf[0] = (char)n; + va_end(ap); + return n; +} + +int main() { + char buf[64]; + assert(extract_first(buf, 1, "%d", 42) == 42); + assert(buf[0] == 42); + assert(extract_first(buf, 1, "%d", 65) == 65); + assert(buf[0] == 'A'); + return 0; +} diff --git a/tests/unit/va_arg_struct_ctx.c b/tests/unit/va_arg_struct_ctx.c new file mode 100644 index 00000000..560cd90c --- /dev/null +++ b/tests/unit/va_arg_struct_ctx.c @@ -0,0 +1,32 @@ +// Pattern from Curl_failf: variadic function receives a struct pointer as first arg +#include +#include + +struct context { + int verbose; + int last_error; +}; + +void set_error(struct context *ctx, const char *fmt, ...) { + if (ctx->verbose) { + va_list ap; + va_start(ap, fmt); + ctx->last_error = va_arg(ap, int); + va_end(ap); + } +} + +int main() { + struct context ctx; + ctx.verbose = 1; + ctx.last_error = 0; + + set_error(&ctx, "error %d", 42); + assert(ctx.last_error == 42); + + ctx.verbose = 0; + set_error(&ctx, "error %d", 99); + assert(ctx.last_error == 42); + + return 0; +} diff --git a/tests/unit/va_arg_two_passes.c b/tests/unit/va_arg_two_passes.c new file mode 100644 index 00000000..9f5cc4d2 --- /dev/null +++ b/tests/unit/va_arg_two_passes.c @@ -0,0 +1,32 @@ +// Pattern from wget's concat_strings: va_start twice to iterate args in two passes +#include +#include + +int sum_then_product(int first, ...) { + va_list ap; + int sum = first; + int product = first; + + // First pass: compute sum + va_start(ap, first); + int val; + while ((val = va_arg(ap, int)) != 0) { + sum += val; + } + va_end(ap); + + // Second pass: compute product + va_start(ap, first); + while ((val = va_arg(ap, int)) != 0) { + product *= val; + } + va_end(ap); + + return sum + product; +} + +int main() { + // sum = 2+3+4 = 9, product = 2*3*4 = 24, total = 33 + assert(sum_then_product(2, 3, 4, 0) == 33); + return 0; +} From f91e7e462848855d1aa3eba103009f2cf045c249 Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Fri, 10 Apr 2026 18:39:59 +0100 Subject: [PATCH 16/23] Use unsafe type to get the arg in va_list --- cpp2rust/converter/models/converter_refcount.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cpp2rust/converter/models/converter_refcount.cpp b/cpp2rust/converter/models/converter_refcount.cpp index 29474a6c..6ba2f556 100644 --- a/cpp2rust/converter/models/converter_refcount.cpp +++ b/cpp2rust/converter/models/converter_refcount.cpp @@ -1398,7 +1398,7 @@ bool ConverterRefCount::VisitVAArgExpr(clang::VAArgExpr *expr) { } StrCat(ConvertLValue(va_list_expr)); StrCat(".arg::<"); - Convert(expr->getType()); + StrCat(GetUnsafeTypeAsString(expr->getType())); StrCat(">()"); return false; } From 8b800d7aa504e50922cc410d32a2b7abd0aa8628 Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Fri, 10 Apr 2026 18:40:31 +0100 Subject: [PATCH 17/23] Detect double borrow error in va_arg usages --- .../converter/models/converter_refcount.cpp | 23 ++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/cpp2rust/converter/models/converter_refcount.cpp b/cpp2rust/converter/models/converter_refcount.cpp index 6ba2f556..cff67ba8 100644 --- a/cpp2rust/converter/models/converter_refcount.cpp +++ b/cpp2rust/converter/models/converter_refcount.cpp @@ -1495,6 +1495,18 @@ void ConverterRefCount::ConvertVarInit(clang::QualType qual_type, in_function_formals_ = false; } +static bool ContainsVAArgExpr(const clang::Stmt *stmt) { + if (clang::isa(stmt)) { + return true; + } + for (auto *child : stmt->children()) { + if (ContainsVAArgExpr(child)) { + return true; + } + } + return false; +} + static std::unordered_set GetAllVars(const clang::Stmt *stmt) { std::unordered_set vars; @@ -1606,9 +1618,14 @@ void ConverterRefCount::ConvertGenericBinaryOperator( auto sides_contain_ptr_or_deref = std::ranges::any_of(rhs_vars, predicate) || std::ranges::any_of(lhs_vars, predicate); - auto may_cause_borrow_mut_err = !sides_contains_literal && - !same_var_on_both_sides && - sides_contain_ptr_or_deref; + auto both_sides_have_va_arg = same_var_on_both_sides && + ContainsVAArgExpr(lhs) && + ContainsVAArgExpr(rhs); + + auto may_cause_borrow_mut_err = + both_sides_have_va_arg || + (!sides_contains_literal && !same_var_on_both_sides && + sides_contain_ptr_or_deref); if (may_cause_borrow_mut_err) { StrCat(std::format("{{ let _lhs = {}; _lhs {} {} }}", From a0e2b8b28172ac6a912550abbc3dfc69c201781e Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Mon, 13 Apr 2026 13:13:23 +0100 Subject: [PATCH 18/23] Remove bloat from VaArg, VaList and VaArgGet --- libcc2rs/src/va_args.rs | 197 ++++++++++++---------------------------- 1 file changed, 60 insertions(+), 137 deletions(-) diff --git a/libcc2rs/src/va_args.rs b/libcc2rs/src/va_args.rs index deba532d..41fc276a 100644 --- a/libcc2rs/src/va_args.rs +++ b/libcc2rs/src/va_args.rs @@ -13,36 +13,30 @@ pub enum VaArg { Ptr(AnyPtr), } -macro_rules! impl_from { - ($ty:ty, $variant:ident) => { +macro_rules! impl_va_arg_from { + (direct: $($ty:ty => $variant:ident),*) => {$( impl From<$ty> for VaArg { - fn from(v: $ty) -> Self { - VaArg::$variant(v) - } + fn from(v: $ty) -> Self { VaArg::$variant(v) } } - }; - ($ty:ty => $variant:ident as $cast:ty) => { + )*}; + (promote: $($ty:ty => $variant:ident as $cast:ty),*) => {$( impl From<$ty> for VaArg { - fn from(v: $ty) -> Self { - VaArg::$variant(v as $cast) - } + fn from(v: $ty) -> Self { VaArg::$variant(v as $cast) } + } + )*}; + (ptr: $($ty:ty),*) => {$( + impl From<*mut $ty> for VaArg { + fn from(v: *mut $ty) -> Self { VaArg::RawPtr(v as *mut c_void) } } - }; + impl From<*const $ty> for VaArg { + fn from(v: *const $ty) -> Self { VaArg::RawPtr(v as *mut c_void) } + } + )*}; } -impl_from!(i32, Int); -impl_from!(u32, UInt); -impl_from!(i64, Long); -impl_from!(u64, ULong); -impl_from!(f64, Double); - -// C promotion: char/short -> int, float -> double -impl_from!(i8 => Int as i32); -impl_from!(i16 => Int as i32); -impl_from!(u8 => UInt as u32); -impl_from!(u16 => UInt as u32); -impl_from!(f32 => Double as f64); -impl_from!(AnyPtr, Ptr); +impl_va_arg_from!(direct: i32 => Int, u32 => UInt, i64 => Long, u64 => ULong, f64 => Double, AnyPtr => Ptr); +impl_va_arg_from!(promote: i8 => Int as i32, i16 => Int as i32, u8 => UInt as u32, u16 => UInt as u32, f32 => Double as f64); +impl_va_arg_from!(ptr: c_void, i8, u8, i16, u16, i32, u32, i64, u64, f32, f64, usize, isize); impl From> for VaArg { fn from(v: crate::rc::Ptr) -> Self { @@ -50,17 +44,7 @@ impl From> } } -macro_rules! impl_from_ptr { - ($($ty:ty),*) => { - $( - impl_from!(*mut $ty => RawPtr as *mut c_void); - impl_from!(*const $ty => RawPtr as *mut c_void); - )* - }; -} - -impl_from_ptr!(c_void, i8, u8, i16, u16, i32, u32, i64, u64, f32, f64, usize, isize); - +#[derive(Clone, Copy, Default)] pub struct VaList<'a> { args: &'a [VaArg], pos: usize, @@ -78,120 +62,59 @@ impl<'a> VaList<'a> { } } -impl Default for VaList<'_> { - fn default() -> Self { - VaList { args: &[], pos: 0 } - } -} - -impl Clone for VaList<'_> { - fn clone(&self) -> Self { - VaList { - args: self.args, - pos: self.pos, - } - } -} - -fn get_int(v: &VaArg) -> i64 { - match v { - VaArg::Int(n) => *n as i64, - VaArg::UInt(n) => *n as i64, - VaArg::Long(n) => *n, - VaArg::ULong(n) => *n as i64, - _ => panic!("VaList::arg: expected integer, got different variant"), - } -} - -fn get_uint(v: &VaArg) -> u64 { - match v { - VaArg::Int(n) => *n as u64, - VaArg::UInt(n) => *n as u64, - VaArg::Long(n) => *n as u64, - VaArg::ULong(n) => *n, - _ => panic!("VaList::arg: expected unsigned integer, got different variant"), - } -} - -fn get_float(v: &VaArg) -> f64 { - match v { - VaArg::Double(n) => *n, - VaArg::Int(n) => *n as f64, - VaArg::Long(n) => *n as f64, - _ => panic!("VaList::arg: expected float, got different variant"), - } -} - -fn get_ptr(v: &VaArg) -> *mut c_void { - match v { - VaArg::RawPtr(p) => *p, - _ => panic!("VaList::arg: expected pointer, got different variant"), - } -} - pub trait VaArgGet { fn get(v: &VaArg) -> Self; } -macro_rules! impl_get_int { - ($($ty:ty),*) => { - $(impl VaArgGet for $ty { - fn get(v: &VaArg) -> Self { get_int(v) as $ty } - })* - }; -} - -macro_rules! impl_get_uint { - ($($ty:ty),*) => { - $(impl VaArgGet for $ty { - fn get(v: &VaArg) -> Self { get_uint(v) as $ty } - })* - }; -} - -macro_rules! impl_get_ptr { - ($($ty:ty),*) => { - $(impl VaArgGet for $ty { - fn get(v: &VaArg) -> Self { get_ptr(v) as $ty } - })* - }; -} - -impl_get_int!(i8, i16, i32, i64); -impl_get_uint!(u8, u16, u32, u64); - -impl VaArgGet for f32 { - fn get(v: &VaArg) -> Self { - get_float(v) as f32 - } -} - -impl VaArgGet for f64 { - fn get(v: &VaArg) -> Self { - get_float(v) - } -} - -macro_rules! impl_get_ptr { - ($($ty:ty),*) => { - $( - impl VaArgGet for *mut $ty { - fn get(v: &VaArg) -> Self { get_ptr(v) as *mut $ty } +macro_rules! impl_va_arg_get { + (int: $($ty:ty),*) => {$( + impl VaArgGet for $ty { + fn get(v: &VaArg) -> Self { + match v { + VaArg::Int(n) => *n as Self, + VaArg::UInt(n) => *n as Self, + VaArg::Long(n) => *n as Self, + VaArg::ULong(n) => *n as Self, + _ => panic!("VaArgGet: expected integer"), + } } - impl VaArgGet for *const $ty { - fn get(v: &VaArg) -> Self { get_ptr(v) as *const $ty } + } + )*}; + (float: $($ty:ty),*) => {$( + impl VaArgGet for $ty { + fn get(v: &VaArg) -> Self { + match v { + VaArg::Double(n) => *n as Self, + VaArg::Int(n) => *n as Self, + VaArg::Long(n) => *n as Self, + _ => panic!("VaArgGet: expected float"), + } + } + } + )*}; + (ptr: $($ty:ty),*) => {$( + impl VaArgGet for *mut $ty { + fn get(v: &VaArg) -> Self { + match v { VaArg::RawPtr(p) => *p as Self, _ => panic!("VaArgGet: expected pointer") } } - )* - }; + } + impl VaArgGet for *const $ty { + fn get(v: &VaArg) -> Self { + match v { VaArg::RawPtr(p) => *p as Self, _ => panic!("VaArgGet: expected pointer") } + } + } + )*}; } -impl_get_ptr!(c_void, i8, u8, i16, u16, i32, u32, i64, u64, f32, f64, usize, isize); +impl_va_arg_get!(int: i8, i16, i32, i64, u8, u16, u32, u64); +impl_va_arg_get!(float: f32, f64); +impl_va_arg_get!(ptr: c_void, i8, u8, i16, u16, i32, u32, i64, u64, f32, f64, usize, isize); impl VaArgGet for crate::rc::Ptr { fn get(v: &VaArg) -> Self { match v { - VaArg::Ptr(any) => any.cast::().expect("VaList::arg: Ptr type mismatch"), - _ => panic!("VaList::arg: expected Ptr, got different variant"), + VaArg::Ptr(any) => any.cast::().expect("VaArgGet: Ptr type mismatch"), + _ => panic!("VaArgGet: expected Ptr"), } } } From 7a8049aff104f7513996f890c849c7d39008f049 Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Mon, 13 Apr 2026 13:24:12 +0100 Subject: [PATCH 19/23] Delete comments and move function in converter_lib --- cpp2rust/converter/converter.cpp | 5 ++--- cpp2rust/converter/converter_lib.cpp | 12 ++++++++++++ cpp2rust/converter/converter_lib.h | 2 ++ cpp2rust/converter/models/converter_refcount.cpp | 16 ++-------------- tests/unit/va_arg_chain.c | 2 -- tests/unit/va_arg_conditional.c | 1 - tests/unit/va_arg_copy.c | 2 -- tests/unit/va_arg_mixed_int_ptr.c | 2 -- tests/unit/va_arg_mixed_types.c | 1 - tests/unit/va_arg_promotion.c | 3 +-- tests/unit/va_arg_struct_ctx.c | 1 - tests/unit/va_arg_two_passes.c | 1 - 12 files changed, 19 insertions(+), 29 deletions(-) diff --git a/cpp2rust/converter/converter.cpp b/cpp2rust/converter/converter.cpp index 7a7930f0..0f6f48d3 100644 --- a/cpp2rust/converter/converter.cpp +++ b/cpp2rust/converter/converter.cpp @@ -128,7 +128,6 @@ bool Converter::VisitBuiltinType(clang::BuiltinType *type) { 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(); @@ -360,9 +359,9 @@ bool Converter::VisitFunctionTemplateDecl(clang::FunctionTemplateDecl *decl) { void Converter::ConvertVaListVarDecl(clang::VarDecl *decl) { if (clang::isa(decl)) { - // va_list parameter (decayed to __va_list_tag *): emit "mut ap: VaList" + // va_list parameter (decayed to __va_list_tag *) } else { - // va_list local variable: emit "let mut ap: VaList" + // va_list local variable StrCat(keyword::kLet); } StrCat(keyword_mut_, GetNamedDeclAsString(decl), token::kColon, "VaList"); diff --git a/cpp2rust/converter/converter_lib.cpp b/cpp2rust/converter/converter_lib.cpp index 16e7d32f..ba3899a4 100644 --- a/cpp2rust/converter/converter_lib.cpp +++ b/cpp2rust/converter/converter_lib.cpp @@ -600,4 +600,16 @@ bool IsBuiltinVaCopy(const clang::CallExpr *expr) { return false; } +bool ContainsVAArgExpr(const clang::Stmt *stmt) { + if (clang::isa(stmt)) { + return true; + } + for (auto *child : stmt->children()) { + if (ContainsVAArgExpr(child)) { + return true; + } + } + return false; +} + } // namespace cpp2rust diff --git a/cpp2rust/converter/converter_lib.h b/cpp2rust/converter/converter_lib.h index 50fa6a9f..c0d2a65d 100644 --- a/cpp2rust/converter/converter_lib.h +++ b/cpp2rust/converter/converter_lib.h @@ -150,4 +150,6 @@ bool IsBuiltinVaEnd(const clang::CallExpr *expr); bool IsBuiltinVaCopy(const clang::CallExpr *expr); +bool ContainsVAArgExpr(const clang::Stmt *stmt); + } // namespace cpp2rust diff --git a/cpp2rust/converter/models/converter_refcount.cpp b/cpp2rust/converter/models/converter_refcount.cpp index cff67ba8..6c589876 100644 --- a/cpp2rust/converter/models/converter_refcount.cpp +++ b/cpp2rust/converter/models/converter_refcount.cpp @@ -479,9 +479,9 @@ void ConverterRefCount::EmitFunctionPreamble(clang::FunctionDecl *decl) { void ConverterRefCount::ConvertVaListVarDecl(clang::VarDecl *decl) { if (clang::isa(decl)) { - // va_list parameter (decayed to __va_list_tag *): emit "mut ap: VaList" + // va_list parameter (decayed to __va_list_tag *) } else { - // va_list local variable: emit "let mut ap: VaList" + // va_list local variable StrCat(keyword::kLet); } @@ -1495,18 +1495,6 @@ void ConverterRefCount::ConvertVarInit(clang::QualType qual_type, in_function_formals_ = false; } -static bool ContainsVAArgExpr(const clang::Stmt *stmt) { - if (clang::isa(stmt)) { - return true; - } - for (auto *child : stmt->children()) { - if (ContainsVAArgExpr(child)) { - return true; - } - } - return false; -} - static std::unordered_set GetAllVars(const clang::Stmt *stmt) { std::unordered_set vars; diff --git a/tests/unit/va_arg_chain.c b/tests/unit/va_arg_chain.c index 87b315c2..5b627395 100644 --- a/tests/unit/va_arg_chain.c +++ b/tests/unit/va_arg_chain.c @@ -1,4 +1,3 @@ -// Pattern from curl's trc_write chain: variadic -> va_list -> va_list forwarding #include #include @@ -22,7 +21,6 @@ int top_level(int n, ...) { } int main() { - // Extract the 3rd arg (0-indexed): 100, 200, 300, 400 -> 300 assert(top_level(2, 100, 200, 300, 400) == 300); assert(top_level(0, 42, 99) == 42); assert(top_level(3, 1, 2, 3, 4) == 4); diff --git a/tests/unit/va_arg_conditional.c b/tests/unit/va_arg_conditional.c index 06c1faec..85e348fd 100644 --- a/tests/unit/va_arg_conditional.c +++ b/tests/unit/va_arg_conditional.c @@ -1,4 +1,3 @@ -// Pattern from Curl_failf: va_start only under a condition #include #include diff --git a/tests/unit/va_arg_copy.c b/tests/unit/va_arg_copy.c index 3e4e34b8..4ddd1544 100644 --- a/tests/unit/va_arg_copy.c +++ b/tests/unit/va_arg_copy.c @@ -1,4 +1,3 @@ -// va_copy: save position, consume from copy, original still usable #include #include @@ -28,7 +27,6 @@ int sum_with_copy(int count, ...) { } int main() { - // sum1 = 10+20+30 = 60, sum2 = 10+20+30 = 60, total = 120 assert(sum_with_copy(3, 10, 20, 30) == 120); return 0; } diff --git a/tests/unit/va_arg_mixed_int_ptr.c b/tests/unit/va_arg_mixed_int_ptr.c index efd9833f..6dcc18c9 100644 --- a/tests/unit/va_arg_mixed_int_ptr.c +++ b/tests/unit/va_arg_mixed_int_ptr.c @@ -1,4 +1,3 @@ -// va_arg with mixed int and pointer types in the same call #include #include @@ -23,7 +22,6 @@ int mixed_args(int count, ...) { int main() { int x = 100; - // tag=0 val=10, tag=1 ptr=&x(100), tag=0 val=20 assert(mixed_args(3, 0, 10, 1, &x, 0, 20) == 130); int y = 50; diff --git a/tests/unit/va_arg_mixed_types.c b/tests/unit/va_arg_mixed_types.c index 2c49c06b..4acf9ddd 100644 --- a/tests/unit/va_arg_mixed_types.c +++ b/tests/unit/va_arg_mixed_types.c @@ -21,7 +21,6 @@ int sum_mixed(int count, ...) { } int main() { - // tag=0 int=10, tag=1 double=20.5, tag=2 long=30 assert(sum_mixed(3, 0, 10, 1, 20.5, 2, 30L) == 60); assert(sum_mixed(1, 0, 42) == 42); assert(sum_mixed(2, 1, 3.7, 2, 100L) == 103); diff --git a/tests/unit/va_arg_promotion.c b/tests/unit/va_arg_promotion.c index e938625c..7b74bbef 100644 --- a/tests/unit/va_arg_promotion.c +++ b/tests/unit/va_arg_promotion.c @@ -1,4 +1,3 @@ -// C promotion rules: char/short promote to int, float promotes to double #include #include @@ -23,7 +22,7 @@ int test_promotions(int count, ...) { } int main() { - char x = 'A'; // 65 + char x = 'A'; short y = 10; float z = 3.0f; // 65 + 10 + 3 = 78 diff --git a/tests/unit/va_arg_struct_ctx.c b/tests/unit/va_arg_struct_ctx.c index 560cd90c..9d59e36d 100644 --- a/tests/unit/va_arg_struct_ctx.c +++ b/tests/unit/va_arg_struct_ctx.c @@ -1,4 +1,3 @@ -// Pattern from Curl_failf: variadic function receives a struct pointer as first arg #include #include diff --git a/tests/unit/va_arg_two_passes.c b/tests/unit/va_arg_two_passes.c index 9f5cc4d2..b4f57ed2 100644 --- a/tests/unit/va_arg_two_passes.c +++ b/tests/unit/va_arg_two_passes.c @@ -1,4 +1,3 @@ -// Pattern from wget's concat_strings: va_start twice to iterate args in two passes #include #include From f4ba9d06b4ade602812762f47d460bf193bdda9c Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Mon, 13 Apr 2026 14:15:06 +0100 Subject: [PATCH 20/23] Add VaList::empty --- libcc2rs/src/va_args.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/libcc2rs/src/va_args.rs b/libcc2rs/src/va_args.rs index 41fc276a..8c21bc6b 100644 --- a/libcc2rs/src/va_args.rs +++ b/libcc2rs/src/va_args.rs @@ -51,6 +51,10 @@ pub struct VaList<'a> { } impl<'a> VaList<'a> { + pub fn empty() -> Self { + VaList { args: &[], pos: 0 } + } + pub fn new(args: &'a [VaArg]) -> Self { VaList { args, pos: 0 } } From 3495165fed58cb3e3a00a70acdb876b4867082ab Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Mon, 13 Apr 2026 14:18:17 +0100 Subject: [PATCH 21/23] Revert "Add VaList::empty" This reverts commit f4ba9d06b4ade602812762f47d460bf193bdda9c. --- libcc2rs/src/va_args.rs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/libcc2rs/src/va_args.rs b/libcc2rs/src/va_args.rs index 8c21bc6b..41fc276a 100644 --- a/libcc2rs/src/va_args.rs +++ b/libcc2rs/src/va_args.rs @@ -51,10 +51,6 @@ pub struct VaList<'a> { } impl<'a> VaList<'a> { - pub fn empty() -> Self { - VaList { args: &[], pos: 0 } - } - pub fn new(args: &'a [VaArg]) -> Self { VaList { args, pos: 0 } } From 4e621b67b7eb400b91c263c198001d4b992e556b Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Mon, 13 Apr 2026 14:18:58 +0100 Subject: [PATCH 22/23] Use default instead of empty --- rules/stdarg/ir_unsafe.json | 2 +- rules/stdarg/tgt_unsafe.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/rules/stdarg/ir_unsafe.json b/rules/stdarg/ir_unsafe.json index e45e600f..afbd20c2 100644 --- a/rules/stdarg/ir_unsafe.json +++ b/rules/stdarg/ir_unsafe.json @@ -1,6 +1,6 @@ { "t1": { - "init": "VaList::empty()", + "init": "VaList::default()", "type": "VaList" } } diff --git a/rules/stdarg/tgt_unsafe.rs b/rules/stdarg/tgt_unsafe.rs index ff48596a..94f88407 100644 --- a/rules/stdarg/tgt_unsafe.rs +++ b/rules/stdarg/tgt_unsafe.rs @@ -1,5 +1,5 @@ use libcc2rs::VaList; fn types() { - let t1: VaList = VaList::empty(); + let t1: VaList = VaList::default(); } From ef2fee1cce4bfc0eae08aef06aef465c9e3b8edd Mon Sep 17 00:00:00 2001 From: Lucian Popescu Date: Mon, 13 Apr 2026 14:52:26 +0100 Subject: [PATCH 23/23] Triger CI