Skip to content

Commit d2eeabe

Browse files
committed
Fix copying in std::sort, std::max and friends
1 parent e47d36f commit d2eeabe

117 files changed

Lines changed: 3478 additions & 1935 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: 105 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1803,13 +1803,17 @@ void Converter::EmitCallResult(clang::CallExpr *expr, std::string str,
18031803
(temps && IsReferenceType(expr))) &&
18041804
!isAddrOf() && !isVoid();
18051805
// The dereference is in the block of the temporaries, so they are still
1806-
// alive.
1806+
// alive, as is the read of a field of the result.
18071807
if (deref && !(temps && Mapper::IsRefExpr(ctx_, expr))) {
18081808
Buffer buf(*this);
18091809
EmitDeref(std::move(str), expr->getType());
18101810
str = std::move(buf).str();
18111811
}
1812-
StrCat(temps ? temps->Wrap(std::move(str)) : str);
1812+
if (deref && DeferTemporaries(expr, temps)) {
1813+
StrCat(str);
1814+
} else {
1815+
StrCat(temps ? temps->Wrap(std::move(str)) : str);
1816+
}
18131817
if (deref) {
18141818
SetValueFreshness(expr->getType());
18151819
} else if (!temps || !IsPassThroughRule(expr)) {
@@ -3296,6 +3300,26 @@ void Converter::ConvertMemberExpr(clang::MemberExpr *expr) {
32963300
return;
32973301
}
32983302

3303+
// A field of the result of a call is read in the block of the temporaries
3304+
// of the arguments of the call.
3305+
if (auto *call = GetFieldReadCall(expr);
3306+
call && (!field_read_call_ || field_read_call_->call != call)) {
3307+
FieldReadCall read{call, ""};
3308+
std::string str;
3309+
{
3310+
ScopedValue push_read(field_read_call_, &read);
3311+
Buffer buf(*this);
3312+
ConvertMemberExpr(expr);
3313+
str = std::move(buf).str();
3314+
}
3315+
if (read.temporary_bindings.empty()) {
3316+
StrCat(str);
3317+
} else {
3318+
StrCat(std::format("{{ {} {} }}", read.temporary_bindings, str));
3319+
}
3320+
return;
3321+
}
3322+
32993323
auto *member = expr->getMemberDecl();
33003324
auto *base = expr->getBase();
33013325
bool base_is_this = clang::isa<clang::CXXThisExpr>(base->IgnoreCasts()) &&
@@ -3319,6 +3343,27 @@ void Converter::ConvertMemberExpr(clang::MemberExpr *expr) {
33193343
}
33203344
}
33213345

