On Wed, 21 May 2025, Jason Merrill wrote: > On 5/20/25 11:28 AM, Patrick Palka wrote: > > On Mon, 19 May 2025, Patrick Palka wrote: > > > > > Bootstrapped and regtested on x86_64-pc-linux-gnu, does this look > > > OK for trunk/15/14? > > > > Whoops, CI reports I missed a testsuite adjustment expecting an > > additional error in other/default13.C, which seems reasonable. Here's > > an updated patch. > > OK.
Thanks. On second thought, the extra expected error in default13.C seems unnecessary to me. We can avoid it if we propagate error_mark_node upon TREE_TYPE substitution failure only when in a SFINAE context, otherwise we can assume we already issued an error, and return a PARM_DECL anyway, for sake of error recovery. Also I noticed that tsubst_tree_list and tsubst_arg_types return error_mark_node upon TREE_CHAIN substitution failure rather than returning something with TREE_CHAIN set to error_mark_node, so I think we should do the same when substituting a chain of PARM_DECLs for consistency. So I think I prefer the following version of the patch. Does it look OK for trunk/15/14? -- >8 -- Subject: [PATCH] c++: substituting fn parm redeclared with dep alias tmpl [PR120224] PR c++/120224 gcc/cp/ChangeLog: * pt.cc (tsubst_function_decl): Return error_mark_node if substituting into the formal parameter list failed. (tsubst_decl) <case PARM_DECL>: Return error_mark_node upon TREE_TYPE substitution failure, when in a SFINAE context. Return error_mark_node upon DECL_CHAIN substitution failure. gcc/testsuite/ChangeLog: * g++.dg/cpp0x/alias-decl-80.C: New test. Reviewed-by: Jason Merrill <ja...@redhat.com> --- gcc/cp/pt.cc | 14 ++++++++++++-- gcc/testsuite/g++.dg/cpp0x/alias-decl-80.C | 21 +++++++++++++++++++++ 2 files changed, 33 insertions(+), 2 deletions(-) create mode 100644 gcc/testsuite/g++.dg/cpp0x/alias-decl-80.C diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc index c5a3abe6d8b9..b5c877a385f5 100644 --- a/gcc/cp/pt.cc +++ b/gcc/cp/pt.cc @@ -14983,6 +14983,8 @@ tsubst_function_decl (tree t, tree args, tsubst_flags_t complain, if (closure && DECL_IOBJ_MEMBER_FUNCTION_P (t)) parms = DECL_CHAIN (parms); parms = tsubst (parms, args, complain, t); + if (parms == error_mark_node) + return error_mark_node; for (tree parm = parms; parm; parm = DECL_CHAIN (parm)) DECL_CONTEXT (parm) = r; if (closure && DECL_IOBJ_MEMBER_FUNCTION_P (t)) @@ -15555,6 +15557,9 @@ tsubst_decl (tree t, tree args, tsubst_flags_t complain, /* We're dealing with a normal parameter. */ type = tsubst (TREE_TYPE (t), args, complain, in_decl); + if (type == error_mark_node && !(complain & tf_error)) + RETURN (error_mark_node); + type = type_decays_to (type); TREE_TYPE (r) = type; cp_apply_type_quals_to_decl (cp_type_quals (type), r); @@ -15592,8 +15597,13 @@ tsubst_decl (tree t, tree args, tsubst_flags_t complain, /* If cp_unevaluated_operand is set, we're just looking for a single dummy parameter, so don't keep going. */ if (DECL_CHAIN (t) && !cp_unevaluated_operand) - DECL_CHAIN (r) = tsubst (DECL_CHAIN (t), args, - complain, DECL_CHAIN (t)); + { + tree chain = tsubst (DECL_CHAIN (t), args, + complain, DECL_CHAIN (t)); + if (chain == error_mark_node) + RETURN (error_mark_node); + DECL_CHAIN (r) = chain; + } /* FIRST_R contains the start of the chain we've built. */ r = first_r; diff --git a/gcc/testsuite/g++.dg/cpp0x/alias-decl-80.C b/gcc/testsuite/g++.dg/cpp0x/alias-decl-80.C new file mode 100644 index 000000000000..9c0eadc967c0 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp0x/alias-decl-80.C @@ -0,0 +1,21 @@ +// PR c++/120224 +// { dg-do compile { target c++11 } } + +template<class> using void_t = void; + +template<class T> +void f(void*); // #1 + +template<class T> +void f(void_t<typename T::type>*) { } // { dg-error "not a class" } defn of #1 + +template<class T> +void g(int, void*); // #2 + +template<class T> +void g(int, void_t<typename T::type>*) { } // { dg-error "not a class" } defn of #2 + +int main() { + f<int>(0); // { dg-error "no match" } + g<int>(0, 0); // { dg-error "no match" } +} -- 2.50.0.rc1 > > > -- >8 -- > > > > Here we declare f twice, first ordinarily and then using a dependent > > alias template. Due to alias template transparency these logically > > declare the same overload. But now the function type of f, which was > > produced from the first declaration, diverges from the type of its > > formal parameter, which is produced from the subsequent redefinition, > > in that substituting T=int succeeds for the function type but not for > > the formal parameter type. This eventually causes us to produce an > > undiagnosed error_mark_node in the AST of function call, leading to > > a sanity check failure added in r14-6343-g0c018a74eb1aff. > > > > Before r14-6343, we would later reject the testcase albeit from > > regenerate_decl_from_template when instantiating the definition of f, > > making this a regression. > > > > To fix this, it seems we just need to check for errors when substituting > > the type of a PARM_DECL, since that could still fail despite substitution > > into the function type succeeding. > > > > PR c++/120224 > > > > gcc/cp/ChangeLog: > > > > * pt.cc (tsubst_function_decl): Return error_mark_node if any > > of the substituted function parameters are erroneous. > > (tsubst_decl) <case PARM_DECL>: Return error_mark_node if > > the substituted function parameter type is erroneous. > > > > gcc/testsuite/ChangeLog: > > > > * g++.dg/other/default13.C: Expect additional overload > > resolution failure diagnostic. > > * g++.dg/cpp0x/alias-decl-80.C: New test. > > --- > > gcc/cp/pt.cc | 9 ++++++++- > > gcc/testsuite/g++.dg/cpp0x/alias-decl-80.C | 14 ++++++++++++++ > > gcc/testsuite/g++.dg/other/default13.C | 2 +- > > 3 files changed, 23 insertions(+), 2 deletions(-) > > create mode 100644 gcc/testsuite/g++.dg/cpp0x/alias-decl-80.C > > > > diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc > > index 1973d25b61a0..df6d7bb136ea 100644 > > --- a/gcc/cp/pt.cc > > +++ b/gcc/cp/pt.cc > > @@ -14903,7 +14903,11 @@ tsubst_function_decl (tree t, tree args, > > tsubst_flags_t complain, > > parms = DECL_CHAIN (parms); > > parms = tsubst (parms, args, complain, t); > > for (tree parm = parms; parm; parm = DECL_CHAIN (parm)) > > - DECL_CONTEXT (parm) = r; > > + { > > + if (parm == error_mark_node) > > + return error_mark_node; > > + DECL_CONTEXT (parm) = r; > > + } > > if (closure && DECL_IOBJ_MEMBER_FUNCTION_P (t)) > > { > > tree tparm = build_this_parm (r, closure, type_memfn_quals (type)); > > @@ -15474,6 +15478,9 @@ tsubst_decl (tree t, tree args, tsubst_flags_t > > complain, > > /* We're dealing with a normal parameter. */ > > type = tsubst (TREE_TYPE (t), args, complain, in_decl); > > + if (type == error_mark_node) > > + RETURN (error_mark_node); > > + > > type = type_decays_to (type); > > TREE_TYPE (r) = type; > > cp_apply_type_quals_to_decl (cp_type_quals (type), r); > > diff --git a/gcc/testsuite/g++.dg/cpp0x/alias-decl-80.C > > b/gcc/testsuite/g++.dg/cpp0x/alias-decl-80.C > > new file mode 100644 > > index 000000000000..e2ff663843de > > --- /dev/null > > +++ b/gcc/testsuite/g++.dg/cpp0x/alias-decl-80.C > > @@ -0,0 +1,14 @@ > > +// PR c++/120224 > > +// { dg-do compile { target c++11 } } > > + > > +template<class> using void_t = void; > > + > > +template<class T> > > +void f(void*); // #1 > > + > > +template<class T> > > +void f(void_t<typename T::type>*) { } // { dg-error "not a class" } #2 > > + > > +int main() { > > + f<int>(0); // { dg-error "no match" } > > +} > > diff --git a/gcc/testsuite/g++.dg/other/default13.C > > b/gcc/testsuite/g++.dg/other/default13.C > > index eae23ffdf2d1..381aee78ea2c 100644 > > --- a/gcc/testsuite/g++.dg/other/default13.C > > +++ b/gcc/testsuite/g++.dg/other/default13.C > > @@ -8,4 +8,4 @@ template < typename > struct B > > int f; > > }; > > -B < int > b (0); > > +B < int > b (0); // { dg-error "no match" } > >