Index: clang/lib/AST/Interp/ByteCodeExprGen.h =================================================================== --- clang/lib/AST/Interp/ByteCodeExprGen.h +++ clang/lib/AST/Interp/ByteCodeExprGen.h @@ -61,6 +61,7 @@ bool VisitFloatingLiteral(const FloatingLiteral *E); bool VisitParenExpr(const ParenExpr *E); bool VisitBinaryOperator(const BinaryOperator *E); + bool VisitLogicalBinOp(const BinaryOperator *E); bool VisitPointerArithBinOp(const BinaryOperator *E); bool VisitCXXDefaultArgExpr(const CXXDefaultArgExpr *E); bool VisitCallExpr(const CallExpr *E); Index: clang/lib/AST/Interp/ByteCodeExprGen.cpp =================================================================== --- clang/lib/AST/Interp/ByteCodeExprGen.cpp +++ clang/lib/AST/Interp/ByteCodeExprGen.cpp @@ -203,6 +203,10 @@ template bool ByteCodeExprGen::VisitBinaryOperator(const BinaryOperator *BO) { + // Need short-circuiting for these. + if (BO->isLogicalOp()) + return this->VisitLogicalBinOp(BO); + const Expr *LHS = BO->getLHS(); const Expr *RHS = BO->getRHS(); @@ -286,8 +290,9 @@ return Discard(this->emitShr(*LT, *RT, BO)); case BO_Xor: return Discard(this->emitBitXor(*T, BO)); - case BO_LAnd: case BO_LOr: + case BO_LAnd: + llvm_unreachable("Already handled earlier"); default: return this->bail(BO); } @@ -345,6 +350,65 @@ return this->bail(E); } +template +bool ByteCodeExprGen::VisitLogicalBinOp(const BinaryOperator *E) { + assert(E->isLogicalOp()); + BinaryOperatorKind Op = E->getOpcode(); + const Expr *LHS = E->getLHS(); + const Expr *RHS = E->getRHS(); + + if (Op == BO_LOr) { + // Logical OR. Visit LHS and only evaluate RHS if LHS was FALSE. + LabelTy LabelTrue = this->getLabel(); + LabelTy LabelEnd = this->getLabel(); + + if (!this->visit(LHS)) + return false; + if (!this->jumpTrue(LabelTrue)) + return false; + + if (!this->visit(RHS)) + return false; + if (!this->jump(LabelEnd)) + return false; + + this->emitLabel(LabelTrue); + this->emitConstBool(true, E); + this->fallthrough(LabelEnd); + this->emitLabel(LabelEnd); + + if (DiscardResult) + return this->emitPopBool(E); + + return true; + } + + // Logical AND. + // Visit LHS. Only visit RHS is LHS was TRUE. + LabelTy LabelFalse = this->getLabel(); + LabelTy LabelEnd = this->getLabel(); + + if (!this->visit(LHS)) + return false; + if (!this->jumpFalse(LabelFalse)) + return false; + + if (!this->visit(RHS)) + return false; + if (!this->jump(LabelEnd)) + return false; + + this->emitLabel(LabelFalse); + this->emitConstBool(false, E); + this->fallthrough(LabelEnd); + this->emitLabel(LabelEnd); + + if (DiscardResult) + return this->emitPopBool(E); + + return true; +} + template bool ByteCodeExprGen::VisitImplicitValueInitExpr(const ImplicitValueInitExpr *E) { std::optional T = classify(E); Index: clang/test/AST/Interp/cond.cpp =================================================================== --- clang/test/AST/Interp/cond.cpp +++ clang/test/AST/Interp/cond.cpp @@ -9,3 +9,29 @@ return a - b; } } + +constexpr int dontCallMe(unsigned m) { + if (m == 0) return 0; + return dontCallMe(m - 2); +} + +// Can't call this because it will run into infinite recursion. +constexpr int assertNotReached() { + return dontCallMe(3); +} + +static_assert(true || true, ""); +static_assert(true || false, ""); +static_assert(false || true, ""); +static_assert(!(false || false), ""); + +static_assert(true || assertNotReached(), ""); +static_assert(true || true || true || false, ""); + +static_assert(true && true, ""); +static_assert(!(true && false), ""); +static_assert(!(false && true), ""); +static_assert(!(false && false), ""); + +static_assert(!(false && assertNotReached()), ""); +static_assert(!(true && true && true && false), "");