Skip to content

Commit f532aa4

Browse files
authored
Translate rvalue references to Ptr<T> (#318)
1 parent 180d440 commit f532aa4

39 files changed

Lines changed: 770 additions & 63 deletions

‎cpp2rust/converter/converter.cpp‎

Lines changed: 47 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -234,9 +234,25 @@ std::string Converter::ConvertFreshRValue(
234234
std::pair<std::string, std::string>
235235
Converter::MaterializeTemp(const std::string &binding_name,
236236
clang::QualType param_type, clang::Expr *expr) {
237-
return {std::format("let mut {} = {} ;", binding_name,
238-
ConvertRValue(expr, param_type.getNonReferenceType())),
239-
std::format("& mut {}", binding_name)};
237+
auto pointee = param_type.getNonReferenceType();
238+
auto value = ConvertRValue(expr, pointee);
239+
auto type_str = ToStringBase(pointee);
240+
const auto *decl = in_const_initializer_ ? keyword::kStatic : keyword::kLet;
241+
242+
auto binding =
243+
std::format("{} mut {} : {} = {};", decl, binding_name, type_str, value);
244+
auto ref = std::format("& mut {}", binding_name);
245+
return {binding, ref};
246+
}
247+
248+
std::string Converter::EmitMaterializedTempBinding(clang::QualType param_type,
249+
clang::Expr *expr) {
250+
assert(materialized_temp_bindings_ && "materialized temp emitted outside a "
251+
"HoistMaterializedTempBindings scope");
252+
auto [binding, ref] = MaterializeTemp(
253+
std::format("__tmp_{}", materialized_temp_id_++), param_type, expr);
254+
*materialized_temp_bindings_ += std::move(binding);
255+
return ref;
240256
}
241257

242258
bool Converter::VisitConstantArrayType(clang::ConstantArrayType *type) {
@@ -254,7 +270,7 @@ bool Converter::VisitIncompleteArrayType(clang::IncompleteArrayType *type) {
254270
return false;
255271
}
256272

257-
bool Converter::VisitLValueReferenceType(clang::LValueReferenceType *type) {
273+
bool Converter::VisitReferenceType(clang::ReferenceType *type) {
258274
auto pointee_type = type->getPointeeType();
259275
StrCat(pointee_type.isConstQualified() ? "*const" : "*mut");
260276
return Convert(pointee_type);
@@ -393,6 +409,7 @@ void Converter::EmitHoistedDecls(clang::CompoundStmt *body) {
393409
}
394410

395411
void Converter::ConvertGotoBlock(clang::CompoundStmt *body) {
412+
HoistMaterializedTempBindings hoist_temps(*this);
396413
PushHoistedDecls push(hoisted_decls_);
397414
EmitHoistedDecls(body);
398415

@@ -457,6 +474,17 @@ void Converter::ConvertVaListVarDecl(clang::VarDecl *decl) {
457474
StrCat(keyword_mut_, GetNamedDeclAsString(decl), token::kColon, "VaList");
458475
}
459476

477+
bool Converter::NeedsMut(const clang::VarDecl *decl, clang::QualType type,
478+
llvm::StringRef name) const {
479+
auto *method_or_null =
480+
curr_function_ ? clang::dyn_cast<clang::CXXMethodDecl>(curr_function_)
481+
: nullptr;
482+
return ((hoisted_decls_.contains(decl) ||
483+
(!type.isConstQualified() && !type->isReferenceType())) &&
484+
((method_or_null == nullptr) || !method_or_null->isVirtual()) &&
485+
!IsGlobalVar(decl) && name != "_");
486+
}
487+
460488
bool Converter::ConvertVarDeclSkipInit(clang::VarDecl *decl) {
461489
auto qual_type = decl->getType();
462490
auto name = GetNamedDeclAsString(decl);
@@ -482,16 +510,10 @@ bool Converter::ConvertVarDeclSkipInit(clang::VarDecl *decl) {
482510
StrCat(keyword::kLet);
483511
}
484512

485-
auto *method_or_null =
486-
curr_function_ ? clang::dyn_cast<clang::CXXMethodDecl>(curr_function_)
487-
: nullptr;
488-
if ((hoisted_decls_.contains(decl) || !qual_type.isConstQualified()) &&
489-
!qual_type->isReferenceType() &&
490-
((method_or_null == nullptr) || !method_or_null->isVirtual()) &&
491-
!IsGlobalVar(decl) && name != "_") {
492-
StrCat(keyword_mut_);
513+
if (NeedsMut(decl, qual_type, name)) {
514+
// If this decl requires 'mut', print it irregardless of the model
515+
StrCat(keyword::kMut);
493516
}
494-
495517
StrCat(name, token::kColon);
496518

497519
bool is_parm_with_default_value = false;
@@ -545,6 +567,8 @@ void Converter::ConvertVarDecl(clang::VarDecl *decl) {
545567
EmitHoistedInArmAssignment(decl);
546568
return;
547569
}
570+
571+
HoistMaterializedTempBindings hoist_temps(*this);
548572
if (!ConvertVarDeclSkipInit(decl)) {
549573
// Skip global variables declared extern
550574
return;
@@ -557,6 +581,7 @@ void Converter::ConvertVarDecl(clang::VarDecl *decl) {
557581
}
558582

559583
void Converter::ConvertGlobalVarDecl(clang::VarDecl *decl) {
584+
HoistMaterializedTempBindings hoist_temps(*this);
560585
if (!ConvertVarDeclSkipInit(decl)) {
561586
// Skip global variables declared extern
562587
return;
@@ -1124,6 +1149,7 @@ bool Converter::VisitDeclStmt(clang::DeclStmt *stmt) {
11241149
bool Converter::VisitReturnStmt(clang::ReturnStmt *stmt) {
11251150
auto return_type = curr_function_->getReturnType();
11261151
if (!return_type->isVoidType()) {
1152+
HoistMaterializedTempBindings hoist_temps(*this);
11271153
StrCat(keyword::kReturn);
11281154
ConvertVarInit(return_type, stmt->getRetValue());
11291155
} else {
@@ -1714,7 +1740,7 @@ Converter::CallInfo Converter::CollectCallInfo(clang::CallExpr *expr) {
17141740
: Kind::Hoisted,
17151741
};
17161742
bool is_materialize = clang::isa<clang::MaterializeTemporaryExpr>(arg);
1717-
if (is_materialize && ca.param_type->isLValueReferenceType()) {
1743+
if (is_materialize && ca.param_type->isReferenceType()) {
17181744
ca.kind = Kind::Materialized;
17191745
} else if (is_materialize) {
17201746
ca.kind = Kind::Inline;
@@ -1750,7 +1776,7 @@ Converter::CallInfo Converter::CollectCallInfo(clang::CallExpr *expr) {
17501776
}
17511777

17521778
void Converter::ConvertParamTy(clang::QualType param_type, clang::Expr *expr) {
1753-
if (param_type->isLValueReferenceType()) {
1779+
if (param_type->isReferenceType()) {
17541780
PushExprKind push(*this, ExprKind::AddrOf);
17551781
ConvertVarInit(param_type, expr);
17561782
} else {
@@ -3106,6 +3132,8 @@ void Converter::ConvertCXXConstructExprArgs(clang::CXXConstructExpr *expr) {
31063132

31073133
if (arg_idx < expr->getNumArgs()) {
31083134
clang::Expr *arg = expr->getArg(arg_idx++);
3135+
PushBrace brace(*this);
3136+
HoistMaterializedTempBindings hoist_temps(*this);
31093137

31103138
if (has_default) {
31113139
StrCat("Some(");
@@ -3629,6 +3657,10 @@ std::string Converter::GetUnsafeTypeAsString(clang::QualType qual_type) const {
36293657

36303658
void Converter::ConvertVarInit(clang::QualType qual_type, clang::Expr *expr) {
36313659
if (qual_type->isReferenceType() && !IsReferenceType(expr)) {
3660+
if (llvm::isa<clang::MaterializeTemporaryExpr>(expr->IgnoreImpCasts())) {
3661+
StrCat(EmitMaterializedTempBinding(qual_type, expr));
3662+
return;
3663+
}
36323664
StrCat(token::kRef);
36333665
if (IsMut(qual_type)) {
36343666
StrCat(keyword_mut_);

‎cpp2rust/converter/converter.h‎

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ class Converter : public clang::RecursiveASTVisitor<Converter> {
2727
public:
2828
explicit Converter(std::string &rs_code, clang::ASTContext &ctx,
2929
const char *keyword_unsafe = "unsafe",
30-
const char *keyword_mut = "mut",
30+
const char *keyword_mut = keyword::kMut,
3131
const char *keyword_const_fn = keyword::kConst)
3232
: rs_code_(&rs_code), ctx_(ctx), keyword_unsafe_(keyword_unsafe),
3333
keyword_mut_(keyword_mut), keyword_const_fn_(keyword_const_fn) {}
@@ -60,7 +60,7 @@ class Converter : public clang::RecursiveASTVisitor<Converter> {
6060

6161
virtual bool VisitIncompleteArrayType(clang::IncompleteArrayType *type);
6262

63-
virtual bool VisitLValueReferenceType(clang::LValueReferenceType *type);
63+
virtual bool VisitReferenceType(clang::ReferenceType *type);
6464

6565
virtual bool VisitPointerType(clang::PointerType *type);
6666

@@ -498,6 +498,9 @@ class Converter : public clang::RecursiveASTVisitor<Converter> {
498498

499499
virtual std::string GetUnsafeTypeAsString(clang::QualType qual_type) const;
500500

501+
virtual bool NeedsMut(const clang::VarDecl *decl, clang::QualType type,
502+
llvm::StringRef name) const;
503+
501504
virtual void ConvertVarInit(clang::QualType qual_type, clang::Expr *expr);
502505

503506
virtual void ConvertUnsignedArithOperand(clang::Expr *expr,
@@ -741,6 +744,32 @@ class Converter : public clang::RecursiveASTVisitor<Converter> {
741744
std::unordered_set<const clang::VarDecl *> saved_;
742745
};
743746

747+
unsigned materialized_temp_id_ = 0;
748+
std::string *materialized_temp_bindings_ = nullptr;
749+
class HoistMaterializedTempBindings {
750+
Converter &c;
751+
std::string *prev;
752+
std::string bindings;
753+
std::optional<Buffer> buf;
754+
755+
public:
756+
explicit HoistMaterializedTempBindings(Converter &c)
757+
: c(c), prev(c.materialized_temp_bindings_), buf(c) {
758+
c.materialized_temp_bindings_ = &bindings;
759+
}
760+
~HoistMaterializedTempBindings() {
761+
c.materialized_temp_bindings_ = prev;
762+
std::string body = std::move(*buf).str();
763+
buf.reset();
764+
765+
c.StrCat(bindings, body);
766+
}
767+
HoistMaterializedTempBindings(const HoistMaterializedTempBindings &) =
768+
delete;
769+
HoistMaterializedTempBindings &
770+
operator=(const HoistMaterializedTempBindings &) = delete;
771+
};
772+
744773
struct ScopedMapIterDecl {
745774
Converter &c;
746775
const clang::VarDecl *decl;
@@ -876,6 +905,11 @@ class Converter : public clang::RecursiveASTVisitor<Converter> {
876905
MaterializeTemp(const std::string &binding_name, clang::QualType param_type,
877906
clang::Expr *expr);
878907

908+
/// Emits binding_code to materialized_temp_bindings_.
909+
/// Returns ref_expression.
910+
std::string EmitMaterializedTempBinding(clang::QualType param_type,
911+
clang::Expr *expr);
912+
879913
// TODO: move this into the Plugin infrastructure. Plugins are used for
880914
// functions that cannot be translated using the rules/ directory. For
881915
// example emplace_back, make_unique, printf, etc. Generally variadic

‎cpp2rust/converter/converter_lib.cpp‎

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -68,13 +68,13 @@ static const char rust_keywords[][12] = {
6868

6969
namespace cpp2rust {
7070

71-
bool IsGlobalVar(clang::VarDecl *decl) {
71+
bool IsGlobalVar(const clang::VarDecl *decl) {
7272
return decl->isFileVarDecl() || decl->isStaticLocal();
7373
}
7474

75-
bool IsGlobalVar(clang::Expr *expr) {
75+
bool IsGlobalVar(const clang::Expr *expr) {
7676
expr = expr->IgnoreImplicit();
77-
clang::DeclRefExpr *decl_ref = clang::dyn_cast<clang::DeclRefExpr>(expr);
77+
const auto *decl_ref = clang::dyn_cast<clang::DeclRefExpr>(expr);
7878
if (!decl_ref) {
7979
return false;
8080
}
@@ -908,7 +908,7 @@ bool NeedsImplicitScalarCast(clang::QualType from, clang::QualType to) {
908908
}
909909

910910
bool NeedsRefBindingTemp(const clang::Expr *arg, clang::QualType param_type) {
911-
if (!param_type->isLValueReferenceType()) {
911+
if (!param_type->isReferenceType()) {
912912
return false;
913913
}
914914
// Materialize a prvalue into a const lvalue reference:

‎cpp2rust/converter/converter_lib.h‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,9 +32,9 @@ std::optional<IteratorCategory>
3232
GetStrongestIteratorCategory(clang::QualType type);
3333
bool IsBuiltinConstantP(const clang::Expr *expr);
3434

35-
bool IsGlobalVar(clang::VarDecl *decl);
35+
bool IsGlobalVar(const clang::VarDecl *decl);
3636

37-
bool IsGlobalVar(clang::Expr *expr);
37+
bool IsGlobalVar(const clang::Expr *expr);
3838

3939
bool IsComparisonWithNullOp(const clang::BinaryOperator *expr);
4040

‎cpp2rust/converter/lex.h‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ inline constexpr const char kFor[] = "for";
5757
inline constexpr const char kIn[] = "in";
5858
inline constexpr const char kTrait[] = "trait";
5959
inline constexpr const char kDyn[] = "dyn";
60+
inline constexpr const char kMut[] = "mut";
6061
} // namespace keyword
6162

6263
namespace token {

‎cpp2rust/converter/models/converter_refcount.cpp‎

Lines changed: 32 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,12 @@ ConverterRefCount::GetSafeTypeAsString(clang::QualType qual_type) const {
117117
return std::string(Trim(type_as_string));
118118
}
119119

120+
bool ConverterRefCount::NeedsMut(const clang::VarDecl *decl,
121+
clang::QualType type,
122+
llvm::StringRef /*name*/) const {
123+
return hoisted_decls_.contains(decl) && type->isReferenceType();
124+
}
125+
120126
std::string ConverterRefCount::BoxType(std::string &&str) const {
121127
switch (getConversionKind()) {
122128
case ConversionKind::Unboxed:
@@ -149,8 +155,7 @@ bool ConverterRefCount::Convert(clang::QualType qual_type) {
149155
if (!Mapper::Contains(qual_type))
150156
qual_type = qual_type.getUnqualifiedType().getDesugaredType(ctx_);
151157

152-
if (qual_type->isLValueReferenceType() ||
153-
qual_type->isIncompleteArrayType()) {
158+
if (qual_type->isReferenceType() || qual_type->isIncompleteArrayType()) {
154159
return Converter::Convert(qual_type);
155160
}
156161

@@ -169,8 +174,7 @@ bool ConverterRefCount::VisitIncompleteArrayType(
169174
return false;
170175
}
171176

172-
bool ConverterRefCount::VisitLValueReferenceType(
173-
clang::LValueReferenceType *type) {
177+
bool ConverterRefCount::VisitReferenceType(clang::ReferenceType *type) {
174178
PushConversionKind push(*this, ConversionKind::Unboxed);
175179
StrCat("Ptr<");
176180
Convert(type->getPointeeType());
@@ -364,11 +368,13 @@ ConverterRefCount::MaterializeTemp(const std::string &binding_name,
364368
auto pointee = param_type.getNonReferenceType();
365369
auto value = ConvertRValue(expr, pointee);
366370
auto type_str = ToStringBase(pointee);
367-
std::string binding =
368-
std::format("let {} : Value < {} > = Rc::new(RefCell::new( {} )) ;",
369-
binding_name, type_str, value);
370-
std::string ref = std::format("{}.as_pointer()", binding_name);
371-
return {binding, ref};
371+
const auto *decl = in_const_initializer_ ? keyword::kStatic : keyword::kLet;
372+
373+
auto binding = std::format("{} {} : Value<{}> = Rc::new(RefCell::new({}));",
374+
decl, binding_name, type_str, value);
375+
auto ref =
376+
in_const_initializer_ ? ".with(Value::as_pointer)" : ".as_pointer()";
377+
return {binding, binding_name + ref};
372378
}
373379

374380
std::string ConverterRefCount::ConvertPtrType(clang::QualType type) {
@@ -732,10 +738,17 @@ void ConverterRefCount::EmitHoistedInArmAssignment(clang::VarDecl *decl) {
732738
if (!decl->hasInit()) {
733739
return;
734740
}
741+
742+
const auto type = decl->getType();
743+
if (type->isReferenceType()) {
744+
StrCat(GetNamedDeclAsString(decl));
745+
} else {
746+
StrCat(token::kStar, GetNamedDeclAsString(decl), ".borrow_mut()");
747+
}
748+
735749
PushConversionKind push(*this, ConversionKind::FullRefCount);
736-
StrCat(token::kStar, GetNamedDeclAsString(decl), ".borrow_mut()",
737-
token::kAssign);
738-
StrCat(ConvertVarInitValue(decl->getType(), decl->getInit()));
750+
StrCat(token::kAssign);
751+
StrCat(ConvertVarInitValue(type, decl->getInit()));
739752
StrCat(token::kSemiColon);
740753
}
741754

@@ -842,11 +855,13 @@ bool ConverterRefCount::VisitDeclRefExpr(clang::DeclRefExpr *expr) {
842855
return false;
843856
}
844857

858+
const auto decl_t = decl->getType();
845859
if (IsGlobalVar(expr)) {
846-
str = std::format("{}.with(Value::clone)", str);
860+
auto tp = decl_t->isReferenceType() ? "Ptr" : "Value";
861+
str = std::format("{}.with({}::clone)", str, std::move(tp));
847862
}
848863

849-
if (auto *ref = decl->getType()->getAs<clang::ReferenceType>()) {
864+
if (auto *ref = decl_t->getAs<clang::ReferenceType>()) {
850865
if (map_iter_decls_.contains(clang::dyn_cast<clang::VarDecl>(decl))) {
851866
StrCat(str);
852867
return false;
@@ -1060,7 +1075,6 @@ bool ConverterRefCount::VisitCallExpr(clang::CallExpr *expr) {
10601075
if (IsUniquePtr(expr->getArg(0)->getType())) {
10611076
StrCat(std::format("{}.take()", ConvertLValue(expr->getArg(0))));
10621077
} else {
1063-
PushExprKind push(*this, ExprKind::XValue);
10641078
Convert(expr->getArg(0));
10651079
}
10661080
computed_expr_type_ = ComputedExprType::FreshValue;
@@ -2028,6 +2042,9 @@ std::string ConverterRefCount::ConvertVarInitValue(clang::QualType qual_type,
20282042

20292043
PushInitType init_type(*this, qual_type);
20302044
if (qual_type->isReferenceType() || qual_type->isFunctionPointerType()) {
2045+
if (llvm::isa<clang::MaterializeTemporaryExpr>(expr->IgnoreImpCasts())) {
2046+
return EmitMaterializedTempBinding(qual_type, expr);
2047+
}
20312048
return ConvertFreshPointer(expr);
20322049
}
20332050
return ConvertFreshRValue(expr, qual_type);

‎cpp2rust/converter/models/converter_refcount.h‎

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ class ConverterRefCount final : public Converter {
1818

1919
bool VisitIncompleteArrayType(clang::IncompleteArrayType *type) override;
2020

21-
bool VisitLValueReferenceType(clang::LValueReferenceType *type) override;
21+
bool VisitReferenceType(clang::ReferenceType *type) override;
2222

2323
bool VisitPointerType(clang::PointerType *type) override;
2424

@@ -250,6 +250,9 @@ class ConverterRefCount final : public Converter {
250250

251251
std::string GetSafeTypeAsString(clang::QualType qual_type) const;
252252

253+
bool NeedsMut(const clang::VarDecl *decl, clang::QualType type,
254+
llvm::StringRef /*name*/) const override;
255+
253256
/// The kind of conversion that should be performed.
254257
enum class ConversionKind : uint8_t {
255258
Unboxed,

0 commit comments

Comments
 (0)