Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
cf96ccd
Refactor ConvertGenericCallExpr
lucic71 May 23, 2026
1c0fb3a
EmitArgBindings -> EmitHoistedArgs
lucic71 May 23, 2026
e98df0c
Drop default initialization fom CallInfo
lucic71 May 24, 2026
01765d8
Add prefix on param_name from construction
lucic71 May 24, 2026
dba4b3b
Pass CallInfo by rvalue reference
lucic71 May 24, 2026
5c1425d
Reorder CallArg fields by size
lucic71 May 24, 2026
06097f8
Reorder initialization
lucic71 May 24, 2026
27fd2af
Reorder CallInfo fields
lucic71 May 27, 2026
768b308
Use template syntax to declare rules for variadic functions
lucic71 May 25, 2026
60fc170
Add src rules for variadic functions
lucic71 May 27, 2026
08d2cc1
Add Rust rule counterpart
lucic71 May 27, 2026
c85429d
Add C rules for builtin mul overflow
lucic71 May 27, 2026
d2ac848
Read the is_variadic attribute on the other side
lucic71 May 27, 2026
8b98f29
Save CallExpr instead of callee in CallInfo
lucic71 May 27, 2026
91b69ea
Set is_libc_passthrough
lucic71 May 27, 2026
ba997cd
Use correct types for arguments
lucic71 May 27, 2026
108cdb7
Use declared type for each argument in passthrough mode
lucic71 May 27, 2026
f000eb5
Fix code
lucic71 May 27, 2026
70f5880
Missing part of c85429df5caa6474c2a9f1f9f2f5afc632e38ef0
lucic71 May 27, 2026
3bc0b4c
Fix is_libc_passthrough condition
lucic71 Jun 2, 2026
b4219d8
Merge branch 'master' into variadic-call
lucic71 Jun 4, 2026
c68dcee
Re-add paren for variadic arg
lucic71 Jun 4, 2026
680424e
Fix merge artifacts
lucic71 Jun 4, 2026
0c3a442
Add Mapper::IsLibcPassthrough
lucic71 Jun 4, 2026
582d5d3
Replace is_variadic with is_extern
lucic71 Jun 4, 2026
eb19e52
Add var args tests
lucic71 Jun 4, 2026
1736ec5
Delete non-portable test code parts
lucic71 Jun 4, 2026
042eac9
Merge branch 'master' into variadic-call
lucic71 Jun 4, 2026
addc4cd
Clang-format
lucic71 Jun 4, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 34 additions & 12 deletions cpp2rust/converter/converter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1524,6 +1524,11 @@ bool Converter::VisitCallExpr(clang::CallExpr *expr) {
}

