Reduces to: int d; int &h = d; double e = h;
... but only fails under -fms-compatibility. On Mon, 26 Oct 2020 at 13:46, Nico Weber <tha...@chromium.org> wrote: > The original snippet: > > const int64_t& kGraceMs = > AffiliationFetchThrottler::kGracePeriodAfterReconnectMs; > ASSERT_NO_FATAL_FAILURE(AssertReleaseInBetween( > true, kGraceMs * (1 - kPolicy.jitter_factor), kGraceMs)); > > kGracePeriod is declared here > https://source.chromium.org/chromium/chromium/src/+/master:components/password_manager/core/browser/android_affiliation/affiliation_fetch_throttler.h;l=109?q=kGracePeriodAfterReconnectMs&ss=chromium > as a class-level > > static const int64_t kGracePeriodAfterReconnectMs; > > which is then defined in some .cc file where the snippet above can't see > the value. > > On Mon, Oct 26, 2020 at 4:40 PM Nico Weber <tha...@chromium.org> wrote: > >> Hi Richard, >> >> this makes clang assert when building chromium/win. >> https://bugs.chromium.org/p/chromium/issues/detail?id=1142009#c4 has a >> reduced repro. Could you take a look? >> >> Thanks, >> Nico >> >> On Mon, Oct 19, 2020 at 10:04 PM Richard Smith via cfe-commits < >> cfe-commits@lists.llvm.org> wrote: >> >>> >>> Author: Richard Smith >>> Date: 2020-10-19T19:04:04-07:00 >>> New Revision: 76c0092665867a6defcd328ba0d0d976eb65d991 >>> >>> URL: >>> https://github.com/llvm/llvm-project/commit/76c0092665867a6defcd328ba0d0d976eb65d991 >>> DIFF: >>> https://github.com/llvm/llvm-project/commit/76c0092665867a6defcd328ba0d0d976eb65d991.diff >>> >>> LOG: 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). >>> >>> Added: >>> >>> >>> Modified: >>> clang/include/clang/AST/Decl.h >>> clang/include/clang/Serialization/ASTRecordWriter.h >>> clang/lib/AST/ComparisonCategories.cpp >>> clang/lib/AST/Decl.cpp >>> clang/lib/AST/ExprConstant.cpp >>> clang/lib/CodeGen/ItaniumCXXABI.cpp >>> clang/lib/Sema/SemaDecl.cpp >>> clang/lib/Sema/SemaDeclCXX.cpp >>> clang/lib/Serialization/ASTReaderDecl.cpp >>> clang/lib/Serialization/ASTWriter.cpp >>> clang/lib/Serialization/ASTWriterDecl.cpp >>> clang/test/CodeGen/enable_if.c >>> clang/test/OpenMP/threadprivate_codegen.cpp >>> clang/test/Sema/enable_if.c >>> clang/test/SemaCXX/constant-expression.cpp >>> clang/test/SemaCXX/i-c-e-cxx.cpp >>> >>> Removed: >>> >>> >>> >>> >>> ################################################################################ >>> diff --git a/clang/include/clang/AST/Decl.h >>> b/clang/include/clang/AST/Decl.h >>> index eae09832160d..e309819400f1 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 @@ class VarDecl : public DeclaratorDecl, public >>> Redeclarable<VarDecl> { >>> /// 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 @@ class VarDecl : public DeclaratorDecl, public >>> Redeclarable<VarDecl> { >>> >>> /// Determine whether the value of the initializer attached to this >>> /// declaration is an integral constant expression. >>> - bool checkInitIsICE() const; >>> + bool checkInitIsICE(SmallVectorImpl<PartialDiagnosticAt> &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 edfcd9c52e2e..e362463b2309 100644 >>> --- a/clang/include/clang/Serialization/ASTRecordWriter.h >>> +++ b/clang/include/clang/Serialization/ASTRecordWriter.h >>> @@ -266,6 +266,9 @@ class ASTRecordWriter >>> >>> 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 6b6826c02a12..896050482644 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 a6c7f30528eb..ee7f51c5218e 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<EvaluatedStmt *>(); >>> +} >>> + >>> APValue *VarDecl::evaluateValue() const { >>> SmallVector<PartialDiagnosticAt, 8> Notes; >>> return evaluateValue(Notes); >>> @@ -2354,19 +2358,17 @@ APValue *VarDecl::evaluateValue( >>> SmallVectorImpl<PartialDiagnosticAt> &Notes) const { >>> EvaluatedStmt *Eval = ensureEvaluatedStmt(); >>> >>> + const auto *Init = cast<Expr>(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<Expr>(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<EvaluatedStmt *>()) >>> + 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<EvaluatedStmt *>()) >>> + if (EvaluatedStmt *Eval = getEvaluatedStmt()) >>> return Eval->CheckedICE; >>> >>> return false; >>> @@ -2417,12 +2412,16 @@ bool VarDecl::isInitICE() const { >>> return Init.get<EvaluatedStmt *>()->IsICE; >>> } >>> >>> -bool VarDecl::checkInitIsICE() const { >>> +bool VarDecl::checkInitIsICE( >>> + SmallVectorImpl<PartialDiagnosticAt> &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<Expr>(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<PartialDiagnosticAt, 8> 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<EvaluatedStmt *>()) >>> + 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 7afc44dffffe..3014f948f9b1 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<EnumConstantDecl>(cast<DeclRefExpr>(E)->getDecl())) >>> return NoDiag(); >>> - const ValueDecl *D = cast<DeclRefExpr>(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<ParmVarDecl>(D)) >>> - return ICEDiag(IK_NotICE, cast<DeclRefExpr>(E)->getLocation()); >>> - >>> + const VarDecl *VD = >>> dyn_cast<VarDecl>(cast<DeclRefExpr>(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<VarDecl>(D)) { >>> - if (!Dcl->getType()->isIntegralOrEnumerationType()) >>> - return ICEDiag(IK_NotICE, >>> cast<DeclRefExpr>(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<DeclRefExpr>(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 cfb736ce0ff1..40cd5c54185f 100644 >>> --- a/clang/lib/CodeGen/ItaniumCXXABI.cpp >>> +++ b/clang/lib/CodeGen/ItaniumCXXABI.cpp >>> @@ -361,8 +361,9 @@ class ItaniumCXXABI : public CodeGen::CGCXXABI { >>> 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 481b48e21942..1a27667fc106 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<DecompositionDecl>(var)) >>> - CheckCompleteDecompositionDeclaration(DD); >>> - >>> QualType type = var->getType(); >>> - if (type->isDependentType()) return; >>> >>> if (var->hasAttr<BlocksAttr>()) >>> 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<PartialDiagnosticAt, 8> 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<ConstInitAttr>()) { >>> - // FIXME: Need strict checking in C++03 here. >>> - bool DiagErr = getLangOpts().CPlusPlus11 >>> - ? !var->checkInitIsICE() : !checkConstInit(); >>> - if (DiagErr) { >>> - auto *Attr = var->getAttr<ConstInitAttr>(); >>> - 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<PartialDiagnosticAt, 8> 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<PartialDiagnosticAt, 8> 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<ConstInitAttr>()) { >>> + auto *Attr = var->getAttr<ConstInitAttr>(); >>> + 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<RecordType>()) >>> - FinalizeVarWithDestructor(var, recordType); >>> + if (!type->isDependentType()) >>> + if (const RecordType *recordType = baseType->getAs<RecordType>()) >>> + 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<DecompositionDecl>(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 cbcaf3cc4360..72dfa37c321e 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 f5a66dc3c2d1..41f2db1ef5f0 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 e793e619381b..6056ed623c69 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<VarDecl>(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<Expr*>(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 911fcb409547..8778f0c02671 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<BlocksAttr>() && 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 5e9f904fdd3f..1d830ae68f54 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 a46bb6907015..2ef6522760ab 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 b4bb2ecd0d20..d96a53d94e1e 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 2bec62f46b66..a5e571a97eb2 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<int n> struct S {}; >>> - S<p> s; // expected-error {{not an integral constant expression}} >>> + S<p> 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 a09ff5ac8d9f..da9be1229a54 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 >>> >>> >>> >>> _______________________________________________ >>> 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