forked from OSchip/llvm-project
Implement if consteval (P1938)
Modify the IfStmt node to suppoort constant evaluated expressions. Add a new ExpressionEvaluationContext::ImmediateFunctionContext to keep track of immediate function contexts. This proved easier/better/probably more efficient than walking the AST backward as it allows diagnosing nested if consteval statements.
This commit is contained in:
parent
b5a11a991e
commit
424733c12a
|
@ -20,6 +20,7 @@
|
|||
#include "clang/Basic/IdentifierTable.h"
|
||||
#include "clang/Basic/LLVM.h"
|
||||
#include "clang/Basic/SourceLocation.h"
|
||||
#include "clang/Basic/Specifiers.h"
|
||||
#include "llvm/ADT/ArrayRef.h"
|
||||
#include "llvm/ADT/BitmaskEnum.h"
|
||||
#include "llvm/ADT/PointerIntPair.h"
|
||||
|
@ -160,8 +161,8 @@ protected:
|
|||
|
||||
unsigned : NumStmtBits;
|
||||
|
||||
/// True if this if statement is a constexpr if.
|
||||
unsigned IsConstexpr : 1;
|
||||
/// Whether this is a constexpr if, or a consteval if, or neither.
|
||||
unsigned Kind : 3;
|
||||
|
||||
/// True if this if statement has storage for an else statement.
|
||||
unsigned HasElse : 1;
|
||||
|
@ -1950,8 +1951,8 @@ class IfStmt final
|
|||
unsigned elseOffset() const { return condOffset() + ElseOffsetFromCond; }
|
||||
|
||||
/// Build an if/then/else statement.
|
||||
IfStmt(const ASTContext &Ctx, SourceLocation IL, bool IsConstexpr, Stmt *Init,
|
||||
VarDecl *Var, Expr *Cond, SourceLocation LParenLoc,
|
||||
IfStmt(const ASTContext &Ctx, SourceLocation IL, IfStatementKind Kind,
|
||||
Stmt *Init, VarDecl *Var, Expr *Cond, SourceLocation LParenLoc,
|
||||
SourceLocation RParenLoc, Stmt *Then, SourceLocation EL, Stmt *Else);
|
||||
|
||||
/// Build an empty if/then/else statement.
|
||||
|
@ -1960,9 +1961,9 @@ class IfStmt final
|
|||
public:
|
||||
/// Create an IfStmt.
|
||||
static IfStmt *Create(const ASTContext &Ctx, SourceLocation IL,
|
||||
bool IsConstexpr, Stmt *Init, VarDecl *Var, Expr *Cond,
|
||||
SourceLocation LPL, SourceLocation RPL, Stmt *Then,
|
||||
SourceLocation EL = SourceLocation(),
|
||||
IfStatementKind Kind, Stmt *Init, VarDecl *Var,
|
||||
Expr *Cond, SourceLocation LPL, SourceLocation RPL,
|
||||
Stmt *Then, SourceLocation EL = SourceLocation(),
|
||||
Stmt *Else = nullptr);
|
||||
|
||||
/// Create an empty IfStmt optionally with storage for an else statement,
|
||||
|
@ -2077,8 +2078,30 @@ public:
|
|||
*getTrailingObjects<SourceLocation>() = ElseLoc;
|
||||
}
|
||||
|
||||
bool isConstexpr() const { return IfStmtBits.IsConstexpr; }
|
||||
void setConstexpr(bool C) { IfStmtBits.IsConstexpr = C; }
|
||||
bool isConsteval() const {
|
||||
return getStatementKind() == IfStatementKind::ConstevalNonNegated ||
|
||||
getStatementKind() == IfStatementKind::ConstevalNegated;
|
||||
}
|
||||
|
||||
bool isNonNegatedConsteval() const {
|
||||
return getStatementKind() == IfStatementKind::ConstevalNonNegated;
|
||||
}
|
||||
|
||||
bool isNegatedConsteval() const {
|
||||
return getStatementKind() == IfStatementKind::ConstevalNegated;
|
||||
}
|
||||
|
||||
bool isConstexpr() const {
|
||||
return getStatementKind() == IfStatementKind::Constexpr;
|
||||
}
|
||||
|
||||
void setStatementKind(IfStatementKind Kind) {
|
||||
IfStmtBits.Kind = static_cast<unsigned>(Kind);
|
||||
}
|
||||
|
||||
IfStatementKind getStatementKind() const {
|
||||
return static_cast<IfStatementKind>(IfStmtBits.Kind);
|
||||
}
|
||||
|
||||
/// If this is an 'if constexpr', determine which substatement will be taken.
|
||||
/// Otherwise, or if the condition is value-dependent, returns None.
|
||||
|
@ -2101,13 +2124,19 @@ public:
|
|||
// Iterators over subexpressions. The iterators will include iterating
|
||||
// over the initialization expression referenced by the condition variable.
|
||||
child_range children() {
|
||||
return child_range(getTrailingObjects<Stmt *>(),
|
||||
// We always store a condition, but there is none for consteval if
|
||||
// statements, so skip it.
|
||||
return child_range(getTrailingObjects<Stmt *>() +
|
||||
(isConsteval() ? thenOffset() : 0),
|
||||
getTrailingObjects<Stmt *>() +
|
||||
numTrailingObjects(OverloadToken<Stmt *>()));
|
||||
}
|
||||
|
||||
const_child_range children() const {
|
||||
return const_child_range(getTrailingObjects<Stmt *>(),
|
||||
// We always store a condition, but there is none for consteval if
|
||||
// statements, so skip it.
|
||||
return const_child_range(getTrailingObjects<Stmt *>() +
|
||||
(isConsteval() ? thenOffset() : 0),
|
||||
getTrailingObjects<Stmt *>() +
|
||||
numTrailingObjects(OverloadToken<Stmt *>()));
|
||||
}
|
||||
|
|
|
@ -626,6 +626,13 @@ def ext_constexpr_if : ExtWarn<
|
|||
def warn_cxx14_compat_constexpr_if : Warning<
|
||||
"constexpr if is incompatible with C++ standards before C++17">,
|
||||
DefaultIgnore, InGroup<CXXPre17Compat>;
|
||||
def ext_consteval_if : ExtWarn<
|
||||
"consteval if is a C++2b extension">,
|
||||
InGroup<CXX2b>;
|
||||
def warn_cxx20_compat_consteval_if : Warning<
|
||||
"consteval if is incompatible with C++ standards before C++2b">,
|
||||
InGroup<CXXPre2bCompat>, DefaultIgnore;
|
||||
|
||||
def ext_init_statement : ExtWarn<
|
||||
"'%select{if|switch}0' initialization statements are a C++17 extension">,
|
||||
InGroup<CXX17>;
|
||||
|
|
|
@ -1508,6 +1508,10 @@ def err_static_assert_failed : Error<"static_assert failed%select{ %1|}0">;
|
|||
def err_static_assert_requirement_failed : Error<
|
||||
"static_assert failed due to requirement '%0'%select{ %2|}1">;
|
||||
|
||||
def warn_consteval_if_always_true : Warning<
|
||||
"consteval if is always true in an %select{unevaluated|immediate}0 context">,
|
||||
InGroup<DiagGroup<"redundant-consteval-if">>;
|
||||
|
||||
def ext_inline_variable : ExtWarn<
|
||||
"inline variables are a C++17 extension">, InGroup<CXX17>;
|
||||
def warn_cxx14_compat_inline_variable : Warning<
|
||||
|
@ -3315,11 +3319,11 @@ def warn_attribute_has_no_effect_on_infinite_loop : Warning<
|
|||
InGroup<IgnoredAttributes>;
|
||||
def note_attribute_has_no_effect_on_infinite_loop_here : Note<
|
||||
"annotating the infinite loop here">;
|
||||
def warn_attribute_has_no_effect_on_if_constexpr : Warning<
|
||||
"attribute %0 has no effect when annotating an 'if constexpr' statement">,
|
||||
def warn_attribute_has_no_effect_on_compile_time_if : Warning<
|
||||
"attribute %0 has no effect when annotating an 'if %select{constexpr|consteval}1' statement">,
|
||||
InGroup<IgnoredAttributes>;
|
||||
def note_attribute_has_no_effect_on_if_constexpr_here : Note<
|
||||
"annotating the 'if constexpr' statement here">;
|
||||
def note_attribute_has_no_effect_on_compile_time_if_here : Note<
|
||||
"annotating the 'if %select{constexpr|consteval}0' statement here">;
|
||||
def err_decl_attribute_invalid_on_stmt : Error<
|
||||
"%0 attribute cannot be applied to a statement">;
|
||||
def err_stmt_attribute_invalid_on_decl : Error<
|
||||
|
@ -5944,6 +5948,8 @@ def note_protected_by_vla_type_alias : Note<
|
|||
"jump bypasses initialization of VLA type alias">;
|
||||
def note_protected_by_constexpr_if : Note<
|
||||
"jump enters controlled statement of constexpr if">;
|
||||
def note_protected_by_consteval_if : Note<
|
||||
"jump enters controlled statement of consteval if">;
|
||||
def note_protected_by_if_available : Note<
|
||||
"jump enters controlled statement of if available">;
|
||||
def note_protected_by_vla : Note<
|
||||
|
|
|
@ -31,6 +31,15 @@ namespace clang {
|
|||
/// Define the kind of constexpr specifier.
|
||||
enum class ConstexprSpecKind { Unspecified, Constexpr, Consteval, Constinit };
|
||||
|
||||
/// In an if statement, this denotes whether the the statement is
|
||||
/// a constexpr or consteval if statement.
|
||||
enum class IfStatementKind : unsigned {
|
||||
Ordinary,
|
||||
Constexpr,
|
||||
ConstevalNonNegated,
|
||||
ConstevalNegated
|
||||
};
|
||||
|
||||
/// Specifies the width of a type, e.g., short, long, or long long.
|
||||
enum class TypeSpecifierWidth { Unspecified, Short, Long, LongLong };
|
||||
|
||||
|
|
|
@ -1214,6 +1214,11 @@ public:
|
|||
/// cases in a switch statement).
|
||||
ConstantEvaluated,
|
||||
|
||||
/// In addition of being constant evaluated, the current expression
|
||||
/// occurs in an immediate function context - either a consteval function
|
||||
/// or a consteval if function.
|
||||
ImmediateFunctionContext,
|
||||
|
||||
/// The current expression is potentially evaluated at run time,
|
||||
/// which means that code may be generated to evaluate the value of the
|
||||
/// expression at run time.
|
||||
|
@ -1302,8 +1307,14 @@ public:
|
|||
Context == ExpressionEvaluationContext::UnevaluatedAbstract ||
|
||||
Context == ExpressionEvaluationContext::UnevaluatedList;
|
||||
}
|
||||
|
||||
bool isConstantEvaluated() const {
|
||||
return Context == ExpressionEvaluationContext::ConstantEvaluated;
|
||||
return Context == ExpressionEvaluationContext::ConstantEvaluated ||
|
||||
Context == ExpressionEvaluationContext::ImmediateFunctionContext;
|
||||
}
|
||||
|
||||
bool isImmediateFunctionContext() const {
|
||||
return Context == ExpressionEvaluationContext::ImmediateFunctionContext;
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -4705,11 +4716,12 @@ public:
|
|||
Stmt *SubStmt);
|
||||
|
||||
class ConditionResult;
|
||||
StmtResult ActOnIfStmt(SourceLocation IfLoc, bool IsConstexpr,
|
||||
|
||||
StmtResult ActOnIfStmt(SourceLocation IfLoc, IfStatementKind StatementKind,
|
||||
SourceLocation LParenLoc, Stmt *InitStmt,
|
||||
ConditionResult Cond, SourceLocation RParenLoc,
|
||||
Stmt *ThenVal, SourceLocation ElseLoc, Stmt *ElseVal);
|
||||
StmtResult BuildIfStmt(SourceLocation IfLoc, bool IsConstexpr,
|
||||
StmtResult BuildIfStmt(SourceLocation IfLoc, IfStatementKind StatementKind,
|
||||
SourceLocation LParenLoc, Stmt *InitStmt,
|
||||
ConditionResult Cond, SourceLocation RParenLoc,
|
||||
Stmt *ThenVal, SourceLocation ElseLoc, Stmt *ElseVal);
|
||||
|
@ -9120,6 +9132,19 @@ public:
|
|||
return ExprEvalContexts.back().isUnevaluated();
|
||||
}
|
||||
|
||||
bool isImmediateFunctionContext() const {
|
||||
assert(!ExprEvalContexts.empty() &&
|
||||
"Must be in an expression evaluation context");
|
||||
for (const ExpressionEvaluationContextRecord &context :
|
||||
llvm::reverse(ExprEvalContexts)) {
|
||||
if (context.isImmediateFunctionContext())
|
||||
return true;
|
||||
if (context.isUnevaluated())
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/// RAII class used to determine whether SFINAE has
|
||||
/// trapped any errors that occur during template argument
|
||||
/// deduction.
|
||||
|
|
|
@ -6401,7 +6401,7 @@ ExpectedStmt ASTNodeImporter::VisitIfStmt(IfStmt *S) {
|
|||
if (Err)
|
||||
return std::move(Err);
|
||||
|
||||
return IfStmt::Create(Importer.getToContext(), ToIfLoc, S->isConstexpr(),
|
||||
return IfStmt::Create(Importer.getToContext(), ToIfLoc, S->getStatementKind(),
|
||||
ToInit, ToConditionVariable, ToCond, ToLParenLoc,
|
||||
ToRParenLoc, ToThen, ToElseLoc, ToElse);
|
||||
}
|
||||
|
|
|
@ -5195,7 +5195,10 @@ static EvalStmtResult EvaluateStmt(StmtResult &Result, EvalInfo &Info,
|
|||
}
|
||||
}
|
||||
bool Cond;
|
||||
if (!EvaluateCond(Info, IS->getConditionVariable(), IS->getCond(), Cond))
|
||||
if (IS->isConsteval())
|
||||
Cond = IS->isNonNegatedConsteval();
|
||||
else if (!EvaluateCond(Info, IS->getConditionVariable(), IS->getCond(),
|
||||
Cond))
|
||||
return ESR_Failed;
|
||||
|
||||
if (const Stmt *SubStmt = Cond ? IS->getThen() : IS->getElse()) {
|
||||
|
|
|
@ -188,6 +188,12 @@ bool ByteCodeStmtGen<Emitter>::visitReturnStmt(const ReturnStmt *RS) {
|
|||
template <class Emitter>
|
||||
bool ByteCodeStmtGen<Emitter>::visitIfStmt(const IfStmt *IS) {
|
||||
BlockScope<Emitter> IfScope(this);
|
||||
|
||||
if (IS->isNonNegatedConsteval())
|
||||
return visitStmt(IS->getThen());
|
||||
if (IS->isNegatedConsteval())
|
||||
return IS->getElse() ? visitStmt(IS->getElse()) : true;
|
||||
|
||||
if (auto *CondInit = IS->getInit())
|
||||
if (!visitStmt(IS->getInit()))
|
||||
return false;
|
||||
|
|
|
@ -1489,6 +1489,8 @@ void JSONNodeDumper::VisitIfStmt(const IfStmt *IS) {
|
|||
attributeOnlyIfTrue("hasVar", IS->hasVarStorage());
|
||||
attributeOnlyIfTrue("hasElse", IS->hasElseStorage());
|
||||
attributeOnlyIfTrue("isConstexpr", IS->isConstexpr());
|
||||
attributeOnlyIfTrue("isConsteval", IS->isConsteval());
|
||||
attributeOnlyIfTrue("constevalIsNegated", IS->isNegatedConsteval());
|
||||
}
|
||||
|
||||
void JSONNodeDumper::VisitSwitchStmt(const SwitchStmt *SS) {
|
||||
|
|
|
@ -912,7 +912,7 @@ void MSAsmStmt::initialize(const ASTContext &C, StringRef asmstr,
|
|||
});
|
||||
}
|
||||
|
||||
IfStmt::IfStmt(const ASTContext &Ctx, SourceLocation IL, bool IsConstexpr,
|
||||
IfStmt::IfStmt(const ASTContext &Ctx, SourceLocation IL, IfStatementKind Kind,
|
||||
Stmt *Init, VarDecl *Var, Expr *Cond, SourceLocation LPL,
|
||||
SourceLocation RPL, Stmt *Then, SourceLocation EL, Stmt *Else)
|
||||
: Stmt(IfStmtClass), LParenLoc(LPL), RParenLoc(RPL) {
|
||||
|
@ -923,7 +923,7 @@ IfStmt::IfStmt(const ASTContext &Ctx, SourceLocation IL, bool IsConstexpr,
|
|||
IfStmtBits.HasVar = HasVar;
|
||||
IfStmtBits.HasInit = HasInit;
|
||||
|
||||
setConstexpr(IsConstexpr);
|
||||
setStatementKind(Kind);
|
||||
|
||||
setCond(Cond);
|
||||
setThen(Then);
|
||||
|
@ -947,9 +947,9 @@ IfStmt::IfStmt(EmptyShell Empty, bool HasElse, bool HasVar, bool HasInit)
|
|||
}
|
||||
|
||||
IfStmt *IfStmt::Create(const ASTContext &Ctx, SourceLocation IL,
|
||||
bool IsConstexpr, Stmt *Init, VarDecl *Var, Expr *Cond,
|
||||
SourceLocation LPL, SourceLocation RPL, Stmt *Then,
|
||||
SourceLocation EL, Stmt *Else) {
|
||||
IfStatementKind Kind, Stmt *Init, VarDecl *Var,
|
||||
Expr *Cond, SourceLocation LPL, SourceLocation RPL,
|
||||
Stmt *Then, SourceLocation EL, Stmt *Else) {
|
||||
bool HasElse = Else != nullptr;
|
||||
bool HasVar = Var != nullptr;
|
||||
bool HasInit = Init != nullptr;
|
||||
|
@ -958,7 +958,7 @@ IfStmt *IfStmt::Create(const ASTContext &Ctx, SourceLocation IL,
|
|||
NumMandatoryStmtPtr + HasElse + HasVar + HasInit, HasElse),
|
||||
alignof(IfStmt));
|
||||
return new (Mem)
|
||||
IfStmt(Ctx, IL, IsConstexpr, Init, Var, Cond, LPL, RPL, Then, EL, Else);
|
||||
IfStmt(Ctx, IL, Kind, Init, Var, Cond, LPL, RPL, Then, EL, Else);
|
||||
}
|
||||
|
||||
IfStmt *IfStmt::CreateEmpty(const ASTContext &Ctx, bool HasElse, bool HasVar,
|
||||
|
|
|
@ -236,6 +236,22 @@ void StmtPrinter::VisitAttributedStmt(AttributedStmt *Node) {
|
|||
}
|
||||
|
||||
void StmtPrinter::PrintRawIfStmt(IfStmt *If) {
|
||||
if (If->isConsteval()) {
|
||||
OS << "if ";
|
||||
if (If->isNegatedConsteval())
|
||||
OS << "!";
|
||||
OS << "consteval";
|
||||
OS << NL;
|
||||
PrintStmt(If->getThen());
|
||||
if (Stmt *Else = If->getElse()) {
|
||||
Indent();
|
||||
OS << "else";
|
||||
PrintStmt(Else);
|
||||
OS << NL;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
OS << "if (";
|
||||
if (If->getInit())
|
||||
PrintInitStmt(If->getInit(), 4);
|
||||
|
|
|
@ -948,6 +948,14 @@ void TextNodeDumper::VisitIfStmt(const IfStmt *Node) {
|
|||
OS << " has_var";
|
||||
if (Node->hasElseStorage())
|
||||
OS << " has_else";
|
||||
if (Node->isConstexpr())
|
||||
OS << " constexpr";
|
||||
if (Node->isConsteval()) {
|
||||
OS << " ";
|
||||
if (Node->isNegatedConsteval())
|
||||
OS << "!";
|
||||
OS << "consteval";
|
||||
}
|
||||
}
|
||||
|
||||
void TextNodeDumper::VisitSwitchStmt(const SwitchStmt *Node) {
|
||||
|
|
|
@ -461,8 +461,7 @@ static Stmt *create_call_once(ASTContext &C, const FunctionDecl *D) {
|
|||
DerefType);
|
||||
|
||||
auto *Out =
|
||||
IfStmt::Create(C, SourceLocation(),
|
||||
/* IsConstexpr=*/false,
|
||||
IfStmt::Create(C, SourceLocation(), IfStatementKind::Ordinary,
|
||||
/* Init=*/nullptr,
|
||||
/* Var=*/nullptr,
|
||||
/* Cond=*/FlagCheck,
|
||||
|
@ -547,8 +546,7 @@ static Stmt *create_dispatch_once(ASTContext &C, const FunctionDecl *D) {
|
|||
|
||||
Expr *GuardCondition = M.makeComparison(LValToRval, DoneValue, BO_NE);
|
||||
// (5) Create the 'if' statement.
|
||||
auto *If = IfStmt::Create(C, SourceLocation(),
|
||||
/* IsConstexpr=*/false,
|
||||
auto *If = IfStmt::Create(C, SourceLocation(), IfStatementKind::Ordinary,
|
||||
/* Init=*/nullptr,
|
||||
/* Var=*/nullptr,
|
||||
/* Cond=*/GuardCondition,
|
||||
|
@ -658,8 +656,7 @@ static Stmt *create_OSAtomicCompareAndSwap(ASTContext &C, const FunctionDecl *D)
|
|||
|
||||
/// Construct the If.
|
||||
auto *If =
|
||||
IfStmt::Create(C, SourceLocation(),
|
||||
/* IsConstexpr=*/false,
|
||||
IfStmt::Create(C, SourceLocation(), IfStatementKind::Ordinary,
|
||||
/* Init=*/nullptr,
|
||||
/* Var=*/nullptr, Comparison,
|
||||
/* LPL=*/SourceLocation(),
|
||||
|
|
|
@ -3047,7 +3047,7 @@ CFGBlock *CFGBuilder::VisitIfStmt(IfStmt *I) {
|
|||
// control-flow transfer of '&&' or '||' go directly into the then/else
|
||||
// blocks directly.
|
||||
BinaryOperator *Cond =
|
||||
I->getConditionVariable()
|
||||
(I->isConsteval() || I->getConditionVariable())
|
||||
? nullptr
|
||||
: dyn_cast<BinaryOperator>(I->getCond()->IgnoreParens());
|
||||
CFGBlock *LastBlock;
|
||||
|
@ -3061,7 +3061,9 @@ CFGBlock *CFGBuilder::VisitIfStmt(IfStmt *I) {
|
|||
Block->setTerminator(I);
|
||||
|
||||
// See if this is a known constant.
|
||||
const TryResult &KnownVal = tryEvaluateBool(I->getCond());
|
||||
TryResult KnownVal;
|
||||
if (!I->isConsteval())
|
||||
KnownVal = tryEvaluateBool(I->getCond());
|
||||
|
||||
// Add the successors. If we know that specific branches are
|
||||
// unreachable, inform addSuccessor() of that knowledge.
|
||||
|
|
|
@ -713,6 +713,17 @@ void CodeGenFunction::EmitIndirectGotoStmt(const IndirectGotoStmt &S) {
|
|||
}
|
||||
|
||||
void CodeGenFunction::EmitIfStmt(const IfStmt &S) {
|
||||
// The else branch of a consteval if statement is always the only branch that
|
||||
// can be runtime evaluated.
|
||||
if (S.isConsteval()) {
|
||||
const Stmt *Executed = S.isNegatedConsteval() ? S.getThen() : S.getElse();
|
||||
if (Executed) {
|
||||
RunCleanupsScope ExecutedScope(*this);
|
||||
EmitStmt(Executed);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// C99 6.8.4.1: The first substatement is executed if the expression compares
|
||||
// unequal to 0. The condition must be a scalar type.
|
||||
LexicalScope ConditionScope(*this, S.getCond()->getSourceRange());
|
||||
|
|
|
@ -649,6 +649,14 @@ struct ComputeRegionCounts : public ConstStmtVisitor<ComputeRegionCounts> {
|
|||
|
||||
void VisitIfStmt(const IfStmt *S) {
|
||||
RecordStmtCount(S);
|
||||
|
||||
if (S->isConsteval()) {
|
||||
const Stmt *Stm = S->isNegatedConsteval() ? S->getThen() : S->getElse();
|
||||
if (Stm)
|
||||
Visit(Stm);
|
||||
return;
|
||||
}
|
||||
|
||||
uint64_t ParentCount = CurrentCount;
|
||||
if (S->getInit())
|
||||
Visit(S->getInit());
|
||||
|
|
|
@ -607,6 +607,7 @@ static void InitializeCPlusPlusFeatureTestMacros(const LangOptions &LangOpts,
|
|||
if (LangOpts.CPlusPlus2b) {
|
||||
Builder.defineMacro("__cpp_implicit_move", "202011L");
|
||||
Builder.defineMacro("__cpp_size_t_suffix", "202011L");
|
||||
Builder.defineMacro("__cpp_if_consteval", "202106L");
|
||||
}
|
||||
if (LangOpts.Char8)
|
||||
Builder.defineMacro("__cpp_char8_t", "201811L");
|
||||
|
|
|
@ -1338,20 +1338,36 @@ struct MisleadingIndentationChecker {
|
|||
/// 'if' '(' expression ')' statement 'else' statement
|
||||
/// [C++] 'if' '(' condition ')' statement
|
||||
/// [C++] 'if' '(' condition ')' statement 'else' statement
|
||||
/// [C++23] 'if' '!' [opt] consteval compound-statement
|
||||
/// [C++23] 'if' '!' [opt] consteval compound-statement 'else' statement
|
||||
///
|
||||
StmtResult Parser::ParseIfStatement(SourceLocation *TrailingElseLoc) {
|
||||
assert(Tok.is(tok::kw_if) && "Not an if stmt!");
|
||||
SourceLocation IfLoc = ConsumeToken(); // eat the 'if'.
|
||||
|
||||
bool IsConstexpr = false;
|
||||
bool IsConsteval = false;
|
||||
SourceLocation NotLocation;
|
||||
SourceLocation ConstevalLoc;
|
||||
|
||||
if (Tok.is(tok::kw_constexpr)) {
|
||||
Diag(Tok, getLangOpts().CPlusPlus17 ? diag::warn_cxx14_compat_constexpr_if
|
||||
: diag::ext_constexpr_if);
|
||||
IsConstexpr = true;
|
||||
ConsumeToken();
|
||||
}
|
||||
} else {
|
||||
if (Tok.is(tok::exclaim)) {
|
||||
NotLocation = ConsumeToken();
|
||||
}
|
||||
|
||||
if (Tok.isNot(tok::l_paren)) {
|
||||
if (Tok.is(tok::kw_consteval)) {
|
||||
Diag(Tok, getLangOpts().CPlusPlus2b ? diag::warn_cxx20_compat_consteval_if
|
||||
: diag::ext_consteval_if);
|
||||
IsConsteval = true;
|
||||
ConstevalLoc = ConsumeToken();
|
||||
}
|
||||
}
|
||||
if (!IsConsteval && (NotLocation.isValid() || Tok.isNot(tok::l_paren))) {
|
||||
Diag(Tok, diag::err_expected_lparen_after) << "if";
|
||||
SkipUntil(tok::semi);
|
||||
return StmtError();
|
||||
|
@ -1378,15 +1394,18 @@ StmtResult Parser::ParseIfStatement(SourceLocation *TrailingElseLoc) {
|
|||
Sema::ConditionResult Cond;
|
||||
SourceLocation LParen;
|
||||
SourceLocation RParen;
|
||||
if (ParseParenExprOrCondition(&InitStmt, Cond, IfLoc,
|
||||
IsConstexpr ? Sema::ConditionKind::ConstexprIf
|
||||
: Sema::ConditionKind::Boolean,
|
||||
&LParen, &RParen))
|
||||
return StmtError();
|
||||
|
||||
llvm::Optional<bool> ConstexprCondition;
|
||||
if (IsConstexpr)
|
||||
ConstexprCondition = Cond.getKnownValue();
|
||||
if (!IsConsteval) {
|
||||
|
||||
if (ParseParenExprOrCondition(&InitStmt, Cond, IfLoc,
|
||||
IsConstexpr ? Sema::ConditionKind::ConstexprIf
|
||||
: Sema::ConditionKind::Boolean,
|
||||
&LParen, &RParen))
|
||||
return StmtError();
|
||||
|
||||
if (IsConstexpr)
|
||||
ConstexprCondition = Cond.getKnownValue();
|
||||
}
|
||||
|
||||
bool IsBracedThen = Tok.is(tok::l_brace);
|
||||
|
||||
|
@ -1418,10 +1437,16 @@ StmtResult Parser::ParseIfStatement(SourceLocation *TrailingElseLoc) {
|
|||
SourceLocation InnerStatementTrailingElseLoc;
|
||||
StmtResult ThenStmt;
|
||||
{
|
||||
bool ShouldEnter =
|
||||
(ConstexprCondition && !*ConstexprCondition) || IsConsteval;
|
||||
Sema::ExpressionEvaluationContext Context =
|
||||
Sema::ExpressionEvaluationContext::DiscardedStatement;
|
||||
if (NotLocation.isInvalid() && IsConsteval)
|
||||
Context = Sema::ExpressionEvaluationContext::ImmediateFunctionContext;
|
||||
|
||||
EnterExpressionEvaluationContext PotentiallyDiscarded(
|
||||
Actions, Sema::ExpressionEvaluationContext::DiscardedStatement, nullptr,
|
||||
Sema::ExpressionEvaluationContextRecord::EK_Other,
|
||||
/*ShouldEnter=*/ConstexprCondition && !*ConstexprCondition);
|
||||
Actions, Context, nullptr,
|
||||
Sema::ExpressionEvaluationContextRecord::EK_Other, ShouldEnter);
|
||||
ThenStmt = ParseStatement(&InnerStatementTrailingElseLoc);
|
||||
}
|
||||
|
||||
|
@ -1456,11 +1481,16 @@ StmtResult Parser::ParseIfStatement(SourceLocation *TrailingElseLoc) {
|
|||
Tok.is(tok::l_brace));
|
||||
|
||||
MisleadingIndentationChecker MIChecker(*this, MSK_else, ElseLoc);
|
||||
bool ShouldEnter =
|
||||
(ConstexprCondition && *ConstexprCondition) || IsConsteval;
|
||||
Sema::ExpressionEvaluationContext Context =
|
||||
Sema::ExpressionEvaluationContext::DiscardedStatement;
|
||||
if (NotLocation.isValid() && IsConsteval)
|
||||
Context = Sema::ExpressionEvaluationContext::ImmediateFunctionContext;
|
||||
|
||||
EnterExpressionEvaluationContext PotentiallyDiscarded(
|
||||
Actions, Sema::ExpressionEvaluationContext::DiscardedStatement, nullptr,
|
||||
Sema::ExpressionEvaluationContextRecord::EK_Other,
|
||||
/*ShouldEnter=*/ConstexprCondition && *ConstexprCondition);
|
||||
Actions, Context, nullptr,
|
||||
Sema::ExpressionEvaluationContextRecord::EK_Other, ShouldEnter);
|
||||
ElseStmt = ParseStatement();
|
||||
|
||||
if (ElseStmt.isUsable())
|
||||
|
@ -1488,14 +1518,40 @@ StmtResult Parser::ParseIfStatement(SourceLocation *TrailingElseLoc) {
|
|||
return StmtError();
|
||||
}
|
||||
|
||||
if (IsConsteval) {
|
||||
auto IsCompoundStatement = [](const Stmt *S) {
|
||||
if (const auto *Outer = dyn_cast_or_null<AttributedStmt>(S))
|
||||
S = Outer->getSubStmt();
|
||||
return isa_and_nonnull<clang::CompoundStmt>(S);
|
||||
};
|
||||
|
||||
if (!IsCompoundStatement(ThenStmt.get())) {
|
||||
Diag(ConstevalLoc, diag::err_expected_after) << "consteval"
|
||||
<< "{";
|
||||
return StmtError();
|
||||
}
|
||||
if (!ElseStmt.isUnset() && !IsCompoundStatement(ElseStmt.get())) {
|
||||
Diag(ElseLoc, diag::err_expected_after) << "else"
|
||||
<< "{";
|
||||
return StmtError();
|
||||
}
|
||||
}
|
||||
|
||||
// Now if either are invalid, replace with a ';'.
|
||||
if (ThenStmt.isInvalid())
|
||||
ThenStmt = Actions.ActOnNullStmt(ThenStmtLoc);
|
||||
if (ElseStmt.isInvalid())
|
||||
ElseStmt = Actions.ActOnNullStmt(ElseStmtLoc);
|
||||
|
||||
return Actions.ActOnIfStmt(IfLoc, IsConstexpr, LParen, InitStmt.get(), Cond,
|
||||
RParen, ThenStmt.get(), ElseLoc, ElseStmt.get());
|
||||
IfStatementKind Kind = IfStatementKind::Ordinary;
|
||||
if (IsConstexpr)
|
||||
Kind = IfStatementKind::Constexpr;
|
||||
else if (IsConsteval)
|
||||
Kind = NotLocation.isValid() ? IfStatementKind::ConstevalNegated
|
||||
: IfStatementKind::ConstevalNonNegated;
|
||||
|
||||
return Actions.ActOnIfStmt(IfLoc, Kind, LParen, InitStmt.get(), Cond, RParen,
|
||||
ThenStmt.get(), ElseLoc, ElseStmt.get());
|
||||
}
|
||||
|
||||
/// ParseSwitchStatement
|
||||
|
|
|
@ -377,11 +377,15 @@ void JumpScopeChecker::BuildScopeInformation(Stmt *S,
|
|||
|
||||
case Stmt::IfStmtClass: {
|
||||
IfStmt *IS = cast<IfStmt>(S);
|
||||
if (!(IS->isConstexpr() || IS->isObjCAvailabilityCheck()))
|
||||
if (!(IS->isConstexpr() || IS->isConsteval() ||
|
||||
IS->isObjCAvailabilityCheck()))
|
||||
break;
|
||||
|
||||
unsigned Diag = IS->isConstexpr() ? diag::note_protected_by_constexpr_if
|
||||
: diag::note_protected_by_if_available;
|
||||
unsigned Diag = diag::note_protected_by_if_available;
|
||||
if (IS->isConstexpr())
|
||||
Diag = diag::note_protected_by_constexpr_if;
|
||||
else if (IS->isConsteval())
|
||||
Diag = diag::note_protected_by_consteval_if;
|
||||
|
||||
if (VarDecl *Var = IS->getConditionVariable())
|
||||
BuildScopeInformation(Var, ParentScope);
|
||||
|
@ -389,7 +393,9 @@ void JumpScopeChecker::BuildScopeInformation(Stmt *S,
|
|||
// Cannot jump into the middle of the condition.
|
||||
unsigned NewParentScope = Scopes.size();
|
||||
Scopes.push_back(GotoScope(ParentScope, Diag, 0, IS->getBeginLoc()));
|
||||
BuildScopeInformation(IS->getCond(), NewParentScope);
|
||||
|
||||
if (!IS->isConsteval())
|
||||
BuildScopeInformation(IS->getCond(), NewParentScope);
|
||||
|
||||
// Jumps into either arm of an 'if constexpr' are not allowed.
|
||||
NewParentScope = Scopes.size();
|
||||
|
|
|
@ -8202,7 +8202,7 @@ private:
|
|||
if (ReturnFalse.isInvalid())
|
||||
return StmtError();
|
||||
|
||||
return S.ActOnIfStmt(Loc, false, Loc, nullptr,
|
||||
return S.ActOnIfStmt(Loc, IfStatementKind::Ordinary, Loc, nullptr,
|
||||
S.ActOnCondition(nullptr, Loc, NotCond.get(),
|
||||
Sema::ConditionKind::Boolean),
|
||||
Loc, ReturnFalse.get(), SourceLocation(), nullptr);
|
||||
|
@ -8357,8 +8357,8 @@ private:
|
|||
return StmtError();
|
||||
|
||||
// if (...)
|
||||
return S.ActOnIfStmt(Loc, /*IsConstexpr=*/false, Loc, InitStmt, Cond, Loc,
|
||||
ReturnStmt.get(),
|
||||
return S.ActOnIfStmt(Loc, IfStatementKind::Ordinary, Loc, InitStmt, Cond,
|
||||
Loc, ReturnStmt.get(),
|
||||
/*ElseLoc=*/SourceLocation(), /*Else=*/nullptr);
|
||||
}
|
||||
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
#include "clang/AST/ExprObjC.h"
|
||||
#include "clang/AST/ExprOpenMP.h"
|
||||
#include "clang/AST/OperationKinds.h"
|
||||
#include "clang/AST/ParentMapContext.h"
|
||||
#include "clang/AST/RecursiveASTVisitor.h"
|
||||
#include "clang/AST/TypeLoc.h"
|
||||
#include "clang/Basic/Builtins.h"
|
||||
|
@ -16642,7 +16643,7 @@ void Sema::CheckUnusedVolatileAssignment(Expr *E) {
|
|||
ExprResult Sema::CheckForImmediateInvocation(ExprResult E, FunctionDecl *Decl) {
|
||||
if (isUnevaluatedContext() || !E.isUsable() || !Decl ||
|
||||
!Decl->isConsteval() || isConstantEvaluated() ||
|
||||
RebuildingImmediateInvocation)
|
||||
RebuildingImmediateInvocation || isImmediateFunctionContext())
|
||||
return E;
|
||||
|
||||
/// Opportunistically remove the callee from ReferencesToConsteval if we can.
|
||||
|
@ -16913,6 +16914,8 @@ static bool isPotentiallyConstantEvaluatedContext(Sema &SemaRef) {
|
|||
// An expression or conversion is potentially constant evaluated if it is
|
||||
switch (SemaRef.ExprEvalContexts.back().Context) {
|
||||
case Sema::ExpressionEvaluationContext::ConstantEvaluated:
|
||||
case Sema::ExpressionEvaluationContext::ImmediateFunctionContext:
|
||||
|
||||
// -- a manifestly constant-evaluated expression,
|
||||
case Sema::ExpressionEvaluationContext::PotentiallyEvaluated:
|
||||
case Sema::ExpressionEvaluationContext::PotentiallyEvaluatedIfUsed:
|
||||
|
@ -17035,6 +17038,7 @@ static OdrUseContext isOdrUseContext(Sema &SemaRef) {
|
|||
return OdrUseContext::None;
|
||||
|
||||
case Sema::ExpressionEvaluationContext::ConstantEvaluated:
|
||||
case Sema::ExpressionEvaluationContext::ImmediateFunctionContext:
|
||||
case Sema::ExpressionEvaluationContext::PotentiallyEvaluated:
|
||||
Result = OdrUseContext::Used;
|
||||
break;
|
||||
|
@ -18958,6 +18962,7 @@ bool Sema::DiagRuntimeBehavior(SourceLocation Loc, ArrayRef<const Stmt*> Stmts,
|
|||
break;
|
||||
|
||||
case ExpressionEvaluationContext::ConstantEvaluated:
|
||||
case ExpressionEvaluationContext::ImmediateFunctionContext:
|
||||
// Relevant diagnostics should be produced by constant evaluation.
|
||||
break;
|
||||
|
||||
|
|
|
@ -144,6 +144,7 @@ static IMAKind ClassifyImplicitMemberAccess(Sema &SemaRef,
|
|||
|
||||
case Sema::ExpressionEvaluationContext::DiscardedStatement:
|
||||
case Sema::ExpressionEvaluationContext::ConstantEvaluated:
|
||||
case Sema::ExpressionEvaluationContext::ImmediateFunctionContext:
|
||||
case Sema::ExpressionEvaluationContext::PotentiallyEvaluated:
|
||||
case Sema::ExpressionEvaluationContext::PotentiallyEvaluatedIfUsed:
|
||||
break;
|
||||
|
|
|
@ -1245,7 +1245,7 @@ void Sema::ActOnStartOfLambdaDefinition(LambdaIntroducer &Intro,
|
|||
// cleanups from the enclosing full-expression.
|
||||
PushExpressionEvaluationContext(
|
||||
LSI->CallOperator->isConsteval()
|
||||
? ExpressionEvaluationContext::ConstantEvaluated
|
||||
? ExpressionEvaluationContext::ImmediateFunctionContext
|
||||
: ExpressionEvaluationContext::PotentiallyEvaluated);
|
||||
}
|
||||
|
||||
|
@ -1948,6 +1948,7 @@ ExprResult Sema::BuildLambdaExpr(SourceLocation StartLoc, SourceLocation EndLoc,
|
|||
// ratified, it lays out the exact set of conditions where we shouldn't
|
||||
// allow a lambda-expression.
|
||||
case ExpressionEvaluationContext::ConstantEvaluated:
|
||||
case ExpressionEvaluationContext::ImmediateFunctionContext:
|
||||
// We don't actually diagnose this case immediately, because we
|
||||
// could be within a context where we might find out later that
|
||||
// the expression is potentially evaluated (e.g., for typeid).
|
||||
|
|
|
@ -862,7 +862,8 @@ public:
|
|||
};
|
||||
}
|
||||
|
||||
StmtResult Sema::ActOnIfStmt(SourceLocation IfLoc, bool IsConstexpr,
|
||||
StmtResult Sema::ActOnIfStmt(SourceLocation IfLoc,
|
||||
IfStatementKind StatementKind,
|
||||
SourceLocation LParenLoc, Stmt *InitStmt,
|
||||
ConditionResult Cond, SourceLocation RParenLoc,
|
||||
Stmt *thenStmt, SourceLocation ElseLoc,
|
||||
|
@ -875,25 +876,36 @@ StmtResult Sema::ActOnIfStmt(SourceLocation IfLoc, bool IsConstexpr,
|
|||
IfLoc),
|
||||
false);
|
||||
|
||||
bool ConstevalOrNegatedConsteval =
|
||||
StatementKind == IfStatementKind::ConstevalNonNegated ||
|
||||
StatementKind == IfStatementKind::ConstevalNegated;
|
||||
|
||||
Expr *CondExpr = Cond.get().second;
|
||||
assert((CondExpr || ConstevalOrNegatedConsteval) &&
|
||||
"If statement: missing condition");
|
||||
// Only call the CommaVisitor when not C89 due to differences in scope flags.
|
||||
if ((getLangOpts().C99 || getLangOpts().CPlusPlus) &&
|
||||
if (CondExpr && (getLangOpts().C99 || getLangOpts().CPlusPlus) &&
|
||||
!Diags.isIgnored(diag::warn_comma_operator, CondExpr->getExprLoc()))
|
||||
CommaVisitor(*this).Visit(CondExpr);
|
||||
|
||||
if (!elseStmt)
|
||||
if (!ConstevalOrNegatedConsteval && !elseStmt)
|
||||
DiagnoseEmptyStmtBody(CondExpr->getEndLoc(), thenStmt,
|
||||
diag::warn_empty_if_body);
|
||||
|
||||
if (IsConstexpr) {
|
||||
if (ConstevalOrNegatedConsteval ||
|
||||
StatementKind == IfStatementKind::Constexpr) {
|
||||
auto DiagnoseLikelihood = [&](const Stmt *S) {
|
||||
if (const Attr *A = Stmt::getLikelihoodAttr(S)) {
|
||||
Diags.Report(A->getLocation(),
|
||||
diag::warn_attribute_has_no_effect_on_if_constexpr)
|
||||
<< A << A->getRange();
|
||||
diag::warn_attribute_has_no_effect_on_compile_time_if)
|
||||
<< A << ConstevalOrNegatedConsteval << A->getRange();
|
||||
Diags.Report(IfLoc,
|
||||
diag::note_attribute_has_no_effect_on_if_constexpr_here)
|
||||
<< SourceRange(IfLoc, LParenLoc.getLocWithOffset(-1));
|
||||
diag::note_attribute_has_no_effect_on_compile_time_if_here)
|
||||
<< ConstevalOrNegatedConsteval
|
||||
<< SourceRange(IfLoc, (ConstevalOrNegatedConsteval
|
||||
? thenStmt->getBeginLoc()
|
||||
: LParenLoc)
|
||||
.getLocWithOffset(-1));
|
||||
}
|
||||
};
|
||||
DiagnoseLikelihood(thenStmt);
|
||||
|
@ -912,11 +924,24 @@ StmtResult Sema::ActOnIfStmt(SourceLocation IfLoc, bool IsConstexpr,
|
|||
}
|
||||
}
|
||||
|
||||
return BuildIfStmt(IfLoc, IsConstexpr, LParenLoc, InitStmt, Cond, RParenLoc,
|
||||
if (ConstevalOrNegatedConsteval) {
|
||||
bool Immediate = isImmediateFunctionContext();
|
||||
if (CurContext->isFunctionOrMethod()) {
|
||||
const auto *FD =
|
||||
dyn_cast<FunctionDecl>(Decl::castFromDeclContext(CurContext));
|
||||
if (FD && FD->isConsteval())
|
||||
Immediate = true;
|
||||
}
|
||||
if (isUnevaluatedContext() || Immediate)
|
||||
Diags.Report(IfLoc, diag::warn_consteval_if_always_true) << Immediate;
|
||||
}
|
||||
|
||||
return BuildIfStmt(IfLoc, StatementKind, LParenLoc, InitStmt, Cond, RParenLoc,
|
||||
thenStmt, ElseLoc, elseStmt);
|
||||
}
|
||||
|
||||
StmtResult Sema::BuildIfStmt(SourceLocation IfLoc, bool IsConstexpr,
|
||||
StmtResult Sema::BuildIfStmt(SourceLocation IfLoc,
|
||||
IfStatementKind StatementKind,
|
||||
SourceLocation LParenLoc, Stmt *InitStmt,
|
||||
ConditionResult Cond, SourceLocation RParenLoc,
|
||||
Stmt *thenStmt, SourceLocation ElseLoc,
|
||||
|
@ -924,12 +949,13 @@ StmtResult Sema::BuildIfStmt(SourceLocation IfLoc, bool IsConstexpr,
|
|||
if (Cond.isInvalid())
|
||||
return StmtError();
|
||||
|
||||
if (IsConstexpr || isa<ObjCAvailabilityCheckExpr>(Cond.get().second))
|
||||
if (StatementKind != IfStatementKind::Ordinary ||
|
||||
isa<ObjCAvailabilityCheckExpr>(Cond.get().second))
|
||||
setFunctionHasBranchProtectedScope();
|
||||
|
||||
return IfStmt::Create(Context, IfLoc, IsConstexpr, InitStmt, Cond.get().first,
|
||||
Cond.get().second, LParenLoc, RParenLoc, thenStmt,
|
||||
ElseLoc, elseStmt);
|
||||
return IfStmt::Create(Context, IfLoc, StatementKind, InitStmt,
|
||||
Cond.get().first, Cond.get().second, LParenLoc,
|
||||
RParenLoc, thenStmt, ElseLoc, elseStmt);
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
|
|
@ -1320,12 +1320,12 @@ public:
|
|||
///
|
||||
/// By default, performs semantic analysis to build the new statement.
|
||||
/// Subclasses may override this routine to provide different behavior.
|
||||
StmtResult RebuildIfStmt(SourceLocation IfLoc, bool IsConstexpr,
|
||||
StmtResult RebuildIfStmt(SourceLocation IfLoc, IfStatementKind Kind,
|
||||
SourceLocation LParenLoc, Sema::ConditionResult Cond,
|
||||
SourceLocation RParenLoc, Stmt *Init, Stmt *Then,
|
||||
SourceLocation ElseLoc, Stmt *Else) {
|
||||
return getSema().ActOnIfStmt(IfLoc, IsConstexpr, LParenLoc, Init, Cond,
|
||||
RParenLoc, Then, ElseLoc, Else);
|
||||
return getSema().ActOnIfStmt(IfLoc, Kind, LParenLoc, Init, Cond, RParenLoc,
|
||||
Then, ElseLoc, Else);
|
||||
}
|
||||
|
||||
/// Start building a new switch statement.
|
||||
|
@ -7371,13 +7371,16 @@ TreeTransform<Derived>::TransformIfStmt(IfStmt *S) {
|
|||
if (Init.isInvalid())
|
||||
return StmtError();
|
||||
|
||||
// Transform the condition
|
||||
Sema::ConditionResult Cond = getDerived().TransformCondition(
|
||||
S->getIfLoc(), S->getConditionVariable(), S->getCond(),
|
||||
S->isConstexpr() ? Sema::ConditionKind::ConstexprIf
|
||||
: Sema::ConditionKind::Boolean);
|
||||
if (Cond.isInvalid())
|
||||
return StmtError();
|
||||
Sema::ConditionResult Cond;
|
||||
if (!S->isConsteval()) {
|
||||
// Transform the condition
|
||||
Cond = getDerived().TransformCondition(
|
||||
S->getIfLoc(), S->getConditionVariable(), S->getCond(),
|
||||
S->isConstexpr() ? Sema::ConditionKind::ConstexprIf
|
||||
: Sema::ConditionKind::Boolean);
|
||||
if (Cond.isInvalid())
|
||||
return StmtError();
|
||||
}
|
||||
|
||||
// If this is a constexpr if, determine which arm we should instantiate.
|
||||
llvm::Optional<bool> ConstexprConditionValue;
|
||||
|
@ -7410,7 +7413,7 @@ TreeTransform<Derived>::TransformIfStmt(IfStmt *S) {
|
|||
return S;
|
||||
|
||||
return getDerived().RebuildIfStmt(
|
||||
S->getIfLoc(), S->isConstexpr(), S->getLParenLoc(), Cond,
|
||||
S->getIfLoc(), S->getStatementKind(), S->getLParenLoc(), Cond,
|
||||
S->getRParenLoc(), Init.get(), Then.get(), S->getElseLoc(), Else.get());
|
||||
}
|
||||
|
||||
|
|
|
@ -213,11 +213,11 @@ void ASTStmtReader::VisitAttributedStmt(AttributedStmt *S) {
|
|||
void ASTStmtReader::VisitIfStmt(IfStmt *S) {
|
||||
VisitStmt(S);
|
||||
|
||||
S->setConstexpr(Record.readInt());
|
||||
bool HasElse = Record.readInt();
|
||||
bool HasVar = Record.readInt();
|
||||
bool HasInit = Record.readInt();
|
||||
|
||||
S->setStatementKind(static_cast<IfStatementKind>(Record.readInt()));
|
||||
S->setCond(Record.readSubExpr());
|
||||
S->setThen(Record.readSubStmt());
|
||||
if (HasElse)
|
||||
|
@ -2753,9 +2753,9 @@ Stmt *ASTReader::ReadStmtFromStream(ModuleFile &F) {
|
|||
case STMT_IF:
|
||||
S = IfStmt::CreateEmpty(
|
||||
Context,
|
||||
/* HasElse=*/Record[ASTStmtReader::NumStmtFields + 1],
|
||||
/* HasVar=*/Record[ASTStmtReader::NumStmtFields + 2],
|
||||
/* HasInit=*/Record[ASTStmtReader::NumStmtFields + 3]);
|
||||
/* HasElse=*/Record[ASTStmtReader::NumStmtFields],
|
||||
/* HasVar=*/Record[ASTStmtReader::NumStmtFields + 1],
|
||||
/* HasInit=*/Record[ASTStmtReader::NumStmtFields + 2]);
|
||||
break;
|
||||
|
||||
case STMT_SWITCH:
|
||||
|
|
|
@ -138,11 +138,10 @@ void ASTStmtWriter::VisitIfStmt(IfStmt *S) {
|
|||
bool HasVar = S->getConditionVariableDeclStmt() != nullptr;
|
||||
bool HasInit = S->getInit() != nullptr;
|
||||
|
||||
Record.push_back(S->isConstexpr());
|
||||
Record.push_back(HasElse);
|
||||
Record.push_back(HasVar);
|
||||
Record.push_back(HasInit);
|
||||
|
||||
Record.push_back(static_cast<uint64_t>(S->getStatementKind()));
|
||||
Record.AddStmt(S->getCond());
|
||||
Record.AddStmt(S->getThen());
|
||||
if (HasElse)
|
||||
|
|
|
@ -1339,7 +1339,10 @@ bool PluralMisuseChecker::MethodCrawler::EndVisitIfStmt(IfStmt *I) {
|
|||
}
|
||||
|
||||
bool PluralMisuseChecker::MethodCrawler::VisitIfStmt(const IfStmt *I) {
|
||||
const Expr *Condition = I->getCond()->IgnoreParenImpCasts();
|
||||
const Expr *Condition = I->getCond();
|
||||
if (!Condition)
|
||||
return true;
|
||||
Condition = Condition->IgnoreParenImpCasts();
|
||||
if (isCheckingPlurality(Condition)) {
|
||||
MatchingStatements.push_back(I);
|
||||
InMatchingStatement = true;
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
// RUN: %clang_cc1 -std=c++2b -fsyntax-only -fexperimental-new-constant-interpreter %s -verify
|
||||
// RUN: %clang_cc1 -std=c++2b -fsyntax-only %s -verify
|
||||
// expected-no-diagnostics
|
||||
|
||||
constexpr void f() {
|
||||
int i = 0;
|
||||
if consteval {
|
||||
i = 1;
|
||||
}
|
||||
else {
|
||||
i = 2;
|
||||
}
|
||||
|
||||
if consteval {
|
||||
i = 1;
|
||||
}
|
||||
|
||||
if !consteval {
|
||||
i = 1;
|
||||
}
|
||||
|
||||
if !consteval {
|
||||
i = 1;
|
||||
}
|
||||
else {
|
||||
i = 1;
|
||||
}
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
// RUN: %clang_cc1 -triple x86_64-pc-linux -std=c++17 -ast-dump=json %s | FileCheck %s
|
||||
// RUN: %clang_cc1 -triple x86_64-pc-linux -std=c++2b -ast-dump=json %s | FileCheck %s
|
||||
|
||||
void func(int val) {
|
||||
if (val)
|
||||
|
@ -24,11 +24,20 @@ void func(int val) {
|
|||
|
||||
if (int i = 12; i)
|
||||
;
|
||||
|
||||
if consteval {}
|
||||
|
||||
if consteval {} else {}
|
||||
|
||||
if not consteval {}
|
||||
|
||||
if not consteval {} else {}
|
||||
}
|
||||
|
||||
// NOTE: CHECK lines have been autogenerated by gen_ast_dump_json_test.py
|
||||
// using --filters=IfStmt
|
||||
|
||||
|
||||
// CHECK: "kind": "IfStmt",
|
||||
// CHECK-NEXT: "range": {
|
||||
// CHECK-NEXT: "begin": {
|
||||
|
@ -140,6 +149,7 @@ void func(int val) {
|
|||
// CHECK-NEXT: ]
|
||||
// CHECK-NEXT: }
|
||||
|
||||
|
||||
// CHECK: "kind": "IfStmt",
|
||||
// CHECK-NEXT: "range": {
|
||||
// CHECK-NEXT: "begin": {
|
||||
|
@ -269,6 +279,7 @@ void func(int val) {
|
|||
// CHECK-NEXT: ]
|
||||
// CHECK-NEXT: }
|
||||
|
||||
|
||||
// CHECK: "kind": "IfStmt",
|
||||
// CHECK-NEXT: "range": {
|
||||
// CHECK-NEXT: "begin": {
|
||||
|
@ -511,6 +522,7 @@ void func(int val) {
|
|||
// CHECK-NEXT: ]
|
||||
// CHECK-NEXT: }
|
||||
|
||||
|
||||
// CHECK: "kind": "IfStmt",
|
||||
// CHECK-NEXT: "range": {
|
||||
// CHECK-NEXT: "begin": {
|
||||
|
@ -637,6 +649,7 @@ void func(int val) {
|
|||
// CHECK-NEXT: ]
|
||||
// CHECK-NEXT: }
|
||||
|
||||
|
||||
// CHECK: "kind": "IfStmt",
|
||||
// CHECK-NEXT: "range": {
|
||||
// CHECK-NEXT: "begin": {
|
||||
|
@ -818,6 +831,7 @@ void func(int val) {
|
|||
// CHECK-NEXT: ]
|
||||
// CHECK-NEXT: }
|
||||
|
||||
|
||||
// CHECK: "kind": "IfStmt",
|
||||
// CHECK-NEXT: "range": {
|
||||
// CHECK-NEXT: "begin": {
|
||||
|
@ -998,3 +1012,183 @@ void func(int val) {
|
|||
// CHECK-NEXT: }
|
||||
// CHECK-NEXT: ]
|
||||
// CHECK-NEXT: }
|
||||
|
||||
|
||||
// CHECK: "kind": "IfStmt",
|
||||
// CHECK-NEXT: "range": {
|
||||
// CHECK-NEXT: "begin": {
|
||||
// CHECK-NEXT: "offset": 298,
|
||||
// CHECK-NEXT: "line": 28,
|
||||
// CHECK-NEXT: "col": 3,
|
||||
// CHECK-NEXT: "tokLen": 2
|
||||
// CHECK-NEXT: },
|
||||
// CHECK-NEXT: "end": {
|
||||
// CHECK-NEXT: "offset": 312,
|
||||
// CHECK-NEXT: "col": 17,
|
||||
// CHECK-NEXT: "tokLen": 1
|
||||
// CHECK-NEXT: }
|
||||
// CHECK-NEXT: },
|
||||
// CHECK-NEXT: "isConsteval": true,
|
||||
// CHECK-NEXT: "inner": [
|
||||
// CHECK-NEXT: {
|
||||
// CHECK-NEXT: "id": "0x{{.*}}",
|
||||
// CHECK-NEXT: "kind": "CompoundStmt",
|
||||
// CHECK-NEXT: "range": {
|
||||
// CHECK-NEXT: "begin": {
|
||||
// CHECK-NEXT: "offset": 311,
|
||||
// CHECK-NEXT: "col": 16,
|
||||
// CHECK-NEXT: "tokLen": 1
|
||||
// CHECK-NEXT: },
|
||||
// CHECK-NEXT: "end": {
|
||||
// CHECK-NEXT: "offset": 312,
|
||||
// CHECK-NEXT: "col": 17,
|
||||
// CHECK-NEXT: "tokLen": 1
|
||||
// CHECK-NEXT: }
|
||||
// CHECK-NEXT: }
|
||||
// CHECK-NEXT: }
|
||||
// CHECK-NEXT: ]
|
||||
// CHECK-NEXT: }
|
||||
|
||||
|
||||
// CHECK: "kind": "IfStmt",
|
||||
// CHECK-NEXT: "range": {
|
||||
// CHECK-NEXT: "begin": {
|
||||
// CHECK-NEXT: "offset": 317,
|
||||
// CHECK-NEXT: "line": 30,
|
||||
// CHECK-NEXT: "col": 3,
|
||||
// CHECK-NEXT: "tokLen": 2
|
||||
// CHECK-NEXT: },
|
||||
// CHECK-NEXT: "end": {
|
||||
// CHECK-NEXT: "offset": 339,
|
||||
// CHECK-NEXT: "col": 25,
|
||||
// CHECK-NEXT: "tokLen": 1
|
||||
// CHECK-NEXT: }
|
||||
// CHECK-NEXT: },
|
||||
// CHECK-NEXT: "hasElse": true,
|
||||
// CHECK-NEXT: "isConsteval": true,
|
||||
// CHECK-NEXT: "inner": [
|
||||
// CHECK-NEXT: {
|
||||
// CHECK-NEXT: "id": "0x{{.*}}",
|
||||
// CHECK-NEXT: "kind": "CompoundStmt",
|
||||
// CHECK-NEXT: "range": {
|
||||
// CHECK-NEXT: "begin": {
|
||||
// CHECK-NEXT: "offset": 330,
|
||||
// CHECK-NEXT: "col": 16,
|
||||
// CHECK-NEXT: "tokLen": 1
|
||||
// CHECK-NEXT: },
|
||||
// CHECK-NEXT: "end": {
|
||||
// CHECK-NEXT: "offset": 331,
|
||||
// CHECK-NEXT: "col": 17,
|
||||
// CHECK-NEXT: "tokLen": 1
|
||||
// CHECK-NEXT: }
|
||||
// CHECK-NEXT: }
|
||||
// CHECK-NEXT: },
|
||||
// CHECK-NEXT: {
|
||||
// CHECK-NEXT: "id": "0x{{.*}}",
|
||||
// CHECK-NEXT: "kind": "CompoundStmt",
|
||||
// CHECK-NEXT: "range": {
|
||||
// CHECK-NEXT: "begin": {
|
||||
// CHECK-NEXT: "offset": 338,
|
||||
// CHECK-NEXT: "col": 24,
|
||||
// CHECK-NEXT: "tokLen": 1
|
||||
// CHECK-NEXT: },
|
||||
// CHECK-NEXT: "end": {
|
||||
// CHECK-NEXT: "offset": 339,
|
||||
// CHECK-NEXT: "col": 25,
|
||||
// CHECK-NEXT: "tokLen": 1
|
||||
// CHECK-NEXT: }
|
||||
// CHECK-NEXT: }
|
||||
// CHECK-NEXT: }
|
||||
// CHECK-NEXT: ]
|
||||
// CHECK-NEXT: }
|
||||
|
||||
|
||||
// CHECK: "kind": "IfStmt",
|
||||
// CHECK-NEXT: "range": {
|
||||
// CHECK-NEXT: "begin": {
|
||||
// CHECK-NEXT: "offset": 344,
|
||||
// CHECK-NEXT: "line": 32,
|
||||
// CHECK-NEXT: "col": 3,
|
||||
// CHECK-NEXT: "tokLen": 2
|
||||
// CHECK-NEXT: },
|
||||
// CHECK-NEXT: "end": {
|
||||
// CHECK-NEXT: "offset": 362,
|
||||
// CHECK-NEXT: "col": 21,
|
||||
// CHECK-NEXT: "tokLen": 1
|
||||
// CHECK-NEXT: }
|
||||
// CHECK-NEXT: },
|
||||
// CHECK-NEXT: "isConsteval": true,
|
||||
// CHECK-NEXT: "constevalIsNegated": true,
|
||||
// CHECK-NEXT: "inner": [
|
||||
// CHECK-NEXT: {
|
||||
// CHECK-NEXT: "id": "0x{{.*}}",
|
||||
// CHECK-NEXT: "kind": "CompoundStmt",
|
||||
// CHECK-NEXT: "range": {
|
||||
// CHECK-NEXT: "begin": {
|
||||
// CHECK-NEXT: "offset": 361,
|
||||
// CHECK-NEXT: "col": 20,
|
||||
// CHECK-NEXT: "tokLen": 1
|
||||
// CHECK-NEXT: },
|
||||
// CHECK-NEXT: "end": {
|
||||
// CHECK-NEXT: "offset": 362,
|
||||
// CHECK-NEXT: "col": 21,
|
||||
// CHECK-NEXT: "tokLen": 1
|
||||
// CHECK-NEXT: }
|
||||
// CHECK-NEXT: }
|
||||
// CHECK-NEXT: }
|
||||
// CHECK-NEXT: ]
|
||||
// CHECK-NEXT: }
|
||||
|
||||
|
||||
// CHECK: "kind": "IfStmt",
|
||||
// CHECK-NEXT: "range": {
|
||||
// CHECK-NEXT: "begin": {
|
||||
// CHECK-NEXT: "offset": 367,
|
||||
// CHECK-NEXT: "line": 34,
|
||||
// CHECK-NEXT: "col": 3,
|
||||
// CHECK-NEXT: "tokLen": 2
|
||||
// CHECK-NEXT: },
|
||||
// CHECK-NEXT: "end": {
|
||||
// CHECK-NEXT: "offset": 393,
|
||||
// CHECK-NEXT: "col": 29,
|
||||
// CHECK-NEXT: "tokLen": 1
|
||||
// CHECK-NEXT: }
|
||||
// CHECK-NEXT: },
|
||||
// CHECK-NEXT: "hasElse": true,
|
||||
// CHECK-NEXT: "isConsteval": true,
|
||||
// CHECK-NEXT: "constevalIsNegated": true,
|
||||
// CHECK-NEXT: "inner": [
|
||||
// CHECK-NEXT: {
|
||||
// CHECK-NEXT: "id": "0x{{.*}}",
|
||||
// CHECK-NEXT: "kind": "CompoundStmt",
|
||||
// CHECK-NEXT: "range": {
|
||||
// CHECK-NEXT: "begin": {
|
||||
// CHECK-NEXT: "offset": 384,
|
||||
// CHECK-NEXT: "col": 20,
|
||||
// CHECK-NEXT: "tokLen": 1
|
||||
// CHECK-NEXT: },
|
||||
// CHECK-NEXT: "end": {
|
||||
// CHECK-NEXT: "offset": 385,
|
||||
// CHECK-NEXT: "col": 21,
|
||||
// CHECK-NEXT: "tokLen": 1
|
||||
// CHECK-NEXT: }
|
||||
// CHECK-NEXT: }
|
||||
// CHECK-NEXT: },
|
||||
// CHECK-NEXT: {
|
||||
// CHECK-NEXT: "id": "0x{{.*}}",
|
||||
// CHECK-NEXT: "kind": "CompoundStmt",
|
||||
// CHECK-NEXT: "range": {
|
||||
// CHECK-NEXT: "begin": {
|
||||
// CHECK-NEXT: "offset": 392,
|
||||
// CHECK-NEXT: "col": 28,
|
||||
// CHECK-NEXT: "tokLen": 1
|
||||
// CHECK-NEXT: },
|
||||
// CHECK-NEXT: "end": {
|
||||
// CHECK-NEXT: "offset": 393,
|
||||
// CHECK-NEXT: "col": 29,
|
||||
// CHECK-NEXT: "tokLen": 1
|
||||
// CHECK-NEXT: }
|
||||
// CHECK-NEXT: }
|
||||
// CHECK-NEXT: }
|
||||
// CHECK-NEXT: ]
|
||||
// CHECK-NEXT: }
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
// Test without serialization:
|
||||
// RUN: %clang_cc1 -std=c++2a -triple x86_64-linux-gnu -fcxx-exceptions -ast-dump %s \
|
||||
// RUN: %clang_cc1 -std=c++2b -triple x86_64-linux-gnu -fcxx-exceptions -ast-dump %s \
|
||||
// RUN: | FileCheck -strict-whitespace %s
|
||||
//
|
||||
// Test with serialization:
|
||||
// RUN: %clang_cc1 -std=c++2a -triple x86_64-linux-gnu -fcxx-exceptions -emit-pch -o %t %s
|
||||
// RUN: %clang_cc1 -x c++ -std=c++2a -triple x86_64-linux-gnu -fcxx-exceptions -include-pch %t -ast-dump-all /dev/null \
|
||||
// RUN: %clang_cc1 -std=c++2b -triple x86_64-linux-gnu -fcxx-exceptions -emit-pch -o %t %s
|
||||
// RUN: %clang_cc1 -x c++ -std=c++2b -triple x86_64-linux-gnu -fcxx-exceptions -include-pch %t -ast-dump-all /dev/null \
|
||||
// RUN: | sed -e "s/ <undeserialized declarations>//" -e "s/ imported//" \
|
||||
// RUN: | FileCheck -strict-whitespace %s
|
||||
|
||||
|
@ -154,6 +154,16 @@ void TestIf(bool b) {
|
|||
// CHECK-NEXT: IntegerLiteral
|
||||
// CHECK-NEXT: NullStmt
|
||||
// CHECK-NEXT: NullStmt
|
||||
|
||||
if consteval {}
|
||||
// CHECK: IfStmt 0x{{[^ ]*}} <line:[[@LINE-1]]:3, col:17> consteval
|
||||
// CHECK-NEXT: CompoundStmt
|
||||
|
||||
if ! consteval {}
|
||||
else {}
|
||||
// CHECK: IfStmt 0x{{[^ ]*}} <line:[[@LINE-2]]:3, line:[[@LINE-1]]:9> has_else !consteval
|
||||
// CHECK-NEXT: CompoundStmt
|
||||
// CHECK-NEXT: CompoundStmt
|
||||
}
|
||||
|
||||
struct Container {
|
||||
|
|
|
@ -0,0 +1,158 @@
|
|||
// RUN: %clang_cc1 -std=c++2b -verify %s
|
||||
|
||||
void test_consteval() {
|
||||
if consteval ({(void)1;}); // expected-error {{expected { after consteval}}
|
||||
if consteval (void) 0; // expected-error {{expected { after consteval}}
|
||||
if consteval {
|
||||
(void)0;
|
||||
} else (void)0; // expected-error {{expected { after else}}
|
||||
|
||||
static_assert([] {
|
||||
if consteval {
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}() == 0);
|
||||
|
||||
static_assert([] {
|
||||
if consteval {
|
||||
return 0;
|
||||
} else {
|
||||
return 1;
|
||||
}
|
||||
}() == 0);
|
||||
|
||||
static_assert([] {
|
||||
if !consteval {
|
||||
return 0;
|
||||
} else {
|
||||
return 1;
|
||||
}
|
||||
}() == 1);
|
||||
|
||||
static_assert([] {
|
||||
if not consteval {
|
||||
return 0;
|
||||
}
|
||||
return 1;
|
||||
}() == 1);
|
||||
|
||||
if consteval [[likely]] { // expected-warning {{attribute 'likely' has no effect when annotating an 'if consteval' statement}}\
|
||||
// expected-note 2{{annotating the 'if consteval' statement here}}
|
||||
|
||||
|
||||
}
|
||||
else [[unlikely]] { // expected-warning {{attribute 'unlikely' has no effect when annotating an 'if consteval' statement}}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void test_consteval_jumps() {
|
||||
if consteval { // expected-note 4{{jump enters controlled statement of consteval if}}
|
||||
goto a;
|
||||
goto b; // expected-error {{cannot jump from this goto statement to its label}}
|
||||
a:;
|
||||
} else {
|
||||
goto b;
|
||||
goto a; // expected-error {{cannot jump from this goto statement to its label}}
|
||||
b:;
|
||||
}
|
||||
goto a; // expected-error {{cannot jump from this goto statement to its label}}
|
||||
goto b; // expected-error {{cannot jump from this goto statement to its label}}
|
||||
}
|
||||
|
||||
void test_consteval_switch() {
|
||||
int x = 42;
|
||||
switch (x) {
|
||||
if consteval { // expected-note 2{{jump enters controlled statement of consteval if}}
|
||||
case 1:; // expected-error {{cannot jump from switch statement to this case label}}
|
||||
default:; // expected-error {{cannot jump from switch statement to this case label}}
|
||||
} else {
|
||||
}
|
||||
}
|
||||
switch (x) {
|
||||
if consteval { // expected-note 2{{jump enters controlled statement of consteval if}}
|
||||
} else {
|
||||
case 2:; // expected-error {{cannot jump from switch statement to this case label}}
|
||||
default:; // expected-error {{cannot jump from switch statement to this case label}}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
consteval int f(int i) { return i; }
|
||||
constexpr int g(int i) {
|
||||
if consteval {
|
||||
return f(i);
|
||||
} else {
|
||||
return 42;
|
||||
}
|
||||
}
|
||||
static_assert(g(10) == 10);
|
||||
|
||||
constexpr int h(int i) { // expected-note {{declared here}}
|
||||
if !consteval {
|
||||
return f(i); // expected-error {{call to consteval function 'f' is not a constant expression}}\
|
||||
// expected-note {{cannot be used in a constant expression}}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
consteval void warn_in_consteval() {
|
||||
if consteval { // expected-warning {{consteval if is always true in an immediate context}}
|
||||
if consteval {} // expected-warning {{consteval if is always true in an immediate context}}
|
||||
}
|
||||
}
|
||||
|
||||
constexpr void warn_in_consteval2() {
|
||||
if consteval {
|
||||
if consteval {} // expected-warning {{consteval if is always true in an immediate context}}
|
||||
}
|
||||
}
|
||||
|
||||
auto y = []() consteval {
|
||||
if consteval { // expected-warning {{consteval if is always true in an immediate context}}
|
||||
if consteval {} // expected-warning {{consteval if is always true in an immediate context}}
|
||||
}
|
||||
};
|
||||
|
||||
namespace test_transform {
|
||||
int f(auto n) {
|
||||
if consteval {
|
||||
n.foo; //expected-error {{no member named}}
|
||||
}
|
||||
else {
|
||||
}
|
||||
|
||||
if !consteval {
|
||||
n.foo; //expected-error {{no member named}}
|
||||
}
|
||||
else {
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
constexpr int g(auto n) {
|
||||
if consteval {
|
||||
}
|
||||
else {
|
||||
n.foo; //expected-error {{no member named}}
|
||||
}
|
||||
|
||||
if !consteval {
|
||||
}
|
||||
else {
|
||||
n.foo; //expected-error {{no member named}}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct S {};
|
||||
void test() {
|
||||
f(S{}); //expected-note {{in instantiation}}
|
||||
g(S{}); //expected-note {{in instantiation}}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
// RUN: %clang_cc1 -std=c++2b %s -emit-llvm -o - | FileCheck %s
|
||||
|
||||
void should_be_used_1();
|
||||
void should_be_used_2();
|
||||
void should_be_used_3();
|
||||
constexpr void should_not_be_used() {}
|
||||
|
||||
constexpr void f() {
|
||||
if consteval {
|
||||
should_not_be_used(); // CHECK-NOT: call {{.*}}should_not_be_used
|
||||
} else {
|
||||
should_be_used_1(); // CHECK: call {{.*}}should_be_used_1
|
||||
}
|
||||
|
||||
if !consteval {
|
||||
should_be_used_2(); // CHECK: call {{.*}}should_be_used_2
|
||||
}
|
||||
|
||||
if !consteval {
|
||||
should_be_used_3(); // CHECK: call {{.*}}should_be_used_3
|
||||
} else {
|
||||
should_not_be_used(); // CHECK-NOT: call {{.*}}should_not_be_used
|
||||
}
|
||||
}
|
||||
|
||||
void g() {
|
||||
f();
|
||||
}
|
Loading…
Reference in New Issue