* C++ PATCH to implement C++20 P1143R2, constinit (PR c++/91360)
@ 2019-08-14 21:37 Marek Polacek
2019-08-23 23:13 ` Jason Merrill
2019-08-27 22:00 ` Paolo Carlini
0 siblings, 2 replies; 7+ messages in thread
From: Marek Polacek @ 2019-08-14 21:37 UTC (permalink / raw)
To: GCC Patches, Jason Merrill
This patch implements the C++20 specifier constinit, as described in
<http://wg21.link/p1143r2>. It makes sure that the compiler requires constant
initialization of a variable (it can only be applied to variables with static
or thread storage duration). Note the variable is *not* const; it is posssible
to modify it even after initialization has taken place.
The main bits are in store_init_value. The c-family/ and parser.c bits
handle parsing this new decl-specifier. decl.c is sprinkled with various
checks, e.g. you can't have a parameter declared "constinit".
It was also needed to add the -Wc++20-compat option to warn when "constinit"
was used as an identifier. Note that I am a bit presumptuous about C++20 here,
perhaps I should be only adding -Wc++2a-compat at this point.
As an extension, I'm also adding the __constinit keyword, so that you can
use this feature in C++17 and lesser, effectively supplanting the clang
require_constant_initialization attribute.
I spent significant time writing the tests (I stol^Wborrowed a couple of
reference-related tests from clang, I'll cop to that).
Here's an example of the new diagnostic. For
int nonconst;
constinit int i = nonconst;
we now issue:
q.C:2:15: error: âconstinitâ variable âiâ does not have a constant initializer
2 | constinit int i = nonconst;
| ^
q.C:2:19: error: the value of ânonconstâ is not usable in a constant expression
2 | constinit int i = nonconst;
| ^~~~~~~~
q.C:1:5: note: âint nonconstâ is not const
1 | int nonconst;
| ^~~~~~~~
I'm not crazy about the two errors, but I felt that revamping the diagnostics
would be out of scope for this patch.
Bootstrapped/regtested on x86_64-linux, ok for trunk?
2019-08-14 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.
* cp-tree.h (DECL_DECLARED_CONSTINIT_P): Define.
(enum cp_decl_spec): Add ds_constinit.
* decl.c (duplicate_decls): Set DECL_DECLARED_CONSTINIT_P.
(check_tag_decl): Give an error for constinit in type declarations.
(check_initializer): Also check DECL_DECLARED_CONSTINIT_P.
(cp_finish_decl): Add checking for a constinit declaration.
(grokdeclarator): Add checking for a declaration with the constinit
specifier. Set DECL_DECLARED_CONSTINIT_P for VAR_Ps.
* 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_decl_specifier_seq): Handle RID_CONSTINIT.
(set_and_check_decl_spec_loc): Add "constinit".
* 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.
diff --git gcc/c-family/c-common.c gcc/c-family/c-common.c
index 610cb905814..eb0f1ba9993 100644
--- gcc/c-family/c-common.c
+++ gcc/c-family/c-common.c
@@ -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 },
@@ -7912,6 +7915,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;
diff --git gcc/c-family/c-common.h gcc/c-family/c-common.h
index 117d729091a..17bd7b1c7d8 100644
--- gcc/c-family/c-common.h
+++ gcc/c-family/c-common.h
@@ -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
diff --git gcc/c-family/c-cppbuiltin.c gcc/c-family/c-cppbuiltin.c
index d389f8ca4a0..a08b5918209 100644
--- gcc/c-family/c-cppbuiltin.c
+++ gcc/c-family/c-cppbuiltin.c
@@ -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");
}
diff --git gcc/c-family/c-format.c gcc/c-family/c-format.c
index 6b059969e67..91bae3d6096 100644
--- gcc/c-family/c-format.c
+++ gcc/c-family/c-format.c
@@ -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),
diff --git gcc/c-family/c.opt gcc/c-family/c.opt
index 257cadfa5f1..4c468d0f6c2 100644
--- gcc/c-family/c.opt
+++ gcc/c-family/c.opt
@@ -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.
diff --git gcc/cp/cp-tree.h gcc/cp/cp-tree.h
index bdb7778c04b..4b4070fdb8e 100644
--- gcc/cp/cp-tree.h
+++ gcc/cp/cp-tree.h
@@ -489,6 +489,7 @@ extern GTY(()) tree cp_global_trees[CPTI_MAX];
DECL_MUTABLE_P (in FIELD_DECL)
DECL_DEPENDENT_P (in USING_DECL)
LABEL_DECL_BREAK (in LABEL_DECL)
+ DECL_DECLARED_CONSTINIT_P (in VAR_DECL)
1: C_TYPEDEF_EXPLICITLY_SIGNED (in TYPE_DECL).
DECL_TEMPLATE_INSTANTIATED (in a VAR_DECL or a FUNCTION_DECL)
DECL_MEMBER_TEMPLATE_P (in TEMPLATE_DECL)
@@ -3162,6 +3163,10 @@ struct GTY(()) lang_decl {
#define DECL_DECLARED_CONSTEXPR_P(DECL) \
DECL_LANG_FLAG_8 (VAR_OR_FUNCTION_DECL_CHECK (STRIP_TEMPLATE (DECL)))
+/* True if DECL is declared 'constinit'. */
+#define DECL_DECLARED_CONSTINIT_P(DECL) \
+ DECL_LANG_FLAG_0 (VAR_DECL_CHECK (STRIP_TEMPLATE (DECL)))
+
// True if NODE was declared as 'concept'. The flag implies that the
// declaration is constexpr, that the declaration cannot be specialized or
// refined, and that the result type must be convertible to bool.
@@ -5815,6 +5820,7 @@ enum cp_decl_spec {
ds_alias,
ds_constexpr,
ds_complex,
+ ds_constinit,
ds_thread,
ds_type_spec,
ds_redefined_builtin_type_spec,
diff --git gcc/cp/decl.c gcc/cp/decl.c
index ff3b90dba54..943f338696c 100644
--- gcc/cp/decl.c
+++ gcc/cp/decl.c
@@ -2205,8 +2205,12 @@ duplicate_decls (tree newdecl, tree olddecl, bool newdecl_is_friend)
DECL_INITIALIZED_BY_CONSTANT_EXPRESSION_P (newdecl)
|= DECL_INITIALIZED_BY_CONSTANT_EXPRESSION_P (olddecl);
if (DECL_CLASS_SCOPE_P (olddecl))
- DECL_DECLARED_CONSTEXPR_P (newdecl)
- |= DECL_DECLARED_CONSTEXPR_P (olddecl);
+ {
+ DECL_DECLARED_CONSTEXPR_P (newdecl)
+ |= DECL_DECLARED_CONSTEXPR_P (olddecl);
+ DECL_DECLARED_CONSTINIT_P (newdecl)
+ |= DECL_DECLARED_CONSTINIT_P (olddecl);
+ }
/* Merge the threadprivate attribute from OLDDECL into NEWDECL. */
if (DECL_LANG_SPECIFIC (olddecl)
@@ -4963,6 +4967,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)
@@ -6596,11 +6603,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)
+ || DECL_DECLARED_CONSTINIT_P (decl))
{
- /* 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))
{
@@ -7254,6 +7262,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 (DECL_DECLARED_CONSTINIT_P (decl)
+ && !(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
@@ -10478,6 +10498,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;
@@ -10764,6 +10785,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. */
@@ -11595,6 +11640,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
@@ -12792,10 +12844,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)
@@ -13069,6 +13128,9 @@ grokdeclarator (const cp_declarator *declarator,
/* Set constexpr flag on vars (functions got it in grokfndecl). */
if (constexpr_p && VAR_P (decl))
DECL_DECLARED_CONSTEXPR_P (decl) = true;
+ /* And the constinit flag (which only applies to variables). */
+ else if (constinit_p && VAR_P (decl))
+ DECL_DECLARED_CONSTINIT_P (decl) = true;
/* Record constancy and volatility on the DECL itself . There's
no need to do this when processing a template; we'll do this
diff --git gcc/cp/lex.c gcc/cp/lex.c
index 12567da39c4..5b43723a8fa 100644
--- gcc/cp/lex.c
+++ gcc/cp/lex.c
@@ -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)
diff --git gcc/cp/parser.c gcc/cp/parser.c
index b56cc6924f4..d5587e2b1fc 100644
--- gcc/cp/parser.c
+++ gcc/cp/parser.c
@@ -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:
@@ -3355,6 +3370,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
@@ -14001,7 +14019,8 @@ cp_parser_decl_specifier_seq (cp_parser* parser,
{
/* decl-specifier:
friend
- constexpr */
+ constexpr
+ constinit */
case RID_FRIEND:
if (!at_class_scope_p ())
{
@@ -14023,6 +14042,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);
@@ -29510,7 +29534,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 ();
diff --git gcc/cp/typeck2.c gcc/cp/typeck2.c
index 02c3ad5efb0..da51b4232d9 100644
--- gcc/cp/typeck2.c
+++ gcc/cp/typeck2.c
@@ -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 (DECL_DECLARED_CONSTINIT_P (decl))
+ {
+ 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);
diff --git gcc/doc/invoke.texi gcc/doc/invoke.texi
index ca111792885..8782317db96 100644
--- gcc/doc/invoke.texi
+++ gcc/doc/invoke.texi
@@ -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
diff --git gcc/testsuite/g++.dg/cpp2a/constinit1.C gcc/testsuite/g++.dg/cpp2a/constinit1.C
new file mode 100644
index 00000000000..9d1c0289a62
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/constinit1.C
@@ -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 gcc/testsuite/g++.dg/cpp2a/constinit10.C gcc/testsuite/g++.dg/cpp2a/constinit10.C
new file mode 100644
index 00000000000..a50f285ecb1
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/constinit10.C
@@ -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 gcc/testsuite/g++.dg/cpp2a/constinit11.C gcc/testsuite/g++.dg/cpp2a/constinit11.C
new file mode 100644
index 00000000000..acdda73d1cf
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/constinit11.C
@@ -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;
+pod S::pc(S::p); // { dg-error "variable .S::pc. does not have a constant initializer|not usable" }
+
+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 gcc/testsuite/g++.dg/cpp2a/constinit12.C gcc/testsuite/g++.dg/cpp2a/constinit12.C
new file mode 100644
index 00000000000..b5b736f87c2
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/constinit12.C
@@ -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 gcc/testsuite/g++.dg/cpp2a/constinit2.C gcc/testsuite/g++.dg/cpp2a/constinit2.C
new file mode 100644
index 00000000000..3e9f578f8ca
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/constinit2.C
@@ -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 gcc/testsuite/g++.dg/cpp2a/constinit3.C gcc/testsuite/g++.dg/cpp2a/constinit3.C
new file mode 100644
index 00000000000..316937e5bf3
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/constinit3.C
@@ -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 gcc/testsuite/g++.dg/cpp2a/constinit4.C gcc/testsuite/g++.dg/cpp2a/constinit4.C
new file mode 100644
index 00000000000..748a7ffa3a9
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/constinit4.C
@@ -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 gcc/testsuite/g++.dg/cpp2a/constinit5.C gcc/testsuite/g++.dg/cpp2a/constinit5.C
new file mode 100644
index 00000000000..9832a561bf8
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/constinit5.C
@@ -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;
+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;
+};
+thread_local const int &T::r1 = gl;
+thread_local const int &T::r2 = 42; // { dg-error "variable .T::r2. does not have a constant initializer|not a constant expression" }
diff --git gcc/testsuite/g++.dg/cpp2a/constinit6.C gcc/testsuite/g++.dg/cpp2a/constinit6.C
new file mode 100644
index 00000000000..73e78832844
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/constinit6.C
@@ -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 gcc/testsuite/g++.dg/cpp2a/constinit7.C gcc/testsuite/g++.dg/cpp2a/constinit7.C
new file mode 100644
index 00000000000..e9a0da3f8c8
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/constinit7.C
@@ -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 gcc/testsuite/g++.dg/cpp2a/constinit8.C gcc/testsuite/g++.dg/cpp2a/constinit8.C
new file mode 100644
index 00000000000..c6b2975350c
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/constinit8.C
@@ -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 gcc/testsuite/g++.dg/cpp2a/constinit9.C gcc/testsuite/g++.dg/cpp2a/constinit9.C
new file mode 100644
index 00000000000..4c7f8925169
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/constinit9.C
@@ -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 ();
+}
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: C++ PATCH to implement C++20 P1143R2, constinit (PR c++/91360)
2019-08-14 21:37 C++ PATCH to implement C++20 P1143R2, constinit (PR c++/91360) Marek Polacek
@ 2019-08-23 23:13 ` Jason Merrill
2019-08-27 19:59 ` Marek Polacek
2019-08-27 22:00 ` Paolo Carlini
1 sibling, 1 reply; 7+ messages in thread
From: Jason Merrill @ 2019-08-23 23:13 UTC (permalink / raw)
To: Marek Polacek, GCC Patches
On 8/14/19 2:22 PM, Marek Polacek wrote:
> This patch implements the C++20 specifier constinit, as described in
> <http://wg21.link/p1143r2>. It makes sure that the compiler requires constant
> initialization of a variable (it can only be applied to variables with static
> or thread storage duration). Note the variable is *not* const; it is posssible
> to modify it even after initialization has taken place.
>
> The main bits are in store_init_value. The c-family/ and parser.c bits
> handle parsing this new decl-specifier. decl.c is sprinkled with various
> checks, e.g. you can't have a parameter declared "constinit".
>
> It was also needed to add the -Wc++20-compat option to warn when "constinit"
> was used as an identifier. Note that I am a bit presumptuous about C++20 here,
> perhaps I should be only adding -Wc++2a-compat at this point.
I think it's a good bet at this point. We can always remove the flag if
somehow it doesn't work out.
> As an extension, I'm also adding the __constinit keyword, so that you can
> use this feature in C++17 and lesser, effectively supplanting the clang
> require_constant_initialization attribute.
>
> I spent significant time writing the tests (I stol^Wborrowed a couple of
> reference-related tests from clang, I'll cop to that).
Yay free software!
> Here's an example of the new diagnostic. For
>
> int nonconst;
> constinit int i = nonconst;
>
> we now issue:
>
> q.C:2:15: error: âconstinitâ variable âiâ does not have a constant initializer
> 2 | constinit int i = nonconst;
> | ^
> q.C:2:19: error: the value of ânonconstâ is not usable in a constant expression
> 2 | constinit int i = nonconst;
> | ^~~~~~~~
> q.C:1:5: note: âint nonconstâ is not const
> 1 | int nonconst;
> | ^~~~~~~~
>
> I'm not crazy about the two errors, but I felt that revamping the diagnostics
> would be out of scope for this patch.
>
> Bootstrapped/regtested on x86_64-linux, ok for trunk?
>
> 2019-08-14 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.
>
> * cp-tree.h (DECL_DECLARED_CONSTINIT_P): Define.
> (enum cp_decl_spec): Add ds_constinit.
> * decl.c (duplicate_decls): Set DECL_DECLARED_CONSTINIT_P.
> (check_tag_decl): Give an error for constinit in type declarations.
> (check_initializer): Also check DECL_DECLARED_CONSTINIT_P.
> (cp_finish_decl): Add checking for a constinit declaration.
> (grokdeclarator): Add checking for a declaration with the constinit
> specifier. Set DECL_DECLARED_CONSTINIT_P for VAR_Ps.
> * 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_decl_specifier_seq): Handle RID_CONSTINIT.
> (set_and_check_decl_spec_loc): Add "constinit".
> * 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.
>
> diff --git gcc/c-family/c-common.c gcc/c-family/c-common.c
> index 610cb905814..eb0f1ba9993 100644
> --- gcc/c-family/c-common.c
> +++ gcc/c-family/c-common.c
> @@ -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 },
> @@ -7912,6 +7915,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;
> diff --git gcc/c-family/c-common.h gcc/c-family/c-common.h
> index 117d729091a..17bd7b1c7d8 100644
> --- gcc/c-family/c-common.h
> +++ gcc/c-family/c-common.h
> @@ -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
> diff --git gcc/c-family/c-cppbuiltin.c gcc/c-family/c-cppbuiltin.c
> index d389f8ca4a0..a08b5918209 100644
> --- gcc/c-family/c-cppbuiltin.c
> +++ gcc/c-family/c-cppbuiltin.c
> @@ -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");
> }
> diff --git gcc/c-family/c-format.c gcc/c-family/c-format.c
> index 6b059969e67..91bae3d6096 100644
> --- gcc/c-family/c-format.c
> +++ gcc/c-family/c-format.c
> @@ -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),
> diff --git gcc/c-family/c.opt gcc/c-family/c.opt
> index 257cadfa5f1..4c468d0f6c2 100644
> --- gcc/c-family/c.opt
> +++ gcc/c-family/c.opt
> @@ -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.
> diff --git gcc/cp/cp-tree.h gcc/cp/cp-tree.h
> index bdb7778c04b..4b4070fdb8e 100644
> --- gcc/cp/cp-tree.h
> +++ gcc/cp/cp-tree.h
> @@ -489,6 +489,7 @@ extern GTY(()) tree cp_global_trees[CPTI_MAX];
> DECL_MUTABLE_P (in FIELD_DECL)
> DECL_DEPENDENT_P (in USING_DECL)
> LABEL_DECL_BREAK (in LABEL_DECL)
> + DECL_DECLARED_CONSTINIT_P (in VAR_DECL)
> 1: C_TYPEDEF_EXPLICITLY_SIGNED (in TYPE_DECL).
> DECL_TEMPLATE_INSTANTIATED (in a VAR_DECL or a FUNCTION_DECL)
> DECL_MEMBER_TEMPLATE_P (in TEMPLATE_DECL)
> @@ -3162,6 +3163,10 @@ struct GTY(()) lang_decl {
> #define DECL_DECLARED_CONSTEXPR_P(DECL) \
> DECL_LANG_FLAG_8 (VAR_OR_FUNCTION_DECL_CHECK (STRIP_TEMPLATE (DECL)))
>
> +/* True if DECL is declared 'constinit'. */
> +#define DECL_DECLARED_CONSTINIT_P(DECL) \
> + DECL_LANG_FLAG_0 (VAR_DECL_CHECK (STRIP_TEMPLATE (DECL)))
Hmm, given that 'constinit' only affects the declaration, do we really
need a flag on the VAR_DECL?
> // True if NODE was declared as 'concept'. The flag implies that the
> // declaration is constexpr, that the declaration cannot be specialized or
> // refined, and that the result type must be convertible to bool.
> @@ -5815,6 +5820,7 @@ enum cp_decl_spec {
> ds_alias,
> ds_constexpr,
> ds_complex,
> + ds_constinit,
> ds_thread,
> ds_type_spec,
> ds_redefined_builtin_type_spec,
> diff --git gcc/cp/decl.c gcc/cp/decl.c
> index ff3b90dba54..943f338696c 100644
> --- gcc/cp/decl.c
> +++ gcc/cp/decl.c
> @@ -2205,8 +2205,12 @@ duplicate_decls (tree newdecl, tree olddecl, bool newdecl_is_friend)
> DECL_INITIALIZED_BY_CONSTANT_EXPRESSION_P (newdecl)
> |= DECL_INITIALIZED_BY_CONSTANT_EXPRESSION_P (olddecl);
> if (DECL_CLASS_SCOPE_P (olddecl))
> - DECL_DECLARED_CONSTEXPR_P (newdecl)
> - |= DECL_DECLARED_CONSTEXPR_P (olddecl);
> + {
> + DECL_DECLARED_CONSTEXPR_P (newdecl)
> + |= DECL_DECLARED_CONSTEXPR_P (olddecl);
> + DECL_DECLARED_CONSTINIT_P (newdecl)
> + |= DECL_DECLARED_CONSTINIT_P (olddecl);
> + }
Hmm, the existing code limiting the unification of constexpr to
class-scope variables seems wrong:
constexpr float pi = 3.14;
extern const float pi;
constexpr float x = pi; // should be OK
>
> /* Merge the threadprivate attribute from OLDDECL into NEWDECL. */
> if (DECL_LANG_SPECIFIC (olddecl)
> @@ -4963,6 +4967,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)
> @@ -6596,11 +6603,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)
> + || DECL_DECLARED_CONSTINIT_P (decl))
> {
> - /* 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))
> {
> @@ -7254,6 +7262,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 (DECL_DECLARED_CONSTINIT_P (decl)
> + && !(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
> @@ -10478,6 +10498,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;
> @@ -10764,6 +10785,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. */
> @@ -11595,6 +11640,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
> @@ -12792,10 +12844,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)
> @@ -13069,6 +13128,9 @@ grokdeclarator (const cp_declarator *declarator,
> /* Set constexpr flag on vars (functions got it in grokfndecl). */
> if (constexpr_p && VAR_P (decl))
> DECL_DECLARED_CONSTEXPR_P (decl) = true;
> + /* And the constinit flag (which only applies to variables). */
> + else if (constinit_p && VAR_P (decl))
> + DECL_DECLARED_CONSTINIT_P (decl) = true;
>
> /* Record constancy and volatility on the DECL itself . There's
> no need to do this when processing a template; we'll do this
> diff --git gcc/cp/lex.c gcc/cp/lex.c
> index 12567da39c4..5b43723a8fa 100644
> --- gcc/cp/lex.c
> +++ gcc/cp/lex.c
> @@ -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)
> diff --git gcc/cp/parser.c gcc/cp/parser.c
> index b56cc6924f4..d5587e2b1fc 100644
> --- gcc/cp/parser.c
> +++ gcc/cp/parser.c
> @@ -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:
> @@ -3355,6 +3370,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
> @@ -14001,7 +14019,8 @@ cp_parser_decl_specifier_seq (cp_parser* parser,
> {
> /* decl-specifier:
> friend
> - constexpr */
> + constexpr
> + constinit */
> case RID_FRIEND:
> if (!at_class_scope_p ())
> {
> @@ -14023,6 +14042,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);
> @@ -29510,7 +29534,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 ();
> diff --git gcc/cp/typeck2.c gcc/cp/typeck2.c
> index 02c3ad5efb0..da51b4232d9 100644
> --- gcc/cp/typeck2.c
> +++ gcc/cp/typeck2.c
> @@ -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 (DECL_DECLARED_CONSTINIT_P (decl))
> + {
> + 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);
>
> diff --git gcc/doc/invoke.texi gcc/doc/invoke.texi
> index ca111792885..8782317db96 100644
> --- gcc/doc/invoke.texi
> +++ gcc/doc/invoke.texi
> @@ -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
> diff --git gcc/testsuite/g++.dg/cpp2a/constinit1.C gcc/testsuite/g++.dg/cpp2a/constinit1.C
> new file mode 100644
> index 00000000000..9d1c0289a62
> --- /dev/null
> +++ gcc/testsuite/g++.dg/cpp2a/constinit1.C
> @@ -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 gcc/testsuite/g++.dg/cpp2a/constinit10.C gcc/testsuite/g++.dg/cpp2a/constinit10.C
> new file mode 100644
> index 00000000000..a50f285ecb1
> --- /dev/null
> +++ gcc/testsuite/g++.dg/cpp2a/constinit10.C
> @@ -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 gcc/testsuite/g++.dg/cpp2a/constinit11.C gcc/testsuite/g++.dg/cpp2a/constinit11.C
> new file mode 100644
> index 00000000000..acdda73d1cf
> --- /dev/null
> +++ gcc/testsuite/g++.dg/cpp2a/constinit11.C
> @@ -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;
> +pod S::pc(S::p); // { dg-error "variable .S::pc. does not have a constant initializer|not usable" }
> +
> +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 gcc/testsuite/g++.dg/cpp2a/constinit12.C gcc/testsuite/g++.dg/cpp2a/constinit12.C
> new file mode 100644
> index 00000000000..b5b736f87c2
> --- /dev/null
> +++ gcc/testsuite/g++.dg/cpp2a/constinit12.C
> @@ -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 gcc/testsuite/g++.dg/cpp2a/constinit2.C gcc/testsuite/g++.dg/cpp2a/constinit2.C
> new file mode 100644
> index 00000000000..3e9f578f8ca
> --- /dev/null
> +++ gcc/testsuite/g++.dg/cpp2a/constinit2.C
> @@ -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 gcc/testsuite/g++.dg/cpp2a/constinit3.C gcc/testsuite/g++.dg/cpp2a/constinit3.C
> new file mode 100644
> index 00000000000..316937e5bf3
> --- /dev/null
> +++ gcc/testsuite/g++.dg/cpp2a/constinit3.C
> @@ -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 gcc/testsuite/g++.dg/cpp2a/constinit4.C gcc/testsuite/g++.dg/cpp2a/constinit4.C
> new file mode 100644
> index 00000000000..748a7ffa3a9
> --- /dev/null
> +++ gcc/testsuite/g++.dg/cpp2a/constinit4.C
> @@ -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 gcc/testsuite/g++.dg/cpp2a/constinit5.C gcc/testsuite/g++.dg/cpp2a/constinit5.C
> new file mode 100644
> index 00000000000..9832a561bf8
> --- /dev/null
> +++ gcc/testsuite/g++.dg/cpp2a/constinit5.C
> @@ -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;
> +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;
> +};
> +thread_local const int &T::r1 = gl;
> +thread_local const int &T::r2 = 42; // { dg-error "variable .T::r2. does not have a constant initializer|not a constant expression" }
> diff --git gcc/testsuite/g++.dg/cpp2a/constinit6.C gcc/testsuite/g++.dg/cpp2a/constinit6.C
> new file mode 100644
> index 00000000000..73e78832844
> --- /dev/null
> +++ gcc/testsuite/g++.dg/cpp2a/constinit6.C
> @@ -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 gcc/testsuite/g++.dg/cpp2a/constinit7.C gcc/testsuite/g++.dg/cpp2a/constinit7.C
> new file mode 100644
> index 00000000000..e9a0da3f8c8
> --- /dev/null
> +++ gcc/testsuite/g++.dg/cpp2a/constinit7.C
> @@ -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 gcc/testsuite/g++.dg/cpp2a/constinit8.C gcc/testsuite/g++.dg/cpp2a/constinit8.C
> new file mode 100644
> index 00000000000..c6b2975350c
> --- /dev/null
> +++ gcc/testsuite/g++.dg/cpp2a/constinit8.C
> @@ -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 gcc/testsuite/g++.dg/cpp2a/constinit9.C gcc/testsuite/g++.dg/cpp2a/constinit9.C
> new file mode 100644
> index 00000000000..4c7f8925169
> --- /dev/null
> +++ gcc/testsuite/g++.dg/cpp2a/constinit9.C
> @@ -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 ();
> +}
>
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: C++ PATCH to implement C++20 P1143R2, constinit (PR c++/91360)
2019-08-23 23:13 ` Jason Merrill
@ 2019-08-27 19:59 ` Marek Polacek
2019-08-28 21:12 ` Jason Merrill
0 siblings, 1 reply; 7+ messages in thread
From: Marek Polacek @ 2019-08-27 19:59 UTC (permalink / raw)
To: Jason Merrill; +Cc: GCC Patches
On Fri, Aug 23, 2019 at 03:10:37PM -0700, Jason Merrill wrote:
> > +/* True if DECL is declared 'constinit'. */
> > +#define DECL_DECLARED_CONSTINIT_P(DECL) \
> > + DECL_LANG_FLAG_0 (VAR_DECL_CHECK (STRIP_TEMPLATE (DECL)))
>
> Hmm, given that 'constinit' only affects the declaration, do we really need
> a flag on the VAR_DECL?
I was able to do without DECL_DECLARED_CONSTINIT_P. To achieve that I
introduced LOOKUP_CONSTINIT (yes, it's an abomination; we should probably
extirpate those LOOKUP_ macros).
But that wasn't enough for variable templates, so I also had to introduce
TINFO_VAR_DECLARED_CONSTINIT. I suppose those DECL_TEMPLATE_INFO aren't
as precious as the VAR_DECL bits.
> Hmm, the existing code limiting the unification of constexpr to class-scope
> variables seems wrong:
>
> constexpr float pi = 3.14;
> extern const float pi;
> constexpr float x = pi; // should be OK
Thanks for fixing this meanwhile.
Is this any better?
Bootstrapped/regtested on x86_64-linux.
2019-08-27 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.
* 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.
diff --git gcc/c-family/c-common.c gcc/c-family/c-common.c
index d516deaf24c..17ca1e683a2 100644
--- gcc/c-family/c-common.c
+++ gcc/c-family/c-common.c
@@ -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 },
@@ -7916,6 +7919,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;
diff --git gcc/c-family/c-common.h gcc/c-family/c-common.h
index 117d729091a..17bd7b1c7d8 100644
--- gcc/c-family/c-common.h
+++ gcc/c-family/c-common.h
@@ -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
diff --git gcc/c-family/c-cppbuiltin.c gcc/c-family/c-cppbuiltin.c
index 6006e95b068..6b18246e0b6 100644
--- gcc/c-family/c-cppbuiltin.c
+++ gcc/c-family/c-cppbuiltin.c
@@ -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");
}
diff --git gcc/c-family/c-format.c gcc/c-family/c-format.c
index 6b059969e67..91bae3d6096 100644
--- gcc/c-family/c-format.c
+++ gcc/c-family/c-format.c
@@ -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),
diff --git gcc/c-family/c.opt gcc/c-family/c.opt
index 257cadfa5f1..4c468d0f6c2 100644
--- gcc/c-family/c.opt
+++ gcc/c-family/c.opt
@@ -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.
diff --git gcc/cp/cp-tree.h gcc/cp/cp-tree.h
index 42f180d1dd3..a4dc084333f 100644
--- gcc/cp/cp-tree.h
+++ gcc/cp/cp-tree.h
@@ -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,
diff --git gcc/cp/decl.c gcc/cp/decl.c
index 847817029e4..c5cc22a8d6d 100644
--- gcc/cp/decl.c
+++ gcc/cp/decl.c
@@ -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)
diff --git gcc/cp/lex.c gcc/cp/lex.c
index 12567da39c4..5b43723a8fa 100644
--- gcc/cp/lex.c
+++ gcc/cp/lex.c
@@ -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)
diff --git gcc/cp/parser.c gcc/cp/parser.c
index 382575320de..93cdaddf52d 100644
--- gcc/cp/parser.c
+++ gcc/cp/parser.c
@@ -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 ();
diff --git gcc/cp/pt.c gcc/cp/pt.c
index 17585119bce..54d36f91ddc 100644
--- gcc/cp/pt.c
+++ gcc/cp/pt.c
@@ -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 ();
diff --git gcc/cp/typeck2.c gcc/cp/typeck2.c
index 02c3ad5efb0..d5098fa24bb 100644
--- gcc/cp/typeck2.c
+++ gcc/cp/typeck2.c
@@ -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);
diff --git gcc/doc/invoke.texi gcc/doc/invoke.texi
index 549e043c67c..1391a562c35 100644
--- gcc/doc/invoke.texi
+++ gcc/doc/invoke.texi
@@ -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
diff --git gcc/testsuite/g++.dg/cpp2a/constinit1.C gcc/testsuite/g++.dg/cpp2a/constinit1.C
new file mode 100644
index 00000000000..9d1c0289a62
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/constinit1.C
@@ -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 gcc/testsuite/g++.dg/cpp2a/constinit10.C gcc/testsuite/g++.dg/cpp2a/constinit10.C
new file mode 100644
index 00000000000..a50f285ecb1
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/constinit10.C
@@ -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 gcc/testsuite/g++.dg/cpp2a/constinit11.C gcc/testsuite/g++.dg/cpp2a/constinit11.C
new file mode 100644
index 00000000000..ab3715b2064
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/constinit11.C
@@ -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 gcc/testsuite/g++.dg/cpp2a/constinit12.C gcc/testsuite/g++.dg/cpp2a/constinit12.C
new file mode 100644
index 00000000000..b5b736f87c2
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/constinit12.C
@@ -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 gcc/testsuite/g++.dg/cpp2a/constinit2.C gcc/testsuite/g++.dg/cpp2a/constinit2.C
new file mode 100644
index 00000000000..3e9f578f8ca
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/constinit2.C
@@ -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 gcc/testsuite/g++.dg/cpp2a/constinit3.C gcc/testsuite/g++.dg/cpp2a/constinit3.C
new file mode 100644
index 00000000000..316937e5bf3
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/constinit3.C
@@ -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 gcc/testsuite/g++.dg/cpp2a/constinit4.C gcc/testsuite/g++.dg/cpp2a/constinit4.C
new file mode 100644
index 00000000000..748a7ffa3a9
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/constinit4.C
@@ -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 gcc/testsuite/g++.dg/cpp2a/constinit5.C gcc/testsuite/g++.dg/cpp2a/constinit5.C
new file mode 100644
index 00000000000..3d21f48dbee
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/constinit5.C
@@ -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 gcc/testsuite/g++.dg/cpp2a/constinit6.C gcc/testsuite/g++.dg/cpp2a/constinit6.C
new file mode 100644
index 00000000000..73e78832844
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/constinit6.C
@@ -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 gcc/testsuite/g++.dg/cpp2a/constinit7.C gcc/testsuite/g++.dg/cpp2a/constinit7.C
new file mode 100644
index 00000000000..e9a0da3f8c8
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/constinit7.C
@@ -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 gcc/testsuite/g++.dg/cpp2a/constinit8.C gcc/testsuite/g++.dg/cpp2a/constinit8.C
new file mode 100644
index 00000000000..c6b2975350c
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/constinit8.C
@@ -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 gcc/testsuite/g++.dg/cpp2a/constinit9.C gcc/testsuite/g++.dg/cpp2a/constinit9.C
new file mode 100644
index 00000000000..4c7f8925169
--- /dev/null
+++ gcc/testsuite/g++.dg/cpp2a/constinit9.C
@@ -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 ();
+}
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: C++ PATCH to implement C++20 P1143R2, constinit (PR c++/91360)
2019-08-14 21:37 C++ PATCH to implement C++20 P1143R2, constinit (PR c++/91360) Marek Polacek
2019-08-23 23:13 ` Jason Merrill
@ 2019-08-27 22:00 ` Paolo Carlini
2019-08-27 22:34 ` Marek Polacek
1 sibling, 1 reply; 7+ messages in thread
From: Paolo Carlini @ 2019-08-27 22:00 UTC (permalink / raw)
To: Marek Polacek, GCC Patches, Jason Merrill
Hi,
On 14/08/19 23:22, Marek Polacek wrote:
> + /* [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");
For this error we also have the option of using a gcc_rich_location, and
add_range, etc, like for signed_p && unsigned_p, for example. Just
saying, since we have the infrastructure ready...
Paolo.
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: C++ PATCH to implement C++20 P1143R2, constinit (PR c++/91360)
2019-08-27 22:00 ` Paolo Carlini
@ 2019-08-27 22:34 ` Marek Polacek
0 siblings, 0 replies; 7+ messages in thread
From: Marek Polacek @ 2019-08-27 22:34 UTC (permalink / raw)
To: Paolo Carlini; +Cc: GCC Patches, Jason Merrill
On Tue, Aug 27, 2019 at 09:54:50PM +0200, Paolo Carlini wrote:
> Hi,
>
> On 14/08/19 23:22, Marek Polacek wrote:
> > + /* [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");
>
> For this error we also have the option of using a gcc_rich_location, and
> add_range, etc, like for signed_p && unsigned_p, for example. Just saying,
> since we have the infrastructure ready...
Happy to polish the diagnostic after the core bits are in ;-).
--
Marek Polacek ⢠Red Hat, Inc. ⢠300 A St, Boston, MA
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: C++ PATCH to implement C++20 P1143R2, constinit (PR c++/91360)
2019-08-27 19:59 ` Marek Polacek
@ 2019-08-28 21:12 ` Jason Merrill
2019-08-28 21:31 ` Marek Polacek
0 siblings, 1 reply; 7+ messages in thread
From: Jason Merrill @ 2019-08-28 21:12 UTC (permalink / raw)
To: Marek Polacek; +Cc: GCC Patches
On 8/27/19 11:24 AM, Marek Polacek wrote:
> On Fri, Aug 23, 2019 at 03:10:37PM -0700, Jason Merrill wrote:
>>> +/* True if DECL is declared 'constinit'. */
>>> +#define DECL_DECLARED_CONSTINIT_P(DECL) \
>>> + DECL_LANG_FLAG_0 (VAR_DECL_CHECK (STRIP_TEMPLATE (DECL)))
>>
>> Hmm, given that 'constinit' only affects the declaration, do we really need
>> a flag on the VAR_DECL?
>
> I was able to do without DECL_DECLARED_CONSTINIT_P. To achieve that I
> introduced LOOKUP_CONSTINIT (yes, it's an abomination; we should probably
> extirpate those LOOKUP_ macros).
>
> But that wasn't enough for variable templates, so I also had to introduce
> TINFO_VAR_DECLARED_CONSTINIT. I suppose those DECL_TEMPLATE_INFO aren't
> as precious as the VAR_DECL bits.
>
>> Hmm, the existing code limiting the unification of constexpr to class-scope
>> variables seems wrong:
>>
>> constexpr float pi = 3.14;
>> extern const float pi;
>> constexpr float x = pi; // should be OK
>
> Thanks for fixing this meanwhile.
>
> Is this any better?
>
> Bootstrapped/regtested on x86_64-linux.
>
> 2019-08-27 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.
>
> * 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.
OK, thanks.
Jason
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: C++ PATCH to implement C++20 P1143R2, constinit (PR c++/91360)
2019-08-28 21:12 ` Jason Merrill
@ 2019-08-28 21:31 ` Marek Polacek
0 siblings, 0 replies; 7+ messages in thread
From: Marek Polacek @ 2019-08-28 21:31 UTC (permalink / raw)
To: Jason Merrill; +Cc: GCC Patches
On Wed, Aug 28, 2019 at 04:15:54PM -0400, Jason Merrill wrote:
> OK, thanks.
Thanks! I've now updated wwwdocs:
Index: gcc-10/changes.html
===================================================================
RCS file: /cvs/gcc/wwwdocs/htdocs/gcc-10/changes.html,v
retrieving revision 1.11
diff -u -r1.11 changes.html
--- gcc-10/changes.html 27 Aug 2019 12:29:26 -0000 1.11
+++ gcc-10/changes.html 28 Aug 2019 20:38:12 -0000
@@ -61,6 +61,7 @@
<li>P1161R3, Deprecate <code>a[b,c]</code></li>
<li>P0848R3, Conditionally Trivial Special Member Functions</li>
<li>P1091R3, Extending structured bindings</li>
+ <li>P1143R2, Adding the <code>constinit</code> keyword</li>
</ul>
</li>
<li>Several C++ Defect Reports have been resolved, e.g.:
Index: projects/cxx-status.html
===================================================================
RCS file: /cvs/gcc/wwwdocs/htdocs/projects/cxx-status.html,v
retrieving revision 1.98
diff -u -r1.98 cxx-status.html
--- projects/cxx-status.html 17 Aug 2019 14:18:38 -0000 1.98
+++ projects/cxx-status.html 28 Aug 2019 20:38:12 -0000
@@ -419,7 +419,7 @@
<tr>
<td><code>constinit</code></td>
<td><a href="http://wg21.link/p1143r2">P1143R2</a></td>
- <td class="unsupported">No (<a href="https://gcc.gnu.org/PR91360">PR91360</a>)</td>
+ <td class="supported"><a href="../gcc-10/changes.html#cxx">10</a></td>
<td></td>
</tr>
<tr>
^ permalink raw reply [flat|nested] 7+ messages in thread
end of thread, other threads:[~2019-08-28 20:40 UTC | newest]
Thread overview: 7+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2019-08-14 21:37 C++ PATCH to implement C++20 P1143R2, constinit (PR c++/91360) Marek Polacek
2019-08-23 23:13 ` Jason Merrill
2019-08-27 19:59 ` Marek Polacek
2019-08-28 21:12 ` Jason Merrill
2019-08-28 21:31 ` Marek Polacek
2019-08-27 22:00 ` Paolo Carlini
2019-08-27 22:34 ` Marek Polacek
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).