Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
19ad74a
Add rules for stdlib, rustls, and stdio
lucic71 Jun 4, 2026
ec950d5
Add support for typedef'ed rules
lucic71 Jun 5, 2026
32cb753
Avoid duplicated rules
lucic71 Jun 5, 2026
32b1612
Change translation of size_t from u64 to usize
lucic71 Jun 9, 2026
7978ebe
Pass implicit conversion type downstream
lucic71 Jun 9, 2026
86e9948
Move from i64 to isize
lucic71 Jun 9, 2026
83bfe3a
Add helper functions for implicit conversion
lucic71 Jun 9, 2026
b8635b0
Replace GetNameOfScalarTypedef with ToStringSugared
lucic71 Jun 9, 2026
7b1a884
Inline ToStringSugared into ToString
lucic71 Jun 10, 2026
910144a
Add builtin rules for size types
lucic71 Jun 10, 2026
803c396
Bind temp variable on unsigned long -> size_t lvalue ref binding
lucic71 Jun 10, 2026
9246703
Load builtin rules in cpp-rule-preprocessor
lucic71 Jun 10, 2026
fd2d88d
Merge branch 'usize' into u64-to-usize
lucic71 Jun 10, 2026
94c0299
Zero-initialize integer literal used as enum
lucic71 Jun 10, 2026
d73a86a
Drop u32 cast from rustls_result
lucic71 Jun 10, 2026
a2c7998
Fix parens
lucic71 Jun 10, 2026
3198871
Update tests
lucic71 Jun 10, 2026
7b380db
Delete rustls rules
lucic71 Jun 10, 2026
9808647
Delete array subscript cast if type is already usize
lucic71 Jun 10, 2026
043eb97
Delete rustls from rule-preprocessor
lucic71 Jun 10, 2026
983aa12
Delete unused header
lucic71 Jun 10, 2026
e612531
Delete integer literal unrelated changes
lucic71 Jun 10, 2026
e05dd7a
Add clarifying comments in NeedsRefBindingTemp
lucic71 Jun 10, 2026
1bf11b3
Delete the glvalue check
lucic71 Jun 10, 2026
8db4b01
Add GetParamImplicitConvertTarget
lucic71 Jun 10, 2026
eaa0d9a
Don't load builtin types in cpp-rule-preprocessor
lucic71 Jun 10, 2026
4dd7b3c
Delete extra paren layer
lucic71 Jun 10, 2026
50e5deb
Add paren in ConvertUnsignedArithOperand
lucic71 Jun 10, 2026
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
78 changes: 54 additions & 24 deletions cpp2rust/converter/converter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -204,14 +204,18 @@ std::string Converter::ConvertLValue(clang::Expr *expr) {
return ToString(expr);
}

