Defect Report: Unspecified value categories of built-in operator operands

191 views
Skip to first unread message

Joseph Mansfield

unread,
Mar 15, 2013, 5:39:44 AM3/15/13
to std-dis...@isocpp.org
Summary:

Section 3.10 [basic.lval] promises that "the discussion of each built-in
operator in Clause 5 indicates [...] the value categories of the operands it
expects." However, many of the value categories of operands are left
unspecified, leaving us to infer that they are prvalues from non-normative
notes and suggestions. Whether an operator expects a prvalue operand is
important in determining whether lvalue-to-rvalue conversion occurs.

Prior Discussion:


Details:

Both Sections 3.10 [basic.lval] and 5 [expr] discuss the value categories of
operands of built-in operators. In a Note in 3.10/1 [basic.lval], it is said:

The discussion of each built-in operator in Clause 5 indicates the category
of the value it yields and the value categories of the operands it expects.
For example, the built-in assignment operators expect that the left operand
is an lvalue and that the right operand is a prvalue and yield an lvalue as
the result.

This tells us two things: we should expect the the value categories of built-in
operands to be specified in Section 5 [expr] and that the built-in assignment
operators expect their right operand to be a prvalue expression. However, this
a note and therefore non-normative.

We are also told in 3.10/2 [basic.lval]:

Whenever a glvalue appears in a context where a prvalue is expected, the
glvalue is converted to a prvalue

That is, when an operator expects a prvalue operand, lvalue-to-rvalue
conversion must occur. This is further backed up in Section 5/8 [expr]:

Whenever a glvalue expression appears as an operand of an operator that
expects a prvalue for that operand, the lvalue-to-rvalue (4.1), array-to-
pointer (4.2), or function-to-pointer (4.3) standard conversions are applied
to convert the expression to a prvalue.

We expect any operators that make use of the value of an operand to require
that operand to be a prvalue expression, because lvalue-to-rvalue
conversion is logically the act of reading the value of an object. However,
only those operands that are required to be glvalues are enumerated in
Section 5 [expr] and many value categories of operands are left unspecified.

One response to this is that these unspecified operands can be expressions
of any value category and should be left unspecified. However, we need to
know if it expects a prvalue to determine if lvalue-to-rvalue conversion
occurs.

As quoted above in Section 3.10/1, the Note says that "the built-in assignment
operators expect [...] that the right operand is a prvalue". This is not
actually specified in Section 5.17 [expr.ass]. Only the left operand being
an lvalue is specified.

Value categories are also left unspecified for the following operators:
indirection [expr.unary.op], multiplicative operators [expr.mul], additive
operators [expr.add], shift operators [expr.shift], relational operators
[expr.rel], equality operators [expr.eq], bitwise and logical operators
[expr.bit.and] [expr.xor] [expr.or] [expr.log.and] [expr.log.or], conditional
operator [expr.cond], assignment operators [expr.ass], and the comma operator
[expr.comma].

It is generally assumed that when the value category is left unspecified, it
should be a prvalue. However, this can only be inferred through non-normative
references and could easily be better specified. It can further be inferred
through the following suggestive quotes. Firstly, Section 5.19/2 [expr.const]:

A conditional-expression is a core constant expression unless it involves one
of the following as a potentially evaluated subexpression [..]
- an lvalue-to-rvalue conversion (4.1) unless it is applied to
  - a glvalue of integral or enumeration type that refers to a non-volatile
    const object with a preceding initialization, initialized with a
    constant expression
  - [...] 
- [...]

And Section 4/5 [conv] suggests that lvalue-to-rvalue conversion is always
done unless specifically suppressed (by taking an lvalue operand):

[ Note: There are some contexts where certain conversions are suppressed. For
example, the lvalue-to-rvalue conversion is not done on the operand of the
unary & operator. Specific exceptions are given in the descriptions of those
operators and contexts. — end note ]

Further, the Note in Section 3.10/4 [basic.lval] of the C++03 standard
previously stated that "the discussion of each built-in operator in clause 5
indicates whether it expects lvalue operands and whether it yields an lvalue."
This suggests that the lvalue operands are enumerated, but the prvalue
operands are not.

Proposed Solutions:

1.  Modify Section 5/8 [expr] to:

When the expected value category of an operand of a built-in
operator is not stated explicitly, the operator expects that
operand to be a prvalue expression. Whenever a glvalue expression
appears as an operand of an operator that expects a prvalue for
that operand, the lvalue-to-rvalue (4.1), array-to-pointer (4.2),
or function-to-pointer (4.3) standard conversions are applied to
convert the expression to a prvalue.

2.  Explicitly provide the value categories of all operators described in
    the subsections of Section 5 [expr].

FrankHB1989

unread,
Mar 23, 2013, 10:14:09 AM3/23/13
to std-dis...@isocpp.org, sftr...@gmail.com
+1 for first proposed solution.

ai.a...@gmail.com

unread,
Mar 29, 2013, 1:48:45 AM3/29/13
to std-dis...@isocpp.org, sftr...@gmail.com
2013年3月15日金曜日 18時39分44秒 UTC+9 Joseph Mansfield:

Proposed Solutions:

1.  Modify Section 5/8 [expr] to:

When the expected value category of an operand of a built-in
operator is not stated explicitly, the operator expects that
operand to be a prvalue expression. Whenever a glvalue expression
appears as an operand of an operator that expects a prvalue for
that operand, the lvalue-to-rvalue (4.1), array-to-pointer (4.2),
or function-to-pointer (4.3) standard conversions are applied to
convert the expression to a prvalue.

2.  Explicitly provide the value categories of all operators described in
    the subsections of Section 5 [expr].

+1 for the first proposed solution.

