Chris Dollin said:
Expressions that "denote a place" are those that can be evaluated
for their lvalues. Those that don't, can't.
[...]
Right, using the old definition of "lvalue", not the one in the C
standard (as you know).
In the C standard, an "lvalue" is not the result of evaluating an
expression; instead, certain expressions are themselves lvalues. I
suspect that if the C committee had stayed with the older meaning of
the term, they could have avoided some serious problems.
The C90 definition of an "lvalue" was (C90 6.2.2.1):
An _lvalue_ is an expression (with an object type or an incomplete
type other than void) that designates an object.
Consider:
int x; /* line 1 */
int *ptr = NULL; /* line 2 */
ptr = &x; /* line 3 */
Before line 3 is executed, the expression *ptr does not designate an
object, so by a literal reading of the definition, *ptr is not an
value, but it becomes one after line 3 is executed. This clearly was
not the intent, since the lvalue-ness of an exression, in many cases,
needs to be determined at compilation time. *ptr should be an lvalue
regardless of the current value of ptr; attempting to evaluate it *as
an lvalue* invokes undefined behavior if it doesn't *currently*
designate an object.
So the C99 committee attempted to solve this problem, but created a
bigger one. C99 6.3.2.1p1:
An _lvalue_ is an expression with an object type or an incomplete
type other than void; if an lvalue does not designate an object
when it is evaluated, the behavior is undefined.
So the lvalue-ness of an expression no longer depends on the current
value of the expression or any subexpression (solving the problem with
the C90 definition) -- *but* the definition no longer says that it
designates an object, which is the whole idea. By a literal reading
of the C99 definition, 42 is an lvalue (it's an expression of an
object type, namely int). Again, this clearly is not the intent.
Stating the actual intent in standardese is difficult, but not
impossible. An improvement would be to revert to the C90 definition
and add the word "potentially", with a footnote to explain what that
means:
An _lvalue_ is an expression (with an object type or an incomplete
type other than void) that potentially (footnote) designates an
object.
(footnote) An expression potentially designates an object either
if it actually does so, or if it would do so given appropriate
values for its subexpressions. For example, if ptr is an object
pointer, *ptr potentially designates an object (though it doesn't
actually designate an object unless ptr has an appropriate value).
That's off the top of my head; I'm sure it could be worded better.
Perhaps if the standard said, instead of an expression *being* an
lvalue, that its lvalue can be evaluated, this problem wouldn't have
occurred. We'd still need rules about which expressions can be
evaluated for their lvalues, and wording about when such an evaluation
invokes undefined behavior. And if such a change were made now, all
the references to "lvalue" in the standard would have to be modified
to reflect the new (old) meaning.
I suspect we're just stuck with the current meaning of lvalue (and we
have to read what the definition *should* say rather than what it
*does* say).