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.compound] p4 What the definition of whether two objects are the same CWG2606 #5356

Open
xmh0511 opened this issue Mar 21, 2022 · 18 comments

Comments

@xmh0511
Copy link
Contributor

xmh0511 commented Mar 21, 2022

[expr.static.cast] p13 states

A prvalue of type “pointer to cv1 void” can be converted to a prvalue of type “pointer to cv2 T”, where T is an object type and cv2 is the same cv-qualification as, or greater cv-qualification than, cv1. ... Otherwise, if the original pointer value points to an object a, and there is an object b of type T (ignoring cv-qualification) that is pointer-interconvertible with a, the result is a pointer to b. Otherwise, the pointer value is unchanged by the conversion.

[basic.compound] p4.1 states

Two objects a and b are pointer-interconvertible if:

  • they are the same object, or
  • [...]

Consider this example:

int const v = 0;
void const* cvptr = &v;
void* vptr =  const_cast<void*>(cvptr);
int* iptr = static_cast<int*>(vptr );  // #3

The pointer value of vptr points to v as per [conv.ptr] p2, the result of const_cast<void*>(cvptr) refers to the original entity(the same pointer value as vptr's) as per [expr.const.cast] p3. After figuring out what vptr is, we can pay more attention to #3, which is the case specified in [expr.static.cast] p13 where the conversion is that converts a prvalue of "pointer to void" to a prvalue of type "pointer to int". In this case, the original pointer value points to v(which is object a, whose type is int const) while the expected object b is of type int. They have different types. So, the issue is whether two objects that have similar types but are not the same can be the same object? From the perspective of English, when we say two objects are the same, they should be identical in any respect at least.

@jensmaurer
Copy link
Member

jensmaurer commented Mar 21, 2022

The definition of v creates a single object in this example. There are different ways to refer to that object, e.g. by saying v or by saying *iptr. Nothing to see here. (Yes, modifying *iptr is undefined behavior because the object designated by v is a const object, but that's not directly relevant here.)

@xmh0511
Copy link
Contributor Author

xmh0511 commented Mar 21, 2022

The issue here is that, whether *iptr can refer to the object denoted by v depends on whether [basic.compound] p4.1 can work here. But whether a and b are the same object is not clearly defined. Specifically, in this case, a is an object of type const int while b is an object of type int. Or, do you mean the case at #3 falls into "Otherwise, the pointer value is unchanged by the conversion." since [basic.compound] p4.1 cannot work?

@jensmaurer
Copy link
Member

[expr.static.cast] p13 says:

Otherwise, if the original pointer value points to an object a, and there is an object b of type T (ignoring cv-qualification) that is pointer-interconvertible (6.8.3) with a, the result is a pointer to b. Otherwise, the pointer value is unchanged by the conversion.

We agree the original pointer points to the v object, of type "const int". And there is an object b of type "int" (ignoring cv-qualification), which is the same object as the v object, so the result is a pointer to that v object. In this example, there is only ever one object we're talking about, and I think it goes without definition that x is the same as x (for any x).

@xmh0511
Copy link
Contributor Author

xmh0511 commented Mar 21, 2022

And there is an object b of type "int" (ignoring cv-qualification), which is the same object as the v object

How could an object of type int and an object of type const int be the same object? If it were, then iptr would point to an object of type int(which is b)

the result is a pointer to b.

which means, any modification to that object is well-defined, because b is not a const object([basic.type.qualifier] p1.1), as per [dcl.type.cv] p4. In my opinion, it is exactly that b and a are not the same object, hence they are not pointer-convertible, then iptr still points to the object v as per:

Otherwise, the pointer value is unchanged by the conversion.

Thus, any modification to *iptr will be UB according to [dcl.type.cv] p4

@jensmaurer
Copy link
Member

"is exactly that b and a are not the same object"

That can't be, because there is only a single object. Either "b" doesn't exist at all ("there is an object b of type T"), or it exists and then it's the same as a.

@xmh0511
Copy link
Contributor Author

xmh0511 commented Mar 21, 2022

This is the obscure point here. Informally, any object is unique, an object cannot be of both type int and const int. In other words, an object has its unique determined type. Any two objects of different types cannot potentially be the same object unless it is Schrödinger’s Cat.

"b" doesn't exist at all ("there is an object b of type T")

The requirement of the first branch is that:

there is an object b of type T

Where T is int since we convert a prvalue of "pointer to cv1 void" to a prvalue of type “pointer to cv2 int”. The complete wording is

there is an object b of type T (ignoring cv-qualification) that is pointer-interconvertible with a

In short, there is an object b of type int that is pointer-interconvertible with an object a of type const int. we define pointer-interconvertible as

Two objects a and b are pointer-interconvertible if

  • they are the same object
  • [...]

The key point is whether a(an object of type const int) and b(an object of type int) are the same object? If it were, iptr will point to b but again that b is an object of type int.

@frederick-vs-ja
Copy link
Contributor

I don't see anything here creating a distinct int object. It is two different lvalue expressions v and *iptr that denote the same object of type const int, although *iptr is of type int (decltype(*iptr) is int&).

@xmh0511
Copy link
Contributor Author

xmh0511 commented Mar 22, 2022

@frederick-vs-ja The key point is whether the conversion falls into

Otherwise, if the original pointer value points to an object a, and there is an object b of type T (ignoring cv-qualification) that is pointer-interconvertible with a, the result is a pointer to b.

or

Otherwise, the pointer value is unchanged by the conversion.


It is two different lvalue expressions v and *iptr that denote the same object of type const int

you seem to admit that the conversion should fall into the second branch.

Take more attention to [expr.unary.op] p1

The unary * operator performs indirection: the expression to which it is applied shall be a pointer to an object type, or a pointer to a function type and the result is an lvalue referring to the object or function to which the expression points.

Assume the case falls into the first branch, that branch specifies that the result is a pointer to the object b(where b is of type int). Conversely, if the case falls into the second branch, since the pointer value remains no change, in other words, iptr still points to the object v(where v is an object of type const int).

@frederick-vs-ja
Copy link
Contributor

you seem to admit that the conversion should fall into the second branch.

When there is not a different b object, IIUC two branches are not distinguishable. Note the "ignoring cv-qualification".

Take more attention to [expr.unary.op] p1

The unary * operator performs indirection: the expression to which it is applied shall be a pointer to an object type, or a pointer to a function type and the result is an lvalue referring to the object or function to which the expression points.

"An lvalue referring to the object" is not needed to have the type of that object.

@xmh0511
Copy link
Contributor Author

xmh0511 commented Mar 22, 2022

Note the "ignoring cv-qualification".

The destination of this conversion is "prvalue of type pointer to int(T)" where T is exact the unqualified-version type, there is no top-level cv-qualification; Moreover, even though the pointed type has cv-qualification, its top-level cv-qualification is denoted by symbol cv2 as per [basic.type.qualifier] p6. And thus, b is said to be an object of type int(again, there is no cv-qualification to be ignored).

"An lvalue referring to the object" is not needed to have the type of that object.

An lvalue certainly can have a different type as the type of the actual object to which the expression points. The correct interpretation to [expr.unary.op] p1 is stated in P1839. As I have said in the above comments, the original object a is the object denoted by v, which is an object of type const int, which is created by the definition of v. The b here is specified to an object of type int(since T is int). Any same object cannot both have different types.

two branches are not distinguishable

No, since b is an object of type int, if the first branch works, the result is a pointer to b, which is an object of type int.

@frederick-vs-ja
Copy link
Contributor

again, there is no cv-qualification to be ignored

IIUC there is, as "ignoring cv-qualification" is applied to the phrase "there is an object b of type T", which means that both the cv-qualification of the type of b and that of T are ignored for such purpose.

@xmh0511
Copy link
Contributor Author

xmh0511 commented Mar 22, 2022

which means that both the cv-qualification of the type of b and that of T are ignored for such purpose.

I don't understand what it means. Would you prefer to directly ignore the introduction of b?

there is an object b of type T

Isn't T is exactly the specified type of b? Could you point out what T is in the complete rule? T is introduced in this sentence

A prvalue of type “pointer to cv1 void” can be converted to a prvalue of type “pointer to cv2 T”, where T is an object type

In the above case, we convert the source vptr(pointer to void) to a prvalue of type "pointer to int"

What is the destination of the conversion? In this case, it is the prvalue of type pointer to cv2 int, Isn't it? So, T is obviously int. Do you agree with that? So, the introduction of b means there is an object of type T[with T = int].

@frederick-vs-ja
Copy link
Contributor

Do you select to directly ignore the introduction of b?

No. How can I select to directly ignore the introduction of b while I paste it?

Isn't T is exactly the specified type of b?

This is what I disagree. Given T being int in the example, my comprehension is that the condition "there is an object b of type T (ignoring cv-qualification)" is transformed into "there is an object b whose type is int, const int, volatile int, or const volatile int".

So, T is obviously int. Do you agree with that?

Totally agree for this.

@xmh0511
Copy link
Contributor Author

xmh0511 commented Mar 22, 2022

my comprehension is that the condition "there is an object b of type T (ignoring cv-qualification)" is transformed into "there is an object b whose type is int, const int, volatile int, or const volatile int".

That is our divergence here. In other words, the ambiguity of this portion. I construe "ignore cv-qualification" for any type T to ignore the top-level cv-qualification of T. Specifically, for any type T, its qualification-decomposition is cv U(where n = 0), we only take U, this is "ignoring cv-qualification".

If it were the meaning of what you said, we should take the similar manner as [over.match.funcs.general] p7 did

for any permissible type cv U, any cv2 U, cv2 U&, or cv2 U&& is also a permissible type.

Instead of saying ignore the cv-qualification. Such as [temp.param] p6

The top-level cv-qualifiers on the template-parameter are ignored when determining its type.

template<int const V>
void fun(){
   decltype(V) i = 0;  // ignore the cv-qualification
   i = 1;
}

Even a more similar context, see [expr.cond] p4

if T1 and T2 are the same class type (ignoring cv-qualification) and T2 is at least as cv-qualified as T1, the target type is T2

which means make the qualification-decomposition of T1 is such that

cv1 U1 (where n = 0)

while the qualification-decomposition of T2

cv2 U2 (where n = 0)

It requires that U1 and U2 are the same class type. For example

struct C{int a;};  
using CvT = C const; 

template<class T>
struct Identity{};
int main(){
   C E;
   std::cout<< typeid(Identity<decltype(true? E: CvT{})>).name();  // Identity<C const>
}

@languagelawyer
Copy link
Contributor

I construe "ignore cv-qualification" for any type T to ignore the top-level cv-qualification of T

When the standard compares 2 types and says «(ignoring cv-qualification)», in all places it means to ignore cv-qualification on both types, not only on that which is close to «(ignoring cv-qualification)».

@xmh0511
Copy link
Contributor Author

xmh0511 commented Mar 25, 2022

in all places it means to ignore cv-qualification on both types, not only on that which is close to «(ignoring cv-qualification)».

That means the result would point to object b, which is of type int(the rule specifies the type of that object). Any modification to object b is well-defined. However, the intent is any modification to the object located at the address should cause UB(similar to the case in [dcl.type.cv] p4). I think the result should be the (unchanged)original pointer value, which points to the const object, and any modification to the glvalue obtained from the pointer value is UB.

@languagelawyer
Copy link
Contributor

That means the result would point to object b, which is of type int(the rule specifies the type of that object)

Where?

@xmh0511
Copy link
Contributor Author

xmh0511 commented Mar 25, 2022

there is an object b of type T

T is the type specified in pointer to cv2 T, where the top-level cv-qualification of the pointed-to type is denoted by cv2, thus T is a cv-unqualified type.

@jensmaurer jensmaurer changed the title [basic.compound] p4 What the definition of whether two objects are the same [basic.compound] p4 What the definition of whether two objects are the same CWG2606 Jul 4, 2022
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

4 participants