Message ID | CADzB+2=6yb+1Jf2xA6to+UDHBh6sqVk1M4A0o8Q3nqdDa0Kvxw@mail.gmail.com |
---|---|
State | New |
Headers | show |
On 02/16/2017 12:49 PM, Jason Merrill wrote: > On Thu, Feb 16, 2017 at 11:33 AM, Jakub Jelinek <jakub@redhat.com> wrote: >> PR c++/79502 >> * pt.c (apply_late_template_attributes): If there are >> no dependent attributes, set *p to attributes. If there were >> some attributes in *p previously with or without dependent >> attributes, chain them after the new attributes. > > Here's the variant of your patch that I'm applying. Sorry to butt in but I feel like I'm missing something basic. Are these attributes (nodiscard, noreturn, maybe_unused, and deprecated) meant to apply to templates? The text in for nodiscard suggests they're not: The attribute-token nodiscard may be applied to the declarator-id in a function declaration or to the declaration of a class or enumeration. Noreturn also doesn't mention templates: The attribute may be applied to the declarator-id in a function declaration. Deprecated explicitly mentions template specializations but not primary templates: The attribute may be applied to the declaration of a class, a typedef-name, a variable, a non-static data member, a function, a namespace, an enumeration, an enumerator, or a template specialization. I can certainly see how applying attributes to the primary template would be useful so it's puzzling to me that the standard seems to preclude it. I ask also because I was just looking at bug 79021 and scratching my head about what to thing about it. While trying to understand how GCC handles attributes for the primary template I came across what doesn't make sense to me. Why would it apply the attribute from the primary to the explicit specialization when the two are distinct entities? Is that a bug? template <class T> [[noreturn]] int f () { throw ""; } template <> int f<void> () { return 0; } t.C: In function ‘int f() [with T = void]’: t.C:4:37: warning: function declared ‘noreturn’ has a ‘return’ statement template <> int f<void> () { return 0; } ^ t.C:4:37: warning: ‘noreturn’ function does return template <> int f<void> () { return 0; } ^ Clang complains on this too with similar errors, but then GCC silently accepts this code (which makes sense to me) which Clang 5 rejects with what looks like a bug: template <class T> int g () { return 0; } template <> [[noreturn]] int g<void> () { throw ""; } template <> [[noreturn]] int g<int> () { throw ""; } Thanks Martin
On Thu, Feb 16, 2017 at 6:13 PM, Martin Sebor <msebor@gmail.com> wrote: > On 02/16/2017 12:49 PM, Jason Merrill wrote: >> >> On Thu, Feb 16, 2017 at 11:33 AM, Jakub Jelinek <jakub@redhat.com> wrote: >>> >>> PR c++/79502 >>> * pt.c (apply_late_template_attributes): If there are >>> no dependent attributes, set *p to attributes. If there were >>> some attributes in *p previously with or without dependent >>> attributes, chain them after the new attributes. >> >> >> Here's the variant of your patch that I'm applying. > > > Sorry to butt in but I feel like I'm missing something basic. Are > these attributes (nodiscard, noreturn, maybe_unused, and deprecated) > meant to apply to templates? The text in for nodiscard suggests > they're not: > > The attribute-token nodiscard may be applied to the declarator-id > in a function declaration or to the declaration of a class or > enumeration. > > Noreturn also doesn't mention templates: > > The attribute may be applied to the declarator-id in a function > declaration. > > Deprecated explicitly mentions template specializations but not > primary templates: > > The attribute may be applied to the declaration of a class, > a typedef-name, a variable, a non-static data member, a function, > a namespace, an enumeration, an enumerator, or a template > specialization. > > I can certainly see how applying attributes to the primary template > would be useful so it's puzzling to me that the standard seems to > preclude it. I don't think it's precluded; a /template-declaration/ syntactically includes a /declaration/, so in general any statement about e.g. a function declaration also applies to a function template declaration. > I ask also because I was just looking at bug 79021 and scratching > my head about what to thing about it. While trying to understand > how GCC handles attributes for the primary template I came across > what doesn't make sense to me. Why would it apply the attribute > from the primary to the explicit specialization when the two are > distinct entities? Is that a bug? This seems like a Core issue; the standard says nothing about how attributes on a template affect specializations. I think that as a general rule, not applying attributes from the template to specializations makes sense. There will be some exceptions, such as the abi_tag attribute which is a property of the name rather than a particular declaration. Jason
On 02/17/2017 09:53 PM, Jason Merrill wrote: > On Thu, Feb 16, 2017 at 6:13 PM, Martin Sebor <msebor@gmail.com> wrote: >> On 02/16/2017 12:49 PM, Jason Merrill wrote: >>> >>> On Thu, Feb 16, 2017 at 11:33 AM, Jakub Jelinek <jakub@redhat.com> wrote: >>>> >>>> PR c++/79502 >>>> * pt.c (apply_late_template_attributes): If there are >>>> no dependent attributes, set *p to attributes. If there were >>>> some attributes in *p previously with or without dependent >>>> attributes, chain them after the new attributes. >>> >>> >>> Here's the variant of your patch that I'm applying. >> >> >> Sorry to butt in but I feel like I'm missing something basic. Are >> these attributes (nodiscard, noreturn, maybe_unused, and deprecated) >> meant to apply to templates? The text in for nodiscard suggests >> they're not: >> >> The attribute-token nodiscard may be applied to the declarator-id >> in a function declaration or to the declaration of a class or >> enumeration. >> >> Noreturn also doesn't mention templates: >> >> The attribute may be applied to the declarator-id in a function >> declaration. >> >> Deprecated explicitly mentions template specializations but not >> primary templates: >> >> The attribute may be applied to the declaration of a class, >> a typedef-name, a variable, a non-static data member, a function, >> a namespace, an enumeration, an enumerator, or a template >> specialization. >> >> I can certainly see how applying attributes to the primary template >> would be useful so it's puzzling to me that the standard seems to >> preclude it. > > I don't think it's precluded; a /template-declaration/ syntactically > includes a /declaration/, so in general any statement about e.g. a > function declaration also applies to a function template declaration. I'm sure you're right that it's not meant to be precluded, otherwise the standard library couldn't declare noreturn the throw_with_nested function template (the only template declared noreturn in the library I could find). FWIW, a context where noreturn clearly is not applicable is function pointers. G++ accepts it there but then ignores it. Clang rejects it. I would think accepting it on function pointers would be useful as well. GCC (in C mode) accepts __attribute__ noreturn on function pointers and treats them as such (G++ does not). Another interesting context is the explicit instantiation directive. Again, the standard seems clear that noreturn isn't allowed but GCC and Microsoft Visual C++ both accept it. Clang and EDG reject it. I think rejecting it here makes sense and accepting is a bug. >> I ask also because I was just looking at bug 79021 and scratching >> my head about what to thing about it. While trying to understand >> how GCC handles attributes for the primary template I came across >> what doesn't make sense to me. Why would it apply the attribute >> from the primary to the explicit specialization when the two are >> distinct entities? Is that a bug? > > This seems like a Core issue; the standard says nothing about how > attributes on a template affect specializations. Yes, I thought so as well. Thanks for confirming that. Unless you would prefer to bring it up yourself let me post it to the core reflector and suggest to open a new issue for it. > I think that as a general rule, not applying attributes from the > template to specializations makes sense. There will be some > exceptions, such as the abi_tag attribute which is a property of the > name rather than a particular declaration. That makes sense to me. Thanks Martin
diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c index 712fb69..c468268 100644 --- a/gcc/cp/pt.c +++ b/gcc/cp/pt.c @@ -10073,29 +10073,43 @@ apply_late_template_attributes (tree *decl_p, tree attributes, int attr_flags, tree t; tree *p; - for (t = attributes; t; t = TREE_CHAIN (t)) - if (ATTR_IS_DEPENDENT (t)) - { - last_dep = t; - attributes = copy_list (attributes); - break; - } + if (attributes == NULL_TREE) + return; if (DECL_P (*decl_p)) { if (TREE_TYPE (*decl_p) == error_mark_node) return; p = &DECL_ATTRIBUTES (*decl_p); + /* DECL_ATTRIBUTES comes from copy_node in tsubst_decl, and identical + to our attributes parameter. */ + gcc_assert (*p == attributes); } else - p = &TYPE_ATTRIBUTES (*decl_p); + { + p = &TYPE_ATTRIBUTES (*decl_p); + /* TYPE_ATTRIBUTES was set up (with abi_tag and may_alias) in + lookup_template_class_1, and should be preserved. */ + gcc_assert (*p != attributes); + while (*p) + p = &TREE_CHAIN (*p); + } + for (t = attributes; t; t = TREE_CHAIN (t)) + if (ATTR_IS_DEPENDENT (t)) + { + last_dep = t; + attributes = copy_list (attributes); + break; + } + + *p = attributes; if (last_dep) { tree late_attrs = NULL_TREE; tree *q = &late_attrs; - for (*p = attributes; *p; ) + for (; *p; ) { t = *p; if (ATTR_IS_DEPENDENT (t)) diff --git a/gcc/testsuite/g++.dg/cpp0x/attrib54.C b/gcc/testsuite/g++.dg/cpp0x/attrib54.C new file mode 100644 index 0000000..e5817c3 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp0x/attrib54.C @@ -0,0 +1,21 @@ +// { dg-do compile { target c++11 } } + +inline namespace N __attribute__((__abi_tag__ ("foo"))) {} +template <typename> struct A {}; +namespace N { +template <typename> class B {}; +} +template <typename T> class __attribute__((__aligned__ (sizeof (T)))) C {}; +template <typename> struct D { + template <typename _Up> using G = C<_Up>; +}; +template <typename T> struct F { + template <typename U> struct H { + typedef typename D<T>::template G<U> I; + }; +}; +template <typename T, typename = C<T>> struct J { + C<A<const B<char>>> L; + typedef F<C<int>>::H<A<const B<char>>>::I M; + J<M> *a; +}; diff --git a/gcc/testsuite/g++.dg/cpp0x/attrib55.C b/gcc/testsuite/g++.dg/cpp0x/attrib55.C new file mode 100644 index 0000000..79d0c8c --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp0x/attrib55.C @@ -0,0 +1,21 @@ +// { dg-do compile { target c++11 } } + +inline namespace N __attribute__((__abi_tag__ ("foo"))) {} +template <typename> struct A {}; +namespace N { +template <typename> class B {}; +} +template <typename T> class __attribute__((__unused__)) C {}; +template <typename> struct D { + template <typename _Up> using G = C<_Up>; +}; +template <typename T> struct F { + template <typename U> struct H { + typedef typename D<T>::template G<U> I; + }; +}; +template <typename T, typename = C<T>> struct J { + C<A<const B<char>>> L; + typedef F<C<int>>::H<A<const B<char>>>::I M; + J<M> *a; +}; diff --git a/gcc/testsuite/g++.dg/cpp1z/nodiscard4.C b/gcc/testsuite/g++.dg/cpp1z/nodiscard4.C new file mode 100644 index 0000000..8a95c94 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp1z/nodiscard4.C @@ -0,0 +1,14 @@ +// PR c++/79502 +// { dg-do compile { target c++11 } } + +template<typename> +struct [[nodiscard]] missiles {}; + +missiles<void> make() { return {}; } +missiles<void> (*fnptr)() = make; + +int main() +{ + make(); // { dg-warning "ignoring returned value of type" } + fnptr(); // { dg-warning "ignoring returned value of type" } +} diff --git a/gcc/testsuite/g++.dg/ext/attrib53.C b/gcc/testsuite/g++.dg/ext/attrib53.C new file mode 100644 index 0000000..408433d --- /dev/null +++ b/gcc/testsuite/g++.dg/ext/attrib53.C @@ -0,0 +1,21 @@ +// { dg-do compile { target c++11 } } + +inline namespace N __attribute__((__abi_tag__ ("foo"))) {} +template <typename> struct A; +namespace N { +template <typename> class B; +} +template <typename> class C {}; +template <typename> struct D { + template <typename _Up> using G = C<_Up>; +}; +template <typename T> struct F { + template <typename U> struct H { + typedef typename D<T>::template G<U> I; + }; +}; +template <typename T, typename = C<T>> struct J { + C<A<const B<char>>> L; + typedef F<C<int>>::H<A<const B<char>>>::I M; + J<M> *a; +};