r370972 - [c++20] P1143R2: Add support for the C++20 'constinit' keyword.

Richard Smith via cfe-commits cfe-commits at lists.llvm.org
Wed Sep 4 13:30:37 PDT 2019


Author: rsmith
Date: Wed Sep  4 13:30:37 2019
New Revision: 370972

URL: http://llvm.org/viewvc/llvm-project?rev=370972&view=rev
Log:
[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.

Added:
    cfe/trunk/test/CXX/dcl.dcl/dcl.spec/dcl.constinit/
    cfe/trunk/test/CXX/dcl.dcl/dcl.spec/dcl.constinit/p1.cpp
    cfe/trunk/test/CXX/dcl.dcl/dcl.spec/dcl.constinit/p2.cpp
    cfe/trunk/test/CXX/dcl.dcl/dcl.spec/dcl.constinit/p3.cpp
Modified:
    cfe/trunk/include/clang/AST/Decl.h
    cfe/trunk/include/clang/Basic/Attr.td
    cfe/trunk/include/clang/Basic/DiagnosticCommonKinds.td
    cfe/trunk/include/clang/Basic/DiagnosticParseKinds.td
    cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td
    cfe/trunk/include/clang/Basic/Specifiers.h
    cfe/trunk/include/clang/Basic/TokenKinds.def
    cfe/trunk/lib/AST/Decl.cpp
    cfe/trunk/lib/Frontend/InitPreprocessor.cpp
    cfe/trunk/lib/Parse/ParseDecl.cpp
    cfe/trunk/lib/Parse/ParseDeclCXX.cpp
    cfe/trunk/lib/Parse/ParseTentative.cpp
    cfe/trunk/lib/Sema/DeclSpec.cpp
    cfe/trunk/lib/Sema/SemaDecl.cpp
    cfe/trunk/lib/Sema/SemaDeclAttr.cpp
    cfe/trunk/lib/Sema/SemaType.cpp
    cfe/trunk/test/FixIt/fixit-c++2a.cpp
    cfe/trunk/test/Lexer/cxx-features.cpp
    cfe/trunk/test/Lexer/cxx2a_keyword_as_cxx17.cpp
    cfe/trunk/test/Misc/pragma-attribute-cxx.cpp
    cfe/trunk/test/Misc/pragma-attribute-supported-attributes-list.test
    cfe/trunk/test/Parser/cxx0x-decl.cpp
    cfe/trunk/test/SemaCXX/attr-require-constant-initialization.cpp
    cfe/trunk/www/cxx_status.html

Modified: cfe/trunk/include/clang/AST/Decl.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/AST/Decl.h?rev=370972&r1=370971&r2=370972&view=diff
==============================================================================
--- cfe/trunk/include/clang/AST/Decl.h (original)
+++ cfe/trunk/include/clang/AST/Decl.h Wed Sep  4 13:30:37 2019
@@ -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

Modified: cfe/trunk/include/clang/Basic/Attr.td
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/Attr.td?rev=370972&r1=370971&r2=370972&view=diff
==============================================================================
--- cfe/trunk/include/clang/Basic/Attr.td (original)
+++ cfe/trunk/include/clang/Basic/Attr.td Wed Sep  4 13:30:37 2019
@@ -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">];

Modified: cfe/trunk/include/clang/Basic/DiagnosticCommonKinds.td
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/DiagnosticCommonKinds.td?rev=370972&r1=370971&r2=370972&view=diff
==============================================================================
--- cfe/trunk/include/clang/Basic/DiagnosticCommonKinds.td (original)
+++ cfe/trunk/include/clang/Basic/DiagnosticCommonKinds.td Wed Sep  4 13:30:37 2019
@@ -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">;

Modified: cfe/trunk/include/clang/Basic/DiagnosticParseKinds.td
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/DiagnosticParseKinds.td?rev=370972&r1=370971&r2=370972&view=diff
==============================================================================
--- cfe/trunk/include/clang/Basic/DiagnosticParseKinds.td (original)
+++ cfe/trunk/include/clang/Basic/DiagnosticParseKinds.td Wed Sep  4 13:30:37 2019
@@ -358,7 +358,8 @@ def err_typename_invalid_storageclass :
 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">;
 

Modified: cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td?rev=370972&r1=370971&r2=370972&view=diff
==============================================================================
--- cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td (original)
+++ cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td Wed Sep  4 13:30:37 2019
@@ -2343,18 +2343,18 @@ def warn_cxx14_compat_constexpr_not_cons
   "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_ass
 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">;

Modified: cfe/trunk/include/clang/Basic/Specifiers.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/Specifiers.h?rev=370972&r1=370971&r2=370972&view=diff
==============================================================================
--- cfe/trunk/include/clang/Basic/Specifiers.h (original)
+++ cfe/trunk/include/clang/Basic/Specifiers.h Wed Sep  4 13:30:37 2019
@@ -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.

Modified: cfe/trunk/include/clang/Basic/TokenKinds.def
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/TokenKinds.def?rev=370972&r1=370971&r2=370972&view=diff
==============================================================================
--- cfe/trunk/include/clang/Basic/TokenKinds.def (original)
+++ cfe/trunk/include/clang/Basic/TokenKinds.def Wed Sep  4 13:30:37 2019
@@ -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)

