Skip to content

Commit

Permalink
Changes for ref-valued properties, indexers and methods
Browse files Browse the repository at this point in the history
(This was originally in dotnet#837.)

The grammar has been extended to be as prescriptive as reasonably possible as befits a language specification[1], this means that former statements such as it being illegal to have a set accessor for a ref-valued property are now covered by the grammar. However the usual verbose description of the grammar remains!

The term ref-valued rather than ref-returning is used, a term closer to the language of fields. To distingush the two kinds of properties, which do follow different rules, the corresponding and somewhat ugly term non-ref-valued is used (non-ref-returning would be just as ugly).

[1] The grammar used by a compiler may be less restrictive to support more descriptive error messages, that is of course an implementation issue!

Fixes dotnet#825.
  • Loading branch information
Nigel-Ecma authored and jskeet committed Aug 17, 2023
1 parent b50bce6 commit a8a5e50
Show file tree
Hide file tree
Showing 7 changed files with 233 additions and 89 deletions.
2 changes: 1 addition & 1 deletion standard/basic-concepts.md
Original file line number Diff line number Diff line change
Expand Up @@ -588,7 +588,7 @@ The ***scope*** of a name is the region of program text within which it is possi
- The scope of a member declared by a *struct_member_declaration* ([§16.3](structs.md#163-struct-members)) is the *struct_body* in which the declaration occurs.
- The scope of a member declared by an *enum_member_declaration* ([§19.4](enums.md#194-enum-members)) is the *enum_body* in which the declaration occurs.
- The scope of a parameter declared in a *method_declaration* ([§15.6](classes.md#156-methods)) is the *method_body* of that *method_declaration*.
- The scope of a parameter declared in a *method_declaration* ([§15.6](classes.md#156-methods)) is the *method_body* or *ref_method_body* of that *method_declaration*.
- The scope of a parameter declared in an *indexer_declaration* ([§15.9](classes.md#159-indexers)) is the *indexer_body* of that *indexer_declaration*.
- The scope of a parameter declared in an *operator_declaration* ([§15.10](classes.md#1510-operators)) is the *operator_body* of that *operator_declaration*.
- The scope of a parameter declared in a *constructor_declaration* ([§15.11](classes.md#1511-instance-constructors)) is the *constructor_initializer* and *block* of that *constructor_declaration*.
Expand Down
232 changes: 175 additions & 57 deletions standard/classes.md

Large diffs are not rendered by default.

27 changes: 18 additions & 9 deletions standard/delegates.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,26 +12,33 @@ A *delegate_declaration* is a *type_declaration* ([§14.7](namespaces.md#147-typ

```ANTLR
delegate_declaration
: attributes? delegate_modifier* 'delegate' ('ref' 'readonly'?)? return_type identifier variant_type_parameter_list?
'(' formal_parameter_list? ')' type_parameter_constraints_clause* ';'
: attributes? delegate_modifier* 'delegate' return_type delegate_header
| attributes? ref_delegate_modifier* 'delegate' ref_kind ref_return_type delegate_header
;
delegate_header
: identifier '(' formal_parameter_list? ')' ';'
| identifier variant_type_parameter_list '(' formal_parameter_list? ')' type_parameter_constraints_clause* ';'
;
delegate_modifier
: ref_delegate_modifier
| unsafe_modifier // unsafe code support
;
ref_delegate_modifier
: 'new'
| 'public'
| 'protected'
| 'internal'
| 'private'
| unsafe_modifier // unsafe code support
;
```

*unsafe_modifier* is defined in [§23.2](unsafe-code.md#232-unsafe-contexts).

It is a compile-time error for the same modifier to appear multiple times in a delegate declaration.

A delegate declaration shall not supply any *type_parameter_constraints_clause*s unless it also supplies a *variant_type_parameter_list*.

A delegate declaration that supplies a *variant_type_parameter_list* is a generic delegate declaration. Additionally, any delegate nested inside a generic class declaration or a generic struct declaration is itself a generic delegate declaration, since type arguments for the containing type shall be supplied to create a constructed type ([§8.4](types.md#84-constructed-types)).

The `new` modifier is only permitted on delegates declared within another type, in which case it specifies that such a delegate hides an inherited member by the same name, as described in [§15.3.5](classes.md#1535-the-new-modifier).
Expand All @@ -40,11 +47,13 @@ The `public`, `protected`, `internal`, and `private` modifiers control the acces

The delegate’s type name is *identifier*.

As with methods ([§15.6.1](classes.md#1561-general)), if `ref` is present, the delegate returns-by-ref; otherwise, if *return_type* is `void`, the delegate returns-no-value; otherwise, the delegate returns-by-value. It is a compile-time error to have both `ref` and a *return_type* of `void`.
As with methods ([§15.6.1](classes.md#1561-general)), if `ref` is present, the delegate returns-by-ref; otherwise, if *return_type* is `void`, the delegate returns-no-value; otherwise, the delegate returns-by-value.

The optional *formal_parameter_list* specifies the parameters of the delegate.

*return_type* indicates the return type of the delegate. The optional `ref` indicates that a *variable reference* ([§9.5](variables.md#95-variable-references)) is returned, with the optional `readonly` indicating that that variable is read-only.
The *return_type* of a returns-by-value or returns-no-value delegate declaration specifies the type of the result, if any, returned by the delegate.

The *ref_return_type* of a returns-by-ref delegate declaration specifies the type of the variable referenced by the *variable_reference* ([§9.5](variables.md#95-variable-references)) returned by the delegate.

The optional *variant_type_parameter_list* ([§18.2.3](interfaces.md#1823-variant-type-parameter-lists)) specifies the type parameters to the delegate itself.

Expand Down Expand Up @@ -76,7 +85,7 @@ The only way to declare a delegate type is via a *delegate_declaration*. Every d
## 20.3 Delegate members
Every delegate type inherits members from the `Delegate` class as described in [§15.3.4](classes.md#1534-inheritance). In addition, every delegate type must provide a non-generic `Invoke` method whose parameter list matches the *formal_parameter_list* in the delegate declaration, and whose return type matches the *return_type* in the delegate declaration. The `Invoke` method shall be at least as accessible as the containing delegate type. Calling the `Invoke` method on a delegate type is semantically equivalent to using the delegate invocation syntax ([§20.6](delegates.md#206-delegate-invocation)) .
Every delegate type inherits members from the `Delegate` class as described in [§15.3.4](classes.md#1534-inheritance). In addition, every delegate type must provide a non-generic `Invoke` method whose parameter list matches the *formal_parameter_list* in the delegate declaration, whose return type matches the *return_type* or *ref_return_type* in the delegate declaration, and for returns-by-ref delegates whose *ref_kind* matches that in the delegate declaration. The `Invoke` method shall be at least as accessible as the containing delegate type. Calling the `Invoke` method on a delegate type is semantically equivalent to using the delegate invocation syntax ([§20.6](delegates.md#206-delegate-invocation)) .
Implementations may define additional members in the delegate type.
Expand All @@ -92,7 +101,7 @@ A method or delegate type `M` is ***compatible*** with a delegate type `D` if al
- One of the following is true:
- `D` and `M` are both *returns-no-value*
- `D` and `M` are returns-by-value ([§15.6.1](classes.md#1561-general), [§20.2](delegates.md#202-delegate-declarations)), and an identity or implicit reference conversion exists from the return type of `M` to the return type of `D`.
- `D` and `M` are both returns-by-ref, and an identity conversion exists between the return type of `M` and the return type of `D`, and both have a *return_type* preceded by `ref readonly`, or both have a *return_type* preceded by `ref` only.
- `D` and `M` are both returns-by-ref, an identity conversion exists between the return type of `M` and the return type of `D`, and both have the same *ref_kind*.
This definition of compatibility allows covariance in return type and contravariance in parameter types.
Expand Down
4 changes: 2 additions & 2 deletions standard/expressions.md
Original file line number Diff line number Diff line change
Expand Up @@ -1539,7 +1539,7 @@ simple_name
A *simple_name* is either of the form `I` or of the form `I<A₁, ..., Aₑ>`, where `I` is a single identifier and `I<A₁, ..., Aₑ>` is an optional *type_argument_list*. When no *type_argument_list* is specified, consider `e` to be zero. The *simple_name* is evaluated and classified as follows:

- If `e` is zero and the *simple_name* appears within a local variable declaration space ([§7.3](basic-concepts.md#73-declarations)) that directly contains a local variable, parameter or constant with name `I`, then the *simple_name* refers to that local variable, parameter or constant and is classified as a variable or value.
- If `e` is zero and the *simple_name* appears within a generic method declaration but outside the *attributes* of its *method_header,* and if that declaration includes a type parameter with name `I`, then the *simple_name* refers to that type parameter.
- If `e` is zero and the *simple_name* appears within a generic method declaration but outside the *attributes* of its *method_declaration*, and if that declaration includes a type parameter with name `I`, then the *simple_name* refers to that type parameter.
- Otherwise, for each instance type `T` ([§15.3.2](classes.md#1532-the-instance-type)), starting with the instance type of the immediately enclosing type declaration and continuing with the instance type of each enclosing class or struct declaration (if any):
- If `e` is zero and the declaration of `T` includes a type parameter with name `I`, then the *simple_name* refers to that type parameter.
- Otherwise, if a member lookup ([§12.5](expressions.md#125-member-lookup)) of `I` in `T` with `e` type arguments produces a match:
Expand Down Expand Up @@ -4749,7 +4749,7 @@ The first operand of the `?:` operator shall be an expression that can be impli
If `ref` is present:

- An identity conversion must exist between the types of the two *variable_reference*s, and type of the result can be either type. If either type is `dynamic`, type inference prefers `dynamic` ([§8.7](types.md#87-the-dynamic-type)). If either type is a tuple type ([§8.3.11](types.md#8311-tuple-types)), type inference includes the element names when the element names in the same ordinal position match in both tuples.
- The result is a variable reference, which is writeable if both *variable_reference*s are writeable.
- The result is a variable reference, which is writeable if both *variable_reference*s are writeable.

> *Note*: When `ref` is present, the *conditional_expression* returns a variable reference, which can be assigned to a reference variable using the `= ref` operator or passed as a reference/input/output parameter. *end note*
Expand Down
32 changes: 21 additions & 11 deletions standard/interfaces.md
Original file line number Diff line number Diff line change
Expand Up @@ -254,16 +254,21 @@ Interface methods are declared using *interface_method_declaration*s:

```ANTLR
interface_method_declaration
: attributes? 'new'? return_type identifier type_parameter_list?
'(' formal_parameter_list? ')' type_parameter_constraints_clause* ';'
: attributes? 'new'? return_type interface_method_header
| attributes? 'new'? ref_kind ref_return_type interface_method_header
;
interface_method_header
: identifier '(' formal_parameter_list? ')' ';'
| identifier type_parameter_list '(' formal_parameter_list? ')' type_parameter_constraints_clause* ';'
;
```

The *attributes*, *return_type*, *identifier*, and *formal_parameter_list* of an interface method declaration have the same meaning as those of a method declaration in a class ([§15.6](classes.md#156-methods)). An interface method declaration is not permitted to specify a method body, and the declaration therefore always ends with a semicolon. An *interface_method_declaration* shall not have *type_parameter_constraints_clause*s unless it also has a *type_parameter_list*.
The *attributes*, *return_type*, *ref_return_type*, *identifier*, and *formal_parameter_list* of an interface method declaration have the same meaning as those of a method declaration in a class ([§15.6](classes.md#156-methods)). An interface method declaration is not permitted to specify a method body, and the declaration therefore always ends with a semicolon.

All formal parameter types of an interface method shall be input-safe ([§18.2.3.2](interfaces.md#18232-variance-safety)), and the return type shall be either `void` or output-safe. In addition, any output or reference formal parameter types shall also be output-safe.

> *Note*: Output parameters are required to be input-safe due to common implementation restrictions. *end note*
> *Note*: Output parameters are required to be input-safe due to common implementation restrictions. *end note*
Furthermore, each class type constraint, interface type constraint and type parameter constraint on any type parameters of the method shall be input-safe.

Expand Down Expand Up @@ -313,21 +318,24 @@ Interface properties are declared using *interface_property_declaration*s:
```ANTLR
interface_property_declaration
: attributes? 'new'? type identifier '{' interface_accessors '}'
| attributes? 'new'? ref_kind type identifier '{' ref_interface_accessor '}'
;
```
```ANTLR
interface_accessors
: attributes? 'get' ';'
| attributes? 'set' ';'
| attributes? 'get' ';' attributes? 'set' ';'
| attributes? 'set' ';' attributes? 'get' ';'
;
ref_interface_accessor
: attributes? 'get' ';'
;
```
The *attributes*, *type*, and *identifier* of an interface property declaration have the same meaning as those of a property declaration in a class ([§15.7](classes.md#157-properties)).

The accessors of an interface property declaration correspond to the accessors of a class property declaration ([§15.7.3](classes.md#1573-accessors)), except that the accessor body shall always be a semicolon. Thus, the accessors simply indicate whether the property is read-write, read-only, or write-only.
The accessors of an interface property declaration correspond to the accessors of a class property declaration ([§15.7.3](classes.md#1573-accessors)), except that the *accessor_body* shall always be a semicolon. Thus, the accessors simply indicate whether the property is read-write, read-only, or write-only.

The type of an interface property shall be output-safe if there is a get accessor, and shall be input-safe if there is a set accessor.

Expand All @@ -350,15 +358,17 @@ The type of an interface event shall be input-safe.
Interface indexers are declared using *interface_indexer_declaration*s:

```ANTLR
interface_indexer_declaration:
attributes? 'new'? type 'this' '[' formal_parameter_list ']'
'{' interface_accessors '}'
interface_indexer_declaration
: attributes? 'new'? type 'this' '[' formal_parameter_list ']'
'{' interface_accessors '}'
| attributes? 'new'? ref_kind type 'this' '[' formal_parameter_list ']'
'{' ref_interface_accessor '}'
;
```

The *attributes*, *type*, and *formal_parameter_list* of an interface indexer declaration have the same meaning as those of an indexer declaration in a class ([§15.9](classes.md#159-indexers)).

The accessors of an interface indexer declaration correspond to the accessors of a class indexer declaration ([§15.9](classes.md#159-indexers)), except that the accessor body shall always be a semicolon. Thus, the accessors simply indicate whether the indexer is read-write, read-only, or write-only.
The accessors of an interface indexer declaration correspond to the accessors of a class indexer declaration ([§15.9](classes.md#159-indexers)), except that the *accessor_body* shall always be a semicolon. Thus, the accessors simply indicate whether the indexer is read-write, read-only, or write-only.

All the formal parameter types of an interface indexer shall be input-safe ([§18.2.3.2](interfaces.md#18232-variance-safety)). In addition, any output or reference formal parameter types shall also be output-safe.

Expand Down
2 changes: 1 addition & 1 deletion standard/lexical-structure.md
Original file line number Diff line number Diff line change
Expand Up @@ -1297,7 +1297,7 @@ Conditional compilation directives shall be written in groups consisting of, in

At most one of the contained conditional sections is selected for normal lexical processing:

- The *PP_Expression*s of the `#if` and `#elif` directives are evaluated in order until one yields `true`. If an expression yields `true`, the conditional section following the corresponding directive is selected.
- The *PP_Expression*s of the `#if` and `#elif` directives are evaluated in order until one yields `true`. If an expression yields `true`, the conditional section following the corresponding directive is selected.
- If all *PP_Expression*s yield `false`, and if a `#else` directive is present, the conditional section following the `#else` directive is selected.
- Otherwise, no conditional section is selected.

Expand Down
Loading

0 comments on commit a8a5e50

Please sign in to comment.