[Version 11.0] Feature support for ref fields and scoped#1614
[Version 11.0] Feature support for ref fields and scoped#1614RexJaeschke wants to merge 25 commits intodraft-v11from
Conversation
BillWagner
left a comment
There was a problem hiding this comment.
Keeping notes on my first read. Some concerns and questions to answer as I read through everything again.
standard/attributes.md
Outdated
|
|
||
| ### §UnscopedRefAttribute The UnscopedRef attribute | ||
|
|
||
| There are several cases in which a ref is treated as being implicitly scoped; that is, the ref is not allowed to escape a method. For example: |
There was a problem hiding this comment.
I'd consider putting the xref on this paragraph. The section on the scoped modifier defines what it means.
standard/variables.md
Outdated
| > | ||
| > *end example.* | ||
|
|
||
| A reference variable can be scoped explicitly; see §scoped-modifier. |
There was a problem hiding this comment.
This needs more qualification. It's true for local variables and parameters, but not for reference fields in a ref struct.
| - For a ref reassignment `e1 = ref e2`, the ref-safe-context of `e2` shall be at least as wide a context as the *ref-safe-context* of `e1`. | ||
| - For a ref return statement `return ref e1`, the ref-safe-context of `e1` shall be the caller-context. | ||
|
|
||
| ### §scoped-modifier The scoped modifier |
There was a problem hiding this comment.
I don't see any restriction here or in the grammar that the scoped modifier can only be applied to local variables and parameters.
standard/variables.md
Outdated
|
|
||
| > *Note*: Initialization to default values is typically done by having the memory manager or garbage collector initialize memory to all-bits-zero before it is allocated for use. For this reason, it is convenient to use all-bits-zero to represent the null reference. *end note* | ||
|
|
||
| To test if a ref variable has been assigned a referent, call `System.Runtime.CompilerServices.Unsafe.IsNullRef(ref fieldName)`. |
There was a problem hiding this comment.
Should this paragraph be informative rather than normative? Possibly combined with the note that follows.
standard/variables.md
Outdated
|
|
||
| ### §scoped-modifier The scoped modifier | ||
|
|
||
| The contextual keyword `scoped` is used as a modifier to restrict the ref-safe-context ([§9.7.2](variables.md#972-ref-safe-contexts)) or safe-context ([§16.5.15](structs.md#16515-safe-context-constraint)) of a variable. The presence of this modifier asserts that related code doesn’t extend the lifetime of the variable. |
There was a problem hiding this comment.
I don't think "asserts" is the right verb here. Maybe state it this way:
| The contextual keyword `scoped` is used as a modifier to restrict the ref-safe-context ([§9.7.2](variables.md#972-ref-safe-contexts)) or safe-context ([§16.5.15](structs.md#16515-safe-context-constraint)) of a variable. The presence of this modifier asserts that related code doesn’t extend the lifetime of the variable. | |
| The contextual keyword `scoped` is used as a modifier to restrict the ref-safe-context ([§9.7.2](variables.md#972-ref-safe-contexts)) or safe-context ([§16.5.15](structs.md#16515-safe-context-constraint)) of a variable. The presence of this modifier requires that related code doesn’t extend the lifetime of the variable. |
standard/expressions.md
Outdated
| - one of the following value types: `sbyte`, `byte`, `short`, `ushort`, `int`, `uint`, `nint`, `nuint`, `long`, `ulong`, `char`, `float`, `double`, `decimal`, `bool`; or | ||
| - any enumeration type. | ||
|
|
||
| A reference variable field `rv` of type `T`, may not have an explicit initializer of `default`. |
There was a problem hiding this comment.
Two thinks to checK:
- Can a
refvariable berefinitialized to point to something (e.g.ref int iRef = ref parameter;) and to the default reference (e.g.ref int iRef = ref default;).
standard/statements.md
Outdated
| > var orders = new Dictionary<int,Order>(); | ||
| > ref var j = ref i; | ||
| > ref readonly var k = ref i; | ||
| > scoped var r = new RS(); // ref struct RS {} |
There was a problem hiding this comment.
In all cases, I think a more realistic example for the Ref Struct type would be one that takes a single parameter that's a ref. That illustrates the reason for scoped restrictions:
scoped var r = new RS(ref orders);
Or similar. This should apply to all examles using RS
standard/expressions.md
Outdated
| @@ -569,7 +569,7 @@ argument_value | |||
| : expression | |||
| | 'in' variable_reference | |||
| | 'ref' variable_reference | |||
There was a problem hiding this comment.
(Need to verify, but I think scoped can be applied to both in and ref variables:
| | 'ref' variable_reference | |
| | 'in' 'scoped'? variable_reference | |
| | 'ref' 'scoped'? variable_reference |
standard/expressions.md
Outdated
|
|
||
| It is a compile time error if the ref-safe-context ([§9.7.2](variables.md#972-ref-safe-contexts)) of the left operand is wider than the ref-safe-context of the right operand. | ||
|
|
||
| The left operand shall have the same safe-context as the right operand. |
There was a problem hiding this comment.
This is a stronger restriction than the one on L7172. L7172 can be deleted, with the appropriate link to 9.7.2 added here.
| - The keyword `in` followed by a *variable_reference* ([§9.5](variables.md#95-variable-references)), indicating that the argument is passed as an input parameter ([§15.6.2.3.2](classes.md#156232-input-parameters)). A variable shall be definitely assigned ([§9.4](variables.md#94-definite-assignment)) before it can be passed as an input parameter. | ||
| - The keyword `ref` followed by a *variable_reference* ([§9.5](variables.md#95-variable-references)), indicating that the argument is passed as a reference parameter ([§15.6.2.3.3](classes.md#156233-reference-parameters)). A variable shall be definitely assigned ([§9.4](variables.md#94-definite-assignment)) before it can be passed as a reference parameter. | ||
| - The keyword `out` followed by a *variable_reference* ([§9.5](variables.md#95-variable-references)), indicating that the argument is passed as an output parameter ([§15.6.2.3.4](classes.md#156234-output-parameters)). A variable is considered definitely assigned ([§9.4](variables.md#94-definite-assignment)) following a function member invocation in which the variable is passed as an output parameter. | ||
| - The keyword `out` optionally followed by `scoped`, optionally followed by a *variable_reference* ([§9.5](variables.md#95-variable-references)), indicating that the argument is passed as an output parameter ([§15.6.2.3.4](classes.md#156234-output-parameters)). A variable is considered definitely assigned ([§9.4](variables.md#94-definite-assignment)) following a function member invocation in which the variable is passed as an output parameter. For a discussion of `scoped`, see §scoped-modifier. |
There was a problem hiding this comment.
Per above, I think these changes need to be applied to L581 and L582 for ref and in variables.
|
Notes on the open issues:
|
| File | Change | |------|--------| | variables.md §9.7.2.1 | Add *return-only* as 4th ref-safe-context. "three" → "four". Restructure bullets: function-member includes `out` (implicitly scoped) + struct `this` (implicitly scoped); return-only covers `ref`/`in`; caller-context covers fields/elements. | | variables.md §9.7.2.3 | ref/in → *return-only*; out → *function-member*; struct this → *function-member*. Add `[UnscopedRef]` widening rule. | | variables.md §9.7.2.9 | Ref return rule: "shall be the caller-context" → "shall be at least *return-only*". | | structs.md §16.5.15.1 | Add *return-only* as 4th safe-context. Return rule → "at least return-only". Remove old MAMM paragraph. | | structs.md §16.5.15.2 | Add: out of ref struct → *return-only*; this in struct constructor → *return-only*. |
| File | Change | |------|--------| | variables.md §9.7.2.6 | Replace function invocation rules: scoped ref → no ref-safe-context contribution; scoped → no safe-context contribution; out → neither. Add ref-to-ref-struct case. | | structs.md §16.5.15.6 | Parallel update: same scoped exclusions, same ref-to-ref-struct return case. |
New constraint subsections in §16.5.15. | File | Change | |------|--------| | structs.md (new §method-arguments-must-match) | Two checks: (1) ref args of ref struct must be assignable by narrowest safe-context of non-scoped inputs; (2) same for out args of ref struct. Replaces the old MAMM paragraph removed in B5. | | structs.md (new §declaration-expression-safe-context) | Infer safe-context of out declaration variables: narrowest of caller-context, scoped → declaration-block, and contributed safe/ref-safe contexts of non-out arguments. | | structs.md (new subsection) | Object initializer safe-context: narrowest of constructor safe-context, member-initializer argument escapes, and RHS of assignments/ref-assignments. |
| File | Change |
|------|--------|
| statements.md foreach_statement | `('scoped'? ref_kind)` → `'scoped'? ref_kind?`. Add semantic restriction: scoped requires ref_kind or ref struct type. |
| structs.md struct_field_declaration | `field_modifier*` before ref group; add `'readonly'?` after `'ref'` for `ref readonly`. |
| expressions.md argument_value | Add `'scoped'?` to `'in'` and `'ref'` alternatives. Update descriptive text ~L581–583. |
|------|--------| | unsafe-code.md §24.3.1 | Permit managed referent types with a warning. | | unsafe-code.md §24.6.5 | Relax address-of to accept managed-type operands with a warning. | | unsafe-code.md §24.7 | Relax fixed statement initializers to accept managed types with a warning. |
| File | Change | |------|--------| | variables.md (new §parameter-scope-variance) | Overrides/implementations/delegate conversions may: add scoped to ref/in, add scoped to ref struct param, remove UnscopedRef from out/ref-of-ref-struct. Other differences = mismatch. No effect on hiding; overloads shall not differ only on scoped/UnscopedRef. | | attributes.md §UnscopedRefAttribute | Add "(§scoped-modifier)" xref to opening paragraph. |
| File | Change | |------|--------| | variables.md ~L1342 | Qualify scoped applicability: "local variable or parameter" (not fields). | | variables.md ~L1443 | Add normative restriction: scoped not on fields, array elements, return types. | | variables.md ~L203 | Make IsNullRef paragraph informative (wrap in note). | | variables.md ~L1441 | "asserts" → "requires". | | expressions.md ~L3521 | "may not" → "shall not" for ref field default. | | statements.md ~L382 | Better RS examples: `new RS(ref orders)` to show why scoped matters. | | expressions.md ~L7172 | Delete old ref-safe-context rule (subsumed); consolidate into two-rule formulation with §9.7.2 xref. | | structs.md ~L164 | Restore note: struct members = class members minus finalizer, plus ref fields. | | structs.md ~L795 | Constructor text: add "and all reference variable fields to null references". |
Add three examples from the speclet for the most complicated rules for `scoped` ref.
Add three more examples to illustrate the ref safety rules for scoped ref. These highlight the potential issues with an unscoped ref.
Under Construction
This is Rex's adaptation of the corresponding MS proposal.
Open Issues
Numerous places in the spec now point back to this section. It is very likely more needs to be said here.
The MS proposal has a section called “Implicitly scoped parameters.” From that, I have added text to say that an output parameter is implicitly scoped, but made no other changes regarding that section. Are other changes needed?
The MS proposal has a section called “Infer safe-context of declaration expressions.” I made no changes for that section. Are changes needed?
The MS proposal has a section called “Deconstruction expressions” whose contents are “[TBD].” What, if anything do we need to say about this proposal in that context?
The MS proposal has a section called “Return-only safe context.” I made no changes for that section; however, presumably, changes are needed.
The MS proposal has a section called “Rules for method invocation.” I made no changes for that section; however, presumably, changes are needed.
The MS proposal has a section called “Rules for object initializers.” I made no changes for that section; however, presumably, changes are needed.
The MS proposal has a section called “Method arguments must match.” I made no changes for that section; however, presumably, changes are needed.
The MS proposal has a section called “Parameter scope variance.” I made no changes for that section; however, presumably, changes are needed.
The MS proposal has a section called “Initializers with ref values in new and with expressions.” I made the grammar changes only. Perhaps other changes are needed based on the accompanying narrative.
The MS proposal has a section called “Changes in unsafe context.” I made no changes for that section, but surely some are needed.
The MS proposal has a section called “ScopedRefAttribute.” I made no changes for that section, as that appears to be an implementation detail.
The MS proposal has a section called “RefSafetyRulesAttribute.” I made no changes for that section, as that appears to be an implementation detail.
The MS proposal has a section called “Change the design to avoid compat breaks.” I made no changes for that section, but some are probably needed.
The MS proposal has a 14-page section (with numerous subsections) called “Related Information.” I made no changes for that section; it all looks like background reading, but perhaps one or more examples from here might be useful in the spec.