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

Alex L via cfe-commits cfe-commits at lists.llvm.org
Wed Jan 8 13:17:04 PST 2020


Hi Richard,

I posted a patch that partially reverts your commit:
https://reviews.llvm.org/D72411. Unfortunately I can't revert it fully.

Thanks,
Alex

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

> 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/20200108/844837c9/attachment-0001.html>


More information about the cfe-commits mailing list