Skip to content

Commit 9cb4e90

Browse files
committed
[clang][Interp] Support base class constructors
Differential Revision: https://reviews.llvm.org/D135025
1 parent 09bbc90 commit 9cb4e90

File tree

4 files changed

+172
-23
lines changed

4 files changed

+172
-23
lines changed

clang/lib/AST/Interp/ByteCodeExprGen.cpp

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,21 @@ bool ByteCodeExprGen<Emitter>::VisitCastExpr(const CastExpr *CE) {
107107
});
108108
}
109109

110+
case CK_UncheckedDerivedToBase: {
111+
if (!this->visit(SubExpr))
112+
return false;
113+
114+
const CXXRecordDecl *FromDecl = getRecordDecl(SubExpr);
115+
assert(FromDecl);
116+
const CXXRecordDecl *ToDecl = getRecordDecl(CE);
117+
assert(ToDecl);
118+
const Record *R = getRecord(FromDecl);
119+
const Record::Base *ToBase = R->getBase(ToDecl);
120+
assert(ToBase);
121+
122+
return this->emitGetPtrBase(ToBase->Offset, CE);
123+
}
124+
110125
case CK_ArrayToPointerDecay:
111126
case CK_AtomicToNonAtomic:
112127
case CK_ConstructorConversion:

clang/lib/AST/Interp/ByteCodeExprGen.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -244,6 +244,15 @@ class ByteCodeExprGen : public ConstStmtVisitor<ByteCodeExprGen<Emitter>, bool>,
244244
return (*InitFn)();
245245
}
246246

247+
/// Returns the CXXRecordDecl for the type of the given expression,
248+
/// or nullptr if no such decl exists.
249+
const CXXRecordDecl *getRecordDecl(const Expr *E) const {
250+
QualType T = E->getType();
251+
if (const auto *RD = T->getPointeeCXXRecordDecl())
252+
return RD;
253+
return T->getAsCXXRecordDecl();
254+
}
255+
247256
protected:
248257
/// Variable to storage mapping.
249258
llvm::DenseMap<const ValueDecl *, Scope::Local> Locals;

clang/lib/AST/Interp/ByteCodeStmtGen.cpp

