On Thu, Mar 05, 2026 at 10:46:22PM -0500, Jason Merrill wrote:
> On 3/5/26 7:03 PM, Marek Polacek wrote:
> > Bootstrapped/regtested on x86_64-pc-linux-gnu, ok for trunk?
> >
> > -- >8 --
> > I noticed that we don't issue an error in the test in [dcl.attr.annotation]
> > for /4: "Substituting into an annotation is not in the immediate context":
> >
> > template<class T>
> > [[=T::type()]] void f(T t);
> >
> > void f(int);
> >
> > void g() {
> > f(0); // OK
> > f('0'); // error, substituting into the annotation results in an
> > invalid expression
> > }
> >
> > PR c++/124381
> >
> > gcc/cp/ChangeLog:
> >
> > * pt.cc (tsubst_attribute): Always complain for annotations.
> >
> > gcc/testsuite/ChangeLog:
> >
> > * g++.dg/reflect/annotations13.C: New test.
> > ---
> > gcc/cp/pt.cc | 5 +++++
> > gcc/testsuite/g++.dg/reflect/annotations13.C | 15 +++++++++++++++
> > 2 files changed, 20 insertions(+)
> > create mode 100644 gcc/testsuite/g++.dg/reflect/annotations13.C
> >
> > diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc
> > index ddf492b3435..36b9fbeb0b6 100644
> > --- a/gcc/cp/pt.cc
> > +++ b/gcc/cp/pt.cc
> > @@ -12347,6 +12347,11 @@ tsubst_attribute (tree t, tree *decl_p, tree args,
> > {
> > gcc_assert (ATTR_IS_DEPENDENT (t));
> > + /* [dcl.attr.annotation]/4: Substituting into an annotation is not in
> > + the immediate context. */
> > + if (annotation_p (t))
> > + complain = tf_warning_or_error;
> > +
> > tree val = TREE_VALUE (t);
> > if (val == NULL_TREE)
> > /* Nothing to do. */;
> > diff --git a/gcc/testsuite/g++.dg/reflect/annotations13.C
> > b/gcc/testsuite/g++.dg/reflect/annotations13.C
> > new file mode 100644
> > index 00000000000..19306956e78
> > --- /dev/null
> > +++ b/gcc/testsuite/g++.dg/reflect/annotations13.C
> > @@ -0,0 +1,15 @@
> > +// PR c++/124381
> > +// { dg-do compile { target c++26 } }
> > +// { dg-additional-options "-freflection" }
> > +// Test from [dcl.attr.annotation].
> > +
> > +template<class T>
> > +[[=T::type()]] void f(T t); // { dg-error "not a member" }
> > +
> > +void f(int);
> > +
> > +void g() {
> > + f(0); // OK
> > + f('0'); // error, substituting into the annotation results in an
> > invalid expression
>
> What if there's another template that's a better match? I'm concerned that
> this only works because f(int) is a perfect match for the first call so we
> never even form the candidate from the template.
>
> We don't want this error until we've chosen the annotated template as the
> best candidate.
If that's really then case then I'm misunderstanding something. In
```
template<typename T>
auto f(T, long) -> decltype([]() { T::invalid; } ()); // #1
template<typename T>
void f(T, int); // #2
void
g ()
{
f (0, 0);
}
```
we report a hard error due to [temp.deduct]/9: "When substituting into
a lambda-expression, substitution into its body is not in the immediate
context." even though #2 is a better match. So I thought this was the
same case.
Note though that the approach this patch takes and what we do in
tsubst_lambda_expr:
/* [temp.deduct] A lambda-expression appearing in a function type or a
template parameter is not considered part of the immediate context for
the purposes of template argument deduction. */
complain = tf_warning_or_error;
is flawed because it can cause the "error reporting routines re-entered"
crash. I opened c++/124397. Either we should not override complain
with tf_warning_or_error when we are dumping from cp_printer, or only do
it when in the fn_type_unification context? But I don't see any flags for
either.
Marek