[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.

llvm-svn: 370972
diff --git a/clang/test/CXX/dcl.dcl/dcl.spec/dcl.constinit/p1.cpp b/clang/test/CXX/dcl.dcl/dcl.spec/dcl.constinit/p1.cpp
new file mode 100644
index 0000000..170c8c7
--- /dev/null
+++ b/clang/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/clang/test/CXX/dcl.dcl/dcl.spec/dcl.constinit/p2.cpp b/clang/test/CXX/dcl.dcl/dcl.spec/dcl.constinit/p2.cpp
new file mode 100644
index 0000000..22ac7a1
--- /dev/null
+++ b/clang/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/clang/test/CXX/dcl.dcl/dcl.spec/dcl.constinit/p3.cpp b/clang/test/CXX/dcl.dcl/dcl.spec/dcl.constinit/p3.cpp
new file mode 100644
index 0000000..0baea03
--- /dev/null
+++ b/clang/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/clang/test/FixIt/fixit-c++2a.cpp b/clang/test/FixIt/fixit-c++2a.cpp
index c97bb7a..6fe05da 100644
--- a/clang/test/FixIt/fixit-c++2a.cpp
+++ b/clang/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 @@
   [&...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/clang/test/Lexer/cxx-features.cpp b/clang/test/Lexer/cxx-features.cpp
index a8ef6291..cda6f88 100644
--- a/clang/test/Lexer/cxx-features.cpp
+++ b/clang/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/clang/test/Lexer/cxx2a_keyword_as_cxx17.cpp b/clang/test/Lexer/cxx2a_keyword_as_cxx17.cpp
index d2ed7d3..a2e8693 100644
--- a/clang/test/Lexer/cxx2a_keyword_as_cxx17.cpp
+++ b/clang/test/Lexer/cxx2a_keyword_as_cxx17.cpp
@@ -11,3 +11,5 @@
 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/clang/test/Misc/pragma-attribute-cxx.cpp b/clang/test/Misc/pragma-attribute-cxx.cpp
index a8a3bde..38b025e 100644
--- a/clang/test/Misc/pragma-attribute-cxx.cpp
+++ b/clang/test/Misc/pragma-attribute-cxx.cpp
@@ -87,14 +87,14 @@
 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/clang/test/Misc/pragma-attribute-supported-attributes-list.test b/clang/test/Misc/pragma-attribute-supported-attributes-list.test
index 83b8b9f..311ba1a 100644
--- a/clang/test/Misc/pragma-attribute-supported-attributes-list.test
+++ b/clang/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/clang/test/Parser/cxx0x-decl.cpp b/clang/test/Parser/cxx0x-decl.cpp
index 7bd82e8..2f219ac 100644
--- a/clang/test/Parser/cxx0x-decl.cpp
+++ b/clang/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 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/clang/test/SemaCXX/attr-require-constant-initialization.cpp b/clang/test/SemaCXX/attr-require-constant-initialization.cpp
index 2dd72ea..12bae81 100644
--- a/clang/test/SemaCXX/attr-require-constant-initialization.cpp
+++ b/clang/test/SemaCXX/attr-require-constant-initialization.cpp
@@ -300,6 +300,17 @@
 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 *) {}