Alternatively, rather than passing the most general template + args to
push_tinst_level, we can pass the partially instantiated template +
innermost args via just:

gcc/cp/ChangeLog:

        * constraint.cc (satisfy_declaration_constraints): Pass the
        original T and ARGS to push_tinst_level.

diff --git a/gcc/cp/constraint.cc b/gcc/cp/constraint.cc
index 2f1678ce4ff9..52768972da43 100644
--- a/gcc/cp/constraint.cc
+++ b/gcc/cp/constraint.cc
@@ -2704,6 +2704,8 @@ satisfy_declaration_constraints (tree t, sat_info info)
 static tree
 satisfy_declaration_constraints (tree t, tree args, sat_info info)
 {
+  tree orig_t = t, orig_args = args;
+
   /* Update the declaration for diagnostics.  */
   info.in_decl = t;
 
@@ -2732,7 +2734,7 @@ satisfy_declaration_constraints (tree t, tree args, 
sat_info info)
   tree result = boolean_true_node;
   if (tree norm = get_normalized_constraints_from_decl (t, info.noisy ()))
     {
-      if (!push_tinst_level (t, args))
+      if (!push_tinst_level (orig_t, orig_args))
        return result;
       tree pattern = DECL_TEMPLATE_RESULT (t);
       push_to_top_level ();

So that for diagnostic20.C in question we emit:

  In substitution of '... void A<int>::f<U>() [with U = char]'.

compared to (with the previous approach)

  In substitution of '... void A<T>::f<U>() [with U = char; T = int]'.

or (wrongly, with the status quo)

  In substitution of '... void A<int>::f<U>() [with U = int]'

Would this be preferable?  I'd be good with either.

On Wed, 9 Apr 2025, Patrick Palka wrote:

> On Wed, 9 Apr 2025, Patrick Palka wrote:
> 
> > On Wed, 5 Mar 2025, Jason Merrill wrote:
> > 
> > > On 3/5/25 10:13 AM, Patrick Palka wrote:
> > > > On Tue, 4 Mar 2025, Jason Merrill wrote:
> > > > 
> > > > > On 3/4/25 2:49 PM, Patrick Palka wrote:
> > > > > > Bootstrapped and regtested on x86_64-pc-linux-gnu, does this look OK
> > > > > > for trunk/14?
> > > > > > 
> > > > > > -- >8 --
> > > > > > 
> > > > > > In the three-parameter version of satisfy_declaration_constraints, 
> > > > > > when
> > > > > > 't' isn't the most general template, then 't' won't correspond with
> > > > > > 'args' after we augment the latter via add_outermost_template_args, 
> > > > > > and
> > > > > > so the instantiation context that we push via push_tinst_level isn't
> > > > > > quite correct: 'args' is a complete set of template arguments, but 
> > > > > > 't'
> > > > > > is not necessarily the most general template.  This manifests as
> > > > > > misleading diagnostic context lines when issuing a hard error (or a
> > > > > > constraint recursion error) that occurred during satisfaction, e.g. 
> > > > > > for
> > > > > > the below testcase without this patch we emit:
> > > > > >     In substitution of '... void A<int>::f<U>() [with U = int]'
> > > > > > and with this patch we emit:
> > > > > >     In substitution of '... void A<T>::f<U>() [with U = char; T = 
> > > > > > int]'.
> > > > > > 
> > > > > > This patch fixes this by always passing the most general template to
> > > > > > push_tinst_level.
> > > > > 
> > > > > That soungs good, but getting it by passing it back from
> > > > > get_normalized_constraints_from_decl seems confusing; I'd think we 
> > > > > should
> > > > > calculate it in parallel to changing args to correspond to that 
> > > > > template.
> > > > 
> > > > Hmm, won't that mean duplicating the template adjustment logic in
> > > > get_normalized_constraints_from_decl, which seems undesirable?  The
> > > > function has many callers, some of which are for satisfaction where
> > > > targs are involved, and the rest are for subsumption where no targs are
> > > > involved, so I don't see a clean way of refactoring the code to avoid
> > > > duplication of the template adjustment logic.  Right now the targ
> > > > adjustment logic is unfortunately duplicated across both overloads
> > > > of satisfy_declaration_constraints and it seems undesirable to add
> > > > more duplication.
> > > 
> > > Fair enough.  Incidentally, I wonder why the two-parm overload doesn't 
> > > call
> > > the three-parm overload?
> > > 
> > > > Maybe one way to reduce the duplication would be to go the other way and
> > > > move the targ adjustment logic to get_normalized_constraints_from_decl
> > > > as well (so that it has two out-parameters, 'gen_d' and 'gen_args').
> > > > The proposed patch then would be an incremental step towards that.
> > > 
> > > That makes sense, passing back something suitable for
> > > add_outermost_template_args.
> > 
> > I tried combining the two overloads, and/or moving the targ adjustment
> > logic to get_normalized_constraints_from_decl, but I couldn't arrive at
> > a formulation that worked and I was happy with (i.e. didn't lead to more
> > code duplication than the original appproach).
> > 
> > In the meantime I noticed that this bug is more pervasive than I
> > thought, and leads to wrong diagnostic context lines printed even in the
> > case of ordinary satisfaction failure -- however the wrong diagnostic
> > lines are more annoying/noticable during a hard error or constraint
> > recursion where there's likely no other useful diagnostic lines that
> > might have the correct args printed.
> > 
> > So I adjusted the testcase in the original patch accordingly.  Could the
> > following go in for now?
> > 
> > I also attached a diff of the output of all our concepts testcases
> > currently, before/after this patch.  Each change seems like a clear
> > improvement/correction to me.
> 
> Oops, that was not a complete diff of all the concepts tests, here is a
> more complete one.
> 
> > 
> > -- >8 --
> > 
> > Subject: [PATCH] c++: wrong targs in satisfaction diagnostic context line
> >  [PR99214]
> > 
> > In the three-parameter version of satisfy_declaration_constraints, when
> > 't' isn't the most general template, then 't' won't correspond with
> > 'args' after we augment the latter via add_outermost_template_args, and
> > so the instantiation context that we push via push_tinst_level isn't
> > quite correct: 'args' is a complete set of template arguments, but 't'
> > is not necessarily the most general template.  This manifests as
> > misleading diagnostic context lines when issuing a satisfaction failure
> > error, e.g.  the below testcase without this patch we emit:
> >   In substitution of '... void A<int>::f<U>() [with U = int]'
> > and with this patch we emit:
> >   In substitution of '... void A<T>::f<U>() [with U = char; T = int]'.
> > 
> > This patch fixes this by always passing the most general template to
> > push_tinst_level.
> > 
> >     PR c++/99214
> > 
> > gcc/cp/ChangeLog:
> > 
> >     * constraint.cc (get_normalized_constraints_from_decl): New
> >     optional out-parameter GEN_D.
> >     (satisfy_declaration_constraints): Use it to pass the most
> >     general version of T to push_tinst_level.
> > 
> > gcc/testsuite/ChangeLog:
> > 
> >     * g++.dg/concepts/diagnostic20.C: New test.
> > ---
> >  gcc/cp/constraint.cc                         | 15 +++++++++++----
> >  gcc/testsuite/g++.dg/concepts/diagnostic20.C | 14 ++++++++++++++
> >  2 files changed, 25 insertions(+), 4 deletions(-)
> >  create mode 100644 gcc/testsuite/g++.dg/concepts/diagnostic20.C
> > 
> > diff --git a/gcc/cp/constraint.cc b/gcc/cp/constraint.cc
> > index a9caba8e2cc7..f688a99c5fd7 100644
> > --- a/gcc/cp/constraint.cc
> > +++ b/gcc/cp/constraint.cc
> > @@ -648,10 +648,13 @@ get_normalized_constraints_from_info (tree ci, tree 
> > in_decl, bool diag = false)
> >    return t;
> >  }
> >  
> > -/* Returns the normalized constraints for the declaration D.  */
> > +/* Returns the normalized constraints for the declaration D.
> > +   If GEN_D is non-NULL, sets *GEN_D to the most general version
> > +   of D that ultimately owns its constraints.  */
> >  
> >  static tree
> > -get_normalized_constraints_from_decl (tree d, bool diag = false)
> > +get_normalized_constraints_from_decl (tree d, bool diag = false,
> > +                                 tree *gen_d = nullptr)
> >  {
> >    tree tmpl;
> >    tree decl;
> > @@ -716,6 +719,8 @@ get_normalized_constraints_from_decl (tree d, bool diag 
> > = false)
> >      tmpl = most_general_template (tmpl);
> >  
> >    d = tmpl ? tmpl : decl;
> > +  if (gen_d)
> > +    *gen_d = d;
> >  
> >    /* If we're not diagnosing errors, use cached constraints, if any.  */
> >    if (!diag)
> > @@ -2730,9 +2735,11 @@ satisfy_declaration_constraints (tree t, tree args, 
> > sat_info info)
> >      return boolean_true_node;
> >  
> >    tree result = boolean_true_node;
> > -  if (tree norm = get_normalized_constraints_from_decl (t, info.noisy ()))
> > +  tree gen_t;
> > +  if (tree norm = get_normalized_constraints_from_decl (t, info.noisy (),
> > +                                                   &gen_t))
> >      {
> > -      if (!push_tinst_level (t, args))
> > +      if (!push_tinst_level (gen_t, args))
> >     return result;
> >        tree pattern = DECL_TEMPLATE_RESULT (t);
> >        push_to_top_level ();
> > diff --git a/gcc/testsuite/g++.dg/concepts/diagnostic20.C 
> > b/gcc/testsuite/g++.dg/concepts/diagnostic20.C
> > new file mode 100644
> > index 000000000000..d88000b342c3
> > --- /dev/null
> > +++ b/gcc/testsuite/g++.dg/concepts/diagnostic20.C
> > @@ -0,0 +1,14 @@
> > +// PR c++/99214
> > +// { dg-do compile { target c++20 } }
> > +
> > +template <class T>
> > +struct A {
> > +  template <class U> static void f() requires requires { T::fail; };
> > +};
> > +
> > +int main() {
> > +  A<int>::f<char>(); // { dg-error "no match" }
> > +}
> > +
> > +// This matches the context line "In substitution of '... [with U = char; 
> > T = int]'"
> > +// { dg-message "U = char; T = int" "" { target *-*-* } 0 }
> > -- 
> > 2.49.0.221.g485f5f8636
> > 

Reply via email to