On Mon, 14 Apr 2025, Patrick Palka wrote:
> Bootstrapped and regtested on x86_64-pc-linux-gnu, does this
> look OK for trunk and perhaps 14?
Ping; this patch slipped through the cracks. Would it be OK
for trunk and perhaps 15?
>
> -- >8 --
>
> Our constraint recursion diagnostics are not ideal because they
> usually show the atom with an uninstantiated parameter mapping, e.g
>
> concepts-recursive-sat5.C:6:41: error: satisfaction of atomic constraint
> ‘requires(A a, T t) {a | t;} [with T = T]’ depends on itself
>
> This is a consequence of our two-level caching of atomic constraints,
> where we first cache the uninstantiated atom+args and then the
> instantiated atom+no args, and most likely the first level of caching
> detects the recursion, at which point we have no way to get a hold of
> the instantiated atom.
>
> This patch fixes this by linking the the first level of caching to the
> second level, so that we can conveniently print the instantiated atom in
> case of constraint recursion detected from the first level of caching.
>
> Alternatively we could make only the second level of caching diagnose
> constraint recursion but then we'd no longer catch constraint recursion
> that occurs during parameter mapping instantiation. This current approach
> seems simpler, and it also seems natural to have the two levels of caching
> linked in some way anyway.
>
> gcc/cp/ChangeLog:
>
> * constraint.cc (struct sat_entry): New data member inst_entry.
> (satisfaction_cache::get): Use it to prefer printing the
> instantiated atom in case of constraint recursion.
> (satisfy_atom): Set inst_entry on the first entry to point to
> the second entry.
>
> gcc/testsuite/ChangeLog:
>
> * g++.dg/cpp2a/concepts-recursive-sat2.C: Verify that the
> instantiated parameter mapping is printed.
> * g++.dg/cpp2a/concepts-recursive-sat5.C: Likewise.
> ---
> gcc/cp/constraint.cc | 12 ++++++++++--
> gcc/testsuite/g++.dg/cpp2a/concepts-recursive-sat2.C | 3 ++-
> gcc/testsuite/g++.dg/cpp2a/concepts-recursive-sat5.C | 3 ++-
> 3 files changed, 14 insertions(+), 4 deletions(-)
>
> diff --git a/gcc/cp/constraint.cc b/gcc/cp/constraint.cc
> index 44fb086c6306..1bb237f5bb4f 100644
> --- a/gcc/cp/constraint.cc
> +++ b/gcc/cp/constraint.cc
> @@ -1895,6 +1895,10 @@ struct GTY((for_user)) sat_entry
> the first time. */
> tree result;
>
> + /* For a !ATOMIC_CONSTR_MAP_INSTANTIATED_P atom, this is the entry
> + for the corresponding atom after instantiating its mapping. */
> + sat_entry *inst_entry;
> +
> /* The value of input_location when satisfaction of ATOM+ARGS was first
> performed. */
> location_t location;
> @@ -2093,10 +2097,12 @@ satisfaction_cache::get ()
> {
> /* If we get here, it means satisfaction is self-recursive. */
> gcc_checking_assert (!entry->result || seen_error ());
> + /* Prefer printing the instantiated mapping. */
> + tree atom = entry->inst_entry ? entry->inst_entry->atom : entry->atom;
> if (info.noisy ())
> - error_at (EXPR_LOCATION (ATOMIC_CONSTR_EXPR (entry->atom)),
> + error_at (EXPR_LOCATION (ATOMIC_CONSTR_EXPR (atom)),
> "satisfaction of atomic constraint %qE depends on itself",
> - entry->atom);
> + atom);
> return error_mark_node;
> }
>
> @@ -2416,6 +2422,8 @@ satisfy_atom (tree t, tree args, sat_info info)
> gcc_assert (!ATOMIC_CONSTR_MAP_INSTANTIATED_P (t));
> ATOMIC_CONSTR_MAP_INSTANTIATED_P (t) = true;
> satisfaction_cache inst_cache (t, /*args=*/NULL_TREE, info);
> + if (cache.entry)
> + cache.entry->inst_entry = inst_cache.entry;
> if (tree r = inst_cache.get ())
> {
> cache.entry->location = inst_cache.entry->location;
> diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-recursive-sat2.C
> b/gcc/testsuite/g++.dg/cpp2a/concepts-recursive-sat2.C
> index 9bc96f58979d..a264f26bc55d 100644
> --- a/gcc/testsuite/g++.dg/cpp2a/concepts-recursive-sat2.C
> +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-recursive-sat2.C
> @@ -1,7 +1,8 @@
> // { dg-do compile { target c++20 } }
>
> template<typename T>
> -concept Fooable = requires(T t) { foo(t); }; // { dg-error "depends on
> itself" }
> +concept Fooable = requires(T t) { foo(t); };
> +// { dg-error "T = test::S]' depends on itself" "" { target *-*-* } .-1 }
>
> template<Fooable T>
> void foo(T t) { }
> diff --git a/gcc/testsuite/g++.dg/cpp2a/concepts-recursive-sat5.C
> b/gcc/testsuite/g++.dg/cpp2a/concepts-recursive-sat5.C
> index ba564873a204..63e032494e3d 100644
> --- a/gcc/testsuite/g++.dg/cpp2a/concepts-recursive-sat5.C
> +++ b/gcc/testsuite/g++.dg/cpp2a/concepts-recursive-sat5.C
> @@ -3,7 +3,8 @@
>
> struct A { };
>
> -template<typename T> concept pipeable = requires(A a, T t) { a | t; }; // {
> dg-error "depends on itself" }
> +template<typename T> concept pipeable = requires(A a, T t) { a | t; };
> +// { dg-error "with T = int]' depends on itself" "" { target *-*-* } .-1 }
>
> template<pipeable T> void operator|(A, T);
>
> --
> 2.49.0.221.g485f5f8636
>
>