[clang] 9cb4e90 - [clang][Interp] Support base class constructors

Timm Bäder via cfe-commits cfe-commits at lists.llvm.org
Fri Oct 21 01:50:20 PDT 2022


Author: Timm Bäder
Date: 2022-10-21T10:49:45+02:00
New Revision: 9cb4e90e72602e0974b2eb9e5eb56fd2cc998db7

URL: https://github.com/llvm/llvm-project/commit/9cb4e90e72602e0974b2eb9e5eb56fd2cc998db7
DIFF: https://github.com/llvm/llvm-project/commit/9cb4e90e72602e0974b2eb9e5eb56fd2cc998db7.diff

LOG: [clang][Interp] Support base class constructors

Differential Revision: https://reviews.llvm.org/D135025

Added: 
    

Modified: 
    clang/lib/AST/Interp/ByteCodeExprGen.cpp
    clang/lib/AST/Interp/ByteCodeExprGen.h
    clang/lib/AST/Interp/ByteCodeStmtGen.cpp
    clang/test/AST/Interp/records.cpp

Removed: 
    


################################################################################
diff  --git a/clang/lib/AST/Interp/ByteCodeExprGen.cpp b/clang/lib/AST/Interp/ByteCodeExprGen.cpp
index 872bbf045cef2..c785d3cafdad6 100644
--- a/clang/lib/AST/Interp/ByteCodeExprGen.cpp
+++ b/clang/lib/AST/Interp/ByteCodeExprGen.cpp
@@ -107,6 +107,21 @@ bool ByteCodeExprGen<Emitter>::VisitCastExpr(const CastExpr *CE) {
         });
   }
 
+  case CK_UncheckedDerivedToBase: {
+    if (!this->visit(SubExpr))
+      return false;
+
+    const CXXRecordDecl *FromDecl = getRecordDecl(SubExpr);
+    assert(FromDecl);
+    const CXXRecordDecl *ToDecl = getRecordDecl(CE);
+    assert(ToDecl);
+    const Record *R = getRecord(FromDecl);
+    const Record::Base *ToBase = R->getBase(ToDecl);
+    assert(ToBase);
+
+    return this->emitGetPtrBase(ToBase->Offset, CE);
+  }
+
   case CK_ArrayToPointerDecay:
   case CK_AtomicToNonAtomic:
   case CK_ConstructorConversion:

diff  --git a/clang/lib/AST/Interp/ByteCodeExprGen.h b/clang/lib/AST/Interp/ByteCodeExprGen.h
index 6cc45bd4d0154..a254a1baee8f9 100644
--- a/clang/lib/AST/Interp/ByteCodeExprGen.h
+++ b/clang/lib/AST/Interp/ByteCodeExprGen.h
@@ -244,6 +244,15 @@ class ByteCodeExprGen : public ConstStmtVisitor<ByteCodeExprGen<Emitter>, bool>,
     return (*InitFn)();
   }
 
+  /// Returns the CXXRecordDecl for the type of the given expression,
+  /// or nullptr if no such decl exists.
+  const CXXRecordDecl *getRecordDecl(const Expr *E) const {
+    QualType T = E->getType();
+    if (const auto *RD = T->getPointeeCXXRecordDecl())
+      return RD;
+    return T->getAsCXXRecordDecl();
+  }
+
 protected:
   /// Variable to storage mapping.
   llvm::DenseMap<const ValueDecl *, Scope::Local> Locals;

