Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
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
223 changes: 139 additions & 84 deletions cpp2rust/converter/converter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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<clang::CXXOperatorCallExpr>(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<clang::PointerType>()) {
proto = ptr_ty->getPointeeType()->getAs<clang::FunctionProtoType>();
}
}

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<std::string> 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<clang::MaterializeTemporaryExpr>(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<clang::MaterializeTemporaryExpr>(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<clang::MaterializeTemporaryExpr>(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<clang::MaterializeTemporaryExpr>(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::TempMaterializationCtx>
Converter::ConvertCallExpr(clang::CallExpr *expr) {
auto *callee = expr->getCallee();
Expand Down
34 changes: 34 additions & 0 deletions cpp2rust/converter/converter.h
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,40 @@ class Converter : public clang::RecursiveASTVisitor<Converter> {

std::optional<TempMaterializationCtx> 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<CallArg> args;
std::vector<clang::Expr *> 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);
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 @@ -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<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 @@ -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);
Expand Down
6 changes: 6 additions & 0 deletions cpp2rust/converter/mapper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 =
Expand Down
3 changes: 3 additions & 0 deletions cpp2rust/converter/translation_rule.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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()) {
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_variadic = false;

void dump() const;
void validate(const std::string &name) const;
Expand Down
Loading
Loading