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
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 42 additions & 1 deletion standard/expressions.md
Original file line number Diff line number Diff line change
Expand Up @@ -1122,6 +1122,7 @@ primary_no_array_creation_expression
| checked_expression
| unchecked_expression
| default_value_expression
| nameof_expression
| anonymous_method_expression
;
```
Expand Down Expand Up @@ -2314,7 +2315,46 @@ If the *type* in a *default_value_expression* evaluates at run-time to a referen

A *default_value_expression* is a constant expression ([§12.20](expressions.md#1220-constant-expressions)) if *type* is a reference type or a type parameter that is known to be a reference type ([§9.2](types.md#92-reference-types)). In addition, a *default_value_expression* is a constant expression if the type is one of the following value types: `sbyte`, `byte`, `short`, `ushort`, `int`, `uint`, `long`, `ulong`, `char`, `float`, `double`, `decimal`, `bool,` or any enumeration type.

### 12.7.16 Anonymous method expressions
### §expressions-nameof-expressions Nameof expressions

A *nameof_expression* is used to obtain the name of a program entity as a constant string.

```antlr
nameof_expression
: 'nameof' '(' named_entity ')'
;

named_entity
: simple_name
| named_entity_target '.' identifier type_argument_list?
;

named_entity_target
: 'this'
| 'base'
| named_entity
| predefined_type
jskeet marked this conversation as resolved.
Show resolved Hide resolved
| qualified_alias_member
;
```

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?

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*.


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.


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

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...

- The prefix "`@`", if used, is removed.
- Each *unicode_escape_sequence* is transformed into its corresponding Unicode character.
- Any *formatting_characters* are removed.
jskeet marked this conversation as resolved.
Show resolved Hide resolved

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));


### 12.7.16 Anonymous method expressions

An *anonymous_method_expression* is one of two ways of defining an anonymous function. These are further described in [§12.16](expressions.md#1216-anonymous-function-expressions).

Expand Down Expand Up @@ -5327,6 +5367,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

- The predefined `+`, `–`, `!`, and `~` unary operators.
- The predefined `+`, `–, `*`, `/`, `%`, `<<`, `>>`, `&`, `|`, `^`, `&&`, `||`, `==`, `!=`, `<`, `>`, `<=`, and `>=` binary operators.
- The `?:` conditional operator.
Expand Down
5 changes: 3 additions & 2 deletions standard/lexical-structure.md
Original file line number Diff line number Diff line change
Expand Up @@ -444,8 +444,9 @@ Contextual_Keyword
: 'add' 'alias' 'ascending' 'async' 'await'
| 'by' 'descending' 'dynamic' 'equals' 'from'
| 'get' 'global' 'group' 'into' 'join'
| 'let' 'orderby' 'partial' 'remove' 'select'
| 'set' 'value' 'var' 'where' 'yield'
| 'let' 'nameof' 'orderby' 'partial' 'remove'
| 'select' 'set' 'value' 'var' 'where'
| 'yield'
;
```

Expand Down
2 changes: 1 addition & 1 deletion standard/variables.md
Original file line number Diff line number Diff line change
Expand Up @@ -492,7 +492,7 @@ For all other constant expressions, the definite assignment state of *v* after t

#### 10.4.4.22 General rules for simple expressions

The following rule applies to these kinds of expressions: literals ([§12.7.2](expressions.md#1272-literals)), simple names ([§12.7.3](expressions.md#1273-simple-names)), member access expressions ([§12.7.5](expressions.md#1275-member-access)), non-indexed base access expressions ([§12.7.9](expressions.md#1279-base-access)), `typeof` expressions ([§12.7.12](expressions.md#12712-the-typeof-operator)), and default value expressions ([§12.7.15](expressions.md#12715-default-value-expressions)).
The following rule applies to these kinds of expressions: literals ([§12.7.2](expressions.md#1272-literals)), simple names ([§12.7.3](expressions.md#1273-simple-names)), member access expressions ([§12.7.5](expressions.md#1275-member-access)), non-indexed base access expressions ([§12.7.9](expressions.md#1279-base-access)), `typeof` expressions ([§12.7.12](expressions.md#12712-the-typeof-operator)), default value expressions ([§12.7.15](expressions.md#12715-default-value-expressions)), and `nameof` expressions (§expressions-nameof-expressions).

- The definite assignment state of *v* at the end of such an expression is the same as the definite assignment state of *v* at the beginning of the expression.

Expand Down