From: Marek Polacek Date: Wed, 28 Aug 2019 20:31:31 +0000 (+0000) Subject: PR c++/91360 - Implement C++20 P1143R2: constinit. X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=4742dbe71804b3db099eb0eb8620dff2c79a71cf;p=gcc.git 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. From-SVN: r275008 --- diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 77dbf876a3a..8b5ccd2f98d 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,8 @@ +2019-08-28 Marek Polacek + + PR c++/91360 - Implement C++20 P1143R2: constinit. + * doc/invoke.texi: Document -Wc++20-compat. + 2019-08-28 Martin Sebor PR tree-optimization/91457 diff --git a/gcc/c-family/ChangeLog b/gcc/c-family/ChangeLog index 0376a7b977d..d0a19e3870d 100644 --- a/gcc/c-family/ChangeLog +++ b/gcc/c-family/ChangeLog @@ -1,3 +1,15 @@ +2019-08-28 Marek Polacek + + PR c++/91360 - Implement C++20 P1143R2: constinit. + * c-common.c (c_common_reswords): Add constinit and __constinit. + (keyword_is_decl_specifier): Handle RID_CONSTINIT. + * c-common.h (enum rid): Add RID_CONSTINIT, RID_FIRST_CXX20, and + RID_LAST_CXX20. + (D_CXX20): Define. + * c-cppbuiltin.c (c_cpp_builtins): Define __cpp_constinit. + * c-format.c (cxx_keywords): Add "constinit". + * c.opt (Wc++2a-compat, Wc++20-compat): New options. + 2019-08-27 Jakub Jelinek PR c++/91415 diff --git a/gcc/c-family/c-common.c b/gcc/c-family/c-common.c index 61ee7543dac..abc85cb2929 100644 --- a/gcc/c-family/c-common.c +++ b/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 }, @@ -7927,6 +7930,7 @@ keyword_is_decl_specifier (enum rid keyword) case RID_TYPEDEF: case RID_FRIEND: case RID_CONSTEXPR: + case RID_CONSTINIT: return true; default: return false; diff --git a/gcc/c-family/c-common.h b/gcc/c-family/c-common.h index 117d729091a..17bd7b1c7d8 100644 --- a/gcc/c-family/c-common.h +++ b/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 a/gcc/c-family/c-cppbuiltin.c b/gcc/c-family/c-cppbuiltin.c index 6006e95b068..6b18246e0b6 100644 --- a/gcc/c-family/c-cppbuiltin.c +++ b/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 a/gcc/c-family/c-format.c b/gcc/c-family/c-format.c index 6b059969e67..91bae3d6096 100644 --- a/gcc/c-family/c-format.c +++ b/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 a/gcc/c-family/c.opt b/gcc/c-family/c.opt index 257cadfa5f1..4c468d0f6c2 100644 --- a/gcc/c-family/c.opt +++ b/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 a/gcc/cp/ChangeLog b/gcc/cp/ChangeLog index 08a44b6c606..2e7c26cd447 100644 --- a/gcc/cp/ChangeLog +++ b/gcc/cp/ChangeLog @@ -1,3 +1,32 @@ +2019-08-28 Marek Polacek + + PR c++/91360 - Implement C++20 P1143R2: constinit. + * cp-tree.h (TINFO_VAR_DECLARED_CONSTINIT): Define. + (LOOKUP_CONSTINIT): Define. + (enum cp_decl_spec): Add ds_constinit. + * decl.c (check_tag_decl): Give an error for constinit in type + declarations. + (check_initializer): Also check LOOKUP_CONSTINIT. + (cp_finish_decl): Add checking for a constinit declaration. Set + TINFO_VAR_DECLARED_CONSTINIT. + (grokdeclarator): Add checking for a declaration with the constinit + specifier. + * lex.c (init_reswords): Handle D_CXX20. + * parser.c (cp_lexer_get_preprocessor_token): Pass a better location + to warning_at. Warn about C++20 keywords. + (cp_keyword_starts_decl_specifier_p): Handle RID_CONSTINIT. + (cp_parser_diagnose_invalid_type_name): Add an inform about constinit. + (cp_parser_decomposition_declaration): Maybe pass LOOKUP_CONSTINIT to + cp_finish_decl. + (cp_parser_decl_specifier_seq): Handle RID_CONSTINIT. + (cp_parser_init_declarator): Maybe pass LOOKUP_CONSTINIT to + cp_finish_decl. + (set_and_check_decl_spec_loc): Add "constinit". + * pt.c (tsubst_decl): Set TINFO_VAR_DECLARED_CONSTINIT. + (instantiate_decl): Maybe pass LOOKUP_CONSTINIT to cp_finish_decl. + * typeck2.c (store_init_value): If a constinit variable wasn't + initialized using a constant initializer, give an error. + 2019-08-28 Nathan Sidwell PR c++/90613 diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h index 225dbb67c63..0e514d5cc22 100644 --- a/gcc/cp/cp-tree.h +++ b/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 a/gcc/cp/decl.c b/gcc/cp/decl.c index 847817029e4..c5cc22a8d6d 100644 --- a/gcc/cp/decl.c +++ b/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], "% cannot be used for type declarations"); + else if (decl_spec_seq_has_spec_p (declspecs, ds_constinit)) + error_at (declspecs->locations[ds_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 **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 ("% 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], + "% 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 % and % " + "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_p = 0; } + else if (constinit_p) + { + error_at (declspecs->locations[ds_constinit], + "a parameter cannot be declared %"); + 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], + "% 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 %", - unqualified_id); + "non-static data member %qE declared " + "%", unqualified_id); constexpr_p = false; } + else if (constinit_p) + { + error_at (declspecs->locations[ds_constinit], + "non-static data member %qE declared " + "%", 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 a/gcc/cp/lex.c b/gcc/cp/lex.c index 12567da39c4..5b43723a8fa 100644 --- a/gcc/cp/lex.c +++ b/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 a/gcc/cp/parser.c b/gcc/cp/parser.c index 382575320de..93cdaddf52d 100644 --- a/gcc/cp/parser.c +++ b/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 % only available with " "%<-std=c++11%> or %<-std=gnu++11%>"); + else if (cxx_dialect < cxx2a && id == ridpointers[(int)RID_CONSTINIT]) + inform (location, "C++20 % only available with " + "%<-std=c++2a%> or %<-std=gnu++2a%>"); else if (!flag_concepts && id == ridpointers[(int)RID_CONCEPT]) inform (location, "% 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 a/gcc/cp/pt.c b/gcc/cp/pt.c index 17585119bce..54d36f91ddc 100644 --- a/gcc/cp/pt.c +++ b/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 a/gcc/cp/typeck2.c b/gcc/cp/typeck2.c index 02c3ad5efb0..d5098fa24bb 100644 --- a/gcc/cp/typeck2.c +++ b/gcc/cp/typeck2.c @@ -885,7 +885,22 @@ store_init_value (tree decl, tree init, vec** 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), + "% 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 a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi index 549e043c67c..1391a562c35 100644 --- a/gcc/doc/invoke.texi +++ b/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 a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog index 7dc95f323ec..77fa37f75b8 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,3 +1,19 @@ +2019-08-28 Marek Polacek + + PR c++/91360 - Implement C++20 P1143R2: constinit. + * g++.dg/cpp2a/constinit1.C: New test. + * g++.dg/cpp2a/constinit2.C: New test. + * g++.dg/cpp2a/constinit3.C: New test. + * g++.dg/cpp2a/constinit4.C: New test. + * g++.dg/cpp2a/constinit5.C: New test. + * g++.dg/cpp2a/constinit6.C: New test. + * g++.dg/cpp2a/constinit7.C: New test. + * g++.dg/cpp2a/constinit8.C: New test. + * g++.dg/cpp2a/constinit9.C: New test. + * g++.dg/cpp2a/constinit10.C: New test. + * g++.dg/cpp2a/constinit11.C: New test. + * g++.dg/cpp2a/constinit12.C: New test. + 2019-08-28 Steven G. Kargl PR fortran/91565 diff --git a/gcc/testsuite/g++.dg/cpp2a/constinit1.C b/gcc/testsuite/g++.dg/cpp2a/constinit1.C new file mode 100644 index 00000000000..9d1c0289a62 --- /dev/null +++ b/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 a/gcc/testsuite/g++.dg/cpp2a/constinit10.C b/gcc/testsuite/g++.dg/cpp2a/constinit10.C new file mode 100644 index 00000000000..a50f285ecb1 --- /dev/null +++ b/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 a/gcc/testsuite/g++.dg/cpp2a/constinit11.C b/gcc/testsuite/g++.dg/cpp2a/constinit11.C new file mode 100644 index 00000000000..ab3715b2064 --- /dev/null +++ b/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 a/gcc/testsuite/g++.dg/cpp2a/constinit12.C b/gcc/testsuite/g++.dg/cpp2a/constinit12.C new file mode 100644 index 00000000000..b5b736f87c2 --- /dev/null +++ b/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 +struct U { + T m; + constexpr U(int i) : m(i) { } // { dg-error "call to non-.constexpr. function" } +}; + +constinit U u(42); // { dg-error "does not have a constant initializer|called in a constant expression" } diff --git a/gcc/testsuite/g++.dg/cpp2a/constinit2.C b/gcc/testsuite/g++.dg/cpp2a/constinit2.C new file mode 100644 index 00000000000..3e9f578f8ca --- /dev/null +++ b/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 a/gcc/testsuite/g++.dg/cpp2a/constinit3.C b/gcc/testsuite/g++.dg/cpp2a/constinit3.C new file mode 100644 index 00000000000..316937e5bf3 --- /dev/null +++ b/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 // { dg-error "a parameter cannot be declared .constinit." } +struct X { }; + +int +fn1 () +{ + // Not static storage + constinit int a1 = 42; // { dg-error ".constinit. can only be applied to a variable with static or thread storage" } + constinit int a2 = 42; // { dg-error ".constinit. can only be applied to a variable with static or thread storage" } + extern constinit int e1; + + return 0; +} + +constinit int // { dg-error ".constinit. on function return type is not allowed" } +fn3 () +{ +} + +void +fn2 (int i, constinit int p) // { dg-error "a parameter cannot be declared .constinit." } +{ + constinit auto l = [i](){ return i; }; // { dg-error ".constinit. can only be applied to a variable with static or thread storage" } +} + +struct B { int d; }; + +void +fn3 (B b) +{ + constinit auto [ a ] = b; // { dg-error ".constinit. can only be applied to a variable with static or thread storage" } +} diff --git a/gcc/testsuite/g++.dg/cpp2a/constinit4.C b/gcc/testsuite/g++.dg/cpp2a/constinit4.C new file mode 100644 index 00000000000..748a7ffa3a9 --- /dev/null +++ b/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 a/gcc/testsuite/g++.dg/cpp2a/constinit5.C b/gcc/testsuite/g++.dg/cpp2a/constinit5.C new file mode 100644 index 00000000000..3d21f48dbee --- /dev/null +++ b/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 a/gcc/testsuite/g++.dg/cpp2a/constinit6.C b/gcc/testsuite/g++.dg/cpp2a/constinit6.C new file mode 100644 index 00000000000..73e78832844 --- /dev/null +++ b/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 a/gcc/testsuite/g++.dg/cpp2a/constinit7.C b/gcc/testsuite/g++.dg/cpp2a/constinit7.C new file mode 100644 index 00000000000..e9a0da3f8c8 --- /dev/null +++ b/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 a/gcc/testsuite/g++.dg/cpp2a/constinit8.C b/gcc/testsuite/g++.dg/cpp2a/constinit8.C new file mode 100644 index 00000000000..c6b2975350c --- /dev/null +++ b/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 +constinit T v1 = 42; + +template +constinit T v2 = nonconst; // { dg-error "does not have a constant initializer|not usable" } + +void +fn () +{ + v1; + v2; +} diff --git a/gcc/testsuite/g++.dg/cpp2a/constinit9.C b/gcc/testsuite/g++.dg/cpp2a/constinit9.C new file mode 100644 index 00000000000..4c7f8925169 --- /dev/null +++ b/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 (); +}