Skip to content

Commit 8a4535e

Browse files
committed
Refactor how function pointer casts work by creating a trampoline automatically
Simplifies the generated code at the expense of more runtime library code
1 parent c16760d commit 8a4535e

29 files changed

Lines changed: 718 additions & 253 deletions

‎cpp2rust/converter/models/converter_refcount.cpp‎

Lines changed: 33 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -215,62 +215,6 @@ bool ConverterRefCount::VisitReferenceType(clang::ReferenceType *type) {
215215
return false;
216216
}
217217

218-
std::string ConverterRefCount::BuildFnAdapter(
219-
const clang::FunctionDecl *src_fn,
220-
const clang::FunctionProtoType *src_proto,
221-
const clang::FunctionProtoType *target_proto) {
222-
223-
// UB: Incompatible arity
224-
if (src_proto->getNumParams() != target_proto->getNumParams()) {
225-
return "None";
226-
}
227-
228-
PushConversionKind push(*this, ConversionKind::Unboxed);
229-
230-
// Build adapter signature: |a0: T0, a1: T1, ...| -> Tr
231-
std::string closure = "(|";
232-
for (unsigned i = 0; i < target_proto->getNumParams(); ++i) {
233-
closure +=
234-
std::format("a{}: {},", i, ToString(target_proto->getParamType(i)));
235-
}
236-
closure += '|';
237-
if (!target_proto->getReturnType()->isVoidType()) {
238-
closure += std::format(" -> {} ", ToString(target_proto->getReturnType()));
239-
}
240-
closure += "{ ";
241-
242-
// Build adapter body: src_fn(convert(a0), convert(a1), ...)
243-
closure += Mapper::MapFunctionName(src_fn) + '(';
244-
for (unsigned i = 0; i < src_proto->getNumParams(); ++i) {
245-
auto src_pty = src_proto->getParamType(i);
246-
auto tgt_pty = target_proto->getParamType(i);
247-
if (ToString(src_pty) == ToString(tgt_pty)) {
248-
closure += std::format("a{}", i);
249-
} else if (src_pty->isPointerType() && tgt_pty->isPointerType()) {
250-
if (tgt_pty->isVoidPointerType()) {
251-
closure += std::format("a{}.reinterpret_cast::<{}>()", i,
252-
ConvertPointeeType(src_pty));
253-
} else if (src_pty->isVoidPointerType()) {
254-
closure += std::format("a{}.to_any()", i);
255-
} else if (tgt_pty->getPointeeType()->isCharType()) {
256-
closure += std::format("a{}.reinterpret_cast::<{}>()", i,
257-
ConvertPointeeType(src_pty));
258-
} else if (src_pty->getPointeeType()->isCharType()) {
259-
closure += std::format("a{}.reinterpret_cast::<{}>()", i,
260-
ConvertPointeeType(src_pty));
261-
}
262-
} else {
263-
// UB: Incompatible types
264-
return "None";
265-
}
266-
closure += ", ";
267-
}
268-
closure += ") })";
269-
270-
return std::format("Some({} as {})", closure,
271-
ConvertFunctionPointerType(target_proto));
272-
}
273-
274218
std::string ConverterRefCount::ConvertFunctionPointerType(
275219
const clang::FunctionProtoType *proto, FnProtoType kind) {
276220
PushConversionKind push(*this, ConversionKind::Unboxed);
@@ -1390,9 +1334,8 @@ bool ConverterRefCount::VisitImplicitCastExpr(clang::ImplicitCastExpr *expr) {
13901334
}
13911335

13921336
void ConverterRefCount::EmitFnPtrCall(clang::Expr *callee) {
1393-
StrCat("(*");
13941337
Convert(callee);
1395-
StrCat(')');
1338+
StrCat(".call");
13961339
}
13971340

13981341
void ConverterRefCount::ConvertFunctionToFunctionPointer(
@@ -1423,25 +1366,8 @@ bool ConverterRefCount::VisitFunctionPointerCast(
14231366
expr->getType()->isFunctionPointerType()) {
14241367
auto target_proto =
14251368
expr->getType()->getPointeeType()->getAs<clang::FunctionProtoType>();
1426-
auto src_proto = expr->getSubExpr()
1427-
->getType()
1428-
->getPointeeType()
1429-
->getAs<clang::FunctionProtoType>();
1430-
auto fn_type = ConvertFunctionPointerType(target_proto);
1431-
1432-
std::string adapter = "None";
1433-
// Only accept direct references to the casted function. Otherwise the
1434-
// closure would be capturing and would not coerce into a fn pointer.
1435-
if (auto *decl_ref = clang::dyn_cast<clang::DeclRefExpr>(
1436-
expr->getSubExpr()->IgnoreImplicit())) {
1437-
if (auto *fn_decl =
1438-
clang::dyn_cast<clang::FunctionDecl>(decl_ref->getDecl())) {
1439-
adapter = BuildFnAdapter(fn_decl, src_proto, target_proto);
1440-
}
1441-
}
1442-
1443-
StrCat(std::format("{}.cast::<{}>({})", ToString(expr->getSubExpr()),
1444-
fn_type, adapter));
1369+
StrCat(std::format("{}.cast::<{}>()", ToString(expr->getSubExpr()),
1370+
ConvertFunctionPointerType(target_proto)));
14451371
} else if (expr->getSubExpr()->getType()->isFunctionPointerType() ||
14461372
expr->getType()->isVoidPointerType()) {
14471373
Convert(expr->getSubExpr());
@@ -1456,6 +1382,7 @@ bool ConverterRefCount::VisitFunctionPointerCast(
14561382
} else {
14571383
assert(0 && "Unhandled function pointer cast");
14581384
}
1385+
computed_expr_type_ = ComputedExprType::FreshPointer;
14591386
return false;
14601387
}
14611388

@@ -2251,11 +2178,40 @@ std::string ConverterRefCount::ConvertVarInitValue(clang::QualType qual_type,
22512178
return std::format("({} as {})", ConvertFreshPointer(expr),
22522179
ToString(qual_type));
22532180
}
2181+
if (qual_type->isFunctionPointerType()) {
2182+
return ConvertFnPtrValue(qual_type, expr);
2183+
}
22542184
return ConvertFreshPointer(expr);
22552185
}
22562186
return ConvertFreshRValue(expr, qual_type);
22572187
}
22582188

