-
Notifications
You must be signed in to change notification settings - Fork 90
12.6.6.1: Defensive copy of input parameter #894
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
Need to verify the behaviour for enum types and for type parameters. |
interface I {
void M();
}
class C {
void N<T>(T t1, in T t2)
where T : struct, I {
t1.M(); // no copying
t2.M(); // defensive copy
}
} IL generated by Roslyn makes a copy even if |
For a using System;
readonly struct R {
public readonly int I;
public R(int i) {
I = i;
}
public int M() {
C.StaticR = new R(2);
return I;
}
}
class C {
public static R StaticR = new R(1);
static void Main() {
N(in StaticR);
}
static void N(in R r) {
Console.WriteLine(r.I); // 1
int i = r.M(); // no copying (would copy if not readonly struct)
Console.WriteLine(i); // 2 (would be 1 if not readonly struct)
Console.WriteLine(r.I); // 2
}
} |
C# programs cannot define methods in enum types, and the methods of System.Enum do not mutate the instance, so defensive copying does not seem to be observable for enums. Enum.ToString() could call a custom IFormatProvider, which could replace the enum instance to which |
Need to check whether the special case for |
@KalleOlaviNiemitalo – Is the “defensive” copy you refer to the one specified in §12.6.2.3, bullet 1, sub-bullet 2? |
@Nigel-Ecma, it is not the same. |
In a method invocation
|
@KalleOlaviNiemitalo – OK, I see what you’re talking about now. Thanks. |
The description of the example in 12.6.2.3 looks wrong though -- for the |
Ugh, the issue isn't even limited to input parameters. The wording needs to cover using System;
struct S {
public void M() {}
}
class C {
readonly S f = new S();
S[] a = new S[1];
void N() {
f.M(); // copy
ref readonly S r = ref a[0];
r.M(); // copy
ReadOnlySpan<S> ros = a;
ros[0].M(); // copy
}
} IIUC, each of these is "classified as a variable". |
I hoped this could use the same distinction between readonly and writable variable_reference as in assignment expressions and in For assignment, 12.21.2 (Simple assignment) has:
Now, is there something that says a variable_reference to an input parameter is classified as a variable that is |
As another example, I think draft-v7 does not currently forbid this, even though it should: class C {
void M(ref int i) {}
void N(in int i) {
M(ref i);
}
} §15.6.2.3 (Input parameters) says:
but the example above does not violate that; although §15.6.2.4 (Reference parameters) does not forbid the variable_reference naming an input parameter. §9.2.8 (Input parameters) restricts capture and specifies definite assignment but does not mention readonly vs. writable. §12.6.2.3 (Run-time evaluation of argument lists) requires that the variable for a reference argument be definitely assigned, but does not require writability. §15.5.3.1 (Readonly fields / General) does say:
but this applies only to readonly fields, not to input parameters. (It doesn't apply to §9.7.2.3 (Parameter ref safe context) says:
but the second sentence does not apply to passing I think the standard should define readonly variable_reference and writable variable_reference, and then use those definitions when specifying assignments, reference arguments, output arguments, and defensive copying. |
Each of these should be a readonly variable_reference:
I hope that's all. |
Huh, apparently partial struct S {
public int I;
}
static class Ex {
public static ref readonly S GetThat(this in S that) => ref that; // OK
}
partial class C {
S M1() => new S { I = 5 };
void M2(in int i) {}
int M3() => M1().I;
void M4() {
//M2(in M1().I); // error CS1612
}
void M5() {
M2(in M1().GetThat().I); // OK, copy M1() to local
}
}
#if false // C# 11 Low Level Struct Improvements
partial struct S {
[System.Diagnostics.CodeAnalysis.UnscopedRef]
public ref readonly S GetThis() => ref this;
}
partial class C {
void M6() {
M2(in M1().GetThis().I); // OK, copy M1() to local
}
}
#endif |
Meeting 2023-09-06: Assigning to Bill, who will find the relevant places and also discuss with the compiler team. |
First commit: Fix the issue raised in the description of dotnet#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.
I'm not sure that #927 addresses ref conditional expressions. It might but I haven't verified. I mean this kind of code struct S {
public void M() {}
}
class C {
void N(bool b, ref S r, in S i) {
(b ? ref r : ref i).M();
}
} The compiler behaviour seems to be the same as S s = (b ? r : i);
s.M(); i.e., if And if I change the parameter list to ref S s = ref (b ? ref r : ref i);
s.M(); i.e., no instance of This behaviour seems to mean that the ref conditional expression as a whole is considered a writable ref or a readonly ref, depending on whether the second and third operands are writable refs, and the defensive copying then depends on this decision. |
First commit: Fix the issue raised in the description of dotnet#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.
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.
Uh oh!
There was an error while loading. Please reload this page.
Describe the bug
In the C# 7.x draft, 12.6.6.1 (Function member invocation / General) describes how a copy of a value-type instance "not classified as a variable" is made and used instead of the original. This defensive copying should also apply to an input parameter unless its type is a
readonly struct
.Example
Expected behavior
In 12.6.6.1, declare:
Additional context
None.
Associated WorkItem - 157261
The text was updated successfully, but these errors were encountered: