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 <arpha...@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@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@lists.llvm.org >> https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits >> >
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits