summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRichard Smith <richard-llvm@metafoo.co.uk>2019-09-04 20:30:37 +0000
committerRichard Smith <richard-llvm@metafoo.co.uk>2019-09-04 20:30:37 +0000
commit940249d8175d8db209f3e6e4557aaa6f1d99c00f (patch)
tree756c9f3cd9ff717961b6dc1a504894be82e6373d
parent3c6ee3dacaccb1f818db5d8b9a6169d2d116e7aa (diff)
downloadclang-940249d8175d8db209f3e6e4557aaa6f1d99c00f.tar.gz
[c++20] P1143R2: Add support for the C++20 'constinit' keyword.
This is mostly the same as the [[clang::require_constant_initialization]] attribute, but has a couple of additional syntactic and semantic restrictions. In passing, I added a warning for the attribute form being added after we have already seen the initialization of the variable (but before we see the definition); that case previously slipped between the cracks and the attribute was silently ignored. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@370972 91177308-0d34-0410-b5e6-96231b3b80d8
-rw-r--r--include/clang/AST/Decl.h8
-rw-r--r--include/clang/Basic/Attr.td20
-rw-r--r--include/clang/Basic/DiagnosticCommonKinds.td12
-rw-r--r--include/clang/Basic/DiagnosticParseKinds.td3
-rw-r--r--include/clang/Basic/DiagnosticSemaKinds.td34
-rw-r--r--include/clang/Basic/Specifiers.h3
-rw-r--r--include/clang/Basic/TokenKinds.def1
-rw-r--r--lib/AST/Decl.cpp16
-rw-r--r--lib/Frontend/InitPreprocessor.cpp4
-rw-r--r--lib/Parse/ParseDecl.cpp12
-rw-r--r--lib/Parse/ParseDeclCXX.cpp2
-rw-r--r--lib/Parse/ParseTentative.cpp1
-rw-r--r--lib/Sema/DeclSpec.cpp15
-rw-r--r--lib/Sema/SemaDecl.cpp172
-rw-r--r--lib/Sema/SemaDeclAttr.cpp4
-rw-r--r--lib/Sema/SemaType.cpp4
-rw-r--r--test/CXX/dcl.dcl/dcl.spec/dcl.constinit/p1.cpp55
-rw-r--r--test/CXX/dcl.dcl/dcl.spec/dcl.constinit/p2.cpp8
-rw-r--r--test/CXX/dcl.dcl/dcl.spec/dcl.constinit/p3.cpp6
-rw-r--r--test/FixIt/fixit-c++2a.cpp38
-rw-r--r--test/Lexer/cxx-features.cpp4
-rw-r--r--test/Lexer/cxx2a_keyword_as_cxx17.cpp2
-rw-r--r--test/Misc/pragma-attribute-cxx.cpp4
-rw-r--r--test/Misc/pragma-attribute-supported-attributes-list.test2
-rw-r--r--test/Parser/cxx0x-decl.cpp19
-rw-r--r--test/SemaCXX/attr-require-constant-initialization.cpp11
-rwxr-xr-xwww/cxx_status.html2
27 files changed, 385 insertions, 77 deletions
diff --git a/include/clang/AST/Decl.h b/include/clang/AST/Decl.h
index e593dafb5f..0d9da7d0dc 100644
--- a/include/clang/AST/Decl.h
+++ b/include/clang/AST/Decl.h
@@ -1226,6 +1226,14 @@ public:
void setInit(Expr *I);
+ /// Get the initializing declaration of this variable, if any. This is
+ /// usually the definition, except that for a static data member it can be
+ /// the in-class declaration.
+ VarDecl *getInitializingDeclaration();
+ const VarDecl *getInitializingDeclaration() const {
+ return const_cast<VarDecl *>(this)->getInitializingDeclaration();
+ }
+
/// Determine whether this variable's value might be usable in a
/// constant expression, according to the relevant language standard.
/// This only checks properties of the declaration, and does not check
diff --git a/include/clang/Basic/Attr.td b/include/clang/Basic/Attr.td
index ac223bd06a..fbf26ec9b9 100644
--- a/include/clang/Basic/Attr.td
+++ b/include/clang/Basic/Attr.td
@@ -911,6 +911,17 @@ def Const : InheritableAttr {
let Documentation = [Undocumented];
}
+def ConstInit : InheritableAttr {
+ // This attribute does not have a C [[]] spelling because it requires the
+ // CPlusPlus language option.
+ let Spellings = [Keyword<"constinit">,
+ Clang<"require_constant_initialization", 0>];
+ let Subjects = SubjectList<[GlobalVar], ErrorDiag>;
+ let Accessors = [Accessor<"isConstinit", [Keyword<"constinit">]>];
+ let Documentation = [RequireConstantInitDocs];
+ let LangOpts = [CPlusPlus];
+}
+
def Constructor : InheritableAttr {
let Spellings = [GCC<"constructor">];
let Args = [DefaultIntArgument<"Priority", 65535>];
@@ -1935,15 +1946,6 @@ def ReqdWorkGroupSize : InheritableAttr {
let Documentation = [Undocumented];
}
-def RequireConstantInit : InheritableAttr {
- // This attribute does not have a C [[]] spelling because it requires the
- // CPlusPlus language option.
- let Spellings = [Clang<"require_constant_initialization", 0>];
- let Subjects = SubjectList<[GlobalVar], ErrorDiag>;
- let Documentation = [RequireConstantInitDocs];
- let LangOpts = [CPlusPlus];
-}
-
def WorkGroupSizeHint : InheritableAttr {
// Does not have a [[]] spelling because it is an OpenCL-related attribute.
let Spellings = [GNU<"work_group_size_hint">];
diff --git a/include/clang/Basic/DiagnosticCommonKinds.td b/include/clang/Basic/DiagnosticCommonKinds.td
index f12ee1ba92..8196a4601c 100644
--- a/include/clang/Basic/DiagnosticCommonKinds.td
+++ b/include/clang/Basic/DiagnosticCommonKinds.td
@@ -12,6 +12,11 @@
let Component = "Common" in {
+// Substitutions.
+
+def select_constexpr_spec_kind : TextSubstitution<
+ "%select{<ERROR>|constexpr|consteval|constinit}0">;
+
// Basic.
def fatal_too_many_errors
@@ -112,6 +117,10 @@ def err_attribute_not_type_attr : Error<
"%0 attribute cannot be applied to types">;
def err_enum_template : Error<"enumeration cannot be a template">;
+def warn_cxx20_compat_consteval : Warning<
+ "'consteval' specifier is incompatible with C++ standards before C++20">,
+ InGroup<CXX2aCompat>, DefaultIgnore;
+
}
let CategoryName = "Nullability Issue" in {
@@ -177,9 +186,6 @@ def ext_cxx11_longlong : Extension<
def warn_cxx98_compat_longlong : Warning<
"'long long' is incompatible with C++98">,
InGroup<CXX98CompatPedantic>, DefaultIgnore;
-def warn_cxx20_compat_consteval : Warning<
- "consteval is incompatible with C++ standards before C++20">,
- InGroup<CXX2aCompat>, DefaultIgnore;
def err_integer_literal_too_large : Error<
"integer literal is too large to be represented in any %select{signed |}0"
"integer type">;
diff --git a/include/clang/Basic/DiagnosticParseKinds.td b/include/clang/Basic/DiagnosticParseKinds.td
index c0b0f444df..a8bc1421ce 100644
--- a/include/clang/Basic/DiagnosticParseKinds.td
+++ b/include/clang/Basic/DiagnosticParseKinds.td
@@ -358,7 +358,8 @@ def err_typename_invalid_storageclass : Error<
def err_typename_invalid_functionspec : Error<
"type name does not allow function specifier to be specified">;
def err_typename_invalid_constexpr : Error<
- "type name does not allow %select{constexpr|consteval}0 specifier to be specified">;
+ "type name does not allow %sub{select_constexpr_spec_kind}0 specifier "
+ "to be specified">;
def err_typename_identifiers_only : Error<
"typename is allowed for identifiers only">;
diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td
index 80f9439c20..278bcae3dc 100644
--- a/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/include/clang/Basic/DiagnosticSemaKinds.td
@@ -2343,18 +2343,18 @@ def warn_cxx14_compat_constexpr_not_const : Warning<
"in C++14; add 'const' to avoid a change in behavior">,
InGroup<DiagGroup<"constexpr-not-const">>;
def err_invalid_constexpr : Error<
- "%select{function parameter|typedef|non-static data member}0 "
- "cannot be %select{constexpr|consteval}1">;
+ "%select{function parameter|typedef}0 "
+ "cannot be %sub{select_constexpr_spec_kind}1">;
def err_invalid_constexpr_member : Error<"non-static data member cannot be "
"constexpr%select{; did you intend to make it %select{const|static}0?|}1">;
def err_constexpr_tag : Error<
"%select{class|struct|interface|union|enum}0 "
- "cannot be marked %select{constexpr|consteval}1">;
+ "cannot be marked %sub{select_constexpr_spec_kind}1">;
def err_constexpr_dtor : Error<
- "destructor cannot be marked %select{constexpr|consteval}0">;
+ "destructor cannot be marked %sub{select_constexpr_spec_kind}0">;
def err_constexpr_wrong_decl_kind : Error<
- "%select{constexpr|consteval}0 can only be used "
- "in %select{variable and |}0function declarations">;
+ "%sub{select_constexpr_spec_kind}0 can only be used "
+ "in %select{|variable and function|function|variable}0 declarations">;
def err_invalid_constexpr_var_decl : Error<
"constexpr variable declaration must be a definition">;
def err_constexpr_static_mem_var_requires_init : Error<
@@ -7514,10 +7514,30 @@ def note_inequality_comparison_to_or_assign : Note<
def err_incomplete_type_used_in_type_trait_expr : Error<
"incomplete type %0 used in type trait expression">;
+// C++20 constinit and require_constant_initialization attribute
+def warn_cxx20_compat_constinit : Warning<
+ "'constinit' specifier is incompatible with C++ standards before C++20">,
+ InGroup<CXX2aCompat>, DefaultIgnore;
+def err_constinit_local_variable : Error<
+ "local variable cannot be declared 'constinit'">;
def err_require_constant_init_failed : Error<
"variable does not have a constant initializer">;
def note_declared_required_constant_init_here : Note<
- "required by 'require_constant_initialization' attribute here">;
+ "required by %select{'require_constant_initialization' attribute|"
+ "'constinit' specifier}0 here">;
+def ext_constinit_missing : ExtWarn<
+ "'constinit' specifier missing on initializing declaration of %0">,
+ InGroup<DiagGroup<"missing-constinit">>;
+def note_constinit_specified_here : Note<"variable declared constinit here">;
+def err_constinit_added_too_late : Error<
+ "'constinit' specifier added after initialization of variable">;
+def warn_require_const_init_added_too_late : Warning<
+ "'require_constant_initialization' attribute added after initialization "
+ "of variable">, InGroup<IgnoredAttributes>;
+def note_constinit_missing_here : Note<
+ "add the "
+ "%select{'require_constant_initialization' attribute|'constinit' specifier}0 "
+ "to the initializing declaration here">;
def err_dimension_expr_not_constant_integer : Error<
"dimension expression does not evaluate to a constant unsigned int">;
diff --git a/include/clang/Basic/Specifiers.h b/include/clang/Basic/Specifiers.h
index d1236e798e..fad97a26d9 100644
--- a/include/clang/Basic/Specifiers.h
+++ b/include/clang/Basic/Specifiers.h
@@ -32,7 +32,8 @@ namespace clang {
enum ConstexprSpecKind {
CSK_unspecified,
CSK_constexpr,
- CSK_consteval
+ CSK_consteval,
+ CSK_constinit
};
/// Specifies the width of a type, e.g., short, long, or long long.
diff --git a/include/clang/Basic/TokenKinds.def b/include/clang/Basic/TokenKinds.def
index 5221fcec92..0a8d27ec2e 100644
--- a/include/clang/Basic/TokenKinds.def
+++ b/include/clang/Basic/TokenKinds.def
@@ -389,6 +389,7 @@ MODULES_KEYWORD(import)
// C++20 keywords.
CXX2A_KEYWORD(char8_t , CHAR8SUPPORT)
CXX2A_KEYWORD(consteval , 0)
+CXX2A_KEYWORD(constinit , 0)
// C11 Extension
KEYWORD(_Float16 , KEYALL)
diff --git a/lib/AST/Decl.cpp b/lib/AST/Decl.cpp
index 21dd542583..6c0c828146 100644
--- a/lib/AST/Decl.cpp
+++ b/lib/AST/Decl.cpp
@@ -2220,6 +2220,22 @@ Stmt **VarDecl::getInitAddress() {
return Init.getAddrOfPtr1();
}
+VarDecl *VarDecl::getInitializingDeclaration() {
+ VarDecl *Def = nullptr;
+ for (auto I : redecls()) {
+ if (I->hasInit())
+ return I;
+
+ if (I->isThisDeclarationADefinition()) {
+ if (isStaticDataMember())
+ return I;
+ else
+ Def = I;
+ }
+ }
+ return Def;
+}
+
bool VarDecl::isOutOfLine() const {
if (Decl::isOutOfLine())
return true;
diff --git a/lib/Frontend/InitPreprocessor.cpp b/lib/Frontend/InitPreprocessor.cpp
index 6eafe81e82..95d9f62c60 100644
--- a/lib/Frontend/InitPreprocessor.cpp
+++ b/lib/Frontend/InitPreprocessor.cpp
@@ -541,8 +541,10 @@ static void InitializeCPlusPlusFeatureTestMacros(const LangOptions &LangOpts,
Builder.defineMacro("__cpp_template_template_args", "201611L");
// C++20 features.
- if (LangOpts.CPlusPlus2a)
+ if (LangOpts.CPlusPlus2a) {
Builder.defineMacro("__cpp_conditional_explicit", "201806L");
+ Builder.defineMacro("__cpp_constinit", "201907L");
+ }
if (LangOpts.Char8)
Builder.defineMacro("__cpp_char8_t", "201811L");
Builder.defineMacro("__cpp_impl_destroying_delete", "201806L");
diff --git a/lib/Parse/ParseDecl.cpp b/lib/Parse/ParseDecl.cpp
index c4c5045d2f..d66c539056 100644
--- a/lib/Parse/ParseDecl.cpp
+++ b/lib/Parse/ParseDecl.cpp
@@ -2514,7 +2514,7 @@ void Parser::ParseSpecifierQualifierList(DeclSpec &DS, AccessSpecifier AS,
// Issue diagnostic and remove constexpr specifier if present.
if (DS.hasConstexprSpecifier() && DSC != DeclSpecContext::DSC_condition) {
Diag(DS.getConstexprSpecLoc(), diag::err_typename_invalid_constexpr)
- << (DS.getConstexprSpecifier() == CSK_consteval);
+ << DS.getConstexprSpecifier();
DS.ClearConstexprSpec();
}
}
@@ -3653,15 +3653,16 @@ void Parser::ParseDeclarationSpecifiers(DeclSpec &DS,
isInvalid = DS.setModulePrivateSpec(Loc, PrevSpec, DiagID);
break;
- // constexpr
+ // constexpr, consteval, constinit specifiers
case tok::kw_constexpr:
isInvalid = DS.SetConstexprSpec(CSK_constexpr, Loc, PrevSpec, DiagID);
break;
-
- // consteval
case tok::kw_consteval:
isInvalid = DS.SetConstexprSpec(CSK_consteval, Loc, PrevSpec, DiagID);
break;
+ case tok::kw_constinit:
+ isInvalid = DS.SetConstexprSpec(CSK_constinit, Loc, PrevSpec, DiagID);
+ break;
// type-specifier
case tok::kw_short:
@@ -5080,8 +5081,9 @@ bool Parser::isDeclarationSpecifier(bool DisambiguatingWithExpression) {
case tok::annot_decltype:
case tok::kw_constexpr:
- // C++20 consteval.
+ // C++20 consteval and constinit.
case tok::kw_consteval:
+ case tok::kw_constinit:
// C11 _Atomic
case tok::kw__Atomic:
diff --git a/lib/Parse/ParseDeclCXX.cpp b/lib/Parse/ParseDeclCXX.cpp
index cf03ed6379..529c09a50e 100644
--- a/lib/Parse/ParseDeclCXX.cpp
+++ b/lib/Parse/ParseDeclCXX.cpp
@@ -1313,6 +1313,8 @@ bool Parser::isValidAfterTypeSpecifier(bool CouldBeBitfield) {
case tok::kw_mutable: // struct foo {...} mutable x;
case tok::kw_thread_local: // struct foo {...} thread_local x;
case tok::kw_constexpr: // struct foo {...} constexpr x;
+ case tok::kw_consteval: // struct foo {...} consteval x;
+ case tok::kw_constinit: // struct foo {...} constinit x;
// As shown above, type qualifiers and storage class specifiers absolutely
// can occur after class specifiers according to the grammar. However,
// almost no one actually writes code like this. If we see one of these,
diff --git a/lib/Parse/ParseTentative.cpp b/lib/Parse/ParseTentative.cpp
index 8e56e0c696..2eee15b7e0 100644
--- a/lib/Parse/ParseTentative.cpp
+++ b/lib/Parse/ParseTentative.cpp
@@ -1408,6 +1408,7 @@ Parser::isCXXDeclarationSpecifier(Parser::TPResult BracedCastResult,
case tok::kw_typedef:
case tok::kw_constexpr:
case tok::kw_consteval:
+ case tok::kw_constinit:
// storage-class-specifier
case tok::kw_register:
case tok::kw_static:
diff --git a/lib/Sema/DeclSpec.cpp b/lib/Sema/DeclSpec.cpp
index 77e5eb0956..639231c872 100644
--- a/lib/Sema/DeclSpec.cpp
+++ b/lib/Sema/DeclSpec.cpp
@@ -569,6 +569,7 @@ const char *DeclSpec::getSpecifierName(ConstexprSpecKind C) {
case CSK_unspecified: return "unspecified";
case CSK_constexpr: return "constexpr";
case CSK_consteval: return "consteval";
+ case CSK_constinit: return "constinit";
}
llvm_unreachable("Unknown ConstexprSpecKind");
}
@@ -1036,13 +1037,9 @@ bool DeclSpec::setModulePrivateSpec(SourceLocation Loc, const char *&PrevSpec,
bool DeclSpec::SetConstexprSpec(ConstexprSpecKind ConstexprKind,
SourceLocation Loc, const char *&PrevSpec,
unsigned &DiagID) {
- if (getConstexprSpecifier() != CSK_unspecified) {
- if (getConstexprSpecifier() == CSK_consteval || ConstexprKind == CSK_consteval)
- return BadSpecifier(ConstexprKind, getConstexprSpecifier(), PrevSpec, DiagID);
- DiagID = diag::warn_duplicate_declspec;
- PrevSpec = "constexpr";
- return true;
- }
+ if (getConstexprSpecifier() != CSK_unspecified)
+ return BadSpecifier(ConstexprKind, getConstexprSpecifier(), PrevSpec,
+ DiagID);
ConstexprSpecifier = ConstexprKind;
ConstexprLoc = Loc;
return false;
@@ -1291,8 +1288,10 @@ void DeclSpec::Finish(Sema &S, const PrintingPolicy &Policy) {
<< (TypeSpecType == TST_char16 ? "char16_t" : "char32_t");
if (getConstexprSpecifier() == CSK_constexpr)
S.Diag(ConstexprLoc, diag::warn_cxx98_compat_constexpr);
- if (getConstexprSpecifier() == CSK_consteval)
+ else if (getConstexprSpecifier() == CSK_consteval)
S.Diag(ConstexprLoc, diag::warn_cxx20_compat_consteval);
+ else if (getConstexprSpecifier() == CSK_constinit)
+ S.Diag(ConstexprLoc, diag::warn_cxx20_compat_constinit);
// C++ [class.friend]p6:
// No storage-class-specifier shall appear in the decl-specifier-seq
// of a friend declaration.
diff --git a/lib/Sema/SemaDecl.cpp b/lib/Sema/SemaDecl.cpp
index a96f0d28e8..a2708223c1 100644
--- a/lib/Sema/SemaDecl.cpp
+++ b/lib/Sema/SemaDecl.cpp
@@ -2661,6 +2661,60 @@ static void checkNewAttributesAfterDef(Sema &S, Decl *New, const Decl *Old) {
}
}
+static void diagnoseMissingConstinit(Sema &S, const VarDecl *InitDecl,
+ const ConstInitAttr *CIAttr,
+ bool AttrBeforeInit) {
+ SourceLocation InsertLoc = InitDecl->getInnerLocStart();
+
+ // Figure out a good way to write this specifier on the old declaration.
+ // FIXME: We should just use the spelling of CIAttr, but we don't preserve
+ // enough of the attribute list spelling information to extract that without
+ // heroics.
+ std::string SuitableSpelling;
+ if (S.getLangOpts().CPlusPlus2a)
+ SuitableSpelling =
+ S.PP.getLastMacroWithSpelling(InsertLoc, {tok::kw_constinit});
+ if (SuitableSpelling.empty() && S.getLangOpts().CPlusPlus11)
+ SuitableSpelling = S.PP.getLastMacroWithSpelling(
+ InsertLoc,
+ {tok::l_square, tok::l_square, S.PP.getIdentifierInfo("clang"),
+ tok::coloncolon,
+ S.PP.getIdentifierInfo("require_constant_initialization"),
+ tok::r_square, tok::r_square});
+ if (SuitableSpelling.empty())
+ SuitableSpelling = S.PP.getLastMacroWithSpelling(
+ InsertLoc,
+ {tok::kw___attribute, tok::l_paren, tok::r_paren,
+ S.PP.getIdentifierInfo("require_constant_initialization"),
+ tok::r_paren, tok::r_paren});
+ if (SuitableSpelling.empty() && S.getLangOpts().CPlusPlus2a)
+ SuitableSpelling = "constinit";
+ if (SuitableSpelling.empty() && S.getLangOpts().CPlusPlus11)
+ SuitableSpelling = "[[clang::require_constant_initialization]]";
+ if (SuitableSpelling.empty())
+ SuitableSpelling = "__attribute__((require_constant_initialization))";
+ SuitableSpelling += " ";
+
+ if (AttrBeforeInit) {
+ // extern constinit int a;
+ // int a = 0; // error (missing 'constinit'), accepted as extension
+ assert(CIAttr->isConstinit() && "should not diagnose this for attribute");
+ S.Diag(InitDecl->getLocation(), diag::ext_constinit_missing)
+ << InitDecl << FixItHint::CreateInsertion(InsertLoc, SuitableSpelling);
+ S.Diag(CIAttr->getLocation(), diag::note_constinit_specified_here);
+ } else {
+ // int a = 0;
+ // constinit extern int a; // error (missing 'constinit')
+ S.Diag(CIAttr->getLocation(),
+ CIAttr->isConstinit() ? diag::err_constinit_added_too_late
+ : diag::warn_require_const_init_added_too_late)
+ << FixItHint::CreateRemoval(SourceRange(CIAttr->getLocation()));
+ S.Diag(InitDecl->getLocation(), diag::note_constinit_missing_here)
+ << CIAttr->isConstinit()
+ << FixItHint::CreateInsertion(InsertLoc, SuitableSpelling);
+ }
+}
+
/// mergeDeclAttributes - Copy attributes from the Old decl to the New one.
void Sema::mergeDeclAttributes(NamedDecl *New, Decl *Old,
AvailabilityMergeKind AMK) {
@@ -2673,6 +2727,41 @@ void Sema::mergeDeclAttributes(NamedDecl *New, Decl *Old,
if (!Old->hasAttrs() && !New->hasAttrs())
return;
+ // [dcl.constinit]p1:
+ // If the [constinit] specifier is applied to any declaration of a
+ // variable, it shall be applied to the initializing declaration.
+ const auto *OldConstInit = Old->getAttr<ConstInitAttr>();
+ const auto *NewConstInit = New->getAttr<ConstInitAttr>();
+ if (bool(OldConstInit) != bool(NewConstInit)) {
+ const auto *OldVD = cast<VarDecl>(Old);
+ auto *NewVD = cast<VarDecl>(New);
+
+ // Find the initializing declaration. Note that we might not have linked
+ // the new declaration into the redeclaration chain yet.
+ const VarDecl *InitDecl = OldVD->getInitializingDeclaration();
+ if (!InitDecl &&
+ (NewVD->hasInit() || NewVD->isThisDeclarationADefinition()))
+ InitDecl = NewVD;
+
+ if (InitDecl == NewVD) {
+ // This is the initializing declaration. If it would inherit 'constinit',
+ // that's ill-formed. (Note that we do not apply this to the attribute
+ // form).
+ if (OldConstInit && OldConstInit->isConstinit())
+ diagnoseMissingConstinit(*this, NewVD, OldConstInit,
+ /*AttrBeforeInit=*/true);
+ } else if (NewConstInit) {
+ // This is the first time we've been told that this declaration should
+ // have a constant initializer. If we already saw the initializing
+ // declaration, this is too late.
+ if (InitDecl && InitDecl != NewVD) {
+ diagnoseMissingConstinit(*this, InitDecl, NewConstInit,
+ /*AttrBeforeInit=*/false);
+ NewVD->dropAttr<ConstInitAttr>();
+ }
+ }
+ }
+
// Attributes declared post-definition are currently ignored.
checkNewAttributesAfterDef(*this, New, Old);
@@ -4315,13 +4404,13 @@ Sema::ParsedFreeStandingDeclSpec(Scope *S, AccessSpecifier AS, DeclSpec &DS,
// and definitions of functions and variables.
// C++2a [dcl.constexpr]p1: The consteval specifier shall be applied only to
// the declaration of a function or function template
- bool IsConsteval = DS.getConstexprSpecifier() == CSK_consteval;
if (Tag)
Diag(DS.getConstexprSpecLoc(), diag::err_constexpr_tag)
- << GetDiagnosticTypeSpecifierID(DS.getTypeSpecType()) << IsConsteval;
+ << GetDiagnosticTypeSpecifierID(DS.getTypeSpecType())
+ << DS.getConstexprSpecifier();
else
Diag(DS.getConstexprSpecLoc(), diag::err_constexpr_wrong_decl_kind)
- << IsConsteval;
+ << DS.getConstexprSpecifier();
// Don't emit warnings after this error.
return TagD;
}
@@ -5776,7 +5865,7 @@ Sema::ActOnTypedefDeclarator(Scope* S, Declarator& D, DeclContext* DC,
<< getLangOpts().CPlusPlus17;
if (D.getDeclSpec().hasConstexprSpecifier())
Diag(D.getDeclSpec().getConstexprSpecLoc(), diag::err_invalid_constexpr)
- << 1 << (D.getDeclSpec().getConstexprSpecifier() == CSK_consteval);
+ << 1 << D.getDeclSpec().getConstexprSpecifier();
if (D.getName().Kind != UnqualifiedIdKind::IK_Identifier) {
if (D.getName().Kind == UnqualifiedIdKind::IK_DeductionGuideName)
@@ -6671,19 +6760,6 @@ NamedDecl *Sema::ActOnVariableDeclarator(
if (TemplateParamLists.size() > VDTemplateParamLists)
NewVD->setTemplateParameterListsInfo(
Context, TemplateParamLists.drop_back(VDTemplateParamLists));
-
- if (D.getDeclSpec().hasConstexprSpecifier()) {
- NewVD->setConstexpr(true);
- // C++1z [dcl.spec.constexpr]p1:
- // A static data member declared with the constexpr specifier is
- // implicitly an inline variable.
- if (NewVD->isStaticDataMember() && getLangOpts().CPlusPlus17)
- NewVD->setImplicitlyInline();
- if (D.getDeclSpec().getConstexprSpecifier() == CSK_consteval)
- Diag(D.getDeclSpec().getConstexprSpecLoc(),
- diag::err_constexpr_wrong_decl_kind)
- << /*consteval*/ 1;
- }
}
if (D.getDeclSpec().isInlineSpecified()) {
@@ -6749,6 +6825,36 @@ NamedDecl *Sema::ActOnVariableDeclarator(
NewVD->setTSCSpec(TSCS);
}
+ switch (D.getDeclSpec().getConstexprSpecifier()) {
+ case CSK_unspecified:
+ break;
+
+ case CSK_consteval:
+ Diag(D.getDeclSpec().getConstexprSpecLoc(),
+ diag::err_constexpr_wrong_decl_kind)
+ << D.getDeclSpec().getConstexprSpecifier();
+ LLVM_FALLTHROUGH;
+
+ case CSK_constexpr:
+ NewVD->setConstexpr(true);
+ // C++1z [dcl.spec.constexpr]p1:
+ // A static data member declared with the constexpr specifier is
+ // implicitly an inline variable.
+ if (NewVD->isStaticDataMember() && getLangOpts().CPlusPlus17)
+ NewVD->setImplicitlyInline();
+ break;
+
+ case CSK_constinit:
+ if (!NewVD->hasGlobalStorage())
+ Diag(D.getDeclSpec().getConstexprSpecLoc(),
+ diag::err_constinit_local_variable);
+ else
+ NewVD->addAttr(::new (Context) ConstInitAttr(
+ SourceRange(D.getDeclSpec().getConstexprSpecLoc()), Context,
+ ConstInitAttr::Keyword_constinit));
+ break;
+ }
+
// C99 6.7.4p3
// An inline definition of a function with external linkage shall
// not contain a definition of a modifiable object with static or
@@ -7989,7 +8095,7 @@ static StorageClass getFunctionStorageClass(Sema &SemaRef, Declarator &D) {
return SC_None;
}
-static FunctionDecl* CreateNewFunctionDecl(Sema &SemaRef, Declarator &D,
+static FunctionDecl *CreateNewFunctionDecl(Sema &SemaRef, Declarator &D,
DeclContext *DC, QualType &R,
TypeSourceInfo *TInfo,
StorageClass SC,
@@ -8021,7 +8127,16 @@ static FunctionDecl* CreateNewFunctionDecl(Sema &SemaRef, Declarator &D,
}
ExplicitSpecifier ExplicitSpecifier = D.getDeclSpec().getExplicitSpecifier();
+
ConstexprSpecKind ConstexprKind = D.getDeclSpec().getConstexprSpecifier();
+ if (ConstexprKind == CSK_constinit) {
+ SemaRef.Diag(D.getDeclSpec().getConstexprSpecLoc(),
+ diag::err_constexpr_wrong_decl_kind)
+ << ConstexprKind;
+ ConstexprKind = CSK_unspecified;
+ D.getMutableDeclSpec().ClearConstexprSpec();
+ }
+
// Check that the return type is not an abstract class type.
// For record types, this is done by the AbstractClassUsageDiagnoser once
// the class has been completely parsed.
@@ -8452,7 +8567,6 @@ Sema::ActOnFunctionDeclarator(Scope *S, Declarator &D, DeclContext *DC,
bool isInline = D.getDeclSpec().isInlineSpecified();
bool isVirtual = D.getDeclSpec().isVirtualSpecified();
bool hasExplicit = D.getDeclSpec().hasExplicitSpecifier();
- ConstexprSpecKind ConstexprKind = D.getDeclSpec().getConstexprSpecifier();
isFriend = D.getDeclSpec().isFriendSpecified();
if (isFriend && !isInline && D.isFunctionDefinition()) {
// C++ [class.friend]p5
@@ -8651,7 +8765,8 @@ Sema::ActOnFunctionDeclarator(Scope *S, Declarator &D, DeclContext *DC,
}
}
- if (ConstexprKind != CSK_unspecified) {
+ if (ConstexprSpecKind ConstexprKind =
+ D.getDeclSpec().getConstexprSpecifier()) {
// C++11 [dcl.constexpr]p2: constexpr functions and constexpr constructors
// are implicitly inline.
NewFD->setImplicitlyInline();
@@ -8659,9 +8774,10 @@ Sema::ActOnFunctionDeclarator(Scope *S, Declarator &D, DeclContext *DC,
// C++11 [dcl.constexpr]p3: functions declared constexpr are required to
// be either constructors or to return a literal type. Therefore,
// destructors cannot be declared constexpr.
- if (isa<CXXDestructorDecl>(NewFD))
+ if (isa<CXXDestructorDecl>(NewFD)) {
Diag(D.getDeclSpec().getConstexprSpecLoc(), diag::err_constexpr_dtor)
- << (ConstexprKind == CSK_consteval);
+ << ConstexprKind;
+ }
}
// If __module_private__ was specified, mark the function accordingly.
@@ -12043,17 +12159,17 @@ void Sema::CheckCompleteVariableDeclaration(VarDecl *var) {
// Don't emit further diagnostics about constexpr globals since they
// were just diagnosed.
- if (!var->isConstexpr() && GlobalStorage &&
- var->hasAttr<RequireConstantInitAttr>()) {
+ 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<RequireConstantInitAttr>();
+ 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();
+ Diag(Attr->getLocation(),
+ diag::note_declared_required_constant_init_here)
+ << Attr->getRange() << Attr->isConstinit();
if (getLangOpts().CPlusPlus11) {
APValue Value;
SmallVector<PartialDiagnosticAt, 8> Notes;
@@ -12546,7 +12662,7 @@ Decl *Sema::ActOnParamDeclarator(Scope *S, Declarator &D) {
<< getLangOpts().CPlusPlus17;
if (DS.hasConstexprSpecifier())
Diag(DS.getConstexprSpecLoc(), diag::err_invalid_constexpr)
- << 0 << (D.getDeclSpec().getConstexprSpecifier() == CSK_consteval);
+ << 0 << D.getDeclSpec().getConstexprSpecifier();
DiagnoseFunctionSpecifiers(DS);
diff --git a/lib/Sema/SemaDeclAttr.cpp b/lib/Sema/SemaDeclAttr.cpp
index 2a72f7d5c6..2890f909dc 100644
--- a/lib/Sema/SemaDeclAttr.cpp
+++ b/lib/Sema/SemaDeclAttr.cpp
@@ -7057,8 +7057,8 @@ static void ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D,
case ParsedAttr::AT_VecTypeHint:
handleVecTypeHint(S, D, AL);
break;
- case ParsedAttr::AT_RequireConstantInit:
- handleSimpleAttribute<RequireConstantInitAttr>(S, D, AL);
+ case ParsedAttr::AT_ConstInit:
+ handleSimpleAttribute<ConstInitAttr>(S, D, AL);
break;
case ParsedAttr::AT_InitPriority:
handleInitPriorityAttr(S, D, AL);
diff --git a/lib/Sema/SemaType.cpp b/lib/Sema/SemaType.cpp
index 4b16a09307..f9c36e0183 100644
--- a/lib/Sema/SemaType.cpp
+++ b/lib/Sema/SemaType.cpp
@@ -5146,9 +5146,9 @@ static TypeSourceInfo *GetFullTypeForDeclarator(TypeProcessingState &state,
// C++0x [dcl.constexpr]p9:
// A constexpr specifier used in an object declaration declares the object
// as const.
- if (D.getDeclSpec().hasConstexprSpecifier() && T->isObjectType()) {
+ if (D.getDeclSpec().getConstexprSpecifier() == CSK_constexpr &&
+ T->isObjectType())
T.addConst();
- }
// If there was an ellipsis in the declarator, the declaration declares a
// parameter pack whose type may be a pack expansion type.
diff --git a/test/CXX/dcl.dcl/dcl.spec/dcl.constinit/p1.cpp b/test/CXX/dcl.dcl/dcl.spec/dcl.constinit/p1.cpp
new file mode 100644
index 0000000000..170c8c7be0
--- /dev/null
+++ b/test/CXX/dcl.dcl/dcl.spec/dcl.constinit/p1.cpp
@@ -0,0 +1,55 @@
+// RUN: %clang_cc1 -std=c++2a -verify %s
+
+constinit int a;
+constinit thread_local int b;
+constinit static int c;
+
+void f() {
+ constinit static int a;
+ constinit thread_local int b;
+ constinit int c; // expected-error {{local variable cannot be declared 'constinit'}}
+}
+
+namespace missing {
+ int a; // expected-note {{add the 'constinit' specifier}}
+ extern constinit int a; // expected-error {{added after initialization}}
+
+ // We allow inheriting 'constinit' from a forward declaration as an extension.
+ extern constinit int b; // expected-note {{here}}
+ int b; // expected-warning {{'constinit' specifier missing}}
+}
+
+struct S {
+ static constinit int a; // expected-note {{here}}
+ static constinit constexpr int b; // expected-error {{cannot combine with previous}} expected-note {{here}}
+ static constinit const int c = 1;
+ static constinit const int d = 1;
+};
+int S::a; // expected-warning {{'constinit' specifier missing}}
+int S::b; // expected-warning {{'constinit' specifier missing}}
+const int S::c;
+inline const int S::d;
+
+struct T {
+ static int a;
+ static constexpr int b = 1; // expected-note {{add the 'constinit' specifier}}
+ static const int c = 1; // expected-note {{add the 'constinit' specifier}}
+ static const int d = 1; // expected-note {{add the 'constinit' specifier}}
+};
+constinit int T::a;
+constinit const int T::b; // expected-error {{'constinit' specifier added after initialization}}
+constinit const int T::c; // expected-error {{'constinit' specifier added after initialization}}
+constinit inline const int T::d; // expected-error {{'constinit' specifier added after initialization}}
+
+constinit void g() {} // expected-error {{constinit can only be used in variable declarations}}
+
+// (These used to trigger crashes.)
+void h();
+constinit void h(); // expected-error {{constinit can only be used in variable declarations}}
+constexpr void i(); // expected-note {{here}}
+constinit void i(); // expected-error {{non-constexpr declaration of 'i' follows constexpr declaration}}
+// expected-error@-1 {{constinit can only be used in variable declarations}}
+
+typedef constinit int type; // expected-error {{typedef cannot be constinit}}
+using type = constinit int; // expected-error {{type name does not allow constinit specifier}}
+auto q() -> int constinit; // expected-error {{type name does not allow constinit specifier}}
diff --git a/test/CXX/dcl.dcl/dcl.spec/dcl.constinit/p2.cpp b/test/CXX/dcl.dcl/dcl.spec/dcl.constinit/p2.cpp
new file mode 100644
index 0000000000..22ac7a191b
--- /dev/null
+++ b/test/CXX/dcl.dcl/dcl.spec/dcl.constinit/p2.cpp
@@ -0,0 +1,8 @@
+// RUN: %clang_cc1 -std=c++2a -verify %s
+
+int f(); // expected-note 2{{declared here}}
+
+constinit int a;
+constinit int b = f(); // expected-error {{does not have a constant initializer}} expected-note {{required by}} expected-note {{non-constexpr function 'f'}}
+extern constinit int c; // expected-note {{here}} expected-note {{required by}}
+int c = f(); // expected-warning {{missing}} expected-error {{does not have a constant initializer}} expected-note {{non-constexpr function 'f'}}
diff --git a/test/CXX/dcl.dcl/dcl.spec/dcl.constinit/p3.cpp b/test/CXX/dcl.dcl/dcl.spec/dcl.constinit/p3.cpp
new file mode 100644
index 0000000000..0baea03e4b
--- /dev/null
+++ b/test/CXX/dcl.dcl/dcl.spec/dcl.constinit/p3.cpp
@@ -0,0 +1,6 @@
+// RUN: %clang_cc1 -std=c++2a -verify %s
+
+const char *g() { return "dynamic initialization"; } // expected-note {{declared here}}
+constexpr const char *f(bool b) { return b ? "constant initialization" : g(); } // expected-note {{non-constexpr function 'g'}}
+constinit const char *c = f(true);
+constinit const char *d = f(false); // expected-error {{does not have a constant initializer}} expected-note 2{{}}
diff --git a/test/FixIt/fixit-c++2a.cpp b/test/FixIt/fixit-c++2a.cpp
index c97bb7ae61..6fe05dabf0 100644
--- a/test/FixIt/fixit-c++2a.cpp
+++ b/test/FixIt/fixit-c++2a.cpp
@@ -1,7 +1,8 @@
-// RUN: %clang_cc1 -verify -std=c++2a %s
+// RUN: %clang_cc1 -verify -std=c++2a -pedantic-errors %s
// RUN: cp %s %t
// RUN: not %clang_cc1 -x c++ -std=c++2a -fixit %t
-// RUN: %clang_cc1 -Wall -pedantic -x c++ -std=c++2a %t
+// RUN: %clang_cc1 -Wall -pedantic-errors -x c++ -std=c++2a %t
+// RUN: cat %t | FileCheck %s
/* This is a test of the various code modification hints that only
apply in C++2a. */
@@ -13,3 +14,36 @@ template<typename ...T> void init_capture_pack(T ...a) {
[&...a]{}; // expected-error {{must appear after the name}}
[...&a]{}; // expected-error {{must appear after the name}}
}
+
+namespace constinit_mismatch {
+ extern thread_local constinit int a; // expected-note {{declared constinit here}}
+ thread_local int a = 123; // expected-error {{'constinit' specifier missing on initializing declaration of 'a'}}
+ // CHECK: {{^}} constinit thread_local int a = 123;
+
+ int b = 123; // expected-note {{add the 'constinit' specifier}}
+ extern constinit int b; // expected-error {{'constinit' specifier added after initialization of variable}}
+ // CHECK: {{^}} extern int b;
+
+ template<typename> struct X {
+ template<int> static constinit int n; // expected-note {{constinit}}
+ };
+ template<typename T> template<int N>
+ int X<T>::n = 123; // expected-error {{missing}}
+ // CHECK: {{^}} constinit int X<T>::n = 123;
+
+#define ABSL_CONST_INIT [[clang::require_constant_initialization]]
+ extern constinit int c; // expected-note {{constinit}}
+ int c; // expected-error {{missing}}
+ // CHECK: {{^}} ABSL_CONST_INIT int c;
+
+#define MY_CONST_INIT constinit
+ extern constinit int d; // expected-note {{constinit}}
+ int d; // expected-error {{missing}}
+ // CHECK: {{^}} MY_CONST_INIT int d;
+#undef MY_CONST_INIT
+
+ extern constinit int e; // expected-note {{constinit}}
+ int e; // expected-error {{missing}}
+ // CHECK: {{^}} ABSL_CONST_INIT int e;
+#undef ABSL_CONST_INIT
+}
diff --git a/test/Lexer/cxx-features.cpp b/test/Lexer/cxx-features.cpp
index a8ef6291f4..cda6f888cb 100644
--- a/test/Lexer/cxx-features.cpp
+++ b/test/Lexer/cxx-features.cpp
@@ -34,6 +34,10 @@
#error "wrong value for __cpp_char8_t"
#endif
+#if check(constinit, 0, 0, 0, 0, 201907)
+#error "wrong value for __cpp_constinit"
+#endif
+
#if check(impl_destroying_delete, 201806, 201806, 201806, 201806, 201806)
#error "wrong value for __cpp_impl_destroying_delete"
#endif
diff --git a/test/Lexer/cxx2a_keyword_as_cxx17.cpp b/test/Lexer/cxx2a_keyword_as_cxx17.cpp
index d2ed7d3380..a2e86931e9 100644
--- a/test/Lexer/cxx2a_keyword_as_cxx17.cpp
+++ b/test/Lexer/cxx2a_keyword_as_cxx17.cpp
@@ -11,3 +11,5 @@ int co_yield = 0; // expected-warning {{'co_yield' is a keyword in C++2a}}
int char8_t = 0; // expected-warning {{'char8_t' is a keyword in C++2a}}
int concept = 0; // expected-warning {{'concept' is a keyword in C++2a}}
int requires = 0; // expected-warning {{'requires' is a keyword in C++2a}}
+int consteval = 0; // expected-warning {{'consteval' is a keyword in C++2a}}
+int constinit = 0; // expected-warning {{'constinit' is a keyword in C++2a}}
diff --git a/test/Misc/pragma-attribute-cxx.cpp b/test/Misc/pragma-attribute-cxx.cpp
index a8a3bdebc6..38b025e476 100644
--- a/test/Misc/pragma-attribute-cxx.cpp
+++ b/test/Misc/pragma-attribute-cxx.cpp
@@ -87,14 +87,14 @@ void testLambdaMethod() {
int testCI1 = 1;
// CHECK-LABEL: VarDecl{{.*}} testCI1
// CHECK-NEXT: IntegerLiteral
-// CHECK-NEXT: RequireConstantInitAttr
+// CHECK-NEXT: ConstInitAttr
#pragma clang attribute pop
int testNoCI = 0;
// CHECK-LABEL: VarDecl{{.*}} testNoCI
// CHECK-NEXT: IntegerLiteral
-// CHECK-NOT: RequireConstantInitAttr
+// CHECK-NOT: ConstInitAttr
// Check support for CXX11 style attributes
#pragma clang attribute push ([[noreturn]], apply_to = function)
diff --git a/test/Misc/pragma-attribute-supported-attributes-list.test b/test/Misc/pragma-attribute-supported-attributes-list.test
index 83b8b9f0b9..311ba1a857 100644
--- a/test/Misc/pragma-attribute-supported-attributes-list.test
+++ b/test/Misc/pragma-attribute-supported-attributes-list.test
@@ -38,6 +38,7 @@
// CHECK-NEXT: CarriesDependency (SubjectMatchRule_variable_is_parameter, SubjectMatchRule_objc_method, SubjectMatchRule_function)
// CHECK-NEXT: Cold (SubjectMatchRule_function)
// CHECK-NEXT: Common (SubjectMatchRule_variable)
+// CHECK-NEXT: ConstInit (SubjectMatchRule_variable_is_global)
// CHECK-NEXT: Constructor (SubjectMatchRule_function)
// CHECK-NEXT: Consumable (SubjectMatchRule_record)
// CHECK-NEXT: ConsumableAutoCast (SubjectMatchRule_record)
@@ -124,7 +125,6 @@
// CHECK-NEXT: Pointer (SubjectMatchRule_record_not_is_union)
// CHECK-NEXT: RenderScriptKernel (SubjectMatchRule_function)
// CHECK-NEXT: ReqdWorkGroupSize (SubjectMatchRule_function)
-// CHECK-NEXT: RequireConstantInit (SubjectMatchRule_variable_is_global)
// CHECK-NEXT: Restrict (SubjectMatchRule_function)
// CHECK-NEXT: ReturnTypestate (SubjectMatchRule_function, SubjectMatchRule_variable_is_parameter)
// CHECK-NEXT: ReturnsNonNull (SubjectMatchRule_objc_method, SubjectMatchRule_function)
diff --git a/test/Parser/cxx0x-decl.cpp b/test/Parser/cxx0x-decl.cpp
index 7bd82e8220..2f219ac87f 100644
--- a/test/Parser/cxx0x-decl.cpp
+++ b/test/Parser/cxx0x-decl.cpp
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -verify -fsyntax-only -std=c++11 -pedantic-errors -triple x86_64-linux-gnu %s
+// RUN: %clang_cc1 -verify -fsyntax-only -std=c++2a -pedantic-errors -triple x86_64-linux-gnu %s
// Make sure we know these are legitimate commas and not typos for ';'.
namespace Commas {
@@ -108,14 +108,25 @@ namespace UsingDeclAttrs {
}
namespace DuplicateSpecifier {
- constexpr constexpr int f(); // expected-warning {{duplicate 'constexpr' declaration specifier}}
- constexpr int constexpr a = 0; // expected-warning {{duplicate 'constexpr' declaration specifier}}
+ constexpr constexpr int f(); // expected-error {{duplicate 'constexpr' declaration specifier}}
+ constexpr int constexpr a = 0; // expected-error {{duplicate 'constexpr' declaration specifier}}
struct A {
friend constexpr int constexpr friend f(); // expected-warning {{duplicate 'friend' declaration specifier}} \
- // expected-warning {{duplicate 'constexpr' declaration specifier}}
+ // expected-error {{duplicate 'constexpr' declaration specifier}}
friend struct A friend; // expected-warning {{duplicate 'friend'}} expected-error {{'friend' must appear first}}
};
+
+ constinit constexpr int n1 = 0; // expected-error {{cannot combine with previous 'constinit'}}
+ constexpr constinit int n2 = 0; // expected-error {{cannot combine with previous 'constexpr'}}
+ constinit constinit int n3 = 0; // expected-error {{duplicate 'constinit' declaration specifier}}
+
+ consteval constexpr int f1(); // expected-error {{cannot combine with previous 'consteval'}}
+ constexpr consteval int f2(); // expected-error {{cannot combine with previous 'constexpr'}}
+ consteval consteval int f3(); // expected-error {{duplicate 'consteval' declaration specifier}}
+
+ constinit consteval int wat = 0; // expected-error {{cannot combine with previous 'constinit'}}
+ consteval constinit int huh(); // expected-error {{cannot combine with previous 'consteval'}}
}
namespace ColonColonDecltype {
diff --git a/test/SemaCXX/attr-require-constant-initialization.cpp b/test/SemaCXX/attr-require-constant-initialization.cpp
index 2dd72ea6db..12bae81fd2 100644
--- a/test/SemaCXX/attr-require-constant-initialization.cpp
+++ b/test/SemaCXX/attr-require-constant-initialization.cpp
@@ -300,6 +300,17 @@ ATTR TestCtor<NotC> t(42); // expected-error {{variable does not have a constant
ATTR const char *foo[] = {"abc", "def"};
ATTR PODType bar[] = {{}, {123, 456}};
+
+namespace AttrAddedTooLate {
+ struct A {
+ static const int n = 0; // expected-note {{here}}
+ };
+ ATTR const int A::n; // expected-warning {{added after initialization}}
+
+ int m = 0; // expected-note {{here}}
+ extern ATTR int m; // expected-warning {{added after initialization}}
+}
+
#elif defined(TEST_TWO) // Test for duplicate warnings
struct NotC {
constexpr NotC(void *) {}
diff --git a/www/cxx_status.html b/www/cxx_status.html
index a3e567824d..151192b2ff 100755
--- a/www/cxx_status.html
+++ b/www/cxx_status.html
@@ -1135,7 +1135,7 @@ as the draft C++2a standard evolves.
<tr>
<td><tt>constinit</tt></td>
<td><a href="http://wg21.link/p1143r2">P1143R2</a></td>
- <td class="none" align="center">No</td>
+ <td class="svn" align="center">SVN</td>
</tr>
</table>