PR c++/91360 - Implement C++20 P1143R2: constinit.
authorMarek Polacek <polacek@redhat.com>
Wed, 28 Aug 2019 20:31:31 +0000 (20:31 +0000)
committerMarek Polacek <mpolacek@gcc.gnu.org>
Wed, 28 Aug 2019 20:31:31 +0000 (20:31 +0000)
* c-common.c (c_common_reswords): Add constinit and __constinit.
(keyword_is_decl_specifier): Handle RID_CONSTINIT.
* c-common.h (enum rid): Add RID_CONSTINIT, RID_FIRST_CXX20, and
RID_LAST_CXX20.
(D_CXX20): Define.
* c-cppbuiltin.c (c_cpp_builtins): Define __cpp_constinit.
* c-format.c (cxx_keywords): Add "constinit".
* c.opt (Wc++2a-compat, Wc++20-compat): New options.

* cp-tree.h (TINFO_VAR_DECLARED_CONSTINIT): Define.
(LOOKUP_CONSTINIT): Define.
(enum cp_decl_spec): Add ds_constinit.
* decl.c (check_tag_decl): Give an error for constinit in type
declarations.
(check_initializer): Also check LOOKUP_CONSTINIT.
(cp_finish_decl): Add checking for a constinit declaration.  Set
TINFO_VAR_DECLARED_CONSTINIT.
(grokdeclarator): Add checking for a declaration with the constinit
specifier.
* lex.c (init_reswords): Handle D_CXX20.
* parser.c (cp_lexer_get_preprocessor_token): Pass a better location
to warning_at.  Warn about C++20 keywords.
(cp_keyword_starts_decl_specifier_p): Handle RID_CONSTINIT.
(cp_parser_diagnose_invalid_type_name): Add an inform about constinit.
(cp_parser_decomposition_declaration): Maybe pass LOOKUP_CONSTINIT to
cp_finish_decl.
(cp_parser_decl_specifier_seq): Handle RID_CONSTINIT.
(cp_parser_init_declarator): Maybe pass LOOKUP_CONSTINIT to
cp_finish_decl.
(set_and_check_decl_spec_loc): Add "constinit".
* pt.c (tsubst_decl): Set TINFO_VAR_DECLARED_CONSTINIT.
(instantiate_decl): Maybe pass LOOKUP_CONSTINIT to cp_finish_decl.
* typeck2.c (store_init_value): If a constinit variable wasn't
initialized using a constant initializer, give an error.

* doc/invoke.texi: Document -Wc++20-compat.

* g++.dg/cpp2a/constinit1.C: New test.
* g++.dg/cpp2a/constinit2.C: New test.
* g++.dg/cpp2a/constinit3.C: New test.
* g++.dg/cpp2a/constinit4.C: New test.
* g++.dg/cpp2a/constinit5.C: New test.
* g++.dg/cpp2a/constinit6.C: New test.
* g++.dg/cpp2a/constinit7.C: New test.
* g++.dg/cpp2a/constinit8.C: New test.
* g++.dg/cpp2a/constinit9.C: New test.
* g++.dg/cpp2a/constinit10.C: New test.
* g++.dg/cpp2a/constinit11.C: New test.
* g++.dg/cpp2a/constinit12.C: New test.

From-SVN: r275008

28 files changed:
gcc/ChangeLog
gcc/c-family/ChangeLog
gcc/c-family/c-common.c
gcc/c-family/c-common.h
gcc/c-family/c-cppbuiltin.c
gcc/c-family/c-format.c
gcc/c-family/c.opt
gcc/cp/ChangeLog
gcc/cp/cp-tree.h
gcc/cp/decl.c
gcc/cp/lex.c
gcc/cp/parser.c
gcc/cp/pt.c
gcc/cp/typeck2.c
gcc/doc/invoke.texi
gcc/testsuite/ChangeLog
gcc/testsuite/g++.dg/cpp2a/constinit1.C [new file with mode: 0644]
gcc/testsuite/g++.dg/cpp2a/constinit10.C [new file with mode: 0644]
gcc/testsuite/g++.dg/cpp2a/constinit11.C [new file with mode: 0644]
gcc/testsuite/g++.dg/cpp2a/constinit12.C [new file with mode: 0644]
gcc/testsuite/g++.dg/cpp2a/constinit2.C [new file with mode: 0644]
gcc/testsuite/g++.dg/cpp2a/constinit3.C [new file with mode: 0644]
gcc/testsuite/g++.dg/cpp2a/constinit4.C [new file with mode: 0644]
gcc/testsuite/g++.dg/cpp2a/constinit5.C [new file with mode: 0644]
gcc/testsuite/g++.dg/cpp2a/constinit6.C [new file with mode: 0644]
gcc/testsuite/g++.dg/cpp2a/constinit7.C [new file with mode: 0644]
gcc/testsuite/g++.dg/cpp2a/constinit8.C [new file with mode: 0644]
gcc/testsuite/g++.dg/cpp2a/constinit9.C [new file with mode: 0644]

index 77dbf876a3a5d0c16fbbcce9f8d127a6d7baa280..8b5ccd2f98d1f6b9f0df04537b6a2d90bcc0c4b8 100644 (file)
@@ -1,3 +1,8 @@
+2019-08-28  Marek Polacek  <polacek@redhat.com>
+
+       PR c++/91360 - Implement C++20 P1143R2: constinit.
+       * doc/invoke.texi: Document -Wc++20-compat.
+
 2019-08-28  Martin Sebor  <msebor@redhat.com>
 
        PR tree-optimization/91457
index 0376a7b977debc721d1a37ac94a1fe585ee3faaa..d0a19e3870dd1ccfbd1c50c5297fa903fb846571 100644 (file)
@@ -1,3 +1,15 @@
+2019-08-28  Marek Polacek  <polacek@redhat.com>
+
+       PR c++/91360 - Implement C++20 P1143R2: constinit.
+       * c-common.c (c_common_reswords): Add constinit and __constinit.
+       (keyword_is_decl_specifier): Handle RID_CONSTINIT.
+       * c-common.h (enum rid): Add RID_CONSTINIT, RID_FIRST_CXX20, and
+       RID_LAST_CXX20.
+       (D_CXX20): Define.
+       * c-cppbuiltin.c (c_cpp_builtins): Define __cpp_constinit.
+       * c-format.c (cxx_keywords): Add "constinit".
+       * c.opt (Wc++2a-compat, Wc++20-compat): New options.
+
 2019-08-27  Jakub Jelinek  <jakub@redhat.com>
 
        PR c++/91415
index 61ee7543dacc447484b2b1955f8366a5f5e1bdc4..abc85cb29291310d28c087e5cd7e9a18686c02ff 100644 (file)
@@ -326,8 +326,9 @@ static bool nonnull_check_p (tree, unsigned HOST_WIDE_INT);
    C --std=c89: D_C99 | D_CXXONLY | D_OBJC | D_CXX_OBJC
    C --std=c99: D_CXXONLY | D_OBJC
    ObjC is like C except that D_OBJC and D_CXX_OBJC are not set
-   C++ --std=c++98: D_CONLY | D_CXX11 | D_OBJC
-   C++ --std=c++11: D_CONLY | D_OBJC
+   C++ --std=c++98: D_CONLY | D_CXX11 | D_CXX20 | D_OBJC
+   C++ --std=c++11: D_CONLY | D_CXX20 | D_OBJC
+   C++ --std=c++2a: D_CONLY | D_OBJC
    ObjC++ is like C++ except that D_OBJC is not set
 
    If -fno-asm is used, D_ASM is added to the mask.  If
@@ -392,6 +393,7 @@ const struct c_common_resword c_common_reswords[] =
   { "__complex__",     RID_COMPLEX,    0 },
   { "__const",         RID_CONST,      0 },
   { "__const__",       RID_CONST,      0 },
+  { "__constinit",     RID_CONSTINIT,  D_CXXONLY },
   { "__decltype",       RID_DECLTYPE,   D_CXXONLY },
   { "__direct_bases",   RID_DIRECT_BASES, D_CXXONLY },
   { "__extension__",   RID_EXTENSION,  0 },
@@ -462,6 +464,7 @@ const struct c_common_resword c_common_reswords[] =
   { "class",           RID_CLASS,      D_CXX_OBJC | D_CXXWARN },
   { "const",           RID_CONST,      0 },
   { "constexpr",       RID_CONSTEXPR,  D_CXXONLY | D_CXX11 | D_CXXWARN },
+  { "constinit",       RID_CONSTINIT,  D_CXXONLY | D_CXX20 | D_CXXWARN },
   { "const_cast",      RID_CONSTCAST,  D_CXXONLY | D_CXXWARN },
   { "continue",                RID_CONTINUE,   0 },
   { "decltype",         RID_DECLTYPE,   D_CXXONLY | D_CXX11 | D_CXXWARN },
@@ -7927,6 +7930,7 @@ keyword_is_decl_specifier (enum rid keyword)
     case RID_TYPEDEF:
     case RID_FRIEND:
     case RID_CONSTEXPR:
+    case RID_CONSTINIT:
       return true;
     default:
       return false;
index 117d729091a502547bdf56cdfddae5d0c39cd05c..17bd7b1c7d8fb0cf6898cdf87122d80758aa2d03 100644 (file)
@@ -180,6 +180,9 @@ enum rid
   /* C++11 */
   RID_CONSTEXPR, RID_DECLTYPE, RID_NOEXCEPT, RID_NULLPTR, RID_STATIC_ASSERT,
 
+  /* C++20 */
+  RID_CONSTINIT,
+
   /* char8_t */
   RID_CHAR8,
 
@@ -250,6 +253,8 @@ enum rid
 
   RID_FIRST_CXX11 = RID_CONSTEXPR,
   RID_LAST_CXX11 = RID_STATIC_ASSERT,
+  RID_FIRST_CXX20 = RID_CONSTINIT,
+  RID_LAST_CXX20 = RID_CONSTINIT,
   RID_FIRST_AT = RID_AT_ENCODE,
   RID_LAST_AT = RID_AT_IMPLEMENTATION,
   RID_FIRST_PQ = RID_IN,
@@ -427,6 +432,7 @@ extern machine_mode c_default_pointer_mode;
 #define D_CXX_CONCEPTS  0x0400 /* In C++, only with concepts.  */
 #define D_TRANSMEM     0X0800  /* C++ transactional memory TS.  */
 #define D_CXX_CHAR8_T  0X1000  /* In C++, only with -fchar8_t.  */
+#define D_CXX20                0x2000  /* In C++, C++20 only.  */
 
 #define D_CXX_CONCEPTS_FLAGS D_CXXONLY | D_CXX_CONCEPTS
 #define D_CXX_CHAR8_T_FLAGS D_CXXONLY | D_CXX_CHAR8_T
index 6006e95b0684f35ff5a4aa73f1616377b2bb1034..6b18246e0b663f3eb705dd82379a9651f616d9f1 100644 (file)
@@ -986,6 +986,7 @@ c_cpp_builtins (cpp_reader *pfile)
        {
          /* Set feature test macros for C++2a.  */
          cpp_define (pfile, "__cpp_conditional_explicit=201806");
+         cpp_define (pfile, "__cpp_constinit=201907");
          cpp_define (pfile, "__cpp_nontype_template_parameter_class=201806");
          cpp_define (pfile, "__cpp_impl_destroying_delete=201806");
        }
index 6b059969e670a25203de193c65d02d2906644e08..91bae3d6096f580feeba794e6ef58a719b40ee8d 100644 (file)
@@ -2958,6 +2958,7 @@ static const token_t cxx_keywords[] =
    NAME ("catch", NULL),
    NAME ("constexpr if", NULL),
    NAME ("constexpr", NULL),
+   NAME ("constinit", NULL),
    NAME ("consteval", NULL),
    NAME ("decltype", NULL),
    NAME ("nullptr", NULL),
index 257cadfa5f1d77d194967e48bf54efdd7e1f339e..4c468d0f6c288a30c2616659a285f795dc2835cc 100644 (file)
@@ -400,6 +400,13 @@ Wc++17-compat
 C++ ObjC++ Var(warn_cxx17_compat) Warning LangEnabledBy(C++ ObjC++,Wall)
 Warn about C++ constructs whose meaning differs between ISO C++ 2014 and ISO C++ 2017.
 
