Bug 87760

Summary: Unable to delete overloads of std::memset on arm
Product: gcc Reporter: Federico Kircheis <federico>
Component: c++Assignee: Not yet assigned to anyone <unassigned>
Status: RESOLVED FIXED    
Severity: normal Keywords: rejects-valid
Priority: P3    
Version: 6.3.0   
Target Milestone: 7.0   
Host: Target: arm
Build: Known to work:
Known to fail: Last reconfirmed:

Description Federico Kircheis 2018-10-26 04:19:55 UTC
Hello,

I know this is technically speaking undefined behavior (not allowed to change things inside namespace std), and thus not a bug but more a feature request.
Since all version of gcc I've tested (x86 and x64), clang, icc and msvc accept following code, I think it makes sense to open this bug because it would be nice for gcc to be more consistent, since it's just gcc on arm rejecting it.

----
#include <cstring>

namespace std{
void* memset(void*, std::size_t count, int ch) = delete;
}

struct foo{ // POD
    int a;
    int* b;
    char c;
};

int main() {
    foo b[10];
    std::memset(&b, 0, sizeof b);
    std::memset(&b, 0u, sizeof b);
}
----

This code on arm64 fails to compile with following error message:

----
<source>:4:7: error: deleted definition of 'void* std::memset(void*, std::size_t, int)'

 void* memset(void*, std::size_t count, int ch) = delete;

       ^~~~~~

<built-in>: note: previous declaration of 'void* std::memset(void*, int, long unsigned int)'

Compiler returned: 1
----

As already mentioned, this hack works with different compilers and helps to avoid common errors like forgetting the right order of parameter of memset, ie it helps at compile time to spot errors where someone writes `std::memset(&b, sizeof b, 0);` instead of `std::memset(&b, 0, sizeof b);`.

Since I do not have locally an arm version, I tested it on compiler explorer: https://godbolt.org/z/aTX9FZ


Also note that removing the overload from the global namespace is not an issue on arm64, ie

----
#include <cstring>

void* memset(void*, std::size_t count, int ch) = delete;

struct foo{ // POD
    int a;
    int* b;
    char c;
};

int main() {
    foo b[10];
    memset(&b, 0, sizeof b);
    memset(&b, 0u, sizeof b);
}
----

compiles fine (except on arm non x64 because the overload is ambiguous, but AFAIK there is nothing we can do about it).
Comment 1 Richard Biener 2018-10-26 07:29:29 UTC
I can only guess that somehow std::size_t is int?!

Can you reproduce it with a simple

void foo (int, std::size_t);
void foo (std::size_t, int) = delete;

that is, avoiding 'memset' and/or the std namespace?  Eventually

namespace bar {
  using ::foo;
}

to mimic what cstring does for memset?
Comment 2 Richard Earnshaw 2018-10-26 09:13:47 UTC
I think this must be some issue with the compiler explorer installation. 
My native AArch64 box compiles your testcase without problems (gcc-7.3).

It's completely perverse that size_t would be an int type, as the ABI requires an unsigned long.

Also note that arm64 (correctly, AArch64) != arm; they're separate compilers.
Comment 3 Jonathan Wakely 2018-10-26 09:27:22 UTC
This is already fixed in gcc-7.

Reduced testcase:

extern "C" void* memset(void*, int, long unsigned int);

namespace std{
  using ::memset;
  using size_t = decltype(sizeof(0));
  void* memset(void*, std::size_t count, int ch) = delete;
}

struct foo{ // POD
    int a;
    int* b;
    char c;
};

int main() {
    foo b[10];
    std::memset(&b, 0, sizeof b);
    std::memset(&b, 0u, sizeof b);
}

GCC 6 says:

memset.cc:6:9: error: deleted definition of 'void* std::memset(void*, std::size_t, int)'
   void* memset(void*, std::size_t count, int ch) = delete;
         ^~~~~~
<built-in>: note: previous declaration of 'void* std::memset(void*, int, long unsigned int)'


But GCC 7 and later accept it.
Comment 4 Jonathan Wakely 2018-10-26 09:29:46 UTC
It was fixed by r242662 for PR c++/71973
Comment 5 Richard Earnshaw 2018-10-26 09:31:10 UTC
So changing to fixed.  gcc-6 has just been frozen for the final release.