On Fri, 18 Apr 2025 at 23:08, Jason Merrill <ja...@redhat.com> wrote: > > limit_bad_template_recursion currently avoids immediate instantiation of > templates from uses in an already ill-formed instantiation, but we still can > get unnecessary recursive instantiation in pending_templates if the > instantiation was queued before the error. > > Currently this regresses several libstdc++ tests which seem to rely on a > static_assert in a function called from another that is separately ill-formed. > For instance, in the 48101_neg.cc tests, we first get an error in find(), then > later instantiate _S_key() (called from find) and get the static_assert error > from there.
I think the current placement of the is_invocable static_assert tested for by map/48101_neg.cc works because of the current G++ behaviour, but if changing the G++ behaviour makes sense (and it seems reasonable to me) then we could put the static_assert somewhere else. Maybe instead of calling _M_impl._M_key_compare directly in find, we could add a new member that does that call, and put the static_assert in there. If I add this to stl_tree.h and move the static_asserts from _S_key into this new function: template<typename _Key1, typename _Key2> bool _M_key_compare(const _Key1& __k1, const _Key2& __k2) const { return _M_impl._M_key_compare(__k1, __k2); } So that it looks like this: template<typename _Key1, typename _Key2> bool _M_key_compare(const _Key1& __k1, const _Key2& __k2) const { #if __cplusplus >= 201103L // If we're asking for the key we're presumably using the comparison // object, and so this is a good place to sanity check it. static_assert(__is_invocable<_Compare&, const _Key&, const _Key&>{}, "comparison object must be invocable " "with two arguments of key type"); # if __cplusplus >= 201703L // _GLIBCXX_RESOLVE_LIB_DEFECTS // 2542. Missing const requirements for associative containers if constexpr (__is_invocable<_Compare&, const _Key&, const _Key&>{}) static_assert( is_invocable_v<const _Compare&, const _Key&, const _Key&>, "comparison object must be invocable as const"); # endif // C++17 #endif // C++11 return _M_impl._M_key_compare(__k1, __k2); } and then update the existing calls to _M_impl._M_key_compare(x,y) to call _M_key_compare(x,y) instead, the errors are much shorter and clearer. The first error is the static_assert (and that would presumably still be true after your change). So I think we can adjust the library and its tests. > > Thoughts? Is this a desirable change, or is the fact that the use precedes > the > error reason to go ahead with the instantiation? > > > FAIL: 23_containers/map/48101_neg.cc -std=gnu++17 (test for errors, line ) > > FAIL: 23_containers/multimap/48101_neg.cc -std=gnu++17 (test for errors, > > line ) > > FAIL: 23_containers/multiset/48101_neg.cc -std=gnu++17 (test for errors, > > line ) > > FAIL: 23_containers/set/48101_neg.cc -std=gnu++17 (test for errors, line ) > > FAIL: 30_threads/packaged_task/cons/dangling_ref.cc -std=gnu++17 (test > > for errors, line ) > > FAIL: 30_threads/packaged_task/cons/lwg4154_neg.cc -std=gnu++17 (test for > > errors, line ) > > > gcc/cp/ChangeLog: > > * cp-tree.h (struct tinst_level): Add had_errors bit. > * pt.cc (push_tinst_level_loc): Clear it. > (pop_tinst_level): Set it. > (reopen_tinst_level): Check it. > (instantiate_pending_templates): Call limit_bad_template_recursion. > > gcc/testsuite/ChangeLog: > > * g++.dg/template/recurse5.C: New test. > --- > gcc/cp/cp-tree.h | 10 ++++++++-- > gcc/cp/pt.cc | 10 ++++++++-- > gcc/testsuite/g++.dg/template/recurse5.C | 17 +++++++++++++++++ > 3 files changed, 33 insertions(+), 4 deletions(-) > create mode 100644 gcc/testsuite/g++.dg/template/recurse5.C > > diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h > index 7798efba3db..856202c65dd 100644 > --- a/gcc/cp/cp-tree.h > +++ b/gcc/cp/cp-tree.h > @@ -6755,8 +6755,14 @@ struct GTY((chain_next ("%h.next"))) tinst_level { > /* The location where the template is instantiated. */ > location_t locus; > > - /* errorcount + sorrycount when we pushed this level. */ > - unsigned short errors; > + /* errorcount + sorrycount when we pushed this level. If the value > + overflows, it will always seem like we currently have more errors, so we > + will limit template recursion even from non-erroneous templates. In a > TU > + with over 32k errors, that's fine. */ > + unsigned short errors : 15; > + > + /* set in pop_tinst_level if there have been errors since we pushed. */ > + bool had_errors : 1; > > /* Count references to this object. If refcount reaches > refcount_infinity value, we don't increment or decrement the > diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc > index a71705fd085..e8d342f99f6 100644 > --- a/gcc/cp/pt.cc > +++ b/gcc/cp/pt.cc > @@ -11418,6 +11418,7 @@ push_tinst_level_loc (tree tldcl, tree targs, > location_t loc) > new_level->targs = targs; > new_level->locus = loc; > new_level->errors = errorcount + sorrycount; > + new_level->had_errors = false; > new_level->next = NULL; > new_level->refcount = 0; > new_level->path = new_level->visible = nullptr; > @@ -11468,6 +11469,9 @@ pop_tinst_level (void) > /* Restore the filename and line number stashed away when we started > this instantiation. */ > input_location = current_tinst_level->locus; > + if (unsigned errs = errorcount + sorrycount) > + if (errs > current_tinst_level->errors) > + current_tinst_level->had_errors = true; > set_refcount_ptr (current_tinst_level, current_tinst_level->next); > --tinst_depth; > } > @@ -11487,7 +11491,7 @@ reopen_tinst_level (struct tinst_level *level) > > set_refcount_ptr (current_tinst_level, level); > pop_tinst_level (); > - if (current_tinst_level) > + if (current_tinst_level && !current_tinst_level->had_errors) > current_tinst_level->errors = errorcount+sorrycount; > > tree decl = level->maybe_get_node (); > @@ -28072,7 +28076,9 @@ instantiate_pending_templates (int retries) > tree instantiation = reopen_tinst_level ((*t)->tinst); > bool complete = false; > > - if (TYPE_P (instantiation)) > + if (limit_bad_template_recursion (instantiation)) > + /* Do nothing. */; > + else if (TYPE_P (instantiation)) > { > if (!COMPLETE_TYPE_P (instantiation)) > { > diff --git a/gcc/testsuite/g++.dg/template/recurse5.C > b/gcc/testsuite/g++.dg/template/recurse5.C > new file mode 100644 > index 00000000000..7bfe5239f0a > --- /dev/null > +++ b/gcc/testsuite/g++.dg/template/recurse5.C > @@ -0,0 +1,17 @@ > +// Test that we don't bother to instantiate add since there were errors in > +// checked_add. > + > +template <class T> T add (T t) { return t+1; } // { dg-bogus "no match" } > + > +template <class T> T checked_add (T t) > +{ > + add (t); > + return t+1; // { dg-error "no match" } > +} > + > +struct A { }; > + > +int main() > +{ > + checked_add (A()); > +} > > base-commit: 6808f74b4f07decb3727624f0e62e7c57ae87022 > -- > 2.49.0 >