]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
c++: delay noexcept parsing for templated friends [PR122668]
authorPatrick Palka <ppalka@redhat.com>
Mon, 15 Dec 2025 19:23:52 +0000 (14:23 -0500)
committerPatrick Palka <ppalka@redhat.com>
Mon, 15 Dec 2025 19:23:52 +0000 (14:23 -0500)
The r16-5425 workaround to delay current-instantiation name lookup
within a friend's noexcept-spec is unsound because it considers the
flag cp_noexcept_operand but that indicates a noexcept-expr, not a
noexcept-spec.  We don't currently have a flag for indicating a
noexcept-spec context (and I don't think we really want one).

This patch reverts that workaround and instead makes us properly
delay noexcept-spec parsing of templated friends, which should fully
address the regression since it's applicable only to ahead of time
name lookup at class template scope.  Delaying noexcept-spec parsing
of non-templated friends is trickier since it means we need to defer
declaration matching of such friends until after delayed parsing rather
than immediately matching.  In contrast, for templated friends we
naturally defer declaration matching until instantiation time, i.e.
well after delayed parsing.

PR c++/122668
PR c++/114764

gcc/cp/ChangeLog:

* parser.cc (cp_parser_class_specifier): Adjust after
changing the element type of unparsed_noexcepts.  Pass
the class type to noexcept_override_late_checks.
(cp_parser_member_declaration): Set
CP_PARSER_FLAGS_DELAY_NOEXCEPT also for templated friends.
(noexcept_override_late_checks): Add class_type parameter.
(cp_parser_single_declaration): Set
CP_PARSER_FLAGS_DELAY_NOEXCEPT also for template friends
at class template scope.
(cp_parser_save_default_args): Push current_class_type
to unparsed_noexcepts.
* parser.h (cp_unparsed_functions_entry::noexcepts):
Change element type to cp_default_arg_entry.
* pt.cc (dependentish_scope_p): Revert r16-5425 change.

gcc/testsuite/ChangeLog:

* g++.dg/cpp0x/noexcept91a.C: New test.

Reviewed-by: Jason Merrill <jason@redhat.com>
gcc/cp/parser.cc
gcc/cp/parser.h
gcc/cp/pt.cc
gcc/testsuite/g++.dg/cpp0x/noexcept91a.C [new file with mode: 0644]

index d6e3298c144e8fe0fea055c29aae2d46f2d5e27e..74c08f231880a0a19ca4e84f9b846046d2aedb4e 100644 (file)
@@ -260,7 +260,7 @@ static cp_token_cache *cp_token_cache_new
 static tree cp_parser_late_noexcept_specifier
   (cp_parser *, tree);
 static void noexcept_override_late_checks
-  (tree);
+  (tree, tree);
 
 static void cp_parser_initial_pragma
   (cp_token *);
@@ -28982,9 +28982,10 @@ cp_parser_class_specifier (cp_parser* parser)
       /* If there are noexcept-specifiers that have not yet been processed,
         take care of them now.  Do this before processing NSDMIs as they
         may depend on noexcept-specifiers already having been processed.  */
-      FOR_EACH_VEC_SAFE_ELT (unparsed_noexcepts, ix, decl)
+      FOR_EACH_VEC_SAFE_ELT (unparsed_noexcepts, ix, e)
        {
-         tree ctx = DECL_CONTEXT (decl);
+         decl = e->decl;
+         tree ctx = e->class_type;
          switch_to_class (ctx);
 
          tree def_parse = TYPE_RAISES_EXCEPTIONS (TREE_TYPE (decl));
@@ -29027,7 +29028,7 @@ cp_parser_class_specifier (cp_parser* parser)
          /* The finish_struct call above performed various override checking,
             but it skipped unparsed noexcept-specifier operands.  Now that we
             have resolved them, check again.  */
-         noexcept_override_late_checks (decl);
+         noexcept_override_late_checks (decl, ctx);
 
          /* Remove any member-function parameters from the symbol table.  */
          pop_injected_parms ();
@@ -30305,10 +30306,11 @@ cp_parser_member_declaration (cp_parser* parser)
              int ctor_dtor_or_conv_p;
              bool static_p = (decl_specifiers.storage_class == sc_static);
              cp_parser_flags flags = CP_PARSER_FLAGS_TYPENAME_OPTIONAL;
-             /* We can't delay parsing for friends,
-                alias-declarations, and typedefs, even though the
-                standard seems to require it.  */
-             if (!friend_p
+             /* We can't delay parsing for alias-declarations and typedefs,
+                even though the standard seems to require it.  */
+             /* FIXME: Delay parsing for all friends, not just templated
+                ones (PR114764).  */
+             if ((!friend_p || current_template_depth > 0)
                  && !decl_spec_seq_has_spec_p (&decl_specifiers, ds_typedef))
                flags |= CP_PARSER_FLAGS_DELAY_NOEXCEPT;
 
@@ -31046,9 +31048,9 @@ cp_parser_late_noexcept_specifier (cp_parser *parser, tree default_arg)
    overrides some virtual function with the same signature.  */
 
 static void
-noexcept_override_late_checks (tree fndecl)
+noexcept_override_late_checks (tree fndecl, tree class_type)
 {
-  tree binfo = TYPE_BINFO (DECL_CONTEXT (fndecl));
+  tree binfo = TYPE_BINFO (class_type);
   tree base_binfo;
 
   if (DECL_STATIC_FUNCTION_P (fndecl))
@@ -35349,9 +35351,10 @@ cp_parser_single_declaration (cp_parser* parser,
          || decl_specifiers.type != error_mark_node))
     {
       int flags = CP_PARSER_FLAGS_TYPENAME_OPTIONAL;
-      /* We don't delay parsing for friends, though CWG 2510 may change
-        that.  */
-      if (member_p && !(friend_p && *friend_p))
+      /* FIXME: Delay parsing for all template friends, not just class
+        template scope ones (PR114764).  */
+      if (member_p && (!(friend_p && *friend_p)
+                      || current_template_depth > 1))
        flags |= CP_PARSER_FLAGS_DELAY_NOEXCEPT;
       decl = cp_parser_init_declarator (parser,
                                        flags,
@@ -35854,7 +35857,10 @@ cp_parser_save_default_args (cp_parser* parser, tree decl)
   /* Remember if there is a noexcept-specifier to post process.  */
   tree spec = TYPE_RAISES_EXCEPTIONS (TREE_TYPE (decl));
   if (UNPARSED_NOEXCEPT_SPEC_P (spec))
-    vec_safe_push (unparsed_noexcepts, decl);
+    {
+      cp_default_arg_entry entry = {current_class_type, decl};
+      vec_safe_push (unparsed_noexcepts, entry);
+    }
 
   /* Contracts are deferred.  */
   for (tree attr = DECL_ATTRIBUTES (decl); attr; attr = TREE_CHAIN (attr))
index 3e7251c38a95b8a35d64cae40ba212f7c2cd52c4..387e4a58e5f48470c6fa69e839b83ce3a218ad1b 100644 (file)
@@ -181,7 +181,7 @@ struct GTY(()) cp_unparsed_functions_entry {
   vec<tree, va_gc> *nsdmis;
 
   /* Functions with noexcept-specifiers that require post-processing.  */
-  vec<tree, va_gc> *noexcepts;
+  vec<cp_default_arg_entry, va_gc> *noexcepts;
 
   /* Functions with contract attributes that require post-processing.  */
   vec<tree, va_gc> *contracts;
index 341e5ab8808a50094a635883e3e3d88285582e8a..f13b3436eb3f072c7c903e4a003fe3ed509c8acc 100644 (file)
@@ -29072,16 +29072,7 @@ dependent_scope_p (tree scope)
 bool
 dependentish_scope_p (tree scope)
 {
-  return dependent_scope_p (scope) || any_dependent_bases_p (scope)
-    /* A noexcept-spec is a complete-class context, so this should never hold.
-       But since we don't implement deferred noexcept-spec parsing of a friend
-       declaration (PR114764) we compensate by treating the current
-       instantiation as dependent to avoid bogus name lookup failures in this
-       case (PR122668).  */
-    || (cp_noexcept_operand
-       && CLASS_TYPE_P (scope)
-       && TYPE_BEING_DEFINED (scope)
-       && dependent_type_p (scope));
+  return dependent_scope_p (scope) || any_dependent_bases_p (scope);
 }
 
 /* T is a SCOPE_REF.  Return whether it represents a non-static member of
diff --git a/gcc/testsuite/g++.dg/cpp0x/noexcept91a.C b/gcc/testsuite/g++.dg/cpp0x/noexcept91a.C
new file mode 100644 (file)
index 0000000..648189e
--- /dev/null
@@ -0,0 +1,26 @@
+// PR c++/122668
+// A version of noexcept91.C where the lookup to be deferred isn't nested
+// inside a noexcept-expr.
+// { dg-do compile { target c++11 } }
+
+template<class T>
+struct A {
+  friend void f(A& a) noexcept(g(0)) { }
+  static constexpr bool g(int) { return true; }
+};
+
+template<class T>
+struct B {
+  struct C {
+    friend void f(C& a) noexcept(g(0)) { }
+    static constexpr bool g(int) { return false; }
+  };
+};
+
+int main() {
+  A<int> a;
+  static_assert(noexcept(f(a)), "");
+
+  B<int>::C c;
+  static_assert(!noexcept(f(c)), "");
+}