2189+
std::string ConverterRefCount::ConvertFnPtrValue(clang::QualType qual_type,
2190+
clang::Expr *expr) {
2191+
auto base = ConvertFreshPointer(expr);
2192+
2193+
// `qual_type` (the expected fn pointer type) and `expr`'s own fn pointer
2194+
// type may differ in spelling only because the translation maps two
2195+
// typedefs of the same C type to distinct Rust types (e.g. size_t vs
2196+
// unsigned long) -- FnPtr::cast handles this (and any other cast)
2197+
// automatically, so just insert it whenever the two differ.
2198+
auto target_proto =
2199+
qual_type->getPointeeType()->getAs<clang::FunctionProtoType>();
2200+
if (!target_proto || !expr->getType()->isFunctionPointerType()) {
2201+
return base;
2202+
}
2203+
auto src_proto =
2204+
expr->getType()->getPointeeType()->getAs<clang::FunctionProtoType>();
2205+
if (!src_proto) {
2206+
return base;
2207+
}
2208+
auto fn_type = ConvertFunctionPointerType(target_proto);
2209+
if (ConvertFunctionPointerType(src_proto) == fn_type) {
2210+
return base;
2211+
}
2212+
return std::format("{}.cast::<{}>()", base, fn_type);
2213+
}
2214+
22592215
void ConverterRefCount::ConvertVarInit(clang::QualType qual_type,
22602216
clang::Expr *expr) {
22612217
bool is_ref = qual_type->isReferenceType();

‎cpp2rust/converter/models/converter_refcount.h‎

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -277,9 +277,12 @@ class ConverterRefCount final : public Converter {
277277
const char *GetPointerDerefSuffix(clang::QualType pointee_type);
278278
const char *GetPointerDerefPrefix(clang::QualType pointee_type) override;
279279

280-
std::string BuildFnAdapter(const clang::FunctionDecl *src_fn,
281-
const clang::FunctionProtoType *src_proto,
282-
const clang::FunctionProtoType *target_proto);
280+
// Converts `expr` for use where a `qual_type` function pointer is
281+
// expected, inserting a `.cast()` if `expr`'s own fn pointer type differs
282+
// from `qual_type` -- e.g. because the two describe the same C function
283+
// pointer type through different typedefs that the translation maps to
284+
// distinct Rust types (`size_t` vs `unsigned long`).
285+
std::string ConvertFnPtrValue(clang::QualType qual_type, clang::Expr *expr);
283286

284287
void EmitSetOrAssign(clang::Expr *lhs, std::string_view rhs);
285288

0 commit comments

Comments
 (0)