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.type]/1 Change "designates" to "result of" #2876

Open
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

sdkrystian
Copy link
Contributor

When the change to the result of the expression is done as specified in [expr.type] p1, the wording does not accurately reflect what happens. The entity the expression refers to does not change, but the result of the expression is changed to be the object or function that the reference refers to does. The wording should reflect that, hence this proposed change.

@jensmaurer
Copy link
Member

Hm... I'm not convinced. Maybe it helps already to switch around the sentence and first say that we turn the expression into an lvalue or xvalue, and then either keep the "designates" or use "denotes" [basic.lval] p5.

@sdkrystian
Copy link
Contributor Author

@jensmaurer Here, let me provide an example:

const std::string& s = "";
decltype(s) y;

Here, y will be of type const std::string&, since this follows the wording of http://eel.is/c++draft/dcl.type.decltype#1.3, which states that the type will be the type of the entity that the expression names. If what the expressions names/denotes/refers to was changed, this would no longer produce the correct type, as it would result in std::string. Hopefully you see my reasoning behind this proposed wording change.

On top of this, not changing what the expression results in would make no sense. A glvalue is said to evaluate to (result in) an object, bit-field, or function. If this wording did not apply to the result of the expression, the result would be a reference, which is clearly not intended.

@jensmaurer
Copy link
Member

jensmaurer commented May 9, 2019

We're riding the edge between expression per the grammar and interpreting that expression as a semantic construct. I think it's clear in [dcl.type.decltype] p1 that we're not applying [dcl.type] p1 to the expression if one of the special cases applies; instead, we're stopping right after name lookup found an entity for the id-expression.

Back to [dcl.type] p1. This is a step in interpreting expression (grammar) as the semantic construct "expression". We're not super-clear on the order here, but what happens is that we (recursively) interpret the expression by the other rules in [expr], and then, at the end, if we get a "reference to T", we perform the adjustment discussed here, and keep interpreting any enclosing expressions.

My specific suggestion here would be: [needs more massaging to reflect that rvalue references to functions yield lvalues, but if we say this, we can replace half of [conv] p6 with a note and a cross-reference]:

Prior to any further analysis, if an expression initially has the type “lvalue reference to T” or "rvalue reference to T" (9.2.3.2, 9.3.3), the type is adjusted to T and the expression is, respectively, an lvalue or xvalue denoting the object or function to which the reference was bound. [Note: Before the lifetime of the reference has started or after it has ended, the behavior is undefined (see 6.6.3). — end note]

I use "denote" here so that we tie into the "result of" definition in [basic.lval] p5.

@sdkrystian
Copy link
Contributor Author

sdkrystian commented May 9, 2019

@jensmaurer It would be more appropriate to use the proper grammar term "result", to describe the semantic construct "expression", since the result of the expression and the entity it denotes can differ. Additionally, [dcl.type] is not clear when it alludes to an entity denoted e, an expression, and does not make a clear distinction between whether if refers to the semantic expression or the grammatical expression. Again, as stated in the the prior comment, interpreting this wording literally will not make sense without this change.

@jensmaurer
Copy link
Member

I see that we're using "the result is" elsewhere, but we're also using "designates", e.g. in [expr.ref] p6.1. Personally, I'd consider a change that talks about the result being a glvalue first and then saying what it refers to more helpful for comprehension than the change from "designates" to "the result is". We'll keep this in the queue so that @zygoloid can decide, eventually.

@sdkrystian
Copy link
Contributor Author

@jensmaurer After some further examination, I've decided that this is not the best way to go. Changing it to "result of" could result in some incongruency between what an expression denotes and what it's result it. In the above example s is an expression, but also an identifier, and while the identifier denotes the reference, the expression should denote the object the reference refers to. I believe your proposed change to change it to "denotes" and to rephrase the sentence would be best, and to possibly apply that to other wording, as currently we use "denotes", "refers to" and "designates" to say the same thing.

@zygoloid zygoloid added the changes requested Changes to the wording or approach have been requested and not yet applied. label Jun 12, 2019
@languagelawyer
Copy link
Contributor

Here are some of my thoughts after reading discussion here and a piece of the standard.
I think they mostly agree with what has been said about expressions having 2 faces: the grammar and the semantic one.
I've tried to further clarify this and the role of [expr.type]/1 (for myself, ofc)

