[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