r373159 - For P0784R7: compute whether a variable has constant destruction if it

Alex L via cfe-commits cfe-commits at lists.llvm.org
Mon Nov 11 15:30:43 PST 2019


Here's a reduced test case.

```
// OPTIONS: -x objective-c -std=gnu99 -fobjc-arc -emit-llvm
@interface Foo
@end

Foo *foo() {
  static Foo *f = ((void*)0;
  return f;
}
// CHECK-NOT: cxa_guard
```

AFAIK clang should not emit C++ guard variables in non-C++ code. Is that
correct?

On Mon, 11 Nov 2019 at 14:16, Alex L <arphaman at gmail.com> wrote:

> Hi Richard,
>
> I have a question about this commit. It looks like it started emitting
> `cxa_guard_acquire/release` calls in Objective-C code, for globals with
> constant initializers, causing link failures for things that don't link
> with libc++abi. Was that intentional? I'm still working on a reduced
> test-case that I can post.
>
> Thanks,
> Alex
>
> On Sat, 28 Sep 2019 at 22:06, Richard Smith via cfe-commits <
> cfe-commits at lists.llvm.org> wrote:
>
>> Author: rsmith
>> Date: Sat Sep 28 22:08:46 2019
>> New Revision: 373159
>>
>> URL: http://llvm.org/viewvc/llvm-project?rev=373159&view=rev
>> Log:
>> For P0784R7: compute whether a variable has constant destruction if it
>> has a constexpr destructor.
>>
>> For constexpr variables, reject if the variable does not have constant
>> destruction. In all cases, do not emit runtime calls to the destructor
>> for variables with constant destruction.
>>
>> Added:
>>     cfe/trunk/test/CXX/expr/expr.const/p6-2a.cpp
>>     cfe/trunk/test/CodeGenCXX/non-const-init-cxx2a.cpp
>> Modified:
>>     cfe/trunk/include/clang/AST/Decl.h
>>     cfe/trunk/include/clang/Basic/DiagnosticASTKinds.td
>>     cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td
>>     cfe/trunk/lib/AST/ASTContext.cpp
>>     cfe/trunk/lib/AST/Decl.cpp
>>     cfe/trunk/lib/AST/ExprConstant.cpp
>>     cfe/trunk/lib/AST/Interp/Interp.cpp
>>     cfe/trunk/lib/AST/TextNodeDumper.cpp
>>     cfe/trunk/lib/CodeGen/CGCall.cpp
>>     cfe/trunk/lib/CodeGen/CGClass.cpp
>>     cfe/trunk/lib/CodeGen/CGDecl.cpp
>>     cfe/trunk/lib/CodeGen/CGDeclCXX.cpp
>>     cfe/trunk/lib/CodeGen/CodeGenModule.cpp
>>     cfe/trunk/lib/CodeGen/ItaniumCXXABI.cpp
>>     cfe/trunk/lib/Sema/SemaDeclCXX.cpp
>>     cfe/trunk/lib/Serialization/ASTReaderDecl.cpp
>>     cfe/trunk/lib/Serialization/ASTWriterDecl.cpp
>>     cfe/trunk/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p9.cpp
>>     cfe/trunk/test/CodeGenCXX/attr-no-destroy-d54344.cpp
>>     cfe/trunk/test/CodeGenCXX/const-init-cxx2a.cpp
>>     cfe/trunk/test/CodeGenCXX/no_destroy.cpp
>>
>> Modified: cfe/trunk/include/clang/AST/Decl.h
>> URL:
>> http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/AST/Decl.h?rev=373159&r1=373158&r2=373159&view=diff
>>
>> ==============================================================================
>> --- cfe/trunk/include/clang/AST/Decl.h (original)
>> +++ cfe/trunk/include/clang/AST/Decl.h Sat Sep 28 22:08:46 2019
>> @@ -808,12 +808,19 @@ struct EvaluatedStmt {
>>    /// valid if CheckedICE is true.
>>    bool IsICE : 1;
>>
>> +  /// Whether this variable is known to have constant destruction. That
>> is,
>> +  /// whether running the destructor on the initial value is a
>> side-effect
>> +  /// (and doesn't inspect any state that might have changed during
>> program
>> +  /// execution). This is currently only computed if the destructor is
>> +  /// non-trivial.
>> +  bool HasConstantDestruction : 1;
>> +
>>    Stmt *Value;
>>    APValue Evaluated;
>>
>> -  EvaluatedStmt() : WasEvaluated(false), IsEvaluating(false),
>> CheckedICE(false),
>> -                    CheckingICE(false), IsICE(false) {}
>> -
>> +  EvaluatedStmt()
>> +      : WasEvaluated(false), IsEvaluating(false), CheckedICE(false),
>> +        CheckingICE(false), IsICE(false), HasConstantDestruction(false)
>> {}
>>  };
>>
>>  /// Represents a variable declaration or definition.
>> @@ -1267,6 +1274,14 @@ public:
>>    /// to untyped APValue if the value could not be evaluated.
>>    APValue *getEvaluatedValue() const;
>>
>> +  /// Evaluate the destruction of this variable to determine if it
>> constitutes
>> +  /// constant destruction.
>> +  ///
>> +  /// \pre isInitICE()
>> +  /// \return \c true if this variable has constant destruction, \c
>> false if
>> +  ///         not.
>> +  bool evaluateDestruction(SmallVectorImpl<PartialDiagnosticAt> &Notes)
>> const;
>> +
>>    /// Determines whether it is already known whether the
>>    /// initializer is an integral constant expression or not.
>>    bool isInitKnownICE() const;
>> @@ -1505,9 +1520,14 @@ public:
>>    // has no definition within this source file.
>>    bool isKnownToBeDefined() const;
>>
>> -  /// Do we need to emit an exit-time destructor for this variable?
>> +  /// Is destruction of this variable entirely suppressed? If so, the
>> variable
>> +  /// need not have a usable destructor at all.
>>    bool isNoDestroy(const ASTContext &) const;
>>
>> +  /// Do we need to emit an exit-time destructor for this variable, and
>> if so,
>> +  /// what kind?
>> +  QualType::DestructionKind needsDestruction(const ASTContext &Ctx)
>> const;
>> +
>>    // Implement isa/cast/dyncast/etc.
>>    static bool classof(const Decl *D) { return classofKind(D->getKind());
>> }
>>    static bool classofKind(Kind K) { return K >= firstVar && K <=
>> lastVar; }
>>
>> Modified: cfe/trunk/include/clang/Basic/DiagnosticASTKinds.td
>> URL:
>> http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/DiagnosticASTKinds.td?rev=373159&r1=373158&r2=373159&view=diff
>>
>> ==============================================================================
>> --- cfe/trunk/include/clang/Basic/DiagnosticASTKinds.td (original)
>> +++ cfe/trunk/include/clang/Basic/DiagnosticASTKinds.td Sat Sep 28
>> 22:08:46 2019
>> @@ -145,8 +145,10 @@ def note_constexpr_access_volatile_obj :
>>    "a constant expression">;
>>  def note_constexpr_volatile_here : Note<
>>    "volatile %select{temporary created|object declared|member declared}0
>> here">;
>> -def note_constexpr_ltor_mutable : Note<
>> -  "read of mutable member %0 is not allowed in a constant expression">;
>> +def note_constexpr_access_mutable : Note<
>> +  "%select{read of|read of|assignment to|increment of|decrement of|"
>> +  "member call on|dynamic_cast of|typeid applied to|destruction of}0 "
>> +  "mutable member %1 is not allowed in a constant expression">;
>>  def note_constexpr_ltor_non_const_int : Note<
>>    "read of non-const variable %0 is not allowed in a constant
>> expression">;
>>  def note_constexpr_ltor_non_constexpr : Note<
>>
>> Modified: cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td
>> URL:
>> http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td?rev=373159&r1=373158&r2=373159&view=diff
>>
>> ==============================================================================
>> --- cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td (original)
>> +++ cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td Sat Sep 28
>> 22:08:46 2019
>> @@ -2384,6 +2384,8 @@ def err_constexpr_var_non_literal : Erro
>>    "constexpr variable cannot have non-literal type %0">;
>>  def err_constexpr_var_requires_const_init : Error<
>>    "constexpr variable %0 must be initialized by a constant expression">;
>> +def err_constexpr_var_requires_const_destruction : Error<
>> +  "constexpr variable %0 must have constant destruction">;
>>  def err_constexpr_redecl_mismatch : Error<
>>    "%select{non-constexpr|constexpr|consteval}1 declaration of %0"
>>    " follows %select{non-constexpr|constexpr|consteval}2 declaration">;
>>
>> Modified: cfe/trunk/lib/AST/ASTContext.cpp
>> URL:
>> http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/ASTContext.cpp?rev=373159&r1=373158&r2=373159&view=diff
>>
>> ==============================================================================
>> --- cfe/trunk/lib/AST/ASTContext.cpp (original)
>> +++ cfe/trunk/lib/AST/ASTContext.cpp Sat Sep 28 22:08:46 2019
>> @@ -10064,7 +10064,7 @@ bool ASTContext::DeclMustBeEmitted(const
>>      return false;
>>
>>    // Variables that have destruction with side-effects are required.
>> -  if (VD->getType().isDestructedType())
>> +  if (VD->needsDestruction(*this))
>>      return true;
>>
>>    // Variables that have initialization with side-effects are required.
>>
>> Modified: cfe/trunk/lib/AST/Decl.cpp
>> URL:
>> http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/Decl.cpp?rev=373159&r1=373158&r2=373159&view=diff
>>
>> ==============================================================================
>> --- cfe/trunk/lib/AST/Decl.cpp (original)
>> +++ cfe/trunk/lib/AST/Decl.cpp Sat Sep 28 22:08:46 2019
>> @@ -2592,6 +2592,18 @@ bool VarDecl::isNoDestroy(const ASTConte
>>                                   !hasAttr<AlwaysDestroyAttr>()));
>>  }
>>
>> +QualType::DestructionKind
>> +VarDecl::needsDestruction(const ASTContext &Ctx) const {
>> +  if (EvaluatedStmt *Eval = Init.dyn_cast<EvaluatedStmt *>())
>> +    if (Eval->HasConstantDestruction)
>> +      return QualType::DK_none;
>> +
>> +  if (isNoDestroy(Ctx))
>> +    return QualType::DK_none;
>> +
>> +  return getType().isDestructedType();
>> +}
>> +
>>  MemberSpecializationInfo *VarDecl::getMemberSpecializationInfo() const {
>>    if (isStaticDataMember())
>>      // FIXME: Remove ?
>>
>> Modified: cfe/trunk/lib/AST/ExprConstant.cpp
>> URL:
>> http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/ExprConstant.cpp?rev=373159&r1=373158&r2=373159&view=diff
>>
>> ==============================================================================
>> --- cfe/trunk/lib/AST/ExprConstant.cpp (original)
>> +++ cfe/trunk/lib/AST/ExprConstant.cpp Sat Sep 28 22:08:46 2019
>> @@ -744,6 +744,15 @@ namespace {
>>      /// evaluated, if any.
>>      APValue::LValueBase EvaluatingDecl;
>>
>> +    enum class EvaluatingDeclKind {
>> +      None,
>> +      /// We're evaluating the construction of EvaluatingDecl.
>> +      Ctor,
>> +      /// We're evaluating the destruction of EvaluatingDecl.
>> +      Dtor,
>> +    };
>> +    EvaluatingDeclKind IsEvaluatingDecl = EvaluatingDeclKind::None;
>> +
>>      /// EvaluatingDeclValue - This is the value being constructed for the
>>      /// declaration whose initializer is being evaluated, if any.
>>      APValue *EvaluatingDeclValue;
>> @@ -902,8 +911,10 @@ namespace {
>>        discardCleanups();
>>      }
>>
>> -    void setEvaluatingDecl(APValue::LValueBase Base, APValue &Value) {
>> +    void setEvaluatingDecl(APValue::LValueBase Base, APValue &Value,
>> +                           EvaluatingDeclKind EDK =
>> EvaluatingDeclKind::Ctor) {
>>        EvaluatingDecl = Base;
>> +      IsEvaluatingDecl = EDK;
>>        EvaluatingDeclValue = &Value;
>>      }
>>
>> @@ -2913,8 +2924,8 @@ static bool isReadByLvalueToRvalueConver
>>
>>  /// Diagnose an attempt to read from any unreadable field within the
>> specified
>>  /// type, which might be a class type.
>> -static bool diagnoseUnreadableFields(EvalInfo &Info, const Expr *E,
>> -                                     QualType T) {
>> +static bool diagnoseMutableFields(EvalInfo &Info, const Expr *E,
>> AccessKinds AK,
>> +                                  QualType T) {
>>    CXXRecordDecl *RD =
>> T->getBaseElementTypeUnsafe()->getAsCXXRecordDecl();
>>    if (!RD)
>>      return false;
>> @@ -2929,17 +2940,17 @@ static bool diagnoseUnreadableFields(Eva
>>      // FIXME: Add core issue number for the union case.
>>      if (Field->isMutable() &&
>>          (RD->isUnion() ||
>> isReadByLvalueToRvalueConversion(Field->getType()))) {
>> -      Info.FFDiag(E, diag::note_constexpr_ltor_mutable, 1) << Field;
>> +      Info.FFDiag(E, diag::note_constexpr_access_mutable, 1) << AK <<
>> Field;
>>        Info.Note(Field->getLocation(), diag::note_declared_at);
>>        return true;
>>      }
>>
>> -    if (diagnoseUnreadableFields(Info, E, Field->getType()))
>> +    if (diagnoseMutableFields(Info, E, AK, Field->getType()))
>>        return true;
>>    }
>>
>>    for (auto &BaseSpec : RD->bases())
>> -    if (diagnoseUnreadableFields(Info, E, BaseSpec.getType()))
>> +    if (diagnoseMutableFields(Info, E, AK, BaseSpec.getType()))
>>        return true;
>>
>>    // All mutable fields were empty, and thus not actually read.
>> @@ -2947,7 +2958,8 @@ static bool diagnoseUnreadableFields(Eva
>>  }
>>
>>  static bool lifetimeStartedInEvaluation(EvalInfo &Info,
>> -                                        APValue::LValueBase Base) {
>> +                                        APValue::LValueBase Base,
>> +                                        bool MutableSubobject = false) {
>>    // A temporary we created.
>>    if (Base.getCallIndex())
>>      return true;
>> @@ -2956,19 +2968,42 @@ static bool lifetimeStartedInEvaluation(
>>    if (!Evaluating)
>>      return false;
>>
>> -  // The variable whose initializer we're evaluating.
>> -  if (auto *BaseD = Base.dyn_cast<const ValueDecl*>())
>> -    if (declaresSameEntity(Evaluating, BaseD))
>> -      return true;
>> +  auto *BaseD = Base.dyn_cast<const ValueDecl*>();
>>
>> -  // A temporary lifetime-extended by the variable whose initializer
>> we're
>> -  // evaluating.
>> -  if (auto *BaseE = Base.dyn_cast<const Expr *>())
>> -    if (auto *BaseMTE = dyn_cast<MaterializeTemporaryExpr>(BaseE))
>> -      if (declaresSameEntity(BaseMTE->getExtendingDecl(), Evaluating))
>> -        return true;
>> +  switch (Info.IsEvaluatingDecl) {
>> +  case EvalInfo::EvaluatingDeclKind::None:
>> +    return false;
>>
>> -  return false;
>> +  case EvalInfo::EvaluatingDeclKind::Ctor:
>> +    // The variable whose initializer we're evaluating.
>> +    if (BaseD)
>> +      return declaresSameEntity(Evaluating, BaseD);
>> +
>> +    // A temporary lifetime-extended by the variable whose initializer
>> we're
>> +    // evaluating.
>> +    if (auto *BaseE = Base.dyn_cast<const Expr *>())
>> +      if (auto *BaseMTE = dyn_cast<MaterializeTemporaryExpr>(BaseE))
>> +        return declaresSameEntity(BaseMTE->getExtendingDecl(),
>> Evaluating);
>> +    return false;
>> +
>> +  case EvalInfo::EvaluatingDeclKind::Dtor:
>> +    // C++2a [expr.const]p6:
>> +    //   [during constant destruction] the lifetime of a and its
>> non-mutable
>> +    //   subobjects (but not its mutable subobjects) [are] considered to
>> start
>> +    //   within e.
>> +    //
>> +    // FIXME: We can meaningfully extend this to cover non-const
>> objects, but
>> +    // we will need special handling: we should be able to access only
>> +    // subobjects of such objects that are themselves declared const.
>> +    if (!BaseD ||
>> +        !(BaseD->getType().isConstQualified() ||
>> +          BaseD->getType()->isReferenceType()) ||
>> +        MutableSubobject)
>> +      return false;
>> +    return declaresSameEntity(Evaluating, BaseD);
>> +  }
>> +
>> +  llvm_unreachable("unknown evaluating decl kind");
>>  }
>>
>>  namespace {
>> @@ -2986,13 +3021,13 @@ struct CompleteObject {
>>    CompleteObject(APValue::LValueBase Base, APValue *Value, QualType Type)
>>        : Base(Base), Value(Value), Type(Type) {}
>>
>> -  bool mayReadMutableMembers(EvalInfo &Info) const {
>> +  bool mayAccessMutableMembers(EvalInfo &Info, AccessKinds AK) const {
>>      // In C++14 onwards, it is permitted to read a mutable member whose
>>      // lifetime began within the evaluation.
>>      // FIXME: Should we also allow this in C++11?
>>      if (!Info.getLangOpts().CPlusPlus14)
>>        return false;
>> -    return lifetimeStartedInEvaluation(Info, Base);
>> +    return lifetimeStartedInEvaluation(Info, Base,
>> /*MutableSubobject*/true);
>>    }
>>
>>    explicit operator bool() const { return !Type.isNull(); }
>> @@ -3097,9 +3132,9 @@ findSubobject(EvalInfo &Info, const Expr
>>        // things we need to check: if there are any mutable subobjects, we
>>        // cannot perform this read. (This only happens when performing a
>> trivial
>>        // copy or assignment.)
>> -      if (ObjType->isRecordType() && isRead(handler.AccessKind) &&
>> -          !Obj.mayReadMutableMembers(Info) &&
>> -          diagnoseUnreadableFields(Info, E, ObjType))
>> +      if (ObjType->isRecordType() &&
>> +          !Obj.mayAccessMutableMembers(Info, handler.AccessKind) &&
>> +          diagnoseMutableFields(Info, E, handler.AccessKind, ObjType))
>>          return handler.failed();
>>      }
>>
>> @@ -3167,10 +3202,10 @@ findSubobject(EvalInfo &Info, const Expr
>>                                     : O->getComplexFloatReal(), ObjType);
>>        }
>>      } else if (const FieldDecl *Field = getAsField(Sub.Entries[I])) {
>> -      if (Field->isMutable() && isRead(handler.AccessKind) &&
>> -          !Obj.mayReadMutableMembers(Info)) {
>> -        Info.FFDiag(E, diag::note_constexpr_ltor_mutable, 1)
>> -          << Field;
>> +      if (Field->isMutable() &&
>> +          !Obj.mayAccessMutableMembers(Info, handler.AccessKind)) {
>> +        Info.FFDiag(E, diag::note_constexpr_access_mutable, 1)
>> +          << handler.AccessKind << Field;
>>          Info.Note(Field->getLocation(), diag::note_declared_at);
>>          return handler.failed();
>>        }
>> @@ -3427,8 +3462,7 @@ static CompleteObject findCompleteObject
>>      // the variable we're reading must be const.
>>      if (!Frame) {
>>        if (Info.getLangOpts().CPlusPlus14 &&
>> -          declaresSameEntity(
>> -              VD, Info.EvaluatingDecl.dyn_cast<const ValueDecl *>())) {
>> +          lifetimeStartedInEvaluation(Info, LVal.Base)) {
>>          // OK, we can read and modify an object if we're in the process
>> of
>>          // evaluating its initializer, because its lifetime began in this
>>          // evaluation.
>> @@ -3518,11 +3552,14 @@ static CompleteObject findCompleteObject
>>          //   int x = ++r;
>>          //   constexpr int k = r;
>>          // Therefore we use the C++14 rules in C++11 too.
>> -        const ValueDecl *VD = Info.EvaluatingDecl.dyn_cast<const
>> ValueDecl*>();
>> -        const ValueDecl *ED = MTE->getExtendingDecl();
>> +        //
>> +        // Note that temporaries whose lifetimes began while evaluating a
>> +        // variable's constructor are not usable while evaluating the
>> +        // corresponding destructor, not even if they're of
>> const-qualified
>> +        // types.
>>          if (!(BaseType.isConstQualified() &&
>>                BaseType->isIntegralOrEnumerationType()) &&
>> -            !(VD && VD->getCanonicalDecl() == ED->getCanonicalDecl())) {
>> +            !lifetimeStartedInEvaluation(Info, LVal.Base)) {
>>            if (!IsAccess)
>>              return CompleteObject(LVal.getLValueBase(), nullptr,
>> BaseType);
>>            Info.FFDiag(E, diag::note_constexpr_access_static_temporary,
>> 1) << AK;
>> @@ -13282,6 +13319,41 @@ bool Expr::EvaluateAsInitializer(APValue
>>           CheckMemoryLeaks(Info);
>>  }
>>
>> +bool VarDecl::evaluateDestruction(
>> +    SmallVectorImpl<PartialDiagnosticAt> &Notes) const {
>> +  assert(getEvaluatedValue() && !getEvaluatedValue()->isAbsent() &&
>> +         "cannot evaluate destruction of non-constant-initialized
>> variable");
>> +
>> +  Expr::EvalStatus EStatus;
>> +  EStatus.Diag = &Notes;
>> +
>> +  // Make a copy of the value for the destructor to mutate.
>> +  APValue DestroyedValue = *getEvaluatedValue();
>> +
>> +  EvalInfo Info(getASTContext(), EStatus,
>> EvalInfo::EM_ConstantExpression);
>> +  Info.setEvaluatingDecl(this, DestroyedValue,
>> +                         EvalInfo::EvaluatingDeclKind::Dtor);
>> +  Info.InConstantContext = true;
>> +
>> +  SourceLocation DeclLoc = getLocation();
>> +  QualType DeclTy = getType();
>> +
>> +  LValue LVal;
>> +  LVal.set(this);
>> +
>> +  // FIXME: Consider storing whether this variable has constant
>> destruction in
>> +  // the EvaluatedStmt so that CodeGen can query it.
>> +  if (!HandleDestruction(Info, DeclLoc, LVal.Base, DestroyedValue,
>> DeclTy) ||
>> +      EStatus.HasSideEffects)
>> +    return false;
>> +
>> +  if (!Info.discardCleanups())
>> +    llvm_unreachable("Unhandled cleanup; missing full expression
>> marker?");
>> +
>> +  ensureEvaluatedStmt()->HasConstantDestruction = true;
>> +  return true;
>> +}
>> +
>>  /// isEvaluatable - Call EvaluateAsRValue to see if this expression can
>> be
>>  /// constant folded, but discard the result.
>>  bool Expr::isEvaluatable(const ASTContext &Ctx, SideEffectsKind SEK)
>> const {
>>
>> Modified: cfe/trunk/lib/AST/Interp/Interp.cpp
>> URL:
>> http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/Interp/Interp.cpp?rev=373159&r1=373158&r2=373159&view=diff
>>
>> ==============================================================================
>> --- cfe/trunk/lib/AST/Interp/Interp.cpp (original)
>> +++ cfe/trunk/lib/AST/Interp/Interp.cpp Sat Sep 28 22:08:46 2019
>> @@ -275,7 +275,7 @@ bool CheckMutable(InterpState &S, CodePt
>>
>>    const SourceInfo &Loc = S.Current->getSource(OpPC);
>>    const FieldDecl *Field = Ptr.getField();
>> -  S.FFDiag(Loc, diag::note_constexpr_ltor_mutable, 1) << Field;
>> +  S.FFDiag(Loc, diag::note_constexpr_access_mutable, 1) << AK_Read <<
>> Field;
>>    S.Note(Field->getLocation(), diag::note_declared_at);
>>    return false;
>>  }
>>
>> Modified: cfe/trunk/lib/AST/TextNodeDumper.cpp
>> URL:
>> http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/TextNodeDumper.cpp?rev=373159&r1=373158&r2=373159&view=diff
>>
>> ==============================================================================
>> --- cfe/trunk/lib/AST/TextNodeDumper.cpp (original)
>> +++ cfe/trunk/lib/AST/TextNodeDumper.cpp Sat Sep 28 22:08:46 2019
>> @@ -1384,6 +1384,8 @@ void TextNodeDumper::VisitVarDecl(const
>>        break;
>>      }
>>    }
>> +  if (D->needsDestruction(D->getASTContext()))
>> +    OS << " destroyed";
>>    if (D->isParameterPack())
>>      OS << " pack";
>>  }
>>
>> Modified: cfe/trunk/lib/CodeGen/CGCall.cpp
>> URL:
>> http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/CodeGen/CGCall.cpp?rev=373159&r1=373158&r2=373159&view=diff
>>
>> ==============================================================================
>> --- cfe/trunk/lib/CodeGen/CGCall.cpp (original)
>> +++ cfe/trunk/lib/CodeGen/CGCall.cpp Sat Sep 28 22:08:46 2019
>> @@ -3093,7 +3093,7 @@ void CodeGenFunction::EmitDelegateCallAr
>>    // Deactivate the cleanup for the callee-destructed param that was
>> pushed.
>>    if (hasAggregateEvaluationKind(type) && !CurFuncIsThunk &&
>>        type->getAs<RecordType>()->getDecl()->isParamDestroyedInCallee() &&
>> -      type.isDestructedType()) {
>> +      param->needsDestruction(getContext())) {
>>      EHScopeStack::stable_iterator cleanup =
>>          CalleeDestructedParamCleanups.lookup(cast<ParmVarDecl>(param));
>>      assert(cleanup.isValid() &&
>>
>> Modified: cfe/trunk/lib/CodeGen/CGClass.cpp
>> URL:
>> http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/CodeGen/CGClass.cpp?rev=373159&r1=373158&r2=373159&view=diff
>>
>> ==============================================================================
>> --- cfe/trunk/lib/CodeGen/CGClass.cpp (original)
>> +++ cfe/trunk/lib/CodeGen/CGClass.cpp Sat Sep 28 22:08:46 2019
>> @@ -2083,7 +2083,7 @@ static bool canEmitDelegateCallArgs(Code
>>    if (CGF.getTarget().getCXXABI().areArgsDestroyedLeftToRightInCallee())
>> {
>>      // If the parameters are callee-cleanup, it's not safe to forward.
>>      for (auto *P : Ctor->parameters())
>> -      if (P->getType().isDestructedType())
>> +      if (P->needsDestruction(CGF.getContext()))
>>          return false;
>>
>>      // Likewise if they're inalloca.
>>
>> Modified: cfe/trunk/lib/CodeGen/CGDecl.cpp
>> URL:
>> http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/CodeGen/CGDecl.cpp?rev=373159&r1=373158&r2=373159&view=diff
>>
>> ==============================================================================
>> --- cfe/trunk/lib/CodeGen/CGDecl.cpp (original)
>> +++ cfe/trunk/lib/CodeGen/CGDecl.cpp Sat Sep 28 22:08:46 2019
>> @@ -305,14 +305,6 @@ llvm::Constant *CodeGenModule::getOrCrea
>>    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
>> @@ -372,7 +364,7 @@ CodeGenFunction::AddInitializerToStaticV
>>
>>    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.
>> @@ -1994,7 +1986,7 @@ void CodeGenFunction::EmitAutoVarCleanup
>>    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.
>> @@ -2404,7 +2396,8 @@ void CodeGenFunction::EmitParmDecl(const
>>      // 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");
>>
>> Modified: cfe/trunk/lib/CodeGen/CGDeclCXX.cpp
>> URL:
>> http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/CodeGen/CGDeclCXX.cpp?rev=373159&r1=373158&r2=373159&view=diff
>>
>> ==============================================================================
>> --- cfe/trunk/lib/CodeGen/CGDeclCXX.cpp (original)
>> +++ cfe/trunk/lib/CodeGen/CGDeclCXX.cpp Sat Sep 28 22:08:46 2019
>> @@ -73,16 +73,10 @@ static void EmitDeclDestroy(CodeGenFunct
>>    // 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;
>> @@ -101,6 +95,9 @@ static void EmitDeclDestroy(CodeGenFunct
>>    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
>>
>> Modified: cfe/trunk/lib/CodeGen/CodeGenModule.cpp
>> URL:
>> http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/CodeGen/CodeGenModule.cpp?rev=373159&r1=373158&r2=373159&view=diff
>>
>> ==============================================================================
>> --- cfe/trunk/lib/CodeGen/CodeGenModule.cpp (original)
>> +++ cfe/trunk/lib/CodeGen/CodeGenModule.cpp Sat Sep 28 22:08:46 2019
>> @@ -3809,9 +3809,9 @@ void CodeGenModule::EmitGlobalVarDefinit
>>      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);
>>
>> Modified: cfe/trunk/lib/CodeGen/ItaniumCXXABI.cpp
>> URL:
>> http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/CodeGen/ItaniumCXXABI.cpp?rev=373159&r1=373158&r2=373159&view=diff
>>
>> ==============================================================================
>> --- cfe/trunk/lib/CodeGen/ItaniumCXXABI.cpp (original)
>> +++ cfe/trunk/lib/CodeGen/ItaniumCXXABI.cpp Sat Sep 28 22:08:46 2019
>> @@ -350,7 +350,7 @@ public:
>>      // 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
>>
>> Modified: cfe/trunk/lib/Sema/SemaDeclCXX.cpp
>> URL:
>> http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaDeclCXX.cpp?rev=373159&r1=373158&r2=373159&view=diff
>>
>> ==============================================================================
>> --- cfe/trunk/lib/Sema/SemaDeclCXX.cpp (original)
>> +++ cfe/trunk/lib/Sema/SemaDeclCXX.cpp Sat Sep 28 22:08:46 2019
>> @@ -13398,6 +13398,19 @@ void Sema::FinalizeVarWithDestructor(Var
>>    }
>>
>>    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,
>>
>> Modified: cfe/trunk/lib/Serialization/ASTReaderDecl.cpp
>> URL:
>> http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Serialization/ASTReaderDecl.cpp?rev=373159&r1=373158&r2=373159&view=diff
>>
>> ==============================================================================
>> --- cfe/trunk/lib/Serialization/ASTReaderDecl.cpp (original)
>> +++ cfe/trunk/lib/Serialization/ASTReaderDecl.cpp Sat Sep 28 22:08:46 2019
>> @@ -1390,10 +1390,11 @@ ASTDeclReader::RedeclarableResult ASTDec
>>
>>    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;
>>      }
>>    }
>>
>>
>> Modified: cfe/trunk/lib/Serialization/ASTWriterDecl.cpp
>> URL:
>> http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Serialization/ASTWriterDecl.cpp?rev=373159&r1=373158&r2=373159&view=diff
>>
>> ==============================================================================
>> --- cfe/trunk/lib/Serialization/ASTWriterDecl.cpp (original)
>> +++ cfe/trunk/lib/Serialization/ASTWriterDecl.cpp Sat Sep 28 22:08:46 2019
>> @@ -968,7 +968,14 @@ void ASTDeclWriter::VisitVarDecl(VarDecl
>>    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);
>> @@ -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));
>>
>> Modified: cfe/trunk/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p9.cpp
>> URL:
>> http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p9.cpp?rev=373159&r1=373158&r2=373159&view=diff
>>
>> ==============================================================================
>> --- cfe/trunk/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p9.cpp (original)
>> +++ cfe/trunk/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p9.cpp Sat Sep 28
>> 22:08:46 2019
>> @@ -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.
>> @@ -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
>>
>> Added: cfe/trunk/test/CXX/expr/expr.const/p6-2a.cpp
>> URL:
>> http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CXX/expr/expr.const/p6-2a.cpp?rev=373159&view=auto
>>
>> ==============================================================================
>> --- cfe/trunk/test/CXX/expr/expr.const/p6-2a.cpp (added)
>> +++ cfe/trunk/test/CXX/expr/expr.const/p6-2a.cpp Sat Sep 28 22:08:46 2019
>> @@ -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}}
>>
>> Modified: cfe/trunk/test/CodeGenCXX/attr-no-destroy-d54344.cpp
>> URL:
>> http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CodeGenCXX/attr-no-destroy-d54344.cpp?rev=373159&r1=373158&r2=373159&view=diff
>>
>> ==============================================================================
>> --- cfe/trunk/test/CodeGenCXX/attr-no-destroy-d54344.cpp (original)
>> +++ cfe/trunk/test/CodeGenCXX/attr-no-destroy-d54344.cpp Sat Sep 28
>> 22:08:46 2019
>> @@ -14,6 +14,7 @@
>>
>>  class a {
>>  public:
>> +  a();
>>    ~a();
>>  };
>>  class logger_base {
>>
>> Modified: cfe/trunk/test/CodeGenCXX/const-init-cxx2a.cpp
>> URL:
>> http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CodeGenCXX/const-init-cxx2a.cpp?rev=373159&r1=373158&r2=373159&view=diff
>>
>> ==============================================================================
>> --- cfe/trunk/test/CodeGenCXX/const-init-cxx2a.cpp (original)
>> +++ cfe/trunk/test/CodeGenCXX/const-init-cxx2a.cpp Sat Sep 28 22:08:46
>> 2019
>> @@ -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;
>> +}
>>
>> Modified: cfe/trunk/test/CodeGenCXX/no_destroy.cpp
>> URL:
>> http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CodeGenCXX/no_destroy.cpp?rev=373159&r1=373158&r2=373159&view=diff
>>
>> ==============================================================================
>> --- cfe/trunk/test/CodeGenCXX/no_destroy.cpp (original)
>> +++ cfe/trunk/test/CodeGenCXX/no_destroy.cpp Sat Sep 28 22:08:46 2019
>> @@ -5,10 +5,8 @@ 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;
>>
>> @@ -16,11 +14,9 @@ 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
>>
>> Added: cfe/trunk/test/CodeGenCXX/non-const-init-cxx2a.cpp
>> URL:
>> http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CodeGenCXX/non-const-init-cxx2a.cpp?rev=373159&view=auto
>>
>> ==============================================================================
>> --- cfe/trunk/test/CodeGenCXX/non-const-init-cxx2a.cpp (added)
>> +++ cfe/trunk/test/CodeGenCXX/non-const-init-cxx2a.cpp Sat Sep 28
>> 22:08:46 2019
>> @@ -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
>>
>>
>> _______________________________________________
>> cfe-commits mailing list
>> cfe-commits at lists.llvm.org
>> https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
>>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.llvm.org/pipermail/cfe-commits/attachments/20191111/3ef442a1/attachment-0001.html>


More information about the cfe-commits mailing list