Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

__builtin_bit_cast does not allow bitcasting from nullptr_t #117166

Closed
tbaederr opened this issue Nov 21, 2024 · 5 comments
Closed

__builtin_bit_cast does not allow bitcasting from nullptr_t #117166

tbaederr opened this issue Nov 21, 2024 · 5 comments
Labels
clang:frontend Language frontend issues, e.g. anything involving "Sema" constexpr Anything related to constant evaluation

Comments

@tbaederr
Copy link
Contributor

nullptr_t is not a pointer type, see the static_assert in https://godbolt.org/z/vjq1Tse7W

In this example, the second bitcast works, but the first one does not:

typedef __UINTPTR_TYPE__ uintptr_t;
constexpr uintptr_t A = __builtin_bit_cast(uintptr_t, nullptr);


typedef decltype(nullptr) nullptr_t;
constexpr decltype(nullptr) N = __builtin_bit_cast(nullptr_t, (uintptr_t)12);
static_assert(N == nullptr);

The output suggests some bogus reason though:

./array.cpp:26:21: error: constexpr variable 'A' must be initialized by a constant expression
   26 | constexpr uintptr_t A = __builtin_bit_cast(uintptr_t, nullptr);
      |                     ^   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
./array.cpp:26:25: note: indeterminate value can only initialize an object of type 'unsigned char' or 'std::byte'; 'unsigned long' is invalid
   26 | constexpr uintptr_t A = __builtin_bit_cast(uintptr_t, nullptr);
      |                         ^

GCC accepts the first one, but not the second. I opened https://gcc.gnu.org/bugzilla/show_bug.cgi?id=117727 for that.

@tbaederr tbaederr added clang:frontend Language frontend issues, e.g. anything involving "Sema" constexpr Anything related to constant evaluation labels Nov 21, 2024
@llvmbot
Copy link
Member

llvmbot commented Nov 21, 2024

@llvm/issue-subscribers-clang-frontend

Author: Timm Baeder (tbaederr)

`nullptr_t` is not a pointer type, see the `static_assert` in https://godbolt.org/z/vjq1Tse7W

In this example, the second bitcast works, but the first one does not:

typedef __UINTPTR_TYPE__ uintptr_t;
constexpr uintptr_t A = __builtin_bit_cast(uintptr_t, nullptr);


typedef decltype(nullptr) nullptr_t;
constexpr decltype(nullptr) N = __builtin_bit_cast(nullptr_t, (uintptr_t)12);
static_assert(N == nullptr);

The output suggests some bogus reason though:

./array.cpp:26:21: error: constexpr variable 'A' must be initialized by a constant expression
   26 | constexpr uintptr_t A = __builtin_bit_cast(uintptr_t, nullptr);
      |                     ^   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
./array.cpp:26:25: note: indeterminate value can only initialize an object of type 'unsigned char' or 'std::byte'; 'unsigned long' is invalid
   26 | constexpr uintptr_t A = __builtin_bit_cast(uintptr_t, nullptr);
      |                         ^

GCC accepts the first one, but not the second. I opened https://gcc.gnu.org/bugzilla/show_bug.cgi?id=117727 for that.

@tbaederr
Copy link
Contributor Author

tbaederr commented Nov 21, 2024

I guess this is on purpose actually:

// As a special case, nullptr_t has an indeterminate value.
if (Ty->isNullPtrType())
return true;

This line is from eee944e, the first commit introducing __builtin_bit_cast.

@tbaederr
Copy link
Contributor Author

Paging @frederick-vs-ja for spec knowledge, since this isn't as clear-cut as I thought, according to the gcc bug linked above.

@zygoloid
Copy link
Collaborator

In Clang, we treat nullptr_t as having no bits in its value representation. (This choice is not specified by the standard, and is instead up to the implementation -- and part of the ABI, but sadly psABIs don't seem to actually specify it.) For example:

struct X {
  X() {}
  decltype(nullptr) p;
};
X f() { return X(); }

... does not store zero bits to the p field within X, instead leaving it indeterminate. (Some other uses of nullptr do store zero bits; I think that's just bad codegen rather than being intentional.)

Because nullptr_t has no value bits, bit_cast treats its bits as being indeterminate. So I think the behavior observed is correct -- you can bit_cast any value to nullptr_t (because it has no bits in its value representation) but you cannot bit_cast any type with a non-empty value representation from nullptr_t.

@tbaederr
Copy link
Contributor Author

That makes sense and matches what Jakub said in the gcc bug. Closing this then, seems clang is doing the right thing.

@tbaederr tbaederr closed this as not planned Won't fix, can't repro, duplicate, stale Nov 22, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
clang:frontend Language frontend issues, e.g. anything involving "Sema" constexpr Anything related to constant evaluation
Projects
None yet
Development

No branches or pull requests

3 participants