Bug 71463 - [6/7 regression] unexpected warning: ignoring function return attributes on template argument
Summary: [6/7 regression] unexpected warning: ignoring function return attributes on t...
Status: RESOLVED INVALID
Alias: None
Product: gcc
Classification: Unclassified
Component: c++ (show other bugs)
Version: 6.1.1
: P2 normal
Target Milestone: 6.4
Assignee: Not yet assigned to anyone
URL:
Keywords: diagnostic
Depends on:
Blocks:
 
Reported: 2016-06-08 21:25 UTC by Milian Wolff
Modified: 2017-01-25 13:59 UTC (History)
4 users (show)

See Also:
Host:
Target:
Build:
Known to work: 5.4.0
Known to fail: 6.1.0, 7.0
Last reconfirmed: 2016-06-13 00:00:00


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Milian Wolff 2016-06-08 21:25:57 UTC
I've seen this error with gcc (GCC) 6.1.1 20160501 and code like this:

~~~~~~~~~~~~~~
#include <cstdlib>

template<typename T> struct foo {};
foo<decltype(&::malloc)> emit_unexpected_warning;

int main() { return 0; }
~~~~~~~~~~~~~~

compiling it without optimizations shows no warning, but from -O1 and onwards I get the unexpected warning on ignored template argument attributes:

~~~~~~~~~~~~~~~
$ g++ -std=c++11 -Wall -Wpedantic -O0 test.cpp
# no warnings
$ g++ -std=c++11 -Wall -Wpedantic -O1 test.cpp
test.cpp:9:24: warning: ignoring attributes on template argument ‘void* (*)(size_t) throw () {aka void* (*)(long unsigned int) throw ()}’ [-Wignored-attributes]
 foo<decltype(&::malloc)> emit_unexpected_warning;
~~~~~~~~~~~~~~~

Clang does not emit any such warning. I believe it's a false-positive, especially considering that it only shows up in -O1 and higher, but not -O0.
Comment 1 Andrew Pinski 2016-06-08 21:30:43 UTC
Well I think the preprocessed source changes between -O0 and -O1 which is why there is a difference there.
Comment 2 Milian Wolff 2016-06-09 09:05:32 UTC
Indeed, there is a difference between malloc in the two levels:

-O0:
~~~~~~~~~~~~
extern void *malloc (size_t __size) throw () __attribute__ ((__malloc__)) ;
~~~~~~~~~~~~

-O1:
~~~~~~~~~~~~
extern void *malloc (size_t __size) throw () __attribute__ ((__malloc__)) __attribute__ ((__warn_unused_result__));
~~~~~~~~~~~~

This simplifies the testcase to:

~~~~~~~~~~~~
void* malloc() __attribute__ ((__warn_unused_result__));

template<typename Ptr> struct foo {};
foo<decltype(&malloc)> emit_unexpected_warning;

int main() { return 0; }
~~~~~~~~~~~~

So, do you say this warning is an expected result? I find it highly unexpected. Can I silence this warning somehow? 

Also, how come that `decltype()` is able to return me a type including the attributes, when these are not storable in the type system? Is that maybe the root of the issue here? Or are there cases where one needs to access the attributes via decltype? I can't think of a way to compare / access that information then.

Any input would be appreciated, thanks!
Comment 3 Andrew Pinski 2016-06-12 21:03:55 UTC
__warn_unused_result__ is only used when Fortified is used.  Which is not the default unless you are using a distro compiler.

#if defined _FORTIFY_SOURCE && _FORTIFY_SOURCE > 0 \
    && __GNUC_PREREQ (4, 1) && defined __OPTIMIZE__ && __OPTIMIZE__ > 0
# if _FORTIFY_SOURCE > 1
#  define __USE_FORTIFY_LEVEL 2
# else
#  define __USE_FORTIFY_LEVEL 1
# endif
#else
# define __USE_FORTIFY_LEVEL 0
#endif

.....

/* If fortification mode, we warn about unused results of certain
   function calls which can lead to problems.  */
#if __GNUC_PREREQ (3,4)
# define __attribute_warn_unused_result__ \
   __attribute__ ((__warn_unused_result__))
