[clang] 1942a25 - [clang][Interp] Start implementing record types
Timm Bäder via cfe-commits
cfe-commits at lists.llvm.org
Fri Oct 14 01:22:20 PDT 2022
Author: Timm Bäder
Date: 2022-10-14T10:21:53+02:00
New Revision: 1942a2538b86fe55b9723800db950391cc05402b
URL: https://github.com/llvm/llvm-project/commit/1942a2538b86fe55b9723800db950391cc05402b
DIFF: https://github.com/llvm/llvm-project/commit/1942a2538b86fe55b9723800db950391cc05402b.diff
LOG: [clang][Interp] Start implementing record types
Implement simple constructors as well as member access expressions.
Differential Revision: https://reviews.llvm.org/D134057
Added:
clang/test/AST/Interp/records.cpp
Modified:
clang/lib/AST/Interp/ByteCodeExprGen.cpp
clang/lib/AST/Interp/ByteCodeExprGen.h
clang/lib/AST/Interp/ByteCodeStmtGen.cpp
clang/lib/AST/Interp/Interp.h
clang/lib/AST/Interp/Record.h
clang/test/AST/Interp/references.cpp
Removed:
################################################################################
diff --git a/clang/lib/AST/Interp/ByteCodeExprGen.cpp b/clang/lib/AST/Interp/ByteCodeExprGen.cpp
index e23727aad256..2e8343504e5a 100644
--- a/clang/lib/AST/Interp/ByteCodeExprGen.cpp
+++ b/clang/lib/AST/Interp/ByteCodeExprGen.cpp
@@ -304,6 +304,28 @@ bool ByteCodeExprGen<Emitter>::VisitUnaryExprOrTypeTraitExpr(
return false;
}
+template <class Emitter>
+bool ByteCodeExprGen<Emitter>::VisitMemberExpr(const MemberExpr *E) {
+ // 'Base.Member'
+ const Expr *Base = E->getBase();
+ const ValueDecl *Member = E->getMemberDecl();
+
+ if (!this->visit(Base))
+ return false;
+
+ // Base above gives us a pointer on the stack.
+ // TODO: Implement non-FieldDecl members.
+ if (const auto *FD = dyn_cast<FieldDecl>(Member)) {
+ const RecordDecl *RD = FD->getParent();
+ const Record *R = getRecord(RD);
+ const Record::Field *F = R->getField(FD);
+ // Leave a pointer to the field on the stack.
+ return this->emitGetPtrField(F->Offset, E);
+ }
+
+ return false;
+}
+
template <class Emitter> bool ByteCodeExprGen<Emitter>::discard(const Expr *E) {
OptionScope<Emitter> Scope(this, /*NewDiscardResult=*/true);
return this->Visit(E);
@@ -609,6 +631,78 @@ bool ByteCodeExprGen<Emitter>::visitArrayInitializer(const Expr *Initializer) {
return true;
}
+template <class Emitter>
+bool ByteCodeExprGen<Emitter>::visitRecordInitializer(const Expr *Initializer) {
+ Initializer = Initializer->IgnoreParenImpCasts();
+ assert(Initializer->getType()->isRecordType());
+
+ if (const auto CtorExpr = dyn_cast<CXXConstructExpr>(Initializer)) {
+ const CXXConstructorDecl *Ctor = CtorExpr->getConstructor();
+ const RecordDecl *RD = Ctor->getParent();
+ const Record *R = getRecord(RD);
+
+ for (const auto *Init : Ctor->inits()) {
+ const FieldDecl *Member = Init->getMember();
+ const Expr *InitExpr = Init->getInit();
+
+ if (Optional<PrimType> T = classify(InitExpr->getType())) {
+ const Record::Field *F = R->getField(Member);
+
+ if (!this->emitDupPtr(Initializer))
+ return false;
+
+ if (!this->visit(InitExpr))
+ return false;
+
+ if (!this->emitInitField(*T, F->Offset, Initializer))
+ return false;
+ } else {
+ assert(false && "Handle initializer for non-primitive values");
+ }
+ }
+
+ // FIXME: Actually visit() the constructor Body
+ const Stmt *Body = Ctor->getBody();
+ (void)Body;
+ return true;
+ } else if (const auto *InitList = dyn_cast<InitListExpr>(Initializer)) {
+ const Record *R = getRecord(InitList->getType());
+
+ unsigned InitIndex = 0;
+ for (const Expr *Init : InitList->inits()) {
+ const Record::Field *FieldToInit = R->getField(InitIndex);
+
+ if (Optional<PrimType> T = classify(Init->getType())) {
+ if (!this->emitDupPtr(Initializer))
+ return false;
+
+ if (!this->visit(Init))
+ return false;
+
+ if (!this->emitInitField(*T, FieldToInit->Offset, Initializer))
+ return false;
+ }
+ ++InitIndex;
+ }
+
+ return true;
+ } else if (const CallExpr *CE = dyn_cast<CallExpr>(Initializer)) {
+ const Decl *Callee = CE->getCalleeDecl();
+ const Function *Func = P.getFunction(dyn_cast<FunctionDecl>(Callee));
+
+ if (Func->hasRVO()) {
+ // RVO functions expect a pointer to initialize on the stack.
+ // Dup our existing pointer so it has its own copy to use.
+ if (!this->emitDupPtr(Initializer))
+ return false;
+
+ return this->visit(CE);
+ }
+ }
+
+ return false;
+}
+
template <class Emitter>
bool ByteCodeExprGen<Emitter>::visitInitializer(const Expr *Initializer) {
QualType InitializerType = Initializer->getType();
@@ -616,6 +710,9 @@ bool ByteCodeExprGen<Emitter>::visitInitializer(const Expr *Initializer) {
if (InitializerType->isArrayType())
return visitArrayInitializer(Initializer);
+ if (InitializerType->isRecordType())
+ return visitRecordInitializer(Initializer);
+
// Otherwise, visit the expression like normal.
return this->Visit(Initializer);
}
@@ -755,6 +852,8 @@ bool ByteCodeExprGen<Emitter>::VisitCallExpr(const CallExpr *E) {
return this->emitCall(*T, Func, E);
return this->emitCallVoid(Func, E);
} else {
+ if (Func->hasRVO())
+ return this->emitCallVoid(Func, E);
assert(false && "Can't classify function return type");
}
@@ -765,6 +864,12 @@ bool ByteCodeExprGen<Emitter>::VisitCallExpr(const CallExpr *E) {
return false;
}
+template <class Emitter>
+bool ByteCodeExprGen<Emitter>::VisitCXXDefaultInitExpr(
+ const CXXDefaultInitExpr *E) {
+ return this->visit(E->getExpr());
+}
+
template <class Emitter>
bool ByteCodeExprGen<Emitter>::VisitCXXDefaultArgExpr(
const CXXDefaultArgExpr *E) {
diff --git a/clang/lib/AST/Interp/ByteCodeExprGen.h b/clang/lib/AST/Interp/ByteCodeExprGen.h
index d73a8dfb18c2..669c8055e884 100644
--- a/clang/lib/AST/Interp/ByteCodeExprGen.h
+++ b/clang/lib/AST/Interp/ByteCodeExprGen.h
@@ -71,6 +71,7 @@ class ByteCodeExprGen : public ConstStmtVisitor<ByteCodeExprGen<Emitter>, bool>,
bool VisitBinaryOperator(const BinaryOperator *E);
bool VisitCXXDefaultArgExpr(const CXXDefaultArgExpr *E);
bool VisitCallExpr(const CallExpr *E);
+ bool VisitCXXDefaultInitExpr(const CXXDefaultInitExpr *E);
bool VisitCXXBoolLiteralExpr(const CXXBoolLiteralExpr *E);
bool VisitCXXNullPtrLiteralExpr(const CXXNullPtrLiteralExpr *E);
bool VisitUnaryOperator(const UnaryOperator *E);
@@ -81,6 +82,7 @@ class ByteCodeExprGen : public ConstStmtVisitor<ByteCodeExprGen<Emitter>, bool>,
bool VisitInitListExpr(const InitListExpr *E);
bool VisitConstantExpr(const ConstantExpr *E);
bool VisitUnaryExprOrTypeTraitExpr(const UnaryExprOrTypeTraitExpr *E);
+ bool VisitMemberExpr(const MemberExpr *E);
protected:
bool visitExpr(const Expr *E) override;
@@ -138,6 +140,8 @@ class ByteCodeExprGen : public ConstStmtVisitor<ByteCodeExprGen<Emitter>, bool>,
bool visitInitializer(const Expr *E);
/// Compiles an array initializer.
bool visitArrayInitializer(const Expr *Initializer);
+ /// Compiles a record initializer.
+ bool visitRecordInitializer(const Expr *Initializer);
/// Visits an expression and converts it to a boolean.
bool visitBool(const Expr *E);
diff --git a/clang/lib/AST/Interp/ByteCodeStmtGen.cpp b/clang/lib/AST/Interp/ByteCodeStmtGen.cpp
index c4a1efc62935..47c55271f46b 100644
--- a/clang/lib/AST/Interp/ByteCodeStmtGen.cpp
+++ b/clang/lib/AST/Interp/ByteCodeStmtGen.cpp
@@ -94,10 +94,6 @@ bool ByteCodeStmtGen<Emitter>::visitFunc(const FunctionDecl *F) {
// Classify the return type.
ReturnType = this->classify(F->getReturnType());
- // Set up fields and context if a constructor.
- if (auto *MD = dyn_cast<CXXMethodDecl>(F))
- return this->bail(MD);
-
if (auto *Body = F->getBody())
if (!visitStmt(Body))
return false;
diff --git a/clang/lib/AST/Interp/Interp.h b/clang/lib/AST/Interp/Interp.h
index 0df7ead28cd3..9356c572ddd9 100644
--- a/clang/lib/AST/Interp/Interp.h
+++ b/clang/lib/AST/Interp/Interp.h
@@ -492,6 +492,9 @@ bool InitThisFieldActive(InterpState &S, CodePtr OpPC, uint32_t I) {
return true;
}
+/// 1) Pops the value from the stack
+/// 2) Pops a pointer from the stack
+/// 3) Writes the value to field I of the pointer
template <PrimType Name, class T = typename PrimConv<Name>::T>
bool InitField(InterpState &S, CodePtr OpPC, uint32_t I) {
const T &Value = S.Stk.pop<T>();
diff --git a/clang/lib/AST/Interp/Record.h b/clang/lib/AST/Interp/Record.h
index c59adef1e73d..7743ad8dbb6f 100644
--- a/clang/lib/AST/Interp/Record.h
+++ b/clang/lib/AST/Interp/Record.h
@@ -67,6 +67,7 @@ class Record final {
}
unsigned getNumFields() const { return Fields.size(); }
+ const Field *getField(unsigned I) const { return &Fields[I]; }
Field *getField(unsigned I) { return &Fields[I]; }
using const_base_iter = BaseList::const_iterator;
diff --git a/clang/test/AST/Interp/records.cpp b/clang/test/AST/Interp/records.cpp
new file mode 100644
index 000000000000..023764022dd9
--- /dev/null
+++ b/clang/test/AST/Interp/records.cpp
@@ -0,0 +1,105 @@
+// RUN: %clang_cc1 -fexperimental-new-constant-interpreter -verify %s
+// RUN: %clang_cc1 -verify=ref %s
+
+// ref-no-diagnostics
+// expected-no-diagnostics
+
+struct BoolPair {
+ bool first;
+ bool second;
+};
+
+struct Ints {
+ int a = 20;
+ int b = 30;
+ bool c = true;
+ // BoolPair bp = {true, false}; FIXME
+
+ static const int five = 5;
+ static constexpr int getFive() {
+ return five;
+ }
+
+ constexpr int getTen() const {
+ return 10;
+ }
+};
+
+static_assert(Ints::getFive() == 5, "");
+
+constexpr Ints ints;
+static_assert(ints.a == 20, "");
+static_assert(ints.b == 30, "");
+static_assert(ints.c, "");
+static_assert(ints.getTen() == 10, "");
+
+constexpr Ints ints2{-20, -30, false};
+static_assert(ints2.a == -20, "");
+static_assert(ints2.b == -30, "");
+static_assert(!ints2.c, "");
+
+#if 0
+constexpr Ints getInts() {
+ return {64, 128, true};
+}
+constexpr Ints ints3 = getInts();
+static_assert(ints3.a == 64, "");
+static_assert(ints3.b == 128, "");
+static_assert(ints3.c, "");
+#endif
+
+constexpr Ints ints4 = {
+ .a = 40 * 50,
+ .b = 0,
+ .c = (ints.a > 0),
+
+};
+static_assert(ints4.a == (40 * 50), "");
+static_assert(ints4.b == 0, "");
+static_assert(ints4.c, "");
+
+
+// FIXME: Implement initialization by DeclRefExpr.
+//constexpr Ints ints4 = ints3; TODO
+
+
+
+struct Ints2 {
+ int a = 10;
+ int b;
+};
+// FIXME: Broken in the new constant interpreter.
+// Should be rejected, but without asan errors.
+//constexpr Ints2 ints2;
+
+class C {
+ public:
+ int a;
+ int b;
+
+ constexpr C() : a(100), b(200) {}
+};
+
+constexpr C c;
+static_assert(c.a == 100, "");
+static_assert(c.b == 200, "");
+
+constexpr int getB() {
+ C c;
+ int &j = c.b;
+
+ j = j * 2;
+
+ return c.b;
+}
+static_assert(getB() == 400, "");
+
+constexpr int getA(const C &c) {
+ return c.a;
+}
+static_assert(getA(c) == 100, "");
+
+constexpr const C* getPointer() {
+ return &c;
+}
+static_assert(getPointer()->a == 100, "");
diff --git a/clang/test/AST/Interp/references.cpp b/clang/test/AST/Interp/references.cpp
index 9a23c0a92e53..7d2ed6058361 100644
--- a/clang/test/AST/Interp/references.cpp
+++ b/clang/test/AST/Interp/references.cpp
@@ -83,9 +83,8 @@ constexpr int RefToMemberExpr() {
S s{1, 2};
int &j = s.i;
- j += 10;
+ j = j + 10;
return j;
}
-// FIXME: Should be accepted.
-static_assert(RefToMemberExpr() == 11, ""); // expected-error {{not an integral constant expression}}
+static_assert(RefToMemberExpr() == 11, "");
More information about the cfe-commits
mailing list