On Fri, 18 Apr 2025, Jason Merrill 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.
> 
> Thoughts?  Is this a desirable change, or is the fact that the use precedes 
> the
> error reason to go ahead with the instantiation?

Not saying we're at this point yet, but I do worry that being too
aggressive about avoiding error cascades could make the compiler less
noisy at the expense of making it less predictable, and make it more
likely that multiple edit+recompile cycles are needed before a TU is
error-free.

In recurse5.C the emitted error (in checked_add) and the suppressed
error (in add) are caused by the same bug so the suprression is clearly
an improvement, but if they're different bugs then the user will first
see only the add error, which they will fix and rightfully hope the TU
is now error-free, but recompilation will then reveal the suppressed
error in checked_add and the user might wonder why wasn't this error
wasn't reported during the first compile.

When it comes to avoiding too many errors from deferred instantiations
in particular, -fmax-errors works nicely for that since such errors are
naturally the last to be emitted.

> 
> > 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" }

FWIW I noticed if we need to eagerly instantiate add (e.g. if we give it
a deduced return type), then we again emit two "no match" errors.  I guess
this is expected/intended?

> +}
> +
> +struct A { };
> +
> +int main()
> +{
> +  checked_add (A());
> +}
> 
> base-commit: 6808f74b4f07decb3727624f0e62e7c57ae87022
> -- 
> 2.49.0
> 
> 

Reply via email to