csharplang icon indicating copy to clipboard operation
csharplang copied to clipboard

Open issues for `ref` fields

Open cston opened this issue 3 years ago • 9 comments

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.

cston avatar May 20 '22 19:05 cston

Allow ref assignment 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 ?

FaustVX avatar May 21 '22 11:05 FaustVX

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; }

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

BreyerW avatar May 21 '22 14:05 BreyerW

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
    }
}

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.

hez2010 avatar May 22 '22 06:05 hez2010

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

333fred avatar May 25 '22 00:05 333fred

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 avatar Jul 15 '22 06:07 hypeartist

@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 avatar Jul 15 '22 09:07 Joe4evr

@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);
		}
	}

hypeartist avatar Jul 15 '22 10:07 hypeartist

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.

hez2010 avatar Jul 15 '22 12:07 hez2010

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.

hypeartist avatar Jul 15 '22 12:07 hypeartist