Lines changed: 35 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -100,31 +100,45 @@ bool ByteCodeStmtGen<Emitter>::visitFunc(const FunctionDecl *F) {
100100
const Record *R = this->getRecord(RD);
101101

102102
for (const auto *Init : Ctor->inits()) {
103-
const FieldDecl *Member = Init->getMember();
104103
const Expr *InitExpr = Init->getInit();
105-
const Record::Field *F = R->getField(Member);
106-
107-
if (Optional<PrimType> T = this->classify(InitExpr->getType())) {
108-
if (!this->emitThis(InitExpr))
109-
return false;
110-
111-
if (!this->visit(InitExpr))
112-
return false;
113-
114-
if (!this->emitInitField(*T, F->Offset, InitExpr))
104+
if (const FieldDecl *Member = Init->getMember()) {
105+
const Record::Field *F = R->getField(Member);
106+
107+
if (Optional<PrimType> T = this->classify(InitExpr->getType())) {
108+
if (!this->emitThis(InitExpr))
109+
return false;
110+
111+
if (!this->visit(InitExpr))
112+
return false;
113+
114+
if (!this->emitInitField(*T, F->Offset, InitExpr))
115+
return false;
116+
} else {
117+
// Non-primitive case. Get a pointer to the field-to-initialize
118+
// on the stack and call visitInitialzer() for it.
119+
if (!this->emitThis(InitExpr))
120+
return false;
121+
122+
if (!this->emitGetPtrField(F->Offset, InitExpr))
123+
return false;
124+
125+
if (!this->visitInitializer(InitExpr))
126+
return false;
127+
128+
if (!this->emitPopPtr(InitExpr))
129+
return false;
130+
}
131+
} else if (const Type *Base = Init->getBaseClass()) {
132+
// Base class initializer.
133+
// Get This Base and call initializer on it.
134+
auto *BaseDecl = Base->getAsCXXRecordDecl();
135+
assert(BaseDecl);
136+
const Record::Base *B = R->getBase(BaseDecl);
137+
assert(B);
138+
if (!this->emitGetPtrThisBase(B->Offset, InitExpr))
115139
return false;
116-
} else {
117-
// Non-primitive case. Get a pointer to the field-to-initialize
118-
// on the stack and call visitInitialzer() for it.
119-
if (!this->emitThis(InitExpr))
120-
return false;
121-
122-
if (!this->emitGetPtrField(F->Offset, InitExpr))
123-
return false;
124-
125140
if (!this->visitInitializer(InitExpr))
126141
return false;
127-
128142
if (!this->emitPopPtr(InitExpr))
129143
return false;
130144
}

clang/test/AST/Interp/records.cpp

Lines changed: 113 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,6 @@
55
// RUN: %clang_cc1 -verify=ref -std=c++14 %s
66
// RUN: %clang_cc1 -verify=ref -triple i686 %s
77

8-
// expected-no-diagnostics
9-
108
struct BoolPair {
119
bool first;
1210
bool second;
@@ -181,3 +179,116 @@ static_assert(LT2.v[0].first == false, "");
181179
static_assert(LT2.v[0].second == false, "");
182180
static_assert(LT2.v[2].first == true, "");
183181
static_assert(LT2.v[2].second == false, "");
182+
183+
class Base {
184+
public:
185+
int i;
186+
constexpr Base() : i(10) {}
187+
constexpr Base(int i) : i(i) {}
188+
};
189+
190+
class A : public Base {
191+
public:
192+
constexpr A() : Base(100) {}
193+
constexpr A(int a) : Base(a) {}
194+
};
195+
constexpr A a{};
196+
static_assert(a.i == 100, "");
197+
constexpr A a2{12};
198+
static_assert(a2.i == 12, "");
199+
static_assert(a2.i == 200, ""); // ref-error {{static assertion failed}} \
200+
// ref-note {{evaluates to '12 == 200'}} \
201+
// expected-error {{static assertion failed}} \
202+
// expected-note {{evaluates to '12 == 200'}}
203+
204+
namespace MI {
205+
class A {
206+
public:
207+
int a;
208+
constexpr A(int a) : a(a) {}
209+
};
210+
211+
class B {
212+
public:
213+
int b;
214+
constexpr B(int b) : b(b) {}
215+
};
216+
217+
class C : public A, public B {
218+
public:
219+
constexpr C() : A(10), B(20) {}
220+
};
221+
constexpr C c = {};
222+
static_assert(c.a == 10, "");
223+
static_assert(c.b == 20, "");
224+
225+
226+
class D : private A, private B {
227+
public:
228+
constexpr D() : A(20), B(30) {}
229+
constexpr int getA() const { return a; }
230+
constexpr int getB() const { return b; }
231+
};
232+
constexpr D d = {};
233+
static_assert(d.getA() == 20, "");
234+
static_assert(d.getB() == 30, "");
235+
};
236+
237+
namespace DeriveFailures {
238+
struct Base { // ref-note 2{{declared here}}
239+
int Val;
240+
};
241+
242+
struct Derived : Base {
243+
int OtherVal;
244+
245+
constexpr Derived(int i) : OtherVal(i) {} // ref-error {{never produces a constant expression}} \
246+
// ref-note 2{{non-constexpr constructor 'Base' cannot be used in a constant expression}}
247+
};
248+
249+
// FIXME: This is currently not being diagnosed with the new constant interpreter.
250+
constexpr Derived D(12); // ref-error {{must be initialized by a constant expression}} \
251+
// ref-note {{in call to 'Derived(12)'}} \
252+
// ref-note {{declared here}} \
253+
// expected-error {{must be initialized by a constant expression}}
254+
static_assert(D.Val == 0, ""); // ref-error {{not an integral constant expression}} \
255+
// ref-note {{initializer of 'D' is not a constant expression}}
256+
257+
#if 0
258+
// FIXME: This test is currently disabled because the failing constructor call
259+
// causes us to run into an assertion later on in the new interpreter.
260+
// Once that is fixed, we fail successfully but the diagnostic uses the
261+
// wrong value.
262+
struct AnotherBase {
263+
int Val;
264+
constexpr AnotherBase(int i) : Val(12 / i) {} //ref-note {{division by zero}} \
265+
//expected-note {{division by zero}}
266+
};
267+
268+
struct AnotherDerived : AnotherBase {
269+
constexpr AnotherDerived(int i) : AnotherBase(i) {}
270+
};
271+
constexpr AnotherBase Derp(0); // ref-error {{must be initialized by a constant expression}} \
272+
// ref-note {{in call to 'AnotherBase(0)'}} \
273+
// expected-error {{must be initialized by a constant expression}} \
274+
// expected-note {{in call to 'AnotherBase(}}
275+
// FIXME Previous note uses the wrong value
276+
#endif
277+
278+
struct YetAnotherBase {
279+
int Val;
280+
constexpr YetAnotherBase(int i) : Val(i) {}
281+
};
282+
283+
struct YetAnotherDerived : YetAnotherBase {
284+
using YetAnotherBase::YetAnotherBase; //ref-note {{declared here}}
285+
int OtherVal;
286+
287+
constexpr bool doit() const { return Val == OtherVal; }
288+
};
289+
290+
constexpr YetAnotherDerived Oops(0); // ref-error {{must be initialized by a constant expression}} \
291+
// ref-note {{constructor inherited from base class 'YetAnotherBase' cannot be used in a constant expression}} \
292+
// expected-error {{must be initialized by a constant expression}}
293+
// FIXME: Missing reason for rejection.
294+
};

0 commit comments

Comments
 (0)