3346+
bool Converter::DeferTemporaries(const clang::Expr *call,
3347+
const TempMaterializationCtx *temps) {
3348+
if (!temps || !field_read_call_ || field_read_call_->call != call) {
3349+
return false;
3350+
}
3351+
field_read_call_->temporary_bindings = temps->temporary_bindings;
3352+
return true;
3353+
}
3354+
3355+
// The call whose result expr reads a field of, through fields of fields.
3356+
const clang::Expr *Converter::GetFieldReadCall(const clang::MemberExpr *expr) {
3357+
const clang::Expr *base = expr;
3358+
while (auto *member = clang::dyn_cast<clang::MemberExpr>(base)) {
3359+
if (member->isArrow()) {
3360+
return nullptr;
3361+
}
3362+
base = member->getBase()->IgnoreParenImpCasts();
3363+
}
3364+
return clang::isa<clang::CallExpr>(base) ? base : nullptr;
3365+
}
3366+
33223367
bool Converter::VisitCXXThisExpr(clang::CXXThisExpr *expr) {
33233368
if (auto field = AsLambdaCaptureThis(curr_function_)) {
33243369
auto name = GetNamedDeclAsString(field);
@@ -3338,21 +3383,37 @@ bool Converter::VisitCXXThisExpr(clang::CXXThisExpr *expr) {
33383383
}
33393384

33403385
bool Converter::VisitOpaqueValueExpr(clang::OpaqueValueExpr *expr) {
3386+
// The common expression of an array init loop uses the indices of the
3387+
// loops outside it.
3388+
auto outer = array_init_outer_depth_.find(expr);
3389+
if (outer == array_init_outer_depth_.end()) {
3390+
Convert(expr->getSourceExpr());
3391+
return false;
3392+
}
3393+
auto depth = std::exchange(array_init_depth_, outer->second);
33413394
Convert(expr->getSourceExpr());
3395+
array_init_depth_ = depth;
33423396
return false;
33433397
}
33443398

3399+
std::string Converter::ArrayInitIndexName() const {
3400+
return array_init_depth_ == 1 ? "__i"
3401+
: std::format("__i{}", array_init_depth_);
3402+
}
3403+
33453404
bool Converter::VisitArrayInitIndexExpr(clang::ArrayInitIndexExpr *expr) {
3346-
EmitFresh("__i");
3405+
EmitFresh(ArrayInitIndexName());
33473406
return false;
33483407
}
33493408

33503409
bool Converter::VisitArrayInitLoopExpr(clang::ArrayInitLoopExpr *expr) {
33513410
StrCat(std::format("std::array::from_fn::<_, {}, _>",
33523411
GetArraySize(expr->getType())));
33533412
PushParen paren(*this);
3354-
StrCat("|__i: usize|");
3413+
array_init_outer_depth_[expr->getCommonExpr()] = array_init_depth_++;
3414+
StrCat(std::format("|{}: usize|", ArrayInitIndexName()));
33553415
ConvertVarInit(expr->getSubExpr()->getType(), expr->getSubExpr());
3416+
--array_init_depth_;
33563417
MarkFresh();
33573418
return false;
33583419
}
@@ -4466,19 +4527,15 @@ std::string Converter::GetComparisonCall(const clang::FunctionDecl *op,
44664527
const clang::CXXRecordDecl *decl,
44674528
std::string_view lhs,
44684529
std::string_view rhs) {
4469-
auto arg = [&](unsigned i, std::string_view value) {
4470-
if (op->getParamDecl(i)->getType()->isReferenceType()) {
4471-
return GetComparisonReferenceArg(decl, value);
4472-
}
4473-
return std::format("{}.clone()", value);
4474-
};
44754530
if (const auto *method = clang::dyn_cast<clang::CXXMethodDecl>(op)) {
44764531
return std::format("{}::{}({}, {})", GetUFCSName(method),
44774532
GetMethodName(method),
4478-
GetComparisonReceiver(method, decl, lhs), arg(0, rhs));
4533+
GetComparisonReceiver(method, lhs),
4534+
GetComparisonArg(op->getParamDecl(0), decl, rhs));
44794535
}
44804536
return std::format("{}({}, {})", GetNamedDeclAsString(op->getCanonicalDecl()),
4481-
arg(0, lhs), arg(1, rhs));
4537+
GetComparisonArg(op->getParamDecl(0), decl, lhs),
4538+
GetComparisonArg(op->getParamDecl(1), decl, rhs));
44824539
}
44834540

