diff --git a/cpp2rust/converter/converter.cpp b/cpp2rust/converter/converter.cpp index 63734c9e..8d2102ed 100644 --- a/cpp2rust/converter/converter.cpp +++ b/cpp2rust/converter/converter.cpp @@ -1447,6 +1447,13 @@ bool Converter::VisitCallExpr(clang::CallExpr *expr) { } if (Mapper::Contains(expr->getCallee())) { + if (auto tgt_ir = Mapper::GetExprRule(GetCalleeOrExpr(expr))) { + if (tgt_ir->body.empty() && tgt_ir->is_variadic) { + ConvertGenericCallExpr(expr); + return false; + } + } + auto **args = expr->getArgs(); auto num_args = expr->getNumArgs(); auto ctx = CollectPrvalueToLRefArgs(expr); @@ -1517,125 +1524,173 @@ void Converter::ConvertFunctionToFunctionPointer( StrCat(std::format("Some({})", Mapper::MapFunctionName(fn_decl))); } -void Converter::ConvertGenericCallExpr(clang::CallExpr *expr) { - clang::Expr *callee = expr->getCallee(); - auto convert_param_ty = [&](clang::QualType param_type, clang::Expr *expr) { - if (param_type->isLValueReferenceType()) { - PushExprKind push(*this, ExprKind::AddrOf); - ConvertVarInit(param_type, expr); - } else { - ConvertVarInit(param_type, expr); - } - }; +Converter::CallInfo Converter::CollectCallInfo(clang::CallExpr *expr) { + using Kind = CallArg::Kind; - unsigned arg_begin = 0; // skip count for operator()'s implicit object arg + CallInfo info; + info.expr = expr; + auto callee = GetCallee(expr); + unsigned arg_begin = 0; if (auto op_call = llvm::dyn_cast(expr)) { if (op_call->getOperator() == clang::OO_Call) { - callee = op_call->getArg(0); arg_begin = 1; } } - PushParen outer(*this); - StrCat(keyword_unsafe_); - PushBrace unsafe_brace(*this); - 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 = callee->getType().getDesugaredType(ctx_); if (auto ptr_ty = callee_ty->getAs()) { proto = ptr_ty->getPointeeType()->getAs(); } } - assert((function || proto) && "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); + unsigned num_args = expr->getNumArgs() - arg_begin; + unsigned num_named_params = + function ? function->getNumParams() : proto->getNumParams(); + info.is_variadic = function ? function->isVariadic() : proto->isVariadic(); + info.is_fn_ptr_call = !function; + info.is_libc_passthrough = + decl && ctx_.getSourceManager().isInSystemHeader(decl->getLocation()); 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() - : ("arg" + std::to_string(i)); - clang::QualType param_type = function ? function->getParamDecl(i)->getType() - : proto->getParamType(i); + CallArg ca{ + .param_name = function + ? ("_" + function->getParamDecl(i)->getNameAsString()) + : ("_arg" + std::to_string(i)), + .param_type = function ? function->getParamDecl(i)->getType() + : proto->getParamType(i), + .expr = arg, + .has_default = function && function->getParamDecl(i)->hasDefaultArg(), + .kind = info.is_libc_passthrough ? Kind::Inline : Kind::Hoisted, + }; + bool is_materialize = clang::isa(arg); + if (is_materialize && ca.param_type->isLValueReferenceType()) { + ca.kind = Kind::Materialized; + } else if (is_materialize) { + ca.kind = Kind::Inline; + } + info.args.push_back(std::move(ca)); + } - bool is_materialize_to_ref = - clang::isa(arg) && - param_type->isLValueReferenceType(); + if (info.is_variadic) { + for (unsigned i = num_named_params; i < num_args; ++i) { + info.variadic_args.push_back(expr->getArg(i + arg_begin)); + } + } + + return info; +} - if (is_materialize_to_ref) { +void Converter::ConvertParamTy(clang::QualType param_type, clang::Expr *expr) { + if (param_type->isLValueReferenceType()) { + PushExprKind push(*this, ExprKind::AddrOf); + ConvertVarInit(param_type, expr); + } else { + ConvertVarInit(param_type, expr); + } +} + +void Converter::EmitHoistedArgs(CallInfo &info) { + using Kind = CallArg::Kind; + for (auto &ca : info.args) { + switch (ca.kind) { + case Kind::Hoisted: + StrCat( + std::format("let {}: {} =", ca.param_name, ToString(ca.param_type))); + ConvertParamTy(ca.param_type, ca.expr); + StrCat(";"); + break; + case Kind::Materialized: { auto [binding, ref] = - MaterializeTemp(std::format("_{}", param_name), param_type, arg); + MaterializeTemp(ca.param_name, ca.param_type, ca.expr); StrCat(binding); - temp_refs[i] = std::move(ref); - } else if (!clang::isa(arg)) { - StrCat("let", std::format("_{}: {}", param_name, ToString(param_type)), - "="); - convert_param_ty(param_type, arg); - StrCat(";"); + ca.ref_temp_name = std::move(ref); + break; + } + case Kind::Inline: + break; } } +} - if (proto && !function) { - EmitFnPtrCall(callee); - } else { - PushExprKind push(*this, ExprKind::Callee); - Convert(callee); - } - { - PushParen call_args(*this); - 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() - : ("arg" + std::to_string(i)); - clang::QualType param_type = function - ? function->getParamDecl(i)->getType() - : proto->getParamType(i); - bool is_parm_with_default_value = - function && function->getParamDecl(i)->hasDefaultArg(); - - if (is_parm_with_default_value) { - StrCat("Some("); - } - if (!temp_refs[i].empty()) { - StrCat(temp_refs[i]); - } else if (clang::isa(arg)) { - convert_param_ty(param_type, arg); - } else { - StrCat(std::format("_{}", param_name)); - } - if (is_parm_with_default_value) { - StrCat(')'); +void Converter::EmitArgList(const CallInfo &info) { + using Kind = CallArg::Kind; + PushParen call_args(*this); + + for (unsigned i = 0; i < info.args.size(); i++) { + const auto &ca = info.args[i]; + + if (ca.has_default) { + StrCat("Some"); + } + + { + PushParen push(*this, ca.has_default); + switch (ca.kind) { + case Kind::Hoisted: + StrCat(ca.param_name); + break; + case Kind::Materialized: + StrCat(ca.ref_temp_name); + 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; } - 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); - ConvertVariadicArg(arg); - StrCat(".into()", token::kComma); + StrCat(token::kComma); + } + + if (info.is_variadic) { + if (!info.is_libc_passthrough) { + StrCat(token::kRef); + } + PushBracket push(*this, !info.is_libc_passthrough); + for (auto *arg : info.variadic_args) { + ConvertVariadicArg(arg); + if (!info.is_libc_passthrough) { + StrCat(".into()"); } - StrCat(']'); + StrCat(token::kComma); } } } +void Converter::EmitCall(CallInfo &&info) { + EmitHoistedArgs(info); + + if (info.is_fn_ptr_call) { + 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(GetCallee(info.expr)); + } + + EmitArgList(info); +} + +void Converter::ConvertGenericCallExpr(clang::CallExpr *expr) { + PushParen outer(*this); + StrCat(keyword_unsafe_); + PushBrace unsafe_brace(*this); + EmitCall(CollectCallInfo(expr)); +} + std::optional Converter::ConvertCallExpr(clang::CallExpr *expr) { auto *callee = expr->getCallee(); diff --git a/cpp2rust/converter/converter.h b/cpp2rust/converter/converter.h index 8d192aa7..c8877a4f 100644 --- a/cpp2rust/converter/converter.h +++ b/cpp2rust/converter/converter.h @@ -219,6 +219,40 @@ class Converter : public clang::RecursiveASTVisitor { std::optional ConvertCallExpr(clang::CallExpr *expr); + struct CallArg { + enum class Kind : int8_t { + Hoisted, + Inline, + Materialized, + }; + + std::string param_name; + std::string ref_temp_name; + clang::QualType param_type; + clang::Expr *expr; + bool has_default; + Kind kind; + }; + + struct CallInfo { + std::vector args; + std::vector variadic_args; + clang::CallExpr *expr; + bool is_variadic; + bool is_fn_ptr_call; + bool is_libc_passthrough; + }; + + CallInfo CollectCallInfo(clang::CallExpr *expr); + + void ConvertParamTy(clang::QualType param_type, clang::Expr *expr); + + void EmitHoistedArgs(CallInfo &info); + + void EmitArgList(const CallInfo &info); + + void EmitCall(CallInfo &&info); + void ConvertGenericCallExpr(clang::CallExpr *expr); virtual void EmitFnPtrCall(clang::Expr *callee); diff --git a/cpp2rust/converter/converter_lib.cpp b/cpp2rust/converter/converter_lib.cpp index 16576371..dcda0905 100644 --- a/cpp2rust/converter/converter_lib.cpp +++ b/cpp2rust/converter/converter_lib.cpp @@ -588,6 +588,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(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(expr)) { return call->getCallee(); diff --git a/cpp2rust/converter/converter_lib.h b/cpp2rust/converter/converter_lib.h index 6a6a70ec..d67c74b9 100644 --- a/cpp2rust/converter/converter_lib.h +++ b/cpp2rust/converter/converter_lib.h @@ -129,6 +129,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); diff --git a/cpp2rust/converter/mapper.cpp b/cpp2rust/converter/mapper.cpp index fc8779fa..506c1430 100644 --- a/cpp2rust/converter/mapper.cpp +++ b/cpp2rust/converter/mapper.cpp @@ -792,6 +792,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 = diff --git a/cpp2rust/converter/translation_rule.cpp b/cpp2rust/converter/translation_rule.cpp index 3f24e59a..02a7d948 100644 --- a/cpp2rust/converter/translation_rule.cpp +++ b/cpp2rust/converter/translation_rule.cpp @@ -104,6 +104,9 @@ ExprRule ParseExprRuleJSON(const llvm::json::Object &obj) { if (auto ms = obj.getBoolean("multi_statement")) ir.multi_statement = *ms; + if (auto v = obj.getBoolean("is_variadic")) + ir.is_variadic = *v; + if (auto *generics = obj.getObject("generics")) { for (auto &[key, val] : *generics) { if (auto *arr = val.getAsArray()) { diff --git a/cpp2rust/converter/translation_rule.h b/cpp2rust/converter/translation_rule.h index a2962cec..e48242d1 100644 --- a/cpp2rust/converter/translation_rule.h +++ b/cpp2rust/converter/translation_rule.h @@ -70,6 +70,7 @@ struct ExprRule { std::vector> generics; // "T1" -> ["Ord", "Clone"] std::vector body; bool multi_statement = false; + bool is_variadic = false; void dump() const; void validate(const std::string &name) const; diff --git a/cpp2rust/cpp_rule_preprocessor.cpp b/cpp2rust/cpp_rule_preprocessor.cpp index e7d1ae2f..3a9c9502 100644 --- a/cpp2rust/cpp_rule_preprocessor.cpp +++ b/cpp2rust/cpp_rule_preprocessor.cpp @@ -349,7 +349,10 @@ class Callback : public clang::ast_matchers::MatchFinder::MatchCallback { createTemplateArguments(clang::TemplateDecl *decl, llvm::SmallVectorImpl &out) { for (clang::NamedDecl *param : *decl->getTemplateParameters()) { - if (llvm::isa(param)) { + if (param->isTemplateParameterPack()) { + out.emplace_back( + clang::TemplateArgument::CreatePackCopy(sema_->Context, {})); + } else if (llvm::isa(param)) { clang::RecordDecl *rdecl = createRecordDecl(param->getName()); clang::QualType type = sema_->Context.getTagType(clang::ElaboratedTypeKeyword::None, @@ -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( @@ -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; } diff --git a/rule-preprocessor/src/ir.rs b/rule-preprocessor/src/ir.rs index 4697724d..cacb3101 100644 --- a/rule-preprocessor/src/ir.rs +++ b/rule-preprocessor/src/ir.rs @@ -57,6 +57,8 @@ pub struct FnIr { pub params: Option>, #[serde(skip_serializing_if = "Option::is_none")] pub return_type: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub is_variadic: Option, } impl FnIr { @@ -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_variadic == Some(true) || !self.body.is_empty(), + "Rule {name}: body must not be empty" + ); } } diff --git a/rule-preprocessor/src/syntactic.rs b/rule-preprocessor/src/syntactic.rs index 191dac3c..c5b9884f 100644 --- a/rule-preprocessor/src/syntactic.rs +++ b/rule-preprocessor/src/syntactic.rs @@ -415,6 +415,14 @@ impl<'a> FnIrBuilder<'a> { .unwrap_or(Access::Read) } + fn is_variadic(&self) -> bool { + self.fn_item + .param_list() + .into_iter() + .flat_map(|pl| pl.params()) + .any(|p| p.dotdotdot_token().is_some()) + } + fn returns_mut_ref(&self) -> bool { self.fn_item .ret_type() @@ -516,6 +524,7 @@ impl<'a> FnIrBuilder<'a> { }, multi_statement, body, + is_variadic: self.is_variadic().then_some(true), }; ir.validate(&format!("{}:{}", path.display(), fn_name)); ir diff --git a/rules/builtin/src.c b/rules/builtin/src.c new file mode 100644 index 00000000..805fbc1c --- /dev/null +++ b/rules/builtin/src.c @@ -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); } diff --git a/rules/builtin/tgt_refcount.rs b/rules/builtin/tgt_refcount.rs index 0f076e1c..4e1afbbc 100644 --- a/rules/builtin/tgt_refcount.rs +++ b/rules/builtin/tgt_refcount.rs @@ -13,3 +13,13 @@ fn f10(a0: i64, a1: i64, a2: Ptr) -> bool { a2.write(val); ovf } +fn f12(a0: i64, a1: i64, a2: Ptr) -> bool { + let (val, ovf) = a0.overflowing_mul(a1); + a2.write(val); + ovf +} +fn f13(a0: i64, a1: i64, a2: Ptr) -> bool { + let (val, ovf) = a0.overflowing_mul(a1); + a2.write(val); + ovf +} diff --git a/rules/builtin/tgt_unsafe.rs b/rules/builtin/tgt_unsafe.rs index 16638e3f..1d0e7cb8 100644 --- a/rules/builtin/tgt_unsafe.rs +++ b/rules/builtin/tgt_unsafe.rs @@ -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 +} diff --git a/rules/fcntl/src.cpp b/rules/fcntl/src.cpp new file mode 100644 index 00000000..657b35cc --- /dev/null +++ b/rules/fcntl/src.cpp @@ -0,0 +1,14 @@ +// Copyright (c) 2022-present INESC-ID. +// Distributed under the MIT license that can be found in the LICENSE file. + +#include + +template +int f1(int a0, int a1, Args... args) { + return fcntl(a0, a1, args...); +} + +template +int f2(const char *a0, int a1, Args... args) { + return open(a0, a1, args...); +} diff --git a/rules/fcntl/tgt_unsafe.rs b/rules/fcntl/tgt_unsafe.rs new file mode 100644 index 00000000..be3a31ab --- /dev/null +++ b/rules/fcntl/tgt_unsafe.rs @@ -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; +} diff --git a/rules/ioctl/src.cpp b/rules/ioctl/src.cpp new file mode 100644 index 00000000..02587ce4 --- /dev/null +++ b/rules/ioctl/src.cpp @@ -0,0 +1,9 @@ +// Copyright (c) 2022-present INESC-ID. +// Distributed under the MIT license that can be found in the LICENSE file. + +#include + +template +int f1(int a0, unsigned long a1, Args... args) { + return ioctl(a0, a1, args...); +} diff --git a/rules/ioctl/tgt_unsafe.rs b/rules/ioctl/tgt_unsafe.rs new file mode 100644 index 00000000..00d6cbea --- /dev/null +++ b/rules/ioctl/tgt_unsafe.rs @@ -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; +} diff --git a/rules/src/modules.rs b/rules/src/modules.rs index 2b5d751c..7f943d30 100644 --- a/rules/src/modules.rs +++ b/rules/src/modules.rs @@ -38,12 +38,16 @@ pub mod deque_tgt_refcount; pub mod deque_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#"../fstream/tgt_refcount.rs"#] pub mod fstream_tgt_refcount; #[path = r#"../fstream/tgt_unsafe.rs"#] pub mod fstream_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"#] diff --git a/rules/stdio/src.cpp b/rules/stdio/src.cpp index b4a62ea4..4c7b9071 100644 --- a/rules/stdio/src.cpp +++ b/rules/stdio/src.cpp @@ -56,3 +56,8 @@ int f19(FILE *stream, off_t offset, int whence) { } FILE *f20(int fd, const char *mode) { return fdopen(fd, mode); } + +template +int f21(char *a0, size_t a1, const char *a2, Args... args) { + return snprintf(a0, a1, a2, args...); +} diff --git a/rules/stdio/tgt_unsafe.rs b/rules/stdio/tgt_unsafe.rs index ce96445f..d63969ff 100644 --- a/rules/stdio/tgt_unsafe.rs +++ b/rules/stdio/tgt_unsafe.rs @@ -85,3 +85,7 @@ unsafe fn f19(a0: *mut ::libc::FILE, a1: i64, a2: i32) -> i32 { unsafe fn f20(a0: i32, a1: *const u8) -> *mut ::libc::FILE { libc::fdopen(a0, a1 as *const i8) } + +unsafe extern "C" { + fn f21(a0: *mut i8, a1: usize, a2: *const i8, ...) -> i32; +}