if (Mapper::Contains(expr->getCallee())) {
if (Mapper::IsLibcPassthrough(GetCalleeOrExpr(expr))) {
ConvertGenericCallExpr(expr);
return false;
}

auto **args = expr->getArgs();
auto num_args = expr->getNumArgs();
auto ctx = CollectPrvalueToLRefArgs(expr);
Expand Down Expand Up @@ -1598,20 +1603,20 @@ Converter::CallInfo Converter::CollectCallInfo(clang::CallExpr *expr) {
using Kind = CallArg::Kind;

CallInfo info;
info.callee = expr->getCallee();
info.expr = expr;
auto callee = GetCallee(expr);
unsigned arg_begin = 0;
if (auto op_call = llvm::dyn_cast<clang::CXXOperatorCallExpr>(expr)) {
if (op_call->getOperator() == clang::OO_Call) {
info.callee = op_call->getArg(0);
arg_begin = 1;
}
}

const auto *function =
expr->getCalleeDecl() ? expr->getCalleeDecl()->getAsFunction() : nullptr;
auto decl = expr->getCalleeDecl();
const auto *function = decl ? decl->getAsFunction() : nullptr;
const clang::FunctionProtoType *proto = nullptr;
if (!function) {
auto callee_ty = info.callee->getType().getDesugaredType(ctx_);
auto callee_ty = callee->getType().getDesugaredType(ctx_);
if (auto ptr_ty = callee_ty->getAs<clang::PointerType>()) {
proto = ptr_ty->getPointeeType()->getAs<clang::FunctionProtoType>();
}
Expand All @@ -1624,6 +1629,7 @@ Converter::CallInfo Converter::CollectCallInfo(clang::CallExpr *expr) {
function ? function->getNumParams() : proto->getNumParams();
info.is_variadic = function ? function->isVariadic() : proto->isVariadic();
info.is_fn_ptr_call = !function;
info.is_libc_passthrough = Mapper::IsLibcPassthrough(GetCalleeOrExpr(expr));

for (unsigned i = 0; i < num_named_params && i < num_args; ++i) {
auto *arg = expr->getArg(i + arg_begin);
Expand All @@ -1635,7 +1641,8 @@ Converter::CallInfo Converter::CollectCallInfo(clang::CallExpr *expr) {
: proto->getParamType(i),
.expr = arg,
.has_default = function && function->getParamDecl(i)->hasDefaultArg(),
.kind = IsLiteral(arg) ? Kind::Inline : Kind::Hoisted,
.kind = (IsLiteral(arg) || info.is_libc_passthrough) ? Kind::Inline
: Kind::Hoisted,
};
bool is_materialize = clang::isa<clang::MaterializeTemporaryExpr>(arg);
if (is_materialize && ca.param_type->isLValueReferenceType()) {
Expand Down Expand Up @@ -1691,7 +1698,9 @@ void Converter::EmitArgList(const CallInfo &info) {
using Kind = CallArg::Kind;
PushParen call_args(*this);

for (const auto &ca : info.args) {
for (unsigned i = 0; i < info.args.size(); i++) {
const auto &ca = info.args[i];

if (ca.has_default) {
StrCat("Some");
}
Expand All @@ -1707,6 +1716,10 @@ void Converter::EmitArgList(const CallInfo &info) {
break;
case Kind::Inline:
ConvertParamTy(ca.param_type, ca.expr);
if (info.is_libc_passthrough) {
StrCat(std::format(
"as {}", Mapper::GetParamType(GetCalleeOrExpr(info.expr), i)));
}
break;
}
}
Expand All @@ -1715,14 +1728,19 @@ void Converter::EmitArgList(const CallInfo &info) {
}

if (info.is_variadic) {
StrCat(token::kRef);
PushBracket push(*this);
if (!info.is_libc_passthrough) {
StrCat(token::kRef);
}
PushBracket push(*this, !info.is_libc_passthrough);
for (auto *arg : info.variadic_args) {
{
PushParen p(*this);
ConvertVariadicArg(arg);
}
StrCat(".into()", token::kComma);
if (!info.is_libc_passthrough) {
StrCat(".into()");
}
StrCat(token::kComma);
}
}
}
Expand All @@ -1731,10 +1749,14 @@ void Converter::EmitCall(CallInfo &&info) {
EmitHoistedArgs(info);

if (info.is_fn_ptr_call) {
EmitFnPtrCall(info.callee);
EmitFnPtrCall(GetCallee(info.expr));
} else if (info.is_libc_passthrough) {
auto *direct_callee = info.expr->getDirectCallee();
assert(direct_callee);
StrCat("libc::", direct_callee->getName());
} else {
PushExprKind push(*this, ExprKind::Callee);
Convert(info.callee);
Convert(GetCallee(info.expr));
}

EmitArgList(info);
Expand Down
3 changes: 2 additions & 1 deletion cpp2rust/converter/converter.h
Original file line number Diff line number Diff line change
Expand Up @@ -242,9 +242,10 @@ class Converter : public clang::RecursiveASTVisitor<Converter> {
struct CallInfo {
std::vector<CallArg> args;
std::vector<clang::Expr *> variadic_args;
clang::Expr *callee;
clang::CallExpr *expr;
bool is_variadic;
bool is_fn_ptr_call;
bool is_libc_passthrough;
};

CallInfo CollectCallInfo(clang::CallExpr *expr);
Expand Down
9 changes: 9 additions & 0 deletions cpp2rust/converter/converter_lib.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -649,6 +649,15 @@ BuildUnifiedArgs(clang::Expr *expr, clang::Expr **args, unsigned num_args) {
return all_args;
}

clang::Expr *GetCallee(clang::CallExpr *expr) {
if (auto op_call = clang::dyn_cast<clang::CXXOperatorCallExpr>(expr)) {
if (op_call->getOperator() == clang::OO_Call) {
return op_call->getArg(0);
}
}
return expr->getCallee();
}

clang::Expr *GetCalleeOrExpr(clang::Expr *expr) {
if (auto *call = clang::dyn_cast<clang::CallExpr>(expr)) {
return call->getCallee();
Expand Down
2 changes: 2 additions & 0 deletions cpp2rust/converter/converter_lib.h
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,8 @@ void ForEachTemplateArgument(

clang::Expr *GetCallObject(clang::CallExpr *expr);

clang::Expr *GetCallee(clang::CallExpr *expr);

clang::Expr *GetCalleeOrExpr(clang::Expr *expr);

bool HasReceiver(clang::Expr *expr);
Expand Down
19 changes: 19 additions & 0 deletions cpp2rust/converter/mapper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -588,6 +588,19 @@ const TranslationRule::ExprRule *GetExprRule(const clang::Expr *expr) {
return search(expr);
}

bool IsLibcPassthrough(const clang::Expr *expr) {
const auto *tgt_ir = GetExprRule(expr);
if (tgt_ir == nullptr || !tgt_ir->body.empty() || !tgt_ir->is_extern) {
return false;
}
const auto *ref =
clang::dyn_cast<clang::DeclRefExpr>(expr->IgnoreParenImpCasts());
const auto *decl = ref != nullptr ? ref->getDecl() : nullptr;
return decl != nullptr &&
decl->getASTContext().getSourceManager().isInSystemHeader(
decl->getLocation());
}

std::string MapFunctionName(const clang::FunctionDecl *decl) {
assert(decl);
if (!IsUserDefinedDecl(decl) &&
Expand Down Expand Up @@ -804,6 +817,12 @@ std::string ToString(const clang::NamedDecl *decl) {
}
os << ToString(func_decl->getParamDecl(i)->getType());
}
if (func_decl->isVariadic()) {
if (func_decl->getNumParams()) {
os << ", ";
}
os << "...";
}
os << ')';

if (const auto *method_decl =
Expand Down
1 change: 1 addition & 0 deletions cpp2rust/converter/mapper.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ bool Contains(const clang::Expr *expr);
std::string Map(clang::QualType qual_type);
std::string MapInitializer(clang::QualType qual_type);
const TranslationRule::ExprRule *GetExprRule(const clang::Expr *expr);
bool IsLibcPassthrough(const clang::Expr *expr);
std::string MapFunctionName(const clang::FunctionDecl *decl);
std::string InstantiateTemplate(const clang::Expr *expr, unsigned n);
bool ReturnsPointer(const clang::Expr *expr);
Expand Down
10 changes: 8 additions & 2 deletions cpp2rust/converter/translation_rule.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -98,11 +98,17 @@ ExprRule ParseExprRuleJSON(const llvm::json::Object &obj) {
}
}

if (auto *rt = obj.getObject("return_type"))
if (auto *rt = obj.getObject("return_type")) {
ir.return_type = ParseTypeInfoJSON(*rt);
}

if (auto ms = obj.getBoolean("multi_statement"))
if (auto ms = obj.getBoolean("multi_statement")) {
ir.multi_statement = *ms;
}

if (auto v = obj.getBoolean("is_extern")) {
ir.is_extern = *v;
}

if (auto *generics = obj.getObject("generics")) {
for (auto &[key, val] : *generics) {
Expand Down
1 change: 1 addition & 0 deletions cpp2rust/converter/translation_rule.h
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ struct ExprRule {
std::vector<std::vector<std::string>> generics; // "T1" -> ["Ord", "Clone"]
std::vector<BodyFragment> body;
bool multi_statement = false;
bool is_extern = false;

void dump() const;
void validate(const std::string &name) const;
Expand Down
15 changes: 13 additions & 2 deletions cpp2rust/cpp_rule_preprocessor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -349,7 +349,10 @@ class Callback : public clang::ast_matchers::MatchFinder::MatchCallback {
createTemplateArguments(clang::TemplateDecl *decl,
llvm::SmallVectorImpl<clang::TemplateArgument> &out) {
for (clang::NamedDecl *param : *decl->getTemplateParameters()) {
if (llvm::isa<clang::TemplateTypeParmDecl>(param)) {
if (param->isTemplateParameterPack()) {
out.emplace_back(
clang::TemplateArgument::CreatePackCopy(sema_->Context, {}));
} else if (llvm::isa<clang::TemplateTypeParmDecl>(param)) {
clang::RecordDecl *rdecl = createRecordDecl(param->getName());
clang::QualType type =
sema_->Context.getTagType(clang::ElaboratedTypeKeyword::None,
Expand Down Expand Up @@ -409,7 +412,14 @@ class Callback : public clang::ast_matchers::MatchFinder::MatchCallback {
clang::OverloadCandidateSet &candidates) {
clang::LookupResult decls(*sema_, name, loc_,
clang::Sema::LookupOrdinaryName);
sema_->LookupQualifiedName(decls, sema_->getStdNamespace());
if (clang::NamespaceDecl *std_ns = sema_->getStdNamespace()) {
sema_->LookupQualifiedName(decls, std_ns);
}
if (decls.empty()) {
decls.clear();
sema_->LookupQualifiedName(decls,
sema_->Context.getTranslationUnitDecl());
}
for (auto *ndecl : decls) {
if (auto *candidate = createCandidate(ndecl, callArgs, explicitTArgs)) {
sema_->AddOverloadCandidate(
Expand Down Expand Up @@ -573,6 +583,7 @@ class Callback : public clang::ast_matchers::MatchFinder::MatchCallback {
cxxConstructorNameLookup(rule->getReturnType(), callArgs, candidates);
break;
case LookupKind::ADL:
regularNameLookup(callArgs, &explicitTArgs, name, candidates);
adlLookup(callArgs, name, candidates);
break;
}
Expand Down
7 changes: 6 additions & 1 deletion rule-preprocessor/src/ir.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,8 @@ pub struct FnIr {
pub params: Option<BTreeMap<String, TypeInfo>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub return_type: Option<TypeInfo>,
#[serde(skip_serializing_if = "Option::is_none")]
pub is_extern: Option<bool>,
}

impl FnIr {
Expand Down Expand Up @@ -104,7 +106,10 @@ impl FnIr {
1,
&format!("Rule {name} generics"),
);
assert!(!self.body.is_empty(), "Rule {name}: body must not be empty");
assert!(
self.is_extern == Some(true) || !self.body.is_empty(),
"Rule {name}: body must not be empty"
);
}
}

Expand Down
8 changes: 8 additions & 0 deletions rule-preprocessor/src/syntactic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -416,6 +416,13 @@ impl<'a> FnIrBuilder<'a> {
.unwrap_or(Access::Read)
}

fn is_extern(&self) -> bool {
self.fn_item
.syntax()
.ancestors()
.any(|a| a.kind() == SyntaxKind::EXTERN_BLOCK)
}

fn returns_mut_ref(&self) -> bool {
self.fn_item
.ret_type()
Expand Down Expand Up @@ -517,6 +524,7 @@ impl<'a> FnIrBuilder<'a> {
},
multi_statement,
body,
is_extern: self.is_extern().then_some(true),
};
ir.validate(&format!("{}:{}", path.display(), fn_name));
ir
Expand Down
2 changes: 2 additions & 0 deletions rules/builtin/src.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
int f12(long a, long b, long *r) { return __builtin_mul_overflow(a, b, r); }
int f13(long long a, long long b, long long *r) { return __builtin_mul_overflow(a, b, r); }
10 changes: 10 additions & 0 deletions rules/builtin/tgt_refcount.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,13 @@ fn f10(a0: i64, a1: i64, a2: Ptr<i64>) -> bool {
a2.write(val);
ovf
}
fn f12(a0: i64, a1: i64, a2: Ptr<i64>) -> bool {
let (val, ovf) = a0.overflowing_mul(a1);
a2.write(val);
ovf
}
fn f13(a0: i64, a1: i64, a2: Ptr<i64>) -> bool {
let (val, ovf) = a0.overflowing_mul(a1);
a2.write(val);
ovf
}
11 changes: 11 additions & 0 deletions rules/builtin/tgt_unsafe.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,3 +39,14 @@ unsafe fn f10(a0: i64, a1: i64, a2: *mut i64) -> bool {
unsafe fn f11() {
std::hint::spin_loop();
}

unsafe fn f12(a0: i64, a1: i64, a2: *mut i64) -> bool {
let (val, ovf) = a0.overflowing_mul(a1);
*a2 = val;
ovf
}
unsafe fn f13(a0: i64, a1: i64, a2: *mut i64) -> bool {
let (val, ovf) = a0.overflowing_mul(a1);
*a2 = val;
ovf
}
14 changes: 14 additions & 0 deletions rules/fcntl/src.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// Copyright (c) 2022-present INESC-ID.
// Distributed under the MIT license that can be found in the LICENSE file.

#include <fcntl.h>

template <typename... Args>
int f1(int a0, int a1, Args... args) {
return fcntl(a0, a1, args...);
}

template <typename... Args>
int f2(const char *a0, int a1, Args... args) {
return open(a0, a1, args...);
}
7 changes: 7 additions & 0 deletions rules/fcntl/tgt_unsafe.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
// Copyright (c) 2022-present INESC-ID.
// Distributed under the MIT license that can be found in the LICENSE file.

unsafe extern "C" {
fn f1(a0: i32, a1: i32, ...) -> i32;
fn f2(a0: *const i8, a1: i32, ...) -> i32;
}
9 changes: 9 additions & 0 deletions rules/ioctl/src.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
// Copyright (c) 2022-present INESC-ID.
// Distributed under the MIT license that can be found in the LICENSE file.

#include <sys/ioctl.h>

template <typename... Args>
int f1(int a0, unsigned long a1, Args... args) {
return ioctl(a0, a1, args...);
}
6 changes: 6 additions & 0 deletions rules/ioctl/tgt_unsafe.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// Copyright (c) 2022-present INESC-ID.
// Distributed under the MIT license that can be found in the LICENSE file.

unsafe extern "C" {
fn f1(a0: i32, a1: u64, ...) -> i32;
}
4 changes: 4 additions & 0 deletions rules/src/modules.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ pub mod deque_tgt_unsafe;
pub mod dirent_tgt_unsafe;
#[path = r#"../errno/tgt_unsafe.rs"#]
pub mod errno_tgt_unsafe;
#[path = r#"../fcntl/tgt_unsafe.rs"#]
pub mod fcntl_tgt_unsafe;
#[path = r#"../fnmatch/tgt_unsafe.rs"#]
pub mod fnmatch_tgt_unsafe;
#[path = r#"../fstream/tgt_refcount.rs"#]
Expand All @@ -50,6 +52,8 @@ pub mod fstream_tgt_unsafe;
pub mod ifaddrs_tgt_unsafe;
#[path = r#"../initializer_list/tgt_unsafe.rs"#]
pub mod initializer_list_tgt_unsafe;
#[path = r#"../ioctl/tgt_unsafe.rs"#]
pub mod ioctl_tgt_unsafe;
#[path = r#"../iomanip/tgt_unsafe.rs"#]
pub mod iomanip_tgt_unsafe;
#[path = r#"../iostream/tgt_refcount.rs"#]
Expand Down
Loading
Loading