namespace interp {
/// Scope used to handle temporaries in toplevel variable declarations.
-template <class Emitter> class DeclScope final : public VariableScope<Emitter> {
+template <class Emitter> class DeclScope final : public LocalScope<Emitter> {
public:
DeclScope(ByteCodeExprGen<Emitter> *Ctx, const ValueDecl *VD)
- : VariableScope<Emitter>(Ctx), Scope(Ctx->P, VD) {}
+ : LocalScope<Emitter>(Ctx), Scope(Ctx->P, VD) {}
void addExtended(const Scope::Local &Local) override {
return this->addLocal(Local);
C->emitDestruction();
}
-/// When calling this, we have a pointer of the local-to-destroy
-/// on the stack.
-/// Emit destruction of record types (or arrays of record types).
-/// FIXME: Handle virtual destructors.
-template <class Emitter>
-bool ByteCodeExprGen<Emitter>::emitRecordDestruction(const Descriptor *Desc) {
- assert(Desc);
- assert(!Desc->isPrimitive());
- assert(!Desc->isPrimitiveArray());
-
- // Arrays.
- if (Desc->isArray()) {
- const Descriptor *ElemDesc = Desc->ElemDesc;
- const Record *ElemRecord = ElemDesc->ElemRecord;
- assert(ElemRecord); // This is not a primitive array.
-
- if (const CXXDestructorDecl *Dtor = ElemRecord->getDestructor();
- Dtor && !Dtor->isTrivial()) {
- for (ssize_t I = Desc->getNumElems() - 1; I >= 0; --I) {
- if (!this->emitConstUint64(I, SourceInfo{}))
- return false;
- if (!this->emitArrayElemPtrUint64(SourceInfo{}))
- return false;
- if (!this->emitRecordDestruction(Desc->ElemDesc))
- return false;
- }
- }
- return this->emitPopPtr(SourceInfo{});
- }
-
- const Record *R = Desc->ElemRecord;
- assert(R);
- // First, destroy all fields.
- for (const Record::Field &Field : llvm::reverse(R->fields())) {
- const Descriptor *D = Field.Desc;
- if (!D->isPrimitive() && !D->isPrimitiveArray()) {
- if (!this->emitDupPtr(SourceInfo{}))
- return false;
- if (!this->emitGetPtrField(Field.Offset, SourceInfo{}))
- return false;
- if (!this->emitRecordDestruction(D))
- return false;
- }
- }
-
- // FIXME: Unions need to be handled differently here. We don't want to
- // call the destructor of its members.
-
- // Now emit the destructor and recurse into base classes.
- if (const CXXDestructorDecl *Dtor = R->getDestructor();
- Dtor && !Dtor->isTrivial()) {
- const Function *DtorFunc = getFunction(Dtor);
- if (DtorFunc && DtorFunc->isConstexpr()) {
- assert(DtorFunc->hasThisPointer());
- assert(DtorFunc->getNumParams() == 1);
- if (!this->emitDupPtr(SourceInfo{}))
- return false;
- if (!this->emitCall(DtorFunc, SourceInfo{}))
- return false;
- }
- }
-
- for (const Record::Base &Base : llvm::reverse(R->bases())) {
- if (!this->emitGetPtrBase(Base.Offset, SourceInfo{}))
- return false;
- if (!this->emitRecordDestruction(Base.Desc))
- return false;
- }
- // FIXME: Virtual bases.
-
- // Remove the instance pointer.
- return this->emitPopPtr(SourceInfo{});
-}
-
namespace clang {
namespace interp {
return FPO.getRoundingMode();
}
- bool emitRecordDestruction(const Descriptor *Desc);
-
protected:
/// Variable to storage mapping.
llvm::DenseMap<const ValueDecl *, Scope::Local> Locals;
this->Ctx->Descriptors[*Idx].emplace_back(Local);
}
- /// Emit destruction of the local variable. This includes
- /// object destructors.
void emitDestruction() override {
if (!Idx)
return;
- // Emit destructor calls for local variables of record
- // type with a destructor.
- for (Scope::Local &Local : this->Ctx->Descriptors[*Idx]) {
- if (!Local.Desc->isPrimitive() && !Local.Desc->isPrimitiveArray()) {
- this->Ctx->emitGetPtrLocal(Local.Offset, SourceInfo{});
- this->Ctx->emitRecordDestruction(Local.Desc);
- }
- }
-
this->Ctx->emitDestroy(*Idx, SourceInfo{});
}
if (!BreakLabel)
return false;
- this->emitCleanup();
return this->jump(*BreakLabel);
}
if (!ContinueLabel)
return false;
- this->emitCleanup();
return this->jump(*ContinueLabel);
}
// ref-error {{must have constant destruction}} \
// ref-note {{in call to}}
};
-
-namespace Destructors {
-
- class Inc final {
- public:
- int &I;
- constexpr Inc(int &I) : I(I) {}
- constexpr ~Inc() {
- I++;
- }
- };
-
- class Dec final {
- public:
- int &I;
- constexpr Dec(int &I) : I(I) {}
- constexpr ~Dec() {
- I--;
- }
- };
-
-
-
- constexpr int m() {
- int i = 0;
- {
- Inc f1(i);
- Inc f2(i);
- Inc f3(i);
- }
- return i;
- }
- static_assert(m() == 3, "");
-
-
- constexpr int C() {
- int i = 0;
-
- while (i < 10) {
- Inc inc(i);
- continue;
- Dec dec(i);
- }
- return i;
- }
- static_assert(C() == 10, "");
-
-
- constexpr int D() {
- int i = 0;
-
- {
- Inc i1(i);
- {
- Inc i2(i);
- return i;
- }
- }
-
- return i;
- }
- static_assert(D() == 0, "");
-
- constexpr int E() {
- int i = 0;
-
- for(;;) {
- Inc i1(i);
- break;
- }
- return i;
- }
- static_assert(E() == 1, "");
-
-
- /// FIXME: This should be rejected, since we call the destructor
- /// twice. However, GCC doesn't care either.
- constexpr int ManualDtor() {
- int i = 0;
- {
- Inc I(i); // ref-note {{destroying object 'I' whose lifetime has already ended}}
- I.~Inc();
- }
- return i;
- }
- static_assert(ManualDtor() == 1, ""); // expected-error {{static assertion failed}} \
- // expected-note {{evaluates to '2 == 1'}} \
- // ref-error {{not an integral constant expression}} \
- // ref-note {{in call to 'ManualDtor()'}}
-
- constexpr void doInc(int &i) {
- Inc I(i);
- return;
- }
- constexpr int testInc() {
- int i = 0;
- doInc(i);
- return i;
- }
- static_assert(testInc() == 1, "");
- constexpr void doInc2(int &i) {
- Inc I(i);
- // No return statement.
- }
- constexpr int testInc2() {
- int i = 0;
- doInc2(i);
- return i;
- }
- static_assert(testInc2() == 1, "");
-
-
- namespace DtorOrder {
- class A {
- public:
- int &I;
- constexpr A(int &I) : I(I) {}
- constexpr ~A() {
- I = 1337;
- }
- };
-
- class B : public A {
- public:
- constexpr B(int &I) : A(I) {}
- constexpr ~B() {
- I = 42;
- }
- };
-
- constexpr int foo() {
- int i = 0;
- {
- B b(i);
- }
- return i;
- }
-
- static_assert(foo() == 1337);
- }
-
- class FieldDtor1 {
- public:
- Inc I1;
- Inc I2;
- constexpr FieldDtor1(int &I) : I1(I), I2(I){}
- };
-
- constexpr int foo2() {
- int i = 0;
- {
- FieldDtor1 FD1(i);
- }
- return i;
- }
-
- static_assert(foo2() == 2);
-
- class FieldDtor2 {
- public:
- Inc Incs[3];
- constexpr FieldDtor2(int &I) : Incs{Inc(I), Inc(I), Inc(I)} {}
- };
-
- constexpr int foo3() {
- int i = 0;
- {
- FieldDtor2 FD2(i);
- }
- return i;
- }
-
- static_assert(foo3() == 3);
-
- struct ArrD {
- int index;
- int *arr;
- int &p;
- constexpr ~ArrD() {
- arr[p] = index;
- ++p;
- }
- };
- constexpr bool ArrayOrder() {
- int order[3] = {0, 0, 0};
- int p = 0;
- {
- ArrD ds[3] = {
- {1, order, p},
- {2, order, p},
- {3, order, p},
- };
- // ds will be destroyed.
- }
- return order[0] == 3 && order[1] == 2 && order[2] == 1;
- }
- static_assert(ArrayOrder());
-
-
- // Static members aren't destroyed.
- class Dec2 {
- public:
- int A = 0;
- constexpr ~Dec2() {
- A++;
- }
- };
- class Foo {
- public:
- static constexpr Dec2 a;
- static Dec2 b;
- };
- static_assert(Foo::a.A == 0);
- constexpr bool f() {
- Foo f;
- return true;
- }
- static_assert(Foo::a.A == 0);
- static_assert(f());
- static_assert(Foo::a.A == 0);
-
-
- struct NotConstexpr {
- NotConstexpr() {}
- ~NotConstexpr() {}
- };
-
- struct Outer {
- constexpr Outer() = default;
- constexpr ~Outer();
-
- constexpr int foo() {
- return 12;
- }
-
- constexpr int bar()const {
- return Outer{}.foo();
- }
-
- static NotConstexpr Val;
- };
-
- constexpr Outer::~Outer() {}
-
- constexpr Outer O;
- static_assert(O.bar() == 12);
-}