Index: clang/lib/AST/Interp/ByteCodeExprGen.cpp =================================================================== --- clang/lib/AST/Interp/ByteCodeExprGen.cpp +++ clang/lib/AST/Interp/ByteCodeExprGen.cpp @@ -682,10 +682,19 @@ 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; @@ -698,7 +707,7 @@ } // Now load RHS. - if (!visit(RHS)) + if (!this->emitGetLocal(*RT, TempOffset, E)) return false; switch (E->getOpcode()) { @@ -796,18 +805,32 @@ 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. Index: clang/test/AST/Interp/floats.cpp =================================================================== --- clang/test/AST/Interp/floats.cpp +++ clang/test/AST/Interp/floats.cpp @@ -86,6 +86,18 @@ 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 { Index: clang/test/AST/Interp/literals.cpp =================================================================== --- clang/test/AST/Interp/literals.cpp +++ clang/test/AST/Interp/literals.cpp @@ -773,6 +773,17 @@ } 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