Discussion:
&*p if p == nullptr
(too old to reply)
j***@gmail.com
2017-06-21 13:31:58 UTC
Permalink
Is this undefined behavior?

int *p = nullptr;
int *q = &*p;

What about this?

std::unique_ptr<int> p = nullptr;
int *q = &*p;

Am I missing something or is the standard kind of unclear on this?
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
Thiago Macieira
2017-06-21 14:16:03 UTC
Permalink
Post by j***@gmail.com
Is this undefined behavior?
int *p = nullptr;
int *q = &*p;
Yes, because *p is UB.
Post by j***@gmail.com
What about this?
std::unique_ptr<int> p = nullptr;
int *q = &*p;
Am I missing something or is the standard kind of unclear on this?
Also UB because std::unique_ptr::operator* is forming a reference to a null
pointer.

You're missing something. It's not unclear at all.
--
Thiago Macieira - thiago (AT) macieira.info - thiago (AT) kde.org
Software Architect - Intel Open Source Technology Center
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
S.B.
2017-06-21 15:11:42 UTC
Permalink
Post by Thiago Macieira
Post by j***@gmail.com
Is this undefined behavior?
int *p = nullptr;
int *q = &*p;
Yes, because *p is UB.
Post by j***@gmail.com
What about this?
std::unique_ptr<int> p = nullptr;
int *q = &*p;
Am I missing something or is the standard kind of unclear on this?
Also UB because std::unique_ptr::operator* is forming a reference to a null
pointer.
You're missing something. It's not unclear at all.
Considering that CWG 232
<http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#232> is still
in the active issue list, maybe the committee thought it's not clear enough.
Post by Thiago Macieira
--
Thiago Macieira - thiago (AT) macieira.info - thiago (AT) kde.org
Software Architect - Intel Open Source Technology Center
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
Myriachan
2017-06-21 19:22:47 UTC
Permalink
Post by S.B.
Post by Thiago Macieira
Post by j***@gmail.com
Is this undefined behavior?
int *p = nullptr;
int *q = &*p;
Yes, because *p is UB.
Post by j***@gmail.com
What about this?
std::unique_ptr<int> p = nullptr;
int *q = &*p;
Am I missing something or is the standard kind of unclear on this?
Also UB because std::unique_ptr::operator* is forming a reference to a null
pointer.
You're missing something. It's not unclear at all.
Considering that CWG 232
<http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#232> is
still in the active issue list, maybe the committee thought it's not clear
enough.
Post by Thiago Macieira
--
Thiago Macieira - thiago (AT) macieira.info - thiago (AT) kde.org
Software Architect - Intel Open Source Technology Center
A related issue I've thought about recently is with binding references to
hardware registers:

volatile char *GetMeow1()
{
return &*reinterpret_cast<volatile char *>(0x12345678);
}

volatile char &GetMeow2()
{
return *reinterpret_cast<volatile char *>(0x12345678);
}

Do these read the hardware register at 0x12345678? Current compilers say
no, so I guess I just missed it in the Standard...?

I suppose it's one of the context-sensitive expression things in C and C++,
like C++'s choosing of an overload from a function pointer by what it's
being cast to. In this case, I suppose that binding a reference or taking
the address would not read a volatile object, but doing nothing would read
it:

void Function()
{
*reinterpret_cast<volatile char *>(0x12345678);
}

Melissa
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
Thiago Macieira
2017-06-21 19:52:49 UTC
Permalink
Post by Myriachan
A related issue I've thought about recently is with binding references to
volatile char *GetMeow1()
{
return &*reinterpret_cast<volatile char *>(0x12345678);
}
volatile char &GetMeow2()
{
return *reinterpret_cast<volatile char *>(0x12345678);
}
Do these read the hardware register at 0x12345678? Current compilers say
no, so I guess I just missed it in the Standard...?
Why do you think this is a problem?

https://godbolt.org/g/ze114W
--
Thiago Macieira - thiago (AT) macieira.info - thiago (AT) kde.org
Software Architect - Intel Open Source Technology Center
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
Myriachan
2017-06-21 20:30:28 UTC
Permalink
Post by Thiago Macieira
Post by Myriachan
A related issue I've thought about recently is with binding references
to
Post by Myriachan
volatile char *GetMeow1()
{
return &*reinterpret_cast<volatile char *>(0x12345678);
}
volatile char &GetMeow2()
{
return *reinterpret_cast<volatile char *>(0x12345678);
}
Do these read the hardware register at 0x12345678? Current compilers
say
Post by Myriachan
no, so I guess I just missed it in the Standard...?
Why do you think this is a problem?
https://godbolt.org/g/ze114W
I don't get what your point there is, sorry. Function g is ill-formed
because you're redeclaring GetMeow1 and GetMeow2 as overloaded functions
differing only by return type, via the most vexing parse rule. (GCC
accepts-invalid bug for not noticing this?)

Also, why is &*p undefined behavior when p is null? If p were a pointer to
a hardware register instead of null, the hardware register would not be
read, but if the pointer is null, it suddenly becomes undefined behavior.
This rule is self-consistent, but it's rather silly. (In my opinion, I
think that references to null ought to be legal, with lvalue to rvalue
conversion being undefined.)

Melissa
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
Daniel Krügler
2017-06-21 20:33:42 UTC
Permalink
Post by Myriachan
Also, why is &*p undefined behavior when p is null? If p were a pointer to
a hardware register instead of null, the hardware register would not be
read, but if the pointer is null, it suddenly becomes undefined behavior.
This rule is self-consistent, but it's rather silly. (In my opinion, I
think that references to null ought to be legal, with lvalue to rvalue
conversion being undefined.)
This is an issue still debated:

http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#232

