Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can there be different implicit objects based on a later runtime decision in C++20?

This question refers to the addition of P0593 to the latest C++20 draft .

Here is my example:

#include <cstdlib>
#include <cstdio>

void foo(void *p)
{
    if ( std::getchar() == 'i' )
    {
        *(int *)p = 2;
        std::printf("%d\n", *(int *)p);
    }
    else
    {
        *(float *)p = 2;
        std::printf("%f\n", *(float *)p);
    }
}

int main()
{
    void *a = std::malloc( sizeof(int) + sizeof(float) );
    if ( !a ) return EXIT_FAILURE;

    foo(a);
    // foo(a);    [2]
}

Is this code well-defined for all inputs under the latest draft?

The rationale expressed in P0593 makes it fairly clear that uncommenting [2] would lead to undefined behaviour due to strict aliasing violation, if the two user input items differ . The implicit object creation is supposed to happen just once, at the point of malloc; it isn't triggered by the assignment statement in foo.

For any actual run of the program , there exists a member of the unspecified set of implicit objects that would make the program well-defined . But it's not clear to me whether the choice of implicit object creation mentioned in [intro.object]/10 must be made when the malloc happens; or whether the decision can "time travel" .

The same issue may arise for a program that reads a binary blob into a buffer and then makes a runtime decision of how to access it (e.g. deserialization; and the header tells us whether a float or an int is coming up).

like image 319
M.M Avatar asked Mar 10 '20 23:03

M.M


Video Answer


1 Answers

The implicit object creation is supposed to happen just once, at the point of malloc; it isn't triggered by the assignment statement in foo.

That's not relevant. What matters is which object gets created. The standard says that the object which gets created is one which makes something which would have been UB into well-defined code:

that operation implicitly creates and starts the lifetime of zero or more objects of implicit-lifetime types ([basic.types]) in its specified region of storage if doing so would result in the program having defined behavior.

Behavior is ultimately based on runtime execution, not static analysis. So you need only follow the execution of the program until you run into a case where behavior would not be defined, yet would be defined if an object of some type had been created in that storage at the time of the operation in question.

So the location of creation is always "the operation", but the determination of what gets created is based on how the memory gets used at runtime (ie: behavior).

like image 112
Nicol Bolas Avatar answered Oct 12 '22 14:10

Nicol Bolas