diff --git a/cpp2rust/compat/assert.h b/cpp2rust/compat/assert.h index a18bec79..a06ef790 100644 --- a/cpp2rust/compat/assert.h +++ b/cpp2rust/compat/assert.h @@ -5,6 +5,10 @@ #undef assert +#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..0f6f48d3 100644 --- a/cpp2rust/converter/converter.cpp +++ b/cpp2rust/converter/converter.cpp @@ -243,11 +243,16 @@ 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_); 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); @@ -352,9 +357,25 @@ 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 *) + } else { + // va_list local variable + 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()) { + ConvertVaListVarDecl(decl); + return true; + } + if (decl->isFileVarDecl()) { name = std::regex_replace(Mapper::ToString(decl), std::regex("::"), "_"); if ((decl->isExternallyDeclarable() && !decl->hasInit()) || @@ -1259,7 +1280,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; @@ -1356,11 +1398,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 +1434,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 +1459,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); @@ -1561,6 +1620,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_); @@ -2209,6 +2273,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; @@ -2807,6 +2883,9 @@ void Converter::ConvertFunctionParameters(clang::FunctionDecl *decl) { ConvertVarDeclSkipInit(parameter); StrCat(token::kComma); } + if (decl->isVariadic()) { + StrCat("args: &[VaArg]", token::kComma); + } in_function_formals_ = false; } diff --git a/cpp2rust/converter/converter.h b/cpp2rust/converter/converter.h index b127a423..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,6 +201,8 @@ class Converter : public clang::RecursiveASTVisitor { virtual void ConvertPrintf(clang::CallExpr *expr); + void ConvertVAArgCall(clang::CallExpr *expr); + virtual bool VisitCallExpr(clang::CallExpr *expr); virtual bool VisitIntegerLiteral(clang::IntegerLiteral *expr); @@ -273,6 +277,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); diff --git a/cpp2rust/converter/converter_lib.cpp b/cpp2rust/converter/converter_lib.cpp index 08a6bd55..ba3899a4 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); @@ -557,4 +558,58 @@ 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; +} + +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 7bc50d6f..c0d2a65d 100644 --- a/cpp2rust/converter/converter_lib.h +++ b/cpp2rust/converter/converter_lib.h @@ -142,4 +142,14 @@ 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); + +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 4a34b951..6c589876 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 *) + } else { + // va_list local variable + 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::<"); + StrCat(GetUnsafeTypeAsString(expr->getType())); + StrCat(">()"); + return false; +} + bool ConverterRefCount::VisitCXXDefaultArgExpr(clang::CXXDefaultArgExpr *expr) { return Converter::VisitCXXDefaultArgExpr(expr); } @@ -1569,9 +1606,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 {} {} }}", 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; diff --git a/libcc2rs/src/lib.rs b/libcc2rs/src/lib.rs index 4e20f35f..42eb4117 100644 --- a/libcc2rs/src/lib.rs +++ b/libcc2rs/src/lib.rs @@ -24,3 +24,6 @@ pub use iterators::*; mod compat; pub use compat::*; + +mod va_args; +pub use va_args::*; diff --git a/libcc2rs/src/va_args.rs b/libcc2rs/src/va_args.rs new file mode 100644 index 00000000..41fc276a --- /dev/null +++ b/libcc2rs/src/va_args.rs @@ -0,0 +1,120 @@ +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_va_arg_from { + (direct: $($ty:ty => $variant:ident),*) => {$( + impl From<$ty> for VaArg { + fn from(v: $ty) -> Self { VaArg::$variant(v) } + } + )*}; + (promote: $($ty:ty => $variant:ident as $cast:ty),*) => {$( + impl From<$ty> for VaArg { + 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_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 { + VaArg::Ptr(v.to_any()) + } +} + +#[derive(Clone, Copy, Default)] +pub struct VaList<'a> { + args: &'a [VaArg], + pos: usize, +} + +impl<'a> VaList<'a> { + 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) + } +} + +pub trait VaArgGet { + fn get(v: &VaArg) -> Self; +} + +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"), + } + } + } + )*}; + (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_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("VaArgGet: Ptr type mismatch"), + _ => panic!("VaArgGet: expected Ptr"), + } + } +} 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..afbd20c2 --- /dev/null +++ b/rules/stdarg/ir_unsafe.json @@ -0,0 +1,6 @@ +{ + "t1": { + "init": "VaList::default()", + "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..94f88407 --- /dev/null +++ b/rules/stdarg/tgt_unsafe.rs @@ -0,0 +1,5 @@ +use libcc2rs::VaList; + +fn types() { + let t1: VaList = VaList::default(); +} 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 new file mode 100644 index 00000000..c39321b1 --- /dev/null +++ b/tests/unit/out/refcount/va_arg_concat.rs @@ -0,0 +1,49 @@ +extern crate libcc2rs; +use libcc2rs::*; +use std::cell::RefCell; +use std::collections::BTreeMap; +use std::io::prelude::*; +use std::io::Seek; +use std::io::{Read, Write}; +use std::os::fd::AsFd; +use std::rc::{Rc, Weak}; +pub fn 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 { + 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 new file mode 100644 index 00000000..25929e68 --- /dev/null +++ b/tests/unit/out/refcount/va_arg_forward.rs @@ -0,0 +1,57 @@ +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 { + 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 new file mode 100644 index 00000000..c54047df --- /dev/null +++ b/tests/unit/out/refcount/va_arg_mixed_types.rs @@ -0,0 +1,63 @@ +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]) -> 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 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()); +} +pub fn main() { + std::process::exit(main_0()); +} +fn main_0() -> i32 { + 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 new file mode 100644 index 00000000..cddda762 --- /dev/null +++ b/tests/unit/out/unsafe/va_arg_concat.rs @@ -0,0 +1,50 @@ +extern crate libc; +use libc::*; +extern crate libcc2rs; +use libcc2rs::*; +use std::collections::BTreeMap; +use std::io::Seek; +use std::io::{Read, Write}; +use std::os::fd::{AsFd, FromRawFd, IntoRawFd}; +use std::rc::Rc; +pub unsafe fn 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 { + 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 new file mode 100644 index 00000000..475fef73 --- /dev/null +++ b/tests/unit/out/unsafe/va_arg_forward.rs @@ -0,0 +1,54 @@ +extern crate libc; +use libc::*; +extern crate libcc2rs; +use libcc2rs::*; +use std::collections::BTreeMap; +use std::io::Seek; +use std::io::{Read, Write}; +use std::os::fd::{AsFd, FromRawFd, IntoRawFd}; +use std::rc::Rc; +pub unsafe fn 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 { + 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 new file mode 100644 index 00000000..0a8411e5 --- /dev/null +++ b/tests/unit/out/unsafe/va_arg_mixed_types.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 sum_mixed_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 if ((tag) == (1)) { + total += (ap.arg::() as i32); + } else { + let mut val: i64 = ap.arg::(); + total += (val as i32); + } + i.postfix_inc(); + } + return total; +} +pub fn main() { + unsafe { + std::process::exit(main_0() as i32); + } +} +unsafe fn main_0() -> i32 { + 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..5b627395 --- /dev/null +++ b/tests/unit/va_arg_chain.c @@ -0,0 +1,28 @@ +#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() { + 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 new file mode 100644 index 00000000..19f77ae7 --- /dev/null +++ b/tests/unit/va_arg_concat.c @@ -0,0 +1,23 @@ +#include +#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() { + 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..85e348fd --- /dev/null +++ b/tests/unit/va_arg_conditional.c @@ -0,0 +1,19 @@ +#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..4ddd1544 --- /dev/null +++ b/tests/unit/va_arg_copy.c @@ -0,0 +1,32 @@ +#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() { + 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 new file mode 100644 index 00000000..33d7e287 --- /dev/null +++ b/tests/unit/va_arg_forward.c @@ -0,0 +1,25 @@ +#include +#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() { + 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..6dcc18c9 --- /dev/null +++ b/tests/unit/va_arg_mixed_int_ptr.c @@ -0,0 +1,31 @@ +#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; + 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 new file mode 100644 index 00000000..4acf9ddd --- /dev/null +++ b/tests/unit/va_arg_mixed_types.c @@ -0,0 +1,28 @@ +#include +#include + +int sum_mixed(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 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() { + 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..7b74bbef --- /dev/null +++ b/tests/unit/va_arg_promotion.c @@ -0,0 +1,31 @@ +#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'; + 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..9d59e36d --- /dev/null +++ b/tests/unit/va_arg_struct_ctx.c @@ -0,0 +1,31 @@ +#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..b4f57ed2 --- /dev/null +++ b/tests/unit/va_arg_two_passes.c @@ -0,0 +1,31 @@ +#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; +}