From 0e6f7bc8f77daf0be9921be3d1a70c10ce1a5d92 Mon Sep 17 00:00:00 2001 From: Bill Wagner Date: Wed, 22 Feb 2023 17:35:42 -0500 Subject: [PATCH 01/26] first pass at safety rules This mostly incorporates the feature spec language. --- standard/variables.md | 135 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 135 insertions(+) diff --git a/standard/variables.md b/standard/variables.md index 36a276785..cd3911e3e 100644 --- a/standard/variables.md +++ b/standard/variables.md @@ -987,3 +987,138 @@ variable_reference ## 9.6 Atomicity of variable references 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. + +## §ref-span-safety Reference variables and returns + +### §ref-span-safety-general General + +A *reference variable* is a local variable or `struct` declared with the `ref` modifier. A ref local does not create a new storage location. Instead, a ref ref local represents the same storage location as its initializing expression. Thus, the value of a reference variable is always the same as the underlying variable. + +A `ref struct` is a `struct` whose fields include a `ref` struct or a ref-like field. A ref-like field does not create a new storage location. Instead, if refers to the same storage as its initializing expression. Ref struct types include `Span`, `ReadOnlySpan`, and other types that include an unmanaged pointer as a member. + +A *reference return* is the expression returned by reference from a method whose return type includes the `ref` or `ref readonly` modifiers (§14.6.1). The return refers to the same storage as its expression. + +### §ref-span-safety-escape-scopes Safe to escape scopes + +At compile-time, each expression is associated with a scope that expression is permitted to escape to, *safe-to-escape-scope*. Each variable is associated with the scope a reference to it is permitted to escape to, *ref-safe-to-escape-scope*. For a given variable expression, these may be different. When the *ref-safe-to-escape* scope is *calling-method*, the variable is *safe-to-return*. A variable that is *safe-to-return* may escape the enclosing method as a whole. In other words, the variable can *returned-by-ref*. + +Given an assignment from an expression E1 with a *safe-to-escape-scope* S1, to a (variable) expression E2 with *safe-to-escape-scope* S2, it is an error if S2 is a wider scope than S1. By construction, the two scopes S1 and S2 are in a nesting relationship, because a legal expression is always *safe-to-return* from some scope enclosing the expression. + +There are three different values for *ref-safe-to-escape-scope*: *block in which variable is declared*, *current-method* and *calling-method*. When the *ref-safe-to-escape-scope* is *calling-method*, the variable is *safe-to-return* by reference. + +An expression whose type is not a `ref struct` type is *safe-to-return* from the entire enclosing method. Otherwise we refer to the rules below. + +### §ref-span-safety-locals Local variable escape scope + +For a local variable `l`: + +- If `l` is a `ref` variable, its *ref-safe-to-escape-scope* is taken from the *ref-safe-to-escape-scope* of its initializing expression. +- Otherwise its *ref-safe-to-escape-scope* is the scope in which it was declared. + +The *safe-to-escape-scope* of an expression that designates the use of a local variable is determined as follows: + +- A local whose type is not a `ref struct` type is *safe-to-return* from the entire enclosing method. Its *safe-to-escape-scope* is the *calling-method*. +- If the variable is an iteration variable of a `foreach` loop, then the variable's *safe-to-escape-scope* is the same as the *safe-to-escape-scope* of the `foreach` loop's expression. +- A local of `ref struct` type and uninitialized at the point of declaration is *safe-to-return* from the entire enclosing method. Its *safe-to-escape-scope* is the calling method. +- Otherwise the variable's type is a `ref struct` type, and the variable's declaration requires an initializer. The variable's *safe-to-escape-scope* is the same as the *safe-to-escape* of its initializer. + +### §ref-span-safety-parameters Parameter escape scope + +For a formal parameter `p`: + +- If `p` is a `ref`, `out`, or `in` parameter, its *ref-safe-to-escape-scope* is the *calling-method*. It is *safe-to-return* by ref. +- Otherwise, if `p` is the `this` parameter of a struct type, its *ref-safe-to-escape-scope* is the *current-method*. +- Otherwise, the parameter is a value parameter, and it is *ref-safe-to-escape-scope* is the *current-method* + +For an expression that designates the value of a formal parameter `p`, its *safe-to-escape-scope* is the calling method. This applies to the `this` parameter. + +### §ref-span-safety-field-reference Field reference escape scope + +For a variable designating a reference to a field, `e.F`: + +- If `e` is of a reference type, its *ref-safe-to-escape-scope* is the *calling-method*. +- Otherwise, if `e` is of a value type, its *ref-safe-to-escape-scope* is taken from the *ref-safe-to-escape* of `e`. + +For an expression that designates a reference to a field, `e.F`, its a *safe-to-escape-scope* is the same as the *safe-to-escape-scope* of `e`. + +### §ref-span-safety-operators Operators including `?:` + +The application of a user-defined operator is treated as a method invocation. + +For an operator that yields an value, such as `e1 + e2` or `c ? e1 : e2`, the *safe-to-escape-scope* of the result is the narrowest scope among the *safe-to-escape-scopes* of the operands of the operator. As a consequence, for a unary operator that yields an value, such as `+e`, the *safe-to-escape-scope* of the result is the *safe-to-escape-scope* of the operand. + +For an operator that yields an variable, such as `c ? ref e1 : ref e2`: + +- The *ref-safe-to-escape-scope* of the result is the narrowest scope among the *ref-safe-to-escape-scopes* of the operands of the operator. +- The *safe-to-escape-scope* of the operands must agree, and that is the *safe-to-escape-scope* of the resulting variable. + +### §ref-span-safety-method-invocation Method invocation + +For a variable `c` resulting from a ref-returning method invocation, `ref e1.M(e2, ...)`, its *ref-safe-to-escape-scope* is the smallest of the following scopes: + +- The *calling-method*. +- The *ref-safe-to-escape-scope* of all `ref` and `out` argument expressions (excluding the receiver). +- For each `in` parameter of the method, if there is a corresponding expression that is an variable, its *ref-safe-to-escape-scope*, otherwise the nearest enclosing scope +- The *safe-to-escape-scope* of all argument expressions (including the receiver) + +> *Example*: the last bullet is necessary to handle code such as +> +> ```csharp +> var sp = new Span(...) +> return ref sp[0]; +> ``` +> +> or +> +> ```csharp +> return ref M(sp, 0); +> ``` +> +> *end example* + +For a value resulting from a method invocation `e1.M(e2, ...)`, its *safe-to-escape-scope* from the smallest of the following scopes: + +- The *calling-method* +- The *safe-to-escape* of all argument expressions (including the receiver) + +A property invocation (either `get` or `set`) it treated as a method invocation of the underlying method by the above rules. + +### §ref-span-safety-an-rvalue A value + +A value's *ref-safe-to-escape-scope* is the nearest enclosing scope. + +> *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. + +### §ref-span-safety-stackalloc `stackalloc` + +For a value `c` resulting from a `stackalloc` expression its *safe-to-escape-scope* to the current method. + +#### §ref-span-safety-constructor-invocations Constructor invocations + +A `new` expression that invokes a constructor obeys the same rules as a method invocation (§ref-span-safety-method-invocation) that is considered to return the type being constructed. + +In addition, for a value `c` that is the result of a `new` expression, the *safe-to-escape-scope* is no wider than the smallest of the *safe-to-escape-scopes* of all arguments and operands of the object initializer expressions, recursively, if any initializer is present. + +### §ref-span-safety-default-expressions `default` expressions + +For a `default` expression, the *safe-to-escape-scope* is the *calling-method*. + +### §ref-span-safety-limitations Limitations on reference variables + +- Neither a `ref` parameter, nor a `ref` local, nor a parameter or local of a `ref struct` type can be lifted into a lambda or local function. +- Neither a `ref` parameter nor a parameter of a `ref struct` type may be an argument on an iterator method or an `async` method. +- Neither a `ref` local, nor a local of a `ref struct` type may be in scope at the point of a `yield return` statement or an `await` expression. +- A `ref struct` type may not be used as a type argument, or as an element type in a tuple type. +- A `ref struct` type may not be the declared type of a field, except that it may be the declared type of an instance field of another `ref struct`. +- A `ref struct` type may not be the element type of an array. +- A value of a `ref struct` type may not be boxed: + - There is no conversion from a `ref struct` type to the type `object` or the type `System.ValueType`. + - A `ref struct` type may not be declared to implement any interface + - No instance method declared in `object` or in `System.ValueType` but not overridden in a `ref struct` type may be called with a receiver of that `ref struct` type. + - No instance method of a `ref struct` type may be captured by method conversion to a delegate type. +- For a ref reassignment `ref e1 = ref e2`, the *ref-safe-to-escape-scope* of `e2` must be at least as wide a scope as the *ref-safe-to-escape-scope* of `e1`. +- For a ref return statement `return ref e1`, the *ref-safe-to-escape-scope* of `e1` must be the *calling-method*. In other words, `e1` must be *safe-to-return*. +- For a return statement `return e1`, the *safe-to-escape-scope* of `e1` must be the *calling-method*. This is always true when `e1` is not a `ref struct`. +- For an assignment `e1 = e2`, if the type of `e1` is a `ref struct` type, then the *safe-to-escape-scope* of `e2` must be at least as wide a scope as the *safe-to-escape-scope* of `e1`. +- For a method invocation if there is a `ref` or `out` argument of a `ref struct` type (including the receiver), with *safe-to-escape-scope* E1, then no argument (including the receiver) may have a narrower *safe-to-escape-scope* than E1. +- A local function or anonymous function may not refer to a local or parameter of `ref struct` type declared in an enclosing scope. From 5d9d4dcb9ecdc8fd10539f23e7316844edea632d Mon Sep 17 00:00:00 2001 From: Bill Wagner Date: Thu, 23 Feb 2023 10:29:42 -0500 Subject: [PATCH 02/26] Add rule for `out` parameters Out parameters have a ref safe to escape scope of the current method, not the calling method. --- standard/variables.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/standard/variables.md b/standard/variables.md index cd3911e3e..41969235d 100644 --- a/standard/variables.md +++ b/standard/variables.md @@ -1026,7 +1026,8 @@ The *safe-to-escape-scope* of an expression that designates the use of a local v For a formal parameter `p`: -- If `p` is a `ref`, `out`, or `in` parameter, its *ref-safe-to-escape-scope* is the *calling-method*. It is *safe-to-return* by ref. +- If `p` is a `ref`, or `in` parameter, its *ref-safe-to-escape-scope* is the *calling-method*. It is *safe-to-return* by ref. +- If `p` is an `out` parameter, its *ref-safe-to-escape-scope* is the *current-method*. It isn't *safe-to-return* by ref. - Otherwise, if `p` is the `this` parameter of a struct type, its *ref-safe-to-escape-scope* is the *current-method*. - Otherwise, the parameter is a value parameter, and it is *ref-safe-to-escape-scope* is the *current-method* @@ -1083,7 +1084,7 @@ For a value resulting from a method invocation `e1.M(e2, ...)`, its *safe-to-esc A property invocation (either `get` or `set`) it treated as a method invocation of the underlying method by the above rules. -### §ref-span-safety-an-rvalue A value +### §ref-span-safety-a-value A value A value's *ref-safe-to-escape-scope* is the nearest enclosing scope. From 312a6f951270d54e31a4ea2a97983335a26292f9 Mon Sep 17 00:00:00 2001 From: Bill Wagner Date: Thu, 23 Feb 2023 10:32:30 -0500 Subject: [PATCH 03/26] Add readonly rule. --- standard/variables.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/standard/variables.md b/standard/variables.md index 41969235d..080dfbb1e 100644 --- a/standard/variables.md +++ b/standard/variables.md @@ -1026,7 +1026,7 @@ The *safe-to-escape-scope* of an expression that designates the use of a local v For a formal parameter `p`: -- If `p` is a `ref`, or `in` parameter, its *ref-safe-to-escape-scope* is the *calling-method*. It is *safe-to-return* by ref. +- If `p` is a `ref`, or `in` parameter, its *ref-safe-to-escape-scope* is the *calling-method*. It is *safe-to-return* by ref. If `p` is an `in` parameter, it can't be returned as a writable `ref` but can be returned as `ref readonly`. - If `p` is an `out` parameter, its *ref-safe-to-escape-scope* is the *current-method*. It isn't *safe-to-return* by ref. - Otherwise, if `p` is the `this` parameter of a struct type, its *ref-safe-to-escape-scope* is the *current-method*. - Otherwise, the parameter is a value parameter, and it is *ref-safe-to-escape-scope* is the *current-method* From d8d6946d3b79318391e16d89910ede5b7c81bb55 Mon Sep 17 00:00:00 2001 From: Bill Wagner Date: Thu, 23 Feb 2023 10:48:11 -0500 Subject: [PATCH 04/26] fix headers no code fences in headers --- standard/variables.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/standard/variables.md b/standard/variables.md index 080dfbb1e..b6268a086 100644 --- a/standard/variables.md +++ b/standard/variables.md @@ -1042,7 +1042,7 @@ For a variable designating a reference to a field, `e.F`: For an expression that designates a reference to a field, `e.F`, its a *safe-to-escape-scope* is the same as the *safe-to-escape-scope* of `e`. -### §ref-span-safety-operators Operators including `?:` +### §ref-span-safety-operators Operators The application of a user-defined operator is treated as a method invocation. @@ -1084,13 +1084,13 @@ For a value resulting from a method invocation `e1.M(e2, ...)`, its *safe-to-esc A property invocation (either `get` or `set`) it treated as a method invocation of the underlying method by the above rules. -### §ref-span-safety-a-value A value +### §ref-span-safety-a-value Values A value's *ref-safe-to-escape-scope* is the nearest enclosing scope. > *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. -### §ref-span-safety-stackalloc `stackalloc` +### §ref-span-safety-stackalloc stackalloc For a value `c` resulting from a `stackalloc` expression its *safe-to-escape-scope* to the current method. @@ -1100,7 +1100,7 @@ A `new` expression that invokes a constructor obeys the same rules as a method i In addition, for a value `c` that is the result of a `new` expression, the *safe-to-escape-scope* is no wider than the smallest of the *safe-to-escape-scopes* of all arguments and operands of the object initializer expressions, recursively, if any initializer is present. -### §ref-span-safety-default-expressions `default` expressions +### §ref-span-safety-default-expressions Default expressions For a `default` expression, the *safe-to-escape-scope* is the *calling-method*. From 61e594cddc409fc6ae53dbf97074e6bdbf69e61e Mon Sep 17 00:00:00 2001 From: Bill Wagner Date: Fri, 24 Feb 2023 11:11:37 -0500 Subject: [PATCH 05/26] Apply suggestions from code review Co-authored-by: Jon Skeet --- standard/variables.md | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/standard/variables.md b/standard/variables.md index b6268a086..b8b49c33a 100644 --- a/standard/variables.md +++ b/standard/variables.md @@ -1000,7 +1000,7 @@ A *reference return* is the expression returned by reference from a method whose ### §ref-span-safety-escape-scopes Safe to escape scopes -At compile-time, each expression is associated with a scope that expression is permitted to escape to, *safe-to-escape-scope*. Each variable is associated with the scope a reference to it is permitted to escape to, *ref-safe-to-escape-scope*. For a given variable expression, these may be different. When the *ref-safe-to-escape* scope is *calling-method*, the variable is *safe-to-return*. A variable that is *safe-to-return* may escape the enclosing method as a whole. In other words, the variable can *returned-by-ref*. +At compile-time, each expression is associated with a scope that expression is permitted to escape to, its *safe-to-escape-scope*. Each variable is associated with the scope a reference to it is permitted to escape to, *ref-safe-to-escape-scope*. For a given variable expression, these may be different. When the *ref-safe-to-escape* scope is *calling-method*, the variable is *safe-to-return*. A variable that is *safe-to-return* may escape the enclosing method as a whole. In other words, the variable can *returned-by-ref*. Given an assignment from an expression E1 with a *safe-to-escape-scope* S1, to a (variable) expression E2 with *safe-to-escape-scope* S2, it is an error if S2 is a wider scope than S1. By construction, the two scopes S1 and S2 are in a nesting relationship, because a legal expression is always *safe-to-return* from some scope enclosing the expression. @@ -1019,7 +1019,7 @@ The *safe-to-escape-scope* of an expression that designates the use of a local v - A local whose type is not a `ref struct` type is *safe-to-return* from the entire enclosing method. Its *safe-to-escape-scope* is the *calling-method*. - If the variable is an iteration variable of a `foreach` loop, then the variable's *safe-to-escape-scope* is the same as the *safe-to-escape-scope* of the `foreach` loop's expression. -- A local of `ref struct` type and uninitialized at the point of declaration is *safe-to-return* from the entire enclosing method. Its *safe-to-escape-scope* is the calling method. +- A local of `ref struct` type which is uninitialized at the point of declaration is *safe-to-return* from the entire enclosing method. Its *safe-to-escape-scope* is the *calling method*. - Otherwise the variable's type is a `ref struct` type, and the variable's declaration requires an initializer. The variable's *safe-to-escape-scope* is the same as the *safe-to-escape* of its initializer. ### §ref-span-safety-parameters Parameter escape scope @@ -1046,9 +1046,9 @@ For an expression that designates a reference to a field, `e.F`, its a *safe-to- The application of a user-defined operator is treated as a method invocation. -For an operator that yields an value, such as `e1 + e2` or `c ? e1 : e2`, the *safe-to-escape-scope* of the result is the narrowest scope among the *safe-to-escape-scopes* of the operands of the operator. As a consequence, for a unary operator that yields an value, such as `+e`, the *safe-to-escape-scope* of the result is the *safe-to-escape-scope* of the operand. +For an operator that yields a value, such as `e1 + e2` or `c ? e1 : e2`, the *safe-to-escape-scope* of the result is the narrowest scope among the *safe-to-escape-scopes* of the operands of the operator. As a consequence, for a unary operator that yields an value, such as `+e`, the *safe-to-escape-scope* of the result is the *safe-to-escape-scope* of the operand. -For an operator that yields an variable, such as `c ? ref e1 : ref e2`: +For an operator that yields a variable, such as `c ? ref e1 : ref e2`: - The *ref-safe-to-escape-scope* of the result is the narrowest scope among the *ref-safe-to-escape-scopes* of the operands of the operator. - The *safe-to-escape-scope* of the operands must agree, and that is the *safe-to-escape-scope* of the resulting variable. @@ -1082,7 +1082,7 @@ For a value resulting from a method invocation `e1.M(e2, ...)`, its *safe-to-esc - The *calling-method* - The *safe-to-escape* of all argument expressions (including the receiver) -A property invocation (either `get` or `set`) it treated as a method invocation of the underlying method by the above rules. +A property invocation (either `get` or `set`) is treated as a method invocation of the underlying method by the above rules. ### §ref-span-safety-a-value Values @@ -1092,7 +1092,7 @@ A value's *ref-safe-to-escape-scope* is the nearest enclosing scope. ### §ref-span-safety-stackalloc stackalloc -For a value `c` resulting from a `stackalloc` expression its *safe-to-escape-scope* to the current method. +The *safe-to-escape-scope* for a value `c` resulting from a `stackalloc` expression is the current method. #### §ref-span-safety-constructor-invocations Constructor invocations @@ -1102,12 +1102,12 @@ In addition, for a value `c` that is the result of a `new` expression, the *safe ### §ref-span-safety-default-expressions Default expressions -For a `default` expression, the *safe-to-escape-scope* is the *calling-method*. +The *safe-to-escape-scope* of a `default` expression is the *calling-method*. ### §ref-span-safety-limitations Limitations on reference variables -- Neither a `ref` parameter, nor a `ref` local, nor a parameter or local of a `ref struct` type can be lifted into a lambda or local function. -- Neither a `ref` parameter nor a parameter of a `ref struct` type may be an argument on an iterator method or an `async` method. +- Neither a `ref` parameter, nor a `ref` local, nor a parameter or local of a `ref struct` type can be lifted into a lambda expression or local function. +- Neither a `ref` parameter nor a parameter of a `ref struct` type may be an argument for an iterator method or an `async` method. - Neither a `ref` local, nor a local of a `ref struct` type may be in scope at the point of a `yield return` statement or an `await` expression. - A `ref struct` type may not be used as a type argument, or as an element type in a tuple type. - A `ref struct` type may not be the declared type of a field, except that it may be the declared type of an instance field of another `ref struct`. From e04d8419fc1fa9d5e8803318c70b87d39d416f3d Mon Sep 17 00:00:00 2001 From: Bill Wagner Date: Fri, 24 Feb 2023 17:37:52 -0500 Subject: [PATCH 06/26] updates from reviews Updates from code review --- standard/variables.md | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/standard/variables.md b/standard/variables.md index b8b49c33a..29eeb76a1 100644 --- a/standard/variables.md +++ b/standard/variables.md @@ -992,19 +992,19 @@ Reads and writes of the following data types shall be atomic: `bool`, `char`, `b ### §ref-span-safety-general General -A *reference variable* is a local variable or `struct` declared with the `ref` modifier. A ref local does not create a new storage location. Instead, a ref ref local represents the same storage location as its initializing expression. Thus, the value of a reference variable is always the same as the underlying variable. +A *reference variable* is a local variable declared with the `ref` modifier, or an instance of a `ref struct` type. A ref local does not create a new storage location. Instead, a ref local represents the same storage location as its initializing expression. Thus, the value of a reference variable is always the same as the underlying variable. -A `ref struct` is a `struct` whose fields include a `ref` struct or a ref-like field. A ref-like field does not create a new storage location. Instead, if refers to the same storage as its initializing expression. Ref struct types include `Span`, `ReadOnlySpan`, and other types that include an unmanaged pointer as a member. +A `ref struct` may include `ref struct` or ref-like fields. A ref-like field does not create a new storage location. Instead, if refers to the same storage as its initializing expression. Ref struct types include `Span`, `ReadOnlySpan`, and other types that include an unmanaged pointer as a member. A *reference return* is the expression returned by reference from a method whose return type includes the `ref` or `ref readonly` modifiers (§14.6.1). The return refers to the same storage as its expression. ### §ref-span-safety-escape-scopes Safe to escape scopes -At compile-time, each expression is associated with a scope that expression is permitted to escape to, its *safe-to-escape-scope*. Each variable is associated with the scope a reference to it is permitted to escape to, *ref-safe-to-escape-scope*. For a given variable expression, these may be different. When the *ref-safe-to-escape* scope is *calling-method*, the variable is *safe-to-return*. A variable that is *safe-to-return* may escape the enclosing method as a whole. In other words, the variable can *returned-by-ref*. +At compile-time, each expression is associated with a scope that expression is permitted to escape to, its *safe-to-escape-scope*. Each variable is associated with the scope a reference to it is permitted to escape to, *ref-safe-to-escape-scope*. For a given variable expression, these may be different. When the *ref-safe-to-escape* scope is *caller-scope*, the variable is *safe-to-return*. A variable that is *safe-to-return* may escape the enclosing method as a whole. In other words, the variable can *returned-by-ref*. -Given an assignment from an expression E1 with a *safe-to-escape-scope* S1, to a (variable) expression E2 with *safe-to-escape-scope* S2, it is an error if S2 is a wider scope than S1. By construction, the two scopes S1 and S2 are in a nesting relationship, because a legal expression is always *safe-to-return* from some scope enclosing the expression. +Given an assignment from an expression E1 with a *safe-to-escape-scope* S1, to a (variable) expression E2 with *safe-to-escape-scope* S2, it is an error if S2 is a wider scope than S1. By construction, the two scopes S1 and S2 are in a nesting relationship. -There are three different values for *ref-safe-to-escape-scope*: *block in which variable is declared*, *current-method* and *calling-method*. When the *ref-safe-to-escape-scope* is *calling-method*, the variable is *safe-to-return* by reference. +There are three different values for *ref-safe-to-escape-scope*: *block-in-which-variable-is-declared*, *current-method* and *caller-scope*. When the *ref-safe-to-escape-scope* is *caller-scope*, the variable is *safe-to-return* by reference. These three values form a nesting relationship from narrowest (*block-in-which-variable-is-declared*) to widest (*caller-scope*). An expression whose type is not a `ref struct` type is *safe-to-return* from the entire enclosing method. Otherwise we refer to the rules below. @@ -1012,21 +1012,21 @@ An expression whose type is not a `ref struct` type is *safe-to-return* from the For a local variable `l`: -- If `l` is a `ref` variable, its *ref-safe-to-escape-scope* is taken from the *ref-safe-to-escape-scope* of its initializing expression. +- If `v` is a `ref` variable, its *ref-safe-to-escape-scope* is taken from the *ref-safe-to-escape-scope* of its initializing expression. - Otherwise its *ref-safe-to-escape-scope* is the scope in which it was declared. The *safe-to-escape-scope* of an expression that designates the use of a local variable is determined as follows: -- A local whose type is not a `ref struct` type is *safe-to-return* from the entire enclosing method. Its *safe-to-escape-scope* is the *calling-method*. +- A local whose type is not a `ref struct` type is *safe-to-return* from the entire enclosing method. Its *safe-to-escape-scope* is the *caller-scope*. - If the variable is an iteration variable of a `foreach` loop, then the variable's *safe-to-escape-scope* is the same as the *safe-to-escape-scope* of the `foreach` loop's expression. - A local of `ref struct` type which is uninitialized at the point of declaration is *safe-to-return* from the entire enclosing method. Its *safe-to-escape-scope* is the *calling method*. -- Otherwise the variable's type is a `ref struct` type, and the variable's declaration requires an initializer. The variable's *safe-to-escape-scope* is the same as the *safe-to-escape* of its initializer. +- Otherwise the variable's type is a `ref struct` type, and the variable's declaration has an initializer. The variable's *safe-to-escape-scope* is the same as the *safe-to-escape* of its initializer. ### §ref-span-safety-parameters Parameter escape scope For a formal parameter `p`: -- If `p` is a `ref`, or `in` parameter, its *ref-safe-to-escape-scope* is the *calling-method*. It is *safe-to-return* by ref. If `p` is an `in` parameter, it can't be returned as a writable `ref` but can be returned as `ref readonly`. +- If `p` is a `ref`, or `in` parameter, its *ref-safe-to-escape-scope* is the *caller-scope*. It is *safe-to-return* by ref. If `p` is an `in` parameter, it can't be returned as a writable `ref` but can be returned as `ref readonly`. - If `p` is an `out` parameter, its *ref-safe-to-escape-scope* is the *current-method*. It isn't *safe-to-return* by ref. - Otherwise, if `p` is the `this` parameter of a struct type, its *ref-safe-to-escape-scope* is the *current-method*. - Otherwise, the parameter is a value parameter, and it is *ref-safe-to-escape-scope* is the *current-method* @@ -1037,7 +1037,7 @@ For an expression that designates the value of a formal parameter `p`, its *safe For a variable designating a reference to a field, `e.F`: -- If `e` is of a reference type, its *ref-safe-to-escape-scope* is the *calling-method*. +- If `e` is of a reference type, its *ref-safe-to-escape-scope* is the *caller-scope*. - Otherwise, if `e` is of a value type, its *ref-safe-to-escape-scope* is taken from the *ref-safe-to-escape* of `e`. For an expression that designates a reference to a field, `e.F`, its a *safe-to-escape-scope* is the same as the *safe-to-escape-scope* of `e`. @@ -1055,9 +1055,9 @@ For an operator that yields a variable, such as `c ? ref e1 : ref e2`: ### §ref-span-safety-method-invocation Method invocation -For a variable `c` resulting from a ref-returning method invocation, `ref e1.M(e2, ...)`, its *ref-safe-to-escape-scope* is the smallest of the following scopes: +For a variable `c` resulting from a ref-returning method invocation, `ref e1.M(e2, ...)`, its *ref-safe-to-escape-scope* is the narrowest of the following scopes: -- The *calling-method*. +- The *caller-scope*. - The *ref-safe-to-escape-scope* of all `ref` and `out` argument expressions (excluding the receiver). - For each `in` parameter of the method, if there is a corresponding expression that is an variable, its *ref-safe-to-escape-scope*, otherwise the nearest enclosing scope - The *safe-to-escape-scope* of all argument expressions (including the receiver) @@ -1077,9 +1077,9 @@ For a variable `c` resulting from a ref-returning method invocation, `ref e1.M(e > > *end example* -For a value resulting from a method invocation `e1.M(e2, ...)`, its *safe-to-escape-scope* from the smallest of the following scopes: +For a value resulting from a method invocation `e1.M(e2, ...)`, its *safe-to-escape-scope* from the narrowest of the following scopes: -- The *calling-method* +- The *caller-scope* - The *safe-to-escape* of all argument expressions (including the receiver) A property invocation (either `get` or `set`) is treated as a method invocation of the underlying method by the above rules. @@ -1102,7 +1102,7 @@ In addition, for a value `c` that is the result of a `new` expression, the *safe ### §ref-span-safety-default-expressions Default expressions -The *safe-to-escape-scope* of a `default` expression is the *calling-method*. +The *safe-to-escape-scope* of a `default` expression is the *caller-scope*. ### §ref-span-safety-limitations Limitations on reference variables @@ -1118,8 +1118,8 @@ The *safe-to-escape-scope* of a `default` expression is the *calling-method*. - No instance method declared in `object` or in `System.ValueType` but not overridden in a `ref struct` type may be called with a receiver of that `ref struct` type. - No instance method of a `ref struct` type may be captured by method conversion to a delegate type. - For a ref reassignment `ref e1 = ref e2`, the *ref-safe-to-escape-scope* of `e2` must be at least as wide a scope as the *ref-safe-to-escape-scope* of `e1`. -- For a ref return statement `return ref e1`, the *ref-safe-to-escape-scope* of `e1` must be the *calling-method*. In other words, `e1` must be *safe-to-return*. -- For a return statement `return e1`, the *safe-to-escape-scope* of `e1` must be the *calling-method*. This is always true when `e1` is not a `ref struct`. +- For a ref return statement `return ref e1`, the *ref-safe-to-escape-scope* of `e1` must be the *caller-scope*. In other words, `e1` must be *safe-to-return*. +- For a return statement `return e1`, the *safe-to-escape-scope* of `e1` must be the *caller-scope*. This is always true when `e1` is not a `ref struct`. - For an assignment `e1 = e2`, if the type of `e1` is a `ref struct` type, then the *safe-to-escape-scope* of `e2` must be at least as wide a scope as the *safe-to-escape-scope* of `e1`. - For a method invocation if there is a `ref` or `out` argument of a `ref struct` type (including the receiver), with *safe-to-escape-scope* E1, then no argument (including the receiver) may have a narrower *safe-to-escape-scope* than E1. - A local function or anonymous function may not refer to a local or parameter of `ref struct` type declared in an enclosing scope. From 008b91c0db9123279d1235c28ca677e4e5936e76 Mon Sep 17 00:00:00 2001 From: Bill Wagner Date: Fri, 24 Feb 2023 17:55:45 -0500 Subject: [PATCH 07/26] style fix --- standard/variables.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/standard/variables.md b/standard/variables.md index 29eeb76a1..f3c1a31f5 100644 --- a/standard/variables.md +++ b/standard/variables.md @@ -1000,7 +1000,7 @@ A *reference return* is the expression returned by reference from a method whose ### §ref-span-safety-escape-scopes Safe to escape scopes -At compile-time, each expression is associated with a scope that expression is permitted to escape to, its *safe-to-escape-scope*. Each variable is associated with the scope a reference to it is permitted to escape to, *ref-safe-to-escape-scope*. For a given variable expression, these may be different. When the *ref-safe-to-escape* scope is *caller-scope*, the variable is *safe-to-return*. A variable that is *safe-to-return* may escape the enclosing method as a whole. In other words, the variable can *returned-by-ref*. +At compile-time, each expression is associated with a scope that expression is permitted to escape to, its *safe-to-escape-scope*. Each variable is associated with the scope a reference to it is permitted to escape to, *ref-safe-to-escape-scope*. For a given variable expression, these may be different. When the *ref-safe-to-escape-scope* scope is *caller-scope*, the variable is *safe-to-return*. A variable that is *safe-to-return* may escape the enclosing method as a whole. In other words, the variable can *returned-by-ref*. Given an assignment from an expression E1 with a *safe-to-escape-scope* S1, to a (variable) expression E2 with *safe-to-escape-scope* S2, it is an error if S2 is a wider scope than S1. By construction, the two scopes S1 and S2 are in a nesting relationship. From 6ce47cda98de9c32e3780a4c8a727315495a9c33 Mon Sep 17 00:00:00 2001 From: Bill Wagner Date: Mon, 27 Feb 2023 17:54:38 -0500 Subject: [PATCH 08/26] respond to feedback Ref like fields do have storage, but that storage may refer to a variable that is a struct parameter or varialbe. --- standard/variables.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/standard/variables.md b/standard/variables.md index f3c1a31f5..cf234ed58 100644 --- a/standard/variables.md +++ b/standard/variables.md @@ -994,10 +994,12 @@ Reads and writes of the following data types shall be atomic: `bool`, `char`, `b A *reference variable* is a local variable declared with the `ref` modifier, or an instance of a `ref struct` type. A ref local does not create a new storage location. Instead, a ref local represents the same storage location as its initializing expression. Thus, the value of a reference variable is always the same as the underlying variable. -A `ref struct` may include `ref struct` or ref-like fields. A ref-like field does not create a new storage location. Instead, if refers to the same storage as its initializing expression. Ref struct types include `Span`, `ReadOnlySpan`, and other types that include an unmanaged pointer as a member. +A `ref struct` may include `ref struct` or ref-like fields. A ref-like field refers to the same storage as its initializing expression. Its storage size in the ref struct is the same storage size of a reference field. Unlike a reference field, a ref-like field may refer to a `struct` whose storage may be on the execution stack. Ref struct types include `Span`, `ReadOnlySpan`, and other types that include an unmanaged pointer as a member. A *reference return* is the expression returned by reference from a method whose return type includes the `ref` or `ref readonly` modifiers (§14.6.1). The return refers to the same storage as its expression. +All reference variables must obey safety rules that ensure the lifetime of the reference variable is not greater than the lifetime of the storage it refers to. + ### §ref-span-safety-escape-scopes Safe to escape scopes At compile-time, each expression is associated with a scope that expression is permitted to escape to, its *safe-to-escape-scope*. Each variable is associated with the scope a reference to it is permitted to escape to, *ref-safe-to-escape-scope*. For a given variable expression, these may be different. When the *ref-safe-to-escape-scope* scope is *caller-scope*, the variable is *safe-to-return*. A variable that is *safe-to-return* may escape the enclosing method as a whole. In other words, the variable can *returned-by-ref*. From a3fb4f0dd003ed6682148233d7e3dfb96f1d3dc3 Mon Sep 17 00:00:00 2001 From: Bill Wagner Date: Tue, 21 Mar 2023 13:00:36 -0400 Subject: [PATCH 09/26] respond to feedback Respond to existing feedback. --- standard/variables.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/standard/variables.md b/standard/variables.md index cf234ed58..d711a0b5e 100644 --- a/standard/variables.md +++ b/standard/variables.md @@ -992,34 +992,34 @@ Reads and writes of the following data types shall be atomic: `bool`, `char`, `b ### §ref-span-safety-general General -A *reference variable* is a local variable declared with the `ref` modifier, or an instance of a `ref struct` type. A ref local does not create a new storage location. Instead, a ref local represents the same storage location as its initializing expression. Thus, the value of a reference variable is always the same as the underlying variable. +A *reference variable* is a local variable declared with the `ref` modifier, or an instance of a `ref struct` type. A ref local does not store the result of its initializing variable. Instead, a ref local refers to the same storage location as its initializing variable. Thus, the value of a reference variable is always the same as the underlying variable. -A `ref struct` may include `ref struct` or ref-like fields. A ref-like field refers to the same storage as its initializing expression. Its storage size in the ref struct is the same storage size of a reference field. Unlike a reference field, a ref-like field may refer to a `struct` whose storage may be on the execution stack. Ref struct types include `Span`, `ReadOnlySpan`, and other types that include an unmanaged pointer as a member. +A `ref struct` may include `ref struct` or ref-like fields. A ref-like field refers to the same storage as its initializing variable. Its storage size in the ref struct is the same storage size of a reference field. Unlike a reference field, a ref-like field may refer to a `struct` whose storage may be on the execution stack. Ref struct types include `Span`, `ReadOnlySpan`, and other types that include an unmanaged pointer as a member. -A *reference return* is the expression returned by reference from a method whose return type includes the `ref` or `ref readonly` modifiers (§14.6.1). The return refers to the same storage as its expression. +A *reference return* is the expression returned by reference from a method whose return type includes the `ref` or `ref readonly` modifiers (§14.6.1). The return refers to the same location as its expression. -All reference variables must obey safety rules that ensure the lifetime of the reference variable is not greater than the lifetime of the storage it refers to. +All reference variables must obey safety rules that ensure the lifetime of the reference variable is not greater than the lifetime of the variable it refers to. ### §ref-span-safety-escape-scopes Safe to escape scopes -At compile-time, each expression is associated with a scope that expression is permitted to escape to, its *safe-to-escape-scope*. Each variable is associated with the scope a reference to it is permitted to escape to, *ref-safe-to-escape-scope*. For a given variable expression, these may be different. When the *ref-safe-to-escape-scope* scope is *caller-scope*, the variable is *safe-to-return*. A variable that is *safe-to-return* may escape the enclosing method as a whole. In other words, the variable can *returned-by-ref*. +At compile-time, each value is associated with a scope that value is permitted to escape to, its *safe-to-escape-scope*. Each variable is associated with the scope a reference to it is permitted to escape to, *ref-safe-to-escape-scope*. For a given variable expression, these may be different. When the *ref-safe-to-escape-scope* scope is *caller-scope*, the variable is *safe-to-return*. A variable that is *safe-to-return* may escape the enclosing method as a whole. In other words, the variable can be *returned-by-ref*. Given an assignment from an expression E1 with a *safe-to-escape-scope* S1, to a (variable) expression E2 with *safe-to-escape-scope* S2, it is an error if S2 is a wider scope than S1. By construction, the two scopes S1 and S2 are in a nesting relationship. There are three different values for *ref-safe-to-escape-scope*: *block-in-which-variable-is-declared*, *current-method* and *caller-scope*. When the *ref-safe-to-escape-scope* is *caller-scope*, the variable is *safe-to-return* by reference. These three values form a nesting relationship from narrowest (*block-in-which-variable-is-declared*) to widest (*caller-scope*). -An expression whose type is not a `ref struct` type is *safe-to-return* from the entire enclosing method. Otherwise we refer to the rules below. +A value whose type is not a `ref struct` type is *safe-to-return* from the entire enclosing method. Otherwise we refer to the rules below. ### §ref-span-safety-locals Local variable escape scope -For a local variable `l`: +For a local variable `v`: - If `v` is a `ref` variable, its *ref-safe-to-escape-scope* is taken from the *ref-safe-to-escape-scope* of its initializing expression. - Otherwise its *ref-safe-to-escape-scope* is the scope in which it was declared. The *safe-to-escape-scope* of an expression that designates the use of a local variable is determined as follows: -- A local whose type is not a `ref struct` type is *safe-to-return* from the entire enclosing method. Its *safe-to-escape-scope* is the *caller-scope*. +- A value whose type is not a `ref struct` type is *safe-to-return* from the entire enclosing method. Its *safe-to-escape-scope* is the *caller-scope*. - If the variable is an iteration variable of a `foreach` loop, then the variable's *safe-to-escape-scope* is the same as the *safe-to-escape-scope* of the `foreach` loop's expression. - A local of `ref struct` type which is uninitialized at the point of declaration is *safe-to-return* from the entire enclosing method. Its *safe-to-escape-scope* is the *calling method*. - Otherwise the variable's type is a `ref struct` type, and the variable's declaration has an initializer. The variable's *safe-to-escape-scope* is the same as the *safe-to-escape* of its initializer. From a2c219bebb83c13065e452b92aa3b640ddf7d17b Mon Sep 17 00:00:00 2001 From: Bill Wagner Date: Tue, 21 Mar 2023 20:49:39 -0400 Subject: [PATCH 10/26] 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. --- standard/variables.md | 80 +++++++++++++++++++++++-------------------- 1 file changed, 42 insertions(+), 38 deletions(-) diff --git a/standard/variables.md b/standard/variables.md index d711a0b5e..83838711b 100644 --- a/standard/variables.md +++ b/standard/variables.md @@ -992,77 +992,81 @@ Reads and writes of the following data types shall be atomic: `bool`, `char`, `b ### §ref-span-safety-general General -A *reference variable* is a local variable declared with the `ref` modifier, or an instance of a `ref struct` type. A ref local does not store the result of its initializing variable. Instead, a ref local refers to the same storage location as its initializing variable. Thus, the value of a reference variable is always the same as the underlying variable. +A *reference variable* is a variable that refers to another variable, called the *referent*. Therefore, a *reference variable* does not store its value. Instead, a *reference variable* refers to its *referent*. Thus, the value of a reference variable is always the same as its referent variable. A *reference variable* is a local variable declared with the `ref` modifier, or an instance of a `ref struct` type. A `ref struct` may include `ref struct` or ref-like fields. A ref-like field refers to the same storage as its initializing variable. Its storage size in the ref struct is the same storage size of a reference field. Unlike a reference field, a ref-like field may refer to a `struct` whose storage may be on the execution stack. Ref struct types include `Span`, `ReadOnlySpan`, and other types that include an unmanaged pointer as a member. -A *reference return* is the expression returned by reference from a method whose return type includes the `ref` or `ref readonly` modifiers (§14.6.1). The return refers to the same location as its expression. +A *reference return* is the expression returned by reference from a method whose return type includes the `ref` or `ref readonly` modifiers (§14.6.1). The variable expression of the *reference return* is the *referent* of the *reference return*. -All reference variables must obey safety rules that ensure the lifetime of the reference variable is not greater than the lifetime of the variable it refers to. +All reference variables obey safety rules that ensure the valid scope of the reference variable is not greater than the valid scope of its referent. -### §ref-span-safety-escape-scopes Safe to escape scopes +### §ref-span-safety-escape-scopes Safe scopes -At compile-time, each value is associated with a scope that value is permitted to escape to, its *safe-to-escape-scope*. Each variable is associated with the scope a reference to it is permitted to escape to, *ref-safe-to-escape-scope*. For a given variable expression, these may be different. When the *ref-safe-to-escape-scope* scope is *caller-scope*, the variable is *safe-to-return*. A variable that is *safe-to-return* may escape the enclosing method as a whole. In other words, the variable can be *returned-by-ref*. +For any variable, the *ref_safe_scope* of that variable is the scope where a *variable_reference* (§9.5) to that variable is valid. The *referent* of a reference variable must have a *ref_safe_scope* that is at least as wide as the scope where the reference variable is valid. -Given an assignment from an expression E1 with a *safe-to-escape-scope* S1, to a (variable) expression E2 with *safe-to-escape-scope* S2, it is an error if S2 is a wider scope than S1. By construction, the two scopes S1 and S2 are in a nesting relationship. +There are three valid *ref_safe_scope*s: -There are three different values for *ref-safe-to-escape-scope*: *block-in-which-variable-is-declared*, *current-method* and *caller-scope*. When the *ref-safe-to-escape-scope* is *caller-scope*, the variable is *safe-to-return* by reference. These three values form a nesting relationship from narrowest (*block-in-which-variable-is-declared*) to widest (*caller-scope*). +- *block*: A variable declared in a block is valid in that block in which it is declared. This includes all local variables. A local variable declared in a method is valid in 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, or a nested block. +- *method*: A value parameter on a method declaration, including the implicit `this` parameter is valid in the entire method. The fields of a `struct` type are valid in the entire method. A variable with *ref_safe_scope* of *method* is a valid referent only if the reference variable is declared in the same method. +- *caller_scope*: Member fields of a `class` type and `ref` parameters have *ref_safe_scope* of *caller_scope*. A variable with *ref_safe_scope* of *caller_scope* can be the referent of a reference return. A variable that can be referent of a *reference-return* is *safe-to-ref-return*. -A value whose type is not a `ref struct` type is *safe-to-return* from the entire enclosing method. Otherwise we refer to the rules below. +These values form a nesting relationship from narrowest (*block*) to widest (*caller-scope*). -### §ref-span-safety-locals Local variable escape scope +For any variable, the *safe_scope* of that variable is the scope where its value may be copied. A variable whose type is not a `ref struct` type is *safe-to-return* from the entire enclosing method. Its *safe_scope* is *caller_method*. Otherwise the following rules apply. + +### §ref-span-safety-locals Local variable safe scope For a local variable `v`: -- If `v` is a `ref` variable, its *ref-safe-to-escape-scope* is taken from the *ref-safe-to-escape-scope* of its initializing expression. -- Otherwise its *ref-safe-to-escape-scope* is the scope in which it was declared. +- If `v` is a `ref` variable, its *ref-safe-scope* is taken from the *ref-safe-scope* of its initializing expression. +- Otherwise its *ref-safe-scope* is the scope in which it was declared. -The *safe-to-escape-scope* of an expression that designates the use of a local variable is determined as follows: +The *safe-scope* of an expression that designates the use of a local variable is determined as follows: -- A value whose type is not a `ref struct` type is *safe-to-return* from the entire enclosing method. Its *safe-to-escape-scope* is the *caller-scope*. -- If the variable is an iteration variable of a `foreach` loop, then the variable's *safe-to-escape-scope* is the same as the *safe-to-escape-scope* of the `foreach` loop's expression. -- A local of `ref struct` type which is uninitialized at the point of declaration is *safe-to-return* from the entire enclosing method. Its *safe-to-escape-scope* is the *calling method*. -- Otherwise the variable's type is a `ref struct` type, and the variable's declaration has an initializer. The variable's *safe-to-escape-scope* is the same as the *safe-to-escape* of its initializer. +- A value whose type is not a `ref struct` type is *safe-to-return* from the entire enclosing method. Its *safe-scope* is the *caller-scope*. +- If the variable is an iteration variable of a `foreach` loop, then the variable's *safe-scope* is the same as the *safe-scope* of the `foreach` loop's expression. +- A local of `ref struct` type which is uninitialized at the point of declaration is *safe-to-return* from the entire enclosing method. Its *safe-scope* is the *calling method*. +- Otherwise the variable's type is a `ref struct` type, and the variable's declaration has an initializer. The variable's *safe-scope* is the same as the *ref-safe-scope* of its initializer. ### §ref-span-safety-parameters Parameter escape scope For a formal parameter `p`: -- If `p` is a `ref`, or `in` parameter, its *ref-safe-to-escape-scope* is the *caller-scope*. It is *safe-to-return* by ref. If `p` is an `in` parameter, it can't be returned as a writable `ref` but can be returned as `ref readonly`. +- If `p` is a `ref`, or `in` parameter, its *ref-safe-scope* is the *caller-scope*. It is *safe-to-return* by ref. If `p` is an `in` parameter, it can't be returned as a writable `ref` but can be returned as `ref readonly`. - If `p` is an `out` parameter, its *ref-safe-to-escape-scope* is the *current-method*. It isn't *safe-to-return* by ref. - Otherwise, if `p` is the `this` parameter of a struct type, its *ref-safe-to-escape-scope* is the *current-method*. - Otherwise, the parameter is a value parameter, and it is *ref-safe-to-escape-scope* is the *current-method* -For an expression that designates the value of a formal parameter `p`, its *safe-to-escape-scope* is the calling method. This applies to the `this` parameter. +For an expression that designates the value of a formal parameter `p`, its *safe-scope* is the calling method. This applies to the `this` parameter. ### §ref-span-safety-field-reference Field reference escape scope For a variable designating a reference to a field, `e.F`: -- If `e` is of a reference type, its *ref-safe-to-escape-scope* is the *caller-scope*. -- Otherwise, if `e` is of a value type, its *ref-safe-to-escape-scope* is taken from the *ref-safe-to-escape* of `e`. +- If `e` is of a reference type, its *ref-safe-scope* is the *caller-scope*. +- Otherwise, if `e` is of a value type, its *ref-safe-scope* is taken from the *ref-safe-scope* of `e`. -For an expression that designates a reference to a field, `e.F`, its a *safe-to-escape-scope* is the same as the *safe-to-escape-scope* of `e`. +For an expression that designates a reference to a field, `e.F`, its a *safe-scope* is the same as the *safe-scope* of `e`. ### §ref-span-safety-operators Operators The application of a user-defined operator is treated as a method invocation. -For an operator that yields a value, such as `e1 + e2` or `c ? e1 : e2`, the *safe-to-escape-scope* of the result is the narrowest scope among the *safe-to-escape-scopes* of the operands of the operator. As a consequence, for a unary operator that yields an value, such as `+e`, the *safe-to-escape-scope* of the result is the *safe-to-escape-scope* of the operand. +For an operator that yields a value, such as `e1 + e2` or `c ? e1 : e2`, the *safe-scope* of the result is the narrowest scope among the *safe-scopes* of the operands of the operator. As a consequence, for a unary operator that yields an value, such as `+e`, the *safe-scope* of the result is the *safe-scope* of the operand. For an operator that yields a variable, such as `c ? ref e1 : ref e2`: -- The *ref-safe-to-escape-scope* of the result is the narrowest scope among the *ref-safe-to-escape-scopes* of the operands of the operator. -- The *safe-to-escape-scope* of the operands must agree, and that is the *safe-to-escape-scope* of the resulting variable. +- The *ref-safe-scope* of the result is the narrowest scope among the *ref-safe-scopes* of the operands of the operator. +- The *safe-scope* of the operands must agree, and that is the *safe-scope* of the resulting variable. ### §ref-span-safety-method-invocation Method invocation -For a variable `c` resulting from a ref-returning method invocation, `ref e1.M(e2, ...)`, its *ref-safe-to-escape-scope* is the narrowest of the following scopes: +For a variable `c` resulting from a ref-returning method invocation, `ref e1.M(e2, ...)`, its *ref-safe-scope* is the narrowest of the following scopes: - The *caller-scope*. -- The *ref-safe-to-escape-scope* of all `ref` and `out` argument expressions (excluding the receiver). -- For each `in` parameter of the method, if there is a corresponding expression that is an variable, its *ref-safe-to-escape-scope*, otherwise the nearest enclosing scope -- The *safe-to-escape-scope* of all argument expressions (including the receiver) +- The *ref-safe-scope* of all `ref` and `out` argument expressions (excluding the receiver). +- For each `in` parameter of the method, if there is a corresponding expression that is an variable, its *ref-safe-scope*, otherwise the nearest enclosing scope +- The *safe-scope* of all argument expressions (including the receiver) > *Example*: the last bullet is necessary to handle code such as > @@ -1079,32 +1083,32 @@ For a variable `c` resulting from a ref-returning method invocation, `ref e1.M(e > > *end example* -For a value resulting from a method invocation `e1.M(e2, ...)`, its *safe-to-escape-scope* from the narrowest of the following scopes: +For a value resulting from a method invocation `e1.M(e2, ...)`, its *safe-scope* from the narrowest of the following scopes: - The *caller-scope* -- The *safe-to-escape* of all argument expressions (including the receiver) +- The *safe-scope* of all argument expressions (including the receiver) A property invocation (either `get` or `set`) is treated as a method invocation of the underlying method by the above rules. ### §ref-span-safety-a-value Values -A value's *ref-safe-to-escape-scope* is the nearest enclosing scope. +A value's *ref-safe-scope* is the nearest enclosing scope. > *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. ### §ref-span-safety-stackalloc stackalloc -The *safe-to-escape-scope* for a value `c` resulting from a `stackalloc` expression is the current method. +The *safe-scope* for a value `c` resulting from a `stackalloc` expression is the current method. #### §ref-span-safety-constructor-invocations Constructor invocations A `new` expression that invokes a constructor obeys the same rules as a method invocation (§ref-span-safety-method-invocation) that is considered to return the type being constructed. -In addition, for a value `c` that is the result of a `new` expression, the *safe-to-escape-scope* is no wider than the smallest of the *safe-to-escape-scopes* of all arguments and operands of the object initializer expressions, recursively, if any initializer is present. +In addition, for a value `c` that is the result of a `new` expression, the *safe-scope* is no wider than the smallest of the *safe-to-escape-scopes* of all arguments and operands of the object initializer expressions, recursively, if any initializer is present. ### §ref-span-safety-default-expressions Default expressions -The *safe-to-escape-scope* of a `default` expression is the *caller-scope*. +The *safe-scope* of a `default` expression is the *caller-scope*. ### §ref-span-safety-limitations Limitations on reference variables @@ -1119,9 +1123,9 @@ The *safe-to-escape-scope* of a `default` expression is the *caller-scope*. - A `ref struct` type may not be declared to implement any interface - No instance method declared in `object` or in `System.ValueType` but not overridden in a `ref struct` type may be called with a receiver of that `ref struct` type. - No instance method of a `ref struct` type may be captured by method conversion to a delegate type. -- For a ref reassignment `ref e1 = ref e2`, the *ref-safe-to-escape-scope* of `e2` must be at least as wide a scope as the *ref-safe-to-escape-scope* of `e1`. -- For a ref return statement `return ref e1`, the *ref-safe-to-escape-scope* of `e1` must be the *caller-scope*. In other words, `e1` must be *safe-to-return*. +- For a ref reassignment `ref e1 = ref e2`, the *ref-safe-scope* of `e2` must be at least as wide a scope as the *ref-safe-scope* of `e1`. +- For a ref return statement `return ref e1`, the *ref-safe-scope* of `e1` must be the *caller-scope*. In other words, `e1` must be *safe-to-return*. - For a return statement `return e1`, the *safe-to-escape-scope* of `e1` must be the *caller-scope*. This is always true when `e1` is not a `ref struct`. -- For an assignment `e1 = e2`, if the type of `e1` is a `ref struct` type, then the *safe-to-escape-scope* of `e2` must be at least as wide a scope as the *safe-to-escape-scope* of `e1`. -- For a method invocation if there is a `ref` or `out` argument of a `ref struct` type (including the receiver), with *safe-to-escape-scope* E1, then no argument (including the receiver) may have a narrower *safe-to-escape-scope* than E1. +- For an assignment `e1 = e2`, if the type of `e1` is a `ref struct` type, then the *safe-scope* of `e2` must be at least as wide a scope as the *safe-scope* of `e1`. +- For a method invocation if there is a `ref` or `out` argument of a `ref struct` type (including the receiver), with *safe-to-escape-scope* E1, then no argument (including the receiver) may have a narrower *safe-scope* than E1. - A local function or anonymous function may not refer to a local or parameter of `ref struct` type declared in an enclosing scope. From b9ccc6d0f017800520b2e88bafddd30a249e1878 Mon Sep 17 00:00:00 2001 From: Bill Wagner Date: Wed, 22 Mar 2023 14:32:15 -0400 Subject: [PATCH 11/26] add examples --- standard/variables.md | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/standard/variables.md b/standard/variables.md index 83838711b..f7864da24 100644 --- a/standard/variables.md +++ b/standard/variables.md @@ -1014,6 +1014,42 @@ These values form a nesting relationship from narrowest (*block*) to widest (*ca For any variable, the *safe_scope* of that variable is the scope where its value may be copied. A variable whose type is not a `ref struct` type is *safe-to-return* from the entire enclosing method. Its *safe_scope* is *caller_method*. Otherwise the following rules apply. +> *Example*: The following code shows examples of the different *safe_scope* and *ref_safe_scope* values. The declarations show values of *safe_scope* for a referent to be the initializing expression for a `ref` variable. The examples show the values of *ref_safe_scope* for a reference return: +> +> ```csharp +> public class C +> { +> private int[] arr = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; +> +> public ref int M1(ref int rv) +> { +> return ref rv; // rv is safe to ref return +> } +> +> public ref int M2(int v) +> { +> return ref v; // error: v isn't safe to ref return +> } +> +> public ref int M3(ref int rv) +> { +> int v = 5; +> +> return ref arr[v]; // arr[v] is safe to ref return +> } +> +> public void M4(int p) +> { +> int v = 6; +> ref int rv = ref p; // safe scope of rv is block, ref safe scope of p is method +> ref int rv2 = ref v; // safe scope of rv2 is block, ref safe scope of v is block +> ref int rv3 = ref arr[v]; // safe scope of rv3 is block, ref safe scope of arr[v] is caller method +> } +> } +> ``` +> +> *end example.* + ### §ref-span-safety-locals Local variable safe scope For a local variable `v`: From 67bba5ff8f10219249e209ddcee2b922d284e7c1 Mon Sep 17 00:00:00 2001 From: Bill Wagner Date: Mon, 27 Mar 2023 12:32:46 -0400 Subject: [PATCH 12/26] respond to feedback. --- standard/variables.md | 122 +++++++++++++++++++++--------------------- 1 file changed, 61 insertions(+), 61 deletions(-) diff --git a/standard/variables.md b/standard/variables.md index f7864da24..05c92e136 100644 --- a/standard/variables.md +++ b/standard/variables.md @@ -994,13 +994,13 @@ Reads and writes of the following data types shall be atomic: `bool`, `char`, `b A *reference variable* is a variable that refers to another variable, called the *referent*. Therefore, a *reference variable* does not store its value. Instead, a *reference variable* refers to its *referent*. Thus, the value of a reference variable is always the same as its referent variable. A *reference variable* is a local variable declared with the `ref` modifier, or an instance of a `ref struct` type. -A `ref struct` may include `ref struct` or ref-like fields. A ref-like field refers to the same storage as its initializing variable. Its storage size in the ref struct is the same storage size of a reference field. Unlike a reference field, a ref-like field may refer to a `struct` whose storage may be on the execution stack. Ref struct types include `Span`, `ReadOnlySpan`, and other types that include an unmanaged pointer as a member. +A `ref struct` may include `ref struct` or ref-like fields. A *ref-like field* refers to the same storage as its initializing variable. Its storage size in the ref struct is the same storage size of a reference field. Unlike a reference field, a ref-like field may refer to a `struct` whose storage may be on the execution stack. Ref struct types include `Span`, `ReadOnlySpan`, and other types that include an unmanaged pointer as a member. A *reference return* is the expression returned by reference from a method whose return type includes the `ref` or `ref readonly` modifiers (§14.6.1). The variable expression of the *reference return* is the *referent* of the *reference return*. -All reference variables obey safety rules that ensure the valid scope of the reference variable is not greater than the valid scope of its referent. +### §ref-span-safety-escape-scopes Ref safe scopes -### §ref-span-safety-escape-scopes Safe scopes +All reference variables obey safety rules that ensure the scope of the reference variable is not greater than the *ref safe scope* of its referent. For any variable, the *ref_safe_scope* of that variable is the scope where a *variable_reference* (§9.5) to that variable is valid. The *referent* of a reference variable must have a *ref_safe_scope* that is at least as wide as the scope where the reference variable is valid. @@ -1010,9 +1010,9 @@ There are three valid *ref_safe_scope*s: - *method*: A value parameter on a method declaration, including the implicit `this` parameter is valid in the entire method. The fields of a `struct` type are valid in the entire method. A variable with *ref_safe_scope* of *method* is a valid referent only if the reference variable is declared in the same method. - *caller_scope*: Member fields of a `class` type and `ref` parameters have *ref_safe_scope* of *caller_scope*. A variable with *ref_safe_scope* of *caller_scope* can be the referent of a reference return. A variable that can be referent of a *reference-return* is *safe-to-ref-return*. -These values form a nesting relationship from narrowest (*block*) to widest (*caller-scope*). +These values form a nesting relationship from narrowest (*block*) to widest (*caller_scope*). -For any variable, the *safe_scope* of that variable is the scope where its value may be copied. A variable whose type is not a `ref struct` type is *safe-to-return* from the entire enclosing method. Its *safe_scope* is *caller_method*. Otherwise the following rules apply. +For any variable, the *safe_scope* of that variable is the scope where its value may be copied. A variable whose type is not a `ref struct` type is *safe_to_return* from the entire enclosing method. Its *safe_scope* is *caller_scope*. Otherwise the following rules apply. > *Example*: The following code shows examples of the different *safe_scope* and *ref_safe_scope* values. The declarations show values of *safe_scope* for a referent to be the initializing expression for a `ref` variable. The examples show the values of *ref_safe_scope* for a reference return: > @@ -1021,88 +1021,88 @@ For any variable, the *safe_scope* of that variable is the scope where its value > { > private int[] arr = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; > -> public ref int M1(ref int rv) +> public ref int M1(ref int r1) > { -> return ref rv; // rv is safe to ref return +> return ref r1; // r1 is safe to ref return > } > -> public ref int M2(int v) +> public ref int M2(int v1) > { -> return ref v; // error: v isn't safe to ref return +> return ref v1; // error: v1 isn't safe to ref return > } > -> public ref int M3(ref int rv) +> public ref int M3() > { -> int v = 5; +> int v2 = 5; > -> return ref arr[v]; // arr[v] is safe to ref return +> return ref arr[v2]; // arr[v2] is safe to ref return > } > > public void M4(int p) > { -> int v = 6; -> ref int rv = ref p; // safe scope of rv is block, ref safe scope of p is method -> ref int rv2 = ref v; // safe scope of rv2 is block, ref safe scope of v is block -> ref int rv3 = ref arr[v]; // safe scope of rv3 is block, ref safe scope of arr[v] is caller method +> int v3 = 6; +> ref int r2 = ref p; // safe scope of r2 is block, ref safe scope of p is method +> ref int r3 = ref v3; // safe scope of r3 is block, ref safe scope of v3 is block +> ref int r4 = ref arr[v3]; // safe scope of r4 is block, ref safe scope of arr[v3] is caller method > } > } > ``` > > *end example.* -### §ref-span-safety-locals Local variable safe scope +#### §ref-span-safety-locals Local variable ref safe scope For a local variable `v`: -- If `v` is a `ref` variable, its *ref-safe-scope* is taken from the *ref-safe-scope* of its initializing expression. -- Otherwise its *ref-safe-scope* is the scope in which it was declared. +- If `v` is a `ref` variable, its *ref_safe_scope* is taken from the *ref_safe_scope* of its initializing expression. +- Otherwise its *ref_safe_scope* is the scope in which it was declared. -The *safe-scope* of an expression that designates the use of a local variable is determined as follows: +The *safe_scope* of an expression that designates the use of a local variable is determined as follows: -- A value whose type is not a `ref struct` type is *safe-to-return* from the entire enclosing method. Its *safe-scope* is the *caller-scope*. -- If the variable is an iteration variable of a `foreach` loop, then the variable's *safe-scope* is the same as the *safe-scope* of the `foreach` loop's expression. -- A local of `ref struct` type which is uninitialized at the point of declaration is *safe-to-return* from the entire enclosing method. Its *safe-scope* is the *calling method*. -- Otherwise the variable's type is a `ref struct` type, and the variable's declaration has an initializer. The variable's *safe-scope* is the same as the *ref-safe-scope* of its initializer. +- A value whose type is not a `ref struct` type is *safe_to_return* from the entire enclosing method. Its *safe_scope* is the *caller_scope*. +- If the variable is an iteration variable of a `foreach` loop, then the variable's *safe_scope* is the same as the *safe_scope* of the `foreach` loop's expression. +- A local of `ref struct` type which is uninitialized at the point of declaration is *safe_to_return* from the entire enclosing method. Its *safe_scope* is the *calling_method*. +- Otherwise the variable's type is a `ref struct` type, and the variable's declaration has an initializer. The variable's *safe_scope* is the same as the *ref_safe_scope* of its initializer. -### §ref-span-safety-parameters Parameter escape scope +#### §ref-span-safety-parameters Parameter ref safe scope For a formal parameter `p`: -- If `p` is a `ref`, or `in` parameter, its *ref-safe-scope* is the *caller-scope*. It is *safe-to-return* by ref. If `p` is an `in` parameter, it can't be returned as a writable `ref` but can be returned as `ref readonly`. -- If `p` is an `out` parameter, its *ref-safe-to-escape-scope* is the *current-method*. It isn't *safe-to-return* by ref. -- Otherwise, if `p` is the `this` parameter of a struct type, its *ref-safe-to-escape-scope* is the *current-method*. -- Otherwise, the parameter is a value parameter, and it is *ref-safe-to-escape-scope* is the *current-method* +- If `p` is a `ref`, or `in` parameter, its *ref_safe_scope* is the *caller_scope*. It is *safe_to_return* by ref. If `p` is an `in` parameter, it can't be returned as a writable `ref` but can be returned as `ref readonly`. +- If `p` is an `out` parameter, its *ref_safe_scope* is the *current_method*. It isn't *safe_to_return* by ref. +- Otherwise, if `p` is the `this` parameter of a struct type, its *ref_safe_scope* is the *current_method*. +- Otherwise, the parameter is a value parameter, and it is *ref_safe_scope* is the *current_method* -For an expression that designates the value of a formal parameter `p`, its *safe-scope* is the calling method. This applies to the `this` parameter. +For an expression that designates the value of a formal parameter `p`, its *safe_scope* is the calling method. This applies to the `this` parameter. -### §ref-span-safety-field-reference Field reference escape scope +#### §ref-span-safety-field-reference Field ref safe scope For a variable designating a reference to a field, `e.F`: -- If `e` is of a reference type, its *ref-safe-scope* is the *caller-scope*. -- Otherwise, if `e` is of a value type, its *ref-safe-scope* is taken from the *ref-safe-scope* of `e`. +- If `e` is of a reference type, its *ref_safe_scope* is the *caller_scope*. +- Otherwise, if `e` is of a value type, its *ref_safe_scope* is taken from the *ref_safe_scope* of `e`. -For an expression that designates a reference to a field, `e.F`, its a *safe-scope* is the same as the *safe-scope* of `e`. +For an expression that designates a reference to a field, `e.F`, its a *safe_scope* is the same as the *safe_scope* of `e`. -### §ref-span-safety-operators Operators +#### §ref-span-safety-operators Operators The application of a user-defined operator is treated as a method invocation. -For an operator that yields a value, such as `e1 + e2` or `c ? e1 : e2`, the *safe-scope* of the result is the narrowest scope among the *safe-scopes* of the operands of the operator. As a consequence, for a unary operator that yields an value, such as `+e`, the *safe-scope* of the result is the *safe-scope* of the operand. +For an operator that yields a value, such as `e1 + e2` or `c ? e1 : e2`, the *safe_scope* of the result is the narrowest scope among the *safe_scopes* of the operands of the operator. As a consequence, for a unary operator that yields an value, such as `+e`, the *safe_scope* of the result is the *safe_scope* of the operand. For an operator that yields a variable, such as `c ? ref e1 : ref e2`: -- The *ref-safe-scope* of the result is the narrowest scope among the *ref-safe-scopes* of the operands of the operator. -- The *safe-scope* of the operands must agree, and that is the *safe-scope* of the resulting variable. +- The *ref_safe_scope* of the result is the narrowest scope among the *ref_safe_scopes* of the operands of the operator. +- The *safe_scope* of the operands must agree, and that is the *safe_scope* of the resulting variable. -### §ref-span-safety-method-invocation Method invocation +#### §ref-span-safety-method-invocation Method invocation -For a variable `c` resulting from a ref-returning method invocation, `ref e1.M(e2, ...)`, its *ref-safe-scope* is the narrowest of the following scopes: +For a variable `c` resulting from a ref-returning method invocation, `ref e1.M(e2, ...)`, its *ref_safe_scope* is the narrowest of the following scopes: -- The *caller-scope*. -- The *ref-safe-scope* of all `ref` and `out` argument expressions (excluding the receiver). -- For each `in` parameter of the method, if there is a corresponding expression that is an variable, its *ref-safe-scope*, otherwise the nearest enclosing scope -- The *safe-scope* of all argument expressions (including the receiver) +- The *caller_scope*. +- The *ref_safe_scope* of all `ref` and `out` argument expressions (excluding the receiver). +- For each `in` parameter of the method, if there is a corresponding expression that is an variable, its *ref_safe_scope*, otherwise the nearest enclosing scope +- The *safe_scope* of all argument expressions (including the receiver) > *Example*: the last bullet is necessary to handle code such as > @@ -1119,36 +1119,36 @@ For a variable `c` resulting from a ref-returning method invocation, `ref e1.M(e > > *end example* -For a value resulting from a method invocation `e1.M(e2, ...)`, its *safe-scope* from the narrowest of the following scopes: +For a value resulting from a method invocation `e1.M(e2, ...)`, its *safe_scope* from the narrowest of the following scopes: -- The *caller-scope* -- The *safe-scope* of all argument expressions (including the receiver) +- The *caller_scope* +- The *safe_scope* of all argument expressions (including the receiver) A property invocation (either `get` or `set`) is treated as a method invocation of the underlying method by the above rules. -### §ref-span-safety-a-value Values +#### §ref-span-safety-a-value Values -A value's *ref-safe-scope* is the nearest enclosing scope. +A value's *ref_safe_scope* is the nearest enclosing scope. > *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. -### §ref-span-safety-stackalloc stackalloc +#### §ref-span-safety-stackalloc stackalloc -The *safe-scope* for a value `c` resulting from a `stackalloc` expression is the current method. +The *safe_scope* for a value `c` resulting from a `stackalloc` expression is the current method. #### §ref-span-safety-constructor-invocations Constructor invocations A `new` expression that invokes a constructor obeys the same rules as a method invocation (§ref-span-safety-method-invocation) that is considered to return the type being constructed. -In addition, for a value `c` that is the result of a `new` expression, the *safe-scope* is no wider than the smallest of the *safe-to-escape-scopes* of all arguments and operands of the object initializer expressions, recursively, if any initializer is present. +In addition, for a value `c` that is the result of a `new` expression, the *safe_scope* is no wider than the smallest of the *safe-scopes* of all arguments and operands of the object initializer expressions, recursively, if any initializer is present. -### §ref-span-safety-default-expressions Default expressions +#### §ref-span-safety-default-expressions Default expressions -The *safe-scope* of a `default` expression is the *caller-scope*. +The *safe_scope* of a `default` expression is the *caller-scope*. -### §ref-span-safety-limitations Limitations on reference variables +#### §ref-span-safety-limitations Limitations on reference variables -- Neither a `ref` parameter, nor a `ref` local, nor a parameter or local of a `ref struct` type can be lifted into a lambda expression or local function. +- Neither a `ref` parameter, nor a `ref` local, nor a parameter or local of a `ref struct` type can be captured by lambda expression or local function. - Neither a `ref` parameter nor a parameter of a `ref struct` type may be an argument for an iterator method or an `async` method. - Neither a `ref` local, nor a local of a `ref struct` type may be in scope at the point of a `yield return` statement or an `await` expression. - A `ref struct` type may not be used as a type argument, or as an element type in a tuple type. @@ -1159,9 +1159,9 @@ The *safe-scope* of a `default` expression is the *caller-scope*. - A `ref struct` type may not be declared to implement any interface - No instance method declared in `object` or in `System.ValueType` but not overridden in a `ref struct` type may be called with a receiver of that `ref struct` type. - No instance method of a `ref struct` type may be captured by method conversion to a delegate type. -- For a ref reassignment `ref e1 = ref e2`, the *ref-safe-scope* of `e2` must be at least as wide a scope as the *ref-safe-scope* of `e1`. -- For a ref return statement `return ref e1`, the *ref-safe-scope* of `e1` must be the *caller-scope*. In other words, `e1` must be *safe-to-return*. -- For a return statement `return e1`, the *safe-to-escape-scope* of `e1` must be the *caller-scope*. This is always true when `e1` is not a `ref struct`. -- For an assignment `e1 = e2`, if the type of `e1` is a `ref struct` type, then the *safe-scope* of `e2` must be at least as wide a scope as the *safe-scope* of `e1`. -- For a method invocation if there is a `ref` or `out` argument of a `ref struct` type (including the receiver), with *safe-to-escape-scope* E1, then no argument (including the receiver) may have a narrower *safe-scope* than E1. +- For a ref reassignment `ref e1 = ref e2`, the *ref_safe_scope* of `e2` must be at least as wide a scope as the *ref_safe_scope* of `e1`. +- For a ref return statement `return ref e1`, the *ref_safe_scope* of `e1` must be the *caller_scope*. In other words, `e1` must be *safe_to_return*. +- For a return statement `return e1`, the *safe_scope* of `e1` must be the *caller_scope*. This is always true when `e1` is not a `ref struct`. +- For an assignment `e1 = e2`, if the type of `e1` is a `ref struct` type, then the *safe_scope* of `e2` must be at least as wide a scope as the *safe_scope* of `e1`. +- For a method invocation if there is a `ref` or `out` argument of a `ref struct` type (including the receiver), with *safe_to_escape_scope* E1, then no argument (including the receiver) may have a narrower *safe_scope* than E1. - A local function or anonymous function may not refer to a local or parameter of `ref struct` type declared in an enclosing scope. From 520d34d5c0ed51083af39d747825a8c1eee8dd58 Mon Sep 17 00:00:00 2001 From: Bill Wagner Date: Mon, 27 Mar 2023 12:33:34 -0400 Subject: [PATCH 13/26] Apply suggestions from code review Co-authored-by: Jon Skeet --- standard/variables.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/standard/variables.md b/standard/variables.md index 05c92e136..f8793ba76 100644 --- a/standard/variables.md +++ b/standard/variables.md @@ -1006,9 +1006,9 @@ For any variable, the *ref_safe_scope* of that variable is the scope where a *va There are three valid *ref_safe_scope*s: -- *block*: A variable declared in a block is valid in that block in which it is declared. This includes all local variables. A local variable declared in a method is valid in 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, or a nested block. +- *block*: A variable declared in a block is valid in the block in which it is declared. This includes all local variables. A local variable declared in a method is valid in 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, or a nested block. - *method*: A value parameter on a method declaration, including the implicit `this` parameter is valid in the entire method. The fields of a `struct` type are valid in the entire method. A variable with *ref_safe_scope* of *method* is a valid referent only if the reference variable is declared in the same method. -- *caller_scope*: Member fields of a `class` type and `ref` parameters have *ref_safe_scope* of *caller_scope*. A variable with *ref_safe_scope* of *caller_scope* can be the referent of a reference return. A variable that can be referent of a *reference-return* is *safe-to-ref-return*. +- *caller_scope*: Member fields of a `class` type and `ref` parameters have *ref_safe_scope* of *caller_scope*. A variable with *ref_safe_scope* of *caller_scope* can be the referent of a reference return. A variable that can be the referent of a *reference-return* is *safe-to-ref-return*. These values form a nesting relationship from narrowest (*block*) to widest (*caller_scope*). From 8bf4c26a06559ea737aa4d20518273e9d5af2f01 Mon Sep 17 00:00:00 2001 From: Bill Wagner Date: Wed, 12 Apr 2023 11:39:29 -0400 Subject: [PATCH 14/26] 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*. --- standard/variables.md | 67 ++++++++++++++++++++++++++++++++++++++----- 1 file changed, 60 insertions(+), 7 deletions(-) diff --git a/standard/variables.md b/standard/variables.md index f8793ba76..7814e54c7 100644 --- a/standard/variables.md +++ b/standard/variables.md @@ -832,7 +832,7 @@ For an expression *expr* of the form: ``` - The definite-assignment state of *v* before *expr_cond* is the same as the state of *v* before *expr*. -- The definite-assignment state of *v* before *expr_true* is definitely assigned if the state of *v* after *expr_cond* is definitely assigned or “definitely assigned after true expression”. +- The definite-assignment state of *v* before *expr_true* is definitely assigned if the state of *v* after *expr_cond* is definitely assigned or “definitely assigned after true expression”1057. - The definite-assignment state of *v* before *expr_false* is definitely assigned if the state of *v* after *expr_cond* is definitely assigned or “definitely assigned after false expression”. - The definite-assignment state of *v* after *expr* is determined by: - If *expr_cond* is a constant expression ([§12.23](expressions.md#1223-constant-expressions)) with value `true` then the state of *v* after *expr* is the same as the state of *v* after *expr_true*. @@ -994,10 +994,63 @@ Reads and writes of the following data types shall be atomic: `bool`, `char`, `b A *reference variable* is a variable that refers to another variable, called the *referent*. Therefore, a *reference variable* does not store its value. Instead, a *reference variable* refers to its *referent*. Thus, the value of a reference variable is always the same as its referent variable. A *reference variable* is a local variable declared with the `ref` modifier, or an instance of a `ref struct` type. +> *Example:* The following example demonstrates a local *reference variable* whose *referent* is an element of an array: +> +> ```csharp +> public class C +> { +> +> public void M() +> { +> int[] arr = new int[10]; +> // element is a reference variable that refers to arr[5] +> ref int element = ref arr[5]; +> element += 5; // arr[5] has been incremented by 5 +> } +> } +> ``` +> +> *end example* + A `ref struct` may include `ref struct` or ref-like fields. A *ref-like field* refers to the same storage as its initializing variable. Its storage size in the ref struct is the same storage size of a reference field. Unlike a reference field, a ref-like field may refer to a `struct` whose storage may be on the execution stack. Ref struct types include `Span`, `ReadOnlySpan`, and other types that include an unmanaged pointer as a member. +> *Example:* The following example demonstrates a `ReadOnlySpan` *reference variable* whose *referent* is the characters in a string. `ReadOnlySpan` is a `ref struct`. One of its internal fields is a *ref-like field* that refers to the character array in the `string` referent. Therefore, `chars` is a *reference variable*: +> +> ```csharp +> string Reverse(string s) +> { +> ReadOnlySpan chars = s.AsSpan(); +> var reversed = new char[chars.Length]; +> for (int i = 0; i < chars.Length; i++) +> { +> reversed[chars.Length-i-1] = chars[i]; +> } +> return new string(reversed); +> } +> ``` +> +> *end example* + A *reference return* is the expression returned by reference from a method whose return type includes the `ref` or `ref readonly` modifiers (§14.6.1). The variable expression of the *reference return* is the *referent* of the *reference return*. +> *Example:* The following example demonstrates a =*reference return* whose *referent* is an element of an array field: +> +> ```csharp +> public class C +> { +> private int[] arr = new int[10]; +> +> public readonly ref int M() +> { +> // element is a reference variable that refers to arr[5] +> ref int element = ref arr[5]; +> return ref element; // return reference to arr[5]; +> } +> } +> ``` +> +> *end example* + ### §ref-span-safety-escape-scopes Ref safe scopes All reference variables obey safety rules that ensure the scope of the reference variable is not greater than the *ref safe scope* of its referent. @@ -1006,8 +1059,8 @@ For any variable, the *ref_safe_scope* of that variable is the scope where a *va There are three valid *ref_safe_scope*s: -- *block*: A variable declared in a block is valid in the block in which it is declared. This includes all local variables. A local variable declared in a method is valid in 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, or a nested block. -- *method*: A value parameter on a method declaration, including the implicit `this` parameter is valid in the entire method. The fields of a `struct` type are valid in the entire method. A variable with *ref_safe_scope* of *method* is a valid referent only if the reference variable is declared in the same method. +- *block*: A *variable_reference* to a local variable declared in a block is valid in the block in which it is declared. The *ref_safe_scope* of a local variable is *block*. A local variable declared in a method has a *ref_safe_scope* 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, or a nested block. +- *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_scope* of the fields of a `struct` type is *function_member*. A variable with *ref_safe_scope* of *function_member* is a valid referent only if the reference variable is declared in the same function member. - *caller_scope*: Member fields of a `class` type and `ref` parameters have *ref_safe_scope* of *caller_scope*. A variable with *ref_safe_scope* of *caller_scope* can be the referent of a reference return. A variable that can be the referent of a *reference-return* is *safe-to-ref-return*. These values form a nesting relationship from narrowest (*block*) to widest (*caller_scope*). @@ -1069,9 +1122,9 @@ The *safe_scope* of an expression that designates the use of a local variable is For a formal parameter `p`: - If `p` is a `ref`, or `in` parameter, its *ref_safe_scope* is the *caller_scope*. It is *safe_to_return* by ref. If `p` is an `in` parameter, it can't be returned as a writable `ref` but can be returned as `ref readonly`. -- If `p` is an `out` parameter, its *ref_safe_scope* is the *current_method*. It isn't *safe_to_return* by ref. -- Otherwise, if `p` is the `this` parameter of a struct type, its *ref_safe_scope* is the *current_method*. -- Otherwise, the parameter is a value parameter, and it is *ref_safe_scope* is the *current_method* +- If `p` is an `out` parameter, its *ref_safe_scope* is the *function_member*. It isn't *safe_to_return* by ref. +- Otherwise, if `p` is the `this` parameter of a struct type, its *ref_safe_scope* is the *function_member*. +- Otherwise, the parameter is a value parameter, and it is *ref_safe_scope* is the *function_member*. For an expression that designates the value of a formal parameter `p`, its *safe_scope* is the calling method. This applies to the `this` parameter. @@ -1140,7 +1193,7 @@ The *safe_scope* for a value `c` resulting from a `stackalloc` expression is the A `new` expression that invokes a constructor obeys the same rules as a method invocation (§ref-span-safety-method-invocation) that is considered to return the type being constructed. -In addition, for a value `c` that is the result of a `new` expression, the *safe_scope* is no wider than the smallest of the *safe-scopes* of all arguments and operands of the object initializer expressions, recursively, if any initializer is present. +In addition, for a value `c` that is the result of a `new` expression, the *safe_scope* is no wider than the smallest of the *safe-scope*s of all arguments and operands of the object initializer expressions, recursively, if any initializer is present. #### §ref-span-safety-default-expressions Default expressions From 0bf617e6388ef9dc31c33bf946cacaea0a1292c4 Mon Sep 17 00:00:00 2001 From: Bill Wagner Date: Wed, 12 Apr 2023 17:26:20 -0400 Subject: [PATCH 15/26] Remove *safe_scope* rules Once I push these, I'll add notes about which rules must be added to #601 --- standard/variables.md | 88 +++++++++++++------------------------------ 1 file changed, 27 insertions(+), 61 deletions(-) diff --git a/standard/variables.md b/standard/variables.md index 7814e54c7..399067904 100644 --- a/standard/variables.md +++ b/standard/variables.md @@ -992,7 +992,7 @@ Reads and writes of the following data types shall be atomic: `bool`, `char`, `b ### §ref-span-safety-general General -A *reference variable* is a variable that refers to another variable, called the *referent*. Therefore, a *reference variable* does not store its value. Instead, a *reference variable* refers to its *referent*. Thus, the value of a reference variable is always the same as its referent variable. A *reference variable* is a local variable declared with the `ref` modifier, or an instance of a `ref struct` type. +A *reference variable* is a variable that refers to another variable, called the *referent*. Therefore, a *reference variable* does not store its value. Instead, a *reference variable* refers to its *referent*. Thus, the value of a reference variable is always the same as its referent variable. A *reference variable* is a local variable declared with the `ref` modifier. > *Example:* The following example demonstrates a local *reference variable* whose *referent* is an element of an array: > @@ -1012,40 +1012,40 @@ A *reference variable* is a variable that refers to another variable, called the > > *end example* -A `ref struct` may include `ref struct` or ref-like fields. A *ref-like field* refers to the same storage as its initializing variable. Its storage size in the ref struct is the same storage size of a reference field. Unlike a reference field, a ref-like field may refer to a `struct` whose storage may be on the execution stack. Ref struct types include `Span`, `ReadOnlySpan`, and other types that include an unmanaged pointer as a member. +A *reference return* is the expression returned by reference from a method whose return type includes the `ref` or `ref readonly` modifiers (§15.6.1). The variable expression of the *reference return* is the *referent* of the *reference return*. -> *Example:* The following example demonstrates a `ReadOnlySpan` *reference variable* whose *referent* is the characters in a string. `ReadOnlySpan` is a `ref struct`. One of its internal fields is a *ref-like field* that refers to the character array in the `string` referent. Therefore, `chars` is a *reference variable*: +> *Example:* The following example demonstrates a =*reference return* whose *referent* is an element of an array field: > > ```csharp -> string Reverse(string s) +> public class C > { -> ReadOnlySpan chars = s.AsSpan(); -> var reversed = new char[chars.Length]; -> for (int i = 0; i < chars.Length; i++) +> private int[] arr = new int[10]; +> +> public readonly ref int M() > { -> reversed[chars.Length-i-1] = chars[i]; -> } -> return new string(reversed); +> // element is a reference variable that refers to arr[5] +> ref int element = ref arr[5]; +> return ref element; // return reference to arr[5]; +> } > } > ``` > > *end example* -A *reference return* is the expression returned by reference from a method whose return type includes the `ref` or `ref readonly` modifiers (§14.6.1). The variable expression of the *reference return* is the *referent* of the *reference return*. +A `ref struct` may include `ref struct` or ref-like fields. A *ref-like field* refers to the same storage as its initializing expression. The initializing expression of the *ref_like field* is its *referent*. Unlike a reference field, a ref-like field may refer to a `struct` whose storage may be on the execution stack. Ref struct types include `Span`, `ReadOnlySpan`, and other types that include an unmanaged pointer as a member. -> *Example:* The following example demonstrates a =*reference return* whose *referent* is an element of an array field: +> *Example:* The following example demonstrates a `ReadOnlySpan` *reference variable* whose *referent* is the characters in a string. `ReadOnlySpan` is a `ref struct`. One of its internal fields is a *ref-like field* that refers to the character array in the `string` referent. Therefore, `chars` is a *reference variable*: > > ```csharp -> public class C +> string Reverse(string s) > { -> private int[] arr = new int[10]; -> -> public readonly ref int M() +> ReadOnlySpan chars = s.AsSpan(); +> var reversed = new char[chars.Length]; +> for (int i = 0; i < chars.Length; i++) > { -> // element is a reference variable that refers to arr[5] -> ref int element = ref arr[5]; -> return ref element; // return reference to arr[5]; -> } +> reversed[chars.Length-i-1] = chars[i]; +> } +> return new string(reversed); > } > ``` > @@ -1053,9 +1053,9 @@ A *reference return* is the expression returned by reference from a method whose ### §ref-span-safety-escape-scopes Ref safe scopes -All reference variables obey safety rules that ensure the scope of the reference variable is not greater than the *ref safe scope* of its referent. +All reference variables obey safety rules that ensure the scope of the reference variable is not greater than the *ref_safe_scope* of its referent. -For any variable, the *ref_safe_scope* of that variable is the scope where a *variable_reference* (§9.5) to that variable is valid. The *referent* of a reference variable must have a *ref_safe_scope* that is at least as wide as the scope where the reference variable is valid. +For any variable, the *ref_safe_scope* of that variable is the scope where a *variable_reference* (§9.5) to that variable is valid. The *referent* of a reference variable must have a *ref_safe_scope* that is at least as wide as the scope of the reference variable. A `ref struct` instance may not be copied by value beyond the value *ref_safe_scope* of the initializing expression(s) of any *ref-like fields*. There are three valid *ref_safe_scope*s: @@ -1065,9 +1065,7 @@ There are three valid *ref_safe_scope*s: These values form a nesting relationship from narrowest (*block*) to widest (*caller_scope*). -For any variable, the *safe_scope* of that variable is the scope where its value may be copied. A variable whose type is not a `ref struct` type is *safe_to_return* from the entire enclosing method. Its *safe_scope* is *caller_scope*. Otherwise the following rules apply. - -> *Example*: The following code shows examples of the different *safe_scope* and *ref_safe_scope* values. The declarations show values of *safe_scope* for a referent to be the initializing expression for a `ref` variable. The examples show the values of *ref_safe_scope* for a reference return: +> *Example*: The following code shows examples of the different *ref_safe_scope* values. The declarations show values of *ref_safe_scope* for a referent to be the initializing expression for a `ref` variable. The examples show the values of *ref_safe_scope* for a reference return: > > ```csharp > public class C @@ -1094,9 +1092,9 @@ For any variable, the *safe_scope* of that variable is the scope where its value > public void M4(int p) > { > int v3 = 6; -> ref int r2 = ref p; // safe scope of r2 is block, ref safe scope of p is method -> ref int r3 = ref v3; // safe scope of r3 is block, ref safe scope of v3 is block -> ref int r4 = ref arr[v3]; // safe scope of r4 is block, ref safe scope of arr[v3] is caller method +> ref int r2 = ref p; // scope of r2 is block, ref safe scope of p is method +> ref int r3 = ref v3; // scope of r3 is block, ref safe scope of v3 is block +> ref int r4 = ref arr[v3]; // scope of r4 is block, ref safe scope of arr[v3] is caller method > } > } > ``` @@ -1110,13 +1108,6 @@ For a local variable `v`: - If `v` is a `ref` variable, its *ref_safe_scope* is taken from the *ref_safe_scope* of its initializing expression. - Otherwise its *ref_safe_scope* is the scope in which it was declared. -The *safe_scope* of an expression that designates the use of a local variable is determined as follows: - -- A value whose type is not a `ref struct` type is *safe_to_return* from the entire enclosing method. Its *safe_scope* is the *caller_scope*. -- If the variable is an iteration variable of a `foreach` loop, then the variable's *safe_scope* is the same as the *safe_scope* of the `foreach` loop's expression. -- A local of `ref struct` type which is uninitialized at the point of declaration is *safe_to_return* from the entire enclosing method. Its *safe_scope* is the *calling_method*. -- Otherwise the variable's type is a `ref struct` type, and the variable's declaration has an initializer. The variable's *safe_scope* is the same as the *ref_safe_scope* of its initializer. - #### §ref-span-safety-parameters Parameter ref safe scope For a formal parameter `p`: @@ -1126,8 +1117,6 @@ For a formal parameter `p`: - Otherwise, if `p` is the `this` parameter of a struct type, its *ref_safe_scope* is the *function_member*. - Otherwise, the parameter is a value parameter, and it is *ref_safe_scope* is the *function_member*. -For an expression that designates the value of a formal parameter `p`, its *safe_scope* is the calling method. This applies to the `this` parameter. - #### §ref-span-safety-field-reference Field ref safe scope For a variable designating a reference to a field, `e.F`: @@ -1135,18 +1124,13 @@ For a variable designating a reference to a field, `e.F`: - If `e` is of a reference type, its *ref_safe_scope* is the *caller_scope*. - Otherwise, if `e` is of a value type, its *ref_safe_scope* is taken from the *ref_safe_scope* of `e`. -For an expression that designates a reference to a field, `e.F`, its a *safe_scope* is the same as the *safe_scope* of `e`. - #### §ref-span-safety-operators Operators The application of a user-defined operator is treated as a method invocation. -For an operator that yields a value, such as `e1 + e2` or `c ? e1 : e2`, the *safe_scope* of the result is the narrowest scope among the *safe_scopes* of the operands of the operator. As a consequence, for a unary operator that yields an value, such as `+e`, the *safe_scope* of the result is the *safe_scope* of the operand. - For an operator that yields a variable, such as `c ? ref e1 : ref e2`: - The *ref_safe_scope* of the result is the narrowest scope among the *ref_safe_scopes* of the operands of the operator. -- The *safe_scope* of the operands must agree, and that is the *safe_scope* of the resulting variable. #### §ref-span-safety-method-invocation Method invocation @@ -1155,7 +1139,7 @@ For a variable `c` resulting from a ref-returning method invocation, `ref e1.M(e - The *caller_scope*. - The *ref_safe_scope* of all `ref` and `out` argument expressions (excluding the receiver). - For each `in` parameter of the method, if there is a corresponding expression that is an variable, its *ref_safe_scope*, otherwise the nearest enclosing scope -- The *safe_scope* of all argument expressions (including the receiver) +- The scope of all argument expressions (including the receiver). > *Example*: the last bullet is necessary to handle code such as > @@ -1172,11 +1156,6 @@ For a variable `c` resulting from a ref-returning method invocation, `ref e1.M(e > > *end example* -For a value resulting from a method invocation `e1.M(e2, ...)`, its *safe_scope* from the narrowest of the following scopes: - -- The *caller_scope* -- The *safe_scope* of all argument expressions (including the receiver) - A property invocation (either `get` or `set`) is treated as a method invocation of the underlying method by the above rules. #### §ref-span-safety-a-value Values @@ -1185,20 +1164,10 @@ A value's *ref_safe_scope* is the nearest enclosing scope. > *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. -#### §ref-span-safety-stackalloc stackalloc - -The *safe_scope* for a value `c` resulting from a `stackalloc` expression is the current method. - #### §ref-span-safety-constructor-invocations Constructor invocations A `new` expression that invokes a constructor obeys the same rules as a method invocation (§ref-span-safety-method-invocation) that is considered to return the type being constructed. -In addition, for a value `c` that is the result of a `new` expression, the *safe_scope* is no wider than the smallest of the *safe-scope*s of all arguments and operands of the object initializer expressions, recursively, if any initializer is present. - -#### §ref-span-safety-default-expressions Default expressions - -The *safe_scope* of a `default` expression is the *caller-scope*. - #### §ref-span-safety-limitations Limitations on reference variables - Neither a `ref` parameter, nor a `ref` local, nor a parameter or local of a `ref struct` type can be captured by lambda expression or local function. @@ -1214,7 +1183,4 @@ The *safe_scope* of a `default` expression is the *caller-scope*. - No instance method of a `ref struct` type may be captured by method conversion to a delegate type. - For a ref reassignment `ref e1 = ref e2`, the *ref_safe_scope* of `e2` must be at least as wide a scope as the *ref_safe_scope* of `e1`. - For a ref return statement `return ref e1`, the *ref_safe_scope* of `e1` must be the *caller_scope*. In other words, `e1` must be *safe_to_return*. -- For a return statement `return e1`, the *safe_scope* of `e1` must be the *caller_scope*. This is always true when `e1` is not a `ref struct`. -- For an assignment `e1 = e2`, if the type of `e1` is a `ref struct` type, then the *safe_scope* of `e2` must be at least as wide a scope as the *safe_scope* of `e1`. -- For a method invocation if there is a `ref` or `out` argument of a `ref struct` type (including the receiver), with *safe_to_escape_scope* E1, then no argument (including the receiver) may have a narrower *safe_scope* than E1. - A local function or anonymous function may not refer to a local or parameter of `ref struct` type declared in an enclosing scope. From c5d16866198b72026f7978a6ea36765a2374608a Mon Sep 17 00:00:00 2001 From: Bill Wagner Date: Thu, 13 Apr 2023 09:45:06 -0400 Subject: [PATCH 16/26] Update standard/variables.md Co-authored-by: Nigel-Ecma --- standard/variables.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/standard/variables.md b/standard/variables.md index 399067904..43dcfdb61 100644 --- a/standard/variables.md +++ b/standard/variables.md @@ -832,7 +832,7 @@ For an expression *expr* of the form: ``` - The definite-assignment state of *v* before *expr_cond* is the same as the state of *v* before *expr*. -- The definite-assignment state of *v* before *expr_true* is definitely assigned if the state of *v* after *expr_cond* is definitely assigned or “definitely assigned after true expression”1057. +- The definite-assignment state of *v* before *expr_true* is definitely assigned if the state of *v* after *expr_cond* is definitely assigned or “definitely assigned after true expression”. - The definite-assignment state of *v* before *expr_false* is definitely assigned if the state of *v* after *expr_cond* is definitely assigned or “definitely assigned after false expression”. - The definite-assignment state of *v* after *expr* is determined by: - If *expr_cond* is a constant expression ([§12.23](expressions.md#1223-constant-expressions)) with value `true` then the state of *v* after *expr* is the same as the state of *v* after *expr_true*. From 75c1c0c3715685ab8b05d50b88a2fbdfc0ca5a86 Mon Sep 17 00:00:00 2001 From: Bill Wagner Date: Thu, 13 Apr 2023 16:02:16 -0400 Subject: [PATCH 17/26] Update standard/variables.md --- standard/variables.md | 1 + 1 file changed, 1 insertion(+) diff --git a/standard/variables.md b/standard/variables.md index 43dcfdb61..bbda323c9 100644 --- a/standard/variables.md +++ b/standard/variables.md @@ -1183,4 +1183,5 @@ A `new` expression that invokes a constructor obeys the same rules as a method i - No instance method of a `ref struct` type may be captured by method conversion to a delegate type. - For a ref reassignment `ref e1 = ref e2`, the *ref_safe_scope* of `e2` must be at least as wide a scope as the *ref_safe_scope* of `e1`. - For a ref return statement `return ref e1`, the *ref_safe_scope* of `e1` must be the *caller_scope*. In other words, `e1` must be *safe_to_return*. +- For a method invocation if there is a `ref` or `out` argument of a `ref struct` type (including the receiver), with *ref_safe_scope* E1, then no argument (including the receiver) may have a narrower *ref_safe_scope* than E1. - A local function or anonymous function may not refer to a local or parameter of `ref struct` type declared in an enclosing scope. From b9acc0bdab22cbca3c64cd2fc3d9cb0d66b694a2 Mon Sep 17 00:00:00 2001 From: Bill Wagner Date: Wed, 19 Apr 2023 11:18:19 -0400 Subject: [PATCH 18/26] Respond to review comments. --- standard/variables.md | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/standard/variables.md b/standard/variables.md index bbda323c9..8e7655148 100644 --- a/standard/variables.md +++ b/standard/variables.md @@ -1014,7 +1014,7 @@ A *reference variable* is a variable that refers to another variable, called the A *reference return* is the expression returned by reference from a method whose return type includes the `ref` or `ref readonly` modifiers (§15.6.1). The variable expression of the *reference return* is the *referent* of the *reference return*. -> *Example:* The following example demonstrates a =*reference return* whose *referent* is an element of an array field: +> *Example:* The following example demonstrates a *reference return* whose *referent* is an element of an array field: > > ```csharp > public class C @@ -1032,9 +1032,9 @@ A *reference return* is the expression returned by reference from a method whose > > *end example* -A `ref struct` may include `ref struct` or ref-like fields. A *ref-like field* refers to the same storage as its initializing expression. The initializing expression of the *ref_like field* is its *referent*. Unlike a reference field, a ref-like field may refer to a `struct` whose storage may be on the execution stack. Ref struct types include `Span`, `ReadOnlySpan`, and other types that include an unmanaged pointer as a member. +A `ref struct` may include `ref struct` or ref-like fields. A *ref-like field* refers to the same storage as its initializing expression. The initializing expression of the *ref-like field* is its *referent*. Unlike a reference field, a ref-like field may refer to a `struct` whose storage may be on the execution stack. Ref struct types include `Span`, `ReadOnlySpan`, and other types that include an unmanaged pointer as a member. -> *Example:* The following example demonstrates a `ReadOnlySpan` *reference variable* whose *referent* is the characters in a string. `ReadOnlySpan` is a `ref struct`. One of its internal fields is a *ref-like field* that refers to the character array in the `string` referent. Therefore, `chars` is a *reference variable*: +> *Example:* The following example demonstrates a `ReadOnlySpan` *reference variable* whose *referent* is the characters in a string. `ReadOnlySpan` is a `ref struct`. One of its internal fields is a *ref-like field* whose *referent* is the character array in the string `s`: > > ```csharp > string Reverse(string s) @@ -1055,17 +1055,17 @@ A `ref struct` may include `ref struct` or ref-like fields. A *ref-like field* r All reference variables obey safety rules that ensure the scope of the reference variable is not greater than the *ref_safe_scope* of its referent. -For any variable, the *ref_safe_scope* of that variable is the scope where a *variable_reference* (§9.5) to that variable is valid. The *referent* of a reference variable must have a *ref_safe_scope* that is at least as wide as the scope of the reference variable. A `ref struct` instance may not be copied by value beyond the value *ref_safe_scope* of the initializing expression(s) of any *ref-like fields*. +For any variable, the *ref_safe_scope* of that variable is the scope where a *variable_reference* (§9.5) to that variable is valid. The *referent* of a reference variable must have a *ref_safe_scope* that is at least as wide as the scope of the reference variable. A `ref struct` instance may not be copied by value beyond the *ref_safe_scope* of the initializing expression(s) of any *ref-like fields*. There are three valid *ref_safe_scope*s: -- *block*: A *variable_reference* to a local variable declared in a block is valid in the block in which it is declared. The *ref_safe_scope* of a local variable is *block*. A local variable declared in a method has a *ref_safe_scope* 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, or a nested block. +- *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. The *ref_safe_scope* of a local variable is *block*. A local variable declared in a method has a *ref_safe_scope* 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. - *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_scope* of the fields of a `struct` type is *function_member*. A variable with *ref_safe_scope* of *function_member* is a valid referent only if the reference variable is declared in the same function member. - *caller_scope*: Member fields of a `class` type and `ref` parameters have *ref_safe_scope* of *caller_scope*. A variable with *ref_safe_scope* of *caller_scope* can be the referent of a reference return. A variable that can be the referent of a *reference-return* is *safe-to-ref-return*. These values form a nesting relationship from narrowest (*block*) to widest (*caller_scope*). -> *Example*: The following code shows examples of the different *ref_safe_scope* values. The declarations show values of *ref_safe_scope* for a referent to be the initializing expression for a `ref` variable. The examples show the values of *ref_safe_scope* for a reference return: +> *Example*: The following code shows examples of the different *ref_safe_scope*s. The declarations show the *ref_safe_scope* for a referent to be the initializing expression for a `ref` variable. The examples show the *ref_safe_scope* for a reference return: > > ```csharp > public class C @@ -1105,24 +1105,24 @@ These values form a nesting relationship from narrowest (*block*) to widest (*ca For a local variable `v`: -- If `v` is a `ref` variable, its *ref_safe_scope* is taken from the *ref_safe_scope* of its initializing expression. +- If `v` is a reference variable, its *ref_safe_scope* is the same as the *ref_safe_scope* of its initializing expression. - Otherwise its *ref_safe_scope* is the scope in which it was declared. #### §ref-span-safety-parameters Parameter ref safe scope For a formal parameter `p`: -- If `p` is a `ref`, or `in` parameter, its *ref_safe_scope* is the *caller_scope*. It is *safe_to_return* by ref. If `p` is an `in` parameter, it can't be returned as a writable `ref` but can be returned as `ref readonly`. -- If `p` is an `out` parameter, its *ref_safe_scope* is the *function_member*. It isn't *safe_to_return* by ref. +- If `p` is a `ref`, or `in` parameter, its *ref_safe_scope* is the *caller_scope*. 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`. +- If `p` is an `out` parameter, its *ref_safe_scope* is the *function_member*. It isn't *safe_to_ref_return*. - Otherwise, if `p` is the `this` parameter of a struct type, its *ref_safe_scope* is the *function_member*. -- Otherwise, the parameter is a value parameter, and it is *ref_safe_scope* is the *function_member*. +- Otherwise, the parameter is a value parameter, and its *ref_safe_scope* is the *function_member*. It isn't *safe_to_ref_return*. #### §ref-span-safety-field-reference Field ref safe scope For a variable designating a reference to a field, `e.F`: - If `e` is of a reference type, its *ref_safe_scope* is the *caller_scope*. -- Otherwise, if `e` is of a value type, its *ref_safe_scope* is taken from the *ref_safe_scope* of `e`. +- Otherwise, if `e` is of a value type, its *ref_safe_scope* is the same as the *ref_safe_scope* of `e`. #### §ref-span-safety-operators Operators @@ -1130,7 +1130,7 @@ The application of a user-defined operator is treated as a method invocation. For an operator that yields a variable, such as `c ? ref e1 : ref e2`: -- The *ref_safe_scope* of the result is the narrowest scope among the *ref_safe_scopes* of the operands of the operator. +- The *ref_safe_scope* of the result is the narrowest scope among the *ref_safe_scopes* of all `ref` operands of the operator. #### §ref-span-safety-method-invocation Method invocation From dbe932bab2910ac37827c58209d75a0fccd19498 Mon Sep 17 00:00:00 2001 From: Bill Wagner Date: Thu, 20 Apr 2023 11:42:52 -0400 Subject: [PATCH 19/26] Feedback from April meeting This include comments, and fixing the formatting after consulting with Rex. --- standard/variables.md | 88 +++++++++++++++++++++---------------------- 1 file changed, 42 insertions(+), 46 deletions(-) diff --git a/standard/variables.md b/standard/variables.md index 8e7655148..0a08d3052 100644 --- a/standard/variables.md +++ b/standard/variables.md @@ -992,9 +992,9 @@ Reads and writes of the following data types shall be atomic: `bool`, `char`, `b ### §ref-span-safety-general General -A *reference variable* is a variable that refers to another variable, called the *referent*. Therefore, a *reference variable* does not store its value. Instead, a *reference variable* refers to its *referent*. Thus, the value of a reference variable is always the same as its referent variable. A *reference variable* is a local variable declared with the `ref` modifier. +A ***reference variable*** is a variable that refers to another variable, called the ***referent***. Therefore, a reference variable does not store its value. Instead, a reference variable refers to its referent. Thus, the value of a reference variable is always the same as its referent variable. A reference variable is a local variable declared with the `ref` modifier. -> *Example:* The following example demonstrates a local *reference variable* whose *referent* is an element of an array: +> *Example:* The following example demonstrates a local reference variable whose referent is an element of an array: > > ```csharp > public class C @@ -1012,9 +1012,9 @@ A *reference variable* is a variable that refers to another variable, called the > > *end example* -A *reference return* is the expression returned by reference from a method whose return type includes the `ref` or `ref readonly` modifiers (§15.6.1). The variable expression of the *reference return* is the *referent* of the *reference return*. +A ***reference return*** is the expression returned by reference from a method whose return type includes the `ref` or `ref readonly` modifiers (§15.6.1). The variable expression of the reference return is the referent of the reference return. -> *Example:* The following example demonstrates a *reference return* whose *referent* is an element of an array field: +> *Example:* The following example demonstrates a reference return whose referent is an element of an array field: > > ```csharp > public class C @@ -1032,9 +1032,9 @@ A *reference return* is the expression returned by reference from a method whose > > *end example* -A `ref struct` may include `ref struct` or ref-like fields. A *ref-like field* refers to the same storage as its initializing expression. The initializing expression of the *ref-like field* is its *referent*. Unlike a reference field, a ref-like field may refer to a `struct` whose storage may be on the execution stack. Ref struct types include `Span`, `ReadOnlySpan`, and other types that include an unmanaged pointer as a member. +A `ref struct` may include `ref struct` or ref-like fields. A ***ref-like field*** refers to the same storage as its initializing expression. The initializing expression of the ref-like fiel* is its referent. Unlike a reference field, a ref-like field may refer to a `struct` whose storage may be on the execution stack. Ref struct types include `Span`, `ReadOnlySpan`, and other types that include an unmanaged pointer as a member. -> *Example:* The following example demonstrates a `ReadOnlySpan` *reference variable* whose *referent* is the characters in a string. `ReadOnlySpan` is a `ref struct`. One of its internal fields is a *ref-like field* whose *referent* is the character array in the string `s`: +> *Example:* The following example demonstrates a `ReadOnlySpan` reference variable whose referent is the characters in a string. `ReadOnlySpan` is a `ref struct`. One of its internal fields is a ref-like field whose referent is the character array in the string `s`: > > ```csharp > string Reverse(string s) @@ -1053,19 +1053,19 @@ A `ref struct` may include `ref struct` or ref-like fields. A *ref-like field* r ### §ref-span-safety-escape-scopes Ref safe scopes -All reference variables obey safety rules that ensure the scope of the reference variable is not greater than the *ref_safe_scope* of its referent. +All reference variables obey safety rules that ensure the scope of the reference variable is not greater than the ref-safe-scope of its referent. -For any variable, the *ref_safe_scope* of that variable is the scope where a *variable_reference* (§9.5) to that variable is valid. The *referent* of a reference variable must have a *ref_safe_scope* that is at least as wide as the scope of the reference variable. A `ref struct` instance may not be copied by value beyond the *ref_safe_scope* of the initializing expression(s) of any *ref-like fields*. +For any variable, the ***ref-safe-scope*** of that variable is the scope where a *variable_reference* (§9.5) to that variable is valid. The referent of a reference variable must have a ref-safe-scope that is at least as wide as the scope of the reference variable. A `ref struct` instance may not be copied by value beyond the ref-safe-scope of the initializing expression(s) of any ref-like fields. -There are three valid *ref_safe_scope*s: +There are three valid ref-safe-scopes: -- *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. The *ref_safe_scope* of a local variable is *block*. A local variable declared in a method has a *ref_safe_scope* 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. -- *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_scope* of the fields of a `struct` type is *function_member*. A variable with *ref_safe_scope* of *function_member* is a valid referent only if the reference variable is declared in the same function member. -- *caller_scope*: Member fields of a `class` type and `ref` parameters have *ref_safe_scope* of *caller_scope*. A variable with *ref_safe_scope* of *caller_scope* can be the referent of a reference return. A variable that can be the referent of a *reference-return* is *safe-to-ref-return*. +- ***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. The ref-safe-scope of a local variable is block. A local variable declared in a method has a ref-safe-scope 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. +- ***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-scope of the fields of a `struct` type is function-member. A variable with ref-safe-scope of function-member is a valid referent only if the reference variable is declared in the same function member. +- ***caller-scope***: Member fields of a `class` type and `ref` parameters have ref-safe-scope of caller-scope. A variable with ref-safe-scope of caller-scope can be the referent of a reference return. A variable that can be the referent of a reference-return is ***safe-to-ref-return***. -These values form a nesting relationship from narrowest (*block*) to widest (*caller_scope*). +These values form a nesting relationship from narrowest (*block*) to widest (caller-scope). -> *Example*: The following code shows examples of the different *ref_safe_scope*s. The declarations show the *ref_safe_scope* for a referent to be the initializing expression for a `ref` variable. The examples show the *ref_safe_scope* for a reference return: +> *Example*: The following code shows examples of the different ref-safe-scopes. The declarations show the ref-safe-scope for a referent to be the initializing expression for a `ref` variable. The examples show the ref-safe-scope for a reference return: > > ```csharp > public class C @@ -1105,40 +1105,36 @@ These values form a nesting relationship from narrowest (*block*) to widest (*ca For a local variable `v`: -- If `v` is a reference variable, its *ref_safe_scope* is the same as the *ref_safe_scope* of its initializing expression. -- Otherwise its *ref_safe_scope* is the scope in which it was declared. +- If `v` is a reference variable, its ref-safe-scope is the same as the ref-safe-scope of its initializing expression. +- Otherwise its ref-safe-scope is the scope in which it was declared. #### §ref-span-safety-parameters Parameter ref safe scope For a formal parameter `p`: -- If `p` is a `ref`, or `in` parameter, its *ref_safe_scope* is the *caller_scope*. 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`. -- If `p` is an `out` parameter, its *ref_safe_scope* is the *function_member*. It isn't *safe_to_ref_return*. -- Otherwise, if `p` is the `this` parameter of a struct type, its *ref_safe_scope* is the *function_member*. -- Otherwise, the parameter is a value parameter, and its *ref_safe_scope* is the *function_member*. It isn't *safe_to_ref_return*. +- If `p` is a `ref`, or `in` parameter, its ref-safe-scope is the caller-scope. 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`. +- If `p` is an `out` parameter, its ref-safe-scope is the function-member. It isn't safe-to-ref-return. +- Otherwise, if `p` is the `this` parameter of a struct type, its ref-safe-scope is the function-member. +- Otherwise, the parameter is a value parameter, and its ref-safe-scope is the function-member. It isn't safe-to-ref-return. #### §ref-span-safety-field-reference Field ref safe scope For a variable designating a reference to a field, `e.F`: -- If `e` is of a reference type, its *ref_safe_scope* is the *caller_scope*. -- Otherwise, if `e` is of a value type, its *ref_safe_scope* is the same as the *ref_safe_scope* of `e`. +- If `e` is of a reference type, its ref-safe-scope is the caller-scope. +- Otherwise, if `e` is of a value type, its ref-safe-scope is the same as the ref-safe-scope of `e`. #### §ref-span-safety-operators Operators -The application of a user-defined operator is treated as a method invocation. - -For an operator that yields a variable, such as `c ? ref e1 : ref e2`: - -- The *ref_safe_scope* of the result is the narrowest scope among the *ref_safe_scopes* of all `ref` operands of the operator. +The conditional operator (§12.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-scope of the result is the narrowest scope among the ref-safe-scopes of all `ref` operands. #### §ref-span-safety-method-invocation Method invocation -For a variable `c` resulting from a ref-returning method invocation, `ref e1.M(e2, ...)`, its *ref_safe_scope* is the narrowest of the following scopes: +For a variable `c` resulting from a ref-returning method invocation, `ref e1.M(e2, ...)`, its ref-safe-scope is the narrowest of the following scopes: -- The *caller_scope*. -- The *ref_safe_scope* of all `ref` and `out` argument expressions (excluding the receiver). -- For each `in` parameter of the method, if there is a corresponding expression that is an variable, its *ref_safe_scope*, otherwise the nearest enclosing scope +- The caller-scope. +- The ref-safe-scope of all `ref` and `out` argument expressions (excluding the receiver). +- For each `in` parameter of the method, if there is a corresponding expression that is an variable, its ref-safe-scope, otherwise the nearest enclosing scope - The scope of all argument expressions (including the receiver). > *Example*: the last bullet is necessary to handle code such as @@ -1160,7 +1156,7 @@ A property invocation (either `get` or `set`) is treated as a method invocation #### §ref-span-safety-a-value Values -A value's *ref_safe_scope* is the nearest enclosing scope. +A value's ref-safe-scope is the nearest enclosing scope. > *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. @@ -1170,18 +1166,18 @@ A `new` expression that invokes a constructor obeys the same rules as a method i #### §ref-span-safety-limitations Limitations on reference variables -- Neither a `ref` parameter, nor a `ref` local, nor a parameter or local of a `ref struct` type can be captured by lambda expression or local function. -- Neither a `ref` parameter nor a parameter of a `ref struct` type may be an argument for an iterator method or an `async` method. -- Neither a `ref` local, nor a local of a `ref struct` type may be in scope at the point of a `yield return` statement or an `await` expression. -- A `ref struct` type may not be used as a type argument, or as an element type in a tuple type. -- A `ref struct` type may not be the declared type of a field, except that it may be the declared type of an instance field of another `ref struct`. -- A `ref struct` type may not be the element type of an array. -- A value of a `ref struct` type may not be boxed: +- 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. +- Neither a `ref` parameter nor a parameter of a `ref struct` type shall be an argument for an iterator method or an `async` method. +- Neither a `ref` local, nor a local of a `ref struct` type shall be in scope at the point of a `yield return` statement or an `await` expression. +- A `ref struct` type shall not be used as a type argument, or as an element type in a tuple type. +- A `ref struct` type shall not be the declared type of a field, except that it may be the declared type of an instance field of another `ref struct`. +- A `ref struct` type shall not be the element type of an array. +- A value of a `ref struct` type shall not be boxed: - There is no conversion from a `ref struct` type to the type `object` or the type `System.ValueType`. - - A `ref struct` type may not be declared to implement any interface - - No instance method declared in `object` or in `System.ValueType` but not overridden in a `ref struct` type may be called with a receiver of that `ref struct` type. - - No instance method of a `ref struct` type may be captured by method conversion to a delegate type. -- For a ref reassignment `ref e1 = ref e2`, the *ref_safe_scope* of `e2` must be at least as wide a scope as the *ref_safe_scope* of `e1`. -- For a ref return statement `return ref e1`, the *ref_safe_scope* of `e1` must be the *caller_scope*. In other words, `e1` must be *safe_to_return*. -- For a method invocation if there is a `ref` or `out` argument of a `ref struct` type (including the receiver), with *ref_safe_scope* E1, then no argument (including the receiver) may have a narrower *ref_safe_scope* than E1. -- A local function or anonymous function may not refer to a local or parameter of `ref struct` type declared in an enclosing scope. + - A `ref struct` type shall not be declared to implement any interface + - An instance method declared in `object` or in `System.ValueType` but not overridden in a `ref struct` type shall not be called with a receiver of that `ref struct` type. + - An instance method of a `ref struct` type shall not be captured by method conversion to a delegate type. +- For a ref reassignment `ref e1 = ref e2`, the ref-safe-scope of `e2` must be at least as wide a scope as the *ref-safe-scope* of `e1`. +- For a ref return statement `return ref e1`, the ref-safe-scope of `e1` must be the caller-scope. In other words, `e1` must be ref-safe-to-return. +- For a method invocation if there is a `ref` or `out` argument of a `ref struct` type (including the receiver), with ref-safe-scope E1, then no argument (including the receiver) may have a narrower ref-safe-scope than E1. +- A local function or anonymous function shall not refer to a local or parameter of `ref struct` type declared in an enclosing scope. From 5103f42cfc198b9b46efbef14eb1bb199e15eb93 Mon Sep 17 00:00:00 2001 From: Bill Wagner Date: Thu, 20 Apr 2023 12:49:49 -0400 Subject: [PATCH 20/26] remove ref struct descriptions These belong in the PR for ref structs, and in the section on ref structs. --- standard/variables.md | 31 +------------------------------ 1 file changed, 1 insertion(+), 30 deletions(-) diff --git a/standard/variables.md b/standard/variables.md index 0a08d3052..89d55f224 100644 --- a/standard/variables.md +++ b/standard/variables.md @@ -1032,30 +1032,11 @@ A ***reference return*** is the expression returned by reference from a method w > > *end example* -A `ref struct` may include `ref struct` or ref-like fields. A ***ref-like field*** refers to the same storage as its initializing expression. The initializing expression of the ref-like fiel* is its referent. Unlike a reference field, a ref-like field may refer to a `struct` whose storage may be on the execution stack. Ref struct types include `Span`, `ReadOnlySpan`, and other types that include an unmanaged pointer as a member. - -> *Example:* The following example demonstrates a `ReadOnlySpan` reference variable whose referent is the characters in a string. `ReadOnlySpan` is a `ref struct`. One of its internal fields is a ref-like field whose referent is the character array in the string `s`: -> -> ```csharp -> string Reverse(string s) -> { -> ReadOnlySpan chars = s.AsSpan(); -> var reversed = new char[chars.Length]; -> for (int i = 0; i < chars.Length; i++) -> { -> reversed[chars.Length-i-1] = chars[i]; -> } -> return new string(reversed); -> } -> ``` -> -> *end example* - ### §ref-span-safety-escape-scopes Ref safe scopes All reference variables obey safety rules that ensure the scope of the reference variable is not greater than the ref-safe-scope of its referent. -For any variable, the ***ref-safe-scope*** of that variable is the scope where a *variable_reference* (§9.5) to that variable is valid. The referent of a reference variable must have a ref-safe-scope that is at least as wide as the scope of the reference variable. A `ref struct` instance may not be copied by value beyond the ref-safe-scope of the initializing expression(s) of any ref-like fields. +For any variable, the ***ref-safe-scope*** of that variable is the scope where a *variable_reference* (§9.5) to that variable is valid. The referent of a reference variable must have a ref-safe-scope that is at least as wide as the scope of the reference variable. There are three valid ref-safe-scopes: @@ -1169,15 +1150,5 @@ A `new` expression that invokes a constructor obeys the same rules as a method i - 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. - Neither a `ref` parameter nor a parameter of a `ref struct` type shall be an argument for an iterator method or an `async` method. - Neither a `ref` local, nor a local of a `ref struct` type shall be in scope at the point of a `yield return` statement or an `await` expression. -- A `ref struct` type shall not be used as a type argument, or as an element type in a tuple type. -- A `ref struct` type shall not be the declared type of a field, except that it may be the declared type of an instance field of another `ref struct`. -- A `ref struct` type shall not be the element type of an array. -- A value of a `ref struct` type shall not be boxed: - - There is no conversion from a `ref struct` type to the type `object` or the type `System.ValueType`. - - A `ref struct` type shall not be declared to implement any interface - - An instance method declared in `object` or in `System.ValueType` but not overridden in a `ref struct` type shall not be called with a receiver of that `ref struct` type. - - An instance method of a `ref struct` type shall not be captured by method conversion to a delegate type. - For a ref reassignment `ref e1 = ref e2`, the ref-safe-scope of `e2` must be at least as wide a scope as the *ref-safe-scope* of `e1`. - For a ref return statement `return ref e1`, the ref-safe-scope of `e1` must be the caller-scope. In other words, `e1` must be ref-safe-to-return. -- For a method invocation if there is a `ref` or `out` argument of a `ref struct` type (including the receiver), with ref-safe-scope E1, then no argument (including the receiver) may have a narrower ref-safe-scope than E1. -- A local function or anonymous function shall not refer to a local or parameter of `ref struct` type declared in an enclosing scope. From f6bb2afb3686d557a4921d8f58a00c894a7732cd Mon Sep 17 00:00:00 2001 From: Bill Wagner Date: Tue, 9 May 2023 10:30:57 -0400 Subject: [PATCH 21/26] Apply suggestions from code review Co-authored-by: Jon Skeet --- standard/variables.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/standard/variables.md b/standard/variables.md index 89d55f224..3326c3d3a 100644 --- a/standard/variables.md +++ b/standard/variables.md @@ -1041,10 +1041,10 @@ For any variable, the ***ref-safe-scope*** of that variable is the scope where a There are three valid ref-safe-scopes: - ***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. The ref-safe-scope of a local variable is block. A local variable declared in a method has a ref-safe-scope 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. -- ***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-scope of the fields of a `struct` type is function-member. A variable with ref-safe-scope of function-member is a valid referent only if the reference variable is declared in the same function member. +- ***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-scope of the fields of a `struct` type is function-member. A variable with ref-safe-scope of function-member is a valid referent only if the reference variable is declared in the same function member. - ***caller-scope***: Member fields of a `class` type and `ref` parameters have ref-safe-scope of caller-scope. A variable with ref-safe-scope of caller-scope can be the referent of a reference return. A variable that can be the referent of a reference-return is ***safe-to-ref-return***. -These values form a nesting relationship from narrowest (*block*) to widest (caller-scope). +These values form a nesting relationship from narrowest (block) to widest (caller-scope). > *Example*: The following code shows examples of the different ref-safe-scopes. The declarations show the ref-safe-scope for a referent to be the initializing expression for a `ref` variable. The examples show the ref-safe-scope for a reference return: > @@ -1115,7 +1115,7 @@ For a variable `c` resulting from a ref-returning method invocation, `ref e1.M(e - The caller-scope. - The ref-safe-scope of all `ref` and `out` argument expressions (excluding the receiver). -- For each `in` parameter of the method, if there is a corresponding expression that is an variable, its ref-safe-scope, otherwise the nearest enclosing scope +- For each `in` parameter of the method, if there is a corresponding expression that is a variable, its ref-safe-scope, otherwise the nearest enclosing scope - The scope of all argument expressions (including the receiver). > *Example*: the last bullet is necessary to handle code such as From 9e516cdde3b152764c71b71a3fb4595582973e38 Mon Sep 17 00:00:00 2001 From: Bill Wagner Date: Tue, 9 May 2023 10:42:50 -0400 Subject: [PATCH 22/26] respond to feedback. --- standard/variables.md | 29 ++++++++++++++++++----------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/standard/variables.md b/standard/variables.md index 89d55f224..eb5b4cbe9 100644 --- a/standard/variables.md +++ b/standard/variables.md @@ -1051,14 +1051,16 @@ These values form a nesting relationship from narrowest (*block*) to widest (cal > ```csharp > public class C > { -> private int[] arr = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; +> // ref safe scope of arr is "caller-scope". +> // ref safe scope of arr[i] is "caller-scope". +> private int[] arr = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; > -> public ref int M1(ref int r1) +> public ref int M1(ref int r1) // ref safe scope is "caller-scope" > { > return ref r1; // r1 is safe to ref return > } > -> public ref int M2(int v1) +> public ref int M2(int v1) // ref safe scope is "function-member" > { > return ref v1; // error: v1 isn't safe to ref return > } @@ -1121,14 +1123,19 @@ For a variable `c` resulting from a ref-returning method invocation, `ref e1.M(e > *Example*: the last bullet is necessary to handle code such as > > ```csharp -> var sp = new Span(...) -> return ref sp[0]; -> ``` -> -> or -> -> ```csharp -> return ref M(sp, 0); +> ref int M2() +> { +> int v = 5; +> // Not valid. +> // ref safe scope of "v" is block. +> // Therefore, ref safe scope of the return value of M() is block. +> return ref M(ref v); +> } +> +> ref int M(ref int p) +> { +> return ref p; +> } > ``` > > *end example* From e05d0bf47a99b46597d9ad2f015ddff66ef26d72 Mon Sep 17 00:00:00 2001 From: Bill Wagner Date: Mon, 15 May 2023 11:15:32 -0400 Subject: [PATCH 23/26] Apply suggestions from code review Co-authored-by: Nigel-Ecma --- standard/variables.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/standard/variables.md b/standard/variables.md index 47d18a05b..64e90dac7 100644 --- a/standard/variables.md +++ b/standard/variables.md @@ -992,7 +992,7 @@ Reads and writes of the following data types shall be atomic: `bool`, `char`, `b ### §ref-span-safety-general General -A ***reference variable*** is a variable that refers to another variable, called the ***referent***. Therefore, a reference variable does not store its value. Instead, a reference variable refers to its referent. Thus, the value of a reference variable is always the same as its referent variable. A reference variable is a local variable declared with the `ref` modifier. +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`) [insert x-ref]. A reference variable is a local variable declared with the `ref` modifier. > *Example:* The following example demonstrates a local reference variable whose referent is an element of an array: > From f9f036fc9e1bcaceadcc310b29e8eb20e1e34c0a Mon Sep 17 00:00:00 2001 From: Bill Wagner Date: Mon, 15 May 2023 11:27:02 -0400 Subject: [PATCH 24/26] remove missing xref This needs to be added to #213 once this PR is merged. --- standard/variables.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/standard/variables.md b/standard/variables.md index 64e90dac7..3d19fa9fe 100644 --- a/standard/variables.md +++ b/standard/variables.md @@ -992,7 +992,7 @@ Reads and writes of the following data types shall be atomic: `bool`, `char`, `b ### §ref-span-safety-general General -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`) [insert x-ref]. A reference variable is a local variable declared with the `ref` modifier. +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. > *Example:* The following example demonstrates a local reference variable whose referent is an element of an array: > From 00f66f8cf74013a3ca7e050294d368bccdfe54de Mon Sep 17 00:00:00 2001 From: Bill Wagner Date: Fri, 19 May 2023 11:42:02 -0400 Subject: [PATCH 25/26] Updates from 5/17 meeting. Updates from the 5/17 meeting. --- standard/variables.md | 84 ++++++++++++++++++++++--------------------- 1 file changed, 43 insertions(+), 41 deletions(-) diff --git a/standard/variables.md b/standard/variables.md index 3d19fa9fe..700d807b6 100644 --- a/standard/variables.md +++ b/standard/variables.md @@ -1032,35 +1032,37 @@ A ***reference return*** is the expression returned by reference from a method w > > *end example* -### §ref-span-safety-escape-scopes Ref safe scopes +### §ref-safe-contexts Ref safe contexts -All reference variables obey safety rules that ensure the scope of the reference variable is not greater than the ref-safe-scope of its referent. +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. -For any variable, the ***ref-safe-scope*** of that variable is the scope where a *variable_reference* (§9.5) to that variable is valid. The referent of a reference variable must have a ref-safe-scope that is at least as wide as the scope of the reference variable. +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. -There are three valid ref-safe-scopes: +> *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* -- ***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. The ref-safe-scope of a local variable is block. A local variable declared in a method has a ref-safe-scope 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. -- ***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-scope of the fields of a `struct` type is function-member. A variable with ref-safe-scope of function-member is a valid referent only if the reference variable is declared in the same function member. -- ***caller-scope***: Member fields of a `class` type and `ref` parameters have ref-safe-scope of caller-scope. A variable with ref-safe-scope of caller-scope can be the referent of a reference return. A variable that can be the referent of a reference-return is ***safe-to-ref-return***. +There are three valid ref-safe-contexts: -These values form a nesting relationship from narrowest (block) to widest (caller-scope). +- ***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. +- ***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. +- ***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***. -> *Example*: The following code shows examples of the different ref-safe-scopes. The declarations show the ref-safe-scope for a referent to be the initializing expression for a `ref` variable. The examples show the ref-safe-scope for a reference return: +These values form a nesting relationship from narrowest (declaration-block) to widest (caller-context). Each nested block represents a different context. + +> *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: > > ```csharp > public class C > { -> // ref safe scope of arr is "caller-scope". -> // ref safe scope of arr[i] is "caller-scope". +> // ref safe context of arr is "caller-context". +> // ref safe context of arr[i] is "caller-context". > private int[] arr = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; > -> public ref int M1(ref int r1) // ref safe scope is "caller-scope" +> public ref int M1(ref int r1) // ref safe context is "caller-context" > { > return ref r1; // r1 is safe to ref return > } > -> public ref int M2(int v1) // ref safe scope is "function-member" +> public ref int M2(int v1) // ref safe context is "function-member" > { > return ref v1; // error: v1 isn't safe to ref return > } @@ -1075,50 +1077,50 @@ These values form a nesting relationship from narrowest (block) to widest (calle > public void M4(int p) > { > int v3 = 6; -> ref int r2 = ref p; // scope of r2 is block, ref safe scope of p is method -> ref int r3 = ref v3; // scope of r3 is block, ref safe scope of v3 is block -> ref int r4 = ref arr[v3]; // scope of r4 is block, ref safe scope of arr[v3] is caller method +> ref int r2 = ref p; // context of r2 is block, ref safe context of p is method +> ref int r3 = ref v3; // context of r3 is block, ref safe context of v3 is block +> ref int r4 = ref arr[v3]; // context of r4 is block, ref safe context of arr[v3] is caller method > } > } > ``` > > *end example.* -#### §ref-span-safety-locals Local variable ref safe scope +#### §ref-span-safety-locals Local variable ref safe context For a local variable `v`: -- If `v` is a reference variable, its ref-safe-scope is the same as the ref-safe-scope of its initializing expression. -- Otherwise its ref-safe-scope is the scope in which it was declared. +- If `v` is a reference variable, its ref-safe-context is the same as the ref-safe-context of its initializing expression. +- Otherwise its ref-safe-context is the context in which it was declared. -#### §ref-span-safety-parameters Parameter ref safe scope +#### §ref-span-safety-parameters Parameter ref safe context For a formal parameter `p`: -- If `p` is a `ref`, or `in` parameter, its ref-safe-scope is the caller-scope. 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`. -- If `p` is an `out` parameter, its ref-safe-scope is the function-member. It isn't safe-to-ref-return. -- Otherwise, if `p` is the `this` parameter of a struct type, its ref-safe-scope is the function-member. -- Otherwise, the parameter is a value parameter, and its ref-safe-scope is the function-member. It isn't safe-to-ref-return. +- 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`. +- If `p` is an `out` parameter, its ref-safe-context is the function-member. It isn't safe-to-ref-return. +- Otherwise, if `p` is the `this` parameter of a struct type, its ref-safe-context is the function-member. +- Otherwise, the parameter is a value parameter, and its ref-safe-context is the function-member. It isn't safe-to-ref-return. -#### §ref-span-safety-field-reference Field ref safe scope +#### §ref-span-safety-field-reference Field ref safe context For a variable designating a reference to a field, `e.F`: -- If `e` is of a reference type, its ref-safe-scope is the caller-scope. -- Otherwise, if `e` is of a value type, its ref-safe-scope is the same as the ref-safe-scope of `e`. +- If `e` is of a reference type, its ref-safe-context is the caller-context. +- Otherwise, if `e` is of a value type, its ref-safe-context is the same as the ref-safe-context of `e`. #### §ref-span-safety-operators Operators -The conditional operator (§12.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-scope of the result is the narrowest scope among the ref-safe-scopes of all `ref` operands. +The conditional operator (§12.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. -#### §ref-span-safety-method-invocation Method invocation +#### §ref-span-safety-function-invocation Function invocation -For a variable `c` resulting from a ref-returning method invocation, `ref e1.M(e2, ...)`, its ref-safe-scope is the narrowest of the following scopes: +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: -- The caller-scope. -- The ref-safe-scope of all `ref` and `out` argument expressions (excluding the receiver). -- For each `in` parameter of the method, if there is a corresponding expression that is a variable, its ref-safe-scope, otherwise the nearest enclosing scope -- The scope of all argument expressions (including the receiver). +- The caller-context. +- The ref-safe-context of all `ref` and `out` argument expressions (excluding the receiver). +- 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 +- The context of all argument expressions (including the receiver). > *Example*: the last bullet is necessary to handle code such as > @@ -1127,8 +1129,8 @@ For a variable `c` resulting from a ref-returning method invocation, `ref e1.M(e > { > int v = 5; > // Not valid. -> // ref safe scope of "v" is block. -> // Therefore, ref safe scope of the return value of M() is block. +> // ref safe context of "v" is block. +> // Therefore, ref safe context of the return value of M() is block. > return ref M(ref v); > } > @@ -1140,11 +1142,11 @@ For a variable `c` resulting from a ref-returning method invocation, `ref e1.M(e > > *end example* -A property invocation (either `get` or `set`) is treated as a method invocation of the underlying method by the above rules. +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. #### §ref-span-safety-a-value Values -A value's ref-safe-scope is the nearest enclosing scope. +A value's ref-safe-context is the nearest enclosing context. > *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. @@ -1156,6 +1158,6 @@ A `new` expression that invokes a constructor obeys the same rules as a method i - 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. - Neither a `ref` parameter nor a parameter of a `ref struct` type shall be an argument for an iterator method or an `async` method. -- Neither a `ref` local, nor a local of a `ref struct` type shall be in scope at the point of a `yield return` statement or an `await` expression. -- For a ref reassignment `ref e1 = ref e2`, the ref-safe-scope of `e2` must be at least as wide a scope as the *ref-safe-scope* of `e1`. -- For a ref return statement `return ref e1`, the ref-safe-scope of `e1` must be the caller-scope. In other words, `e1` must be ref-safe-to-return. +- 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. +- 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`. +- 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. From 87204bc253b4fc0d8f76be85d474fd041b4bdd41 Mon Sep 17 00:00:00 2001 From: Bill Wagner Date: Fri, 19 May 2023 14:13:35 -0400 Subject: [PATCH 26/26] fix build warning --- standard/variables.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/standard/variables.md b/standard/variables.md index 700d807b6..c3367d59a 100644 --- a/standard/variables.md +++ b/standard/variables.md @@ -1152,7 +1152,7 @@ A value's ref-safe-context is the nearest enclosing context. #### §ref-span-safety-constructor-invocations Constructor invocations -A `new` expression that invokes a constructor obeys the same rules as a method invocation (§ref-span-safety-method-invocation) that is considered to return the type being constructed. +A `new` expression that invokes a constructor obeys the same rules as a method invocation (§ref-span-safety-function-invocation) that is considered to return the type being constructed. #### §ref-span-safety-limitations Limitations on reference variables