[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{};
+  };
+}
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.