Modified: cfe/trunk/lib/AST/Decl.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/Decl.cpp?rev=370972&r1=370971&r2=370972&view=diff
==============================================================================
--- cfe/trunk/lib/AST/Decl.cpp (original)
+++ cfe/trunk/lib/AST/Decl.cpp Wed Sep  4 13:30:37 2019
@@ -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;

Modified: cfe/trunk/lib/Frontend/InitPreprocessor.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Frontend/InitPreprocessor.cpp?rev=370972&r1=370971&r2=370972&view=diff
==============================================================================
--- cfe/trunk/lib/Frontend/InitPreprocessor.cpp (original)
+++ cfe/trunk/lib/Frontend/InitPreprocessor.cpp Wed Sep  4 13:30:37 2019
@@ -541,8 +541,10 @@ static void InitializeCPlusPlusFeatureTe
     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");

Modified: cfe/trunk/lib/Parse/ParseDecl.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Parse/ParseDecl.cpp?rev=370972&r1=370971&r2=370972&view=diff
==============================================================================
--- cfe/trunk/lib/Parse/ParseDecl.cpp (original)
+++ cfe/trunk/lib/Parse/ParseDecl.cpp Wed Sep  4 13:30:37 2019
@@ -2514,7 +2514,7 @@ void Parser::ParseSpecifierQualifierList
   // 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(
       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
   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:

Modified: cfe/trunk/lib/Parse/ParseDeclCXX.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Parse/ParseDeclCXX.cpp?rev=370972&r1=370971&r2=370972&view=diff
==============================================================================
--- cfe/trunk/lib/Parse/ParseDeclCXX.cpp (original)
+++ cfe/trunk/lib/Parse/ParseDeclCXX.cpp Wed Sep  4 13:30:37 2019
@@ -1313,6 +1313,8 @@ bool Parser::isValidAfterTypeSpecifier(b
   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,

Modified: cfe/trunk/lib/Parse/ParseTentative.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Parse/ParseTentative.cpp?rev=370972&r1=370971&r2=370972&view=diff
==============================================================================
--- cfe/trunk/lib/Parse/ParseTentative.cpp (original)
+++ cfe/trunk/lib/Parse/ParseTentative.cpp Wed Sep  4 13:30:37 2019
@@ -1408,6 +1408,7 @@ Parser::isCXXDeclarationSpecifier(Parser
   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:

Modified: cfe/trunk/lib/Sema/DeclSpec.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/DeclSpec.cpp?rev=370972&r1=370971&r2=370972&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/DeclSpec.cpp (original)
+++ cfe/trunk/lib/Sema/DeclSpec.cpp Wed Sep  4 13:30:37 2019
@@ -569,6 +569,7 @@ const char *DeclSpec::getSpecifierName(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(Sour
 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 Pri
       << (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.

Modified: cfe/trunk/lib/Sema/SemaDecl.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaDecl.cpp?rev=370972&r1=370971&r2=370972&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaDecl.cpp (original)
+++ cfe/trunk/lib/Sema/SemaDecl.cpp Wed Sep  4 13:30:37 2019
@@ -2661,6 +2661,60 @@ static void checkNewAttributesAfterDef(S
   }
 }
 
+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
   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 *
     // 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, D
         << 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 getFunctionStorageCl
   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* CreateNewFunctionDe
   }
 
   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,
     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,
       }
     }
 
-    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,
       // 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::CheckCompleteVariableDeclarat
 
     // 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 *
         << getLangOpts().CPlusPlus17;
   if (DS.hasConstexprSpecifier())
     Diag(DS.getConstexprSpecLoc(), diag::err_invalid_constexpr)
-        << 0 << (D.getDeclSpec().getConstexprSpecifier() == CSK_consteval);
+        << 0 << D.getDeclSpec().getConstexprSpecifier();
 
   DiagnoseFunctionSpecifiers(DS);
 

Modified: cfe/trunk/lib/Sema/SemaDeclAttr.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaDeclAttr.cpp?rev=370972&r1=370971&r2=370972&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaDeclAttr.cpp (original)
+++ cfe/trunk/lib/Sema/SemaDeclAttr.cpp Wed Sep  4 13:30:37 2019
@@ -7057,8 +7057,8 @@ static void ProcessDeclAttribute(Sema &S
   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);

Modified: cfe/trunk/lib/Sema/SemaType.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaType.cpp?rev=370972&r1=370971&r2=370972&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaType.cpp (original)
+++ cfe/trunk/lib/Sema/SemaType.cpp Wed Sep  4 13:30:37 2019
@@ -5146,9 +5146,9 @@ static TypeSourceInfo *GetFullTypeForDec
   // 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.

Added: cfe/trunk/test/CXX/dcl.dcl/dcl.spec/dcl.constinit/p1.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CXX/dcl.dcl/dcl.spec/dcl.constinit/p1.cpp?rev=370972&view=auto
==============================================================================
--- cfe/trunk/test/CXX/dcl.dcl/dcl.spec/dcl.constinit/p1.cpp (added)
+++ cfe/trunk/test/CXX/dcl.dcl/dcl.spec/dcl.constinit/p1.cpp Wed Sep  4 13:30:37 2019
@@ -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 at -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}}

