[clang] 76c0092 - Ensure that checkInitIsICE is called exactly once for every variable
Richard Smith via cfe-commits
cfe-commits at lists.llvm.org
Mon Oct 26 15:59:00 PDT 2020
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 <thakis at 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 <thakis at 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 at 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 at -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 at lists.llvm.org
>>> https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
>>>
>>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.llvm.org/pipermail/cfe-commits/attachments/20201026/fbfc46fd/attachment-0001.html>
More information about the cfe-commits
mailing list