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

C# 6.0 feature: nameof expressions #10

Closed
wants to merge 1 commit into from

Conversation

BillWagner
Copy link
Member

Work done to date on nameof expressions.

Creating draft PR because of outstanding TODO item.

@BillWagner BillWagner force-pushed the feature-nameof-expressions branch 2 times, most recently from fa71860 to 5a0f68d Compare October 26, 2020 21:17
@BillWagner BillWagner mentioned this pull request Nov 30, 2020
@jskeet jskeet added the meeting: discuss This issue should be discussed at the next TC49-TG2 meeting label Feb 5, 2021
;
```

Grammatically speaking, the *named_entity* operand is always an expression. Because `nameof` is not a keyword, a *nameof_expression* is always syntactically ambiguous with an invocation of the simple name `nameof`. For compatibility reasons, if a name lookup ([§12.7.3](expressions.md#simple-names)) of the name `nameof` succeeds, the expression is treated as an *invocation_expression* -- regardless of whether the invocation is valid. Otherwise it is a *nameof_expression*.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This does not read correctly to me. By "Grammatically" is "Semantically" or "Syntactically" meant? From a syntactic view a name_entity is always a name_entity. Now the language the grammar represents may be a subset of the language of expressions but that does not make it an expression either syntactically or semantically. I think what we are trying to say is that a name lookup succeeds then the named_entity is treated semantically as an invocation_expression.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Continuing thought... This para is a backward compatibility statement, should there be some typographical indication that this is what it is? And (having not looked myself so there might be...) should there be a corresponding compatibility note where function declarations are describe a note stating that nameof should be avoided by is (currently?) allowed for backward compatibility?


It is a compile-time error for a *named_entity* designating a method group to have a *type_argument_list*. It is a compile time error for a *named_entity_target* to have the type `dynamic`.

A *nameof_expression* is a constant expression of type `string`, and has no effect at runtime. Specifically, its *named_entity* is not evaluated, and is ignored for the purposes of definite assignment analysis ([§10.4.4.22](variables.md#104422-general-rules-for-simple-expressions)). Its value is the last identifier of the *named_entity* before the optional final *type_argument_list*, transformed in the following way:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this not why a named_entity is not an expression but a named_entity? I.e. something like (typed on the fly, not wordsmithed!) "its named_entity is not an expression (despite its syntactic similarity?) and therefore is not part of definite assignment analysis"

Copy link
Contributor

@jskeet jskeet left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we probably need to talk about Nigel's comments - mine are somewhat more simplistic, I think :)

standard/expressions.md Show resolved Hide resolved
standard/expressions.md Show resolved Hide resolved

The meaning of the *named_entity* of a *nameof_expression* is the meaning of it as an expression; that is, either as a *simple_name*, a *base_access* or a *member_access*. However, where the lookup described in [§12.7.3](expressions.md#1273-simple-names) and [§12.7.5](expressions.md#1275-member-access) results in an error because an instance member was found in a static context, a *nameof_expression* produces no such error.

It is a compile-time error for a *named_entity* designating a method group to have a *type_argument_list*. It is a compile time error for a *named_entity_target* to have the type `dynamic`.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it would be worth highlighting somewhere that if the name in the source code nameof expression is an alias for the entity, it's that source expression that's relevant, not the resolved entity. For example:

using MyAlias = System.Guid;
...
Console.WriteLine(nameof(MyAlias)); // Prints MyAlias, not Guid

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"Its value is the last identifier of the named_entity" probably covers it...

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TODO: Add a note to explain this after that paragraph.

@@ -5381,6 +5421,7 @@ Only the following constructs are permitted in constant expressions:
- Parenthesized subexpressions, which are themselves constant expressions.
- Cast expressions.
- `checked` and `unchecked` expressions.
- Nameof expressions
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
- Nameof expressions
- `nameof` expressions


These are the same transformations applied in [§7.4.3](lexical-structure.md#743-identifiers) when testing equality between identifiers.

> TODO: examples
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suspect that a single example referring to a lot of different kinds of entity would be best here.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A number of entities that should be used here:

  • a local variable
  • a simple type name
  • a globally qualified type name (e.g. System.Int)
  • A closed generic name (e.g. List<int>).
  • An open generic type, where it is allowed:
    public static void Thing<T>(T arg)
    {
      Console.WriteLine(nameof(List<T>)); // writes "List"
    }
    public static void DoesntCompile()
    {
      Console.WriteLine(nameof(List<T>)); // `T` is not defined
    }
  • The System namespace
  • An additional namespace, such as System.Collections.Generic
  • Tuple member:
     var point = (x: 3, y: 4);
     Console.WriteLine(nameof(point.x));

;
```

