From: Patrick Palka Date: Mon, 15 Dec 2025 19:23:52 +0000 (-0500) Subject: c++: delay noexcept parsing for templated friends [PR122668] X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=4a718209d448f6a0b609421f7df7ad2761182c72;p=thirdparty%2Fgcc.git c++: delay noexcept parsing for templated friends [PR122668] 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 --- diff --git a/gcc/cp/parser.cc b/gcc/cp/parser.cc index d6e3298c144..74c08f23188 100644 --- a/gcc/cp/parser.cc +++ b/gcc/cp/parser.cc @@ -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)) diff --git a/gcc/cp/parser.h b/gcc/cp/parser.h index 3e7251c38a9..387e4a58e5f 100644 --- a/gcc/cp/parser.h +++ b/gcc/cp/parser.h @@ -181,7 +181,7 @@ struct GTY(()) cp_unparsed_functions_entry { vec *nsdmis; /* Functions with noexcept-specifiers that require post-processing. */ - vec *noexcepts; + vec *noexcepts; /* Functions with contract attributes that require post-processing. */ vec *contracts; diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc index 341e5ab8808..f13b3436eb3 100644 --- a/gcc/cp/pt.cc +++ b/gcc/cp/pt.cc @@ -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 index 00000000000..648189e07ea --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp0x/noexcept91a.C @@ -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 +struct A { + friend void f(A& a) noexcept(g(0)) { } + static constexpr bool g(int) { return true; } +}; + +template +struct B { + struct C { + friend void f(C& a) noexcept(g(0)) { } + static constexpr bool g(int) { return false; } + }; +}; + +int main() { + A a; + static_assert(noexcept(f(a)), ""); + + B::C c; + static_assert(!noexcept(f(c)), ""); +}