First of all, names denote. [basic.lookup]/1 says:

The name lookup rules apply uniformly to all names (including typedef-names, namespace-names ([basic.namespace]), and class-names ([class.name])) wherever the grammar allows such names in the context discussed by a particular rule.
Name lookup associates the use of a name with a set of declarations ([basic.def]) of that name.
The declarations found by name lookup shall either all denote the same entity or shall all denote functions or function templates; in the latter case, the declarations are said to form a set of overloaded functions ([over.load]).
Overload resolution takes place after name lookup has succeeded.
The access rules are considered only once name lookup and function overload resolution (if applicable) have succeeded.
Only after name lookup, function overload resolution (if applicable) and access checking have succeeded are the semantic properties introduced by the name's declaration and its reachable ([module.reach]) redeclarations used further in expression processing ([expr]).

But they don't denote in the sense how glvalues denote objects or functions.
Also, I suspect when the wording above speaks about entities, it ignores that the definition of entity includes value, object or reference.

expr.prim.id speaks about id-expression similarly: when it says "denotes", it doesn't mean "denotes" in the glvalue sence.

So, this is the first "face" of id-expression -- what it denotes after name lookup.
This may be a variable, type, class member etc. or a set of overload functions and function templates.

This is kinda unfortunate, because id-expression is not always an expression.
It does appear in contexts where an expression is not even expected!
For example, it is part of declarator-id grammar:

declarator-id:
	...[opt] id-expression

(i.e. i in int i; is id-expression)
or template type-parameter default argument grammar:

type-parameter:
	...
	template-head type-parameter-key identifier[opt] = id-expression

Maybe, id-expression should be renamed to id or name?

Now, to id-expression as "semantic" expression. The main difficulty is that expressions are sometimes evaluated and sometimes not and [basic.lval] is well aware of this.
Whilst I can say an obvious thing that there shall be some clear mechanism transferring an id-expression from the name lookup world into the "semantic" expression world, I don't believe [expr.type]/1 is the right way to do it. I don't know how is it possible to say that an expression denotes object (or function) when it is not evaluated.
Because of this difficulty, I won't continue speaking about "semantic" expressions. Sorry.

Let me add a couple more words about [expr.type]/1.
In short, I think this rule shall not exist.

Just look what it says «The expression designates the object or function denoted by the reference». As if it were possible to denote a reference!

Well, technically, "reference" is among things which are called "entities". There is an opinion that "value" and "object" should not be "entities". I believe "reference" also should not be "entity".

One can denote a variable of reference type, or just reference type. I don't know what does it mean to denote a reference. I believe it is impossible to "create" a reference the same way it is possible to create an object. Any wording that speaks about reference creation is IMO defective.
In C++14 and before, [stmt.return] was saying «A return statement with a braced-init-list initializes the object or reference to be returned from the function». I'm very glad that this offensive wording is not present in C++17 [stmt.return].

@languagelawyer
Copy link
Contributor

I think it would be great to syntactically separate cases when id-expression is a name (I mean when it denotes declaration(s)) and when it is an expression (I mean "semantic" expression, with value category etc.). And specify how a name becomes an expression (name denoting a local entity -> expression denoting the object/function; name denoting a set of functions and/or function templates ->[overload resolution]-> lvalue denoting the function; etc.)

The changes in the grammar would be:
Currently:

id-expression:
	unqualified-id
	qualified-id

declarator-id:
	...[opt] id-expression

Proposed:

id:
	unqualified-id
	qualified-id

id-expression:
	id

declarator-id:
	...[opt] id

Rationale:
id-expression in declarator-id is not an expression.

Currently:

postfix-expression:
	postfix-expression . template[opt] id-expression
	postfix-expression -> template[opt] id-expression

Proposed:

postfix-expression:
	postfix-expression . template[opt] id
	postfix-expression -> template[opt] id

unary-expression:
	& qualified-id

Rationale:
id-expression after -> or . is never an expression, but just a name. Only the whole E1.E2 is expression.
& qualified-id is added to unary-expression because qualified-id denoting non-static class member is never an expression, but always just a name.

Currently:

