Skip to content

Commit 4aff3f6

Browse files
authored
[clang][Interp] Fix assignment operator call eval order (#101845)
1 parent f2f410c commit 4aff3f6

File tree

2 files changed

+29
-18
lines changed

2 files changed

+29
-18
lines changed

clang/lib/AST/Interp/Compiler.cpp

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3977,7 +3977,19 @@ bool Compiler<Emitter>::VisitCallExpr(const CallExpr *E) {
39773977
}
39783978
}
39793979

3980-
auto Args = llvm::ArrayRef(E->getArgs(), E->getNumArgs());
3980+
SmallVector<const Expr *, 8> Args(
3981+
llvm::ArrayRef(E->getArgs(), E->getNumArgs()));
3982+
3983+
bool IsAssignmentOperatorCall = false;
3984+
if (const auto *OCE = dyn_cast<CXXOperatorCallExpr>(E);
3985+
OCE && OCE->isAssignmentOp()) {
3986+
// Just like with regular assignments, we need to special-case assignment
3987+
// operators here and evaluate the RHS (the second arg) before the LHS (the
3988+
// first arg. We fix this by using a Flip op later.
3989+
assert(Args.size() == 2);
3990+
IsAssignmentOperatorCall = true;
3991+
std::reverse(Args.begin(), Args.end());
3992+
}
39813993
// Calling a static operator will still
39823994
// pass the instance, but we don't need it.
39833995
// Discard it here.
@@ -3986,7 +3998,8 @@ bool Compiler<Emitter>::VisitCallExpr(const CallExpr *E) {
39863998
MD && MD->isStatic()) {
39873999
if (!this->discard(E->getArg(0)))
39884000
return false;
3989-
Args = Args.drop_front();
4001+
// Drop first arg.
4002+
Args.erase(Args.begin());
39904003
}
39914004
}
39924005

@@ -4038,6 +4051,15 @@ bool Compiler<Emitter>::VisitCallExpr(const CallExpr *E) {
40384051
++ArgIndex;
40394052
}
40404053

4054+
// Undo the argument reversal we did earlier.
4055+
if (IsAssignmentOperatorCall) {
4056+
assert(Args.size() == 2);
4057+
PrimType Arg1T = classify(Args[0]).value_or(PT_Ptr);
4058+
PrimType Arg2T = classify(Args[1]).value_or(PT_Ptr);
4059+
if (!this->emitFlip(Arg2T, Arg1T, E))
4060+
return false;
4061+
}
4062+
40414063
if (FuncDecl) {
40424064
const Function *Func = getFunction(FuncDecl);
40434065
if (!Func)

clang/test/AST/Interp/eval-order.cpp

Lines changed: 5 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,7 @@
11
// RUN: %clang_cc1 -std=c++1z -verify=ref,both %s -fcxx-exceptions -triple=x86_64-linux-gnu
22
// RUN: %clang_cc1 -std=c++1z -verify=expected,both %s -fcxx-exceptions -triple=x86_64-linux-gnu -fexperimental-new-constant-interpreter
33

4-
// ref-no-diagnostics
5-
6-
/// Check that assignment operators evaluate their operands right-to-left.
7-
/// Copied from test/SemaCXX/constant-expression-cxx1z.cpp
8-
///
9-
/// As you can see from the FIXME comments, some of these are not yet working correctly
10-
/// in the new interpreter.
4+
// both-no-diagnostics
115
namespace EvalOrder {
126
template<typename T> struct lvalue {
137
T t;
@@ -45,7 +39,7 @@ namespace EvalOrder {
4539
}
4640
template <typename T> constexpr T &&b(T &&v) {
4741
if (!done_a)
48-
throw "wrong"; // expected-note 3{{not valid}}
42+
throw "wrong";
4943
done_b = true;
5044
return (T &&)v;
5145
}
@@ -79,15 +73,10 @@ namespace EvalOrder {
7973

8074
// Rule 5: b = a, b @= a
8175
SEQ(B(lvalue<int>().get()) = A(0));
82-
SEQ(B(lvalue<UserDefined>().get()) = A(ud)); // expected-error {{not an integral constant expression}} FIXME \
83-
// expected-note 2{{in call to}}
76+
SEQ(B(lvalue<UserDefined>().get()) = A(ud));
8477
SEQ(B(lvalue<int>().get()) += A(0));
85-
SEQ(B(lvalue<UserDefined>().get()) += A(ud)); // expected-error {{not an integral constant expression}} FIXME \
86-
// expected-note 2{{in call to}}
87-
88-
SEQ(B(lvalue<NonMember>().get()) += A(nm)); // expected-error {{not an integral constant expression}} FIXME \
89-
// expected-note 2{{in call to}}
90-
78+
SEQ(B(lvalue<UserDefined>().get()) += A(ud));
79+
SEQ(B(lvalue<NonMember>().get()) += A(nm));
9180

9281
// Rule 6: a[b]
9382
constexpr int arr[3] = {};

0 commit comments

Comments
 (0)