Fixed in a5c7b46862ec0531964eb52329cdf009862abecf. On Mon, 26 Oct 2020 at 15:59, Richard Smith <rich...@metafoo.co.uk> wrote:
> 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