- Daniel
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
Brian Bi
2017-06-21 20:38:20 UTC
Permalink
Post by S.B.
Post by Myriachan
Also, why is &*p undefined behavior when p is null? If p were a pointer
to
Post by Myriachan
a hardware register instead of null, the hardware register would not be
read, but if the pointer is null, it suddenly becomes undefined behavior.
This rule is self-consistent, but it's rather silly. (In my opinion, I
think that references to null ought to be legal, with lvalue to rvalue
conversion being undefined.)
http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#232
- Daniel
It doesn't look like there's any real ongoing debate on this issue. It
seems we may be permanently stuck in this embarrassing state where
beginners will ask whether it's really illegal to dereference null pointers
and we'll just have to reply, "well... nobody is really sure"
--
*Brian Bi*
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
Daniel Krügler
2017-06-21 20:41:13 UTC
Permalink
Post by S.B.
http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#232
- Daniel
It doesn't look like there's any real ongoing debate on this issue. It seems
we may be permanently stuck in this embarrassing state where beginners will
ask whether it's really illegal to dereference null pointers and we'll just
have to reply, "well... nobody is really sure"
The core group doesn't ignore that issue, even though you might not
see much progress reflected in the issue state.

- Daniel
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
Brian Bi
2017-06-21 20:59:38 UTC
Permalink
Post by Daniel Krügler
On Wed, Jun 21, 2017 at 1:33 PM, Daniel KrÃŒgler <
Post by S.B.
http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#232
- Daniel
It doesn't look like there's any real ongoing debate on this issue. It
seems
we may be permanently stuck in this embarrassing state where beginners
will
ask whether it's really illegal to dereference null pointers and we'll
just
have to reply, "well... nobody is really sure"
The core group doesn't ignore that issue, even though you might not
see much progress reflected in the issue state.
- Daniel
Thanks, I didn't know that. Now I'm really curious, what has been said
about this issue since the last update on the issues page?
--
*Brian Bi*
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
Thiago Macieira
2017-06-21 20:54:34 UTC
Permalink
Post by Myriachan
I don't get what your point there is, sorry. Function g is ill-formed
because you're redeclaring GetMeow1 and GetMeow2 as overloaded functions
differing only by return type, via the most vexing parse rule. (GCC
accepts-invalid bug for not noticing this?)
It's not ill-formed. It just doesn't do what I expected, which is to call the
function.
Post by Myriachan
Also, why is &*p undefined behavior when p is null? If p were a pointer to
a hardware register instead of null, the hardware register would not be
read, but if the pointer is null, it suddenly becomes undefined behavior.
There are two distinct issues here:

First, *p is invalid if p is an invalid pointer, whether you do &*p or bind it
to a reference or call a function in it (p->foo()). That includes the null
pointer, but the rule is valid for any invalid pointer.

Then there's the null pointer invalidity: the C++ standard says the null
pointer is never valid, but you're right that some particular platforms may
have valid data at the zero address. If you need access to that, you need to
tell your compiler about it (for GCC, -fno-delete-null-pointer-checks). On the
overwhelming majority of other platforms, that's either an invalid page or
it's only accessed by assembly code anyway.
Post by Myriachan
This rule is self-consistent, but it's rather silly. (In my opinion, I
think that references to null ought to be legal, with lvalue to rvalue
conversion being undefined.)
I'm not arguing with that. We were bitten by UBSAN when doing:

p->static_function()

because it was shorter than

ClassName::static_function()

But p was null.
--
Thiago Macieira - thiago (AT) macieira.info - thiago (AT) kde.org
Software Architect - Intel Open Source Technology Center
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
Myriachan
2017-06-22 21:20:00 UTC
Permalink
Post by Thiago Macieira
Post by Myriachan
I don't get what your point there is, sorry. Function g is ill-formed
because you're redeclaring GetMeow1 and GetMeow2 as overloaded functions
differing only by return type, via the most vexing parse rule. (GCC
accepts-invalid bug for not noticing this?)
It's not ill-formed. It just doesn't do what I expected, which is to call the
function.
It's ill-formed because you're declaring an overload of a function that is
different from another overload by only its return type.

void g()
{
char(*GetMeow1());
char(GetMeow2());
}

These are function declarations, not function calls, by the "most vexing
parse rule". For whatever reason, GCC doesn't diagnose the code as
ill-formed, but Clang, Intel C++ and MSVC all do.

Did you mean this instead, using the unambiguous constructor call syntax to
avoid the "most vexing parse rule"?

void g()
{
char{*GetMeow1()};
char{GetMeow2()};
}
Post by Thiago Macieira
Post by Myriachan
Also, why is &*p undefined behavior when p is null? If p were a pointer
to
Post by Myriachan
a hardware register instead of null, the hardware register would not be
read, but if the pointer is null, it suddenly becomes undefined
behavior.
First, *p is invalid if p is an invalid pointer, whether you do &*p or bind it
to a reference or call a function in it (p->foo()). That includes the null
pointer, but the rule is valid for any invalid pointer.
Then there's the null pointer invalidity: the C++ standard says the null
pointer is never valid, but you're right that some particular platforms may
have valid data at the zero address. If you need access to that, you need to
tell your compiler about it (for GCC, -fno-delete-null-pointer-checks). On the
overwhelming majority of other platforms, that's either an invalid page or
it's only accessed by assembly code anyway.
My point was about the ambiguity of whether lvalue-to-rvalue conversion
occurs in various situations. I used volatile pointers to represent my
question, because lvalue-to-rvalue conversion has side effects, even reads.
Post by Thiago Macieira
Post by Myriachan
This rule is self-consistent, but it's rather silly. (In my opinion, I
think that references to null ought to be legal, with lvalue to rvalue
conversion being undefined.)
p->static_function()
because it was shorter than
ClassName::static_function()
But p was null.
I agree that this is a dumb rule.