# if __USE_FORTIFY_LEVEL > 0
#  define __wur __attribute_warn_unused_result__
# endif
#else
# define __attribute_warn_unused_result__ /* empty */
#endif
#ifndef __wur
# define __wur /* Ignore */
#endif
Comment 4 Martin Sebor 2016-06-13 15:19:47 UTC
I looked into this a bit and I'm not sure the warning works quite as intended in this case.

First, the warning doesn't say which of the attributes is ignored, and so when multiple attributes are specified, it suggests that all of them are ignored.  But tests with single attributes show that only some trigger the warning, raising the question of whether the warning works correctly.

Second, it's unclear to me what purpose the warning is meant to serve in this case.  Since a function attribute always applies to the instance of the function it decorates and never affects its type the warning doesn't indicate anything unusual or unexpected, and only serves to confuse users.  (In cases where the function is declared in a system header it's also unclear how the should be avoided.)

Looking at the history of the warning for the test case, it started with r222530 committed to fix bug 50800 which has to do with type attributes, not those of functions (or variables), and there is no test that verifies that it should be issued for the case of functions (or variables).  I'm inclined to agree that this is a bug.  Confirming as a 6/7 regression with the test case below:

$ cat t.C && /home/msebor/build/gcc-6-branch/gcc/xgcc -B /home/msebor/build/gcc-6-branch/gcc -S -Wall -Wextra -Wpedantic t.C
void* __attribute__ ((assume_aligned (32))) f0 ();
void* __attribute__ ((returns_nonnull)) f1 ();

void* __attribute__ ((const)) f2 ();
void* __attribute__ ((const, warn_unused_result)) f3 ();

template <class T> struct S { };

S<decltype (&f0)> s0;
S<decltype (&f1)> s1;
S<decltype (&f2)> s2;   // no warning
S<decltype (&f3)> s3;   // which of the two attributes are ignored?
t.C:9:17: warning: ignoring attributes on template argument ‘void* (*)()’ [-Wignored-attributes]
 S<decltype (&f0)> s0;
                 ^
t.C:10:17: warning: ignoring attributes on template argument ‘void* (*)()’ [-Wignored-attributes]
 S<decltype (&f1)> s1;
                 ^
t.C:12:17: warning: ignoring attributes on template argument ‘void* (*)()’ [-Wignored-attributes]
 S<decltype (&f3)> s3;   // which of the two attributes are ignored?
                 ^
Comment 5 Jason Merrill 2016-07-15 00:02:10 UTC
(In reply to Martin Sebor from comment #4)
> Second, it's unclear to me what purpose the warning is meant to serve in
> this case.  Since a function attribute always applies to the instance of the
> function it decorates and never affects its type the warning doesn't
> indicate anything unusual or unexpected, and only serves to confuse users. 

Actually, most of these attributes, including warn_unused_result, apply to the function type, not to the function declaration.  That's why you get the warning.  As an example of this:

__attribute ((warn_unused_result)) int f();

decltype(f)* p;

int main()
{
  p(); // warns about unused result, by design.
}

The warning is letting you know that the attribute is being discarded, so you don't get the warning within a template:

__attribute ((warn_unused_result)) int f();

template <class T>
struct A {
  T *p;
  void g() { p(); }             // no warning                                                                  
};

int main()
{
  A<decltype(f)>().g();
}

Note that the C++17 [[nodiscard]] attribute applies to the function, so it works differently.

> (In cases where the function is declared in a system header it's also
> unclear how the should be avoided.)

If you don't want this warning, -Wno-ignored-attributes will silence it.
Comment 6 Martin Sebor 2016-07-16 17:58:16 UTC
I didn't know that GCC considers attribute warn_unused_result part of the function type.  When you say that most of these attributes apply to the function type, which others are you referring to?

FWIW, I find this surprising not only because the attribute is documented as a function attribute and not one that applies to types, but also because it's different from other common function attributes that affect warnings such as attribute unused or used.  Those are not considered to be part of the function type as the test case below shows.

$ cat x.C && /home/msebor/build/gcc-trunk-svn/gcc/xgcc -B /home/msebor/build/gcc-trunk-svn/gcc -O2 -S -Wall -Wextra -Wpedantic -xc++ x.C
static void __attribute ((unused)) f_unused (void);
static void f_unused (void) { }   // no warning as expected

static void __attribute ((used)) f_used (void);
static void f_used (void) { }   // no warning as expected

static __typeof__ (f_unused) g_unused;
static void g_unused (void) { }   // emits -Wnunused-function

static __typeof__ (f_used) g_used;
static void g_used (void) { }   // emits -Wnunused-function
x.C:11:13: warning: ‘void g_used()’ defined but not used [-Wunused-function]
 static void g_used (void) { }   // emits -Wnunused-function
             ^~~~~~
x.C:8:13: warning: ‘void g_unused()’ defined but not used [-Wunused-function]
 static void g_unused (void) { }   // emits -Wnunused-function
             ^~~~~~~~

I also tried a few other others, including attribute weak and attribute nothrow, but couldn't find one where the attribute is treated as part of the function type.

In any case, if warn_unused_result is meant to be part of the function type then the right resolution is to update the documentation to make it clear.  I'm willing to put together a documentation patch if you're convinced that's the right fix though it seems to me that changing GCC to have the attribute behave like the others above would be preferable.
Comment 7 Jason Merrill 2016-07-18 16:59:02 UTC
(In reply to Martin Sebor from comment #6)
> I didn't know that GCC considers attribute warn_unused_result part of the
> function type.  When you say that most of these attributes apply to the
> function type, which others are you referring to?

Other attributes that apply to a function type: nonnull, returns_nonnull, sentinel, alloc_size, alloc_align, assume_aligned, format, format_arg.

I don't know why they were defined this way; I imagine it was to support using them on function pointers.
Comment 8 Milian Wolff 2016-07-20 23:10:36 UTC
As an interested bystander, may I ask: If the attribute is part of the type, shouldn't it then be transferred via decltype() and then also used in the template to trigger the warning there? To me, the example you gave shows a defect in GCC (no warning inside the template), and not a nice feature. What am I missing?
Comment 9 Jason Merrill 2016-07-21 05:06:32 UTC
(In reply to Milian Wolff from comment #8)
> As an interested bystander, may I ask: If the attribute is part of the type,
> shouldn't it then be transferred via decltype() and then also used in the
> template to trigger the warning there?

The attribute isn't properly part of the C++ type, so in the testcase below A<fna> needs to be the same type as A<fn>, and so they share a member function.  So we have to choose which type to use as the canonical type, and we choose the type without the attribute.  And that's what the "ignoring attributes" warning is telling you.

typedef int fn();
typedef int fna() __attribute ((warn_unused_result));

template <class T>
struct A {
  T *p;
  void g() { p(); }             // no warning                                                                  
};

int main()
{
  A<fn>().g();
  A<fna>().g();
}
Comment 10 Richard Biener 2016-08-22 08:13:38 UTC
GCC 6.2 is being released, adjusting target milestone.
Comment 11 Richard Biener 2016-08-22 08:14:41 UTC
GCC 6.2 is being released, adjusting target milestone.
Comment 12 Jakub Jelinek 2016-12-21 10:54:49 UTC
GCC 6.3 is being released, adjusting target milestone.
Comment 13 Jakub Jelinek 2017-01-10 14:01:07 UTC
So isn't this actually NOTABUG?
Comment 14 Jakub Jelinek 2017-01-10 14:03:01 UTC
> I imagine it was to support using them on function pointers.

Yes, that is the reason why they apply to function types rather than decls.
Comment 15 Martin Sebor 2017-01-10 15:52:00 UTC
As Jason explained it works as designed.  But the warning is certainly confusing.  It doesn't help that not all attributes enjoy this special treatment or that the manual doesn't explain it.  At a minimum, I think the manual needs to be updated.  It would help if the warning mentioned the names of the attributes that are being ignored and, perhaps in a note, gave a hint as to why.
Comment 16 Milian Wolff 2017-01-16 13:43:43 UTC
So how can I silence the warning then for the case I pasted in the first comment:

~~~~~~~~~~~~~~~+
#include <cstdlib>

template<typename T> struct foo {};
foo<decltype(&::malloc)> emit_unexpected_warning;

int main() { return 0; }
~~~~~~~~~~~~~~~+

Most notably when you take into account that this is only sometimes required based on the optimization level. Should I simply not use decltype and instead paste the definition of malloc inline? Or is there a "silent" decltype alternative I could use?
Comment 17 Jakub Jelinek 2017-01-25 13:59:38 UTC
Avoid decltype in that case, or use decltype on some other function that has the same arguments as malloc and same return value, just not such attributes.
void *my_malloc (size_t);
... decltype (my_malloc) ...