Skip to content

Commit 157da01

Browse files
Sirraidetbaederr
andcommitted
New constant interpreter support
Co-authored-by: Timm Bäder <[email protected]>
1 parent 8571f6e commit 157da01

File tree

3 files changed

+66
-14
lines changed

3 files changed

+66
-14
lines changed

clang/lib/AST/ByteCode/Compiler.cpp

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5533,6 +5533,9 @@ template <class Emitter> bool Compiler<Emitter>::visitStmt(const Stmt *S) {
55335533
return this->emitInvalid(S);
55345534
case Stmt::LabelStmtClass:
55355535
return this->visitStmt(cast<LabelStmt>(S)->getSubStmt());
5536+
case Stmt::CXXExpansionStmtInstantiationClass:
5537+
return this->visitCXXExpansionStmtInstantiation(
5538+
cast<CXXExpansionStmtInstantiation>(S));
55365539
default: {
55375540
if (const auto *E = dyn_cast<Expr>(S))
55385541
return this->discard(E);
@@ -5616,6 +5619,13 @@ bool Compiler<Emitter>::visitDeclStmt(const DeclStmt *DS,
56165619
FunctionDecl, NamespaceAliasDecl, UsingDirectiveDecl>(D))
56175620
continue;
56185621

5622+
if (const auto *ESD = dyn_cast<CXXExpansionStmtDecl>(D)) {
5623+
assert(ESD->getInstantiations() && "not expanded?");
5624+
if (!this->visitStmt(ESD->getInstantiations()))
5625+
return false;
5626+
continue;
5627+
}
5628+
56195629
const auto *VD = dyn_cast<VarDecl>(D);
56205630
if (!VD)
56215631
return false;
@@ -6180,6 +6190,38 @@ bool Compiler<Emitter>::visitCXXTryStmt(const CXXTryStmt *S) {
61806190
return this->visitStmt(S->getTryBlock());
61816191
}
61826192

6193+
/// template for (auto x : {1, 2}) {}
6194+
///
6195+
/// This is not a loop from an AST perspective at all since it has already
6196+
/// been instantiated to a list of compound statements.
6197+
///
6198+
/// Since we can have control flow in those compound statements, we need to
6199+
/// handle it mostly like a loop though.
6200+
template <class Emitter>
6201+
bool Compiler<Emitter>::visitCXXExpansionStmtInstantiation(
6202+
const CXXExpansionStmtInstantiation *S) {
6203+
LocalScope<Emitter> WholeLoopScope(this, ScopeKind::Block);
6204+
6205+
for (const Stmt *Shared : S->getSharedStmts()) {
6206+
if (!this->visitDeclStmt(cast<DeclStmt>(Shared), true))
6207+
return false;
6208+
}
6209+
6210+
LabelTy EndLabel = this->getLabel();
6211+
for (const Stmt *Instantiation : S->getInstantiations()) {
6212+
LabelTy ContinueLabel = this->getLabel();
6213+
LoopScope<Emitter> LS(this, S, EndLabel, ContinueLabel);
6214+
6215+
if (!this->visitStmt(Instantiation))
6216+
return false;
6217+
this->emitLabel(ContinueLabel);
6218+
}
6219+
6220+
this->emitLabel(EndLabel);
6221+
6222+
return WholeLoopScope.destroyLocals();
6223+
}
6224+
61836225
template <class Emitter>
61846226
bool Compiler<Emitter>::emitLambdaStaticInvokerBody(const CXXMethodDecl *MD) {
61856227
assert(MD->isLambdaStaticInvoker());

clang/lib/AST/ByteCode/Compiler.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -247,7 +247,8 @@ class Compiler : public ConstStmtVisitor<Compiler<Emitter>, bool>,
247247
bool visitDefaultStmt(const DefaultStmt *S);
248248
bool visitAttributedStmt(const AttributedStmt *S);
249249
bool visitCXXTryStmt(const CXXTryStmt *S);
250-
250+
bool
251+
visitCXXExpansionStmtInstantiation(const CXXExpansionStmtInstantiation *S);
251252
protected:
252253
bool visitStmt(const Stmt *S);
253254
bool visitExpr(const Expr *E, bool DestroyToplevelScope) override;

clang/test/SemaCXX/cxx2c-expansion-stmts.cpp

Lines changed: 22 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
// RUN: %clang_cc1 %s -std=c++2c -fsyntax-only -fdeclspec -fblocks -Wno-vla-cxx-extension -fconstexpr-steps=1000 -verify
1+
// RUN: %clang_cc1 %s -std=c++2c -fsyntax-only -fdeclspec -fblocks -Wno-vla-cxx-extension -fconstexpr-steps=10000 -verify=expected,old-interp
2+
// RUN: %clang_cc1 %s -std=c++2c -fsyntax-only -fdeclspec -fblocks -Wno-vla-cxx-extension -fconstexpr-steps=10000 -verify=expected,new-interp -fexperimental-new-constant-interpreter
23
namespace std {
34
template <typename T>
45
struct initializer_list {
@@ -154,10 +155,12 @@ struct NegativeSize {
154155
void negative_size() {
155156
static constexpr NegativeSize n;
156157
template for (auto x : n) g(x); // expected-error {{expansion size is not a constant expression}} \
157-
expected-note {{constexpr evaluation hit maximum step limit}} \
158+
old-interp-note {{constexpr evaluation hit maximum step limit}} \
159+
new-interp-note {{cannot refer to element 5 of array of 4 elements in a constant expression}} \
158160
expected-note {{in call to}}
159161
template for (constexpr auto x : n) g(x); // expected-error {{expansion size is not a constant expression}} \
160-
expected-note {{constexpr evaluation hit maximum step limit}} \
162+
old-interp-note {{constexpr evaluation hit maximum step limit}} \
163+
new-interp-note {{cannot refer to element 5 of array of 4 elements in a constant expression}} \
161164
expected-note {{in call to}}
162165
}
163166

@@ -626,24 +629,26 @@ static_assert(f(c1, c2) == 5);
626629
// TODO: This entire example should work without issuing any diagnostics once
627630
// we have full support for references to constexpr variables (P2686).
628631
consteval int f() {
629-
constexpr Array<int, 3> arr {1, 2, 3}; // expected-note{{add 'static' to give it a constant address}}
632+
constexpr Array<int, 3> arr {1, 2, 3}; // expected-note{{add 'static' to give it a constant address}} \
633+
new-interp-note 2 {{add 'static' to give it a constant address}}
630634

631635
int result = 0;
632636

633637
// expected-error@#invalid-ref {{constexpr variable '__range1' must be initialized by a constant expression}}
634638
// expected-error@#invalid-ref {{constexpr variable '__begin1' must be initialized by a constant expression}}
635639
// expected-error@#invalid-ref {{constexpr variable '__end1' must be initialized by a constant expression}}
636-
// expected-error@#invalid-ref {{expansion size is not a constant expression}}
637-
// expected-note@#invalid-ref 3 {{member call on variable '__range1' whose value is not known}}
638-
// expected-note@#invalid-ref 3 {{declared here}}
639640
// expected-note@#invalid-ref {{reference to 'arr' is not a constant expression}}
640-
// expected-note@#invalid-ref {{in call to}}
641+
// old-interp-error@#invalid-ref {{expansion size is not a constant expression}}
642+
// old-interp-note@#invalid-ref {{in call to}}
643+
// old-interp-note@#invalid-ref 3 {{member call on variable '__range1' whose value is not known}}
644+
// old-interp-note@#invalid-ref 3 {{declared here}}
645+
// new-interp-note@#invalid-ref 2 {{pointer to subobject of 'arr' is not a constant expression}}
641646
template for (constexpr int s : arr) { // #invalid-ref // OK, iterating expansion statement
642647
result += sizeof(char[s]);
643648
}
644649
return result;
645650
}
646-
static_assert(f() == 6); // expected-error {{static assertion failed due to requirement 'f() == 6'}} expected-note {{expression evaluates to '0 == 6'}}
651+
static_assert(f() == 6); // old-interp-error {{static assertion failed due to requirement 'f() == 6'}} old-interp-note {{expression evaluates to '0 == 6'}}
647652

648653
struct S {
649654
int i;
@@ -666,8 +671,9 @@ void not_constant_expression() {
666671
expected-note {{temporary created here}} \
667672
expected-error {{constexpr variable 'x' must be initialized by a constant expression}} \
668673
expected-note {{in instantiation of expansion statement requested here}} \
669-
expected-note {{read of variable '[__u0]' whose value is not known}} \
670-
expected-note {{declared here}}
674+
old-interp-note {{read of variable '[__u0]' whose value is not known}} \
675+
old-interp-note {{declared here}} \
676+
new-interp-note {{cannot access field of null pointer}}
671677
g(x);
672678
}
673679
}
@@ -1105,12 +1111,15 @@ constexpr int f1() {
11051111
constexpr int f2() {
11061112
Array<int, 3> a{1, 2, 3};
11071113
int j = 0;
1108-
template for (auto i : a) j +=i;
1114+
template for (auto i : a) j +=i; // new-interp-error {{expansion size is not a constant expression}} \
1115+
new-interp-note {{initializer of '__range1' is not a constant expression}} \
1116+
new-interp-note {{declared here}}
11091117
return j;
11101118
}
11111119

11121120
static_assert(f1() == 6);
1113-
static_assert(f2() == 6);
1121+
static_assert(f2() == 6); // new-interp-error {{static assertion failed due to requirement 'f2() == 6'}} \
1122+
new-interp-note {{expression evaluates to '0 == 6'}}
11141123

11151124
template <typename T>
11161125
struct Span {

0 commit comments

Comments
 (0)