Melissa
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
Thiago Macieira
2017-06-23 03:52:08 UTC
Permalink
Post by Myriachan
Did you mean this instead, using the unambiguous constructor call syntax to
avoid the "most vexing parse rule"?
No, I missed the fact that it was an overload: there's no volatile reference
or pointer in the new declaration. You're right, it's ill-formed.
--
Thiago Macieira - thiago (AT) macieira.info - thiago (AT) kde.org
Software Architect - Intel Open Source Technology Center
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
Andrey Semashev
2017-06-21 21:12:41 UTC
Permalink
Post by Myriachan
Post by Myriachan
A related issue I've thought about recently is with binding
references to
Post by Myriachan
volatile char *GetMeow1()
{
return &*reinterpret_cast<volatile char *>(0x12345678);
}
volatile char &GetMeow2()
{
return *reinterpret_cast<volatile char *>(0x12345678);
}
Do these read the hardware register at 0x12345678? Current
compilers say
Post by Myriachan
no, so I guess I just missed it in the Standard...?
Why do you think this is a problem?
https://godbolt.org/g/ze114W
I don't get what your point there is, sorry. Function g is ill-formed
because you're redeclaring GetMeow1 and GetMeow2 as overloaded functions
differing only by return type, via the most vexing parse rule. (GCC
accepts-invalid bug for not noticing this?)
Also, why is &*p undefined behavior when p is null? If p were a pointer
to a hardware register instead of null, the hardware register would not
be read, but if the pointer is null, it suddenly becomes undefined
behavior. This rule is self-consistent, but it's rather silly. (In my
opinion, I think that references to null ought to be legal, with lvalue
to rvalue conversion being undefined.)
Is there actually a concept of a pointer to a register in any of the
hardware architectures? I mean, a pointer as a runtime value that can be
stored in memory or another register.

Regarding your original examples, I don't see why there would be any
reason to generate a "read" instruction for these functions, unless the
hardware has to load the 0x12345678 constant from memory. Otherwise, I
would expect both functions to be equivalent and do nothing but to load
the constant to a register (and then maybe store it to the stack to
return or assign it to a variable). That is assuming the code is not
removed by DCE or transformed to something different entirely depending
on the context. None of the functions read from address 0x12345678.

The desire of banning null references is rather understandable as
references are viewed as an indication that the underlying object
exists. That is opposed to pointers, which explicitly have the special
null value.

I agree that dereferencing a null pointer is useful and doesn't cause
any effect that could have an UB in real life. But the never-null
property of references is also useful. If I had to choose between the
two, I'd probably choose the former.
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
Ville Voutilainen
2017-06-21 21:15:01 UTC
Permalink
The desire of banning null references is rather understandable as references
are viewed as an indication that the underlying object exists. That is
opposed to pointers, which explicitly have the special null value.
I agree that dereferencing a null pointer is useful and doesn't cause any
effect that could have an UB in real life. But the never-null property of
references is also useful. If I had to choose between the two, I'd probably
choose the former.
The ability to dereference a null pointer doesn't result in null
references springing into
existence.
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
Andrey Semashev
2017-06-21 21:17:51 UTC
Permalink
Post by Ville Voutilainen
The desire of banning null references is rather understandable as references
are viewed as an indication that the underlying object exists. That is
opposed to pointers, which explicitly have the special null value.
I agree that dereferencing a null pointer is useful and doesn't cause any
effect that could have an UB in real life. But the never-null property of
references is also useful. If I had to choose between the two, I'd probably
choose the former.
The ability to dereference a null pointer doesn't result in null
references springing into
existence.
What other result could this operation have?
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
Ville Voutilainen
2017-06-21 21:23:15 UTC
Permalink
Post by Andrey Semashev
Post by Ville Voutilainen
The desire of banning null references is rather understandable as references
are viewed as an indication that the underlying object exists. That is
opposed to pointers, which explicitly have the special null value.
I agree that dereferencing a null pointer is useful and doesn't cause any
effect that could have an UB in real life. But the never-null property of
references is also useful. If I had to choose between the two, I'd probably
choose the former.
The ability to dereference a null pointer doesn't result in null
references springing into
existence.
What other result could this operation have?
An lvalue of the pointed-to type.
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
Andrey Semashev
2017-06-21 21:33:20 UTC
Permalink
Post by Ville Voutilainen
Post by Andrey Semashev
Post by Ville Voutilainen
The ability to dereference a null pointer doesn't result in null
references springing into
existence.
What other result could this operation have?
An lvalue of the pointed-to type.
Do you mean that dereferencing a pointer could potentially construct an
object? I hope not.

If not, and we allow dereferencing invalid pointers, then we allow
references to non-existing objects. Such a reference formed from
dereferencing a null pointer I call a null reference.

Also, I hope you're not implying that dereferencing a null pointer would
somehow result in a valid reference to an existing object because I
would expect this to work:

int* p = nullptr;
int& r = *p;
int* q = &r;
assert(q == nullptr);
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
Ville Voutilainen
2017-06-22 00:32:04 UTC
Permalink
Forgot to reply to the list...
Post by Andrey Semashev
Post by Ville Voutilainen
Post by Andrey Semashev
Post by Ville Voutilainen
The ability to dereference a null pointer doesn't result in null
references springing into
existence.
What other result could this operation have?
An lvalue of the pointed-to type.
Do you mean that dereferencing a pointer could potentially construct an
object? I hope not.
No, I said no such thing.
Post by Andrey Semashev
If not, and we allow dereferencing invalid pointers, then we allow
references to non-existing objects. Such a reference formed from
dereferencing a null pointer I call a null reference.
No reference is ever created by the original example. And dereferencing
a null pointer doesn't mean we allow references to non-existing
objects; the result
of dereferencing a pointer is not a reference.
Post by Andrey Semashev
Also, I hope you're not implying that dereferencing a null pointer would
somehow result in a valid reference to an existing object because I would
int* p = nullptr;
int& r = *p;
int* q = &r;
assert(q == nullptr);
There's no intention to make that valid. A part of the reason why the
issue is still open is
that the specification needs to be changed so that null references
don't come about, but
the dereferencing of a null pointer in and of itself is ok as long as
it's not followed by
an lvalue-to-rvalue conversion.
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
Thiago Macieira
2017-06-22 04:20:43 UTC
Permalink
Post by Andrey Semashev
Is there actually a concept of a pointer to a register in any of the
hardware architectures? I mean, a pointer as a runtime value that can be
stored in memory or another register.
Not in the sense that you're thinking. Modern architectural registers are not
addressable in memory, only by instructions. The closest are indirect
registers, such as several types of OS-level registers found in IA-64.

