cor3ntin updated this revision to Diff 375615.
cor3ntin added a comment.
- Use a dedicated enums
- Add more tests
- Improve CodeGen tests
Repository:
rG LLVM Github Monorepo
CHANGES SINCE LAST ACTION
https://reviews.llvm.org/D110482/new/
https://reviews.llvm.org/D110482
Files:
clang/include/clang/AST/Stmt.h
clang/include/clang/Basic/DiagnosticParseKinds.td
clang/include/clang/Basic/DiagnosticSemaKinds.td
clang/include/clang/Basic/Specifiers.h
clang/include/clang/Sema/Sema.h
clang/lib/AST/ASTImporter.cpp
clang/lib/AST/ExprConstant.cpp
clang/lib/AST/Interp/ByteCodeStmtGen.cpp
clang/lib/AST/JSONNodeDumper.cpp
clang/lib/AST/Stmt.cpp
clang/lib/AST/StmtPrinter.cpp
clang/lib/AST/TextNodeDumper.cpp
clang/lib/Analysis/BodyFarm.cpp
clang/lib/Analysis/CFG.cpp
clang/lib/CodeGen/CGStmt.cpp
clang/lib/CodeGen/CodeGenPGO.cpp
clang/lib/Frontend/InitPreprocessor.cpp
clang/lib/Parse/ParseStmt.cpp
clang/lib/Sema/JumpDiagnostics.cpp
clang/lib/Sema/SemaDeclCXX.cpp
clang/lib/Sema/SemaExpr.cpp
clang/lib/Sema/SemaExprMember.cpp
clang/lib/Sema/SemaLambda.cpp
clang/lib/Sema/SemaStmt.cpp
clang/lib/Sema/TreeTransform.h
clang/lib/Serialization/ASTReaderStmt.cpp
clang/lib/Serialization/ASTWriterStmt.cpp
clang/lib/StaticAnalyzer/Checkers/LocalizationChecker.cpp
clang/test/AST/ast-dump-if-json.cpp
clang/test/AST/ast-dump-stmt.cpp
clang/test/CXX/stmt.stmt/stmt.select/stmt.if/p4.cpp
clang/test/CodeGenCXX/cxx2b-consteval-if.cpp
Index: clang/test/CodeGenCXX/cxx2b-consteval-if.cpp
===================================================================
--- /dev/null
+++ clang/test/CodeGenCXX/cxx2b-consteval-if.cpp
@@ -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();
+}
Index: clang/test/CXX/stmt.stmt/stmt.select/stmt.if/p4.cpp
===================================================================
--- /dev/null
+++ clang/test/CXX/stmt.stmt/stmt.select/stmt.if/p4.cpp
@@ -0,0 +1,147 @@
+// RUN: %clang_cc1 -std=c++2b -verify %s
+
+void test_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);
+}
+
+void test_consteval_jumps() {
+ if consteval { // expected-note 4{{jump enters controlled statement of if consteval}}
+ 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 if consteval}}
+ 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 if consteval}}
+ } 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 {{if consteval is always true in an immediate context}}
+ if consteval {} // expected-warning {{if consteval is always true in an immediate context}}
+ }
+}
+
+constexpr void warn_in_consteval2() {
+ if consteval {
+ if consteval {} // expected-warning {{if consteval is always true in an immediate context}}
+ }
+}
+
+auto y = []() consteval {
+ if consteval { // expected-warning {{if consteval is always true in an immediate context}}
+ if consteval {} // expected-warning {{if consteval 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}}
+}
+
+}
\ No newline at end of file
Index: clang/test/AST/ast-dump-stmt.cpp
===================================================================
--- clang/test/AST/ast-dump-stmt.cpp
+++ clang/test/AST/ast-dump-stmt.cpp
@@ -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 @@
// 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 {
Index: clang/test/AST/ast-dump-if-json.cpp
===================================================================
--- clang/test/AST/ast-dump-if-json.cpp
+++ clang/test/AST/ast-dump-if-json.cpp
@@ -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 @@
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 @@
// CHECK-NEXT: ]
// CHECK-NEXT: }
+
// CHECK: "kind": "IfStmt",
// CHECK-NEXT: "range": {
// CHECK-NEXT: "begin": {
@@ -269,6 +279,7 @@
// CHECK-NEXT: ]
// CHECK-NEXT: }
+
// CHECK: "kind": "IfStmt",
// CHECK-NEXT: "range": {
// CHECK-NEXT: "begin": {
@@ -511,6 +522,7 @@
// CHECK-NEXT: ]
// CHECK-NEXT: }
+
// CHECK: "kind": "IfStmt",
// CHECK-NEXT: "range": {
// CHECK-NEXT: "begin": {
@@ -637,6 +649,7 @@
// CHECK-NEXT: ]
// CHECK-NEXT: }
+
// CHECK: "kind": "IfStmt",
// CHECK-NEXT: "range": {
// CHECK-NEXT: "begin": {
@@ -818,6 +831,7 @@
// CHECK-NEXT: ]
// CHECK-NEXT: }
+
// CHECK: "kind": "IfStmt",
// CHECK-NEXT: "range": {
// CHECK-NEXT: "begin": {
@@ -998,3 +1012,183 @@
// 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: }
Index: clang/lib/StaticAnalyzer/Checkers/LocalizationChecker.cpp
===================================================================
--- clang/lib/StaticAnalyzer/Checkers/LocalizationChecker.cpp
+++ clang/lib/StaticAnalyzer/Checkers/LocalizationChecker.cpp
@@ -1339,7 +1339,10 @@
}
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;
Index: clang/lib/Serialization/ASTWriterStmt.cpp
===================================================================
--- clang/lib/Serialization/ASTWriterStmt.cpp
+++ clang/lib/Serialization/ASTWriterStmt.cpp
@@ -138,11 +138,10 @@
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)
Index: clang/lib/Serialization/ASTReaderStmt.cpp
===================================================================
--- clang/lib/Serialization/ASTReaderStmt.cpp
+++ clang/lib/Serialization/ASTReaderStmt.cpp
@@ -213,11 +213,11 @@
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 @@
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:
Index: clang/lib/Sema/TreeTransform.h
===================================================================
--- clang/lib/Sema/TreeTransform.h
+++ clang/lib/Sema/TreeTransform.h
@@ -1320,12 +1320,12 @@
///
/// 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 @@
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->isConstevalOrNegatedConsteval()) {
+ // 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 @@
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());
}
Index: clang/lib/Sema/SemaStmt.cpp
===================================================================
--- clang/lib/Sema/SemaStmt.cpp
+++ clang/lib/Sema/SemaStmt.cpp
@@ -858,7 +858,8 @@
};
}
-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,
@@ -871,17 +872,24 @@
IfLoc),
false);
+ bool ConstevalOrNegatedConsteval =
+ StatementKind == IfStatementKind::Consteval ||
+ 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(),
@@ -908,11 +916,26 @@
}
}
- return BuildIfStmt(IfLoc, IsConstexpr, LParenLoc, InitStmt, Cond, RParenLoc,
+ if (ConstevalOrNegatedConsteval) {
+ bool Immediate = isImmediateFunctionContext();
+ if (CurContext->isFunctionOrMethod()) {
+ const Decl *D = Decl::castFromDeclContext(CurContext);
+ if (D->getAsFunction() && D->getAsFunction()->isConsteval()) {
+ Immediate = true;
+ }
+ }
+ if (isUnevaluatedContext() || Immediate) {
+ Diags.Report(IfLoc, diag::warn_if_consteval_always_true)
+ << (Immediate ? 1 : 0);
+ }
+ }
+
+ 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,
@@ -920,12 +943,13 @@
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 {
Index: clang/lib/Sema/SemaLambda.cpp
===================================================================
--- clang/lib/Sema/SemaLambda.cpp
+++ clang/lib/Sema/SemaLambda.cpp
@@ -1245,7 +1245,7 @@
// cleanups from the enclosing full-expression.
PushExpressionEvaluationContext(
LSI->CallOperator->isConsteval()
- ? ExpressionEvaluationContext::ConstantEvaluated
+ ? ExpressionEvaluationContext::ImmediateFunctionContext
: ExpressionEvaluationContext::PotentiallyEvaluated);
}
@@ -1948,6 +1948,7 @@
// 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).
Index: clang/lib/Sema/SemaExprMember.cpp
===================================================================
--- clang/lib/Sema/SemaExprMember.cpp
+++ clang/lib/Sema/SemaExprMember.cpp
@@ -144,6 +144,7 @@
case Sema::ExpressionEvaluationContext::DiscardedStatement:
case Sema::ExpressionEvaluationContext::ConstantEvaluated:
+ case Sema::ExpressionEvaluationContext::ImmediateFunctionContext:
case Sema::ExpressionEvaluationContext::PotentiallyEvaluated:
case Sema::ExpressionEvaluationContext::PotentiallyEvaluatedIfUsed:
break;
Index: clang/lib/Sema/SemaExpr.cpp
===================================================================
--- clang/lib/Sema/SemaExpr.cpp
+++ clang/lib/Sema/SemaExpr.cpp
@@ -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"
@@ -16641,7 +16642,7 @@
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.
@@ -16912,6 +16913,8 @@
// 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:
@@ -17034,6 +17037,7 @@
return OdrUseContext::None;
case Sema::ExpressionEvaluationContext::ConstantEvaluated:
+ case Sema::ExpressionEvaluationContext::ImmediateFunctionContext:
case Sema::ExpressionEvaluationContext::PotentiallyEvaluated:
Result = OdrUseContext::Used;
break;
@@ -18925,6 +18929,7 @@
break;
case ExpressionEvaluationContext::ConstantEvaluated:
+ case ExpressionEvaluationContext::ImmediateFunctionContext:
// Relevant diagnostics should be produced by constant evaluation.
break;
Index: clang/lib/Sema/SemaDeclCXX.cpp
===================================================================
--- clang/lib/Sema/SemaDeclCXX.cpp
+++ clang/lib/Sema/SemaDeclCXX.cpp
@@ -8202,7 +8202,7 @@
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 @@
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);
}
Index: clang/lib/Sema/JumpDiagnostics.cpp
===================================================================
--- clang/lib/Sema/JumpDiagnostics.cpp
+++ clang/lib/Sema/JumpDiagnostics.cpp
@@ -377,11 +377,15 @@
case Stmt::IfStmtClass: {
IfStmt *IS = cast<IfStmt>(S);
- if (!(IS->isConstexpr() || IS->isObjCAvailabilityCheck()))
+ if (!(IS->isConstexpr() || IS->isConstevalOrNegatedConsteval() ||
+ 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->isConstevalOrNegatedConsteval())
+ Diag = diag::note_protected_by_if_consteval;
if (VarDecl *Var = IS->getConditionVariable())
BuildScopeInformation(Var, ParentScope);
@@ -389,7 +393,9 @@
// 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->isConstevalOrNegatedConsteval())
+ BuildScopeInformation(IS->getCond(), NewParentScope);
// Jumps into either arm of an 'if constexpr' are not allowed.
NewParentScope = Scopes.size();
Index: clang/lib/Parse/ParseStmt.cpp
===================================================================
--- clang/lib/Parse/ParseStmt.cpp
+++ clang/lib/Parse/ParseStmt.cpp
@@ -1338,20 +1338,35 @@
/// 'if' '(' expression ')' statement 'else' statement
/// [C++] 'if' '(' condition ')' statement
/// [C++] 'if' '(' condition ')' statement 'else' statement
+/// [C++] 'if' consteval 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 +1393,18 @@
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 +1436,16 @@
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 +1480,16 @@
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 +1517,35 @@
return StmtError();
}
+ if (IsConsteval) {
+ if (!isa_and_nonnull<class CompoundStmt>(ThenStmt.get())) {
+ Diag(ConstevalLoc, diag::err_expected_after) << "consteval"
+ << "{";
+ return StmtError();
+ }
+ if (!ElseStmt.isUnset() &&
+ !isa_and_nonnull<class CompoundStmt>(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::Consteval;
+
+ return Actions.ActOnIfStmt(IfLoc, Kind, LParen, InitStmt.get(), Cond, RParen,
+ ThenStmt.get(), ElseLoc, ElseStmt.get());
}
/// ParseSwitchStatement
Index: clang/lib/Frontend/InitPreprocessor.cpp
===================================================================
--- clang/lib/Frontend/InitPreprocessor.cpp
+++ clang/lib/Frontend/InitPreprocessor.cpp
@@ -607,6 +607,7 @@
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");
Index: clang/lib/CodeGen/CodeGenPGO.cpp
===================================================================
--- clang/lib/CodeGen/CodeGenPGO.cpp
+++ clang/lib/CodeGen/CodeGenPGO.cpp
@@ -649,6 +649,14 @@
void VisitIfStmt(const IfStmt *S) {
RecordStmtCount(S);
+
+ if (S->isConstevalOrNegatedConsteval()) {
+ const Stmt *Stm = S->isNegatedConsteval() ? S->getThen() : S->getElse();
+ if (Stm)
+ Visit(Stm);
+ return;
+ }
+
uint64_t ParentCount = CurrentCount;
if (S->getInit())
Visit(S->getInit());
Index: clang/lib/CodeGen/CGStmt.cpp
===================================================================
--- clang/lib/CodeGen/CGStmt.cpp
+++ clang/lib/CodeGen/CGStmt.cpp
@@ -712,6 +712,17 @@
}
void CodeGenFunction::EmitIfStmt(const IfStmt &S) {
+ // The else branch of a consteval statement is always the only branch that can
+ // be runtime evaluated
+ if (S.isConstevalOrNegatedConsteval()) {
+ 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());
Index: clang/lib/Analysis/CFG.cpp
===================================================================
--- clang/lib/Analysis/CFG.cpp
+++ clang/lib/Analysis/CFG.cpp
@@ -3047,7 +3047,7 @@
// control-flow transfer of '&&' or '||' go directly into the then/else
// blocks directly.
BinaryOperator *Cond =
- I->getConditionVariable()
+ (I->isConstevalOrNegatedConsteval() || I->getConditionVariable())
? nullptr
: dyn_cast<BinaryOperator>(I->getCond()->IgnoreParens());
CFGBlock *LastBlock;
@@ -3061,7 +3061,9 @@
Block->setTerminator(I);
// See if this is a known constant.
- const TryResult &KnownVal = tryEvaluateBool(I->getCond());
+ TryResult KnownVal;
+ if (!I->isConstevalOrNegatedConsteval())
+ KnownVal = tryEvaluateBool(I->getCond());
// Add the successors. If we know that specific branches are
// unreachable, inform addSuccessor() of that knowledge.
Index: clang/lib/Analysis/BodyFarm.cpp
===================================================================
--- clang/lib/Analysis/BodyFarm.cpp
+++ clang/lib/Analysis/BodyFarm.cpp
@@ -461,8 +461,7 @@
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 @@
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 @@
/// Construct the If.
auto *If =
- IfStmt::Create(C, SourceLocation(),
- /* IsConstexpr=*/false,
+ IfStmt::Create(C, SourceLocation(), IfStatementKind::Ordinary,
/* Init=*/nullptr,
/* Var=*/nullptr, Comparison,
/* LPL=*/SourceLocation(),
Index: clang/lib/AST/TextNodeDumper.cpp
===================================================================
--- clang/lib/AST/TextNodeDumper.cpp
+++ clang/lib/AST/TextNodeDumper.cpp
@@ -948,6 +948,14 @@
OS << " has_var";
if (Node->hasElseStorage())
OS << " has_else";
+ if (Node->isConstexpr())
+ OS << " constexpr";
+ if (Node->isConstevalOrNegatedConsteval()) {
+ OS << " ";
+ if (Node->isNegatedConsteval())
+ OS << "!";
+ OS << "consteval";
+ }
}
void TextNodeDumper::VisitSwitchStmt(const SwitchStmt *Node) {
Index: clang/lib/AST/StmtPrinter.cpp
===================================================================
--- clang/lib/AST/StmtPrinter.cpp
+++ clang/lib/AST/StmtPrinter.cpp
@@ -236,6 +236,22 @@
}
void StmtPrinter::PrintRawIfStmt(IfStmt *If) {
+ if (If->isConstevalOrNegatedConsteval()) {
+ OS << "if ";
+ if (If->isNegatedConsteval())
+ OS << "!";
+ OS << "consteval";
+ OS << NL;
+ PrintStmt(If->getThen());
+ if (If->getElse()) {
+ Indent();
+ OS << "else";
+ PrintStmt(If->getElse());
+ OS << NL;
+ }
+ return;
+ }
+
OS << "if (";
if (If->getInit())
PrintInitStmt(If->getInit(), 4);
Index: clang/lib/AST/Stmt.cpp
===================================================================
--- clang/lib/AST/Stmt.cpp
+++ clang/lib/AST/Stmt.cpp
@@ -912,7 +912,7 @@
});
}
-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 @@
IfStmtBits.HasVar = HasVar;
IfStmtBits.HasInit = HasInit;
- setConstexpr(IsConstexpr);
+ setStatementKind(Kind);
setCond(Cond);
setThen(Then);
@@ -947,9 +947,9 @@
}
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 @@
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,
Index: clang/lib/AST/JSONNodeDumper.cpp
===================================================================
--- clang/lib/AST/JSONNodeDumper.cpp
+++ clang/lib/AST/JSONNodeDumper.cpp
@@ -1489,6 +1489,8 @@
attributeOnlyIfTrue("hasVar", IS->hasVarStorage());
attributeOnlyIfTrue("hasElse", IS->hasElseStorage());
attributeOnlyIfTrue("isConstexpr", IS->isConstexpr());
+ attributeOnlyIfTrue("isConsteval", IS->isConstevalOrNegatedConsteval());
+ attributeOnlyIfTrue("constevalIsNegated", IS->isNegatedConsteval());
}
void JSONNodeDumper::VisitSwitchStmt(const SwitchStmt *SS) {
Index: clang/lib/AST/Interp/ByteCodeStmtGen.cpp
===================================================================
--- clang/lib/AST/Interp/ByteCodeStmtGen.cpp
+++ clang/lib/AST/Interp/ByteCodeStmtGen.cpp
@@ -188,6 +188,12 @@
template <class Emitter>
bool ByteCodeStmtGen<Emitter>::visitIfStmt(const IfStmt *IS) {
BlockScope<Emitter> IfScope(this);
+
+ if (IS->isConsteval())
+ return visitStmt(IS->getThen());
+ if (IS->isNegatedConsteval())
+ return IS->getElse();
+
if (auto *CondInit = IS->getInit())
if (!visitStmt(IS->getInit()))
return false;
Index: clang/lib/AST/ExprConstant.cpp
===================================================================
--- clang/lib/AST/ExprConstant.cpp
+++ clang/lib/AST/ExprConstant.cpp
@@ -5195,7 +5195,12 @@
}
}
bool Cond;
- if (!EvaluateCond(Info, IS->getConditionVariable(), IS->getCond(), Cond))
+ if (IS->isConsteval())
+ Cond = true;
+ else if (IS->isNegatedConsteval())
+ Cond = false;
+ else if (!EvaluateCond(Info, IS->getConditionVariable(), IS->getCond(),
+ Cond))
return ESR_Failed;
if (const Stmt *SubStmt = Cond ? IS->getThen() : IS->getElse()) {
Index: clang/lib/AST/ASTImporter.cpp
===================================================================
--- clang/lib/AST/ASTImporter.cpp
+++ clang/lib/AST/ASTImporter.cpp
@@ -6359,7 +6359,7 @@
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);
}
Index: clang/include/clang/Sema/Sema.h
===================================================================
--- clang/include/clang/Sema/Sema.h
+++ clang/include/clang/Sema/Sema.h
@@ -1218,6 +1218,11 @@
/// 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 an 'if consteval' 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.
@@ -1306,8 +1311,14 @@
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;
}
};
@@ -4709,11 +4720,12 @@
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);
@@ -9114,6 +9126,19 @@
return ExprEvalContexts.back().isUnevaluated();
}
+ bool isImmediateFunctionContext() const {
+ assert(!ExprEvalContexts.empty() &&
+ "Must be in an expression evaluation context");
+ for (auto it = ExprEvalContexts.rbegin(); it != ExprEvalContexts.rend();
+ it++) {
+ if (it->isImmediateFunctionContext())
+ return true;
+ if (it->isUnevaluated())
+ return false;
+ }
+ return false;
+ }
+
/// RAII class used to determine whether SFINAE has
/// trapped any errors that occur during template argument
/// deduction.
Index: clang/include/clang/Basic/Specifiers.h
===================================================================
--- clang/include/clang/Basic/Specifiers.h
+++ clang/include/clang/Basic/Specifiers.h
@@ -29,7 +29,21 @@
};
/// Define the kind of constexpr specifier.
- enum class ConstexprSpecKind { Unspecified, Constexpr, Consteval, Constinit };
+ enum class ConstexprSpecKind : unsigned {
+ Unspecified,
+ Constexpr,
+ Consteval,
+ Constinit
+ };
+
+ /// In an if statement, this denotes whether the the statement is
+ /// an if constexpr or if consteval statement.
+ enum class IfStatementKind : unsigned {
+ Ordinary,
+ Constexpr,
+ Consteval,
+ ConstevalNegated
+ };
/// Specifies the width of a type, e.g., short, long, or long long.
enum class TypeSpecifierWidth { Unspecified, Short, Long, LongLong };
Index: clang/include/clang/Basic/DiagnosticSemaKinds.td
===================================================================
--- clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -1502,6 +1502,9 @@
def err_static_assert_requirement_failed : Error<
"static_assert failed due to requirement '%0'%select{ %2|}1">;
+def warn_if_consteval_always_true : Warning<
+ "if consteval is always true in an %select{unevaluated|immediate}0 context">, InGroup<DiagGroup<"redundant-if-consteval">>;
+
def ext_inline_variable : ExtWarn<
"inline variables are a C++17 extension">, InGroup<CXX17>;
def warn_cxx14_compat_inline_variable : Warning<
@@ -5938,6 +5941,8 @@
"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_if_consteval : Note<
+ "jump enters controlled statement of if consteval">;
def note_protected_by_if_available : Note<
"jump enters controlled statement of if available">;
def note_protected_by_vla : Note<
Index: clang/include/clang/Basic/DiagnosticParseKinds.td
===================================================================
--- clang/include/clang/Basic/DiagnosticParseKinds.td
+++ clang/include/clang/Basic/DiagnosticParseKinds.td
@@ -626,6 +626,13 @@
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>;
Index: clang/include/clang/AST/Stmt.h
===================================================================
--- clang/include/clang/AST/Stmt.h
+++ clang/include/clang/AST/Stmt.h
@@ -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 @@
unsigned : NumStmtBits;
- /// True if this if statement is a constexpr if.
- unsigned IsConstexpr : 1;
+ /// Whether this is an if constexpr if or a consteval if or neither.
+ IfStatementKind Kind : 3;
/// True if this if statement has storage for an else statement.
unsigned HasElse : 1;
@@ -1950,8 +1951,8 @@
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 @@
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,25 @@
*getTrailingObjects<SourceLocation>() = ElseLoc;
}
- bool isConstexpr() const { return IfStmtBits.IsConstexpr; }
- void setConstexpr(bool C) { IfStmtBits.IsConstexpr = C; }
+ bool isConsteval() const {
+ return IfStmtBits.Kind == IfStatementKind::Consteval;
+ }
+ bool isConstexpr() const {
+ return IfStmtBits.Kind == IfStatementKind::Constexpr;
+ }
+
+ bool isNegatedConsteval() const {
+ return IfStmtBits.Kind == IfStatementKind::ConstevalNegated;
+ }
+
+ bool isConstevalOrNegatedConsteval() const {
+ return IfStmtBits.Kind == IfStatementKind::Consteval ||
+ IfStmtBits.Kind == IfStatementKind::ConstevalNegated;
+ }
+
+ void setStatementKind(IfStatementKind Kind) { IfStmtBits.Kind = Kind; }
+
+ IfStatementKind getStatementKind() const { return 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,15 +2119,22 @@
// 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 if consteval
+ // statements, so skip it.
+ return child_range(getTrailingObjects<Stmt *>() +
+ (isConstevalOrNegatedConsteval() ? thenOffset() : 0),
getTrailingObjects<Stmt *>() +
numTrailingObjects(OverloadToken<Stmt *>()));
}
const_child_range children() const {
- return const_child_range(getTrailingObjects<Stmt *>(),
- getTrailingObjects<Stmt *>() +
- numTrailingObjects(OverloadToken<Stmt *>()));
+ // We always store a condition, but there is none for if consteval
+ // statements, so skip it.
+ return const_child_range(
+ getTrailingObjects<Stmt *>() +
+ (isConstevalOrNegatedConsteval() ? thenOffset() : 0),
+ getTrailingObjects<Stmt *>() +
+ numTrailingObjects(OverloadToken<Stmt *>()));
}
static bool classof(const Stmt *T) {
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits