[gcc r17-2718] c++: parse trivial DMI immediately [PR96645]
Jason Merrill via Gcc-cvs <[email protected]>
| Newsgroups | gmane.comp.gcc.cvs |
|---|---|
| Message-ID | <[email protected]> |
https://gcc.gnu.org/g:e3e1c72649c3a0b48af70f88bcd942c46b0d00ab commit r17-2718-ge3e1c72649c3a0b48af70f88bcd942c46b0d00ab Author: Jason Merrill <[email protected]> Date: Fri Jul 24 15:17:32 2026 -0400 c++: parse trivial DMI immediately [PR96645] The current direction of CWG2335 is to parse complete-class contexts as needed rather than always wait until the end of the outermost class. This doesn't fully implement that (nor fully fix PR96645), but takes a step in that direction by allowing simple cases that do no name lookup; then being in complete class scope makes no difference. CWG 2335 PR c++/96645 gcc/cp/ChangeLog: * parser.cc (cp_parser_early_parsing_nsdmi): New. (cp_parser_member_declaration): Call it. gcc/testsuite/ChangeLog: * g++.dg/cpp0x/nsdmi10.C: Distinguish simple case. * g++.dg/ext/is_constructible7.C: New. Diff: --- gcc/cp/parser.cc | 42 +++++++++++++++++++++++++++- gcc/testsuite/g++.dg/cpp0x/nsdmi10.C | 4 +-- gcc/testsuite/g++.dg/ext/is_constructible7.C | 27 ++++++++++++++++++ 3 files changed, 70 insertions(+), 3 deletions(-) diff --git a/gcc/cp/parser.cc b/gcc/cp/parser.cc index 13ebd64c4836..a4cfcfa480ef 100644 --- a/gcc/cp/parser.cc +++ b/gcc/cp/parser.cc @@ -3108,6 +3108,8 @@ static tree cp_parser_late_parse_one_default_arg (cp_parser *, tree, tree, tree); static void cp_parser_late_parsing_nsdmi (cp_parser *, tree); +static bool cp_parser_early_parsing_nsdmi + (cp_parser *, tree); static void cp_parser_late_parsing_default_args (cp_parser *, tree); static tree cp_parser_sizeof_operand @@ -31748,7 +31750,8 @@ cp_parser_member_declaration (cp_parser* parser) if (DECL_DECLARES_FUNCTION_P (decl)) cp_parser_save_default_args (parser, STRIP_TEMPLATE (decl)); else if (TREE_CODE (decl) == FIELD_DECL - && DECL_INITIAL (decl)) + && DECL_INITIAL (decl) + && !cp_parser_early_parsing_nsdmi (parser, decl)) /* Add DECL to the queue of NSDMI to be parsed later. */ vec_safe_push (unparsed_nsdmis, decl); } @@ -37282,6 +37285,43 @@ cp_parser_late_parsing_nsdmi (cp_parser *parser, tree field) DECL_INITIAL (field) = def; } +/* If the DEFERRED_PARSE for FIELD is safe to parse immediately, do so. + Returns true if deferred parsing is no longer needed. + + CWG direction on issue 2335 is to parse DMI as needed rather than only at + the end of the class, which we do not yet implement; this function allows a + subset of trivial cases that are intended to be well-formed. + + A further subset could be trying to parse immediately but giving up if name + lookup fails, since "A name N used in a class S shall refer to the same + declaration in its context and when re-evaluated in the completed scope of + S." ([basic.scope.class]. + + WIP for c++/96645 tries to parse as a pseudo-template and then + pseudo-instantiate when needed, like requires-expressions; this would + completely implement the CWG direction (and also rely on the above + rule). */ + +static bool +cp_parser_early_parsing_nsdmi (cp_parser *parser, tree field) +{ + tree init = DECL_INITIAL (field); + if (TREE_CODE (init) != DEFERRED_PARSE) + return true; + + cp_token_cache *tokens = DEFPARSE_TOKENS (init); + for (cp_token *p = tokens->first; p != tokens->last; ++p) + if (p->type == CPP_NAME + || p->keyword == RID_THIS + || p->keyword == RID_OPERATOR) + /* There's a name to look up or 'this', give up. */ + return false; + + /* It's trivial, parse now. */ + cp_parser_late_parsing_nsdmi (parser, field); + return true; +} + /* FN is a FUNCTION_DECL which may contains a parameter with an unparsed DEFERRED_PARSE. Parse the default args now. This function assumes that the current scope is the scope in which the default diff --git a/gcc/testsuite/g++.dg/cpp0x/nsdmi10.C b/gcc/testsuite/g++.dg/cpp0x/nsdmi10.C index d8588b7f29ea..ee0d79570b4b 100644 --- a/gcc/testsuite/g++.dg/cpp0x/nsdmi10.C +++ b/gcc/testsuite/g++.dg/cpp0x/nsdmi10.C @@ -6,12 +6,12 @@ struct A1 { int y1 = 1; }; - A1(const B1& opts = B1()) {} // { dg-error "default member initializer" } + A1(const B1& opts = B1()) {} }; struct A2 { struct B2 { - int x2, y2 = 1; + int x2, y2 = x2; }; A2(const B2& opts = B2()) {} // { dg-error "default member initializer" } diff --git a/gcc/testsuite/g++.dg/ext/is_constructible7.C b/gcc/testsuite/g++.dg/ext/is_constructible7.C new file mode 100644 index 000000000000..013a1df03c6a --- /dev/null +++ b/gcc/testsuite/g++.dg/ext/is_constructible7.C @@ -0,0 +1,27 @@ +// PR c++/96645 +// { dg-do compile { target c++11 } } + +template<bool B> +struct bool_constant +{ + static constexpr bool value = B; + using type = bool_constant; +}; + +using true_type = bool_constant<true>; + +template<typename T> +struct is_default_constructible + : bool_constant<__is_constructible(T)> +{ }; + +void testVarStruct() +{ + struct DataWithStruct { + struct A { + int number = 5; // compiles, if remove initialization + }; + + is_default_constructible<A>::type t = true_type{}; + }; +}