+Wc++2a-compat
+C++ ObjC++ Warning Alias(Wc++20-compat) Undocumented
+
+Wc++20-compat
+C++ ObjC++ Var(warn_cxx20_compat) Warning LangEnabledBy(C++ ObjC++,Wall)
+Warn about C++ constructs whose meaning differs between ISO C++ 2017 and ISO C++ 2020.
+
 Wcast-function-type
 C ObjC C++ ObjC++ Var(warn_cast_function_type) Warning EnabledBy(Wextra)
 Warn about casts between incompatible function types.
index 08a44b6c6067f4a273f0c69943f63edbb35cfd21..2e7c26cd4476d062d6929fdd66849f2f8e87ed9d 100644 (file)
@@ -1,3 +1,32 @@
+2019-08-28  Marek Polacek  <polacek@redhat.com>
+
+       PR c++/91360 - Implement C++20 P1143R2: constinit.
+       * cp-tree.h (TINFO_VAR_DECLARED_CONSTINIT): Define.
+       (LOOKUP_CONSTINIT): Define.
+       (enum cp_decl_spec): Add ds_constinit.
+       * decl.c (check_tag_decl): Give an error for constinit in type
+       declarations.
+       (check_initializer): Also check LOOKUP_CONSTINIT.
+       (cp_finish_decl): Add checking for a constinit declaration.  Set
+       TINFO_VAR_DECLARED_CONSTINIT.
+       (grokdeclarator): Add checking for a declaration with the constinit
+       specifier.
+       * lex.c (init_reswords): Handle D_CXX20.
+       * parser.c (cp_lexer_get_preprocessor_token): Pass a better location
+       to warning_at.  Warn about C++20 keywords.
+       (cp_keyword_starts_decl_specifier_p): Handle RID_CONSTINIT.
+       (cp_parser_diagnose_invalid_type_name): Add an inform about constinit.
+       (cp_parser_decomposition_declaration): Maybe pass LOOKUP_CONSTINIT to
+       cp_finish_decl.
+       (cp_parser_decl_specifier_seq): Handle RID_CONSTINIT.
+       (cp_parser_init_declarator): Maybe pass LOOKUP_CONSTINIT to
+       cp_finish_decl.
+       (set_and_check_decl_spec_loc): Add "constinit".
+       * pt.c (tsubst_decl): Set TINFO_VAR_DECLARED_CONSTINIT.
+       (instantiate_decl): Maybe pass LOOKUP_CONSTINIT to cp_finish_decl.
+       * typeck2.c (store_init_value): If a constinit variable wasn't
+       initialized using a constant initializer, give an error.
+
 2019-08-28  Nathan Sidwell  <nathan@acm.org>
 
        PR c++/90613
index 225dbb67c635ce214613173978a849bf58e5980d..0e514d5cc221163c2472a8f73934a50fd77af0eb 100644 (file)
@@ -443,6 +443,7 @@ extern GTY(()) tree cp_global_trees[CPTI_MAX];
       SWITCH_STMT_NO_BREAK_P (in SWITCH_STMT)
       LAMBDA_EXPR_CAPTURE_OPTIMIZED (in LAMBDA_EXPR)
       IMPLICIT_CONV_EXPR_BRACED_INIT (in IMPLICIT_CONV_EXPR)
+      TINFO_VAR_DECLARED_CONSTINIT (in TEMPLATE_INFO)
    3: (TREE_REFERENCE_EXPR) (in NON_LVALUE_EXPR) (commented-out).
       ICS_BAD_FLAG (in _CONV)
       FN_TRY_BLOCK_P (in TRY_BLOCK)
@@ -1435,6 +1436,11 @@ typedef struct qualified_typedef_usage_s qualified_typedef_usage_t;
 #define TINFO_USED_TEMPLATE_ID(NODE) \
   (TREE_LANG_FLAG_1 (TEMPLATE_INFO_CHECK (NODE)))
 
+/* Non-zero if this variable template specialization was declared with the
+   `constinit' specifier.  */
+#define TINFO_VAR_DECLARED_CONSTINIT(NODE) \
+  (TREE_LANG_FLAG_2 (TEMPLATE_INFO_CHECK (NODE)))
+
 struct GTY(()) tree_template_info {
   struct tree_base base;
   tree tmpl;
@@ -5502,6 +5508,8 @@ enum overload_flags { NO_SPECIAL = 0, DTOR_FLAG, TYPENAME_FLAG };
 #define LOOKUP_DELEGATING_CONS (LOOKUP_NO_NON_INTEGRAL << 1)
 /* Allow initialization of a flexible array members.  */
 #define LOOKUP_ALLOW_FLEXARRAY_INIT (LOOKUP_DELEGATING_CONS << 1)
+/* Require constant initialization of a non-constant variable.  */
+#define LOOKUP_CONSTINIT (LOOKUP_ALLOW_FLEXARRAY_INIT << 1)
 
 #define LOOKUP_NAMESPACES_ONLY(F)  \
   (((F) & LOOKUP_PREFER_NAMESPACES) && !((F) & LOOKUP_PREFER_TYPES))
@@ -5815,6 +5823,7 @@ enum cp_decl_spec {
   ds_alias,
   ds_constexpr,
   ds_complex,
+  ds_constinit,
   ds_thread,
   ds_type_spec,
   ds_redefined_builtin_type_spec,
index 847817029e443a2d871d72adf02ca9c5688456a6..c5cc22a8d6d646cf250c7e4becfd2a60f4af7ba9 100644 (file)
@@ -4962,6 +4962,9 @@ check_tag_decl (cp_decl_specifier_seq *declspecs,
       else if (decl_spec_seq_has_spec_p (declspecs,  ds_constexpr))
         error_at (declspecs->locations[ds_constexpr],
                  "%<constexpr%> cannot be used for type declarations");
+      else if (decl_spec_seq_has_spec_p (declspecs,  ds_constinit))
+       error_at (declspecs->locations[ds_constinit],
+                 "%<constinit%> cannot be used for type declarations");
     }
 
   if (declspecs->attributes && warn_attributes && declared_type)
@@ -6595,11 +6598,12 @@ check_initializer (tree decl, tree init, int flags, vec<tree, va_gc> **cleanups)
                 about aggregate initialization of non-aggregate classes.  */
              flags |= LOOKUP_ALREADY_DIGESTED;
            }
-         else if (DECL_DECLARED_CONSTEXPR_P (decl))
+         else if (DECL_DECLARED_CONSTEXPR_P (decl)
+                  || (flags & LOOKUP_CONSTINIT))
            {
-             /* Declared constexpr, but no suitable initializer; massage
-                init appropriately so we can pass it into store_init_value
-                for the error.  */
+             /* Declared constexpr or constinit, but no suitable initializer;
+                massage init appropriately so we can pass it into
+                store_init_value for the error.  */
              if (CLASS_TYPE_P (type)
                  && (!init || TREE_CODE (init) == TREE_LIST))
                {
@@ -7162,6 +7166,10 @@ cp_finish_decl (tree decl, tree init, bool init_const_expr_p,
          DECL_INITIAL (decl) = NULL_TREE;
        }
 
+      /* Handle `constinit' on variable templates.  */
+      if (flags & LOOKUP_CONSTINIT)
+       TINFO_VAR_DECLARED_CONSTINIT (DECL_TEMPLATE_INFO (decl)) = true;
+
       /* Generally, initializers in templates are expanded when the
         template is instantiated.  But, if DECL is a variable constant
         then it can be used in future constant expressions, so its value
@@ -7253,6 +7261,18 @@ cp_finish_decl (tree decl, tree init, bool init_const_expr_p,
 
   if (VAR_P (decl))
     {
+      duration_kind dk = decl_storage_duration (decl);
+      /* [dcl.constinit]/1 "The constinit specifier shall be applied
+        only to a declaration of a variable with static or thread storage
+        duration."  */
+      if ((flags & LOOKUP_CONSTINIT)
+         && !(dk == dk_thread || dk == dk_static))
+       {
+         error ("%<constinit%> can only be applied to a variable with static "
+                "or thread storage duration");
+         return;
+       }
+
       /* If this is a local variable that will need a mangled name,
         register it now.  We must do this before processing the
         initializer for the variable, since the initialization might
@@ -10477,6 +10497,7 @@ grokdeclarator (const cp_declarator *declarator,
   bool template_parm_flag = false;
   bool typedef_p = decl_spec_seq_has_spec_p (declspecs, ds_typedef);
   bool constexpr_p = decl_spec_seq_has_spec_p (declspecs, ds_constexpr);
+  bool constinit_p = decl_spec_seq_has_spec_p (declspecs, ds_constinit);
   bool late_return_type_p = false;
   bool array_parameter_p = false;
   location_t saved_loc = input_location;
@@ -10763,6 +10784,24 @@ grokdeclarator (const cp_declarator *declarator,
       return error_mark_node;
     }
 
+  if (constinit_p && typedef_p)
+    {
+      error_at (declspecs->locations[ds_constinit],
+               "%<constinit%> cannot appear in a typedef declaration");
+      return error_mark_node;
+    }
+
+  /* [dcl.spec]/2 "At most one of the constexpr, consteval, and constinit
+     keywords shall appear in a decl-specifier-seq."  */
+  if (constinit_p && constexpr_p)
+    {
+      error_at (min_location (declspecs->locations[ds_constinit],
+                             declspecs->locations[ds_constexpr]),
+               "can use at most one of the %<constinit%> and %<constexpr%> "
+               "specifiers");
+      return error_mark_node;
+    }
+
   /* If there were multiple types specified in the decl-specifier-seq,
      issue an error message.  */
   if (declspecs->multiple_types_p)
@@ -11155,6 +11194,12 @@ grokdeclarator (const cp_declarator *declarator,
                    "a parameter cannot be declared %<constexpr%>");
           constexpr_p = 0;
         }
+      else if (constinit_p)
+       {
+         error_at (declspecs->locations[ds_constinit],
+                   "a parameter cannot be declared %<constinit%>");
+         constexpr_p = 0;
+       }
     }
 
   /* Give error if `virtual' is used outside of class declaration.  */
@@ -11597,6 +11642,13 @@ grokdeclarator (const cp_declarator *declarator,
                          "an array", name);
                return error_mark_node;
              }
+           if (constinit_p)
+             {
+               error_at (declspecs->locations[ds_constinit],
+                         "%<constinit%> on function return type is not "
+                         "allowed");
+               return error_mark_node;
+             }
 
            if (ctype == NULL_TREE
                && decl_context == FIELD
@@ -12794,10 +12846,17 @@ grokdeclarator (const cp_declarator *declarator,
                 else if (constexpr_p)
                  {
                    error_at (declspecs->locations[ds_constexpr],
-                             "non-static data member %qE declared %<constexpr%>",
-                             unqualified_id);
+                             "non-static data member %qE declared "
+                             "%<constexpr%>", unqualified_id);
                    constexpr_p = false;
                  }
+               else if (constinit_p)
+                 {
+                   error_at (declspecs->locations[ds_constinit],
+                             "non-static data member %qE declared "
+                             "%<constinit%>", unqualified_id);
+                   constinit_p = false;
+                 }
                decl = build_decl (id_loc, FIELD_DECL, unqualified_id, type);
                DECL_NONADDRESSABLE_P (decl) = bitfield;
                if (bitfield && !unqualified_id)
index 12567da39c4fb454606a7e6f0726e4a2753b4371..5b43723a8fac961978cb949fcdde9bef22892afd 100644 (file)
@@ -229,6 +229,8 @@ init_reswords (void)
 
   if (cxx_dialect < cxx11)
     mask |= D_CXX11;
+  if (cxx_dialect < cxx2a)
+    mask |= D_CXX20;
   if (!flag_concepts)
     mask |= D_CXX_CONCEPTS;
   if (!flag_tm)
index 382575320de53f89a6a1cd7c41acc8b104372421..93cdaddf52d51e86ca194e120fe77e258207ddc0 100644 (file)
@@ -834,14 +834,28 @@ cp_lexer_get_preprocessor_token (cp_lexer *lexer, cp_token *token)
             {
               /* Warn about the C++0x keyword (but still treat it as
                  an identifier).  */
-              warning (OPT_Wc__11_compat,
-                       "identifier %qE is a keyword in C++11",
-                       token->u.value);
+             warning_at (token->location, OPT_Wc__11_compat,
+                         "identifier %qE is a keyword in C++11",
+                         token->u.value);
 
               /* Clear out the C_RID_CODE so we don't warn about this
                  particular identifier-turned-keyword again.  */
               C_SET_RID_CODE (token->u.value, RID_MAX);
             }
+         if (warn_cxx20_compat
+             && C_RID_CODE (token->u.value) >= RID_FIRST_CXX20
+             && C_RID_CODE (token->u.value) <= RID_LAST_CXX20)
+           {
+             /* Warn about the C++20 keyword (but still treat it as
+                an identifier).  */
+             warning_at (token->location, OPT_Wc__20_compat,
+                         "identifier %qE is a keyword in C++20",
+                         token->u.value);
+
+             /* Clear out the C_RID_CODE so we don't warn about this
+                particular identifier-turned-keyword again.  */
+             C_SET_RID_CODE (token->u.value, RID_MAX);
+           }
 
          token->keyword = RID_MAX;
        }
