[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