diff --git a/clang/lib/AST/Interp/ByteCodeExprGen.h b/clang/lib/AST/Interp/ByteCodeExprGen.h --- a/clang/lib/AST/Interp/ByteCodeExprGen.h +++ b/clang/lib/AST/Interp/ByteCodeExprGen.h @@ -85,6 +85,7 @@ bool VisitAbstractConditionalOperator(const AbstractConditionalOperator *E); bool VisitStringLiteral(const StringLiteral *E); bool VisitCharacterLiteral(const CharacterLiteral *E); + bool VisitCompoundAssignOperator(const CompoundAssignOperator *E); protected: bool visitExpr(const Expr *E) override; diff --git a/clang/lib/AST/Interp/ByteCodeExprGen.cpp b/clang/lib/AST/Interp/ByteCodeExprGen.cpp --- a/clang/lib/AST/Interp/ByteCodeExprGen.cpp +++ b/clang/lib/AST/Interp/ByteCodeExprGen.cpp @@ -414,6 +414,57 @@ return this->emitConst(E, E->getValue()); } +template +bool ByteCodeExprGen::VisitCompoundAssignOperator( + const CompoundAssignOperator *E) { + const Expr *LHS = E->getLHS(); + const Expr *RHS = E->getRHS(); + Optional LT = classify(E->getLHS()->getType()); + Optional RT = classify(E->getRHS()->getType()); + + if (!LT || !RT) + return false; + + assert(!E->getType()->isPointerType() && + "Support pointer arithmethic in compound assignment operators"); + + // Get LHS pointer, load its value and get RHS value. + if (!visit(LHS)) + return false; + if (!this->emitLoad(*LT, E)) + return false; + if (!visit(RHS)) + return false; + + // Perform operation. + switch (E->getOpcode()) { + case BO_AddAssign: + if (!this->emitAdd(*LT, E)) + return false; + break; + case BO_SubAssign: + if (!this->emitSub(*LT, E)) + return false; + break; + + case BO_MulAssign: + case BO_DivAssign: + case BO_RemAssign: + case BO_ShlAssign: + case BO_ShrAssign: + case BO_AndAssign: + case BO_XorAssign: + case BO_OrAssign: + default: + llvm_unreachable("Unimplemented compound assign operator"); + } + + // And store the result in LHS. + if (DiscardResult) + return this->emitStorePop(*LT, E); + return this->emitStore(*LT, E); +} + template bool ByteCodeExprGen::discard(const Expr *E) { OptionScope Scope(this, /*NewDiscardResult=*/true); return this->Visit(E); diff --git a/clang/test/AST/Interp/literals.cpp b/clang/test/AST/Interp/literals.cpp --- a/clang/test/AST/Interp/literals.cpp +++ b/clang/test/AST/Interp/literals.cpp @@ -415,5 +415,43 @@ // expected-note {{in call to 'UnderFlow()'}} \ // ref-error {{not an integral constant expression}} \ // ref-note {{in call to 'UnderFlow()'}} + + constexpr int getTwo() { + int i = 1; + return (i += 1); + } + static_assert(getTwo() == 2, ""); + + constexpr int sub(int a) { + return (a -= 2); + } + static_assert(sub(7) == 5, ""); + + constexpr int add(int a, int b) { + a += b; // expected-note {{is outside the range of representable values}} \ + // ref-note {{is outside the range of representable values}} + return a; + } + static_assert(add(1, 2) == 3, ""); + static_assert(add(INT_MAX, 1) == 0, ""); // expected-error {{not an integral constant expression}} \ + // expected-note {{in call to 'add}} \ + // ref-error {{not an integral constant expression}} \ + // ref-note {{in call to 'add}} + + constexpr int sub(int a, int b) { + a -= b; // expected-note {{is outside the range of representable values}} \ + // ref-note {{is outside the range of representable values}} + return a; + } + static_assert(sub(10, 20) == -10, ""); + static_assert(sub(INT_MIN, 1) == 0, ""); // expected-error {{not an integral constant expression}} \ + // expected-note {{in call to 'sub}} \ + // ref-error {{not an integral constant expression}} \ + // ref-note {{in call to 'sub}} + + constexpr int subAll(int a) { + return (a -= a); + } + static_assert(subAll(213) == 0, ""); }; #endif