diff --git a/clang/lib/AST/Interp/ByteCodeExprGen.cpp b/clang/lib/AST/Interp/ByteCodeExprGen.cpp index ad838755c47a5..b5df00e245aaf 100644 --- a/clang/lib/AST/Interp/ByteCodeExprGen.cpp +++ b/clang/lib/AST/Interp/ByteCodeExprGen.cpp @@ -620,10 +620,19 @@ bool ByteCodeExprGen::VisitFloatCompoundAssignOperator( if (!LT || !RT) return false; + // C++17 onwards require that we evaluate the RHS first. + // Compute RHS and save it in a temporary variable so we can + // load it again later. + if (!visit(RHS)) + return false; + + unsigned TempOffset = this->allocateLocalPrimitive(E, *RT, /*IsConst=*/true); + if (!this->emitSetLocal(*RT, TempOffset, E)) + return false; + // First, visit LHS. if (!visit(LHS)) return false; - if (!this->emitLoad(*LT, E)) return false; @@ -636,7 +645,7 @@ bool ByteCodeExprGen::VisitFloatCompoundAssignOperator( } // Now load RHS. - if (!visit(RHS)) + if (!this->emitGetLocal(*RT, TempOffset, E)) return false; switch (E->getOpcode()) { @@ -734,18 +743,32 @@ bool ByteCodeExprGen::VisitCompoundAssignOperator( assert(!E->getType()->isPointerType() && "Handled above"); assert(!E->getType()->isFloatingType() && "Handled above"); - // Get LHS pointer, load its value and get RHS value. + // C++17 onwards require that we evaluate the RHS first. + // Compute RHS and save it in a temporary variable so we can + // load it again later. + // FIXME: Compound assignments are unsequenced in C, so we might + // have to figure out how to reject them. + if (!visit(RHS)) + return false; + + unsigned TempOffset = this->allocateLocalPrimitive(E, *RT, /*IsConst=*/true); + + if (!this->emitSetLocal(*RT, TempOffset, E)) + return false; + + // Get LHS pointer, load its value and cast it to the + // computation type if necessary. if (!visit(LHS)) return false; if (!this->emitLoad(*LT, E)) return false; - // If necessary, cast LHS to its computation type. if (*LT != *LHSComputationT) { if (!this->emitCast(*LT, *LHSComputationT, E)) return false; } - if (!visit(RHS)) + // Get the RHS value on the stack. + if (!this->emitGetLocal(*RT, TempOffset, E)) return false; // Perform operation. diff --git a/clang/test/AST/Interp/floats.cpp b/clang/test/AST/Interp/floats.cpp index fa1f52317988d..05081c7516877 100644 --- a/clang/test/AST/Interp/floats.cpp +++ b/clang/test/AST/Interp/floats.cpp @@ -86,6 +86,18 @@ namespace compound { return f; } static_assert(f2() == __FLT_MAX__, ""); + + constexpr float ff() { + float a[] = {1,2}; + int i = 0; + + // RHS should be evaluated before LHS, so this should + // write to a[1]; + a[i++] += ++i; + + return a[1]; + } + static_assert(ff() == 3, ""); } namespace unary { diff --git a/clang/test/AST/Interp/literals.cpp b/clang/test/AST/Interp/literals.cpp index 882742b2314b4..b9d1d2d59d11a 100644 --- a/clang/test/AST/Interp/literals.cpp +++ b/clang/test/AST/Interp/literals.cpp @@ -764,6 +764,17 @@ namespace IncDec { } static_assert(bug1Dec() == 3); + constexpr int f() { + int a[] = {1,2}; + int i = 0; + + // RHS should be evaluated before LHS, so this should + // write to a[1]; + a[i++] += ++i; + + return a[1]; + } + static_assert(f() == 3, ""); }; #endif