On Mon, Mar 09, 2026 at 05:53:35PM -0400, Jason Merrill wrote:
> On 3/9/26 3:49 PM, Marek Polacek wrote:
> > 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 what seems right to me.  But now I realize the standard doesn't
> actually say when (any) attributes are substituted; that seems like an
> omission if we're going to say such substitution is not in the immediate
> context of template argument deduction substitution; more clarity is needed.

Aha, thanks.  So for noexcept we follow [temp.inst]/14: The noexcept-specifier
and function-contract-specifiers of a function template specialization are
not instantiated along with the function declaration; they are instantiated
when needed.

...which was added by CWG 1330.  But for attributes we have no such wording.
I can email the reflector with the test above and ask "when are attributes
supposed to be instantiated?", if you think it would be useful.

I won't pursue this PR further right now; it's hardly a blocker.

> > 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.

Marek

Reply via email to