15 changes: 4 additions & 11 deletions clang/lib/CodeGen/CGDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -305,14 +305,6 @@ llvm::Constant *CodeGenModule::getOrCreateStaticVarDecl(
return Addr;
}

/// hasNontrivialDestruction - Determine whether a type's destruction is
/// non-trivial. If so, and the variable uses static initialization, we must
/// register its destructor to run on exit.
static bool hasNontrivialDestruction(QualType T) {
CXXRecordDecl *RD = T->getBaseElementTypeUnsafe()->getAsCXXRecordDecl();
return RD && !RD->hasTrivialDestructor();
}

/// AddInitializerToStaticVarDecl - Add the initializer for 'D' to the
/// global variable that has already been created for it. If the initializer
/// has a different type than GV does, this may free GV and return a different
Expand Down Expand Up @@ -372,7 +364,7 @@ CodeGenFunction::AddInitializerToStaticVarDecl(const VarDecl &D,

emitter.finalize(GV);

if (hasNontrivialDestruction(D.getType()) && HaveInsertPoint()) {
if (D.needsDestruction(getContext()) && HaveInsertPoint()) {
// We have a constant initializer, but a nontrivial destructor. We still
// need to perform a guarded "initialization" in order to register the
// destructor.
Expand Down Expand Up @@ -1994,7 +1986,7 @@ void CodeGenFunction::EmitAutoVarCleanups(const AutoVarEmission &emission) {
const VarDecl &D = *emission.Variable;

// Check the type for a cleanup.
if (QualType::DestructionKind dtorKind = D.getType().isDestructedType())
if (QualType::DestructionKind dtorKind = D.needsDestruction(getContext()))
emitAutoVarTypeCleanup(emission, dtorKind);

// In GC mode, honor objc_precise_lifetime.
Expand Down Expand Up @@ -2404,7 +2396,8 @@ void CodeGenFunction::EmitParmDecl(const VarDecl &D, ParamValue Arg,
// cleanup.
if (hasAggregateEvaluationKind(Ty) && !CurFuncIsThunk &&
Ty->getAs<RecordType>()->getDecl()->isParamDestroyedInCallee()) {
if (QualType::DestructionKind DtorKind = Ty.isDestructedType()) {
if (QualType::DestructionKind DtorKind =
D.needsDestruction(getContext())) {
assert((DtorKind == QualType::DK_cxx_destructor ||
DtorKind == QualType::DK_nontrivial_c_struct) &&
"unexpected destructor type");
Expand Down
11 changes: 4 additions & 7 deletions clang/lib/CodeGen/CGDeclCXX.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -73,16 +73,10 @@ static void EmitDeclDestroy(CodeGenFunction &CGF, const VarDecl &D,
// that isn't balanced out by a destructor call as intended by the
// attribute. This also checks for -fno-c++-static-destructors and
// bails even if the attribute is not present.
if (D.isNoDestroy(CGF.getContext()))
return;

CodeGenModule &CGM = CGF.CGM;
QualType::DestructionKind DtorKind = D.needsDestruction(CGF.getContext());

// FIXME: __attribute__((cleanup)) ?

QualType Type = D.getType();
QualType::DestructionKind DtorKind = Type.isDestructedType();

switch (DtorKind) {
case QualType::DK_none:
return;
Expand All @@ -101,6 +95,9 @@ static void EmitDeclDestroy(CodeGenFunction &CGF, const VarDecl &D,
llvm::FunctionCallee Func;
llvm::Constant *Argument;

CodeGenModule &CGM = CGF.CGM;
QualType Type = D.getType();

// Special-case non-array C++ destructors, if they have the right signature.
// Under some ABIs, destructors return this instead of void, and cannot be
// passed directly to __cxa_atexit if the target does not allow this
Expand Down
4 changes: 2 additions & 2 deletions clang/lib/CodeGen/CodeGenModule.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3809,9 +3809,9 @@ void CodeGenModule::EmitGlobalVarDefinition(const VarDecl *D,
return;

llvm::Constant *Init = nullptr;
CXXRecordDecl *RD = ASTTy->getBaseElementTypeUnsafe()->getAsCXXRecordDecl();
bool NeedsGlobalCtor = false;
bool NeedsGlobalDtor = RD && !RD->hasTrivialDestructor();
bool NeedsGlobalDtor =
D->needsDestruction(getContext()) == QualType::DK_cxx_destructor;

const VarDecl *InitDecl;
const Expr *InitExpr = D->getAnyInitializer(InitDecl);
Expand Down
2 changes: 1 addition & 1 deletion clang/lib/CodeGen/ItaniumCXXABI.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -350,7 +350,7 @@ class ItaniumCXXABI : public CodeGen::CGCXXABI {
// If we have the only definition, we don't need a thread wrapper if we
// will emit the value as a constant.
if (isUniqueGVALinkage(getContext().GetGVALinkageForVariable(VD)))
return !VD->getType().isDestructedType() && InitDecl->evaluateValue();
return !VD->needsDestruction(getContext()) && InitDecl->evaluateValue();

// Otherwise, we need a thread wrapper unless we know that every
// translation unit will emit the value as a constant. We rely on
Expand Down
13 changes: 13 additions & 0 deletions clang/lib/Sema/SemaDeclCXX.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13398,6 +13398,19 @@ void Sema::FinalizeVarWithDestructor(VarDecl *VD, const RecordType *Record) {
}

if (Destructor->isTrivial()) return;

// If the destructor is constexpr, check whether the variable has constant
// destruction now.
if (Destructor->isConstexpr() && VD->evaluateValue()) {
SmallVector<PartialDiagnosticAt, 8> Notes;
if (!VD->evaluateDestruction(Notes) && VD->isConstexpr()) {
Diag(VD->getLocation(),
diag::err_constexpr_var_requires_const_destruction) << VD;
for (unsigned I = 0, N = Notes.size(); I != N; ++I)
Diag(Notes[I].first, Notes[I].second);
}
}

if (!VD->hasGlobalStorage()) return;

// Emit warning for non-trivial dtor in global scope (a real global,
Expand Down
5 changes: 3 additions & 2 deletions clang/lib/Serialization/ASTReaderDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1390,10 +1390,11 @@ ASTDeclReader::RedeclarableResult ASTDeclReader::VisitVarDeclImpl(VarDecl *VD) {

if (uint64_t Val = Record.readInt()) {
VD->setInit(Record.readExpr());
if (Val > 1) { // IsInitKnownICE = 1, IsInitNotICE = 2, IsInitICE = 3
if (Val > 1) {
EvaluatedStmt *Eval = VD->ensureEvaluatedStmt();
Eval->CheckedICE = true;
Eval->IsICE = Val == 3;
Eval->IsICE = (Val & 1) != 0;
Eval->HasConstantDestruction = (Val & 4) != 0;
}
}

Expand Down
11 changes: 9 additions & 2 deletions clang/lib/Serialization/ASTWriterDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -968,7 +968,14 @@ void ASTDeclWriter::VisitVarDecl(VarDecl *D) {
Record.push_back(D->getLinkageInternal());

if (D->getInit()) {
Record.push_back(!D->isInitKnownICE() ? 1 : (D->isInitICE() ? 3 : 2));
if (!D->isInitKnownICE())
Record.push_back(1);
else {
Record.push_back(
2 |
(D->isInitICE() ? 1 : 0) |
(D->ensureEvaluatedStmt()->HasConstantDestruction ? 4 : 0));
}
Record.AddStmt(D->getInit());
} else {
Record.push_back(0);
Expand Down Expand Up @@ -2140,7 +2147,7 @@ void ASTWriter::WriteDeclAbbrevs() {
Abv->Add(BitCodeAbbrevOp(0)); // ImplicitParamKind
Abv->Add(BitCodeAbbrevOp(0)); // EscapingByref
Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 3)); // Linkage
Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 2)); // IsInitICE (local)
Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 3)); // IsInitICE (local)
Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 2)); // VarKind (local enum)
// Type Source Info
Abv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Array));
Expand Down
17 changes: 17 additions & 0 deletions clang/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p9.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
// RUN: %clang_cc1 -fsyntax-only -verify -std=c++11 %s
// RUN: %clang_cc1 -fsyntax-only -verify -std=c++2a %s

// A constexpr specifier used in an object declaration declares the object as
// const.
Expand Down Expand Up @@ -35,3 +36,19 @@ struct pixel {
};
constexpr pixel ur = { 1294, 1024 }; // ok
constexpr pixel origin; // expected-error {{default initialization of an object of const type 'const pixel' without a user-provided default constructor}}

#if __cplusplus > 201702L
// A constexpr variable shall have constant destruction.
struct A {
bool ok;
constexpr A(bool ok) : ok(ok) {}
constexpr ~A() noexcept(false) {
void oops(); // expected-note 2{{declared here}}
if (!ok) oops(); // expected-note 2{{non-constexpr function}}
}
};

constexpr A const_dtor(true);
constexpr A non_const_dtor(false); // expected-error {{must have constant destruction}} expected-note {{in call}}
constexpr A arr_dtor[5] = {true, true, true, false, true}; // expected-error {{must have constant destruction}} expected-note {{in call to '&arr_dtor[3]->~A()'}}
#endif
43 changes: 43 additions & 0 deletions clang/test/CXX/expr/expr.const/p6-2a.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
// RUN: %clang_cc1 -std=c++2a -verify %s

constexpr int non_class = 42;
constexpr int arr_non_class[5] = {1, 2, 3};

struct A {
int member = 1;
constexpr ~A() { member = member + 1; }
};
constexpr A class_ = {};
constexpr A arr_class[5] = {{}, {}};

struct Mutable {
mutable int member = 1; // expected-note {{declared here}}
constexpr ~Mutable() { member = member + 1; } // expected-note {{read of mutable member}}
};
constexpr Mutable mut_member; // expected-error {{must have constant destruction}} expected-note {{in call}}

struct MutableStore {
mutable int member = 1; // expected-note {{declared here}}
constexpr ~MutableStore() { member = 2; } // expected-note {{assignment to mutable member}}
};
constexpr MutableStore mut_store; // expected-error {{must have constant destruction}} expected-note {{in call}}

// Note: the constant destruction rules disallow this example even though hcm.n is a const object.
struct MutableConst {
struct HasConstMember {
const int n = 4;
};
mutable HasConstMember hcm; // expected-note {{here}}
constexpr ~MutableConst() {
int q = hcm.n; // expected-note {{read of mutable}}
}
};
constexpr MutableConst mc; // expected-error {{must have constant destruction}} expected-note {{in call}}

struct Temporary {
int &&temp;
constexpr ~Temporary() {
int n = temp; // expected-note {{outside the expression that created the temporary}}
}
};
constexpr Temporary t = {3}; // expected-error {{must have constant destruction}} expected-note {{created here}} expected-note {{in call}}
1 change: 1 addition & 0 deletions clang/test/CodeGenCXX/attr-no-destroy-d54344.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

class a {
public:
a();
~a();
};
class logger_base {
Expand Down
57 changes: 55 additions & 2 deletions clang/test/CodeGenCXX/const-init-cxx2a.cpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,58 @@
// RUN: %clang_cc1 -verify -triple x86_64-apple-darwin -emit-llvm -o - %s -std=c++2a | FileCheck %s
// expected-no-diagnostics
// RUN: %clang_cc1 -triple x86_64-linux-gnu -emit-llvm -o - %s -std=c++2a | FileCheck %s --implicit-check-not=cxx_global_var_init --implicit-check-not=cxa_atexit

// RUN: %clang_cc1 -triple x86_64-linux-gnu -emit-pch -o %t.pch %s -std=c++2a
// RUN: %clang_cc1 -triple x86_64-linux-gnu -include-pch %t.pch -x c++ /dev/null -emit-llvm -o - -std=c++2a | FileCheck %s --implicit-check-not=cxx_global_var_init --implicit-check-not=cxa_atexit

// CHECK: @a = global i32 123,
int a = (delete new int, 123);

struct B {
constexpr B() {}
constexpr ~B() { n *= 5; }
int n = 123;
};
// CHECK: @b = global {{.*}} i32 123
extern constexpr B b = B();

// CHECK: @_ZL1c = internal global {{.*}} i32 123
const B c;
int use_c() { return c.n; }

struct D {
int n;
constexpr ~D() {}
};
D d;
// CHECK: @d = global {{.*}} zeroinitializer

D d_arr[3];
// CHECK: @d_arr = global {{.*}} zeroinitializer

thread_local D d_tl;
// CHECK: @d_tl = thread_local global {{.*}} zeroinitializer

// CHECK-NOT: @llvm.global_ctors

// CHECK-LABEL: define {{.*}} @_Z1fv(
void f() {
// CHECK-NOT: call
// CHECK: call {{.*}}memcpy
// CHECK-NOT: call
// CHECK: call {{.*}}memset
// CHECK-NOT: call
// CHECK: }
constexpr B b;
D d = D();
}

// CHECK-LABEL: define {{.*}} @_Z1gv(
void g() {
// CHECK-NOT: call
// CHECK-NOT: cxa_guard
// CHECK-NOT: _ZGV
// CHECK: }
static constexpr B b1;
static const B b2;
static D d;
thread_local D d_tl;
}
8 changes: 2 additions & 6 deletions clang/test/CodeGenCXX/no_destroy.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,22 +5,18 @@ struct NonTrivial {
~NonTrivial();
};

// CHECK-LABEL: define internal void @__cxx_global_var_init
// CHECK-NOT: __cxa_atexit{{.*}}_ZN10NonTrivialD1Ev
[[clang::no_destroy]] NonTrivial nt1;
// CHECK-LABEL: define internal void @__cxx_global_var_init
// CHECK-NOT: _tlv_atexit{{.*}}_ZN10NonTrivialD1Ev
[[clang::no_destroy]] thread_local NonTrivial nt2;

struct NonTrivial2 {
~NonTrivial2();
};

// CHECK-LABEL: define internal void @__cxx_global_var_init
// CHECK: __cxa_atexit{{.*}}_ZN11NonTrivial2D1Ev
// CHECK: __cxa_atexit{{.*}}_ZN11NonTrivial2D1Ev{{.*}}nt21
NonTrivial2 nt21;
// CHECK-LABEL: define internal void @__cxx_global_var_init
// CHECK: _tlv_atexit{{.*}}_ZN11NonTrivial2D1Ev
// CHECK: _tlv_atexit{{.*}}_ZN11NonTrivial2D1Ev{{.*}}nt22
thread_local NonTrivial2 nt22;

// CHECK-LABEL: define void @_Z1fv
Expand Down
19 changes: 19 additions & 0 deletions clang/test/CodeGenCXX/non-const-init-cxx2a.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// RUN: %clang_cc1 -triple x86_64-apple-darwin -emit-llvm -o - %s -std=c++2a | FileCheck %s

// RUN: %clang_cc1 -triple x86_64-apple-darwin -emit-pch -o %t.pch %s -std=c++2a
// RUN: %clang_cc1 -triple x86_64-apple-darwin -include-pch %t.pch -x c++ /dev/null -emit-llvm -o - -std=c++2a | FileCheck %s

struct B {
constexpr B() {}
constexpr ~B() { n *= 5; }
int n = 123;
};

// We emit a dynamic destructor here because b.n might have been modified
// before b is destroyed.
//
// CHECK: @b = global {{.*}} i32 123
B b = B();

// CHECK: define {{.*}}cxx_global_var_init
// CHECK: call {{.*}} @__cxa_atexit({{.*}} @_ZN1BD1Ev {{.*}} @b