What Melissa was talking about maps more directly to modern MMIO. In some
older architectures (Apollo Guidance Calculator comes to mind), registers were
actually addressable in a special portion of memory.
Post by Andrey Semashev
Regarding your original examples, I don't see why there would be any
reason to generate a "read" instruction for these functions, unless the
hardware has to load the 0x12345678 constant from memory. Otherwise, I
would expect both functions to be equivalent and do nothing but to load
the constant to a register (and then maybe store it to the stack to
return or assign it to a variable). That is assuming the code is not
removed by DCE or transformed to something different entirely depending
on the context. None of the functions read from address 0x12345678.
I didn't understand what Melissa's problem was either. But it's not trying to
load the 0x12345678 constant, but read a value from the address 0x12345678.
Since the memory location is accessed volatilely, it can't be deleted by the
compiler, provided the code was actually reached (note:: volatile != atomic).
DCE obviously still applies, as do the optimisations based on UB.
Post by Andrey Semashev
I agree that dereferencing a null pointer is useful and doesn't cause
any effect that could have an UB in real life. But the never-null
property of references is also useful. If I had to choose between the
two, I'd probably choose the former.
Sorry, I disagree. Dereferencing a pointer is akin to loading a value from it,
so dereferencing a null pointer is like trying to load a value from the zero
address. On most systems, that will cause a general protection fault -- but
not all of them. Hence, UB.
--
Thiago Macieira - thiago (AT) macieira.info - thiago (AT) kde.org
Software Architect - Intel Open Source Technology Center
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
Richard Smith
2017-06-22 19:26:52 UTC
Permalink
Post by Thiago Macieira
Post by Andrey Semashev
Is there actually a concept of a pointer to a register in any of the
hardware architectures? I mean, a pointer as a runtime value that can be
stored in memory or another register.
Not in the sense that you're thinking. Modern architectural registers are not
addressable in memory, only by instructions. The closest are indirect
registers, such as several types of OS-level registers found in IA-64.
What Melissa was talking about maps more directly to modern MMIO. In some
older architectures (Apollo Guidance Calculator comes to mind), registers were
actually addressable in a special portion of memory.
Post by Andrey Semashev
Regarding your original examples, I don't see why there would be any
reason to generate a "read" instruction for these functions, unless the
hardware has to load the 0x12345678 constant from memory. Otherwise, I
would expect both functions to be equivalent and do nothing but to load
the constant to a register (and then maybe store it to the stack to
return or assign it to a variable). That is assuming the code is not
removed by DCE or transformed to something different entirely depending
on the context. None of the functions read from address 0x12345678.
I didn't understand what Melissa's problem was either. But it's not trying to
load the 0x12345678 constant, but read a value from the address 0x12345678.
Since the memory location is accessed volatilely, it can't be deleted by the
compiler, provided the code was actually reached (note:: volatile != atomic).
DCE obviously still applies, as do the optimisations based on UB.
Post by Andrey Semashev
I agree that dereferencing a null pointer is useful and doesn't cause
any effect that could have an UB in real life. But the never-null
property of references is also useful. If I had to choose between the
two, I'd probably choose the former.
Sorry, I disagree. Dereferencing a pointer is akin to loading a value from it,
so dereferencing a null pointer is like trying to load a value from the zero
address. On most systems, that will cause a general protection fault -- but
not all of them. Hence, UB.
Dereferencing a pointer ("indirection" is the standard's term) is a
conversion from a pointer to an lvalue. The load (lvalue-to-rvalue
conversion), if any, is separate and comes later. There are at least three
other things that could happen to the result of indirection through a
pointer: it could be bound to a reference (which would then require that
the pointer points to suitable storage for the underlying type of the
reference), its address could be taken (in which case, per the direction of
core issue 232, there is no problem and you get back your original
pointer), or it could be discarded (in which case a load may be synthesized
if the type of the lvalue is volatile-qualified).

If you keep in mind that indirection, reference binding, and the
lvalue-to-rvalue conversion are three separate operations, the behavior
here is a lot less confusing. (And yes, it'd be nice if we didn't have so
much complexity in the first place, but I think that ship has sailed.) So:

int *p = nullptr;
*p; // ok (per core issue 232), indirection has no side-conditions
&*p; // ok (per core issue 232), address of this empty lvalue is a null
pointer value
int &r = *p; // UB: reference not bound to suitable storage for int object
int n = *p; // UB: lvalue *p does not denote an 'int' object within its
lifetime
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
Brian Bi
2017-06-22 19:28:40 UTC
Permalink
Post by Richard Smith
Post by Thiago Macieira
Post by Andrey Semashev
Is there actually a concept of a pointer to a register in any of the
hardware architectures? I mean, a pointer as a runtime value that can be
stored in memory or another register.
Not in the sense that you're thinking. Modern architectural registers are not
addressable in memory, only by instructions. The closest are indirect
registers, such as several types of OS-level registers found in IA-64.
What Melissa was talking about maps more directly to modern MMIO. In some
older architectures (Apollo Guidance Calculator comes to mind), registers were
actually addressable in a special portion of memory.
Post by Andrey Semashev
Regarding your original examples, I don't see why there would be any
reason to generate a "read" instruction for these functions, unless the
hardware has to load the 0x12345678 constant from memory. Otherwise, I
would expect both functions to be equivalent and do nothing but to load
the constant to a register (and then maybe store it to the stack to
return or assign it to a variable). That is assuming the code is not
removed by DCE or transformed to something different entirely depending
on the context. None of the functions read from address 0x12345678.
I didn't understand what Melissa's problem was either. But it's not trying to
load the 0x12345678 constant, but read a value from the address 0x12345678.
Since the memory location is accessed volatilely, it can't be deleted by the
compiler, provided the code was actually reached (note:: volatile != atomic).
DCE obviously still applies, as do the optimisations based on UB.
Post by Andrey Semashev
I agree that dereferencing a null pointer is useful and doesn't cause
any effect that could have an UB in real life. But the never-null
property of references is also useful. If I had to choose between the
two, I'd probably choose the former.
Sorry, I disagree. Dereferencing a pointer is akin to loading a value from it,
so dereferencing a null pointer is like trying to load a value from the zero
address. On most systems, that will cause a general protection fault -- but
not all of them. Hence, UB.
Dereferencing a pointer ("indirection" is the standard's term) is a
conversion from a pointer to an lvalue. The load (lvalue-to-rvalue
conversion), if any, is separate and comes later. There are at least three
other things that could happen to the result of indirection through a
pointer: it could be bound to a reference (which would then require that
the pointer points to suitable storage for the underlying type of the
reference), its address could be taken (in which case, per the direction of
core issue 232, there is no problem and you get back your original
pointer), or it could be discarded (in which case a load may be synthesized
if the type of the lvalue is volatile-qualified).
If you keep in mind that indirection, reference binding, and the
lvalue-to-rvalue conversion are three separate operations, the behavior
here is a lot less confusing. (And yes, it'd be nice if we didn't have so
int *p = nullptr;
*p; // ok (per core issue 232), indirection has no side-conditions
&*p; // ok (per core issue 232), address of this empty lvalue is a null
pointer value
int &r = *p; // UB: reference not bound to suitable storage for int object
int n = *p; // UB: lvalue *p does not denote an 'int' object within its
lifetime
Is this the consensus of the core working group? If so, is the standard
going to be updated to reflect it any time soon? If not, what are the
obstacles?
--
*Brian Bi*
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
Richard Smith
2017-06-22 19:54:14 UTC
Permalink
Post by Brian Bi
Post by Richard Smith
Post by Thiago Macieira
Post by Andrey Semashev
Is there actually a concept of a pointer to a register in any of the
hardware architectures? I mean, a pointer as a runtime value that can
be
Post by Andrey Semashev
stored in memory or another register.
Not in the sense that you're thinking. Modern architectural registers are not
addressable in memory, only by instructions. The closest are indirect
registers, such as several types of OS-level registers found in IA-64.
What Melissa was talking about maps more directly to modern MMIO. In some
older architectures (Apollo Guidance Calculator comes to mind), registers were
actually addressable in a special portion of memory.
Post by Andrey Semashev
Regarding your original examples, I don't see why there would be any
reason to generate a "read" instruction for these functions, unless the
hardware has to load the 0x12345678 constant from memory. Otherwise, I
would expect both functions to be equivalent and do nothing but to load
the constant to a register (and then maybe store it to the stack to
return or assign it to a variable). That is assuming the code is not
removed by DCE or transformed to something different entirely depending
on the context. None of the functions read from address 0x12345678.
I didn't understand what Melissa's problem was either. But it's not trying to
load the 0x12345678 constant, but read a value from the address 0x12345678.
Since the memory location is accessed volatilely, it can't be deleted by the
compiler, provided the code was actually reached (note:: volatile != atomic).
DCE obviously still applies, as do the optimisations based on UB.
Post by Andrey Semashev
I agree that dereferencing a null pointer is useful and doesn't cause
any effect that could have an UB in real life. But the never-null
property of references is also useful. If I had to choose between the
two, I'd probably choose the former.
Sorry, I disagree. Dereferencing a pointer is akin to loading a value from it,
so dereferencing a null pointer is like trying to load a value from the zero
address. On most systems, that will cause a general protection fault -- but
not all of them. Hence, UB.
Dereferencing a pointer ("indirection" is the standard's term) is a
conversion from a pointer to an lvalue. The load (lvalue-to-rvalue
conversion), if any, is separate and comes later. There are at least three
other things that could happen to the result of indirection through a
pointer: it could be bound to a reference (which would then require that
the pointer points to suitable storage for the underlying type of the
reference), its address could be taken (in which case, per the direction of
core issue 232, there is no problem and you get back your original
pointer), or it could be discarded (in which case a load may be synthesized
if the type of the lvalue is volatile-qualified).
If you keep in mind that indirection, reference binding, and the
lvalue-to-rvalue conversion are three separate operations, the behavior
here is a lot less confusing. (And yes, it'd be nice if we didn't have so
int *p = nullptr;
*p; // ok (per core issue 232), indirection has no side-conditions
&*p; // ok (per core issue 232), address of this empty lvalue is a null
pointer value
int &r = *p; // UB: reference not bound to suitable storage for int object
int n = *p; // UB: lvalue *p does not denote an 'int' object within its
lifetime
Is this the consensus of the core working group? If so, is the standard
going to be updated to reflect it any time soon? If not, what are the
obstacles?
This was the consensus last time it was discussed. I think the biggest
obstacle is that there is simply more work than we have time to do.
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
Myriachan
2017-06-22 21:38:47 UTC
Permalink
Post by j***@gmail.com
int *p = nullptr;
Post by Brian Bi
Post by Richard Smith
*p; // ok (per core issue 232), indirection has no side-conditions
&*p; // ok (per core issue 232), address of this empty lvalue is a null
pointer value
int &r = *p; // UB: reference not bound to suitable storage for int object
int n = *p; // UB: lvalue *p does not denote an 'int' object within its
lifetime
Is this the consensus of the core working group? If so, is the standard
going to be updated to reflect it any time soon? If not, what are the
obstacles?
This was the consensus last time it was discussed. I think the biggest
obstacle is that there is simply more work than we have time to do.
The above is problematic due to the existence of hardware registers in
embedded platforms. One intended purpose of "volatile" is to support
accessing memory-mapped hardware registers in C/C++. There is a ton of
code for embedded platforms (and OS kernels) that relies upon this.

Some hardware is sensitive to reads. Sometimes, it is useful to merely
read such registers, because whatever side effect reading has is desirable
in the given situation, but the value read may not be useful in whatever
circumstance.

The above example breaks this design. Consider:

// 0x12345678 is a read-sensitive hardware register on this platform.
volatile int *p = reinterpret_cast<volatile int *>(0x12345678);
*p;
volatile int *q = nullptr;
*q;

By what was said above, the consensus in core issue 232 is that *q should
have no effect. But should *p? It certainly was the intent to trigger a
read from the hardware register in this situation.

Is it meant that in order to trigger a read from a pointer, you have to put
the value somewhere? What is "somewhere"?

I don't see a solution here other than to introduce context-sensitivity to
the use of pointers and references.

This issue applies to both C and C++. C++ references just add a question
of whether null references are allowed.

Melissa
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
Richard Smith
2017-06-22 22:14:19 UTC
Permalink
Post by Myriachan
Post by j***@gmail.com
int *p = nullptr;
Post by Brian Bi
Post by Richard Smith
*p; // ok (per core issue 232), indirection has no side-conditions
&*p; // ok (per core issue 232), address of this empty lvalue is a null
pointer value
int &r = *p; // UB: reference not bound to suitable storage for int object
int n = *p; // UB: lvalue *p does not denote an 'int' object within its
lifetime
Is this the consensus of the core working group? If so, is the standard
going to be updated to reflect it any time soon? If not, what are the
obstacles?
This was the consensus last time it was discussed. I think the biggest
obstacle is that there is simply more work than we have time to do.
The above is problematic due to the existence of hardware registers in
embedded platforms. One intended purpose of "volatile" is to support
accessing memory-mapped hardware registers in C/C++. There is a ton of
code for embedded platforms (and OS kernels) that relies upon this.
Some hardware is sensitive to reads. Sometimes, it is useful to merely
read such registers, because whatever side effect reading has is desirable
in the given situation, but the value read may not be useful in whatever
circumstance.
// 0x12345678 is a read-sensitive hardware register on this platform.
volatile int *p = reinterpret_cast<volatile int *>(0x12345678);
*p;
volatile int *q = nullptr;
*q;
By what was said above, the consensus in core issue 232 is that *q should
have no effect. But should *p? It certainly was the intent to trigger a
read from the hardware register in this situation.
That's not quite right; this is a case I mentioned in my prior message: "it
could be discarded (in which case a load may be synthesized if the type of
the lvalue is volatile-qualified)."

The gory details are in http://eel.is/c++draft/expr#12 -- because *p is a
glvalue of volatile-qualified type, and the form of the expression is one
of those listed in p12, there is an implicit lvalue-to-rvalue conversion
performed here. So both the *p and *q cases trigger a read, which may or
may not have defined behavior depending on the implementation-defined
semantics of volatile accesses.
Post by Myriachan
Is it meant that in order to trigger a read from a pointer, you have to
put the value somewhere? What is "somewhere"?
I don't see a solution here other than to introduce context-sensitivity to
the use of pointers and references.
That's more or less where the standard is today.
Post by Myriachan
This issue applies to both C and C++. C++ references just add a question
of whether null references are allowed.
Melissa
--
---
You received this message because you are subscribed to the Google Groups
"ISO C++ Standard - Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an
Visit this group at https://groups.google.com/a/isocpp.org/group/std-
discussion/.
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
Thiago Macieira
2017-06-23 04:15:44 UTC
Permalink
Post by Myriachan
// 0x12345678 is a read-sensitive hardware register on this platform.
volatile int *p = reinterpret_cast<volatile int *>(0x12345678);
*p;
volatile int *q = nullptr;
*q;
By what was said above, the consensus in core issue 232 is that *q should
have no effect. But should *p? It certainly was the intent to trigger a
read from the hardware register in this situation.
Is it meant that in order to trigger a read from a pointer, you have to put
the value somewhere? What is "somewhere"?
I don't see a solution here other than to introduce context-sensitivity to
the use of pointers and references.
I'd redesign and work around the problem. Instead:

inline int read_register(unsigned long address)
{
return *reinterpret_cast<volatile int *>(address);
}

void f()
{
read_register(0x12345678);
}

That produces a conversion to rvalue, so there is a load. Since it's volatile,
the load cannot be eliminated. As a bonus, it's clear this is loading a
register from the caller code and that there could be side-effects.

Or if you even want to hide the null pointer dereference from GCC:

inline int read_register(unsigned long address)
{
int result;
asm volatile ("mov (%1), %0" : "=r" (result) : "r" (address));
return result;
}

void f()
{
read_register(0);
}
--
Thiago Macieira - thiago (AT) macieira.info - thiago (AT) kde.org
Software Architect - Intel Open Source Technology Center
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
Columbo
2017-06-27 10:06:43 UTC
Permalink
Post by Richard Smith
Post by Brian Bi
Post by Richard Smith
Post by Thiago Macieira
Post by Andrey Semashev
Is there actually a concept of a pointer to a register in any of the
hardware architectures? I mean, a pointer as a runtime value that can
be
Post by Andrey Semashev
stored in memory or another register.
Not in the sense that you're thinking. Modern architectural registers are not
addressable in memory, only by instructions. The closest are indirect
registers, such as several types of OS-level registers found in IA-64.
What Melissa was talking about maps more directly to modern MMIO. In some
older architectures (Apollo Guidance Calculator comes to mind), registers were
actually addressable in a special portion of memory.
Post by Andrey Semashev
Regarding your original examples, I don't see why there would be any
reason to generate a "read" instruction for these functions, unless
the
Post by Andrey Semashev
hardware has to load the 0x12345678 constant from memory. Otherwise, I
would expect both functions to be equivalent and do nothing but to
load
Post by Andrey Semashev
the constant to a register (and then maybe store it to the stack to
return or assign it to a variable). That is assuming the code is not
removed by DCE or transformed to something different entirely
depending
Post by Andrey Semashev
on the context. None of the functions read from address 0x12345678.
I didn't understand what Melissa's problem was either. But it's not trying to
load the 0x12345678 constant, but read a value from the address 0x12345678.
Since the memory location is accessed volatilely, it can't be deleted by the
compiler, provided the code was actually reached (note:: volatile != atomic).
DCE obviously still applies, as do the optimisations based on UB.
Post by Andrey Semashev
I agree that dereferencing a null pointer is useful and doesn't cause
any effect that could have an UB in real life. But the never-null
property of references is also useful. If I had to choose between the
two, I'd probably choose the former.
Sorry, I disagree. Dereferencing a pointer is akin to loading a value from it,
so dereferencing a null pointer is like trying to load a value from the zero
address. On most systems, that will cause a general protection fault -- but
not all of them. Hence, UB.
Dereferencing a pointer ("indirection" is the standard's term) is a
conversion from a pointer to an lvalue. The load (lvalue-to-rvalue
conversion), if any, is separate and comes later. There are at least three
other things that could happen to the result of indirection through a
pointer: it could be bound to a reference (which would then require that
the pointer points to suitable storage for the underlying type of the
reference), its address could be taken (in which case, per the direction of
core issue 232, there is no problem and you get back your original
pointer), or it could be discarded (in which case a load may be synthesized
if the type of the lvalue is volatile-qualified).
If you keep in mind that indirection, reference binding, and the
lvalue-to-rvalue conversion are three separate operations, the behavior
here is a lot less confusing. (And yes, it'd be nice if we didn't have so
int *p = nullptr;
*p; // ok (per core issue 232), indirection has no side-conditions
&*p; // ok (per core issue 232), address of this empty lvalue is a null
pointer value
int &r = *p; // UB: reference not bound to suitable storage for int object
int n = *p; // UB: lvalue *p does not denote an 'int' object within its
lifetime
Is this the consensus of the core working group? If so, is the standard
going to be updated to reflect it any time soon? If not, what are the
obstacles?
This was the consensus last time it was discussed. I think the biggest
obstacle is that there is simply more work than we have time to do.
I did start drafting a paper last year: http://arcoth.github.io/Proposals/EmptyLvalues.html

If I get some guidance, this could be properly formalized and sent to Core.
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
Brian Bi
2018-10-09 19:45:20 UTC
Permalink
Post by Columbo
Post by Richard Smith
Post by Brian Bi
Post by Richard Smith
Post by Andrey Semashev
Post by Andrey Semashev
Is there actually a concept of a pointer to a register in any of the
hardware architectures? I mean, a pointer as a runtime value that
can be
Post by Andrey Semashev
stored in memory or another register.
Not in the sense that you're thinking. Modern architectural registers are not
addressable in memory, only by instructions. The closest are indirect
registers, such as several types of OS-level registers found in IA-64.
What Melissa was talking about maps more directly to modern MMIO. In some
older architectures (Apollo Guidance Calculator comes to mind), registers were
actually addressable in a special portion of memory.
Post by Andrey Semashev
Regarding your original examples, I don't see why there would be any
reason to generate a "read" instruction for these functions, unless
the
Post by Andrey Semashev
hardware has to load the 0x12345678 constant from memory. Otherwise,
I
Post by Andrey Semashev
would expect both functions to be equivalent and do nothing but to
load
Post by Andrey Semashev
the constant to a register (and then maybe store it to the stack to
return or assign it to a variable). That is assuming the code is not
removed by DCE or transformed to something different entirely
depending
Post by Andrey Semashev
on the context. None of the functions read from address 0x12345678.
I didn't understand what Melissa's problem was either. But it's not trying to
load the 0x12345678 constant, but read a value from the address 0x12345678.
Since the memory location is accessed volatilely, it can't be deleted by the
compiler, provided the code was actually reached (note:: volatile != atomic).
DCE obviously still applies, as do the optimisations based on UB.
Post by Andrey Semashev
I agree that dereferencing a null pointer is useful and doesn't cause
any effect that could have an UB in real life. But the never-null
property of references is also useful. If I had to choose between the
two, I'd probably choose the former.
Sorry, I disagree. Dereferencing a pointer is akin to loading a value from it,
so dereferencing a null pointer is like trying to load a value from the zero
address. On most systems, that will cause a general protection fault -- but
not all of them. Hence, UB.
Dereferencing a pointer ("indirection" is the standard's term) is a
conversion from a pointer to an lvalue. The load (lvalue-to-rvalue
conversion), if any, is separate and comes later. There are at least three
other things that could happen to the result of indirection through a
pointer: it could be bound to a reference (which would then require that
the pointer points to suitable storage for the underlying type of the
reference), its address could be taken (in which case, per the direction of
core issue 232, there is no problem and you get back your original
pointer), or it could be discarded (in which case a load may be synthesized
if the type of the lvalue is volatile-qualified).
If you keep in mind that indirection, reference binding, and the
lvalue-to-rvalue conversion are three separate operations, the behavior
here is a lot less confusing. (And yes, it'd be nice if we didn't have so
int *p = nullptr;
*p; // ok (per core issue 232), indirection has no side-conditions
&*p; // ok (per core issue 232), address of this empty lvalue is a null
pointer value
int &r = *p; // UB: reference not bound to suitable storage for int object
int n = *p; // UB: lvalue *p does not denote an 'int' object within its
lifetime
Is this the consensus of the core working group? If so, is the standard
going to be updated to reflect it any time soon? If not, what are the
obstacles?
This was the consensus last time it was discussed. I think the biggest
obstacle is that there is simply more work than we have time to do.
I did start drafting a paper last year: http://arcoth.github.io/Proposals/EmptyLvalues.html
If I get some guidance, this could be properly formalized and sent to Core.
Sorry to revive this dead thread, but have you thought about posting that
paper to std-proposals? It might get more eyeballs if you do that.
Post by Columbo
--
---
You received this message because you are subscribed to the Google Groups
"ISO C++ Standard - Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an
Visit this group at
https://groups.google.com/a/isocpp.org/group/std-discussion/.
--
*Brian Bi*
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
b***@gmail.com
2017-06-22 19:53:57 UTC
Permalink
Post by Richard Smith
If you keep in mind that indirection, reference binding, and the
lvalue-to-rvalue conversion are three separate operations, the behavior
here is a lot less confusing. (And yes, it'd be nice if we didn't have so
int *p = nullptr;
*p; // ok (per core issue 232), indirection has no side-conditions
&*p; // ok (per core issue 232), address of this empty lvalue is a null
pointer value
int &r = *p; // UB: reference not bound to suitable storage for int object
int n = *p; // UB: lvalue *p does not denote an 'int' object within its
lifetime
How does this play with non-null pointers? Say something like:

int c[10];
std::array<int, 10> cpp;

foo(&c[0], &c[10]); // okay because we're never forming a reference to
c[10]?
foo(&cpp[0], &cpp[10]); // not okay because we have to form a reference to
cpp[10]?

(foo here is just a stand-in for some algorithm that takes an iterator-pair)
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
Richard Smith
2017-06-22 20:03:11 UTC
Permalink
Post by Richard Smith
If you keep in mind that indirection, reference binding, and the
Post by Richard Smith
lvalue-to-rvalue conversion are three separate operations, the behavior
here is a lot less confusing. (And yes, it'd be nice if we didn't have so
int *p = nullptr;
*p; // ok (per core issue 232), indirection has no side-conditions
&*p; // ok (per core issue 232), address of this empty lvalue is a null
pointer value
int &r = *p; // UB: reference not bound to suitable storage for int object
int n = *p; // UB: lvalue *p does not denote an 'int' object within its
lifetime
int c[10];
std::array<int, 10> cpp;
foo(&c[0], &c[10]); // okay because we're never forming a reference
to c[10]?
foo(&cpp[0], &cpp[10]); // not okay because we have to form a reference
to cpp[10]?
(foo here is just a stand-in for some algorithm that takes an
iterator-pair)
Yes, your comments appear to be correct. (Also of note: cpp.at(10) throws.)

We are trying to serve two masters here: C explicitly permits "&c[10]" (and
code relies on it working), whereas C++ wants its references to be "just
another name for some existing object". So it seems that we must either
violate the principle of C compatibility, violate the principle that
references refer to objects, violate the principle that user-defined types
can act exactly like builtin types, or introduce some new language
extension (such as a "nullable reference"). None of those options seems
ideal.
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
Myriachan
2017-06-22 21:22:33 UTC
Permalink
Post by Richard Smith
Yes, your comments appear to be correct. (Also of note: cpp.at(10) throws.)
We are trying to serve two masters here: C explicitly permits "&c[10]"
(and code relies on it working), whereas C++ wants its references to be
"just another name for some existing object". So it seems that we must
either violate the principle of C compatibility, violate the principle that
references refer to objects, violate the principle that user-defined types
can act exactly like builtin types, or introduce some new language
extension (such as a "nullable reference"). None of those options seems
ideal.
I don't see a good solution other than to say that references are pointers
with a different syntax. That's how it works in implementations anyway.

Melissa
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
Brian Bi
2018-10-09 19:54:22 UTC
Permalink
Post by Richard Smith
Post by Richard Smith
If you keep in mind that indirection, reference binding, and the
Post by Richard Smith
lvalue-to-rvalue conversion are three separate operations, the behavior
here is a lot less confusing. (And yes, it'd be nice if we didn't have so
int *p = nullptr;
*p; // ok (per core issue 232), indirection has no side-conditions
&*p; // ok (per core issue 232), address of this empty lvalue is a null
pointer value
int &r = *p; // UB: reference not bound to suitable storage for int object
int n = *p; // UB: lvalue *p does not denote an 'int' object within its
lifetime
int c[10];
std::array<int, 10> cpp;
foo(&c[0], &c[10]); // okay because we're never forming a reference
to c[10]?
foo(&cpp[0], &cpp[10]); // not okay because we have to form a reference
to cpp[10]?
(foo here is just a stand-in for some algorithm that takes an
iterator-pair)
Yes, your comments appear to be correct. (Also of note: cpp.at(10) throws.)
We are trying to serve two masters here: C explicitly permits "&c[10]"
(and code relies on it working), whereas C++ wants its references to be
"just another name for some existing object". So it seems that we must
either violate the principle of C compatibility, violate the principle that
references refer to objects, violate the principle that user-defined types
can act exactly like builtin types, or introduce some new language
extension (such as a "nullable reference"). None of those options seems
ideal.
Another option is to add a special exception that binding a null or
past-the-end lvalue to a reference is well-defined only when returning from
a function. Obviously this approach is also not without its disadvantages.

template <class T, size_t size>
T& array<T, size>::operator[](size_t index) { return data_[index]; }
// ...
int* p = &cpp[10]; // ok
int& r = cpp[10]; // not ok
--
Post by Richard Smith
---
You received this message because you are subscribed to the Google Groups
"ISO C++ Standard - Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an
Visit this group at
https://groups.google.com/a/isocpp.org/group/std-discussion/.
--
*Brian Bi*
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
Andrey Semashev
2017-06-22 21:14:25 UTC
Permalink
Post by Richard Smith
Dereferencing a pointer ("indirection" is the standard's term) is a
conversion from a pointer to an lvalue. The load (lvalue-to-rvalue
conversion), if any, is separate and comes later. There are at least
three other things that could happen to the result of indirection
through a pointer: it could be bound to a reference (which would then
require that the pointer points to suitable storage for the underlying
type of the reference), its address could be taken (in which case, per
the direction of core issue 232, there is no problem and you get back
your original pointer), or it could be discarded (in which case a load
may be synthesized if the type of the lvalue is volatile-qualified).
If you keep in mind that indirection, reference binding, and the
lvalue-to-rvalue conversion are three separate operations, the behavior
here is a lot less confusing. (And yes, it'd be nice if we didn't have
so much complexity in the first place, but I think that ship has
int *p = nullptr;
*p; // ok (per core issue 232), indirection has no side-conditions
&*p; // ok (per core issue 232), address of this empty lvalue is a null
pointer value
int &r = *p; // UB: reference not bound to suitable storage for int object
int n = *p; // UB: lvalue *p does not denote an 'int' object within its
lifetime
I was mistakenly assuming that the result of the builtin indirection
operator was a reference. The fact that the result is an lvalue which is
not a reference is confusing because such interface can't be expressed
in C++. I mean, `T operator*()` returns an rvalue, not lvalue. This also
signifies that the solution proposed in the issue 232 does not work for
smart pointers and iterators.
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
Daniel Krügler
2017-06-21 20:37:46 UTC
Permalink
Post by j***@gmail.com
Is this undefined behavior?
int *p = nullptr;
int *q = &*p;
There exists a core issue to clarify the situation here:

http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#232
Post by j***@gmail.com
What about this?
std::unique_ptr<int> p = nullptr;
int *q = &*p;
The state is clearly undefined, because of the violation of the
following precondition of operator* ([unique.ptr.single.observers]):

1 Requires: get() != nullptr.

- Daniel
--
---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-discussion+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
Visit this group at https://groups.google.com/a/isocpp.org/group/std-discussion/.
Loading...