Skip to content

Commit 65d1c53

Browse files
BillWagnerjskeetNigel-Ecma
authored
Add rules for ref safety (#742)
* first pass at safety rules This mostly incorporates the feature spec language. * Add rule for `out` parameters Out parameters have a ref safe to escape scope of the current method, not the calling method. * Add readonly rule. * fix headers no code fences in headers * Apply suggestions from code review Co-authored-by: Jon Skeet <[email protected]> * updates from reviews Updates from code review * style fix * respond to feedback Ref like fields do have storage, but that storage may refer to a variable that is a struct parameter or varialbe. * respond to feedback Respond to existing feedback. * Introduce definitions Introduce better definitions for the "lifetime" of reference variables. I avoided the term "lifetime' because of its runtime connotation. Instead, I used the "scope" where a "variable declaration" is valid. From there, next define the safe scopes and the ref safe scopes for different variable classifications. Finally, I shortened the terms *safe-to-escape-scope* and *ref-safe-to-escape-scope* to "*safe-scope* and *ref-safe-scope*. The text doesn't make it clear why the "escape" term is used, and it doesn't seem to add clarity. * add examples * respond to feedback. * Apply suggestions from code review Co-authored-by: Jon Skeet <[email protected]> * address review comments This addresses most of the comments in the most recent reviews. The next commit will make an attempt to use a single term for *ref_safe_scope*. * Remove *safe_scope* rules Once I push these, I'll add notes about which rules must be added to #601 * Update standard/variables.md Co-authored-by: Nigel-Ecma <[email protected]> * Update standard/variables.md * Respond to review comments. * Feedback from April meeting This include comments, and fixing the formatting after consulting with Rex. * remove ref struct descriptions These belong in the PR for ref structs, and in the section on ref structs. * Apply suggestions from code review Co-authored-by: Jon Skeet <[email protected]> * respond to feedback. * Apply suggestions from code review Co-authored-by: Nigel-Ecma <[email protected]> * remove missing xref This needs to be added to #213 once this PR is merged. * Updates from 5/17 meeting. Updates from the 5/17 meeting. * fix build warning --------- Co-authored-by: Jon Skeet <[email protected]> Co-authored-by: Nigel-Ecma <[email protected]>
1 parent d90f486 commit 65d1c53

File tree

1 file changed

+174
-0
lines changed

1 file changed

+174
-0
lines changed

standard/variables.md

Lines changed: 174 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -988,3 +988,177 @@ variable_reference
988988
## 9.6 Atomicity of variable references
989989

990990
Reads and writes of the following data types shall be atomic: `bool`, `char`, `byte`, `sbyte`, `short`, `ushort`, `uint`, `int`, `float`, and reference types. In addition, reads and writes of enum types with an underlying type in the previous list shall also be atomic. Reads and writes of other types, including `long`, `ulong`, `double`, and `decimal`, as well as user-defined types, need not be atomic. Aside from the library functions designed for that purpose, there is no guarantee of atomic read-modify-write, such as in the case of increment or decrement.
991+
992+
## §ref-span-safety Reference variables and returns
993+
994+
### §ref-span-safety-general General
995+
996+
A ***reference variable*** is a variable that refers to another variable, called the ***referent***. A reference variable does not store the value of its referent. When a reference variable is used where a value is required its referent's value is returned; similarly when a reference variable is the target of an assignment it is the referent which is assigned to. The variable to which a reference variable refers, i.e. its referent, can be changed using a ref assignment (`= ref`). A reference variable is a local variable declared with the `ref` modifier.
997+
998+
> *Example:* The following example demonstrates a local reference variable whose referent is an element of an array:
999+
>
1000+
> ```csharp
1001+
> public class C
1002+
> {
1003+
>
1004+
> public void M()
1005+
> {
1006+
> int[] arr = new int[10];
1007+
> // element is a reference variable that refers to arr[5]
1008+
> ref int element = ref arr[5];
1009+
> element += 5; // arr[5] has been incremented by 5
1010+
> }
1011+
> }
1012+
> ```
1013+
>
1014+
> *end example*
1015+
1016+
A ***reference return*** is the expression returned by reference from a method whose return type includes the `ref` or `ref readonly` modifiers15.6.1). The variable expression of the reference return is the referent of the reference return.
1017+
1018+
> *Example:* The following example demonstrates a reference return whose referent is an element of an array field:
1019+
>
1020+
> ```csharp
1021+
> public class C
1022+
> {
1023+
> private int[] arr = new int[10];
1024+
>
1025+
> public readonly ref int M()
1026+
> {
1027+
> // element is a reference variable that refers to arr[5]
1028+
> ref int element = ref arr[5];
1029+
> return ref element; // return reference to arr[5];
1030+
> }
1031+
> }
1032+
> ```
1033+
>
1034+
> *end example*
1035+
1036+
### §ref-safe-contexts Ref safe contexts
1037+
1038+
All reference variables obey safety rules that ensure the context of the reference variable is not greater than the ref-safe-context of its referent.
1039+
1040+
For any variable, the ***ref-safe-context*** of that variable is the context where a *variable_reference* (§9.5) to that variable is valid. The referent of a reference variable must have a ref-safe-context that is at least as wide as the context of the reference variable.
1041+
1042+
> *Note*: The compiler determines the safe context through a static analysis of the program text. The safe context reflects the lifetime of a variable at runtime. *end note*
1043+
1044+
There are three valid ref-safe-contexts:
1045+
1046+
- ***declaration-block***: A *variable_reference* to a local variable declared in a block is valid from its declaration to the end of the block in which it is declared. Each nested block represents a different context. The ref-safe-context of a local variable is the declaration-block in which it is declared. A local variable declared in a method has a ref-safe-context of the block that defines the method. A variable declared in a block is a valid referent only if the reference variable is declared in the same block after the referent, or a nested block.
1047+
- ***function-member***: A *variable_reference* to a value parameter on a function member declaration, including the implicit `this` parameter, is valid in the entire function member. The ref-safe-context of the fields of a `struct` type is function-member. A variable with ref-safe-context of function-member is a valid referent only if the reference variable is declared in the same function member.
1048+
- ***caller-context***: Member fields of a `class` type and `ref` parameters have ref-safe-context of caller-context. A variable with ref-safe-context of caller-context can be the referent of a reference return. A variable that can be the referent of a reference-return is ***safe-to-ref-return***.
1049+
1050+
These values form a nesting relationship from narrowest (declaration-block) to widest (caller-context). Each nested block represents a different context.
1051+
1052+
> *Example*: The following code shows examples of the different ref-safe-contexts. The declarations show the ref-safe-context for a referent to be the initializing expression for a `ref` variable. The examples show the ref-safe-context for a reference return:
1053+
>
1054+
> ```csharp
1055+
> public class C
1056+
> {
1057+
> // ref safe context of arr is "caller-context".
1058+
> // ref safe context of arr[i] is "caller-context".
1059+
> private int[] arr = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
1060+
>
1061+
> public ref int M1(ref int r1) // ref safe context is "caller-context"
1062+
> {
1063+
> return ref r1; // r1 is safe to ref return
1064+
> }
1065+
>
1066+
> public ref int M2(int v1) // ref safe context is "function-member"
1067+
> {
1068+
> return ref v1; // error: v1 isn't safe to ref return
1069+
> }
1070+
>
1071+
> public ref int M3()
1072+
> {
1073+
> int v2 = 5;
1074+
>
1075+
> return ref arr[v2]; // arr[v2] is safe to ref return
1076+
> }
1077+
>
1078+
> public void M4(int p)
1079+
> {
1080+
> int v3 = 6;
1081+
> ref int r2 = ref p; // context of r2 is block, ref safe context of p is method
1082+
> ref int r3 = ref v3; // context of r3 is block, ref safe context of v3 is block
1083+
> ref int r4 = ref arr[v3]; // context of r4 is block, ref safe context of arr[v3] is caller method
1084+
> }
1085+
> }
1086+
> ```
1087+
>
1088+
> *end example.*
1089+
1090+
#### §ref-span-safety-locals Local variable ref safe context
1091+
1092+
For a local variable `v`:
1093+
1094+
- If `v` is a reference variable, its ref-safe-context is the same as the ref-safe-context of its initializing expression.
1095+
- Otherwise its ref-safe-context is the context in which it was declared.
1096+
1097+
#### §ref-span-safety-parameters Parameter ref safe context
1098+
1099+
For a formal parameter `p`:
1100+
1101+
- If `p` is a `ref`, or `in` parameter, its ref-safe-context is the caller-context. It is safe-to-ref-return. If `p` is an `in` parameter, it can't be returned as a writable `ref` but can be returned as `ref readonly`.
1102+
- If `p` is an `out` parameter, its ref-safe-context is the function-member. It isn't safe-to-ref-return.
1103+
- Otherwise, if `p` is the `this` parameter of a struct type, its ref-safe-context is the function-member.
1104+
- Otherwise, the parameter is a value parameter, and its ref-safe-context is the function-member. It isn't safe-to-ref-return.
1105+
1106+
#### §ref-span-safety-field-reference Field ref safe context
1107+
1108+
For a variable designating a reference to a field, `e.F`:
1109+
1110+
- If `e` is of a reference type, its ref-safe-context is the caller-context.
1111+
- Otherwise, if `e` is of a value type, its ref-safe-context is the same as the ref-safe-context of `e`.
1112+
1113+
#### §ref-span-safety-operators Operators
1114+
1115+
The conditional operator12.18), `c ? ref e1 : ref e2`, and reference assignment operator, `= ref e` (§12.21.1) have reference variables as operands and yield a reference variable. For those operators, the ref-safe-context of the result is the narrowest context among the ref-safe-contexts of all `ref` operands.
1116+
1117+
#### §ref-span-safety-function-invocation Function invocation
1118+
1119+
For a variable `c` resulting from a ref-returning function invocation, `ref e1.M(e2, ...)`, its ref-safe-context is the narrowest of the following contexts:
1120+
1121+
- The caller-context.
1122+
- The ref-safe-context of all `ref` and `out` argument expressions (excluding the receiver).
1123+
- For each `in` parameter of the method, if there is a corresponding expression that is a variable, its ref-safe-context, otherwise the nearest enclosing context
1124+
- The context of all argument expressions (including the receiver).
1125+
1126+
> *Example*: the last bullet is necessary to handle code such as
1127+
>
1128+
> ```csharp
1129+
> ref int M2()
1130+
> {
1131+
> int v = 5;
1132+
> // Not valid.
1133+
> // ref safe context of "v" is block.
1134+
> // Therefore, ref safe context of the return value of M() is block.
1135+
> return ref M(ref v);
1136+
> }
1137+
>
1138+
> ref int M(ref int p)
1139+
> {
1140+
> return ref p;
1141+
> }
1142+
> ```
1143+
>
1144+
> *end example*
1145+
1146+
A property invocation and an indexer invocation (either `get` or `set`) is treated as a function invocation of the underlying accessor by the above rules. A local function invocation is a function invocation.
1147+
1148+
#### §ref-span-safety-a-value Values
1149+
1150+
A value's ref-safe-context is the nearest enclosing context.
1151+
1152+
> *Note:* This occurs in an invocation such as `M(ref d.Length)` where `d` is of type `dynamic`. It is also consistent with arguments corresponding to `in` parameters.
1153+
1154+
#### §ref-span-safety-constructor-invocations Constructor invocations
1155+
1156+
A `new` expression that invokes a constructor obeys the same rules as a method invocationref-span-safety-function-invocation) that is considered to return the type being constructed.
1157+
1158+
#### §ref-span-safety-limitations Limitations on reference variables
1159+
1160+
- Neither a `ref` parameter, nor a `ref` local, nor a parameter or local of a `ref struct` type shall be captured by lambda expression or local function.
1161+
- Neither a `ref` parameter nor a parameter of a `ref struct` type shall be an argument for an iterator method or an `async` method.
1162+
- Neither a `ref` local, nor a local of a `ref struct` type shall be in context at the point of a `yield return` statement or an `await` expression.
1163+
- For a ref reassignment `ref e1 = ref e2`, the ref-safe-context of `e2` must be at least as wide a context as the *ref-safe-context* of `e1`.
1164+
- For a ref return statement `return ref e1`, the ref-safe-context of `e1` must be the caller-context. In other words, `e1` must be ref-safe-to-return.

0 commit comments

Comments
 (0)