@@ -986,6 +1000,7 @@ cp_keyword_starts_decl_specifier_p (enum rid keyword)
     case RID_DECLTYPE:
     case RID_UNDERLYING_TYPE:
     case RID_CONSTEXPR:
+    case RID_CONSTINIT:
       return true;
 
     default:
@@ -3353,6 +3368,9 @@ cp_parser_diagnose_invalid_type_name (cp_parser *parser, tree id,
               && id_equal (id, "thread_local"))
        inform (location, "C++11 %<thread_local%> only available with "
                "%<-std=c++11%> or %<-std=gnu++11%>");
+      else if (cxx_dialect < cxx2a && id == ridpointers[(int)RID_CONSTINIT])
+       inform (location, "C++20 %<constinit%> only available with "
+               "%<-std=c++2a%> or %<-std=gnu++2a%>");
       else if (!flag_concepts && id == ridpointers[(int)RID_CONCEPT])
        inform (location, "%<concept%> only available with %<-fconcepts%>");
       else if (processing_template_decl && current_class_type
@@ -13839,9 +13857,12 @@ cp_parser_decomposition_declaration (cp_parser *parser,
 
       if (decl != error_mark_node)
        {
+         int flags = (decl_spec_seq_has_spec_p (decl_specifiers, ds_constinit)
+                      ? LOOKUP_CONSTINIT : 0);
          cp_maybe_mangle_decomp (decl, prev, v.length ());
          cp_finish_decl (decl, initializer, non_constant_p, NULL_TREE,
-                         is_direct_init ? LOOKUP_NORMAL : LOOKUP_IMPLICIT);
+                         (is_direct_init ? LOOKUP_NORMAL : LOOKUP_IMPLICIT)
+                         | flags);
          cp_finish_decomp (decl, prev, v.length ());
        }
     }
@@ -13993,7 +14014,8 @@ cp_parser_decl_specifier_seq (cp_parser* parser,
        {
          /* decl-specifier:
               friend
-               constexpr */
+              constexpr
+              constinit */
        case RID_FRIEND:
          if (!at_class_scope_p ())
            {
@@ -14015,6 +14037,11 @@ cp_parser_decl_specifier_seq (cp_parser* parser,
           cp_lexer_consume_token (parser->lexer);
           break;
 
+       case RID_CONSTINIT:
+         ds = ds_constinit;
+         cp_lexer_consume_token (parser->lexer);
+         break;
+
         case RID_CONCEPT:
           ds = ds_concept;
           cp_lexer_consume_token (parser->lexer);
@@ -20532,6 +20559,8 @@ cp_parser_init_declarator (cp_parser* parser,
      declarations.  */
   if (!member_p && decl && decl != error_mark_node && !range_for_decl_p)
     {
+      int cf = (decl_spec_seq_has_spec_p (decl_specifiers, ds_constinit)
+               ? LOOKUP_CONSTINIT : 0);
       cp_finish_decl (decl,
                      initializer, !is_non_constant_init,
                      asm_specification,
@@ -20540,7 +20569,7 @@ cp_parser_init_declarator (cp_parser* parser,
                         `explicit' constructor is OK.  Otherwise, an
                         `explicit' constructor cannot be used.  */
                      ((is_direct_init || !is_initialized)
-                      ? LOOKUP_NORMAL : LOOKUP_IMPLICIT));
+                      ? LOOKUP_NORMAL : LOOKUP_IMPLICIT) | cf);
     }
   else if ((cxx_dialect != cxx98) && friend_p
           && decl && TREE_CODE (decl) == FUNCTION_DECL)
@@ -29470,7 +29499,8 @@ set_and_check_decl_spec_loc (cp_decl_specifier_seq *decl_specs,
            "typedef",
            "using",
             "constexpr",
-           "__complex"
+           "__complex",
+           "constinit"
          };
          gcc_rich_location richloc (location);
          richloc.add_fixit_remove ();
index 17585119bceb2e6416b548676e07059aa911a208..54d36f91ddccd84cfc40c40f800f6d150aaa5c58 100644 (file)
@@ -13955,6 +13955,10 @@ tsubst_decl (tree t, tree args, tsubst_flags_t complain)
 
            DECL_TEMPLATE_INFO (r) = build_template_info (tmpl, argvec);
            SET_DECL_IMPLICIT_INSTANTIATION (r);
+           /* Remember whether we require constant initialization of
+              a non-constant template variable.  */
+           TINFO_VAR_DECLARED_CONSTINIT (DECL_TEMPLATE_INFO (r))
+             = TINFO_VAR_DECLARED_CONSTINIT (DECL_TEMPLATE_INFO (t));
            if (!error_operand_p (r) || (complain & tf_error))
              register_specialization (r, gen_tmpl, argvec, false, hash);
          }
@@ -24744,7 +24748,9 @@ instantiate_decl (tree d, bool defer_ok, bool expl_inst_class_mem_p)
         push_nested_class (DECL_CONTEXT (d));
 
       const_init = DECL_INITIALIZED_BY_CONSTANT_EXPRESSION_P (code_pattern);
-      cp_finish_decl (d, init, const_init, NULL_TREE, 0);
+      int flags = (TINFO_VAR_DECLARED_CONSTINIT (DECL_TEMPLATE_INFO (d))
+                  ? LOOKUP_CONSTINIT : 0);
+      cp_finish_decl (d, init, const_init, NULL_TREE, flags);
 
       if (enter_context)
         pop_nested_class ();
index 02c3ad5efb096bed95522fd9fb596bd48ed752a4..d5098fa24bb465ce70d95eae82bc62f16d4b2a0d 100644 (file)
@@ -885,7 +885,22 @@ store_init_value (tree decl, tree init, vec<tree, va_gc>** cleanups, int flags)
       if (!TYPE_REF_P (type))
        TREE_CONSTANT (decl) = const_init && decl_maybe_constant_var_p (decl);
       if (!const_init)
-       value = oldval;
+       {
+         /* [dcl.constinit]/2 "If a variable declared with the constinit
+            specifier has dynamic initialization, the program is
+            ill-formed."  */
+         if (flags & LOOKUP_CONSTINIT)
+           {
+             error_at (location_of (decl),
+                       "%<constinit%> variable %qD does not have a constant "
+                       "initializer", decl);
+             if (require_constant_expression (value))
+               cxx_constant_init (value, decl);
+             value = error_mark_node;
+           }
+         else
+           value = oldval;
+       }
     }
   value = cp_fully_fold_init (value);
 
index 549e043c67ca2067cbfc58055fe106e491425403..1391a562c352f44f86715c52572963eab7759641 100644 (file)
@@ -295,6 +295,7 @@ Objective-C and Objective-C++ Dialects}.
 -Wno-builtin-declaration-mismatch @gol
 -Wno-builtin-macro-redefined  -Wc90-c99-compat  -Wc99-c11-compat @gol
 -Wc++-compat  -Wc++11-compat  -Wc++14-compat  -Wc++17-compat  @gol
+-Wc++20-compat  @gol
 -Wcast-align  -Wcast-align=strict  -Wcast-function-type  -Wcast-qual  @gol
 -Wchar-subscripts  -Wcatch-value  -Wcatch-value=@var{n} @gol
 -Wclobbered  -Wcomment  -Wconditionally-supported @gol
@@ -6792,6 +6793,12 @@ and ISO C++ 2014.  This warning is enabled by @option{-Wall}.
 Warn about C++ constructs whose meaning differs between ISO C++ 2014
 and ISO C++ 2017.  This warning is enabled by @option{-Wall}.
 
+@item -Wc++20-compat @r{(C++ and Objective-C++ only)}
+@opindex Wc++20-compat
+@opindex Wno-c++20-compat
+Warn about C++ constructs whose meaning differs between ISO C++ 2017
+and ISO C++ 2020.  This warning is enabled by @option{-Wall}.
+
 @item -Wcast-qual
 @opindex Wcast-qual
 @opindex Wno-cast-qual
index 7dc95f323ec2a66499806bade5703a1682887f57..77fa37f75b827bc8417f6cc33b75f99bb81a9e22 100644 (file)
@@ -1,3 +1,19 @@
+2019-08-28  Marek Polacek  <polacek@redhat.com>
+
+       PR c++/91360 - Implement C++20 P1143R2: constinit.
+       * g++.dg/cpp2a/constinit1.C: New test.
+       * g++.dg/cpp2a/constinit2.C: New test.
+       * g++.dg/cpp2a/constinit3.C: New test.
+       * g++.dg/cpp2a/constinit4.C: New test.
+       * g++.dg/cpp2a/constinit5.C: New test.
+       * g++.dg/cpp2a/constinit6.C: New test.
+       * g++.dg/cpp2a/constinit7.C: New test.
+       * g++.dg/cpp2a/constinit8.C: New test.
+       * g++.dg/cpp2a/constinit9.C: New test.
+       * g++.dg/cpp2a/constinit10.C: New test.
+       * g++.dg/cpp2a/constinit11.C: New test.
+       * g++.dg/cpp2a/constinit12.C: New test.
+
 2019-08-28  Steven G. Kargl  <kargl@gcc.gnu.org>
 
        PR fortran/91565
diff --git a/gcc/testsuite/g++.dg/cpp2a/constinit1.C b/gcc/testsuite/g++.dg/cpp2a/constinit1.C
new file mode 100644 (file)
index 0000000..9d1c028
--- /dev/null
@@ -0,0 +1,38 @@
+// PR c++/91360 - Implement C++20 P1143R2: constinit
+// { dg-do compile { target c++2a } }
+// Test basic usage of 'constinit'.
+
+const char *g() { return "dynamic init"; }
+constexpr const char *f(bool p) { return p ? "constant init" : g(); } // { dg-error "call to non-.constexpr. function" }
+
+constinit const char *c = f(true);
+constinit const char *d = f(false); // { dg-error "variable .d. does not have a constant initializer" }
+// { dg-message "in .constexpr. expansion of" "" { target *-*-* } .-1 }
+static constinit const char *e = f(true);
+
+constexpr int foo(int x) { return x; }
+constinit int i = foo(42);
+constinit int j // { dg-error "variable .j. does not have a constant initializer" }
+  = foo(i); // { dg-error "not usable in a constant expression" }
+
+int y = 42;
+constinit int x // { dg-error "variable .x. does not have a constant initializer" }
+  = y; // { dg-error "not usable in a constant expression" }
+
+constinit int z;
+const constinit unsigned cst = 1u;
+
+void
+fn ()
+{
+  static constinit int m = foo(42);
+  static constinit int n // { dg-error "variable .n. does not have a constant initializer" }
+    = foo(m); // { dg-error "not usable in a constant expression" }
+
+  // Make sure we can still modify constinit variables.
+  c = "foo";
+  i = 10;
+  m = 90;
+  // ... unless they're 'const'.
+  cst *= 2; // { dg-error "assignment of read-only variable" }
+}
diff --git a/gcc/testsuite/g++.dg/cpp2a/constinit10.C b/gcc/testsuite/g++.dg/cpp2a/constinit10.C
new file mode 100644 (file)
index 0000000..a50f285
--- /dev/null
@@ -0,0 +1,26 @@
+// PR c++/91360 - Implement C++20 P1143R2: constinit
+// { dg-do compile { target c++2a } }
+// From PR83428.
+
+struct S1
+{
+    constexpr S1 ();
+    int m_i;
+};
+
+struct alignas(64) S2
+{
+    constexpr S2 ()
+    : m_tabS1()
+    {}
+
+    S1 m_tabS1[7];
+};
+
+constinit S2 objX; // { dg-error ".constinit. variable .objX. does not have a constant initializer" }
+// { dg-error "used before its definition" "" { target *-*-* } .-1 }
+// // { dg-message "in .constexpr. expansion of" "" { target *-*-* } .-2 }
+
+constexpr S1::S1 ()
+: m_i(14)
+{}
diff --git a/gcc/testsuite/g++.dg/cpp2a/constinit11.C b/gcc/testsuite/g++.dg/cpp2a/constinit11.C
new file mode 100644 (file)
index 0000000..ab3715b
--- /dev/null
@@ -0,0 +1,79 @@
+// PR c++/91360 - Implement C++20 P1143R2: constinit
+// { dg-do compile { target c++2a } }
+
+int foo ();
+constexpr int constfoo () { return 42; }
+int gl = 42;
+
+struct nonliteral {
+  int m;
+  nonliteral() : m() { }
+  nonliteral(int n) : m(n) { }
+  ~nonliteral() {}
+};
+
+struct literal {
+  int m;
+  constexpr literal() : m() { }
+  constexpr literal(int n) : m(n) { }
+};
+
+struct pod {
+  int m;
+};
+
+struct S {
+  static constinit pod p;
+  static constinit pod pc;
+  static const constinit nonliteral n;
+};
+
+struct W {
+  int w = 42;
+};
+
+constinit W w;
+
+constinit const int &r1 = gl;
+constinit thread_local const int &r2 = gl;
+constinit const int &r3 // { dg-error "variable .r3. does not have a constant initializer" }
+  = foo (); // { dg-error "call to non-.constexpr. function" }
+constinit const literal &r4 = 42;
+constinit const nonliteral &r5 // { dg-error "variable .r5. does not have a constant initializer" }
+  = 42; // { dg-error "call to non-.constexpr. function" }
+constinit const int &r6 = nonliteral(2).m; // { dg-error "variable .r6. does not have a constant initializer|call to non-.constexpr. function" }
+
+constinit pod p1;
+constinit pod p2 = { 42 };
+constinit pod p3 = { constfoo() };
+constinit pod p4 = { foo() }; // { dg-error "variable .p4. does not have a constant initializer|call to non-.constexpr. function" }
+
+constexpr literal lit;
+constinit literal l1 = lit;
+constinit literal l2 = 42;
+constinit literal l3 = constfoo();
+constinit literal l4 = foo(); // { dg-error "variable .l4. does not have a constant initializer|call to non-.constexpr. function" }
+constinit literal l5 = {};
+constinit literal l6{};
+constinit thread_local literal l7 = lit;
+constinit thread_local literal l8 = 42;
+constinit thread_local literal l9 = constfoo();
+constinit thread_local literal l10 = foo(); // { dg-error "variable .l10. does not have a constant initializer|call to non-.constexpr. function" }
+constinit thread_local literal l11{};
+
+pod S::p;
+constinit pod S::pc(S::p); // { dg-error "variable .S::pc. does not have a constant initializer|not usable" }
+
+constinit const nonliteral S::n(42); // { dg-error "variable .S::n. does not have a constant initializer|call to non-.constexpr. function" }
+constinit int n1 = nonliteral{42}.m; // { dg-error "variable .n1. does not have a constant initializer|temporary of non-literal type" }
+constinit int n2 = literal{42}.m;
+
+void
+fn1 ()
+{
+  const int c = 42;
+  static constinit const int &l // { dg-error "variable .l. does not have a constant initializer" }
+    = c; // { dg-error "not a constant" }
+  static const int &l2 = 10;
+  static const int &l3 = gl;
+}
diff --git a/gcc/testsuite/g++.dg/cpp2a/constinit12.C b/gcc/testsuite/g++.dg/cpp2a/constinit12.C
new file mode 100644 (file)
index 0000000..b5b736f
--- /dev/null
@@ -0,0 +1,14 @@
+// PR c++/91360 - Implement C++20 P1143R2: constinit
+// { dg-do compile { target c++2a } }
+
+struct S {
+  S(int) { }
+};
+
+template <class T>
+struct U {
+  T m;
+  constexpr U(int i) : m(i) { } // { dg-error "call to non-.constexpr. function" }
+};
+
+constinit U<S> u(42); // { dg-error "does not have a constant initializer|called in a constant expression" }
diff --git a/gcc/testsuite/g++.dg/cpp2a/constinit2.C b/gcc/testsuite/g++.dg/cpp2a/constinit2.C
new file mode 100644 (file)
index 0000000..3e9f578
--- /dev/null
@@ -0,0 +1,14 @@
+// PR c++/91360 - Implement C++20 P1143R2: constinit
+// { dg-do compile { target c++11 } }
+// Test that 'constinit' isn't recognized pre-C++2a, but '__constinit' is.
+
+constinit int g = 42; // { dg-error ".constinit. does not name a type" "" { target c++17_down } }
+__constinit int g2 = 42;
+static __constinit int g3 = 42;
+
+void
+fn ()
+{
+  static constinit int x = 69; // { dg-error ".constinit. does not name a type" "" { target c++17_down } }
+  static __constinit int x2 = 69;
+}
diff --git a/gcc/testsuite/g++.dg/cpp2a/constinit3.C b/gcc/testsuite/g++.dg/cpp2a/constinit3.C
new file mode 100644 (file)
index 0000000..316937e
--- /dev/null
@@ -0,0 +1,58 @@
+// PR c++/91360 - Implement C++20 P1143R2: constinit
+// { dg-do compile { target c++2a } }
+
+constinit constinit int v1; // { dg-error "duplicate .constinit." }
+constexpr constinit int v2 = 1; // { dg-error "can use at most one of the .constinit. and .constexpr. specifiers" }
+constinit constexpr int v3 = 1; // { dg-error "an use at most one of the .constinit. and .constexpr. specifiers" }
+
+extern static constinit int v4; // { dg-error "conflicting specifiers" }
+extern thread_local constinit int v5;
+extern constinit int v6;
+
+constinit typedef int T; // { dg-error ".constinit. cannot appear in a typedef declaration" }
+
+struct S2 {
+  constinit int m1; // { dg-error "non-static data member .m1. declared .constinit." }
+  constinit unsigned int b : 32; // { dg-error " non-static data member .b. declared .constinit." }
+};
+
+struct S3 {
+  constinit S3() {} // { dg-error ".constinit. on function return type is not allowed" }
+  constinit ~S3() {} // { dg-error ".constinit. on function return type is not allowed" }
+};
+
+constinit struct S4 { // { dg-error ".constinit. cannot be used for type declarations" }
+};
+
+template<constinit int I> // { dg-error "a parameter cannot be declared .constinit." }
+struct X { };
+
+int
+fn1 ()
+{
+  // Not static storage
+  constinit int a1 = 42; // { dg-error ".constinit. can only be applied to a variable with static or thread storage" }
+  constinit int a2 = 42; // { dg-error ".constinit. can only be applied to a variable with static or thread storage" }
+  extern constinit int e1;
+
+  return 0;
+}
+
+constinit int // { dg-error ".constinit. on function return type is not allowed" }
+fn3 ()
+{
+}
+
+void
+fn2 (int i, constinit int p) // { dg-error "a parameter cannot be declared .constinit." }
+{
+  constinit auto l = [i](){ return i; }; // { dg-error ".constinit. can only be applied to a variable with static or thread storage" }
+}
+
+struct B { int d; };
+
+void
+fn3 (B b)
+{
+  constinit auto [ a ] = b; // { dg-error ".constinit. can only be applied to a variable with static or thread storage" }
+}
diff --git a/gcc/testsuite/g++.dg/cpp2a/constinit4.C b/gcc/testsuite/g++.dg/cpp2a/constinit4.C
new file mode 100644 (file)
index 0000000..748a7ff
--- /dev/null
@@ -0,0 +1,16 @@
+// PR c++/91360 - Implement C++20 P1143R2: constinit
+// { dg-do compile { target c++2a } }
+
+struct S { };
+constinit extern S s;
+constinit S s2 = { };
+
+struct T {
+  int i;
+};
+
+constinit T t;
+struct U : T {
+  int j;
+};
+constinit U u;
diff --git a/gcc/testsuite/g++.dg/cpp2a/constinit5.C b/gcc/testsuite/g++.dg/cpp2a/constinit5.C
new file mode 100644 (file)
index 0000000..3d21f48
--- /dev/null
@@ -0,0 +1,27 @@
+// PR c++/91360 - Implement C++20 P1143R2: constinit
+// { dg-do compile { target c++2a } }
+// Check that we preserve DECL_DECLARED_CONSTINIT_P in duplicate_decls.
+
+int gl = 42;
+
+struct S {
+  constinit static int m;
+  constinit static int n;
+  constinit static const int &r1;
+  constinit static const int &r2;
+};
+
+int S::m = 42;
+int nonconst;
+constinit int S::n = nonconst; // { dg-error "variable .S::n. does not have a constant initializer" }
+// { dg-error "not usable in a constant expression" "" { target *-*-* } .-1 }
+
+const int &S::r1 = gl;
+const int &S::r2 = 42;
+
+struct T {
+  constinit static thread_local const int &r1;
+  constinit static thread_local const int &r2;
+};
+constinit thread_local const int &T::r1 = gl;
+constinit thread_local const int &T::r2 = 42; // { dg-error "variable .T::r2. does not have a constant initializer|not a constant expression" }
diff --git a/gcc/testsuite/g++.dg/cpp2a/constinit6.C b/gcc/testsuite/g++.dg/cpp2a/constinit6.C
new file mode 100644 (file)
index 0000000..73e7883
--- /dev/null
@@ -0,0 +1,5 @@
+// PR c++/91360 - Implement C++20 P1143R2: constinit
+// { dg-do compile { target c++17_down } }
+// { dg-options "-Wc++20-compat" }
+
+int constinit; // { dg-warning "identifier .constinit. is a keyword" }
diff --git a/gcc/testsuite/g++.dg/cpp2a/constinit7.C b/gcc/testsuite/g++.dg/cpp2a/constinit7.C
new file mode 100644 (file)
index 0000000..e9a0da3
--- /dev/null
@@ -0,0 +1,11 @@
+// PR c++/91360 - Implement C++20 P1143R2: constinit
+// { dg-do compile { target c++11 } }
+
+struct T {
+  constexpr T(int) {}
+  ~T();
+};
+__constinit T x = { 42 };
+// ??? This should be rejected in C++14: copy initialization is not a constant
+// expression on a non-literal type in C++14.  But 'constinit' is C++20 only.
+__constinit T y = 42;
diff --git a/gcc/testsuite/g++.dg/cpp2a/constinit8.C b/gcc/testsuite/g++.dg/cpp2a/constinit8.C
new file mode 100644 (file)
index 0000000..c6b2975
--- /dev/null
@@ -0,0 +1,18 @@
+// PR c++/91360 - Implement C++20 P1143R2: constinit
+// { dg-do compile { target c++2a } }
+// Variable templates.
+
+int nonconst;
+
+template<typename T>
+constinit T v1 = 42;
+
+template<typename T>
+constinit T v2 = nonconst; // { dg-error "does not have a constant initializer|not usable" }
+
+void
+fn ()
+{
+  v1<int>;
+  v2<int>;
+}
diff --git a/gcc/testsuite/g++.dg/cpp2a/constinit9.C b/gcc/testsuite/g++.dg/cpp2a/constinit9.C
new file mode 100644 (file)
index 0000000..4c7f892
--- /dev/null
@@ -0,0 +1,24 @@
+// PR c++/91360 - Implement C++20 P1143R2: constinit
+// { dg-do run { target c++2a } }
+// A run-time test.
+
+constexpr int foo (int x) { return x; }
+constinit int b = foo(42);
+
+int
+main ()
+{
+  if (b != 42)
+    __builtin_abort ();
+  // We can still modify 'b'.
+  b = 10;
+  if (b != 10)
+    __builtin_abort ();
+
+  constinit static int s = foo(14);
+  if (s != 14)
+    __builtin_abort ();
+  s++;
+  if (s != 15)
+    __builtin_abort ();
+}