Added: cfe/trunk/test/CXX/dcl.dcl/dcl.spec/dcl.constinit/p2.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CXX/dcl.dcl/dcl.spec/dcl.constinit/p2.cpp?rev=370972&view=auto
==============================================================================
--- cfe/trunk/test/CXX/dcl.dcl/dcl.spec/dcl.constinit/p2.cpp (added)
+++ cfe/trunk/test/CXX/dcl.dcl/dcl.spec/dcl.constinit/p2.cpp Wed Sep  4 13:30:37 2019
@@ -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'}}

Added: cfe/trunk/test/CXX/dcl.dcl/dcl.spec/dcl.constinit/p3.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CXX/dcl.dcl/dcl.spec/dcl.constinit/p3.cpp?rev=370972&view=auto
==============================================================================
--- cfe/trunk/test/CXX/dcl.dcl/dcl.spec/dcl.constinit/p3.cpp (added)
+++ cfe/trunk/test/CXX/dcl.dcl/dcl.spec/dcl.constinit/p3.cpp Wed Sep  4 13:30:37 2019
@@ -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{{}}

Modified: cfe/trunk/test/FixIt/fixit-c++2a.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/FixIt/fixit-c%2B%2B2a.cpp?rev=370972&r1=370971&r2=370972&view=diff
==============================================================================
--- cfe/trunk/test/FixIt/fixit-c++2a.cpp (original)
+++ cfe/trunk/test/FixIt/fixit-c++2a.cpp Wed Sep  4 13:30:37 2019
@@ -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_captur
   [&...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
+}

Modified: cfe/trunk/test/Lexer/cxx-features.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Lexer/cxx-features.cpp?rev=370972&r1=370971&r2=370972&view=diff
==============================================================================
--- cfe/trunk/test/Lexer/cxx-features.cpp (original)
+++ cfe/trunk/test/Lexer/cxx-features.cpp Wed Sep  4 13:30:37 2019
@@ -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

Modified: cfe/trunk/test/Lexer/cxx2a_keyword_as_cxx17.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Lexer/cxx2a_keyword_as_cxx17.cpp?rev=370972&r1=370971&r2=370972&view=diff
==============================================================================
--- cfe/trunk/test/Lexer/cxx2a_keyword_as_cxx17.cpp (original)
+++ cfe/trunk/test/Lexer/cxx2a_keyword_as_cxx17.cpp Wed Sep  4 13:30:37 2019
@@ -11,3 +11,5 @@ int co_yield = 0; // expected-warning {{
 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}}

Modified: cfe/trunk/test/Misc/pragma-attribute-cxx.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Misc/pragma-attribute-cxx.cpp?rev=370972&r1=370971&r2=370972&view=diff
==============================================================================
--- cfe/trunk/test/Misc/pragma-attribute-cxx.cpp (original)
+++ cfe/trunk/test/Misc/pragma-attribute-cxx.cpp Wed Sep  4 13:30:37 2019
@@ -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)

Modified: cfe/trunk/test/Misc/pragma-attribute-supported-attributes-list.test
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Misc/pragma-attribute-supported-attributes-list.test?rev=370972&r1=370971&r2=370972&view=diff
==============================================================================
--- cfe/trunk/test/Misc/pragma-attribute-supported-attributes-list.test (original)
+++ cfe/trunk/test/Misc/pragma-attribute-supported-attributes-list.test Wed Sep  4 13:30:37 2019
@@ -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)

Modified: cfe/trunk/test/Parser/cxx0x-decl.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Parser/cxx0x-decl.cpp?rev=370972&r1=370971&r2=370972&view=diff
==============================================================================
--- cfe/trunk/test/Parser/cxx0x-decl.cpp (original)
+++ cfe/trunk/test/Parser/cxx0x-decl.cpp Wed Sep  4 13:30:37 2019
@@ -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 {

Modified: cfe/trunk/test/SemaCXX/attr-require-constant-initialization.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaCXX/attr-require-constant-initialization.cpp?rev=370972&r1=370971&r2=370972&view=diff
==============================================================================
--- cfe/trunk/test/SemaCXX/attr-require-constant-initialization.cpp (original)
+++ cfe/trunk/test/SemaCXX/attr-require-constant-initialization.cpp Wed Sep  4 13:30:37 2019
@@ -300,6 +300,17 @@ ATTR TestCtor<NotC> t(42); // expected-e
 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 *) {}

Modified: cfe/trunk/www/cxx_status.html
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/www/cxx_status.html?rev=370972&r1=370971&r2=370972&view=diff
==============================================================================
--- cfe/trunk/www/cxx_status.html (original)
+++ cfe/trunk/www/cxx_status.html Wed Sep  4 13:30:37 2019
@@ -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>
 




More information about the cfe-commits mailing list