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

[expr.add]/6 unfortunate example in Note #3614

Closed
languagelawyer opened this issue Jan 8, 2020 · 10 comments
Closed

[expr.add]/6 unfortunate example in Note #3614

languagelawyer opened this issue Jan 8, 2020 · 10 comments
Labels
cwg Issue must be reviewed by CWG. not-editorial Issue is not deemed editorial; the editorial issue is kept open for tracking.

Comments

@languagelawyer
Copy link
Contributor

languagelawyer commented Jan 8, 2020

[expr.add]/6 says:

For addition or subtraction, if the expressions P or Q have type “pointer to cv T”, where T and the array element type are not similar, the behavior is undefined. [Note: In particular, a pointer to a base class cannot be used for pointer arithmetic when the array contains objects of a derived class type. — end note]

The paragraph was added in the resolution of CWG1504 (the normative text has been a lil bit changed by CWG1865). Description of the issue says:

The current wording is not sufficiently clear that a pointer to a base class subobject of an array element cannot be used in pointer arithmetic.

I suspect this meant something like

struct B {};
struct D : B {};
D d[3];
B* bp = &d[0];
bp + 0; // This should have become UB with the resolution of the issue

At C++11 times (when the issue was reported), [conv.ptr]/3 was saying that bp would be «pointer to the base class subobject of the derived class object». In C++17, [conv.ptr]/3 says the same thing.
C++11 [expr.add]/4 was saying that «…a pointer to a nonarray object behaves the same as a pointer to the first element of an array of length one…». We may claim that currently the Standard says mostly the same thing.
This means that expressions bp + 0, bp + 1 were/are fine (before and after the resolution of the issue). bp + 2 was/is UB, but not because of the paragraph added by the resolution of the issue.
So, the resolution of the issue changed nothing for the code above (if something like the code above was meant by the issue description). That's why I call the example in the Note unfortunate.

It is possible to construct an example with base and derived classes which would match [expr.add]/6 Note, however, this would be quite non-trivial, involving multiple or virtual inheritance, to make it possible to obtain a pointer of type "pointer to base" pointing to the derived class object, not the base class subobject.

I would suggest to change the Note to say, for example:

In particular, a pointer to int cannot be used for pointer arithmetic when the array contains objects of unsigned int type.

@jensmaurer
Copy link
Member

It was intentional to talk about "pointer to base class" here, because you can get that via implicit conversions. I think the "array of one element" model doesn't go well with the broad restrictions in [expr.add]. I think we need a fix to the normative wording here.

@jensmaurer jensmaurer added the decision-required A decision of the editorial group (or the Project Editor) is required. label Jan 10, 2020
@jensmaurer jensmaurer removed the decision-required A decision of the editorial group (or the Project Editor) is required. label Feb 10, 2020
@jensmaurer
Copy link
Member

jensmaurer commented Feb 10, 2020

Editorial meeting: Return to CWG, normatively covered by p4.2. The added paragraph quoted above should be a note instead.

@jensmaurer jensmaurer added cwg Issue must be reviewed by CWG. not-editorial Issue is not deemed editorial; the editorial issue is kept open for tracking. labels Feb 10, 2020
@languagelawyer
Copy link
Contributor Author

Entire [expr.add]/6 should be a note? This doesn't sound right.

@jensmaurer
Copy link
Member

@languagelawyer , the idea is that if P or Q point to a base class, they can't point to an array element (even if the addresses are the same), so the precondition in p4.2 is not satisfied and we fall through to "undefined behavior".

@languagelawyer
Copy link
Contributor Author

@jensmaurer I see the idea. But we still need normative [expr.add]/6.

float farr[2];
reinterpret_cast<double*>(&farr[0]) + 1;

currently, there is UB in the second line because of [expr.add]/6.

Without [expr.add]/6, the result of the full expression in the second line would be "pointer to fadd[1]", which is nonsense. (Here, reinterpret_cast doesn't change the pointer value, so reinterpret_cast<double*>(&farr[0]) still "points to fadd[0]", an array element, so [expr.add]/4.2 is applicable.)

@xmh0511
Copy link
Contributor

xmh0511 commented Jul 28, 2021

Why are bp + 0, bp+1 fine? The type of bp is pointer to B and it points to the subobject of the first element of array d, hence [expr.add#4.3] applies to them, which is UB.

@languagelawyer
Copy link
Contributor Author

@xmh0511
Copy link
Contributor

xmh0511 commented Jul 28, 2021

Did you mean that, since the bp points to the base class subobject of an array element, it could be said that it's not an array element, hence an object of type T that is not an array element is considered to belong to an array with one element of type T could be applied to the subobject? Hence, bp could be considered to point to the first element of an array with a single-element of type B.

I think change [basic.compound#3.sentence-12] to that

a complete object of type T is considered to belong to an array with one element of type T.

Which might fix this issue.

@languagelawyer
Copy link
Contributor Author

This issue is not about which objects are considered to be array elements.

@xmh0511
Copy link
Contributor

xmh0511 commented Jul 28, 2021

Yes, I see your master argument in this issue. However, this issue actually introduces "This means that expressions bp + 0, bp + 1 were/are fine (before and after the resolution of the issue). ", which should also be fixed.

struct B{
  char b;
};
struct D: B{
  char d;
};
D d[3];
B* bp = reinterpret_cast<B*>(&d[0]);  // D*->void* still points to the element, void* -> B* not  pointer-interconvertible

Maybe this example could be accompanied with [expr.add]/6

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
cwg Issue must be reviewed by CWG. not-editorial Issue is not deemed editorial; the editorial issue is kept open for tracking.
Projects
None yet
Development

No branches or pull requests

3 participants