Date: Thu, 27 Apr 2023 15:16:53 -0400
cv qualifiers are always discarded on function types, they have no effect:
```
using ft = void();
using cft = const cft;
static_assert(std::is_same_v<ft, cft>); // passes
```
Hence, there are no type reference/pointers to const function. Function types are not object types, and cannot be assigned to, and their concrete representation cannot change at runtime: in a sense, they are always 'logically const' (and never 'logically volatile'). Since there is no reasonable semantics for runtime modification of a function's source code, having cv qualifiers on them seems superfluous.
With respect to this view, it seems logical to say that casting to a function type does not cast away const (but does cast away volatile). Conversely, casting from function type to void* could be viewed as casting away const. This boils down to 2 possible 'cv violations', but only one which could actually lead to a cv related UB without first encountering a strict aliasing UB:
```
void f();
// 1) volatile object pointer -> function pointer
volatile int i;
// bad: cast away volatile
auto* pf = reinterpret_cast<void(*)()>(&i);
// Ok
int const* pi = reinterpret_cast<int const*>(pf);
// UB: access to volatile object through non
// volatile lvalue. The first cast should be made
// illegal
int j = *pi;
// 2) function pointer -> non const object
// pointer
void f();
// ok: we cast away const, but see below
int* pi = reinterpret_cast<int*>(&f);
```
This would seem like reasonable implementation, but is not mandated by the standard, and I think implementation are free to document how they support conversion to/from function pointer an object pointers.
I didn't go through explaining member function pointers; but they behave the same with respect to cv-ness. Although, their syntax is a different beast. Unlike function types, there exist no member function types, nor reference to member function types, so it is impossible to name or declare a 'const member function' type (the const qualifier appearing after the argument list is completely unrelated as it applies to the implicit 'this' and has no relationship on the constness of a pointed to member function.
On April 26, 2023 7:19:26 p.m. EDT, Myria via Std-Discussion <std-discussion_at_[hidden]> wrote:
>C++ says that reinterpret_casting an object pointer to a function pointer
>is conditionally supported. However, there seems to be implementation
>divergence on what happens if the object pointer is cv-qualified. GCC
>allows casts from cv object * to function pointer, but Clang and Visual C++
>do not.
>
>I mentioned this some years ago as a possible defect report, and I sent a
>request for one, but it never got filed.
>
>Should something like this be a defect report? What is the correct
>interpretation of the Standard on this? Being a conditionally supported
>operation, is either interpretation allowed?
>
>[expr.reinterpret.cast]/2 <https://eel.is/c++draft/expr.reinterpret.cast#2>
>says that reinterpret_cast "shall not cast away constness". But is casting
>to function pointer type removing constness? [expr.reinterpret.cast]/8
><https://eel.is/c++draft/expr.reinterpret.cast#8> mentions the possibility
>of cv-qualified casts in the other direction.
>
>Function types have no cv qualification in the normal sense. A
>cv-qualified member function is referring to the "this" parameter, not the
>constness of the function itself. Is a cast to function type removing a cv
>qualifier when functions having them is nonsensical to begin with?
>
>#include <cstdint>
>#include <cstdio>
>
>#if !defined(__x86_64__) && !defined(_M_X64)
> #error "This is an x86-64-only program"
>#endif
>
>#ifdef _MSC_VER
> #pragma code_seg(push, ".text")
> #pragma code_seg(pop)
> #define EXECUTABLE_SEGMENT __declspec(allocate(".text"))
>#elif defined(__linux__)
> #define EXECUTABLE_SEGMENT [[gnu::section(".text.meow")]]
>#else
> #error "Unknown platform"
>#endif
>
>// movl $0x12345678, %eax
>// ret
>EXECUTABLE_SEGMENT const std::uint8_t s_code[] =
>{
> 0xB8, 0x78, 0x56, 0x34, 0x12, 0xC3
>};
>
>int main()
>{
> // an intervening std::uintptr_t cast works with all compilers relevant
>here
> int result = reinterpret_cast<int (*)()>(s_code)();
> std::printf("%08X\n", result);
> return 0;
>}
>
>Melissa
```
using ft = void();
using cft = const cft;
static_assert(std::is_same_v<ft, cft>); // passes
```
Hence, there are no type reference/pointers to const function. Function types are not object types, and cannot be assigned to, and their concrete representation cannot change at runtime: in a sense, they are always 'logically const' (and never 'logically volatile'). Since there is no reasonable semantics for runtime modification of a function's source code, having cv qualifiers on them seems superfluous.
With respect to this view, it seems logical to say that casting to a function type does not cast away const (but does cast away volatile). Conversely, casting from function type to void* could be viewed as casting away const. This boils down to 2 possible 'cv violations', but only one which could actually lead to a cv related UB without first encountering a strict aliasing UB:
```
void f();
// 1) volatile object pointer -> function pointer
volatile int i;
// bad: cast away volatile
auto* pf = reinterpret_cast<void(*)()>(&i);
// Ok
int const* pi = reinterpret_cast<int const*>(pf);
// UB: access to volatile object through non
// volatile lvalue. The first cast should be made
// illegal
int j = *pi;
// 2) function pointer -> non const object
// pointer
void f();
// ok: we cast away const, but see below
int* pi = reinterpret_cast<int*>(&f);
```
This would seem like reasonable implementation, but is not mandated by the standard, and I think implementation are free to document how they support conversion to/from function pointer an object pointers.
I didn't go through explaining member function pointers; but they behave the same with respect to cv-ness. Although, their syntax is a different beast. Unlike function types, there exist no member function types, nor reference to member function types, so it is impossible to name or declare a 'const member function' type (the const qualifier appearing after the argument list is completely unrelated as it applies to the implicit 'this' and has no relationship on the constness of a pointed to member function.
On April 26, 2023 7:19:26 p.m. EDT, Myria via Std-Discussion <std-discussion_at_[hidden]> wrote:
>C++ says that reinterpret_casting an object pointer to a function pointer
>is conditionally supported. However, there seems to be implementation
>divergence on what happens if the object pointer is cv-qualified. GCC
>allows casts from cv object * to function pointer, but Clang and Visual C++
>do not.
>
>I mentioned this some years ago as a possible defect report, and I sent a
>request for one, but it never got filed.
>
>Should something like this be a defect report? What is the correct
>interpretation of the Standard on this? Being a conditionally supported
>operation, is either interpretation allowed?
>
>[expr.reinterpret.cast]/2 <https://eel.is/c++draft/expr.reinterpret.cast#2>
>says that reinterpret_cast "shall not cast away constness". But is casting
>to function pointer type removing constness? [expr.reinterpret.cast]/8
><https://eel.is/c++draft/expr.reinterpret.cast#8> mentions the possibility
>of cv-qualified casts in the other direction.
>
>Function types have no cv qualification in the normal sense. A
>cv-qualified member function is referring to the "this" parameter, not the
>constness of the function itself. Is a cast to function type removing a cv
>qualifier when functions having them is nonsensical to begin with?
>
>#include <cstdint>
>#include <cstdio>
>
>#if !defined(__x86_64__) && !defined(_M_X64)
> #error "This is an x86-64-only program"
>#endif
>
>#ifdef _MSC_VER
> #pragma code_seg(push, ".text")
> #pragma code_seg(pop)
> #define EXECUTABLE_SEGMENT __declspec(allocate(".text"))
>#elif defined(__linux__)
> #define EXECUTABLE_SEGMENT [[gnu::section(".text.meow")]]
>#else
> #error "Unknown platform"
>#endif
>
>// movl $0x12345678, %eax
>// ret
>EXECUTABLE_SEGMENT const std::uint8_t s_code[] =
>{
> 0xB8, 0x78, 0x56, 0x34, 0x12, 0xC3
>};
>
>int main()
>{
> // an intervening std::uintptr_t cast works with all compilers relevant
>here
> int result = reinterpret_cast<int (*)()>(s_code)();
> std::printf("%08X\n", result);
> return 0;
>}
>
>Melissa
Received on 2023-04-27 19:17:07