diff  --git a/clang/lib/AST/Interp/ByteCodeStmtGen.cpp b/clang/lib/AST/Interp/ByteCodeStmtGen.cpp
index 3cdb87fcd7f6e..addcb4dff1ff8 100644
--- a/clang/lib/AST/Interp/ByteCodeStmtGen.cpp
+++ b/clang/lib/AST/Interp/ByteCodeStmtGen.cpp
@@ -100,31 +100,45 @@ bool ByteCodeStmtGen<Emitter>::visitFunc(const FunctionDecl *F) {
     const Record *R = this->getRecord(RD);
 
     for (const auto *Init : Ctor->inits()) {
-      const FieldDecl *Member = Init->getMember();
       const Expr *InitExpr = Init->getInit();
-      const Record::Field *F = R->getField(Member);
-
-      if (Optional<PrimType> T = this->classify(InitExpr->getType())) {
-        if (!this->emitThis(InitExpr))
-          return false;
-
-        if (!this->visit(InitExpr))
-          return false;
-
-        if (!this->emitInitField(*T, F->Offset, InitExpr))
+      if (const FieldDecl *Member = Init->getMember()) {
+        const Record::Field *F = R->getField(Member);
+
+        if (Optional<PrimType> T = this->classify(InitExpr->getType())) {
+          if (!this->emitThis(InitExpr))
+            return false;
+
+          if (!this->visit(InitExpr))
+            return false;
+
+          if (!this->emitInitField(*T, F->Offset, InitExpr))
+            return false;
+        } else {
+          // Non-primitive case. Get a pointer to the field-to-initialize
+          // on the stack and call visitInitialzer() for it.
+          if (!this->emitThis(InitExpr))
+            return false;
+
+          if (!this->emitGetPtrField(F->Offset, InitExpr))
+            return false;
+
+          if (!this->visitInitializer(InitExpr))
+            return false;
+
+          if (!this->emitPopPtr(InitExpr))
+            return false;
+        }
+      } else if (const Type *Base = Init->getBaseClass()) {
+        // Base class initializer.
+        // Get This Base and call initializer on it.
+        auto *BaseDecl = Base->getAsCXXRecordDecl();
+        assert(BaseDecl);
+        const Record::Base *B = R->getBase(BaseDecl);
+        assert(B);
+        if (!this->emitGetPtrThisBase(B->Offset, InitExpr))
           return false;
-      } else {
-        // Non-primitive case. Get a pointer to the field-to-initialize
-        // on the stack and call visitInitialzer() for it.
-        if (!this->emitThis(InitExpr))
-          return false;
-
-        if (!this->emitGetPtrField(F->Offset, InitExpr))
-          return false;
-
         if (!this->visitInitializer(InitExpr))
           return false;
-
         if (!this->emitPopPtr(InitExpr))
           return false;
       }

diff  --git a/clang/test/AST/Interp/records.cpp b/clang/test/AST/Interp/records.cpp
index d0a40c8d25838..98d4582b961b7 100644
--- a/clang/test/AST/Interp/records.cpp
+++ b/clang/test/AST/Interp/records.cpp
@@ -5,8 +5,6 @@
 // RUN: %clang_cc1 -verify=ref -std=c++14 %s
 // RUN: %clang_cc1 -verify=ref -triple i686 %s
 
-// expected-no-diagnostics
-
 struct BoolPair {
   bool first;
   bool second;
@@ -181,3 +179,116 @@ static_assert(LT2.v[0].first == false, "");
 static_assert(LT2.v[0].second == false, "");
 static_assert(LT2.v[2].first == true, "");
 static_assert(LT2.v[2].second == false, "");
+
+class Base {
+public:
+  int i;
+  constexpr Base() : i(10) {}
+  constexpr Base(int i) : i(i) {}
+};
+
+class A : public Base {
+public:
+  constexpr A() : Base(100) {}
+  constexpr A(int a) : Base(a) {}
+};
+constexpr A a{};
+static_assert(a.i == 100, "");
+constexpr A a2{12};
+static_assert(a2.i == 12, "");
+static_assert(a2.i == 200, ""); // ref-error {{static assertion failed}} \
+                                // ref-note {{evaluates to '12 == 200'}} \
+                                // expected-error {{static assertion failed}} \
+                                // expected-note {{evaluates to '12 == 200'}}
+
+namespace MI {
+  class A {
+  public:
+    int a;
+    constexpr A(int a) : a(a) {}
+  };
+
+  class B {
+  public:
+    int b;
+    constexpr B(int b) : b(b) {}
+  };
+
+  class C : public A, public B {
+  public:
+    constexpr C() : A(10), B(20) {}
+  };
+  constexpr C c = {};
+  static_assert(c.a == 10, "");
+  static_assert(c.b == 20, "");
+
+
+  class D : private A, private B {
+    public:
+    constexpr D() : A(20), B(30) {}
+    constexpr int getA() const { return a; }
+    constexpr int getB() const { return b; }
+  };
+  constexpr D d = {};
+  static_assert(d.getA() == 20, "");
+  static_assert(d.getB() == 30, "");
+};
+
+namespace DeriveFailures {
+  struct Base { // ref-note 2{{declared here}}
+    int Val;
+  };
+
+  struct Derived : Base {
+    int OtherVal;
+
+    constexpr Derived(int i) : OtherVal(i) {} // ref-error {{never produces a constant expression}} \
+                                              // ref-note 2{{non-constexpr constructor 'Base' cannot be used in a constant expression}}
+  };
+
+  // FIXME: This is currently not being diagnosed with the new constant interpreter.
+  constexpr Derived D(12); // ref-error {{must be initialized by a constant expression}} \
+                           // ref-note {{in call to 'Derived(12)'}} \
+                           // ref-note {{declared here}} \
+                           // expected-error {{must be initialized by a constant expression}}
+  static_assert(D.Val == 0, ""); // ref-error {{not an integral constant expression}} \
+                                 // ref-note {{initializer of 'D' is not a constant expression}}
+
+#if 0
+  // FIXME: This test is currently disabled because the failing constructor call
+  //   causes us to run into an assertion later on in the new interpreter.
+  //   Once that is fixed, we fail successfully but the diagnostic uses the
+  //   wrong value.
+  struct AnotherBase {
+    int Val;
+    constexpr AnotherBase(int i) : Val(12 / i) {} //ref-note {{division by zero}} \
+                                                  //expected-note {{division by zero}}
+  };
+
+  struct AnotherDerived : AnotherBase {
+    constexpr AnotherDerived(int i) : AnotherBase(i) {}
+  };
+  constexpr AnotherBase Derp(0); // ref-error {{must be initialized by a constant expression}} \
+                                 // ref-note {{in call to 'AnotherBase(0)'}} \
+                                 // expected-error {{must be initialized by a constant expression}} \
+                                 // expected-note {{in call to 'AnotherBase(}}
+                                 // FIXME Previous note uses the wrong value
+#endif
+
+  struct YetAnotherBase {
+    int Val;
+    constexpr YetAnotherBase(int i) : Val(i) {}
+  };
+
+  struct YetAnotherDerived : YetAnotherBase {
+    using YetAnotherBase::YetAnotherBase; //ref-note {{declared here}}
+    int OtherVal;
+
+    constexpr bool doit() const { return Val == OtherVal; }
+  };
+
+  constexpr YetAnotherDerived Oops(0); // ref-error {{must be initialized by a constant expression}} \
+                                       // ref-note {{constructor inherited from base class 'YetAnotherBase' cannot be used in a constant expression}} \
+                                       // expected-error {{must be initialized by a constant expression}}
+                                       // FIXME: Missing reason for rejection.
+};


        


More information about the cfe-commits mailing list