44844541
std::string Converter::GetComparisonBool(const clang::FunctionDecl *op,
@@ -4499,47 +4556,62 @@ std::string Converter::GetComparisonBool(const clang::FunctionDecl *op,
44994556
for (const auto *found : record->getVisibleConversionFunctions()) {
45004557
const auto *conversion = clang::dyn_cast<clang::CXXConversionDecl>(found);
45014558
if (conversion && conversion->getConversionType()->isBooleanType()) {
4502-
auto receiver =
4503-
returns_ref
4504-
? GetComparisonReferenceReceiver(conversion, record, "__cmp")
4505-
: GetComparisonReceiver(conversion, record, "__cmp");
4506-
return std::format("{{ let __cmp = {}{}; {}::{}({}) }}",
4507-
returns_ref ? "" : "&", call, GetUFCSName(conversion),
4508-
GetMethodName(conversion), receiver);
4559+
return std::format(
4560+
"{{ let __cmp = {}; {}::{}({}) }}", call, GetUFCSName(conversion),
4561+
GetMethodName(conversion),
4562+
GetComparisonResultReceiver(conversion, returns_ref, "__cmp"));
45094563
}
45104564
}
45114565
assert(0 && "comparison operator result has no conversion to bool");
45124566
return call;
45134567
}
45144568

4515-
std::string
4516-
Converter::GetComparisonReferenceArg(const clang::CXXRecordDecl *decl,
4517-
std::string_view value) {
4518-
return std::format("{} as *const {}", value, GetRecordName(decl));
4569+
std::string Converter::GetComparisonArg(const clang::ParmVarDecl *param,
4570+
const clang::CXXRecordDecl *decl,
4571+
std::string_view value) {
4572+
if (param->getType()->isReferenceType()) {
4573+
return std::format("{} as *const {}", value, GetRecordName(decl));
4574+
}
4575+
return std::format("{}.clone()", value);
45194576
}
45204577

45214578
std::string
4522-
Converter::GetComparisonReferenceReceiver(const clang::CXXMethodDecl *method,
4523-
const clang::CXXRecordDecl *decl,
4524-
std::string_view ptr) {
4525-
return GetComparisonReceiver(method, decl, std::format("&*{}", ptr));
4579+
Converter::GetComparisonResultReceiver(const clang::CXXMethodDecl *method,
4580+
bool returns_ref,
4581+
std::string_view result) {
4582+
return GetComparisonReceiver(
4583+
method, std::format("{}{}", returns_ref ? "&*" : "&", result));
45264584
}
45274585

45284586
std::string Converter::GetComparisonReceiver(const clang::CXXMethodDecl *method,
4529-
const clang::CXXRecordDecl *,
45304587
std::string_view lhs) {
45314588
if (!MethodNeedsMutableReceiver(method)) {
45324589
return std::string(lhs);
45334590
}
45344591
return std::format("&mut *(&raw const *{}).cast_mut()", lhs);
45354592
}
45364593

4594+
std::string Converter::GetEqBody(const clang::CXXRecordDecl *decl,
4595+
const clang::FunctionDecl *eq,
4596+
const clang::FunctionDecl *lt,
4597+
const clang::FunctionDecl *cmp,
4598+
std::string_view lhs, std::string_view rhs) {
4599+
if (eq) {
4600+
return GetComparisonBool(eq, decl, lhs, rhs);
4601+
}
4602+
if (cmp) {
4603+
return std::format("{} == std::cmp::Ordering::Equal",
4604+
GetComparisonCall(cmp, decl, lhs, rhs));
4605+
}
4606+
return std::format("!({}) && !({})", GetComparisonBool(lt, decl, lhs, rhs),
4607+
GetComparisonBool(lt, decl, rhs, lhs));
4608+
}
4609+
45374610
void Converter::ConvertOrdAndPartialOrdTraits(const clang::CXXRecordDecl *decl,
45384611
const clang::FunctionDecl *eq,
45394612
const clang::FunctionDecl *lt,
45404613
const clang::FunctionDecl *cmp) {
4541-
std::string cmp_body, eq_body;
4542-
4614+
std::string cmp_body;
45434615
if (cmp) {
45444616
cmp_body = GetComparisonCall(cmp, decl, "self", "other");
45454617
} else if (lt) {
@@ -4549,19 +4621,9 @@ void Converter::ConvertOrdAndPartialOrdTraits(const clang::CXXRecordDecl *decl,
45494621
GetComparisonBool(lt, decl, "self", "other"),
45504622
GetComparisonBool(lt, decl, "other", "self"));
45514623
}
4552-
4553-
if (eq) {
4554-
eq_body = GetComparisonBool(eq, decl, "self", "other");
4555-
} else if (cmp) {
4556-
eq_body = std::format("{} == std::cmp::Ordering::Equal",
4557-
GetComparisonCall(cmp, decl, "self", "other"));
4558-
} else {
4559-
eq_body = std::format("!({}) && !({})",
4560-
GetComparisonBool(lt, decl, "self", "other"),
4561-
GetComparisonBool(lt, decl, "other", "self"));
4562-
}
4563-
4564-
ConvertOrdAndPartialOrdTraitsBase(cmp_body, eq_body, GetRecordName(decl));
4624+
ConvertOrdAndPartialOrdTraitsBase(
4625+
cmp_body, GetEqBody(decl, eq, lt, cmp, "self", "other"),
4626+
GetRecordName(decl));
45654627
}
45664628

45674629
void Converter::AddOrdTrait(const clang::CXXRecordDecl *decl) {

‎cpp2rust/converter/converter.h‎

Lines changed: 41 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -510,6 +510,8 @@ class Converter : public clang::RecursiveASTVisitor<Converter> {
510510

511511
void ConvertMemberExpr(clang::MemberExpr *expr);
512512

513+
static const clang::Expr *GetFieldReadCall(const clang::MemberExpr *expr);
514+
513515
virtual bool VisitMemberExpr(clang::MemberExpr *expr);
514516

515517
virtual bool VisitCXXThisExpr(clang::CXXThisExpr *expr);
@@ -759,10 +761,16 @@ class Converter : public clang::RecursiveASTVisitor<Converter> {
759761

760762
void AddOrdTrait(const clang::CXXRecordDecl *decl);
761763

762-
void ConvertOrdAndPartialOrdTraits(const clang::CXXRecordDecl *decl,
763-
const clang::FunctionDecl *eq,
764-
const clang::FunctionDecl *lt,
765-
const clang::FunctionDecl *cmp);
764+
virtual void ConvertOrdAndPartialOrdTraits(const clang::CXXRecordDecl *decl,
765+
const clang::FunctionDecl *eq,
766+
const clang::FunctionDecl *lt,
767+
const clang::FunctionDecl *cmp);
768+
769+
std::string GetEqBody(const clang::CXXRecordDecl *decl,
770+
const clang::FunctionDecl *eq,
771+
const clang::FunctionDecl *lt,
772+
const clang::FunctionDecl *cmp, std::string_view lhs,
773+
std::string_view rhs);
766774

767775
void ConvertOrdAndPartialOrdTraitsBase(std::string_view cmp_body,
768776
std::string_view eq_body,
@@ -776,17 +784,17 @@ class Converter : public clang::RecursiveASTVisitor<Converter> {
776784
const clang::CXXRecordDecl *decl,
777785
std::string_view lhs, std::string_view rhs);
778786

779-
virtual std::string
780-
GetComparisonReferenceArg(const clang::CXXRecordDecl *decl,
781-
std::string_view value);
787+
// The argument for param of a comparison operator of decl, from an operand
788+
// of the comparison.
789+
virtual std::string GetComparisonArg(const clang::ParmVarDecl *param,
790+
const clang::CXXRecordDecl *decl,
791+
std::string_view value);
782792

783793
virtual std::string
784-
GetComparisonReferenceReceiver(const clang::CXXMethodDecl *method,
785-
const clang::CXXRecordDecl *decl,
786-
std::string_view ptr);
794+
GetComparisonResultReceiver(const clang::CXXMethodDecl *method,
795+
bool returns_ref, std::string_view result);
787796

788797
virtual std::string GetComparisonReceiver(const clang::CXXMethodDecl *method,
789-
const clang::CXXRecordDecl *decl,
790798
std::string_view lhs);
791799

792800
virtual void AddCloneTrait(const clang::RecordDecl *decl);
@@ -928,6 +936,28 @@ class Converter : public clang::RecursiveASTVisitor<Converter> {
928936
// use the variables.
929937
std::unordered_set<const clang::VarDecl *> hoisted_decls_;
930938

939+
// A call whose result a field is read from, e.g., `f(g()).x`, and the
940+
// bindings of the temporaries of its arguments, which the read is in the
941+
// block of, so that they are alive during it.
942+
struct FieldReadCall {
943+
const clang::Expr *call;
944+
std::string temporary_bindings;
945+
};
946+
FieldReadCall *field_read_call_ = nullptr;
947+
// Whether the bindings of temps, of the arguments of call, are deferred to
948+
// the read of a field of its result.
949+
bool DeferTemporaries(const clang::Expr *call,
950+
const TempMaterializationCtx *temps);
951+
952+
// The number of array init loops being converted, which copy arrays
953+
// element by element, e.g., in implicit copy constructors, and the number
954+
// of loops outside each of them, whose indices their common expressions
955+
// use.
956+
unsigned array_init_depth_ = 0;
957+
std::unordered_map<const clang::OpaqueValueExpr *, unsigned>
958+
array_init_outer_depth_;
959+
std::string ArrayInitIndexName() const;
960+
931961
unsigned materialized_temp_id_ = 0;
932962
std::string NewTempName() {
933963
return std::format("__tmp_{}", materialized_temp_id_++);

0 commit comments

Comments
 (0)