Bug 80886 - __builtin_constant_p magic has broken at some point
Summary: __builtin_constant_p magic has broken at some point
Status: RESOLVED INVALID
Alias: None
Product: gcc
Classification: Unclassified
Component: c++ (show other bugs)
Version: 7.3.1
: P3 normal
Target Milestone: ---
Assignee: Not yet assigned to anyone
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2017-05-26 10:25 UTC by steveren
Modified: 2022-02-14 03:03 UTC (History)
2 users (show)

See Also:
Host:
Target:
Build:
Known to work: 6.4.0
Known to fail: 7.3.0, 8.0.1
Last reconfirmed:


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description steveren 2017-05-26 10:25:46 UTC
I'm using g++ for an embedded system, and as is common with such things, hardware addresses are provided as hex constants. I want to create pointers from them, using constexpr because they are known at compile time.

Now the C++ Standard forbids reinterpret_cast<> or the equivalent in a constexpr so it can't be done directly, which is annoying but that's the Standard.

However, with older versions of g++, one could solve this with the magic use of __builtin_constant_p() in a ternary expression. Thus:

test.cpp:
    #define CONST(x) (__builtin_constant_p(x) ? x : x)

    constexpr void *phardware {CONST ((void *) 0x1000)};

(This is sufficient for a complete test program, BTW)

This use appears to be documented, although it's not 100% clear. It's certainly very desirable.

My cross-development system recently upgraded from 4.9.2 to 6.2.1 and the magic has stopped working. The loss of magic holds in other 6.x versions I've tried, but still works in clang 3.8.1 which is the latest version in my distro (Fedora 24)

Compilation results:

$ <4.9.2>-g++  -c -std=c++14 test.cpp
$

$ <6.2.1>-g++  -c -std=c++14 test.cpp
test.cpp:4:28: error: reinterpret_cast from integer to pointer
 constexpr void *phardware {CONST ((void *) 0x1000)};
                            ^~~~~~~~~~
test.cpp:2:45: note: in definition of macro 'CONST'
 #define CONST(x) (__builtin_constant_p(x) ? x : x)
$

Has this behaviour changed deliberately? As I say, it is contrary to the Standard, but it would be disappointing to lose such a useful extension.
Comment 1 Jonathan Wakely 2018-03-16 00:53:03 UTC
I can't reproduce this with GCC 6, but can with GCC 7:


un.cc:3:55: error: value '4096' of type 'void*' is not a constant expression
     constexpr void *phardware {CONST ((void *) 0x1000)};
                                                       ^

and with trunk:


un.cc:3:39: error: reinterpret_cast from integer to pointer
     constexpr void *phardware {CONST ((void *) 0x1000)};
                                       ^~~~~~~~~~~~~~~
un.cc:1:45: note: in definition of macro 'CONST'
 #define CONST(x) (__builtin_constant_p(x) ? x : x)
                                             ^
Comment 2 Andrew Pinski 2022-01-07 03:53:45 UTC
This has nothing to do with __builtin_constant_p really.
Just GCC started to rejecting:
constexpr void *phardware {((void *) 0x1000)};

Starting in GCC 7; it was accepted before.

What is interesting is the __builtin_constant_p makes clang accept it but that is a clang bug.

>This use appears to be documented

I don't think it was ever documented to be accepted this way but I could be wrong.
Comment 3 Jonathan Wakely 2022-01-07 11:34:26 UTC
(In reply to steveren from comment #0)
> Has this behaviour changed deliberately?

Yes, because the standard forbids it.

As Andrew said, the __builtin_constant_p part (and what the docs say about it) is irrelevant because the (void *) 0x1000 expression is what gives an error.

> As I say, it is contrary to the
> Standard, but it would be disappointing to lose such a useful extension.

I think it would be better to add support to C++ for "pointer literals", I've been talking with a few people about proposing that for the standard.

That would allow you to create constexpr pointers from literal integers, but still disallow arbitrary reinterpret casts between them.

Closing as G++ is correct to disallow this.