Skip to content

Commit 073103f

Browse files
authored
Refactor ConvertGenericCallExpr (#145)
ConvertGenericCallExpr is a messy function and it became difficult to add new functoinality on top of it. So I decided to refactor it into: `EmitCall(CollectCallInfo(expr))`. In the near future I plan to add 3 new features on top of it: 1. Don't hoist arguments that are integer literals, they don't alias with any other argument 2. Don't hoist if all passed arguments are local variables and all have non-reference/non-pointer types, local variables don't alias each other 3. Translate mapped (variadic) functions that have a direct mapping in libc, for example fcntl -> libc::fcntl In the new version of ConvertGenericCallExpr, CollectCallInfo is responsible for returning the call information: ```cpp struct CallArg { enum class Kind { Hoisted, Inline, Materialized, }; clang::Expr *expr; Kind kind; std::string param_name; clang::QualType param_type; bool has_default; std::string ref_temp_name; }; struct CallInfo { clang::Expr *callee; bool is_variadic; bool is_fn_ptr_call; std::vector<CallArg> args; std::vector<clang::Expr *> variadic_args; }; ``` And EmitCall is responsible for emitting the hoisted arguments, the callee and the argument list using a CallInfo. This model makes the addition of new strategies, for example `Don't hoist arguments that are integer literals, they don't alias with any other argument`, easier because only the CallInfo fields have to be modified.
1 parent 9019390 commit 073103f

2 files changed

Lines changed: 150 additions & 86 deletions

File tree

cpp2rust/converter/converter.cpp

Lines changed: 117 additions & 86 deletions
Original file line numberDiff line numberDiff line change
@@ -1594,128 +1594,159 @@ void Converter::ConvertFunctionToFunctionPointer(
15941594
StrCat(std::format("Some({})", Mapper::MapFunctionName(fn_decl)));
15951595
}
15961596

1597-
void Converter::ConvertGenericCallExpr(clang::CallExpr *expr) {
1598-
clang::Expr *callee = expr->getCallee();
1599-
auto convert_param_ty = [&](clang::QualType param_type, clang::Expr *expr) {
1600-
if (param_type->isLValueReferenceType()) {
1601-
PushExprKind push(*this, ExprKind::AddrOf);
1602-
ConvertVarInit(param_type, expr);
1603-
} else {
1604-
ConvertVarInit(param_type, expr);
1605-
}
1606-
};
1597+
Converter::CallInfo Converter::CollectCallInfo(clang::CallExpr *expr) {
1598+
using Kind = CallArg::Kind;
16071599

1608-
unsigned arg_begin = 0; // skip count for operator()'s implicit object arg
1600+
CallInfo info;
1601+
info.callee = expr->getCallee();
1602+
unsigned arg_begin = 0;
16091603
if (auto op_call = llvm::dyn_cast<clang::CXXOperatorCallExpr>(expr)) {
16101604
if (op_call->getOperator() == clang::OO_Call) {
1611-
callee = op_call->getArg(0);
1605+
info.callee = op_call->getArg(0);
16121606
arg_begin = 1;
16131607
}
16141608
}
16151609

1616-
PushParen outer(*this);
1617-
StrCat(keyword_unsafe_);
1618-
PushBrace unsafe_brace(*this);
16191610
const auto *function =
16201611
expr->getCalleeDecl() ? expr->getCalleeDecl()->getAsFunction() : nullptr;
16211612
const clang::FunctionProtoType *proto = nullptr;
1622-
16231613
if (!function) {
1624-
auto callee_ty = callee->getType().getDesugaredType(ctx_);
1614+
auto callee_ty = info.callee->getType().getDesugaredType(ctx_);
16251615
if (auto ptr_ty = callee_ty->getAs<clang::PointerType>()) {
16261616
proto = ptr_ty->getPointeeType()->getAs<clang::FunctionProtoType>();
16271617
}
16281618
}
1629-
16301619
assert((function || proto) &&
16311620
"Either function decl or function prototype should be known");
16321621

1633-
auto num_args = expr->getNumArgs() - arg_begin;
1634-
bool is_variadic =
1635-
function ? function->isVariadic() : (proto && proto->isVariadic());
1636-
unsigned num_named_params = function
1637-
? function->getNumParams()
1638-
: (proto ? proto->getNumParams() : num_args);
1639-
1640-
// Track which args are materialized temps bound to reference params
1641-
std::vector<std::string> temp_refs(num_args);
1622+
unsigned num_args = expr->getNumArgs() - arg_begin;
1623+
unsigned num_named_params =
1624+
function ? function->getNumParams() : proto->getNumParams();
1625+
info.is_variadic = function ? function->isVariadic() : proto->isVariadic();
1626+
info.is_fn_ptr_call = !function;
16421627

16431628
for (unsigned i = 0; i < num_named_params && i < num_args; ++i) {
16441629
auto *arg = expr->getArg(i + arg_begin);
1645-
std::string param_name = function
1646-
? function->getParamDecl(i)->getNameAsString()
1647-
: ("arg" + std::to_string(i));
1648-
clang::QualType param_type = function ? function->getParamDecl(i)->getType()
1649-
: proto->getParamType(i);
1630+
CallArg ca{
1631+
.param_name = function
1632+
? ("_" + function->getParamDecl(i)->getNameAsString())
1633+
: ("_arg" + std::to_string(i)),
1634+
.param_type = function ? function->getParamDecl(i)->getType()
1635+
: proto->getParamType(i),
1636+
.expr = arg,
1637+
.has_default = function && function->getParamDecl(i)->hasDefaultArg(),
1638+
.kind = Kind::Hoisted,
1639+
};
1640+
bool is_materialize = clang::isa<clang::MaterializeTemporaryExpr>(arg);
1641+
if (is_materialize && ca.param_type->isLValueReferenceType()) {
1642+
ca.kind = Kind::Materialized;
1643+
} else if (is_materialize) {
1644+
ca.kind = Kind::Inline;
1645+
}
1646+
info.args.push_back(std::move(ca));
1647+
}
1648+
1649+
if (info.is_variadic) {
1650+
for (unsigned i = num_named_params; i < num_args; ++i) {
1651+
info.variadic_args.push_back(expr->getArg(i + arg_begin));
1652+
}
1653+
}
16501654

1651-
bool is_materialize_to_ref =
1652-
clang::isa<clang::MaterializeTemporaryExpr>(arg) &&
1653-
param_type->isLValueReferenceType();
1655+
return info;
1656+
}
16541657

1655-
if (is_materialize_to_ref) {
1658+
void Converter::ConvertParamTy(clang::QualType param_type, clang::Expr *expr) {
1659+
if (param_type->isLValueReferenceType()) {
1660+
PushExprKind push(*this, ExprKind::AddrOf);
1661+
ConvertVarInit(param_type, expr);
1662+
} else {
1663+
ConvertVarInit(param_type, expr);
1664+
}
1665+
}
1666+
1667+
void Converter::EmitHoistedArgs(CallInfo &info) {
1668+
using Kind = CallArg::Kind;
1669+
for (auto &ca : info.args) {
1670+
switch (ca.kind) {
1671+
case Kind::Hoisted:
1672+
StrCat(
1673+
std::format("let {}: {} =", ca.param_name, ToString(ca.param_type)));
1674+
ConvertParamTy(ca.param_type, ca.expr);
1675+
StrCat(";");
1676+
break;
1677+
case Kind::Materialized: {
16561678
auto [binding, ref] =
1657-
MaterializeTemp(std::format("_{}", param_name), param_type, arg);
1679+
MaterializeTemp(ca.param_name, ca.param_type, ca.expr);
16581680
StrCat(binding);
1659-
temp_refs[i] = std::move(ref);
1660-
} else if (!clang::isa<clang::MaterializeTemporaryExpr>(arg)) {
1661-
StrCat("let", std::format("_{}: {}", param_name, ToString(param_type)),
1662-
'=');
1663-
convert_param_ty(param_type, arg);
1664-
StrCat(';');
1681+
ca.ref_temp_name = std::move(ref);
1682+
break;
1683+
}
1684+
case Kind::Inline:
1685+
break;
16651686
}
16661687
}
1688+
}
16671689

1668-
if (proto && !function) {
1669-
EmitFnPtrCall(callee);
1670-
} else {
1671-
PushExprKind push(*this, ExprKind::Callee);
1672-
Convert(callee);
1673-
}
1674-
{
1675-
PushParen call_args(*this);
1676-
for (unsigned i = 0; i < num_named_params && i < num_args; ++i) {
1677-
auto *arg = expr->getArg(i + arg_begin);
1678-
std::string param_name =
1679-
function ? function->getParamDecl(i)->getNameAsString()
1680-
: ("arg" + std::to_string(i));
1681-
clang::QualType param_type = function
1682-
? function->getParamDecl(i)->getType()
1683-
: proto->getParamType(i);
1684-
bool is_parm_with_default_value =
1685-
function && function->getParamDecl(i)->hasDefaultArg();
1686-
1687-
if (is_parm_with_default_value) {
1688-
StrCat("Some(");
1689-
}
1690-
if (!temp_refs[i].empty()) {
1691-
StrCat(temp_refs[i]);
1692-
} else if (clang::isa<clang::MaterializeTemporaryExpr>(arg)) {
1693-
convert_param_ty(param_type, arg);
1694-
} else {
1695-
StrCat(std::format("_{}", param_name));
1696-
}
1697-
if (is_parm_with_default_value) {
1698-
StrCat(')');
1690+
void Converter::EmitArgList(const CallInfo &info) {
1691+
using Kind = CallArg::Kind;
1692+
PushParen call_args(*this);
1693+
1694+
for (const auto &ca : info.args) {
1695+
if (ca.has_default) {
1696+
StrCat("Some");
1697+
}
1698+
1699+
{
1700+
PushParen push(*this, ca.has_default);
1701+
switch (ca.kind) {
1702+
case Kind::Hoisted:
1703+
StrCat(ca.param_name);
1704+
break;
1705+
case Kind::Materialized:
1706+
StrCat(ca.ref_temp_name);
1707+
break;
1708+
case Kind::Inline:
1709+
ConvertParamTy(ca.param_type, ca.expr);
1710+
break;
16991711
}
1700-
StrCat(token::kComma);
17011712
}
17021713

1703-
// Variadic args: wrap in &[arg.into(), ...]
1704-
if (is_variadic) {
1705-
StrCat("& [");
1706-
for (unsigned i = num_named_params; i < num_args; ++i) {
1707-
auto *arg = expr->getArg(i + arg_begin);
1708-
{
1709-
PushParen p(*this);
1710-
ConvertVariadicArg(arg);
1711-
}
1712-
StrCat(".into()", token::kComma);
1714+
StrCat(token::kComma);
1715+
}
1716+
1717+
if (info.is_variadic) {
1718+
StrCat(token::kRef);
1719+
PushBracket push(*this);
1720+
for (auto *arg : info.variadic_args) {
1721+
{
1722+
PushParen p(*this);
1723+
ConvertVariadicArg(arg);
17131724
}
1714-
StrCat(']');
1725+
StrCat(".into()", token::kComma);
17151726
}
17161727
}
17171728
}
17181729

1730+
void Converter::EmitCall(CallInfo &&info) {
1731+
EmitHoistedArgs(info);
1732+
1733+
if (info.is_fn_ptr_call) {
1734+
EmitFnPtrCall(info.callee);
1735+
} else {
1736+
PushExprKind push(*this, ExprKind::Callee);
1737+
Convert(info.callee);
1738+
}
1739+
1740+
EmitArgList(info);
1741+
}
1742+
1743+
void Converter::ConvertGenericCallExpr(clang::CallExpr *expr) {
1744+
PushParen outer(*this);
1745+
StrCat(keyword_unsafe_);
1746+
PushBrace unsafe_brace(*this);
1747+
EmitCall(CollectCallInfo(expr));
1748+
}
1749+
17191750
std::optional<Converter::TempMaterializationCtx>
17201751
Converter::ConvertCallExpr(clang::CallExpr *expr) {
17211752
auto *callee = expr->getCallee();

cpp2rust/converter/converter.h

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -224,6 +224,39 @@ class Converter : public clang::RecursiveASTVisitor<Converter> {
224224

225225
std::optional<TempMaterializationCtx> ConvertCallExpr(clang::CallExpr *expr);
226226

227+
struct CallArg {
228+
enum class Kind : int8_t {
229+
Hoisted,
230+
Inline,
231+
Materialized,
232+
};
233+
234+
std::string param_name;
235+
std::string ref_temp_name;
236+
clang::QualType param_type;
237+
clang::Expr *expr;
238+
bool has_default;
239+
Kind kind;
240+
};
241+
242+
struct CallInfo {
243+
std::vector<CallArg> args;
244+
std::vector<clang::Expr *> variadic_args;
245+
clang::Expr *callee;
246+
bool is_variadic;
247+
bool is_fn_ptr_call;
248+
};
249+
250+
CallInfo CollectCallInfo(clang::CallExpr *expr);
251+
252+
void ConvertParamTy(clang::QualType param_type, clang::Expr *expr);
253+
254+
void EmitHoistedArgs(CallInfo &info);
255+
256+
void EmitArgList(const CallInfo &info);
257+
258+
void EmitCall(CallInfo &&info);
259+
227260
void ConvertGenericCallExpr(clang::CallExpr *expr);
228261

229262
virtual void EmitFnPtrCall(clang::Expr *callee);

0 commit comments

Comments
 (0)