public inbox for gcc-bugs@sourceware.org
help / color / mirror / Atom feed
* [Bug c++/96516] New: template + __attribute__((copy)) produce compiler errors
@ 2020-08-07  8:37 sagebar at web dot de
  2020-08-07 13:44 ` [Bug c++/96516] " msebor at gcc dot gnu.org
                   ` (3 more replies)
  0 siblings, 4 replies; 5+ messages in thread
From: sagebar at web dot de @ 2020-08-07  8:37 UTC (permalink / raw)
  To: gcc-bugs

https://gcc.gnu.org/bugzilla/show_bug.cgi?id=96516

            Bug ID: 96516
           Summary: template + __attribute__((copy)) produce compiler
                    errors
           Product: gcc
           Version: 9.1.0
            Status: UNCONFIRMED
          Severity: normal
          Priority: P3
         Component: c++
          Assignee: unassigned at gcc dot gnu.org
          Reporter: sagebar at web dot de
  Target Milestone: ---

Created attachment 49016
  --> https://gcc.gnu.org/bugzilla/attachment.cgi?id=49016&action=edit
Same code as already seen in the bug description

Using `__attribute__((copy(...)))' to inherit an `returns_nonnull' attribute
will cause a compiler error if the applied-to function's return type uses a
decltype type-expression, or has it's is-pointer-y-ness depend on a template
parameter. However, when `returns_nonnull' is not inherited via `copy()', but
explicitly used, then everything still works fine, so there appears to be a
discrepency between using `copy()' to inherit `returns_nonnull', and specifying
`returns_nonnull' directly.

```
__attribute__((returns_nonnull)) void *base(void *);

                  __attribute__((copy(base))) __typeof__((void *)0) wrap1(void
*);
                  __attribute__((copy(base)))   decltype((void *)0) wrap2(void
*);
template<class T> __attribute__((copy(base)))      T               *wrap3(T *);
template<class T> __attribute__((copy(base)))      decltype(T())   *wrap4(T *);
template<class T> __attribute__((returns_nonnull)) decltype((T *)0) wrap5(T *);
template<class T> __attribute__((copy(base)))      decltype((T *)0) wrap6(T *);
template<class T> __attribute__((copy(base)))    __typeof__((T *)0) wrap7(T *);
template<class T> __attribute__((copy(base)))      T                wrap8(T);
template<class T> __attribute__((returns_nonnull)) T                wrap9(T);
```

All of these `wrapN()' functions should work the same, and none of them should
produce an error, but `wrap6()', `wrap7()' and `wrap8()' produce errors:
`returns_nonnull attribute on a function not returning a pointer'

I know that `__attribute__((copy(...)))' is a gcc extension, and this bug only
happens with g++, and I also know that it being an extension, it's correct
behavior isn't written in stone. But in this example, it's behavior is plainly
inconsistent (if `returns_nonnull' wasn't allowed with a template-dependent
return expression, then `wrap5()' and `wrap9()' should also cause errors, even
though they don't), and as already stated, _all_ of the above should work
without issues in my opinion.

I've placed this Report under c++ since this bug seems to be specific to the
combination of `template' + `__attribute__((copy))', where the template-part is
specific to c++. But if I had to guess, this is probably caused by a minor
difference in how `copy()' sets the `returns_nonnull' attribute vs. how
directly making use of `returns_nonnull' does the same.


Note: The same thing also happens with `__attribute((malloc))' and
`__attribute((nonnull(...)))' (in the case of `nonnull(...)', it even appears
to produce errors for _all_ template-functions that use `copy()' (`wrap5' and
`wrap9' continue to work correctly, so this is most definitely a problem with
`copy()')) (s.a. the attached `bug.cc`, which is replicated in the following):
```
__attribute__((returns_nonnull, malloc, nonnull(1))) void *base(void *);

                  __attribute__((copy(base)))             __typeof__((void *)0)
wrap1(decltype((void *)0));
                  __attribute__((copy(base)))               decltype((void *)0)
wrap2(decltype((void *)0));
template<class T> __attribute__((copy(base)))                  T              
*wrap3(decltype((T *)0));
template<class T> __attribute__((copy(base)))                  decltype(T())  
*wrap4(decltype((T *)0));
template<class T> __attribute__((returns_nonnull, nonnull(1))) decltype((T *)0)
wrap5(decltype((T *)0));
template<class T> __attribute__((copy(base)))                  decltype((T *)0)
wrap6(decltype((T *)0));
template<class T> __attribute__((copy(base)))                __typeof__((T *)0)
wrap7(decltype((T *)0));
template<class T> __attribute__((copy(base)))                  T               
wrap8(T);
template<class T> __attribute__((returns_nonnull, nonnull(1))) T               
wrap9(T);
```
The following are the errors produced by the above code. Note that inheriting
`__attribute__((malloc))' causes the `returns_nonnull` error to become
duplicated for `wrap6()', `wrap7()' and `wrap8()' for some reason (possibly an
unrelated bug), and that `wrap5()' and `wrap9()' (i.e. the declarations that
don't use `copy()') produces no errors or warnings:
```
[wrap3]: warning: 'nonnull' attribute argument value '1' refers to parameter
type 'decltype ((T*)(0))' [-Wattributes]
[wrap4]: warning: 'nonnull' attribute argument value '1' refers to parameter
type 'decltype ((T*)(0))' [-Wattributes]
[wrap6]: warning: 'malloc' attribute ignored [-Wattributes]
[wrap6]: warning: 'nonnull' attribute argument value '1' refers to parameter
type 'decltype ((T*)(0))' [-Wattributes]
[wrap6]: error: returns_nonnull attribute on a function not returning a pointer
[wrap6]: error: returns_nonnull attribute on a function not returning a pointer
[wrap7]: warning: 'malloc' attribute ignored [-Wattributes]
[wrap7]: warning: 'nonnull' attribute argument value '1' refers to parameter
type 'decltype ((T*)(0))' [-Wattributes]
[wrap7]: error: returns_nonnull attribute on a function not returning a pointer
[wrap7]: error: returns_nonnull attribute on a function not returning a pointer
[wrap8]: warning: 'malloc' attribute ignored [-Wattributes]
[wrap8]: warning: 'nonnull' attribute argument value '1' refers to parameter
type 'T' [-Wattributes]
[wrap8]: error: returns_nonnull attribute on a function not returning a pointer
[wrap8]: error: returns_nonnull attribute on a function not returning a pointer
```

^ permalink raw reply	[flat|nested] 5+ messages in thread

* [Bug c++/96516] template + __attribute__((copy)) produce compiler errors
  2020-08-07  8:37 [Bug c++/96516] New: template + __attribute__((copy)) produce compiler errors sagebar at web dot de
@ 2020-08-07 13:44 ` msebor at gcc dot gnu.org
  2020-08-07 13:45 ` msebor at gcc dot gnu.org
                   ` (2 subsequent siblings)
  3 siblings, 0 replies; 5+ messages in thread
From: msebor at gcc dot gnu.org @ 2020-08-07 13:44 UTC (permalink / raw)
  To: gcc-bugs

https://gcc.gnu.org/bugzilla/show_bug.cgi?id=96516

Martin Sebor <msebor at gcc dot gnu.org> changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
                 CC|                            |msebor at gcc dot gnu.org
   Last reconfirmed|                            |2020-08-07
             Status|UNCONFIRMED                 |NEW
     Ever confirmed|0                           |1

--- Comment #1 from Martin Sebor <msebor at gcc dot gnu.org> ---
I think the C++ front end needs to hold off applying the copy attribute until
the template it's on is instantiated.  This is hardcoded for other attributes
but not for copy, so it won't work very well with templates.

This doesn't work correctly either (although probably for a different reason):

template <class T>
__attribute__((returns_nonnull)) T foo () { return T (); }

__attribute__((copy (foo<void*>))) void* bar () { return 0; }

t.C:4:47: warning: ‘copy’ attribute ignored on a declaration of a different
kind than referenced symbol [-Wattributes]
    4 | __attribute__((copy (foo<void*>))) void* bar () { return 0; }
      |                                               ^

^ permalink raw reply	[flat|nested] 5+ messages in thread

* [Bug c++/96516] template + __attribute__((copy)) produce compiler errors
  2020-08-07  8:37 [Bug c++/96516] New: template + __attribute__((copy)) produce compiler errors sagebar at web dot de
  2020-08-07 13:44 ` [Bug c++/96516] " msebor at gcc dot gnu.org
@ 2020-08-07 13:45 ` msebor at gcc dot gnu.org
  2020-08-07 15:17 ` sagebar at web dot de
  2020-08-07 15:25 ` sagebar at web dot de
  3 siblings, 0 replies; 5+ messages in thread
From: msebor at gcc dot gnu.org @ 2020-08-07 13:45 UTC (permalink / raw)
  To: gcc-bugs

https://gcc.gnu.org/bugzilla/show_bug.cgi?id=96516

--- Comment #2 from Martin Sebor <msebor at gcc dot gnu.org> ---
Thank you for taking the time to put together all the test cases and reporting
the problem, by the way!

^ permalink raw reply	[flat|nested] 5+ messages in thread

* [Bug c++/96516] template + __attribute__((copy)) produce compiler errors
  2020-08-07  8:37 [Bug c++/96516] New: template + __attribute__((copy)) produce compiler errors sagebar at web dot de
  2020-08-07 13:44 ` [Bug c++/96516] " msebor at gcc dot gnu.org
  2020-08-07 13:45 ` msebor at gcc dot gnu.org
@ 2020-08-07 15:17 ` sagebar at web dot de
  2020-08-07 15:25 ` sagebar at web dot de
  3 siblings, 0 replies; 5+ messages in thread
From: sagebar at web dot de @ 2020-08-07 15:17 UTC (permalink / raw)
  To: gcc-bugs

https://gcc.gnu.org/bugzilla/show_bug.cgi?id=96516

--- Comment #3 from sagebar at web dot de ---
No problem. Also: What you just said made me realize that once
__attribute__((copy)) works correctly with templates, _any_ attribute that can
be inherited via copy() can be made template-conditional in c++:

```
template<bool B> struct conditional_returns_nonnull {
        static __attribute__((returns_nonnull)) void *func(void);
};
template<> struct conditional_returns_nonnull<false> {
        static void *func(void);
};

template<bool B> __attribute__((copy(conditional_returns_nonnull<B>::func)))
void *function_with_conditional_returns_nonnull_attribute();

#define SASS(x) static_assert(x, #x)
SASS(!__builtin_has_attribute(
        conditional_returns_nonnull<false>::func,
        returns_nonnull));
SASS(__builtin_has_attribute(
        conditional_returns_nonnull<true>::func,
        returns_nonnull));

SASS(!__builtin_has_attribute(
        function_with_conditional_returns_nonnull_attribute<false>,
        returns_nonnull));
SASS(__builtin_has_attribute(
        function_with_conditional_returns_nonnull_attribute<true>,
        returns_nonnull));
```

Currently, this doesn't compile due to the last `static_assert()' failing
(Which I'm guessing is due to the same bug)
-> So I guess: Here's another test

^ permalink raw reply	[flat|nested] 5+ messages in thread

* [Bug c++/96516] template + __attribute__((copy)) produce compiler errors
  2020-08-07  8:37 [Bug c++/96516] New: template + __attribute__((copy)) produce compiler errors sagebar at web dot de
                   ` (2 preceding siblings ...)
  2020-08-07 15:17 ` sagebar at web dot de
@ 2020-08-07 15:25 ` sagebar at web dot de
  3 siblings, 0 replies; 5+ messages in thread
From: sagebar at web dot de @ 2020-08-07 15:25 UTC (permalink / raw)
  To: gcc-bugs

https://gcc.gnu.org/bugzilla/show_bug.cgi?id=96516

--- Comment #4 from sagebar at web dot de ---
(In reply to sagebar from comment #3)
> ..., _any_ attribute that
> can be inherited via copy() can be made template-conditional in c++:

Also note that I've tested if gcc (`-x c') allows multiple copy attributes on
the same declaration, and it appears to do allow this, meaning that deferring
attribute copy until template instantiation should be made functional for 0-N
copy attributes, rather than 0-1:

gcc currently allows multiple __attribute__((copy)) within the same
declaration:
```

__attribute__((returns_nonnull)) void *func_with_returns_nonnull();
__attribute__((malloc)) void *func_with_malloc();

__attribute__((
        copy(func_with_returns_nonnull),
        copy(func_with_malloc)))
void *func_with_both();

#define SASS(x) static_assert(x, #x)
SASS(__builtin_has_attribute(func_with_returns_nonnull, returns_nonnull));
SASS(!__builtin_has_attribute(func_with_returns_nonnull, malloc));
SASS(!__builtin_has_attribute(func_with_malloc, returns_nonnull));
SASS(__builtin_has_attribute(func_with_malloc, malloc));
SASS(__builtin_has_attribute(func_with_both, returns_nonnull));
SASS(__builtin_has_attribute(func_with_both, malloc));
```

^ permalink raw reply	[flat|nested] 5+ messages in thread

end of thread, other threads:[~2020-08-07 15:25 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-08-07  8:37 [Bug c++/96516] New: template + __attribute__((copy)) produce compiler errors sagebar at web dot de
2020-08-07 13:44 ` [Bug c++/96516] " msebor at gcc dot gnu.org
2020-08-07 13:45 ` msebor at gcc dot gnu.org
2020-08-07 15:17 ` sagebar at web dot de
2020-08-07 15:25 ` sagebar at web dot de

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).