Skip to content

Commit

Permalink
[clang][Interp] Record initialization via conditional operator
Browse files Browse the repository at this point in the history
Differential Revision: https://reviews.llvm.org/D141497
  • Loading branch information
tbaederr committed Apr 6, 2023
1 parent 53f7f85 commit 92417f2
Show file tree
Hide file tree
Showing 4 changed files with 66 additions and 31 deletions.
67 changes: 41 additions & 26 deletions clang/lib/AST/Interp/ByteCodeExprGen.cpp
Expand Up @@ -560,32 +560,8 @@ bool ByteCodeExprGen<Emitter>::VisitOpaqueValueExpr(const OpaqueValueExpr *E) {
template <class Emitter>
bool ByteCodeExprGen<Emitter>::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 <class Emitter>
Expand Down Expand Up @@ -921,6 +897,41 @@ bool ByteCodeExprGen<Emitter>::visitBool(const Expr *E) {
}
}

/// Visit a conditional operator, i.e. `A ? B : C`.
/// \V determines what function to call for the B and C expressions.
template <class Emitter>
bool ByteCodeExprGen<Emitter>::visitConditional(
const AbstractConditionalOperator *E,
llvm::function_ref<bool(const Expr *)> 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 <class Emitter>
bool ByteCodeExprGen<Emitter>::visitZeroInitializer(PrimType T, const Expr *E) {
switch (T) {
Expand Down Expand Up @@ -1429,6 +1440,10 @@ bool ByteCodeExprGen<Emitter>::visitRecordInitializer(const Expr *Initializer) {
return this->visitInitializer(CE->getSubExpr());
} else if (const auto *CE = dyn_cast<CXXBindTemporaryExpr>(Initializer)) {
return this->visitInitializer(CE->getSubExpr());
} else if (const auto *ACO =
dyn_cast<AbstractConditionalOperator>(Initializer)) {
return this->visitConditional(
ACO, [this](const Expr *E) { return this->visitRecordInitializer(E); });
}

return false;
Expand Down
3 changes: 3 additions & 0 deletions clang/lib/AST/Interp/ByteCodeExprGen.h
Expand Up @@ -181,6 +181,9 @@ class ByteCodeExprGen : public ConstStmtVisitor<ByteCodeExprGen<Emitter>, bool>,
return this->emitPopPtr(I);
}

bool visitConditional(const AbstractConditionalOperator *E,
llvm::function_ref<bool(const Expr *)> V);

/// Creates a local primitive value.
unsigned allocateLocalPrimitive(DeclTy &&Decl, PrimType Ty, bool IsMutable,
bool IsExtended = false);
Expand Down
16 changes: 11 additions & 5 deletions clang/test/AST/Interp/constexpr-nqueens.cpp
Expand Up @@ -17,7 +17,8 @@ struct Board {
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;
Expand Down Expand Up @@ -47,17 +48,22 @@ constexpr Board tryBoard(const Board &Try,
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)'}}
11 changes: 11 additions & 0 deletions clang/test/AST/Interp/records.cpp
Expand Up @@ -368,3 +368,14 @@ namespace EmptyCtor {
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, "");
};

0 comments on commit 92417f2

Please sign in to comment.