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 @@ -181,6 +181,9 @@ return this->emitPopPtr(I); } + bool visitConditional(const AbstractConditionalOperator *E, + llvm::function_ref V); + /// Creates a local primitive value. unsigned allocateLocalPrimitive(DeclTy &&Decl, PrimType Ty, bool IsMutable, bool IsExtended = false); 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 @@ -560,32 +560,8 @@ template bool ByteCodeExprGen::VisitAbstractConditionalOperator( const AbstractConditionalOperator *E) { - const Expr *Condition = E->getCond(); - const Expr *TrueExpr = E->getTrueExpr(); - const Expr *FalseExpr = E->getFalseExpr(); - - LabelTy LabelEnd = this->getLabel(); // Label after the operator. - LabelTy LabelFalse = this->getLabel(); // Label for the false expr. - - if (!this->visit(Condition)) - return false; - if (!this->jumpFalse(LabelFalse)) - return false; - - if (!this->visit(TrueExpr)) - return false; - if (!this->jump(LabelEnd)) - return false; - - this->emitLabel(LabelFalse); - - if (!this->visit(FalseExpr)) - return false; - - this->fallthrough(LabelEnd); - this->emitLabel(LabelEnd); - - return true; + return this->visitConditional( + E, [this](const Expr *E) { return this->visit(E); }); } template @@ -921,6 +897,41 @@ } } +/// Visit a conditional operator, i.e. `A ? B : C`. +/// \V determines what function to call for the B and C expressions. +template +bool ByteCodeExprGen::visitConditional( + const AbstractConditionalOperator *E, + llvm::function_ref V) { + + const Expr *Condition = E->getCond(); + const Expr *TrueExpr = E->getTrueExpr(); + const Expr *FalseExpr = E->getFalseExpr(); + + LabelTy LabelEnd = this->getLabel(); // Label after the operator. + LabelTy LabelFalse = this->getLabel(); // Label for the false expr. + + if (!this->visit(Condition)) + return false; + if (!this->jumpFalse(LabelFalse)) + return false; + + if (!V(TrueExpr)) + return false; + if (!this->jump(LabelEnd)) + return false; + + this->emitLabel(LabelFalse); + + if (!V(FalseExpr)) + return false; + + this->fallthrough(LabelEnd); + this->emitLabel(LabelEnd); + + return true; +} + template bool ByteCodeExprGen::visitZeroInitializer(PrimType T, const Expr *E) { switch (T) { @@ -1429,6 +1440,10 @@ return this->visitInitializer(CE->getSubExpr()); } else if (const auto *CE = dyn_cast(Initializer)) { return this->visitInitializer(CE->getSubExpr()); + } else if (const auto *ACO = + dyn_cast(Initializer)) { + return this->visitConditional( + ACO, [this](const Expr *E) { return this->visitRecordInitializer(E); }); } return false; diff --git a/clang/test/AST/Interp/constexpr-nqueens.cpp b/clang/test/AST/Interp/constexpr-nqueens.cpp --- a/clang/test/AST/Interp/constexpr-nqueens.cpp +++ b/clang/test/AST/Interp/constexpr-nqueens.cpp @@ -17,7 +17,8 @@ constexpr Board(uint64_t State, bool Failed = false) : Failed(Failed) {} constexpr Board addQueen(int Row, int Col) const { - return Board(State | ((uint64_t)Row << (Col * 4))); // ref-note {{read of uninitialized object}} + return Board(State | ((uint64_t)Row << (Col * 4))); // ref-note {{read of uninitialized object}} \ + // expected-note {{read of object outside its lifetime}} } constexpr int getQueenRow(int Col) const { return (State >> (Col * 4)) & 0xf; @@ -47,17 +48,22 @@ constexpr Board buildBoardScan(int N, int Col, int Row, const Board &B) { return Row == N ? Board(0, true) : B.ok(Row, Col) ? - tryBoard(buildBoardRecurse(N, Col + 1, B.addQueen(Row, Col)), // ref-note {{in call to '&Board()->addQueen(0, 0)}} + tryBoard(buildBoardRecurse(N, Col + 1, B.addQueen(Row, Col)), // ref-note {{in call to '&Board()->addQueen(0, 0)}} \ + // expected-note {{in call to '&Board()->addQueen(0, 0)}} N, Col, Row+1, B) : buildBoardScan(N, Col, Row + 1, B); } constexpr Board buildBoardRecurse(int N, int Col, const Board &B) { - return Col == N ? B : buildBoardScan(N, Col, 0, B); // ref-note {{in call to 'buildBoardScan(8, 0, 0, Board())'}} + return Col == N ? B : buildBoardScan(N, Col, 0, B); // ref-note {{in call to 'buildBoardScan(8, 0, 0, Board())'}} \ + // expected-note {{in call to 'buildBoardScan(8, 0, 0, Board())'}} + } constexpr Board buildBoard(int N) { - return buildBoardRecurse(N, 0, Board()); // ref-note {{in call to 'buildBoardRecurse(8, 0, Board())'}} + return buildBoardRecurse(N, 0, Board()); // ref-note {{in call to 'buildBoardRecurse(8, 0, Board())'}} \ + // expected-note {{in call to 'buildBoardRecurse(8, 0, Board())'}} } constexpr Board q8 = buildBoard(8); // ref-error {{must be initialized by a constant expression}} \ // ref-note {{in call to 'buildBoard(8)'}} \ - // expected-error {{must be initialized by a constant expression}} + // expected-error {{must be initialized by a constant expression}} \ + // expected-note {{in call to 'buildBoard(8)'}} diff --git a/clang/test/AST/Interp/records.cpp b/clang/test/AST/Interp/records.cpp --- a/clang/test/AST/Interp/records.cpp +++ b/clang/test/AST/Interp/records.cpp @@ -368,3 +368,14 @@ constexpr piecewise_construct_t piecewise_construct = piecewise_construct_t(); }; + +namespace ConditionalInit { + struct S { int a; }; + + constexpr S getS(bool b) { + return b ? S{12} : S{13}; + } + + static_assert(getS(true).a == 12, ""); + static_assert(getS(false).a == 13, ""); +};