No other compilers in C++ mode support this, so I’d be just as happy to have us make this an error to be more consistent with the other implementations.
Does anyone have a good reason why we should keep this extension? It seems thoroughly unmotivated.
The really fun part is that clang rejects arithmetic on a void*, but gcc accepts it as an extentsion and MSVC rejects both: Compiler Explorer. I also don’t really see how either of them are a useful extension. It just makes it more likely to introduce bugs.
It was suggested on the defect report that perhaps we could do Warn-As-Error, and release-notes document that it will be going away in a future release.
I’m in favor of this change in theory, but have two practical situations I’m worried about. Dereferencing void * very rarely makes sense, but consider:
When you call this templated function with a void *, I believe it’s harmless to dereference the result to return it because the return type of the function is void: Compiler Explorer. AIUI, this situation comes up in generic programming from time to time.
The other situation is in unevaluated contexts. I don’t see the harm in allowing something like:
I don’t know how common this code is, but I have a hazy recollection of it being related to why we accept this, similar to how C++ accepts returning a void expression: [stmt.return]
For your first exmaple, Both GCC and MSVC reject that for the same reason: Compiler Explorer
Note that the remove_pointer_t part is fine, but the dereference in the return is important there. NOTE of course that the dereferenceable concept that motivated this patch is actually commonly used for exactly this situation!
For the second example, the same: GCC and MSVC already reject it: Compiler Explorer
While I also don’t see the harm of it, I don’t see value in allowing it if no one else does.
Not everyone worries about cross-compiler compatibility; if Clang users are relying on the behavior in reasonable circumstances, we should be supporting them.
That said, I still think it’s reasonable to see how well this behavior change goes over with our users. This search does not suggest to me that there’s considerable usage in the wild. It might be hard for us to test the patch ourselves because I’m not aware of any large Clang-only corpus of code that we can test against, but that would raise our confidence if you know of any.
So my feeling is (having seen that search result), let’s move forward with it and identify it as a potentially breaking change, and see how it goes.
I’m pretty sure this is always always a bug.
Ie, i don’t think in generic context we can establish a meaningful relation between void* and an object of type void like we do for any other T (both because “object of type void” is currently nonsensical and because a void* is not really a pointer to void, even in an unevaluated context, and we are misled by the spellings void and void*
I agree that void can be painful to deal with but I don’t think clang should try to solve that problem that way, it’s not portable and error prone.
I’d move ahead and reconsider if we find issues in real world scenario.
This seems very undesirable. It’s one thing to allow it in non-SFINAE contexts, where you just turn ill-formed code into well-formed, but this change the meaning of valid code in observable ways. The function above should result in a substitution error for func<void>.
GCC’s arithmetic on void* extension (and related sizeof void extension) is disabled in SFINAE and constraint checks, so the answer to “can you do arithmetic on void*?” is always no (even though actually you can do it).
The point of GCC’s arithmetic on void extension is so you can just write ptr + n instead of (void*)((char*)ptr + n) i.e. use a void* as an arbitrary pointer to untyped memory, which is common in systems programming (c.f. caddr_t in old UNIXes). What is the result of adding 4 to the address 0x1234? Well it’s 0x1238 of course. That’s what you get when using char* or unsigned char* or byte*, but unlike those pointers, with void* you can’t dereference it (except with clang ) and there’s no mistaking the result for a string of char or other character data. Arguably, using char* or unsigned char* as the general purpose “pointer to arbitrary memory” type makes less sense than using void*, but you’re forced to do that for any arithmetic, at least before we got std::byte.
I find it much harder to justify GCC allowing arithmetic on function pointers though!