From 76c0092665867a6defcd328ba0d0d976eb65d991 Mon Sep 17 00:00:00 2001 From: Richard Smith Date: Mon, 19 Oct 2020 19:01:48 -0700 Subject: [PATCH] Ensure that checkInitIsICE is called exactly once for every variable for which it matters. This is a step towards separating checking for a constant initializer (in which std::is_constant_evaluated returns true) and any other evaluation of a variable initializer (in which it returns false). --- clang/include/clang/AST/Decl.h | 13 +-- .../include/clang/Serialization/ASTRecordWriter.h | 3 + clang/lib/AST/ComparisonCategories.cpp | 2 +- clang/lib/AST/Decl.cpp | 57 +++++---- clang/lib/AST/ExprConstant.cpp | 50 +++----- clang/lib/CodeGen/ItaniumCXXABI.cpp | 5 +- clang/lib/Sema/SemaDecl.cpp | 129 +++++++++++---------- clang/lib/Sema/SemaDeclCXX.cpp | 7 +- clang/lib/Serialization/ASTReaderDecl.cpp | 11 +- clang/lib/Serialization/ASTWriter.cpp | 29 +++-- clang/lib/Serialization/ASTWriterDecl.cpp | 14 +-- clang/test/CodeGen/enable_if.c | 10 +- clang/test/OpenMP/threadprivate_codegen.cpp | 32 ++--- clang/test/Sema/enable_if.c | 4 +- clang/test/SemaCXX/constant-expression.cpp | 4 +- clang/test/SemaCXX/i-c-e-cxx.cpp | 3 - 16 files changed, 180 insertions(+), 193 deletions(-) diff --git a/clang/include/clang/AST/Decl.h b/clang/include/clang/AST/Decl.h index eae0983..e309819 100644 --- a/clang/include/clang/AST/Decl.h +++ b/clang/include/clang/AST/Decl.h @@ -807,10 +807,6 @@ struct EvaluatedStmt { /// integral constant expression. bool CheckedICE : 1; - /// Whether we are checking whether this statement is an - /// integral constant expression. - bool CheckingICE : 1; - /// Whether this statement is an integral constant expression, /// or in C++11, whether the statement is a constant expression. Only /// valid if CheckedICE is true. @@ -828,7 +824,7 @@ struct EvaluatedStmt { EvaluatedStmt() : WasEvaluated(false), IsEvaluating(false), CheckedICE(false), - CheckingICE(false), IsICE(false), HasConstantDestruction(false) {} + IsICE(false), HasConstantDestruction(false) {} }; /// Represents a variable declaration or definition. @@ -1263,14 +1259,15 @@ public: /// constant expression, according to the relevant language standard. /// This only checks properties of the declaration, and does not check /// whether the initializer is in fact a constant expression. - bool mightBeUsableInConstantExpressions(ASTContext &C) const; + bool mightBeUsableInConstantExpressions(const ASTContext &C) const; /// Determine whether this variable's value can be used in a /// constant expression, according to the relevant language standard, /// including checking whether it was initialized by a constant expression. - bool isUsableInConstantExpressions(ASTContext &C) const; + bool isUsableInConstantExpressions(const ASTContext &C) const; EvaluatedStmt *ensureEvaluatedStmt() const; + EvaluatedStmt *getEvaluatedStmt() const; /// Attempt to evaluate the value of the initializer attached to this /// declaration, and produce notes explaining why it cannot be evaluated or is @@ -1305,7 +1302,7 @@ public: /// Determine whether the value of the initializer attached to this /// declaration is an integral constant expression. - bool checkInitIsICE() const; + bool checkInitIsICE(SmallVectorImpl &Notes) const; void setInitStyle(InitializationStyle Style) { VarDeclBits.InitStyle = Style; diff --git a/clang/include/clang/Serialization/ASTRecordWriter.h b/clang/include/clang/Serialization/ASTRecordWriter.h index edfcd9c..e362463 100644 --- a/clang/include/clang/Serialization/ASTRecordWriter.h +++ b/clang/include/clang/Serialization/ASTRecordWriter.h @@ -266,6 +266,9 @@ public: void AddCXXDefinitionData(const CXXRecordDecl *D); + /// Emit information about the initializer of a VarDecl. + void AddVarDeclInit(const VarDecl *VD); + /// Write an OMPTraitInfo object. void writeOMPTraitInfo(const OMPTraitInfo *TI); diff --git a/clang/lib/AST/ComparisonCategories.cpp b/clang/lib/AST/ComparisonCategories.cpp index 6b6826c..8960504 100644 --- a/clang/lib/AST/ComparisonCategories.cpp +++ b/clang/lib/AST/ComparisonCategories.cpp @@ -42,7 +42,7 @@ clang::getComparisonCategoryForBuiltinCmp(QualType T) { bool ComparisonCategoryInfo::ValueInfo::hasValidIntValue() const { assert(VD && "must have var decl"); - if (!VD->checkInitIsICE()) + if (!VD->isUsableInConstantExpressions(VD->getASTContext())) return false; // Before we attempt to get the value of the first field, ensure that we diff --git a/clang/lib/AST/Decl.cpp b/clang/lib/AST/Decl.cpp index a6c7f30..ee7f51c 100644 --- a/clang/lib/AST/Decl.cpp +++ b/clang/lib/AST/Decl.cpp @@ -2277,7 +2277,7 @@ void VarDecl::setInit(Expr *I) { Init = I; } -bool VarDecl::mightBeUsableInConstantExpressions(ASTContext &C) const { +bool VarDecl::mightBeUsableInConstantExpressions(const ASTContext &C) const { const LangOptions &Lang = C.getLangOpts(); if (!Lang.CPlusPlus) @@ -2312,7 +2312,7 @@ bool VarDecl::mightBeUsableInConstantExpressions(ASTContext &C) const { return Lang.CPlusPlus11 && isConstexpr(); } -bool VarDecl::isUsableInConstantExpressions(ASTContext &Context) const { +bool VarDecl::isUsableInConstantExpressions(const ASTContext &Context) const { // C++2a [expr.const]p3: // A variable is usable in constant expressions after its initializing // declaration is encountered... @@ -2325,7 +2325,7 @@ bool VarDecl::isUsableInConstantExpressions(ASTContext &Context) const { if (!DefVD->mightBeUsableInConstantExpressions(Context)) return false; // ... and its initializer is a constant initializer. - return DefVD->checkInitIsICE(); + return DefVD->isInitKnownICE() && DefVD->isInitICE(); } /// Convert the initializer for this declaration to the elaborated EvaluatedStmt @@ -2345,6 +2345,10 @@ EvaluatedStmt *VarDecl::ensureEvaluatedStmt() const { return Eval; } +EvaluatedStmt *VarDecl::getEvaluatedStmt() const { + return Init.dyn_cast(); +} + APValue *VarDecl::evaluateValue() const { SmallVector Notes; return evaluateValue(Notes); @@ -2354,19 +2358,17 @@ APValue *VarDecl::evaluateValue( SmallVectorImpl &Notes) const { EvaluatedStmt *Eval = ensureEvaluatedStmt(); + const auto *Init = cast(Eval->Value); + assert(!Init->isValueDependent()); + // We only produce notes indicating why an initializer is non-constant the // first time it is evaluated. FIXME: The notes won't always be emitted the // first time we try evaluation, so might not be produced at all. if (Eval->WasEvaluated) return Eval->Evaluated.isAbsent() ? nullptr : &Eval->Evaluated; - const auto *Init = cast(Eval->Value); - assert(!Init->isValueDependent()); - if (Eval->IsEvaluating) { // FIXME: Produce a diagnostic for self-initialization. - Eval->CheckedICE = true; - Eval->IsICE = false; return nullptr; } @@ -2386,18 +2388,11 @@ APValue *VarDecl::evaluateValue( Eval->IsEvaluating = false; Eval->WasEvaluated = true; - // In C++11, we have determined whether the initializer was a constant - // expression as a side-effect. - if (getASTContext().getLangOpts().CPlusPlus11 && !Eval->CheckedICE) { - Eval->CheckedICE = true; - Eval->IsICE = Result && Notes.empty(); - } - return Result ? &Eval->Evaluated : nullptr; } APValue *VarDecl::getEvaluatedValue() const { - if (EvaluatedStmt *Eval = Init.dyn_cast()) + if (EvaluatedStmt *Eval = getEvaluatedStmt()) if (Eval->WasEvaluated) return &Eval->Evaluated; @@ -2405,7 +2400,7 @@ APValue *VarDecl::getEvaluatedValue() const { } bool VarDecl::isInitKnownICE() const { - if (EvaluatedStmt *Eval = Init.dyn_cast()) + if (EvaluatedStmt *Eval = getEvaluatedStmt()) return Eval->CheckedICE; return false; @@ -2417,12 +2412,16 @@ bool VarDecl::isInitICE() const { return Init.get()->IsICE; } -bool VarDecl::checkInitIsICE() const { +bool VarDecl::checkInitIsICE( + SmallVectorImpl &Notes) const { EvaluatedStmt *Eval = ensureEvaluatedStmt(); - if (Eval->CheckedICE) - // We have already checked whether this subexpression is an - // integral constant expression. - return Eval->IsICE; + assert(!Eval->CheckedICE && + "should check whether var has constant init at most once"); + // If we ask for the value before we know whether we have a constant + // initializer, we can compute the wrong value (for example, due to + // std::is_constant_evaluated()). + assert(!Eval->WasEvaluated && + "already evaluated var value before checking for constant init"); const auto *Init = cast(Eval->Value); assert(!Init->isValueDependent()); @@ -2430,8 +2429,8 @@ bool VarDecl::checkInitIsICE() const { // In C++11, evaluate the initializer to check whether it's a constant // expression. if (getASTContext().getLangOpts().CPlusPlus11) { - SmallVector Notes; - evaluateValue(Notes); + Eval->IsICE = evaluateValue(Notes) && Notes.empty(); + Eval->CheckedICE = true; return Eval->IsICE; } @@ -2439,12 +2438,8 @@ bool VarDecl::checkInitIsICE() const { // out-of-line. See DR 721 and the discussion in Clang PR // 6206 for details. - if (Eval->CheckingICE) - return false; - Eval->CheckingICE = true; - - Eval->IsICE = Init->isIntegerConstantExpr(getASTContext()); - Eval->CheckingICE = false; + Eval->IsICE = getType()->isIntegralOrEnumerationType() && + Init->isIntegerConstantExpr(getASTContext()); Eval->CheckedICE = true; return Eval->IsICE; } @@ -2599,7 +2594,7 @@ bool VarDecl::isNoDestroy(const ASTContext &Ctx) const { QualType::DestructionKind VarDecl::needsDestruction(const ASTContext &Ctx) const { - if (EvaluatedStmt *Eval = Init.dyn_cast()) + if (EvaluatedStmt *Eval = getEvaluatedStmt()) if (Eval->HasConstantDestruction) return QualType::DK_none; diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp index 7afc44d..3014f94 100644 --- a/clang/lib/AST/ExprConstant.cpp +++ b/clang/lib/AST/ExprConstant.cpp @@ -3278,12 +3278,17 @@ static bool evaluateVarDeclInit(EvalInfo &Info, const Expr *E, return false; } - // Check that the variable is actually usable in constant expressions. - if (!VD->checkInitIsICE()) { - Info.CCEDiag(E, diag::note_constexpr_var_init_non_constant, - Notes.size() + 1) << VD; + // Check that the variable is actually usable in constant expressions. For a + // const integral variable or a reference, we might have a non-constant + // initializer that we can nonetheless evaluate the initializer for. Such + // variables are not usable in constant expressions. + // + // FIXME: It would be cleaner to check VD->isUsableInConstantExpressions + // here, but that regresses diagnostics for things like reading from a + // volatile constexpr variable. + if (VD->isInitKnownICE() && !VD->isInitICE()) { + Info.CCEDiag(E, diag::note_constexpr_var_init_non_constant, 1) << VD; NoteLValueLocation(Info, Base); - Info.addNotes(Notes); } // Never use the initializer of a weak variable, not even for constant @@ -3298,11 +3303,6 @@ static bool evaluateVarDeclInit(EvalInfo &Info, const Expr *E, return true; } -static bool IsConstNonVolatile(QualType T) { - Qualifiers Quals = T.getQualifiers(); - return Quals.hasConst() && !Quals.hasVolatile(); -} - /// Get the base index of the given base class within an APValue representing /// the given derived class. static unsigned getBaseIndex(const CXXRecordDecl *Derived, @@ -8114,6 +8114,12 @@ bool LValueExprEvaluator::VisitVarDecl(const Expr *E, const VarDecl *VD) { return Success(VD); } + if (!Info.getLangOpts().CPlusPlus11) { + Info.CCEDiag(E, diag::note_constexpr_ltor_non_integral, 1) + << VD << VD->getType(); + Info.Note(VD->getLocation(), diag::note_declared_at); + } + APValue *V; if (!evaluateVarDeclInit(Info, E, VD, Frame, Version, V)) return false; @@ -15030,30 +15036,12 @@ static ICEDiag CheckICE(const Expr* E, const ASTContext &Ctx) { case Expr::DeclRefExprClass: { if (isa(cast(E)->getDecl())) return NoDiag(); - const ValueDecl *D = cast(E)->getDecl(); - if (Ctx.getLangOpts().CPlusPlus && - D && IsConstNonVolatile(D->getType())) { - // Parameter variables are never constants. Without this check, - // getAnyInitializer() can find a default argument, which leads - // to chaos. - if (isa(D)) - return ICEDiag(IK_NotICE, cast(E)->getLocation()); - + const VarDecl *VD = dyn_cast(cast(E)->getDecl()); + if (VD && VD->isUsableInConstantExpressions(Ctx)) { // C++ 7.1.5.1p2 // A variable of non-volatile const-qualified integral or enumeration // type initialized by an ICE can be used in ICEs. - if (const VarDecl *Dcl = dyn_cast(D)) { - if (!Dcl->getType()->isIntegralOrEnumerationType()) - return ICEDiag(IK_NotICE, cast(E)->getLocation()); - - const VarDecl *VD; - // Look for a declaration of this variable that has an initializer, and - // check whether it is an ICE. - if (Dcl->getAnyInitializer(VD) && !VD->isWeak() && VD->checkInitIsICE()) - return NoDiag(); - else - return ICEDiag(IK_NotICE, cast(E)->getLocation()); - } + return NoDiag(); } return ICEDiag(IK_NotICE, E->getBeginLoc()); } diff --git a/clang/lib/CodeGen/ItaniumCXXABI.cpp b/clang/lib/CodeGen/ItaniumCXXABI.cpp index cfb736c..40cd5c5 100644 --- a/clang/lib/CodeGen/ItaniumCXXABI.cpp +++ b/clang/lib/CodeGen/ItaniumCXXABI.cpp @@ -361,8 +361,9 @@ public: 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 - // ICE-ness not varying between translation units, which isn't actually + // translation unit will emit the value as a constant. We rely on the + // variable being constant-initialized in every translation unit if it's + // constant-initialized in any translation unit, which isn't actually // guaranteed by the standard but is necessary for sanity. return InitDecl->isInitKnownICE() && InitDecl->isInitICE(); } diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index 481b48e..1a27667 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -12958,18 +12958,14 @@ void Sema::CheckCompleteVariableDeclaration(VarDecl *var) { // All the following checks are C++ only. if (!getLangOpts().CPlusPlus) { - // If this variable must be emitted, add it as an initializer for the - // current module. - if (Context.DeclMustBeEmitted(var) && !ModuleScopes.empty()) - Context.addModuleInitializer(ModuleScopes.back().Module, var); - return; + // If this variable must be emitted, add it as an initializer for the + // current module. + if (Context.DeclMustBeEmitted(var) && !ModuleScopes.empty()) + Context.addModuleInitializer(ModuleScopes.back().Module, var); + return; } - if (auto *DD = dyn_cast(var)) - CheckCompleteDecompositionDeclaration(DD); - QualType type = var->getType(); - if (type->isDependentType()) return; if (var->hasAttr()) getCurFunction()->addByrefBlockVar(var); @@ -12978,79 +12974,86 @@ void Sema::CheckCompleteVariableDeclaration(VarDecl *var) { bool IsGlobal = GlobalStorage && !var->isStaticLocal(); QualType baseType = Context.getBaseElementType(type); - if (Init && !Init->isValueDependent()) { - if (var->isConstexpr()) { - SmallVector Notes; - if (!var->evaluateValue(Notes) || !var->isInitICE()) { - SourceLocation DiagLoc = var->getLocation(); - // If the note doesn't add any useful information other than a source - // location, fold it into the primary diagnostic. - if (Notes.size() == 1 && Notes[0].second.getDiagID() == - diag::note_invalid_subexpr_in_const_expr) { - DiagLoc = Notes[0].first; - Notes.clear(); - } - Diag(DiagLoc, diag::err_constexpr_var_requires_const_init) - << var << Init->getSourceRange(); - for (unsigned I = 0, N = Notes.size(); I != N; ++I) - Diag(Notes[I].first, Notes[I].second); - } - } else if (var->mightBeUsableInConstantExpressions(Context)) { - // Check whether the initializer of a const variable of integral or - // enumeration type is an ICE now, since we can't tell whether it was - // initialized by a constant expression if we check later. - var->checkInitIsICE(); - } - - // Don't emit further diagnostics about constexpr globals since they - // were just diagnosed. - if (!var->isConstexpr() && GlobalStorage && var->hasAttr()) { - // FIXME: Need strict checking in C++03 here. - bool DiagErr = getLangOpts().CPlusPlus11 - ? !var->checkInitIsICE() : !checkConstInit(); - if (DiagErr) { - auto *Attr = var->getAttr(); - Diag(var->getLocation(), diag::err_require_constant_init_failed) - << Init->getSourceRange(); - Diag(Attr->getLocation(), - diag::note_declared_required_constant_init_here) - << Attr->getRange() << Attr->isConstinit(); - if (getLangOpts().CPlusPlus11) { - APValue Value; - SmallVector Notes; - Init->EvaluateAsInitializer(Value, getASTContext(), var, Notes); - for (auto &it : Notes) - Diag(it.first, it.second); - } else { - Diag(CacheCulprit->getExprLoc(), - diag::note_invalid_subexpr_in_const_expr) - << CacheCulprit->getSourceRange(); - } + // Check whether the initializer is sufficiently constant. + if (!type->isDependentType() && Init && !Init->isValueDependent() && + (GlobalStorage || var->isConstexpr() || + var->mightBeUsableInConstantExpressions(Context))) { + // If this variable might have a constant initializer or might be usable in + // constant expressions, check whether or not it actually is now. We can't + // do this lazily, because the result might depend on things that change + // later, such as which constexpr functions happen to be defined. + SmallVector Notes; + bool HasConstInit = var->checkInitIsICE(Notes); + + // Prior to C++11, in contexts where a constant initializer is required, + // additional kinds of constant expression are permitted beyond ICEs, as + // described in [expr.const]p2-6. + // FIXME: Stricter checking for these rules would be useful for constinit / + // -Wglobal-constructors. + if (!getLangOpts().CPlusPlus11 && !HasConstInit) { + HasConstInit = checkConstInit(); + Notes.clear(); + if (CacheCulprit) { + Notes.emplace_back(CacheCulprit->getExprLoc(), + PDiag(diag::note_invalid_subexpr_in_const_expr)); + Notes.back().second << CacheCulprit->getSourceRange(); } } - else if (!var->isConstexpr() && IsGlobal && - !getDiagnostics().isIgnored(diag::warn_global_constructor, - var->getLocation())) { + + if (HasConstInit) { + // FIXME: Consider replacing the initializer with a ConstantExpr. + } else if (var->isConstexpr()) { + SourceLocation DiagLoc = var->getLocation(); + // If the note doesn't add any useful information other than a source + // location, fold it into the primary diagnostic. + if (Notes.size() == 1 && Notes[0].second.getDiagID() == + diag::note_invalid_subexpr_in_const_expr) { + DiagLoc = Notes[0].first; + Notes.clear(); + } + Diag(DiagLoc, diag::err_constexpr_var_requires_const_init) + << var << Init->getSourceRange(); + for (unsigned I = 0, N = Notes.size(); I != N; ++I) + Diag(Notes[I].first, Notes[I].second); + } else if (GlobalStorage && var->hasAttr()) { + auto *Attr = var->getAttr(); + Diag(var->getLocation(), diag::err_require_constant_init_failed) + << Init->getSourceRange(); + Diag(Attr->getLocation(), diag::note_declared_required_constant_init_here) + << Attr->getRange() << Attr->isConstinit(); + for (auto &it : Notes) + Diag(it.first, it.second); + } else if (IsGlobal && + !getDiagnostics().isIgnored(diag::warn_global_constructor, + var->getLocation())) { // Warn about globals which don't have a constant initializer. Don't // warn about globals with a non-trivial destructor because we already // warned about them. CXXRecordDecl *RD = baseType->getAsCXXRecordDecl(); if (!(RD && !RD->hasTrivialDestructor())) { + // checkConstInit() here permits trivial default initialization even in + // C++11 onwards, where such an initializer is not a constant initializer + // but nonetheless doesn't require a global constructor. if (!checkConstInit()) Diag(var->getLocation(), diag::warn_global_constructor) - << Init->getSourceRange(); + << Init->getSourceRange(); } } } // Require the destructor. - if (const RecordType *recordType = baseType->getAs()) - FinalizeVarWithDestructor(var, recordType); + if (!type->isDependentType()) + if (const RecordType *recordType = baseType->getAs()) + FinalizeVarWithDestructor(var, recordType); // If this variable must be emitted, add it as an initializer for the current // module. if (Context.DeclMustBeEmitted(var) && !ModuleScopes.empty()) Context.addModuleInitializer(ModuleScopes.back().Module, var); + + // Build the bindings if this is a structured binding declaration. + if (auto *DD = dyn_cast(var)) + CheckCompleteDecompositionDeclaration(DD); } /// Determines if a variable's alignment is dependent. diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp index cbcaf3c..72dfa37 100644 --- a/clang/lib/Sema/SemaDeclCXX.cpp +++ b/clang/lib/Sema/SemaDeclCXX.cpp @@ -1250,8 +1250,7 @@ static bool checkTupleLikeDecomposition(Sema &S, if (E.isInvalid()) return true; RefVD->setInit(E.get()); - if (!E.get()->isValueDependent()) - RefVD->checkInitIsICE(); + S.CheckCompleteVariableDeclaration(RefVD); E = S.BuildDeclarationNameExpr(CXXScopeSpec(), DeclarationNameInfo(B->getDeclName(), Loc), @@ -11113,8 +11112,8 @@ QualType Sema::CheckComparisonCategoryType(ComparisonCategoryType Kind, // Attempt to diagnose reasons why the STL definition of this type // might be foobar, including it failing to be a constant expression. // TODO Handle more ways the lookup or result can be invalid. - if (!VD->isStaticDataMember() || !VD->isConstexpr() || !VD->hasInit() || - VD->isWeak() || !VD->checkInitIsICE()) + if (!VD->isStaticDataMember() || + !VD->isUsableInConstantExpressions(Context)) return UnsupportedSTLError(USS_InvalidMember, MemName, VD); // Attempt to evaluate the var decl as a constant expression and extract diff --git a/clang/lib/Serialization/ASTReaderDecl.cpp b/clang/lib/Serialization/ASTReaderDecl.cpp index f5a66dc..41f2db1 100644 --- a/clang/lib/Serialization/ASTReaderDecl.cpp +++ b/clang/lib/Serialization/ASTReaderDecl.cpp @@ -1425,8 +1425,8 @@ ASTDeclReader::RedeclarableResult ASTDeclReader::VisitVarDeclImpl(VarDecl *VD) { VD->setInit(Record.readExpr()); if (Val > 1) { EvaluatedStmt *Eval = VD->ensureEvaluatedStmt(); - Eval->CheckedICE = true; - Eval->IsICE = (Val & 1) != 0; + Eval->CheckedICE = (Val & 2) != 0; + Eval->IsICE = (Val & 3) == 3; Eval->HasConstantDestruction = (Val & 4) != 0; } } @@ -4438,10 +4438,11 @@ void ASTDeclReader::UpdateDecl(Decl *D, uint64_t Val = Record.readInt(); if (Val && !VD->getInit()) { 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->CheckedICE = (Val & 2) != 0; + Eval->IsICE = (Val & 3) == 3; + Eval->HasConstantDestruction = (Val & 4) != 0; } } break; diff --git a/clang/lib/Serialization/ASTWriter.cpp b/clang/lib/Serialization/ASTWriter.cpp index e793e61..6056ed6 100644 --- a/clang/lib/Serialization/ASTWriter.cpp +++ b/clang/lib/Serialization/ASTWriter.cpp @@ -4980,13 +4980,7 @@ void ASTWriter::WriteDeclUpdatesBlocks(RecordDataImpl &OffsetsRecord) { const VarDecl *VD = cast(D); Record.push_back(VD->isInline()); Record.push_back(VD->isInlineSpecified()); - if (VD->getInit()) { - Record.push_back(!VD->isInitKnownICE() ? 1 - : (VD->isInitICE() ? 3 : 2)); - Record.AddStmt(const_cast(VD->getInit())); - } else { - Record.push_back(0); - } + Record.AddVarDeclInit(VD); break; } @@ -5746,6 +5740,27 @@ void ASTRecordWriter::AddCXXDefinitionData(const CXXRecordDecl *D) { } } +void ASTRecordWriter::AddVarDeclInit(const VarDecl *VD) { + const Expr *Init = VD->getInit(); + if (!Init) { + push_back(0); + return; + } + + // Bottom two bits are as follows: + // 01 -- initializer not checked for ICE + // 10 -- initializer not ICE + // 11 -- initializer ICE + unsigned Val = 1; + if (EvaluatedStmt *ES = VD->getEvaluatedStmt()) { + if (ES->CheckedICE) + Val = 2 | ES->IsICE; + Val |= (ES->HasConstantDestruction ? 4 : 0); + } + push_back(Val); + writeStmtRef(Init); +} + void ASTWriter::ReaderInitialized(ASTReader *Reader) { assert(Reader && "Cannot remove chain"); assert((!Chain || Chain == Reader) && "Cannot replace chain"); diff --git a/clang/lib/Serialization/ASTWriterDecl.cpp b/clang/lib/Serialization/ASTWriterDecl.cpp index 911fcb4..8778f0c 100644 --- a/clang/lib/Serialization/ASTWriterDecl.cpp +++ b/clang/lib/Serialization/ASTWriterDecl.cpp @@ -1000,19 +1000,7 @@ void ASTDeclWriter::VisitVarDecl(VarDecl *D) { } Record.push_back(D->getLinkageInternal()); - if (D->getInit()) { - 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); - } + Record.AddVarDeclInit(D); if (D->hasAttr() && D->getType()->getAsCXXRecordDecl()) { BlockVarCopyInit Init = Writer.Context->getBlockVarCopyInit(D); diff --git a/clang/test/CodeGen/enable_if.c b/clang/test/CodeGen/enable_if.c index 5e9f904..1d830ae 100644 --- a/clang/test/CodeGen/enable_if.c +++ b/clang/test/CodeGen/enable_if.c @@ -65,19 +65,19 @@ void test3() { } -const int TRUEFACTS = 1; +enum { TRUEFACTS = 1 }; void qux(int m) __attribute__((overloadable, enable_if(1, ""), enable_if(TRUEFACTS, ""))); void qux(int m) __attribute__((overloadable, enable_if(1, ""))); // CHECK-LABEL: define void @test4 void test4() { - // CHECK: store void (i32)* @_Z3quxUa9enable_ifIXLi1EEXL_Z9TRUEFACTSEEEi + // CHECK: store void (i32)* @_Z3quxUa9enable_ifIXLi1EEXLi1EEEi void (*p)(int) = qux; - // CHECK: store void (i32)* @_Z3quxUa9enable_ifIXLi1EEXL_Z9TRUEFACTSEEEi + // CHECK: store void (i32)* @_Z3quxUa9enable_ifIXLi1EEXLi1EEEi void (*p2)(int) = &qux; - // CHECK: store void (i32)* @_Z3quxUa9enable_ifIXLi1EEXL_Z9TRUEFACTSEEEi + // CHECK: store void (i32)* @_Z3quxUa9enable_ifIXLi1EEXLi1EEEi p = qux; - // CHECK: store void (i32)* @_Z3quxUa9enable_ifIXLi1EEXL_Z9TRUEFACTSEEEi + // CHECK: store void (i32)* @_Z3quxUa9enable_ifIXLi1EEXLi1EEEi p = &qux; } diff --git a/clang/test/OpenMP/threadprivate_codegen.cpp b/clang/test/OpenMP/threadprivate_codegen.cpp index a46bb69..2ef6522 100644 --- a/clang/test/OpenMP/threadprivate_codegen.cpp +++ b/clang/test/OpenMP/threadprivate_codegen.cpp @@ -598,8 +598,8 @@ int main() { // CHECK-DEBUG-NEXT: [[RES:%.*]] = load [[INT]], [[INT]]* [[RES_ADDR]] // CHECK-DEBUG-NEXT: [[ADD:%.*]] = add {{.*}} [[INT]] [[RES]], [[ST_INT_ST_VAL]] // CHECK-DEBUG-NEXT: store [[INT]] [[ADD]], [[INT]]* [[RES:.+]] - // CHECK-TLS: [[ST_INT_ST_ADDR:%.*]] = call i32* [[ST_INT_ST_TLS_INITD:[^,]+]] - // CHECK-TLS-NEXT: [[ST_INT_ST_VAL:%.*]] = load i32, i32* [[ST_INT_ST_ADDR]] + // + // CHECK-TLS: [[ST_INT_ST_VAL:%.*]] = load i32, i32* [[ST_INT_ST_ADDR:[^,]+]] // CHECK-TLS-NEXT: [[RES:%.*]] = load i32, i32* [[RES_ADDR]] // CHECK-TLS-NEXT: [[ADD:%.*]] = add {{.*}} i32 [[RES]], [[ST_INT_ST_VAL]] // CHECK-TLS-NEXT: store i32 [[ADD]], i32* [[RES_ADDR]] @@ -620,8 +620,8 @@ int main() { // CHECK-DEBUG-NEXT: [[RES:%.*]] = load [[INT]], [[INT]]* [[RES_ADDR]] // CHECK-DEBUG-NEXT: [[ADD:%.*]] = add {{.*}} [[INT]] [[RES]], [[FLOAT_TO_INT_CONV]] // CHECK-DEBUG-NEXT: store [[INT]] [[ADD]], [[INT]]* [[RES:.+]] - // CHECK-TLS: [[ST_FLOAT_ST_ADDR:%.*]] = call float* [[ST_FLOAT_ST_TLS_INITD:[^,]+]] - // CHECK-TLS-NEXT: [[ST_FLOAT_ST_VAL:%.*]] = load float, float* [[ST_FLOAT_ST_ADDR]] + // + // CHECK-TLS: [[ST_FLOAT_ST_VAL:%.*]] = load float, float* [[ST_FLOAT_ST_ADDR:[^,]+]] // CHECK-TLS-NEXT: [[FLOAT_TO_INT_CONV:%.*]] = fptosi float [[ST_FLOAT_ST_VAL]] to i32 // CHECK-TLS-NEXT: [[RES:%.*]] = load i32, i32* [[RES_ADDR]] // CHECK-TLS-NEXT: [[ADD:%.*]] = add {{.*}} i32 [[RES]], [[FLOAT_TO_INT_CONV]] @@ -727,14 +727,14 @@ int main() { // CHECK-TLS: call void [[ARR_X_TLS_INIT]] // CHECK-TLS: ret [2 x [3 x [[S1]]]]* [[ARR_X]] // CHECK-TLS: } -// CHECK-TLS: define {{.*}} i32* [[ST_INT_ST_TLS_INITD]] {{#[0-9]+}} comdat { -// CHECK-TLS-NOT: call -// CHECK-TLS: ret i32* [[ST_INT_ST]] -// CHECK-TLS: } -// CHECK-TLS: define {{.*}} float* [[ST_FLOAT_ST_TLS_INITD]] {{#[0-9]+}} comdat { -// CHECK-TLS-NOT: call -// CHECK-TLS: ret float* [[ST_FLOAT_ST]] -// CHECK-TLS: } +// +// +// +// +// +// +// +// // CHECK-TLS: define {{.*}} [[S4]]* [[ST_S4_ST_TLS_INITD]] {{#[0-9]+}} comdat { // CHECK-TLS: call void [[ST_S4_ST_TLS_INIT]] // CHECK-TLS: ret [[S4]]* [[ST_S4_ST]] @@ -874,8 +874,8 @@ int foobar() { // CHECK-DEBUG-NEXT: [[RES:%.*]] = load [[INT]], [[INT]]* [[RES_ADDR]] // CHECK-DEBUG-NEXT: [[ADD:%.*]] = add {{.*}} [[INT]] [[RES]], [[ST_INT_ST_VAL]] // CHECK-DEBUG-NEXT: store [[INT]] [[ADD]], [[INT]]* [[RES:.+]] - // OMP45-TLS: [[ST_INT_ST_ADDR:%.*]] = call i32* [[ST_INT_ST_TLS_INITD]] - // OMP45-TLS-NEXT: [[ST_INT_ST_VAL:%.*]] = load [[INT]], [[INT]]* [[ST_INT_ST_ADDR]] + // + // OMP45-TLS: [[ST_INT_ST_VAL:%.*]] = load [[INT]], [[INT]]* [[ST_INT_ST_ADDR:[^,]+]] // OMP45-TLS-NEXT: [[RES:%.*]] = load [[INT]], [[INT]]* [[RES_ADDR]] // OMP45-TLS-NEXT: [[ADD:%.*]] = add {{.*}} [[INT]] [[RES]], [[ST_INT_ST_VAL]] // OMP45-TLS-NEXT: store [[INT]] [[ADD]], [[INT]]* [[RES:.+]] @@ -896,8 +896,8 @@ int foobar() { // CHECK-DEBUG-NEXT: [[RES:%.*]] = load [[INT]], [[INT]]* [[RES_ADDR]] // CHECK-DEBUG-NEXT: [[ADD:%.*]] = add {{.*}} [[INT]] [[RES]], [[FLOAT_TO_INT_CONV]] // CHECK-DEBUG-NEXT: store [[INT]] [[ADD]], [[INT]]* [[RES:.+]] - // OMP45-TLS: [[ST_FLOAT_ST_ADDR:%.*]] = call float* [[ST_FLOAT_ST_TLS_INITD]] - // OMP45-TLS-NEXT: [[ST_FLOAT_ST_VAL:%.*]] = load float, float* [[ST_FLOAT_ST_ADDR]] + // + // OMP45-TLS: [[ST_FLOAT_ST_VAL:%.*]] = load float, float* [[ST_FLOAT_ST_ADDR:[^,]+]] // OMP45-TLS-NEXT: [[FLOAT_TO_INT_CONV:%.*]] = fptosi float [[ST_FLOAT_ST_VAL]] to [[INT]] // OMP45-TLS-NEXT: [[RES:%.*]] = load [[INT]], [[INT]]* [[RES_ADDR]] // OMP45-TLS-NEXT: [[ADD:%.*]] = add {{.*}} [[INT]] [[RES]], [[FLOAT_TO_INT_CONV]] diff --git a/clang/test/Sema/enable_if.c b/clang/test/Sema/enable_if.c index b4bb2ec..d96a53d 100644 --- a/clang/test/Sema/enable_if.c +++ b/clang/test/Sema/enable_if.c @@ -5,7 +5,7 @@ typedef int mode_t; typedef unsigned long size_t; -const int TRUE = 1; +enum { TRUE = 1 }; int open(const char *pathname, int flags) __attribute__((enable_if(!(flags & O_CREAT), "must specify mode when using O_CREAT"))) __attribute__((overloadable)); // expected-note{{candidate disabled: must specify mode when using O_CREAT}} int open(const char *pathname, int flags, mode_t mode) __attribute__((overloadable)); // expected-note{{candidate function not viable: requires 3 arguments, but 2 were provided}} @@ -114,7 +114,7 @@ void f(int n) __attribute__((enable_if(unresolvedid, "chosen when 'unresolvedid' int global; void f(int n) __attribute__((enable_if(global == 0, "chosen when 'global' is zero"))); // expected-error{{'enable_if' attribute expression never produces a constant expression}} // expected-note{{subexpression not valid in a constant expression}} -const int cst = 7; +enum { cst = 7 }; void return_cst(void) __attribute__((overloadable)) __attribute__((enable_if(cst == 7, "chosen when 'cst' is 7"))); void test_return_cst() { return_cst(); } diff --git a/clang/test/SemaCXX/constant-expression.cpp b/clang/test/SemaCXX/constant-expression.cpp index 2bec62f..a5e571a 100644 --- a/clang/test/SemaCXX/constant-expression.cpp +++ b/clang/test/SemaCXX/constant-expression.cpp @@ -98,9 +98,9 @@ void diags(int n) { namespace IntOrEnum { const int k = 0; - const int &p = k; + const int &p = k; // expected-note {{declared here}} template struct S {}; - S

s; // expected-error {{not an integral constant expression}} + S

s; // expected-error {{not an integral constant expression}} expected-note {{read of variable 'p' of non-integral, non-enumeration type 'const int &'}} } extern const int recurse1; diff --git a/clang/test/SemaCXX/i-c-e-cxx.cpp b/clang/test/SemaCXX/i-c-e-cxx.cpp index a09ff5a..da9be12 100644 --- a/clang/test/SemaCXX/i-c-e-cxx.cpp +++ b/clang/test/SemaCXX/i-c-e-cxx.cpp @@ -19,9 +19,6 @@ void f() { int a() { const int t=t; // expected-note {{declared here}} -#if __cplusplus <= 199711L - // expected-note@-2 {{read of object outside its lifetime}} -#endif switch(1) { // do not warn that 1 is not a case value; // 't' might have been expected to evalaute to 1 -- 2.7.4