On Sat, Mar 07, 2026 at 05:38:10PM -0500, Jason Merrill wrote:
> On 3/7/26 12:04 PM, Marek Polacek wrote:
> > 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.
>
> Yes.
>
> > So I thought this was the same case.
>
> I don't think it is, though. Lambdas are unique in a lot of ways.
>
> I think this is more like deferred instantiation of noexcept: a property of
> a declaration that does not participate in overload resolution, only later
> uses of the result.
Aha, I see, so:
```
template<typename T>
[[=T::type()]] void f(T, long);
template<typename T>
void f(T, int);
void g ()
{
f (1, 1);
}
```
should compile but not if I swap long and int.
That's tricky to fix though, for noexcept we have maybe_instantiate_noexcept
but attributes are tsubsted more eagerly: tsubst_function_decl ->
apply_late_template_attributes -> tsubst_attribute and so by the time
we get around to calling finish_call_expr we've already tsubsted
annotations.
> > 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.
>
> I'm not sure if there's already anything that tries to adjust for this. It
> might be useful to make global_diagnostic_context.m_lock accessible somehow.
Sounds good; I posted a patch for that.
Marek