From 8a205407fcc118b70bb6fdf50a8ee2fb946ef91d Mon Sep 17 00:00:00 2001 From: Bill Wagner Date: Tue, 12 Sep 2023 16:01:13 -0400 Subject: [PATCH 1/4] Specify temporary for `this` as an input parameter First commit: Fix the issue raised in the description of #894: If a method takes an `in` parameter of a struct type that is not a `readonly struct`, then the compiler must create a temporary to invoke a non-readonly member of that type. --- standard/expressions.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/standard/expressions.md b/standard/expressions.md index 6b0906611..ef5d667ff 100644 --- a/standard/expressions.md +++ b/standard/expressions.md @@ -1211,7 +1211,7 @@ The run-time processing of a function member invocation consists of the followin - `M` is invoked. - Otherwise, if the type of `E` is a value-type `V`, and `M` is declared or overridden in `V`: - `E` is evaluated. If this evaluation causes an exception, then no further steps are executed. For an instance constructor, this evaluation consists of allocating storage (typically from an execution stack) for the new object. In this case `E` is classified as a variable. - - If `E` is not classified as a variable, then a temporary local variable of `E`’s type is created and the value of `E` is assigned to that variable. `E` is then reclassified as a reference to that temporary local variable. The temporary variable is accessible as `this` within `M`, but not in any other way. Thus, only when `E` is a true variable is it possible for the caller to observe the changes that `M` makes to `this`. + - If `E` is not classified as a variable, or if `E` is an input parameter (§15.6.2.3) and `V` is not a readonly struct type (§16.2.2), then a temporary local variable of `E`’s type is created and the value of `E` is assigned to that variable. `E` is then reclassified as a reference to that temporary local variable. The temporary variable is accessible as `this` within `M`, but not in any other way. Thus, only when `E` is a true variable is it possible for the caller to observe the changes that `M` makes to `this`. - The argument list is evaluated as described in [§12.6.2](expressions.md#1262-argument-lists). - `M` is invoked. The variable referenced by `E` becomes the variable referenced by `this`. - Otherwise: From 12f2e798f8ef1bf09e7557924e3ad45c74324e50 Mon Sep 17 00:00:00 2001 From: Bill Wagner Date: Tue, 12 Sep 2023 17:07:15 -0400 Subject: [PATCH 2/4] Specify defensive copy for other readonly variables Addresses https://github.com/dotnet/csharpstandard/issues/894#issuecomment-1681573559 --- standard/expressions.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/standard/expressions.md b/standard/expressions.md index ef5d667ff..60902b2e4 100644 --- a/standard/expressions.md +++ b/standard/expressions.md @@ -1211,7 +1211,11 @@ The run-time processing of a function member invocation consists of the followin - `M` is invoked. - Otherwise, if the type of `E` is a value-type `V`, and `M` is declared or overridden in `V`: - `E` is evaluated. If this evaluation causes an exception, then no further steps are executed. For an instance constructor, this evaluation consists of allocating storage (typically from an execution stack) for the new object. In this case `E` is classified as a variable. - - If `E` is not classified as a variable, or if `E` is an input parameter (§15.6.2.3) and `V` is not a readonly struct type (§16.2.2), then a temporary local variable of `E`’s type is created and the value of `E` is assigned to that variable. `E` is then reclassified as a reference to that temporary local variable. The temporary variable is accessible as `this` within `M`, but not in any other way. Thus, only when `E` is a true variable is it possible for the caller to observe the changes that `M` makes to `this`. + - If `E` is not classified as a variable, or if `V` is not a readonly struct type (§16.2.2), and `E` is one of: + - an input parameter (§15.6.2.3), or + - a `readonly` field (§15.5.3), or + - a `readonly` reference variable or return (§9.7), + then a temporary local variable of `E`’s type is created and the value of `E` is assigned to that variable. `E` is then reclassified as a reference to that temporary local variable. The temporary variable is accessible as `this` within `M`, but not in any other way. Thus, only when `E` is a true variable is it possible for the caller to observe the changes that `M` makes to `this`. - The argument list is evaluated as described in [§12.6.2](expressions.md#1262-argument-lists). - `M` is invoked. The variable referenced by `E` becomes the variable referenced by `this`. - Otherwise: From 34f77e6356df79f88164fe1f108de9727e9e3d64 Mon Sep 17 00:00:00 2001 From: Bill Wagner Date: Thu, 14 Sep 2023 12:02:11 -0400 Subject: [PATCH 3/4] fix formatting of nested list. --- standard/expressions.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/standard/expressions.md b/standard/expressions.md index 60902b2e4..9ecfe7f61 100644 --- a/standard/expressions.md +++ b/standard/expressions.md @@ -1215,7 +1215,8 @@ The run-time processing of a function member invocation consists of the followin - an input parameter (§15.6.2.3), or - a `readonly` field (§15.5.3), or - a `readonly` reference variable or return (§9.7), - then a temporary local variable of `E`’s type is created and the value of `E` is assigned to that variable. `E` is then reclassified as a reference to that temporary local variable. The temporary variable is accessible as `this` within `M`, but not in any other way. Thus, only when `E` is a true variable is it possible for the caller to observe the changes that `M` makes to `this`. + + then a temporary local variable of `E`’s type is created and the value of `E` is assigned to that variable. `E` is then reclassified as a reference to that temporary local variable. The temporary variable is accessible as `this` within `M`, but not in any other way. Thus, only when `E` is a true variable is it possible for the caller to observe the changes that `M` makes to `this`. - The argument list is evaluated as described in [§12.6.2](expressions.md#1262-argument-lists). - `M` is invoked. The variable referenced by `E` becomes the variable referenced by `this`. - Otherwise: From 0ecb733b01fef7e5b88c7a5b678cb1ea72a08583 Mon Sep 17 00:00:00 2001 From: Bill Wagner Date: Tue, 19 Sep 2023 11:26:41 -0400 Subject: [PATCH 4/4] Update expressions.md --- standard/expressions.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/standard/expressions.md b/standard/expressions.md index 9ecfe7f61..543178647 100644 --- a/standard/expressions.md +++ b/standard/expressions.md @@ -1216,7 +1216,7 @@ The run-time processing of a function member invocation consists of the followin - a `readonly` field (§15.5.3), or - a `readonly` reference variable or return (§9.7), - then a temporary local variable of `E`’s type is created and the value of `E` is assigned to that variable. `E` is then reclassified as a reference to that temporary local variable. The temporary variable is accessible as `this` within `M`, but not in any other way. Thus, only when `E` is a true variable is it possible for the caller to observe the changes that `M` makes to `this`. + then a temporary local variable of `E`’s type is created and the value of `E` is assigned to that variable. `E` is then reclassified as a reference to that temporary local variable. The temporary variable is accessible as `this` within `M`, but not in any other way. Thus, only when `E` can be written is it possible for the caller to observe the changes that `M` makes to `this`. - The argument list is evaluated as described in [§12.6.2](expressions.md#1262-argument-lists). - `M` is invoked. The variable referenced by `E` becomes the variable referenced by `this`. - Otherwise: