diff --git a/cpp2rust/converter/converter.cpp b/cpp2rust/converter/converter.cpp index f6702629..336fc3d5 100644 --- a/cpp2rust/converter/converter.cpp +++ b/cpp2rust/converter/converter.cpp @@ -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); @@ -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(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()) { proto = ptr_ty->getPointeeType()->getAs(); } @@ -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); @@ -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(arg); if (is_materialize && ca.param_type->isLValueReferenceType()) { @@ -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"); } @@ -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; } } @@ -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); } } } @@ -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); diff --git a/cpp2rust/converter/converter.h b/cpp2rust/converter/converter.h index 9d64ef22..4be203b9 100644 --- a/cpp2rust/converter/converter.h +++ b/cpp2rust/converter/converter.h @@ -242,9 +242,10 @@ class Converter : public clang::RecursiveASTVisitor { struct CallInfo { std::vector args; std::vector 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); diff --git a/cpp2rust/converter/converter_lib.cpp b/cpp2rust/converter/converter_lib.cpp index 227a7d2a..72498259 100644 --- a/cpp2rust/converter/converter_lib.cpp +++ b/cpp2rust/converter/converter_lib.cpp @@ -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(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 e606a2e3..ec391929 100644 --- a/cpp2rust/converter/converter_lib.h +++ b/cpp2rust/converter/converter_lib.h @@ -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); diff --git a/cpp2rust/converter/mapper.cpp b/cpp2rust/converter/mapper.cpp index 3b1f7bc9..955132df 100644 --- a/cpp2rust/converter/mapper.cpp +++ b/cpp2rust/converter/mapper.cpp @@ -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(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) && @@ -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 = diff --git a/cpp2rust/converter/mapper.h b/cpp2rust/converter/mapper.h index cd4523c6..cdeba4a5 100644 --- a/cpp2rust/converter/mapper.h +++ b/cpp2rust/converter/mapper.h @@ -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); diff --git a/cpp2rust/converter/translation_rule.cpp b/cpp2rust/converter/translation_rule.cpp index 3f24e59a..0efe254a 100644 --- a/cpp2rust/converter/translation_rule.cpp +++ b/cpp2rust/converter/translation_rule.cpp @@ -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) { diff --git a/cpp2rust/converter/translation_rule.h b/cpp2rust/converter/translation_rule.h index a2962cec..bc6bd84b 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_extern = 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..95f2e4cb 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_extern: 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_extern == 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 3e448636..4adb08b6 100644 --- a/rule-preprocessor/src/syntactic.rs +++ b/rule-preprocessor/src/syntactic.rs @@ -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() @@ -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 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 efd63874..e23de13d 100644 --- a/rules/src/modules.rs +++ b/rules/src/modules.rs @@ -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"#] @@ -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"#] diff --git a/rules/stdio/src.cpp b/rules/stdio/src.cpp index c172b5fd..e4ddb109 100644 --- a/rules/stdio/src.cpp +++ b/rules/stdio/src.cpp @@ -57,6 +57,11 @@ 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...); +} + int f22(const char *a0, const char *a1) { return rename(a0, a1); } diff --git a/rules/stdio/tgt_unsafe.rs b/rules/stdio/tgt_unsafe.rs index 72aacd08..343dacd5 100644 --- a/rules/stdio/tgt_unsafe.rs +++ b/rules/stdio/tgt_unsafe.rs @@ -85,6 +85,10 @@ 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; +} + unsafe fn f22(a0: *const i8, a1: *const i8) -> i32 { libc::rename(a0 as *const i8, a1 as *const i8) } diff --git a/tests/unit/out/unsafe/unistd.rs b/tests/unit/out/unsafe/unistd.rs index 1c7f073f..96f51690 100644 --- a/tests/unit/out/unsafe/unistd.rs +++ b/tests/unit/out/unsafe/unistd.rs @@ -225,19 +225,58 @@ pub unsafe fn test_ftruncate_5() { assert!(((((libc::fclose(fp)) == (0)) as i32) != 0)); libc::unlink(path as *const i8); } -pub unsafe fn test_isatty_6() { +pub unsafe fn test_open_6() { + let mut fd: i32 = (unsafe { + libc::open( + (b"/dev/null\0".as_ptr().cast_mut()).cast_const() as *const i8, + 0 as i32, + (420), + ) + }); + assert!(((((fd) >= (-1_i32)) as i32) != 0)); + if ((((fd) >= (0)) as i32) != 0) { + libc::close(fd); + } + fd = (unsafe { + libc::open( + (b"/dev/null\0".as_ptr().cast_mut()).cast_const() as *const i8, + 0 as i32, + ) + }); + assert!(((((fd) >= (-1_i32)) as i32) != 0)); + if ((((fd) >= (0)) as i32) != 0) { + libc::close(fd); + } +} +pub unsafe fn test_fcntl_7() { + assert!(((((unsafe { libc::fcntl(0 as i32, 1 as i32,) }) >= (-1_i32)) as i32) != 0)); + let mut duped: i32 = (unsafe { libc::fcntl(0 as i32, 0 as i32, (100)) }); + assert!(((((duped) >= (-1_i32)) as i32) != 0)); + if ((((duped) >= (0)) as i32) != 0) { + libc::close(duped); + } +} +pub unsafe fn test_ioctl_8() { + let mut arg: i32 = 0; + assert!( + ((((unsafe { libc::ioctl(0 as i32, 0_u64 as u64, (&mut arg as *mut i32),) }) >= (-1_i32)) + as i32) + != 0) + ); +} +pub unsafe fn test_isatty_9() { printf( (b"%d\n\0".as_ptr().cast_mut()).cast_const() as *const i8, libc::isatty(0), ); } -pub unsafe fn test_geteuid_7() { +pub unsafe fn test_geteuid_10() { printf( (b"%u\n\0".as_ptr().cast_mut()).cast_const() as *const i8, libc::geteuid(), ); } -pub unsafe fn test_gethostname_8() { +pub unsafe fn test_gethostname_11() { let mut name: [u8; 256] = [0_u8; 256]; assert!( ((((libc::gethostname( @@ -263,8 +302,11 @@ unsafe fn main_0() -> i32 { (unsafe { test_unlink_3() }); (unsafe { test_pipe_4() }); (unsafe { test_ftruncate_5() }); - (unsafe { test_isatty_6() }); - (unsafe { test_geteuid_7() }); - (unsafe { test_gethostname_8() }); + (unsafe { test_open_6() }); + (unsafe { test_fcntl_7() }); + (unsafe { test_ioctl_8() }); + (unsafe { test_isatty_9() }); + (unsafe { test_geteuid_10() }); + (unsafe { test_gethostname_11() }); return 0; } diff --git a/tests/unit/unistd.c b/tests/unit/unistd.c index 2ed49f68..de539f10 100644 --- a/tests/unit/unistd.c +++ b/tests/unit/unistd.c @@ -1,7 +1,9 @@ -// no-compile: refcount +// translation-fail: refcount #include +#include #include #include +#include #include #include @@ -87,6 +89,33 @@ static void test_ftruncate(void) { unlink(path); } +static void test_open(void) { + int fd = open("/dev/null", 0, 0644); + assert(fd >= -1); + if (fd >= 0) { + close(fd); + } + fd = open("/dev/null", 0); + assert(fd >= -1); + if (fd >= 0) { + close(fd); + } +} + +static void test_fcntl(void) { + assert(fcntl(0, 1) >= -1); + int duped = fcntl(0, 0, 100); + assert(duped >= -1); + if (duped >= 0) { + close(duped); + } +} + +static void test_ioctl(void) { + int arg = 0; + assert(ioctl(0, 0, &arg) >= -1); +} + static void test_isatty(void) { printf("%d\n", isatty(0)); } static void test_geteuid(void) { printf("%u\n", geteuid()); } @@ -104,6 +133,9 @@ int main(void) { test_unlink(); test_pipe(); test_ftruncate(); + test_open(); + test_fcntl(); + test_ioctl(); test_isatty(); test_geteuid(); test_gethostname();