Date: Mon, 22 Aug 2022 09:35:26 +0100
On Mon, 22 Aug 2022 at 07:55, David Ledger <davidledger_at_[hidden]> wrote:
> Well, the dynamic type of the object is unchanging and this appears
> glaring inconsistancy with other static_cast behaviour:
> ```CPP
> consteval void fromp()
> {
> int v = 10;
> auto lambda = [v](int)
> {
> return v + 32;
> };
> void const * ptr = λ
> auto const * reverse = static_cast<decltype(lambda) const *>(ptr); //
> Error: cast from 'const void*' is not allowed
> }
>
> void glump()
> {
> int v = 10;
> auto lambda = [v](int)
> {
> return v + 32;
> };
> void const * ptr = λ
> auto const * reverse = static_cast<decltype(lambda) const *>(ptr); //
> Perfectly fine.
> }
>
> int main()
> {
> glump();
> fromp();
> }
> ```
>
> To a non-standardese expert, this behaviour seems unexpected. Inverse
> conversion sequence isn't changing the dynamic type, before dismissing it
> offhand please consider that type erasure through virtual functions is
> already achieved through constexpr virtual calls. This appears just an odd
> inconcistancy with 5.14.
>
I'm sure it's feasible (and thanks for correcting me about virtual
functions); I'm just saying that static_cast from void* to an object's
dynamic type is going to require a paper. The lack of a feature is not
grounds for a DR.
> Regarding impact:
>
> Clang appears to already support this in some special cases, making
> exceptions for calls for allocate and source_location, a check for dynamic
> type can be made with the existing call to ComputeDynamicType:
>
> clang/lib/AST/ExprConstant.cpp:8877
> ```CPP
> // 1. We'll allow it in std::allocator::allocate, and anything which
> that
> // calls.
> // 2. HACK 2022-03-28: Work around an issue with libstdc++'s
> // <source_location> header. Fixed in GCC 12 and later
> (2022-04-??).
> // We'll allow it in the body of std::source_location::current.
> GCC's
> // implementation had a parameter of type `void*`, and casts from
> // that back to `const __impl*` in its body.
> if (VoidPtrCastMaybeOK &&
> (Info.getStdAllocatorCaller("allocate") ||
> IsDeclSourceLocationCurrent(Info.CurrentCall->Callee))) {
> // Permitted.
> } else {
> ```
>
> GCC similarly also has to special case to work around the over constrained
> clause:
> gcc\cp\constexpr.cc:7390
> ```CPP
> /* [expr.const]: a conversion from type cv void* to a pointer-to-object
> type cannot be part of a core constant expression as a resolution to
> DR 1312. */
> if (TYPE_PTROB_P(type) && TYPE_PTR_P(TREE_TYPE(op)) &&
> VOID_TYPE_P(TREE_TYPE(TREE_TYPE(op)))
> /* Inside a call to std::construct_at or to
> std::allocator<T>::{,de}allocate, we permit casting from void*
> because that is compiler-generated code. */
> && !is_std_construct_at(ctx->call) &&
> !is_std_allocator_allocate(ctx->call))
> {
> /* Likewise, don't error when casting from void* when OP is
> &heap uninit and similar. */
> tree sop = tree_strip_nop_conversions(op);
> ```
> GCC also has the ability to evaluate dynamic type, atleast according to
> this comment:
> ```CPP
> auto obj = cxx_eval_constant_expression (ctx, obj, vc_prvalue,
> non_constant_p, overflow_p);
> const tree dynamic_type = TREE_TYPE (obj);
> ```
> For example.
>
Great, hopefully this should smooth the way.
However, I can see some potential issues; just off the top of my head:
- doesn't this allow bypassing access checks on inheritance - cast to void*
then to a private first base? Perhaps this should be restricted to casting
back to the most derived type?
- what if there is more than one object with the same type at an address
(specifically, if one provides storage for the other) - which object is
referenced by the converted pointer?
Or when you say that the cast should be the "inverse of a standard
conversion sequence", do you mean that the void* pointer should track its
provenance and allow only conversions that unwind that provenance? Because
I'm not sure that compilers implement that currently.
On 22/08/2022 2:21 pm, Edward Catmur wrote:
>
>
> On Mon, 22 Aug 2022, 02:00 Brian Bi via Std-Proposals, <
> std-proposals_at_[hidden]> wrote:
>
>>
>>
>> On Sun, Aug 21, 2022 at 6:06 AM David Ledger via Std-Proposals <
>> std-proposals_at_[hidden]> wrote:
>>
>>> Hello W21,
>>>
>>> The following example shows a static_cast as per [expr.static.cast#7]:
>>>
>>> ```CPP
>>> int v = 10;
>>> auto lambda = [v](int)
>>> {
>>> return v + 32;
>>> };
>>> void * ptr = λ
>>> auto * reverse = static_cast<decltype(lambda) *>(ptr);
>>> ```
>>>
>>> In a constant expression, this is invalid because of [expr.const-5.14].
>>>
>>> However, in this case it is an inverse of a conversion sequence. Since
>>> this causes issues when implementing a constexpr `std::function_ref` and
>>> similar usages I believe the clause should be modified to the following:
>>> > (5.14) a conversion from type cv void* to a pointer-to-object type<ins>*,
>>> other than when this conversion is the inverse of any standard conversion
>>> sequence*</ins>;
>>>
>>
>> I seem to recall some discussion on the predecessor of this mailing list
>> about implementation difficulty of allowing casts from void* in constant
>> expressions, but I keep failing to find the thread.
>>
>> I'm not sure if your proposed restriction makes it any easier to
>> implement. I wish whoever originally posted the post that I'm thinking
>> about would post a reply :)
>>
>
> Yes, you're not going to get compile time type erasure via function
> pointers in as a defect report. Even virtual functions would be a full on
> proposal.
>
>>
> Well, the dynamic type of the object is unchanging and this appears
> glaring inconsistancy with other static_cast behaviour:
> ```CPP
> consteval void fromp()
> {
> int v = 10;
> auto lambda = [v](int)
> {
> return v + 32;
> };
> void const * ptr = λ
> auto const * reverse = static_cast<decltype(lambda) const *>(ptr); //
> Error: cast from 'const void*' is not allowed
> }
>
> void glump()
> {
> int v = 10;
> auto lambda = [v](int)
> {
> return v + 32;
> };
> void const * ptr = λ
> auto const * reverse = static_cast<decltype(lambda) const *>(ptr); //
> Perfectly fine.
> }
>
> int main()
> {
> glump();
> fromp();
> }
> ```
>
> To a non-standardese expert, this behaviour seems unexpected. Inverse
> conversion sequence isn't changing the dynamic type, before dismissing it
> offhand please consider that type erasure through virtual functions is
> already achieved through constexpr virtual calls. This appears just an odd
> inconcistancy with 5.14.
>
I'm sure it's feasible (and thanks for correcting me about virtual
functions); I'm just saying that static_cast from void* to an object's
dynamic type is going to require a paper. The lack of a feature is not
grounds for a DR.
> Regarding impact:
>
> Clang appears to already support this in some special cases, making
> exceptions for calls for allocate and source_location, a check for dynamic
> type can be made with the existing call to ComputeDynamicType:
>
> clang/lib/AST/ExprConstant.cpp:8877
> ```CPP
> // 1. We'll allow it in std::allocator::allocate, and anything which
> that
> // calls.
> // 2. HACK 2022-03-28: Work around an issue with libstdc++'s
> // <source_location> header. Fixed in GCC 12 and later
> (2022-04-??).
> // We'll allow it in the body of std::source_location::current.
> GCC's
> // implementation had a parameter of type `void*`, and casts from
> // that back to `const __impl*` in its body.
> if (VoidPtrCastMaybeOK &&
> (Info.getStdAllocatorCaller("allocate") ||
> IsDeclSourceLocationCurrent(Info.CurrentCall->Callee))) {
> // Permitted.
> } else {
> ```
>
> GCC similarly also has to special case to work around the over constrained
> clause:
> gcc\cp\constexpr.cc:7390
> ```CPP
> /* [expr.const]: a conversion from type cv void* to a pointer-to-object
> type cannot be part of a core constant expression as a resolution to
> DR 1312. */
> if (TYPE_PTROB_P(type) && TYPE_PTR_P(TREE_TYPE(op)) &&
> VOID_TYPE_P(TREE_TYPE(TREE_TYPE(op)))
> /* Inside a call to std::construct_at or to
> std::allocator<T>::{,de}allocate, we permit casting from void*
> because that is compiler-generated code. */
> && !is_std_construct_at(ctx->call) &&
> !is_std_allocator_allocate(ctx->call))
> {
> /* Likewise, don't error when casting from void* when OP is
> &heap uninit and similar. */
> tree sop = tree_strip_nop_conversions(op);
> ```
> GCC also has the ability to evaluate dynamic type, atleast according to
> this comment:
> ```CPP
> auto obj = cxx_eval_constant_expression (ctx, obj, vc_prvalue,
> non_constant_p, overflow_p);
> const tree dynamic_type = TREE_TYPE (obj);
> ```
> For example.
>
Great, hopefully this should smooth the way.
However, I can see some potential issues; just off the top of my head:
- doesn't this allow bypassing access checks on inheritance - cast to void*
then to a private first base? Perhaps this should be restricted to casting
back to the most derived type?
- what if there is more than one object with the same type at an address
(specifically, if one provides storage for the other) - which object is
referenced by the converted pointer?
Or when you say that the cast should be the "inverse of a standard
conversion sequence", do you mean that the void* pointer should track its
provenance and allow only conversions that unwind that provenance? Because
I'm not sure that compilers implement that currently.
On 22/08/2022 2:21 pm, Edward Catmur wrote:
>
>
> On Mon, 22 Aug 2022, 02:00 Brian Bi via Std-Proposals, <
> std-proposals_at_[hidden]> wrote:
>
>>
>>
>> On Sun, Aug 21, 2022 at 6:06 AM David Ledger via Std-Proposals <
>> std-proposals_at_[hidden]> wrote:
>>
>>> Hello W21,
>>>
>>> The following example shows a static_cast as per [expr.static.cast#7]:
>>>
>>> ```CPP
>>> int v = 10;
>>> auto lambda = [v](int)
>>> {
>>> return v + 32;
>>> };
>>> void * ptr = λ
>>> auto * reverse = static_cast<decltype(lambda) *>(ptr);
>>> ```
>>>
>>> In a constant expression, this is invalid because of [expr.const-5.14].
>>>
>>> However, in this case it is an inverse of a conversion sequence. Since
>>> this causes issues when implementing a constexpr `std::function_ref` and
>>> similar usages I believe the clause should be modified to the following:
>>> > (5.14) a conversion from type cv void* to a pointer-to-object type<ins>*,
>>> other than when this conversion is the inverse of any standard conversion
>>> sequence*</ins>;
>>>
>>
>> I seem to recall some discussion on the predecessor of this mailing list
>> about implementation difficulty of allowing casts from void* in constant
>> expressions, but I keep failing to find the thread.
>>
>> I'm not sure if your proposed restriction makes it any easier to
>> implement. I wish whoever originally posted the post that I'm thinking
>> about would post a reply :)
>>
>
> Yes, you're not going to get compile time type erasure via function
> pointers in as a defect report. Even virtual functions would be a full on
> proposal.
>
>>
Received on 2022-08-22 08:35:39