Skip to content

Commit cedabd5

Browse files
committed
Fix double evaluation of arguments by rules
Rules won't need to create temporaries by hand anymore; cpp2rust will do it if needed
1 parent 337827d commit cedabd5

198 files changed

Lines changed: 6701 additions & 5565 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: 108 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -229,10 +229,11 @@ std::string Converter::ConvertFreshObject(clang::Expr *expr) {
229229
return ConvertFreshPointer(expr);
230230
}
231231

232-
std::string Converter::ConvertPointerReceiver(clang::Expr *arg,
233-
std::string_view param_type) {
232+
std::string Converter::ConvertPointerReceiver(
233+
clang::Expr *arg, std::string_view param_type,
234+
std::optional<clang::QualType> implicit_convert_to) {
234235
auto ptr = clang::isa<clang::MaterializeTemporaryExpr>(arg->IgnoreImpCasts())
235-
? ConvertTemporaryPointer(arg, std::nullopt)
236+
? ConvertTemporaryPointer(arg, implicit_convert_to)
236237
: ConvertFreshObject(arg);
237238
return std::format("({} as {})", ptr, param_type);
238239
}
@@ -3174,6 +3175,13 @@ bool Converter::ConvertCXXOperatorCallExpr(clang::CXXOperatorCallExpr *expr) {
31743175
case clang::OverloadedOperatorKind::OO_Subscript: {
31753176
auto *base = expr->getArg(0);
31763177
auto *idx = expr->getArg(1);
3178+
// A pointer to the element is a reference to it.
3179+
std::optional<PushExprKind> place;
3180+
if (isAddrOf()) {
3181+
StrCat(token::kRef,
3182+
expr->getType().isConstQualified() ? "" : keyword_mut_);
3183+
place.emplace(*this, ExprKind::LValue);
3184+
}
31773185
// Rust borrows the container before computing the index, so an index that
31783186
// reads the container, or that has side effects through anything the
31793187
// container is reached through, is computed beforehand.
@@ -4940,15 +4948,8 @@ void Converter::ConvertCast(clang::QualType qual_type, int line) {
49404948
Converter::TempMaterializationCtx
49414949
Converter::CollectRefBindingTempArgs(clang::CallExpr *expr) {
49424950
TempMaterializationCtx ctx(expr->getNumArgs());
4943-
if (auto *fn = expr->getCalleeDecl() ? expr->getCalleeDecl()->getAsFunction()
4944-
: nullptr) {
4945-
for (unsigned i = 0; i < expr->getNumArgs() && i < fn->getNumParams();
4946-
++i) {
4947-
auto param_type = fn->getParamDecl(i)->getType();
4948-
if (NeedsRefBindingTemp(ctx_, expr->getArg(i), param_type)) {
4949-
ctx.materialized_args[i] = param_type;
4950-
}
4951-
}
4951+
for (unsigned i = 0; i < expr->getNumArgs(); ++i) {
4952+
ctx.materialized_args[i] = GetRefBindingTempType(ctx_, expr, i);
49524953
}
49534954
return ctx;
49544955
}
@@ -5034,16 +5035,19 @@ std::string Converter::ConvertPlaceholder(clang::Expr *expr, clang::Expr *arg,
50345035
bool borrowed = ph_ctx.access == TranslationRule::Access::kBorrow ||
50355036
ph_ctx.access == TranslationRule::Access::kBorrowMut;
50365037
PushExprKind push(*this, borrowed ? ExprKind::Object : ExprKind::XValue);
5037-
return ConvertPointerReceiver(arg, param_type);
5038+
return ConvertPointerReceiver(arg, param_type, ph_ctx.implicit_convert_to);
50385039
}
50395040

5041+
// A container reached through a pointer, e.g., a reference, is indexed
5042+
// through an explicit reference to it.
5043+
ScopedValue autoref(
5044+
autoref_mut_,
5045+
ph_ctx.is_index_base
5046+
? std::optional(ph_ctx.access == TranslationRule::Access::kBorrowMut)
5047+
: std::nullopt);
5048+
50405049
if (ph_ctx.needs_object_receiver()) {
50415050
Buffer buf(*this);
5042-
ScopedValue autoref(autoref_mut_,
5043-
ph_ctx.is_index_base
5044-
? std::optional(ph_ctx.access ==
5045-
TranslationRule::Access::kBorrowMut)
5046-
: std::nullopt);
50475051
PushExprKind push(*this, ExprKind::RValue);
50485052
ConvertDeref(arg);
50495053
return std::move(buf).str();
@@ -5110,10 +5114,10 @@ std::string Converter::ConvertTake(clang::Expr *arg) {
51105114

51115115
std::string Converter::ConvertMappedMethodCall(
51125116
clang::Expr *expr, const TranslationRule::MethodCallFragment &mc,
5113-
llvm::ArrayRef<clang::TemplateArgument> bindings, clang::Expr **args,
5114-
unsigned num_args, TempMaterializationCtx *ctx) {
5115-
return ConvertIRFragment(mc.receiver, bindings, expr, args, num_args, ctx) +
5116-
ConvertIRFragment(mc.body, bindings, expr, args, num_args, ctx);
5117+
llvm::ArrayRef<clang::TemplateArgument> bindings,
5118+
llvm::ArrayRef<clang::Expr *> args, TempMaterializationCtx *ctx) {
5119+
return ConvertIRFragment(mc.receiver, bindings, expr, args, ctx) +
5120+
ConvertIRFragment(mc.body, bindings, expr, args, ctx);
51175121
}
51185122

51195123
std::string Converter::GetMappedAsString(clang::Expr *expr, clang::Expr **args,
@@ -5123,11 +5127,66 @@ std::string Converter::GetMappedAsString(clang::Expr *expr, clang::Expr **args,
51235127
if (!tgt_ir)
51245128
return {};
51255129

5130+
// The arguments that the rule would not evaluate once and in order are bound
5131+
// to variables by a match. A value is bound by value, unless the rule takes a
5132+
// pointer to it, and an object by a pointer to it. The temporaries that
5133+
// objects are bound to live until the end of the statement, like those of
5134+
// the C++ arguments.
5135+
auto all_args = BuildUnifiedArgs(expr, args, num_args);
5136+
auto bind = tgt_ir->argsToBind(ctx_, expr, all_args);
5137+
std::vector<std::string> values, names;
5138+
// The variables are local to the function.
5139+
for (size_t i = 0; curr_function_ && i < all_args.size(); ++i) {
5140+
if (!bind[i]) {
5141+
continue;
5142+
}
5143+
// The rule takes the value of a transparent union.
5144+
auto *arg = IgnoreTransparentUnion(all_args[i]);
5145+
auto type = arg->getType();
5146+
bool by_value = arg->isPRValue() && !tgt_ir->takesAddress(ctx_, i, arg);
5147+
if (!by_value && arg->isPRValue()) {
5148+
arg = new (ctx_) clang::MaterializeTemporaryExpr(type, arg, false);
5149+
}
5150+
// A temporary is of the type of the parameter.
5151+
std::optional<clang::QualType> convert_to;
5152+
if (clang::isa<clang::MaterializeTemporaryExpr>(arg->IgnoreImpCasts())) {
5153+
convert_to = GetParamImplicitConvertTarget(expr, i);
5154+
type = convert_to.value_or(type);
5155+
}
5156+
auto var_type = by_value ? type : ctx_.getLValueReferenceType(type);
5157+
auto loc = arg->getExprLoc();
5158+
auto *var = clang::VarDecl::Create(
5159+
ctx_, curr_function_, loc, loc, &ctx_.Idents.get(NewTempName()),
5160+
var_type, ctx_.getTrivialTypeSourceInfo(var_type, loc), clang::SC_None);
5161+
// The temporaries of a value, e.g., borrows, end with its block.
5162+
if (by_value) {
5163+
bound_values_.insert(var);
5164+
values.push_back(std::format("{{ {} }}", ConvertFreshRValue(arg)));
5165+
} else {
5166+
PushExprKind push(*this, ExprKind::Object);
5167+
values.push_back(ConvertPointerReceiver(
5168+
arg, ToString(ctx_.getPointerType(type)), convert_to));
5169+
}
5170+
names.push_back((by_value ? "mut " : "") + GetNamedDeclAsString(var));
5171+
all_args[i] = clang::DeclRefExpr::Create(ctx_, {}, {}, var, false, loc,
5172+
type, clang::VK_LValue);
5173+
if (by_value && !type->isRecordType()) {
5174+
all_args[i] = clang::ImplicitCastExpr::Create(
5175+
ctx_, type, clang::CK_LValueToRValue, all_args[i], nullptr,
5176+
clang::VK_PRValue, clang::FPOptionsOverride());
5177+
}
5178+
// The variable is passed instead of a temporary.
5179+
if (ctx && i >= HasReceiver(expr)) {
5180+
ctx->materialized_args[i - HasReceiver(expr)].reset();
5181+
}
5182+
}
5183+
51265184
auto saved_borrows = std::exchange(mut_borrows_, {});
51275185
auto *operand = tgt_ir->refExprOperand();
51285186
bool lvalue = operand && !isAddrOf();
5129-
auto result = ConvertIRFragment(lvalue ? *operand : tgt_ir->body, bindings,
5130-
expr, args, num_args, ctx);
5187+
auto result =
5188+
ConvertIRFragment(lvalue && values.empty() ? *operand : tgt_ir->body,
5189+
bindings, expr, all_args, ctx);
51315190
if (tgt_ir->multi_statement) {
51325191
result = '{' + result + '}';
51335192
}
@@ -5136,20 +5195,25 @@ std::string Converter::GetMappedAsString(clang::Expr *expr, clang::Expr **args,
51365195
result = std::format("{}.with_mut(|{}| {})", place, name, result);
51375196
}
51385197
mut_borrows_ = std::move(saved_borrows);
5198+
// A place is reached through a reference to it, out of the match, so that it
5199+
// can be assigned.
5200+
if (!values.empty()) {
5201+
result =
5202+
std::format("match ({},) {{ ({},) => {{ {} }} }}",
5203+
llvm::join(values, ", "), llvm::join(names, ", "), result);
5204+
if (lvalue) {
5205+
result = std::format("(*{})", result);
5206+
}
5207+
}
51395208
return result;
51405209
}
51415210

51425211
std::string Converter::ConvertIRFragment(
51435212
const std::vector<TranslationRule::BodyFragment> &fragments,
51445213
llvm::ArrayRef<clang::TemplateArgument> bindings, clang::Expr *expr,
5145-
clang::Expr **args, unsigned num_args, TempMaterializationCtx *ctx) {
5214+
llvm::ArrayRef<clang::Expr *> all_args, TempMaterializationCtx *ctx) {
51465215
using namespace TranslationRule;
51475216

5148-
std::vector<clang::Expr *> all_args;
5149-
if (expr) {
5150-
all_args = BuildUnifiedArgs(expr, args, num_args);
5151-
}
5152-
51535217
std::string result;
51545218
for (auto &frag : fragments) {
51555219
if (auto *t = std::get_if<TextFragment>(&frag)) {
@@ -5191,31 +5255,27 @@ std::string Converter::ConvertIRFragment(
51915255
result += ConvertInitFragment(expr, all_args);
51925256
} else if (auto *mc =
51935257
std::get_if<std::unique_ptr<MethodCallFragment>>(&frag)) {
5194-
result +=
5195-
ConvertMappedMethodCall(expr, **mc, bindings, args, num_args, ctx);
5258+
result += ConvertMappedMethodCall(expr, **mc, bindings, all_args, ctx);
51965259
} else if (auto *re =
51975260
std::get_if<std::unique_ptr<RefExprFragment>>(&frag)) {
5198-
result +=
5199-
(*re)->prefix + ' ' +
5200-
ConvertIRFragment((*re)->inner, bindings, expr, args, num_args, ctx);
5261+
result += (*re)->prefix + ' ' +
5262+
ConvertIRFragment((*re)->inner, bindings, expr, all_args, ctx);
52015263
} else if (auto *call =
52025264
std::get_if<std::unique_ptr<GenericCallFragment>>(&frag)) {
5203-
result += ConvertGenericCall(**call, bindings, expr, args, num_args, ctx);
5265+
result += ConvertGenericCall(**call, bindings, expr, all_args, ctx);
52045266
}
52055267
}
52065268

52075269
return result;
52085270
}
52095271

5210-
std::string
5211-
Converter::ConvertGenericCall(const TranslationRule::GenericCallFragment &call,
5212-
llvm::ArrayRef<clang::TemplateArgument> bindings,
5213-
clang::Expr *expr, clang::Expr **args,
5214-
unsigned num_args, TempMaterializationCtx *ctx) {
5272+
std::string Converter::ConvertGenericCall(
5273+
const TranslationRule::GenericCallFragment &call,
5274+
llvm::ArrayRef<clang::TemplateArgument> bindings, clang::Expr *expr,
5275+
llvm::ArrayRef<clang::Expr *> args, TempMaterializationCtx *ctx) {
52155276
std::vector<std::string> call_args;
52165277
for (const auto &arg : call.args) {
5217-
call_args.push_back(
5218-
ConvertIRFragment(arg, bindings, expr, args, num_args, ctx));
5278+
call_args.push_back(ConvertIRFragment(arg, bindings, expr, args, ctx));
52195279
}
52205280
clang::QualType type;
52215281
if (call.generic <= bindings.size() &&
@@ -5272,21 +5332,21 @@ std::string Converter::DerefPtrExpr(std::string_view ptr_expr,
52725332

52735333
std::string
52745334
Converter::ConvertVariadicTail(clang::Expr *expr,
5275-
const std::vector<clang::Expr *> &all_args) {
5335+
llvm::ArrayRef<clang::Expr *> all_args) {
52765336
const auto *tgt_ir = RuleRegistry::GetExprRule(ctx_, expr);
52775337
unsigned fixed = tgt_ir ? tgt_ir->params.size() : 0;
52785338

52795339
Buffer buf(*this);
52805340
StrCat("Box::<[VaArg]>::from([");
5281-
EmitVariadicArgs(llvm::ArrayRef(all_args).drop_front(fixed),
5341+
EmitVariadicArgs(all_args.drop_front(fixed),
52825342
/*into_va_arg=*/true);
52835343
StrCat("])");
52845344
return std::move(buf).str();
52855345
}
52865346

52875347
std::string
52885348
Converter::ConvertInitFragment(clang::Expr *expr,
5289-
const std::vector<clang::Expr *> &all_args) {
5349+
llvm::ArrayRef<clang::Expr *> all_args) {
52905350
const auto *tgt_ir = RuleRegistry::GetExprRule(ctx_, expr);
52915351
if (!tgt_ir || !tgt_ir->usesInit()) {
52925352
llvm::report_fatal_error("expression rule does not use Init");
@@ -5295,9 +5355,8 @@ Converter::ConvertInitFragment(clang::Expr *expr,
52955355

52965356
Buffer buf(*this);
52975357
PushExprKind push(*this, ExprKind::RValue);
5298-
ConvertConstructFromArgs(
5299-
type, llvm::ArrayRef(all_args).drop_front(tgt_ir->params.size()),
5300-
expr->getExprLoc());
5358+
ConvertConstructFromArgs(type, all_args.drop_front(tgt_ir->params.size()),
5359+
expr->getExprLoc());
53015360
return std::move(buf).str();
53025361
}
53035362

‎cpp2rust/converter/converter.h‎

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -301,16 +301,17 @@ class Converter : public clang::RecursiveASTVisitor<Converter> {
301301
std::vector<std::string> materialized_refs_;
302302
};
303303

304+
// args are the arguments of expr, including its receiver.
304305
std::string
305306
ConvertIRFragment(const std::vector<TranslationRule::BodyFragment> &fragments,
306307
llvm::ArrayRef<clang::TemplateArgument> bindings,
307-
clang::Expr *expr, clang::Expr **args, unsigned num_args,
308+
clang::Expr *expr, llvm::ArrayRef<clang::Expr *> args,
308309
TempMaterializationCtx *ctx);
309310

310311
std::string
311312
ConvertGenericCall(const TranslationRule::GenericCallFragment &call,
312313
llvm::ArrayRef<clang::TemplateArgument> bindings,
313-
clang::Expr *expr, clang::Expr **args, unsigned num_args,
314+
clang::Expr *expr, llvm::ArrayRef<clang::Expr *> args,
314315
TempMaterializationCtx *ctx);
315316

316317
std::string ConvertConversion(clang::QualType to, clang::QualType from,
@@ -873,11 +874,15 @@ class Converter : public clang::RecursiveASTVisitor<Converter> {
873874
// closure parameter of its with_mut.
874875
std::vector<std::pair<std::string, std::string>> mut_borrows_;
875876

877+
// The variables that arguments of rules are bound to by value, whose
878+
// address is not taken.
879+
std::unordered_set<const clang::VarDecl *> bound_values_;
880+
876881
std::string ConvertVariadicTail(clang::Expr *expr,
877-
const std::vector<clang::Expr *> &all_args);
882+
llvm::ArrayRef<clang::Expr *> all_args);
878883

879884
std::string ConvertInitFragment(clang::Expr *expr,
880-
const std::vector<clang::Expr *> &all_args);
885+
llvm::ArrayRef<clang::Expr *> all_args);
881886

882887
void ConvertConstructFromArgs(clang::QualType type,
883888
llvm::ArrayRef<clang::Expr *> args,
@@ -890,8 +895,8 @@ class Converter : public clang::RecursiveASTVisitor<Converter> {
890895

891896
virtual std::string ConvertMappedMethodCall(
892897
clang::Expr *expr, const TranslationRule::MethodCallFragment &mc,
893-
llvm::ArrayRef<clang::TemplateArgument> bindings, clang::Expr **args,
894-
unsigned num_args, TempMaterializationCtx *ctx);
898+
llvm::ArrayRef<clang::TemplateArgument> bindings,
899+
llvm::ArrayRef<clang::Expr *> args, TempMaterializationCtx *ctx);
895900

896901
virtual std::string AccessLValueObject(clang::MemberExpr *member);
897902

@@ -1160,8 +1165,11 @@ class Converter : public clang::RecursiveASTVisitor<Converter> {
11601165
std::optional<clang::QualType> implicit_convert_to = {});
11611166
virtual std::string ConvertFreshObject(clang::Expr *expr);
11621167

1163-
virtual std::string ConvertPointerReceiver(clang::Expr *arg,
1164-
std::string_view param_type);
1168+
// A pointer of param_type to arg, which is converted to implicit_convert_to
1169+
// if it is a temporary.
1170+
virtual std::string
1171+
ConvertPointerReceiver(clang::Expr *arg, std::string_view param_type,
1172+
std::optional<clang::QualType> implicit_convert_to);
11651173

11661174
// A pointer to the elements of the container arg.
11671175
virtual std::string ConvertElementPtr(clang::Expr *arg) {

‎cpp2rust/converter/converter_lib.cpp‎

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2276,6 +2276,32 @@ bool NeedsRefBindingTemp(clang::ASTContext &ctx, const clang::Expr *arg,
22762276
param_type.getNonReferenceType());
22772277
}
22782278

2279+
std::optional<clang::QualType>
2280+
GetRefBindingTempType(clang::ASTContext &ctx, const clang::CallExpr *call,
2281+
unsigned i) {
2282+
auto *decl = call->getCalleeDecl();
2283+
auto *fn = decl ? decl->getAsFunction() : nullptr;
2284+
if (!fn || i >= call->getNumArgs() || i >= fn->getNumParams()) {
2285+
return std::nullopt;
2286+
}
2287+
auto param_type = fn->getParamDecl(i)->getType();
2288+
if (!NeedsRefBindingTemp(ctx, call->getArg(i), param_type)) {
2289+
return std::nullopt;
2290+
}
2291+
return param_type;
2292+
}
2293+
2294+
clang::Expr *IgnoreTransparentUnion(clang::Expr *expr) {
2295+
auto *literal = clang::dyn_cast<clang::CompoundLiteralExpr>(expr);
2296+
auto *record = literal ? literal->getType()->getAsRecordDecl() : nullptr;
2297+
if (!record || !record->hasAttr<clang::TransparentUnionAttr>()) {
2298+
return expr;
2299+
}
2300+
auto *init = clang::cast<clang::InitListExpr>(literal->getInitializer());
2301+
assert(init->getNumInits() == 1);
2302+
return init->getInit(0);
2303+
}
2304+
22792305
bool IsSizeType(clang::ASTContext &ctx, clang::QualType type) {
22802306
auto rust_type = Mapper::Map(ctx, type);
22812307
return rust_type == "usize" || rust_type == "isize";

‎cpp2rust/converter/converter_lib.h‎

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -363,6 +363,15 @@ bool NeedsImplicitPointeeCast(clang::ASTContext &ctx, const clang::Expr *from,
363363
bool NeedsRefBindingTemp(clang::ASTContext &ctx, const clang::Expr *arg,
364364
clang::QualType param_type);
365365

366+
// The type of the reference parameter that argument i of call is bound to
367+
// through a temporary, if it is.
368+
std::optional<clang::QualType>
369+
GetRefBindingTempType(clang::ASTContext &ctx, const clang::CallExpr *call,
370+
unsigned i);
371+
372+
// The value that a transparent union argument is initialized with, or expr.
373+
clang::Expr *IgnoreTransparentUnion(clang::Expr *expr);
374+
366375
bool IsSizeType(clang::ASTContext &ctx, clang::QualType type);
367376

368377
std::optional<clang::QualType> GetOperandImplicitConversionTarget(

‎cpp2rust/converter/mapper.cpp‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -62,8 +62,8 @@ std::string MapInitializer(Converter &converter, clang::QualType qual_type) {
6262
auto [rule, subs] =
6363
RuleRegistry::Search(converter.GetASTContext(), qual_type);
6464
if (rule && !rule->initializer.empty()) {
65-
return converter.ConvertIRFragment(rule->initializer, subs, nullptr,
66-
nullptr, 0, nullptr);
65+
return converter.ConvertIRFragment(rule->initializer, subs, nullptr, {},
66+
nullptr);
6767
}
6868
return {};
6969
}

0 commit comments

Comments
 (0)