csharplang
csharplang copied to clipboard
Open issues for `ref` fields
1. Parse scoped as a modifier with -langversion:11?
What is the effect of parsing scoped as a modifier on existing code that uses scoped as a type name or identifier?
// C#10: 'scoped' type
class scoped { }
scoped s;
static void F(scoped x, ref scoped y) { }
// C#10: 'scoped' identifier
bool scoped;
scoped = true;
2. Allow ref auto properties?
Support ref auto property declarations? If so, allow set or init?
ref T P0 { get; }
ref T P1 { get; set; }
ref T P2 { get; init; }
3. Allow ref assignment in object initializers?
ref struct R<T>
{
public ref T F;
}
int i = 0;
var r = new R<int> { F = ref i };
4. Report diagnostic for constructor that does not set ref field?
Report a diagnostic for an explicit constructor that does not assign to a ref field, or that assigns a value?
ref struct R<T>
{
private ref T _t;
public R() { } // warning: ref is not assigned
public R(ref T t)
{
_t = t; // warning: assigning value not ref
}
}
5. scoped differences in overrides and interface implementations
Report a diagnostic for mismatches of scoped across overrides or implicit or explicit interface implementations? Allow "safe" variance differences?
ref struct R<T> { }
abstract class A<T>
{
public abstract R<T> F1(scoped R<T> r);
public abstract R<T> F2(R<T> r);
}
class B : A<int>
{
public override R<int> F1(R<int> r) => r; // error?
public override R<int> F2(scoped R<int> r) => default; // ok?
}
Proposed: Report errors for any differences in scoped, even "safe" differences.
6. scoped differences in delegate conversions
Report a diagnostic for mismatches of scoped in conversions from lambda expressions or method groups to delegate types? Allow "safe" variance differences?
ref struct R { }
delegate R D1(scoped R r);
delegate R D2(R r);
D1 i1 = (R r) => r; // error?
D2 i2 = (scoped R r) => default; // ok?
D1 e1 = (D1)((R r) => r); // error?
D2 e2 = (D2)((scoped R r) => default); // ok?
Proposed: Report errors for any differences in scoped, even "safe" differences.
Allow
refassignment in object initializers?ref struct R<T> { public ref T F; } int i = 0; var r = new R<int> { ref F = ref i };
the field F should be initialized with ref F = … to be consistent with other ref variables assignment ?
Allow
refauto properties?Support
refauto property declarations? If so, allowsetorinit?ref T P0 { get; } ref T P1 { get; set; } ref T P2 { get; init; }
Ref setters would be useful on its own for event patterns where you would still want to avoid copying large struct and fire some kind of event. Like detect if new matrix4x4 (which is decently big struct 16x float fields) changed position or rotation or scale and fire appropiate events. Yes its niche but are so many other perf oriented features. Unless you propose ref setters w/o body only in which case it would be unfortunate but reduces feature scope i guess
As for object initializer allowing it would remove yet another suprise that u cant object initialize everything so i hope it lands somewhere in future
Report diagnostic for constructor that does not set
reffield?Report a diagnostic for an explicit constructor that does not assign to a
reffield, or that assigns a value?ref struct R<T> { private ref T _t; public R() { } // warning: ref is not assigned public R(ref T t) { _t = t; // warning: assigning value not ref } }
I think at least the assigning value not ref should be reported. I used to prototype something using ref fields but immediately came into a problem that caused a NRE in the runtime because I missed a ref in the right operand of assignment.
Questions 1-3 were discussed in LDM on May 23rd, 2022. We still need to confirm question 3, which we will do after discussing question 4.
https://github.com/dotnet/csharplang/blob/main/meetings/2022/LDM-2022-05-23.md#open-issues-for-ref-fields
Could someone explain why A ref field cannot have a type that is ref struct (https://github.com/dotnet/csharplang/blob/main/proposals/low-level-struct-improvements.md#:~:text=A%20ref%20field%20cannot%20have%20a%20type%20that%20is%20ref%20struct)?
This is such a huge limitation that it makes the whole idea with ref fields to look quite meaningless to me.
@hypeartist Because a ref is not allowed to live longer than the object it's referring to. If the ref struct restriction did not exist, you could end up with refs referring into stack-space above whatever holds it, which is incredibly dangerous.
@Joe4evr
you could end up with
refs referring into stack-space above whatever holds it,
You perfectly can end up with it via non-ref type field too:
ref struct S
{
public ref int F;
public static void M(ref S s)
{
ReadOnlySpan<int> f = stackalloc int[1];
s.F = MemoryMarshal.GetReference(f);
}
}
Could someone explain why
A ref field cannot have a type that is ref struct
See https://github.com/dotnet/roslyn/pull/62186#issuecomment-1170244475. It just doesn't catch up in C# 11 (because it involves complicated lifetime analysis which needs more time to implement), while it will be enabled in the future.
You perfectly can end up with it via non-ref type field too
MemoryMarshal.GetReference is an unsafe API so definitely you can bypass the check.
It just doesn't catch up in C# 11 (because it involves complicated lifetime analysis which needs more time to implement), while it will be enabled in the future.
That's exactly the answer I was hoping to hear! Thank you.