Grammatically speaking, the *named_entity* operand is always an expression. Because `nameof` is not a keyword, a *nameof_expression* is always syntactically ambiguous with an invocation of the simple name `nameof`. For compatibility reasons, if a name lookup ([§12.7.3](expressions.md#simple-names)) of the name `nameof` succeeds, the expression is treated as an *invocation_expression* -- regardless of whether the invocation is valid. Otherwise it is a *nameof_expression*.
Copy link
Contributor

@jskeet jskeet Feb 10, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Grammatically speaking, the *named_entity* operand is always an expression. Because `nameof` is not a keyword, a *nameof_expression* is always syntactically ambiguous with an invocation of the simple name `nameof`. For compatibility reasons, if a name lookup ([§12.7.3](expressions.md#simple-names)) of the name `nameof` succeeds, the expression is treated as an *invocation_expression* -- regardless of whether the invocation is valid. Otherwise it is a *nameof_expression*.
Because `nameof` is not a keyword, a *nameof_expression* is always syntactically ambiguous with an invocation of the simple name `nameof`. For compatibility reasons, if a name lookup ([§12.7.3](expressions.md#simple-names)) of the name `nameof` succeeds, the *nameof_expression* is treated as an *invocation_expression* -- regardless of whether the invocation is valid. Otherwise it is a *nameof_expression*.

It is a compile-time error for a *named_entity* designating a method group to have a *type_argument_list*. It is a compile time error for a *named_entity_target* to have the type `dynamic`.

A *nameof_expression* is a constant expression of type `string`, and has no effect at runtime. Specifically, its *named_entity* is not evaluated, and is ignored for the purposes of definite assignment analysis ([§10.4.4.22](variables.md#104422-general-rules-for-simple-expressions)). Its value is the last identifier of the *named_entity* before the optional final *type_argument_list*, transformed in the following way:

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need to work out whether this should compile:

Console.WriteLine(nameof(x));
int x = 10;

(It doesn't currently.)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If this is deemed a lookup error, it's already covered - Jon to check that.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've currently only found a reference in https://github.com/ECMA-TC49-TG2/csharpstandard/blob/draft-v6/standard/variables.md, which is within a note:

Within the scope of a local variable, it is a compile-time error to refer to that local variable in a textual position that precedes its local_variable_declarator

I'm assuming there must be something normative somewhere... but I can't see anything in 12.7.3. (It's a shame if someone has to read the whole spec to know that the result of a lookup in 12.7.3 is invalid.) Let's discuss whether this needs a new issue...

@jskeet jskeet self-assigned this Feb 10, 2021
@jskeet
Copy link
Contributor

jskeet commented Feb 10, 2021

Jon to spend some time polishing (hopefully).

@jskeet
Copy link
Contributor

jskeet commented Mar 5, 2021

I think we can remove the aspects of the "meaning" of the named_entity. New proposal:

Because nameof is not a keyword, a nameof_expression is always syntactically ambiguous with an invocation of the simple name nameof. For compatibility reasons, if a name lookup (§12.7.3) of the name nameof
succeeds, the expression is treated as an invocation_expression -- regardless of whether the invocation is valid. Otherwise it is a nameof_expression.

Simple name and member access lookups are performed on the named_entity at compile time, following the rules described in
§12.7.3 and §12.7.5. However, where the lookup described in §12.7.3 and §12.7.5 results in an error because an instance member was found in a static context, a nameof_expression produces no such error.

(Do we need to say that any other lookup error results in a compile-time error for the nameof_expression?)

I'm still not sure about the "access to a local variable before it's declared" - let's discuss that in the meeting. (If it's really only mentioned in a note, we should file an issue about that and make sure it's included in lookup rules.)

@jskeet
Copy link
Contributor

jskeet commented Mar 10, 2021

Just remembered that as well as the section I've quoted/proposed, I need to fully spec examples, and add a note around the MyAlias example mentioned earlier.

@RexJaeschke RexJaeschke added this to the C# 6 milestone Mar 17, 2021
Work done to date on nameof expressions
@BillWagner BillWagner force-pushed the feature-nameof-expressions branch from 5a0f68d to 2ed4b4f Compare March 17, 2021 14:40
@jskeet
Copy link
Contributor

jskeet commented Apr 1, 2021

Local variable part: it's mentioned in https://github.com/dotnet/csharpstandard/blob/draft-v6/standard/statements.md#1362-local-variable-declarations:

The scope of a local variable declared in a local_variable_declaration is the block in which the declaration occurs. It is an error to refer to a local variable in a textual position that precedes the local_variable_declarator of the local variable. Within the scope of a local variable, it is a compile-time error to declare another local variable or constant with the same name.

That section of the standard is quite a long way from name lookup, so I'm not sure it should really apply within nameof, but at least we don't have the bigger problem I thought we might have.

jskeet added a commit to jskeet/csharpstandard that referenced this pull request Apr 1, 2021
Builds on dotnet#10

- Reworded to avoid the "meaning" confusion
- Added examples
@jskeet jskeet removed the meeting: discuss This issue should be discussed at the next TC49-TG2 meeting label Apr 1, 2021
@BillWagner
Copy link
Member Author

closing in favor of #250 (which builds on this branch.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants