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

[basic.life] p9 Does an expression happen-before itself? #584

Open
xmh0511 opened this issue Jul 28, 2024 · 10 comments
Open

[basic.life] p9 Does an expression happen-before itself? #584

xmh0511 opened this issue Jul 28, 2024 · 10 comments

Comments

@xmh0511
Copy link

xmh0511 commented Jul 28, 2024

Full name of submitter (unless configured in github; will be published with the issue): Jim X

[basic.life] p9 says:

After the lifetime of an object has ended and before the storage which the object occupied is reused or released, if a new object is created at the storage location which the original object occupied and the original object was transparently replaceable by the new object, a pointer that pointed to the original object, a reference that referred to the original object

This wording is changed to

If, in an evaluation that is in a storage-only phase of an object and where the evaluation does not happen before the lifetime of an the object ends, a new object is created at the storage location which the original object occupied, a pointer that pointed to the original object, a reference that referred to the original object, or the name of the original object will automatically refer to the new object

Consider this example:

int main(){
   int obj = 0;
   auto ptr = &obj;
  new (ptr) int{1};  // #1
}

The new-expression at #1 comprises reusing the storage ptr points to and creating an object at the storage, however, [basic.life] p9 requires that: before the storage which the object occupied is reused or released

This implies that the creating object at #1 should happen before the storage is reused at #1, however, it is underspecified whether the new-expression at #1 happens before itself. Strictly speaking, does the creating object in the expression happen before the storage reusing in the same expression?

@frederick-vs-ja
Copy link

We can't treat the new-expression as a whole here. CWG2721 should have clarified this.

@xmh0511
Copy link
Author

xmh0511 commented Jul 28, 2024

We can't treat the new-expression as a whole here. CWG2721 should have clarified this.

The issue here is that the text "before" and "after" in this subclause refer to "happen-before" and "happen-after", and we say an evaluation A happens-before/after another evaluation B. However, [basic.life] p9 didn't specify what evaluation should happen after the evaluation that ended the lifetime of an object and happen before the evaluation that reused or released the storage of the object.

@jensmaurer
Copy link
Member

See CWG2863.

@xmh0511
Copy link
Author

xmh0511 commented Jul 28, 2024

See CWG2863.

IIUC, CWG2863 seems not clear this example:

std::atomic<std::shared_ptr<int>> ptr;

// thread 1: 
std::shared_ptr<int> x(new int); //#0
new (x.get()) int{1}; //#1
ptr.store(x,std::memory_order_release); // #2


//thread 2:
auto p = ptr.load(std::memory_order_acquire); // #4
int I = *(p.get()); // #5

Assuming, in this case, #4 read the value written at #2 and thread 1 exists after thread 2 exits(which means, thread 1 is responsible for destroying x). According to CWG2863, is #5 in a storage-only phase of the int object created at #0 such that p.get() at #5 atomically points to the new object created at #1?

E happens after the storage which O will occupy is allocated?

yes

and E does not happen after the lifetime of O starts; or

no

E does not happen before the lifetime of O ends

yes, E happens-after the lifetime of O ends

E happens before the storage which O occupied is reused or released.

E happens-after the storage which O occupied is reused, so it's false. Whether E happens before the storage which O occupied is released is unknown.

Does it mean, the pointer #5 cannot atomically point to the new created object?

@jensmaurer
Copy link
Member

jensmaurer commented Jul 28, 2024

The intent is that we have a transparent-replacement case here; see discussion in the reflector thread starting at http://lists.isocpp.org/core/2024/04/15756.php

@xmh0511
Copy link
Author

xmh0511 commented Jul 29, 2024

The intent is that we have a transparent-replacement case here; see discussion in the reflector thread starting at http://lists.isocpp.org/core/2024/04/15756.php

How can I access this website? The website seems to need authorization

@jensmaurer
Copy link
Member

How can I access this website? The website seems to need authorization

Please contact your national standardization body and ask to participate in ISO/IEC JTC1 SC22 WG21.

@xmh0511
Copy link
Author

xmh0511 commented Jul 30, 2024

Another example is:

#include <iostream>
int main(){
    int* ptr = new int;
    int v = *ptr;  //#1
}

The wording in P2434 says:

A pointer value P is valid in the context of an evaluation E if P is a null pointer value, or if it is a pointer to or past the end of an object O and E happens after the beginning and happens before the end of the duration of the region of storage for O.

In this example, we don't know whether #1 happens before the end of the duration of the region of storage for object to which ptr points because there is no deallocation to release the storage of ptr.

If we cannot prove an evaluation A happens-before B, then we can only say A does not happen-before B, in this example, the wording says ptr is invalid.

Similarly, CWG2863 has the same issue of whether E happens before the storage is released, so it is also unclear whether ptr at #1 is in a storage-only phase of that object.

@frederick-vs-ja
Copy link

frederick-vs-ja commented Jul 30, 2024

In this example, we don't know whether #1 happens before the end of the duration of the region of storage for object to which ptr points because there is no deallocation to release the storage of ptr.

If we cannot prove an evaluation A happens-before B, then we can only say A does not happen-before B, in this example, the wording says ptr is invalid.

Such reading seemingly indicates that a leaked storage is effectively invalid at first, because the end of the duration of that storage doesn't exist and nothing can happen before such an end.

But, IMO, we generally think the end to be in the infinite future or at the termination of the program, so everything happens before such an end. I'm not sure whether wording fix is necessary.

@xmh0511
Copy link
Author

xmh0511 commented Jul 30, 2024

In this example, we don't know whether #1 happens before the end of the duration of the region of storage for object to which ptr points because there is no deallocation to release the storage of ptr.
If we cannot prove an evaluation A happens-before B, then we can only say A does not happen-before B, in this example, the wording says ptr is invalid.

Such reading seemingly indicates that a leaked storage is effectively invalid at first, because the end of the duration of that storage doesn't exist and nothing can happen before such an end.

But, IMO, we generally think the end to be in the infinite future or at the termination of the program, so everything happens before such an end. I'm not sure whether wording fix is necessary.

Incidentally, Even if we say the evaluation that uses ptr happens-before the end of that storage, we can only get a consequence that the pointer is valid and the evaluation is not in the storage-only phase of that object. However, we lack the wording to say we are permitted to manipulate the whole object without limitation.

We may say

evaluation that happens-after the starts of the lifetime and happen-before the end of the lifetime can manipulate the object without limitation.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants