Skip to content

Commit 92e2e20

Browse files
authored
Translate size_t/ssize_t as usize/isize (#185)
This PR translates `size_t/ssize_t` as `usize/isize` instead of `u64/i64`. All tests and rules are affected by this change. That's why this PR is very big. However the core of this change is quite small. In `rules/` I had to change every `u64/i64` with `usize/isize`. This makes the rules cleaner because we get rid of artificial `as usize` casts in arguments and artificial `as u64` casts in return values. In `libcc2rs/` I changed the arguments of some transalted functions (malloc, fwrite, etc) from u64 to usize. I also added usize/isize branches for VaArg. In `cpp2rust/` the change has 3 parts: 1. I put usize/isize in `addBuiltinTypes`. This adds rules for: `size_t / size_type / __size_t -> usize` and `ssize_t -> isize`. __size_t is an internal type that describes the return value of sizeof 2. `ToString` desugars its type by default, so I gave it a flag to leave scalars alone, and made `search(QualType)` look for the sugared type before the desugared one: ```cpp std::string ToString(clang::QualType qual_type, ScalarSugar sugar = ScalarSugar::kDesugar); ``` This is needed because size_t isn't a real type, just a typedef over an integer. 3. In `Converter`, I added an extra default argument to `Convert`, `ConvertRValue` and `ConvertFreshRValue`: `std::optional<clang::QualType> implicit_convert_to`. In Clang, both `size_t` and `unsigned long` are represented as the same type, but in Rust, `usize` and `u64` are different types, an implicit cast between the two is not allowed. To solve this, `Convert` is the single point of adding a cast between 2 types that share the same underlying type in C but have different types in Rust, this is decided by `NeedsImplicitScalarCast`. Callers of `Convert` have to pass the optional `implicit_convert_to` in order to trigger the scalar cast. Callers pass the target type, for example `ConvertFreshRValue(rhs, lhs->getType());` in ConvertAssignment, and `Convert` takes care of the explicit Rust call if needed. Binary operations do the same using GetOperandImplicitConversionTarget, which converts both arguments to the same Rust type.
1 parent 81c040d commit 92e2e20

180 files changed

Lines changed: 2485 additions & 1643 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

cpp2rust/converter/converter.cpp

Lines changed: 54 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -204,14 +204,18 @@ std::string Converter::ConvertLValue(clang::Expr *expr) {
204204
return ToString(expr);
205205
}
206206

207-
std::string Converter::ConvertRValue(clang::Expr *expr, int line) {
207+
std::string
208+
Converter::ConvertRValue(clang::Expr *expr,
209+
std::optional<clang::QualType> implicit_convert_to,
210+
int line) {
208211
log() << "ConvertRValue called from line " << line << '\n';
209212
PushExprKind push(*this, ExprKind::RValue);
210-
return ToString(expr);
213+
return ToString(expr, implicit_convert_to);
211214
}
212215

213-
std::string Converter::ConvertFreshRValue(clang::Expr *expr) {
214-
auto str = ConvertRValue(expr);
216+
std::string Converter::ConvertFreshRValue(
217+
clang::Expr *expr, std::optional<clang::QualType> implicit_convert_to) {
218+
auto str = ConvertRValue(expr, implicit_convert_to);
215219
if (!isFresh() && !expr->getType()->isVoidType() &&
216220
!expr->getType()->isPointerType()) {
217221
SetFresh();
@@ -224,7 +228,8 @@ std::string Converter::ConvertFreshRValue(clang::Expr *expr) {
224228
std::pair<std::string, std::string>
225229
Converter::MaterializeTemp(const std::string &binding_name,
226230
clang::QualType param_type, clang::Expr *expr) {
227-
return {std::format("let mut {} = {} ;", binding_name, ConvertRValue(expr)),
231+
return {std::format("let mut {} = {} ;", binding_name,
232+
ConvertRValue(expr, param_type.getNonReferenceType())),
228233
std::format("& mut {}", binding_name)};
229234
}
230235

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

1298-
bool Converter::Convert(clang::Expr *expr) { return TraverseStmt(expr); }
1303+
bool Converter::Convert(clang::Expr *expr,
1304+
std::optional<clang::QualType> implicit_convert_to) {
1305+
bool needs_conversion =
1306+
expr && implicit_convert_to &&
1307+
NeedsImplicitScalarCast(expr->IgnoreImplicit()->getType(),
1308+
*implicit_convert_to);
1309+
PushParen paren(*this, needs_conversion);
1310+
bool result = TraverseStmt(expr);
1311+
if (needs_conversion) {
1312+
ConvertCast(*implicit_convert_to);
1313+
computed_expr_type_ = ComputedExprType::FreshValue;
1314+
}
1315+
return result;
1316+
}
12991317

13001318
const clang::Expr *Converter::GetParentExpr(const clang::Expr *expr) {
13011319
if (!expr) {
@@ -1527,7 +1545,7 @@ bool Converter::VisitCallExpr(clang::CallExpr *expr) {
15271545
if (Mapper::Contains(expr->getCallee())) {
15281546
auto **args = expr->getArgs();
15291547
auto num_args = expr->getNumArgs();
1530-
auto ctx = CollectPrvalueToLRefArgs(expr);
1548+
auto ctx = CollectRefBindingTempArgs(expr);
15311549
std::string str;
15321550
{
15331551
PushExprKind push(*this, ExprKind::RValue);
@@ -1763,7 +1781,7 @@ Converter::ConvertCallExpr(clang::CallExpr *expr) {
17631781
} else if (Mapper::Contains(callee)) {
17641782
auto **args = expr->getArgs();
17651783
auto num_args = expr->getNumArgs();
1766-
auto ctx = CollectPrvalueToLRefArgs(expr);
1784+
auto ctx = CollectRefBindingTempArgs(expr);
17671785
StrCat(GetMappedAsString(expr, args, num_args, &ctx));
17681786
return ctx;
17691787
} else if (auto *opcall = clang::dyn_cast<clang::CXXOperatorCallExpr>(expr)) {
@@ -2291,17 +2309,20 @@ void Converter::ConvertBinaryOperator(clang::BinaryOperator *expr) {
22912309
}
22922310

22932311
void Converter::ConvertGenericBinaryOperator(clang::BinaryOperator *expr) {
2312+
auto *lhs = expr->getLHS();
2313+
auto *rhs = expr->getRHS();
2314+
22942315
PushParen outer(*this);
22952316
{
22962317
PushParen lhs_paren(*this);
2297-
Convert(expr->getLHS());
2318+
Convert(lhs, GetOperandImplicitConversionTarget(expr, lhs, rhs));
22982319
}
22992320

23002321
StrCat(expr->getOpcodeStr());
23012322

23022323
{
23032324
PushParen rhs_paren(*this);
2304-
Convert(expr->getRHS());
2325+
Convert(rhs, GetOperandImplicitConversionTarget(expr, rhs, lhs));
23052326
}
23062327
}
23072328

@@ -2450,7 +2471,9 @@ bool Converter::VisitConditionalOperator(clang::ConditionalOperator *expr) {
24502471
}
24512472
PushExplicitAutoref no_autoref(*this, branch_is_addr ? std::nullopt
24522473
: autoref_mut_);
2453-
Convert(expr->getTrueExpr());
2474+
Convert(expr->getTrueExpr(), branch_is_addr
2475+
? std::nullopt
2476+
: std::make_optional(expr->getType()));
24542477
}
24552478
StrCat(keyword::kElse);
24562479
{
@@ -2460,7 +2483,9 @@ bool Converter::VisitConditionalOperator(clang::ConditionalOperator *expr) {
24602483
}
24612484
PushExplicitAutoref no_autoref(*this, branch_is_addr ? std::nullopt
24622485
: autoref_mut_);
2463-
Convert(expr->getFalseExpr());
2486+
Convert(expr->getFalseExpr(), branch_is_addr
2487+
? std::nullopt
2488+
: std::make_optional(expr->getType()));
24642489
}
24652490
return false;
24662491
}
@@ -3031,7 +3056,7 @@ bool Converter::VisitUnaryExprOrTypeTraitExpr(
30313056
switch (expr->getKind()) {
30323057
case clang::UnaryExprOrTypeTrait::UETT_SizeOf:
30333058
StrCat(std::format(
3034-
"::std::mem::size_of::<{}>() as u64",
3059+
"::std::mem::size_of::<{}>()",
30353060
GetUnsafeTypeAsString(expr->isArgumentType()
30363061
? expr->getArgumentType()
30373062
: expr->getArgumentExpr()->getType())));
@@ -3511,11 +3536,11 @@ void Converter::ConvertVarInit(clang::QualType qual_type, clang::Expr *expr) {
35113536
} else if (IsReferenceType(expr) || qual_type->isFunctionPointerType()) {
35123537
PushExprKind push(*this, ExprKind::AddrOf);
35133538
PushInitType init_type(*this, qual_type);
3514-
Convert(expr);
3539+
Convert(expr, qual_type);
35153540
} else {
35163541
PushExprKind push(*this, ExprKind::RValue);
35173542
PushInitType init_type(*this, qual_type);
3518-
Convert(expr);
3543+
Convert(expr, qual_type);
35193544
}
35203545
if (qual_type->isReferenceType() && !IsReferenceType(expr)) {
35213546
StrCat(keyword::kAs);
@@ -3525,10 +3550,12 @@ void Converter::ConvertVarInit(clang::QualType qual_type, clang::Expr *expr) {
35253550

35263551
void Converter::ConvertUnsignedArithOperand(clang::Expr *expr,
35273552
clang::QualType type) {
3553+
bool needs_cast = (expr->isIntegerConstantExpr(ctx_) &&
3554+
!clang::isa<clang::ImplicitCastExpr>(expr)) ||
3555+
Mapper::Map(expr->getType()) != Mapper::Map(type);
3556+
PushParen paren(*this, needs_cast);
35283557
Convert(expr);
3529-
if ((expr->isIntegerConstantExpr(ctx_) &&
3530-
!clang::isa<clang::ImplicitCastExpr>(expr)) ||
3531-
Mapper::Map(expr->getType()) != Mapper::Map(type)) {
3558+
if (needs_cast) {
35323559
ConvertCast(type);
35333560
}
35343561
}
@@ -3622,7 +3649,10 @@ void Converter::ConvertArraySubscript(clang::Expr *base, clang::Expr *idx,
36223649
PushParen paren(*this);
36233650
Convert(idx);
36243651
}
3625-
StrCat(keyword::kAs, "usize");
3652+
3653+
if (Mapper::Map(idx->getType()) != "usize") {
3654+
StrCat(keyword::kAs, "usize");
3655+
}
36263656
}
36273657

36283658
void Converter::ConvertAssignment(clang::Expr *lhs, clang::Expr *rhs,
@@ -3632,7 +3662,7 @@ void Converter::ConvertAssignment(clang::Expr *lhs, clang::Expr *rhs,
36323662
PushInitType init_type(*this, lhs->getType());
36333663
lhs_as_string = ConvertLValue(lhs);
36343664
}
3635-
auto rhs_as_string = ConvertFreshRValue(rhs);
3665+
auto rhs_as_string = ConvertFreshRValue(rhs, lhs->getType());
36363666

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

@@ -3969,15 +3999,14 @@ void Converter::ConvertCast(clang::QualType qual_type, int line) {
39693999
}
39704000

39714001
Converter::TempMaterializationCtx
3972-
Converter::CollectPrvalueToLRefArgs(clang::CallExpr *expr) {
4002+
Converter::CollectRefBindingTempArgs(clang::CallExpr *expr) {
39734003
TempMaterializationCtx ctx(expr->getNumArgs());
39744004
if (auto *fn = expr->getCalleeDecl() ? expr->getCalleeDecl()->getAsFunction()
39754005
: nullptr) {
39764006
for (unsigned i = 0; i < expr->getNumArgs() && i < fn->getNumParams();
39774007
++i) {
39784008
auto param_type = fn->getParamDecl(i)->getType();
3979-
if (param_type->isLValueReferenceType() &&
3980-
clang::isa<clang::MaterializeTemporaryExpr>(expr->getArg(i))) {
4009+
if (NeedsRefBindingTemp(expr->getArg(i), param_type)) {
39814010
ctx.materialized_args[i] = param_type;
39824011
}
39834012
}
@@ -4075,7 +4104,7 @@ std::string Converter::ConvertPlaceholder(clang::Expr *expr, clang::Expr *arg,
40754104
return std::format("std::mem::take(&mut {})", ConvertLValue(arg));
40764105
}
40774106

4078-
return ConvertRValue(arg);
4107+
return ConvertRValue(arg, ph_ctx.implicit_convert_to);
40794108
}
40804109

40814110
std::string Converter::ConvertMappedMethodCall(
@@ -4121,6 +4150,7 @@ std::string Converter::ConvertIRFragment(
41214150

41224151
PlaceholderCtx ph_ctx{
41234152
.param_type = Mapper::GetParamType(GetCalleeOrExpr(expr), arg_idx),
4153+
.implicit_convert_to = GetParamImplicitConvertTarget(expr, arg_idx),
41244154
.materialize_ctx = ctx,
41254155
.materialize_idx =
41264156
is_receiver ? -1 : ((int)arg_idx - HasReceiver(expr)),

cpp2rust/converter/converter.h

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
#include <functional>
1111
#include <optional>
1212
#include <string>
13+
#include <type_traits>
1314
#include <unordered_map>
1415
#include <unordered_set>
1516
#include <utility>
@@ -188,6 +189,7 @@ class Converter : public clang::RecursiveASTVisitor<Converter> {
188189

189190
struct PlaceholderCtx {
190191
std::string param_type;
192+
std::optional<clang::QualType> implicit_convert_to;
191193
TempMaterializationCtx *materialize_ctx;
192194
int materialize_idx; // <0 = no idx, >=0 idx valid
193195
TranslationRule::Access access;
@@ -432,9 +434,15 @@ class Converter : public clang::RecursiveASTVisitor<Converter> {
432434
using PushParen = PushDelim<token::kOpenParen, token::kCloseParen>;
433435
using PushBracket = PushDelim<token::kOpenBracket, token::kCloseBracket>;
434436

435-
template <typename T> inline std::string ToString(T node) {
437+
template <typename T>
438+
inline std::string
439+
ToString(T node, std::optional<clang::QualType> implicit_convert_to = {}) {
436440
Buffer buf(*this);
437-
Convert(node);
441+
if constexpr (std::is_convertible_v<T, clang::Expr *>) {
442+
Convert(node, implicit_convert_to);
443+
} else {
444+
Convert(node);
445+
}
438446
return std::move(buf).str();
439447
}
440448

@@ -451,7 +459,8 @@ class Converter : public clang::RecursiveASTVisitor<Converter> {
451459

452460
virtual bool Convert(clang::Decl *decl);
453461
virtual bool Convert(clang::Stmt *stmt);
454-
virtual bool Convert(clang::Expr *expr);
462+
virtual bool Convert(clang::Expr *expr,
463+
std::optional<clang::QualType> implicit_convert_to = {});
455464

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

@@ -825,8 +834,13 @@ class Converter : public clang::RecursiveASTVisitor<Converter> {
825834
void SetFreshType(clang::QualType type);
826835

827836
std::string ConvertLValue(clang::Expr *expr);
828-
std::string ConvertRValue(clang::Expr *expr, int line = __builtin_LINE());
829-
virtual std::string ConvertFreshRValue(clang::Expr *expr);
837+
std::string
838+
ConvertRValue(clang::Expr *expr,
839+
std::optional<clang::QualType> implicit_convert_to = {},
840+
int line = __builtin_LINE());
841+
virtual std::string
842+
ConvertFreshRValue(clang::Expr *expr,
843+
std::optional<clang::QualType> implicit_convert_to = {});
830844
virtual std::string ConvertFreshPointer(clang::Expr *expr);
831845
virtual std::string ConvertFreshObject(clang::Expr *expr);
832846
std::string ConvertPointer(clang::Expr *expr, int line = __builtin_LINE());
@@ -852,7 +866,7 @@ class Converter : public clang::RecursiveASTVisitor<Converter> {
852866

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

855-
TempMaterializationCtx CollectPrvalueToLRefArgs(clang::CallExpr *expr);
869+
TempMaterializationCtx CollectRefBindingTempArgs(clang::CallExpr *expr);
856870

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

cpp2rust/converter/converter_lib.cpp

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -667,6 +667,23 @@ bool HasReceiver(clang::Expr *expr) {
667667
return false;
668668
}
669669

670+
std::optional<clang::QualType> GetParamImplicitConvertTarget(clang::Expr *expr,
671+
unsigned arg_idx) {
672+
auto *call = clang::dyn_cast<clang::CallExpr>(expr);
673+
if (!call) {
674+
return std::nullopt;
675+
}
676+
auto *fn = call->getDirectCallee();
677+
if (!fn) {
678+
return std::nullopt;
679+
}
680+
unsigned param_idx = arg_idx - HasReceiver(expr);
681+
if (param_idx >= fn->getNumParams()) {
682+
return std::nullopt;
683+
}
684+
return fn->getParamDecl(param_idx)->getType();
685+
}
686+
670687
std::optional<IteratorCategory>
671688
GetStrongestIteratorCategory(clang::QualType type) {
672689
type = type.getNonReferenceType().getUnqualifiedType();
@@ -753,6 +770,57 @@ bool IsBuiltinVaStart(const clang::CallExpr *expr) {
753770
return false;
754771
}
755772

773+
bool NeedsImplicitScalarCast(clang::QualType from, clang::QualType to) {
774+
return !from.isNull() && !to.isNull() && from->isIntegerType() &&
775+
to->isIntegerType() &&
776+
from.getCanonicalType().getUnqualifiedType() ==
777+
to.getCanonicalType().getUnqualifiedType() &&
778+
Mapper::Map(from) != Mapper::Map(to);
779+
}
780+
781+
bool NeedsRefBindingTemp(const clang::Expr *arg, clang::QualType param_type) {
782+
if (!param_type->isLValueReferenceType()) {
783+
return false;
784+
}
785+
// Materialize a prvalue into a const lvalue reference:
786+
// void foo(const int &) {}
787+
// foo(1)
788+
if (clang::isa<clang::MaterializeTemporaryExpr>(arg)) {
789+
return true;
790+
}
791+
// Not a MaterializeTemporaryExpr: the lvalue arg binds directly because it
792+
// has the same underlying C type as the param, but the Rust types differ so a
793+
// temp is still needed for the cast:
794+
// void foo(const size_t &) {} <-- size_t -> usize
795+
// unsigned long x = 1; foo(x); <-- unsigned long -> u64
796+
return param_type->getPointeeType().isConstQualified() &&
797+
NeedsImplicitScalarCast(arg->IgnoreImplicit()->getType(),
798+
param_type.getNonReferenceType());
799+
}
800+
801+
bool IsSizeType(clang::QualType type) {
802+
auto rust_type = Mapper::Map(type);
803+
return rust_type == "usize" || rust_type == "isize";
804+
}
805+
806+
std::optional<clang::QualType>
807+
GetOperandImplicitConversionTarget(const clang::BinaryOperator *op,
808+
const clang::Expr *operand,
809+
const clang::Expr *sibling) {
810+
if (op->isComparisonOp()) {
811+
if (NeedsImplicitScalarCast(operand->getType(), sibling->getType()) &&
812+
IsSizeType(sibling->getType())) {
813+
return sibling->getType();
814+
}
815+
return std::nullopt;
816+
}
817+
if ((op->isAdditiveOp() || op->isMultiplicativeOp() || op->isBitwiseOp()) &&
818+
NeedsImplicitScalarCast(operand->getType(), op->getType())) {
819+
return op->getType();
820+
}
821+
return std::nullopt;
822+
}
823+
756824
bool IsBuiltinVaEnd(const clang::CallExpr *expr) {
757825
if (auto *fn = expr->getDirectCallee()) {
758826
return fn->getBuiltinID() == clang::Builtin::BI__builtin_va_end;

cpp2rust/converter/converter_lib.h

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,9 @@ clang::Expr *GetCalleeOrExpr(clang::Expr *expr);
143143

144144
bool HasReceiver(clang::Expr *expr);
145145

146+
std::optional<clang::QualType> GetParamImplicitConvertTarget(clang::Expr *expr,
147+
unsigned arg_idx);
148+
146149
// Build unified args for a call expression: for member calls, the receiver
147150
// becomes a0 and call args shift to a1, a2, etc. For operator/free calls,
148151
// args are used as-is.
@@ -159,6 +162,17 @@ std::string GetClassName(clang::QualType type);
159162

160163
bool IsVaListType(clang::QualType type);
161164

165+
bool NeedsImplicitScalarCast(clang::QualType from, clang::QualType to);
166+
167+
bool NeedsRefBindingTemp(const clang::Expr *arg, clang::QualType param_type);
168+
169+
bool IsSizeType(clang::QualType type);
170+
171+
std::optional<clang::QualType>
172+
GetOperandImplicitConversionTarget(const clang::BinaryOperator *op,
173+
const clang::Expr *operand,
174+
const clang::Expr *sibling);
175+
162176
bool IsBuiltinVaStart(const clang::CallExpr *expr);
163177

164178
bool IsBuiltinVaEnd(const clang::CallExpr *expr);

0 commit comments

Comments
 (0)