std::string Converter::ConvertRValue(clang::Expr *expr, int line) {
std::string
Converter::ConvertRValue(clang::Expr *expr,
std::optional<clang::QualType> implicit_convert_to,
int line) {
log() << "ConvertRValue called from line " << line << '\n';
PushExprKind push(*this, ExprKind::RValue);
return ToString(expr);
return ToString(expr, implicit_convert_to);
}

std::string Converter::ConvertFreshRValue(clang::Expr *expr) {
auto str = ConvertRValue(expr);
std::string Converter::ConvertFreshRValue(
clang::Expr *expr, std::optional<clang::QualType> implicit_convert_to) {
auto str = ConvertRValue(expr, implicit_convert_to);
if (!isFresh() && !expr->getType()->isVoidType() &&
!expr->getType()->isPointerType()) {
SetFresh();
Expand All @@ -224,7 +228,8 @@ std::string Converter::ConvertFreshRValue(clang::Expr *expr) {
std::pair<std::string, std::string>
Converter::MaterializeTemp(const std::string &binding_name,
clang::QualType param_type, clang::Expr *expr) {
return {std::format("let mut {} = {} ;", binding_name, ConvertRValue(expr)),
return {std::format("let mut {} = {} ;", binding_name,
ConvertRValue(expr, param_type.getNonReferenceType())),
std::format("& mut {}", binding_name)};
}

Expand Down Expand Up @@ -1295,7 +1300,20 @@ bool Converter::VisitContinueStmt([[maybe_unused]] clang::ContinueStmt *stmt) {
return false;
}

bool Converter::Convert(clang::Expr *expr) { return TraverseStmt(expr); }
bool Converter::Convert(clang::Expr *expr,
std::optional<clang::QualType> implicit_convert_to) {
bool needs_conversion =
expr && implicit_convert_to &&
NeedsImplicitScalarCast(expr->IgnoreImplicit()->getType(),
*implicit_convert_to);
PushParen paren(*this, needs_conversion);
bool result = TraverseStmt(expr);
if (needs_conversion) {
ConvertCast(*implicit_convert_to);
computed_expr_type_ = ComputedExprType::FreshValue;
}
return result;
}

const clang::Expr *Converter::GetParentExpr(const clang::Expr *expr) {
if (!expr) {
Expand Down Expand Up @@ -1527,7 +1545,7 @@ bool Converter::VisitCallExpr(clang::CallExpr *expr) {
if (Mapper::Contains(expr->getCallee())) {
auto **args = expr->getArgs();
auto num_args = expr->getNumArgs();
auto ctx = CollectPrvalueToLRefArgs(expr);
auto ctx = CollectRefBindingTempArgs(expr);
std::string str;
{
PushExprKind push(*this, ExprKind::RValue);
Expand Down Expand Up @@ -1763,7 +1781,7 @@ Converter::ConvertCallExpr(clang::CallExpr *expr) {
} else if (Mapper::Contains(callee)) {
auto **args = expr->getArgs();
auto num_args = expr->getNumArgs();
auto ctx = CollectPrvalueToLRefArgs(expr);
auto ctx = CollectRefBindingTempArgs(expr);
StrCat(GetMappedAsString(expr, args, num_args, &ctx));
return ctx;
} else if (auto *opcall = clang::dyn_cast<clang::CXXOperatorCallExpr>(expr)) {
Expand Down Expand Up @@ -2291,17 +2309,20 @@ void Converter::ConvertBinaryOperator(clang::BinaryOperator *expr) {
}

void Converter::ConvertGenericBinaryOperator(clang::BinaryOperator *expr) {
auto *lhs = expr->getLHS();
auto *rhs = expr->getRHS();

PushParen outer(*this);
{
PushParen lhs_paren(*this);
Convert(expr->getLHS());
Convert(lhs, GetOperandImplicitConversionTarget(expr, lhs, rhs));
}

StrCat(expr->getOpcodeStr());

{
PushParen rhs_paren(*this);
Convert(expr->getRHS());
Convert(rhs, GetOperandImplicitConversionTarget(expr, rhs, lhs));
}
}

Expand Down Expand Up @@ -2450,7 +2471,9 @@ bool Converter::VisitConditionalOperator(clang::ConditionalOperator *expr) {
}
PushExplicitAutoref no_autoref(*this, branch_is_addr ? std::nullopt
: autoref_mut_);
Convert(expr->getTrueExpr());
Convert(expr->getTrueExpr(), branch_is_addr
? std::nullopt
: std::make_optional(expr->getType()));
}
StrCat(keyword::kElse);
{
Expand All @@ -2460,7 +2483,9 @@ bool Converter::VisitConditionalOperator(clang::ConditionalOperator *expr) {
}
PushExplicitAutoref no_autoref(*this, branch_is_addr ? std::nullopt
: autoref_mut_);
Convert(expr->getFalseExpr());
Convert(expr->getFalseExpr(), branch_is_addr
? std::nullopt
: std::make_optional(expr->getType()));
}
return false;
}
Expand Down Expand Up @@ -3031,7 +3056,7 @@ bool Converter::VisitUnaryExprOrTypeTraitExpr(
switch (expr->getKind()) {
case clang::UnaryExprOrTypeTrait::UETT_SizeOf:
StrCat(std::format(
"::std::mem::size_of::<{}>() as u64",
"::std::mem::size_of::<{}>()",
GetUnsafeTypeAsString(expr->isArgumentType()
? expr->getArgumentType()
: expr->getArgumentExpr()->getType())));
Expand Down Expand Up @@ -3488,11 +3513,11 @@ void Converter::ConvertVarInit(clang::QualType qual_type, clang::Expr *expr) {
} else if (IsReferenceType(expr) || qual_type->isFunctionPointerType()) {
PushExprKind push(*this, ExprKind::AddrOf);
PushInitType init_type(*this, qual_type);
Convert(expr);
Convert(expr, qual_type);
} else {
PushExprKind push(*this, ExprKind::RValue);
PushInitType init_type(*this, qual_type);
Convert(expr);
Convert(expr, qual_type);
}
if (qual_type->isReferenceType() && !IsReferenceType(expr)) {
StrCat(keyword::kAs);
Expand All @@ -3502,10 +3527,12 @@ void Converter::ConvertVarInit(clang::QualType qual_type, clang::Expr *expr) {

void Converter::ConvertUnsignedArithOperand(clang::Expr *expr,
clang::QualType type) {
bool needs_cast = (expr->isIntegerConstantExpr(ctx_) &&
!clang::isa<clang::ImplicitCastExpr>(expr)) ||
Mapper::Map(expr->getType()) != Mapper::Map(type);
PushParen paren(*this, needs_cast);
Convert(expr);
if ((expr->isIntegerConstantExpr(ctx_) &&
!clang::isa<clang::ImplicitCastExpr>(expr)) ||
Mapper::Map(expr->getType()) != Mapper::Map(type)) {
if (needs_cast) {
ConvertCast(type);
}
}
Expand Down Expand Up @@ -3599,7 +3626,10 @@ void Converter::ConvertArraySubscript(clang::Expr *base, clang::Expr *idx,
PushParen paren(*this);
Convert(idx);
}
StrCat(keyword::kAs, "usize");

if (Mapper::Map(idx->getType()) != "usize") {
StrCat(keyword::kAs, "usize");
}
}

void Converter::ConvertAssignment(clang::Expr *lhs, clang::Expr *rhs,
Expand All @@ -3609,7 +3639,7 @@ void Converter::ConvertAssignment(clang::Expr *lhs, clang::Expr *rhs,
PushInitType init_type(*this, lhs->getType());
lhs_as_string = ConvertLValue(lhs);
}
auto rhs_as_string = ConvertFreshRValue(rhs);
auto rhs_as_string = ConvertFreshRValue(rhs, lhs->getType());

PushBrace brace(*this, !isVoid());

Expand Down Expand Up @@ -3946,15 +3976,14 @@ void Converter::ConvertCast(clang::QualType qual_type, int line) {
}

Converter::TempMaterializationCtx
Converter::CollectPrvalueToLRefArgs(clang::CallExpr *expr) {
Converter::CollectRefBindingTempArgs(clang::CallExpr *expr) {
TempMaterializationCtx ctx(expr->getNumArgs());
if (auto *fn = expr->getCalleeDecl() ? expr->getCalleeDecl()->getAsFunction()
: nullptr) {
for (unsigned i = 0; i < expr->getNumArgs() && i < fn->getNumParams();
++i) {
auto param_type = fn->getParamDecl(i)->getType();
if (param_type->isLValueReferenceType() &&
clang::isa<clang::MaterializeTemporaryExpr>(expr->getArg(i))) {
if (NeedsRefBindingTemp(expr->getArg(i), param_type)) {
ctx.materialized_args[i] = param_type;
}
}
Expand Down Expand Up @@ -4052,7 +4081,7 @@ std::string Converter::ConvertPlaceholder(clang::Expr *expr, clang::Expr *arg,
return std::format("std::mem::take(&mut {})", ConvertLValue(arg));
}

return ConvertRValue(arg);
return ConvertRValue(arg, ph_ctx.implicit_convert_to);
}

std::string Converter::ConvertMappedMethodCall(
Expand Down Expand Up @@ -4098,6 +4127,7 @@ std::string Converter::ConvertIRFragment(

PlaceholderCtx ph_ctx{
.param_type = Mapper::GetParamType(GetCalleeOrExpr(expr), arg_idx),
.implicit_convert_to = GetParamImplicitConvertTarget(expr, arg_idx),
.materialize_ctx = ctx,
.materialize_idx =
is_receiver ? -1 : ((int)arg_idx - HasReceiver(expr)),
Expand Down
26 changes: 20 additions & 6 deletions cpp2rust/converter/converter.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#include <functional>
#include <optional>
#include <string>
#include <type_traits>
#include <unordered_map>
#include <unordered_set>
#include <utility>
Expand Down Expand Up @@ -188,6 +189,7 @@ class Converter : public clang::RecursiveASTVisitor<Converter> {

struct PlaceholderCtx {
std::string param_type;
std::optional<clang::QualType> implicit_convert_to;
TempMaterializationCtx *materialize_ctx;
int materialize_idx; // <0 = no idx, >=0 idx valid
TranslationRule::Access access;
Expand Down Expand Up @@ -430,9 +432,15 @@ class Converter : public clang::RecursiveASTVisitor<Converter> {
using PushParen = PushDelim<token::kOpenParen, token::kCloseParen>;
using PushBracket = PushDelim<token::kOpenBracket, token::kCloseBracket>;

template <typename T> inline std::string ToString(T node) {
template <typename T>
inline std::string
ToString(T node, std::optional<clang::QualType> implicit_convert_to = {}) {
Buffer buf(*this);
Convert(node);
if constexpr (std::is_convertible_v<T, clang::Expr *>) {
Convert(node, implicit_convert_to);
} else {
Convert(node);
}
return std::move(buf).str();
}

Expand All @@ -449,7 +457,8 @@ class Converter : public clang::RecursiveASTVisitor<Converter> {

virtual bool Convert(clang::Decl *decl);
virtual bool Convert(clang::Stmt *stmt);
virtual bool Convert(clang::Expr *expr);
virtual bool Convert(clang::Expr *expr,
std::optional<clang::QualType> implicit_convert_to = {});

virtual std::string GetDefaultAsString(clang::QualType qual_type);

Expand Down Expand Up @@ -823,8 +832,13 @@ class Converter : public clang::RecursiveASTVisitor<Converter> {
void SetFreshType(clang::QualType type);

std::string ConvertLValue(clang::Expr *expr);
std::string ConvertRValue(clang::Expr *expr, int line = __builtin_LINE());
virtual std::string ConvertFreshRValue(clang::Expr *expr);
std::string
ConvertRValue(clang::Expr *expr,
std::optional<clang::QualType> implicit_convert_to = {},
int line = __builtin_LINE());
virtual std::string
ConvertFreshRValue(clang::Expr *expr,
std::optional<clang::QualType> implicit_convert_to = {});
virtual std::string ConvertFreshPointer(clang::Expr *expr);
virtual std::string ConvertFreshObject(clang::Expr *expr);
std::string ConvertPointer(clang::Expr *expr, int line = __builtin_LINE());
Expand All @@ -850,7 +864,7 @@ class Converter : public clang::RecursiveASTVisitor<Converter> {

virtual const char *GetPointerDerefPrefix(clang::QualType pointee_type);

TempMaterializationCtx CollectPrvalueToLRefArgs(clang::CallExpr *expr);
TempMaterializationCtx CollectRefBindingTempArgs(clang::CallExpr *expr);

bool IsCastRedundantInRust(clang::Expr *expr, clang::QualType target_type);

Expand Down
68 changes: 68 additions & 0 deletions cpp2rust/converter/converter_lib.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -667,6 +667,23 @@ bool HasReceiver(clang::Expr *expr) {
return false;
}

std::optional<clang::QualType> GetParamImplicitConvertTarget(clang::Expr *expr,
unsigned arg_idx) {
auto *call = clang::dyn_cast<clang::CallExpr>(expr);
if (!call) {
return std::nullopt;
}
auto *fn = call->getDirectCallee();
if (!fn) {
return std::nullopt;
}
unsigned param_idx = arg_idx - HasReceiver(expr);
if (param_idx >= fn->getNumParams()) {
return std::nullopt;
}
return fn->getParamDecl(param_idx)->getType();
}

std::optional<IteratorCategory>
GetStrongestIteratorCategory(clang::QualType type) {
type = type.getNonReferenceType().getUnqualifiedType();
Expand Down Expand Up @@ -753,6 +770,57 @@ bool IsBuiltinVaStart(const clang::CallExpr *expr) {
return false;
}

bool NeedsImplicitScalarCast(clang::QualType from, clang::QualType to) {
return !from.isNull() && !to.isNull() && from->isIntegerType() &&
to->isIntegerType() &&
from.getCanonicalType().getUnqualifiedType() ==
to.getCanonicalType().getUnqualifiedType() &&
Mapper::Map(from) != Mapper::Map(to);
}

bool NeedsRefBindingTemp(const clang::Expr *arg, clang::QualType param_type) {
if (!param_type->isLValueReferenceType()) {
return false;
}
// Materialize a prvalue into a const lvalue reference:
// void foo(const int &) {}
// foo(1)
if (clang::isa<clang::MaterializeTemporaryExpr>(arg)) {
return true;
}
// Not a MaterializeTemporaryExpr: the lvalue arg binds directly because it
// has the same underlying C type as the param, but the Rust types differ so a
// temp is still needed for the cast:
// void foo(const size_t &) {} <-- size_t -> usize
// unsigned long x = 1; foo(x); <-- unsigned long -> u64
return param_type->getPointeeType().isConstQualified() &&
NeedsImplicitScalarCast(arg->IgnoreImplicit()->getType(),
param_type.getNonReferenceType());
}

bool IsSizeType(clang::QualType type) {
auto rust_type = Mapper::Map(type);
return rust_type == "usize" || rust_type == "isize";
}

std::optional<clang::QualType>
GetOperandImplicitConversionTarget(const clang::BinaryOperator *op,
const clang::Expr *operand,
const clang::Expr *sibling) {
if (op->isComparisonOp()) {
if (NeedsImplicitScalarCast(operand->getType(), sibling->getType()) &&
IsSizeType(sibling->getType())) {
return sibling->getType();
}
return std::nullopt;
}
if ((op->isAdditiveOp() || op->isMultiplicativeOp() || op->isBitwiseOp()) &&
NeedsImplicitScalarCast(operand->getType(), op->getType())) {
return op->getType();
}
return std::nullopt;
}

bool IsBuiltinVaEnd(const clang::CallExpr *expr) {
if (auto *fn = expr->getDirectCallee()) {
return fn->getBuiltinID() == clang::Builtin::BI__builtin_va_end;
Expand Down
14 changes: 14 additions & 0 deletions cpp2rust/converter/converter_lib.h
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,9 @@ clang::Expr *GetCalleeOrExpr(clang::Expr *expr);

bool HasReceiver(clang::Expr *expr);

std::optional<clang::QualType> GetParamImplicitConvertTarget(clang::Expr *expr,
unsigned arg_idx);

// Build unified args for a call expression: for member calls, the receiver
// becomes a0 and call args shift to a1, a2, etc. For operator/free calls,
// args are used as-is.
Expand All @@ -159,6 +162,17 @@ std::string GetClassName(clang::QualType type);

bool IsVaListType(clang::QualType type);

bool NeedsImplicitScalarCast(clang::QualType from, clang::QualType to);

bool NeedsRefBindingTemp(const clang::Expr *arg, clang::QualType param_type);

bool IsSizeType(clang::QualType type);

std::optional<clang::QualType>
GetOperandImplicitConversionTarget(const clang::BinaryOperator *op,
const clang::Expr *operand,
const clang::Expr *sibling);

bool IsBuiltinVaStart(const clang::CallExpr *expr);

bool IsBuiltinVaEnd(const clang::CallExpr *expr);
Expand Down
Loading
Loading