type-parameter:
	template-head type-parameter-key identifier[opt] = id-expression

template-argument:
	constant-expression
	type-id
	id-expression

Proposed:

type-parameter:
	template-head type-parameter-key identifier[opt] = id

template-argument:
	constant-expression
	type-id
	id

Rationale:
This is obvious.

The definition of decltype(e) would say that if e is id-expression or class member access expression, the type denoted by this decltype(e) is the type of the identifier denoted by id.

Thoughts?

@sdkrystian
Copy link
Contributor Author

I would argue that the id-expression on the right side of a class member access is an expression. Anyways, the syntax tree is only significant in establishing syntax, so it would really be much different other than having two names.

@languagelawyer
Copy link
Contributor

I would argue that the id-expression on the right side of a class member access is an expression.

[expr.ref] doesn't read to me so.
What would be the value category of id-expression denoting non-static data member C::m? prvalue designating C::m, like &C::m produces?

@sdkrystian
Copy link
Contributor Author

It would be a prvalue, as specified by [expr.prim.id]

@languagelawyer
Copy link
Contributor

languagelawyer commented Jan 17, 2020

It would be a prvalue, as specified by [expr.prim.id]

Hmmm....
[expr.prim.id.unqual]/2 says «The expression is an lvalue if the entity is a function, variable, structured binding ([dcl.struct.bind]), data member, or template parameter object and a prvalue otherwise»
[expr.prim.id.qual]/2 says «The result is an lvalue if the member is a static member function or a data member and a prvalue otherwise.»
So, data members are always denoted by lvalue, even non-static ones...
(UPD: and indeed, decltype((C::m)), with C::m denoting non-static data member, produces an lvalue reference type, so C::m is indeed lvalue here.)
What do these lvalues denote, without a class instance?
(UPD: Ah, I think, [expr.prim.id], when it speaks about id-expressions denoting non-static data members being lvalues, means only cases when such an id-expression appears in an unevaluated operand. And this wording doesn't apply to class member access expression case, where id-expression after -> or . is just a name.)

Anyways, the syntax tree is only significant in establishing syntax

Well, look at the template-argument grammar:

template-argument:
	constant-expression
	type-id
	id-expression

It has id-expression separately, even though id-expression is constant-expression. I think, here some semantic information is encoded in this syntax, namely, that id-expression alone is meant to represent types or templates.

so it would really be much different other than having two names.

I think I don't really get what do you want to say here.

@sdkrystian
Copy link
Contributor Author

Ah, whoops. I didn't check [expr.prim.id.qual], my bad. Either way, its an expression.

@jensmaurer
Copy link
Member

I am sympathetic to the id / id-expression idea, where "id" is used when we have a name that is not in an expression context, similar to conditional-expression / constant-expression. Do we have a grammar-term that is slightly longer than "id"?

However, introducing id / id-expression seems a bit large of a change for an editorial issue, for instance because it involves introducing new grammar non-terminals. Can we maybe proceed with the rather narrow change that we discussed earlier?

@languagelawyer
Copy link
Contributor

I am sympathetic to the id / id-expression idea, where "id" is used when we have a name that is not in an expression context

What do you think about s/id-expression/id/ after ->/. in class member access expression?

Do we have a grammar-term that is slightly longer than "id"?

id is, lets say, a placeholder. identifier is busy and I didn't want to spend much time inventing other grammar-term.

However, introducing id / id-expression seems a bit large of a change for an editorial issue, for instance because it involves introducing new grammar non-terminals

I'm not proposing this as an editorial fix. I'm more like thinking out loud how a paper fixing the mess could look like. And the fix of grammar is only a tiny part of this imaginary paper.

@sdkrystian
Copy link
Contributor Author

name would work, but it would introduce a disparity between the defined term and the grammatical production (since a name is not qualified).

@jensmaurer
Copy link
Member

I agree that declarator-id as well as the E2 in class member access are candidates for id instead of id-expression. We don't have to talk about what E2 means alone as long as we know what E1.E2 means. (Actually, you can't even do name lookup for E2 without looking at E1 first.)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
changes requested Changes to the wording or approach have been requested and not yet applied. needs rebase The pull request needs a git rebase to resolve merge conflicts.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

5 participants