However, let me note that similar problems also exist in the case of statements or the source of initializations.

Jens Maurer

unread,
Mar 29, 2013, 7:28:03 AM3/29/13
to std-dis...@isocpp.org

Thanks for the comment.

On 03/15/2013 10:39 AM, Joseph Mansfield wrote:
> As quoted above in Section 3.10/1, the Note says that "the built-in assignment
> operators expect [...] that the right operand is a prvalue". This is not
> actually specified in Section 5.17 [expr.ass]. Only the left operand being
> an lvalue is specified.
>
> Value categories are also left unspecified for the following operators:
> indirection [expr.unary.op], multiplicative operators [expr.mul], additive
> operators [expr.add], shift operators [expr.shift], relational operators
> [expr.rel], equality operators [expr.eq], bitwise and logical operators
> [expr.bit.and] [expr.xor] [expr.or] [expr.log.and] [expr.log.or], conditional
> operator [expr.cond], assignment operators [expr.ass], and the comma operator
> [expr.comma].

For the conditional operator, your assertion appears to be inaccurate;
5.16p4+5+6 says:
"If the second and third operands are glvalues of the same value category and
have the same type, the result is of that type and value category ...

Otherwise, the result is a prvalue. ...

Lvalue-to-rvalue (4.1), array-to-pointer (4.2), and function-to-pointer (4.3)
standard conversions are performed on the second and third operands. ...
"

For the comma operator, your assertion appears to be inaccurate; 5.18p1 says
"... the result is of the same value category as its right operand, ..."

I think we could go a long way if the "usual arithmetic conversions"
would say that they always apply the lvalue-to-rvalue conversion.

> *Proposed Solutions:*
>
> 1. Modify Section 5/8 [expr] to:
>
> When the expected value category of an operand of a built-in
> operator is not stated explicitly, the operator expects that
> operand to be a prvalue expression. Whenever a glvalue expression
> appears as an operand of an operator that expects a prvalue for
> that operand, the lvalue-to-rvalue (4.1), array-to-pointer (4.2),
> or function-to-pointer (4.3) standard conversions are applied to
> convert the expression to a prvalue.

This appears to be dangerous to me; I'd rather fix the few cases
(after extending "usual arithmetic conversions") where this is missing
explicitly.

Thanks,
Jens

sftr...@gmail.com

unread,
Mar 29, 2013, 2:29:07 PM3/29/13
to std-dis...@isocpp.org
On Friday, 29 March 2013 11:28:03 UTC, Jens Maurer wrote:

Thanks for the comment.

On 03/15/2013 10:39 AM, Joseph Mansfield wrote:
> As quoted above in Section 3.10/1, the Note says that "the built-in assignment
> operators expect [...] that the right operand is a prvalue". This is not
> actually specified in Section 5.17 [expr.ass]. Only the left operand being
> an lvalue is specified.
>
> Value categories are also left unspecified for the following operators:
> indirection [expr.unary.op], multiplicative operators [expr.mul], additive
> operators [expr.add], shift operators [expr.shift], relational operators
> [expr.rel], equality operators [expr.eq], bitwise and logical operators
> [expr.bit.and] [expr.xor] [expr.or] [expr.log.and] [expr.log.or], conditional
> operator [expr.cond], assignment operators [expr.ass], and the comma operator
> [expr.comma].

For the conditional operator, your assertion appears to be inaccurate;
5.16p4+5+6 says:
"If the second and third operands are glvalues of the same value category and
have the same type, the result is of that type and value category ...

Otherwise, the result is a prvalue. ...

Lvalue-to-rvalue (4.1), array-to-pointer (4.2), and function-to-pointer (4.3)
standard conversions are performed on the second and third operands. ...
"

For the comma operator, your assertion appears to be inaccurate; 5.18p1 says
"... the result is of the same value category as its right operand, ..."

Thanks, I hadn't noticed those. I have since noticed that some more operands are also left unspecified: unary +, -, !, and ~; and the second operand of the pointer-to-member operators. The corrected list would then be:

Value categories are also left unspecified for the following operators: indirection, unary +, unary -, logical negation operator, unary complement operator [expr.unary.op], pointer-to-member operators [expr.mptr.oper], multiplicative operators [expr.mul], additive operators [expr.add], shift operators [expr.shift], relational operators [expr.rel], equality operators [expr.eq], bitwise and logical operators [expr.bit.and] [expr.xor] [expr.or] [expr.log.and] [expr.log.or], and the assignment operators [expr.ass].
 
I think we could go a long way if the "usual arithmetic conversions"
would say that they always apply the lvalue-to-rvalue conversion.

> *Proposed Solutions:*
>
> 1.  Modify Section 5/8 [expr] to:
>
>     When the expected value category of an operand of a built-in
>     operator is not stated explicitly, the operator expects that
>     operand to be a prvalue expression. Whenever a glvalue expression
>     appears as an operand of an operator that expects a prvalue for
>     that operand, the lvalue-to-rvalue (4.1), array-to-pointer (4.2),
>     or function-to-pointer (4.3) standard conversions are applied to
>     convert the expression to a prvalue.

This appears to be dangerous to me; I'd rather fix the few cases
(after extending "usual arithmetic conversions") where this is missing
explicitly.

Thanks,
Jens

That sounds like an interesting idea. I hadn't thought of it. However, most of the operators that perform the usual arithmetic conversions also take non-arithmetic types, such as pointer types, which would still require lvalue-to-rvalue conversion. We would have to fix those cases anyway and might as well have just written it there for all types in the first place.

Perhaps the best solution is to explicitly state it in all cases where it is needed, regardless of the types of the arguments.

Regards,
Joe
Reply all
Reply to author
Forward
0 new messages