Write a test plan for "ref readonly", "readonly structs", "ref and ref readonly extensions" and "ref ternary"
LDM
Spec
Misc
Ref readonly parameters
Ref readonly returns
Readonly struct
Ref ternary
Ref readonly extension methods
Ref-like types and safety (Span)
Misc
See also
Write a test plan for "ref readonly", "readonly structs", "ref and ref readonly extensions" and "ref ternary"
LDM
insyntax from initial release? (merged)ref readonlyat call site (for those who dislike auto-ref)? (yes, but we're switching toinas keyword)Spec
inparameters are not allowed in iterator or async methodsvar x = stackalloc ...is pointer type for compat reasons, butcondition ? stackalloc ... : stackalloc ...isSpan)Misc
ref readonly,in,return ref ...,refternary,inversusref readonlyreadonly structref readonlyat call site, CodeStyle for call site (for those who don't like auto-ref)Ref readonly parameters
Spec exists
test passing too many modifiers ("ref readonly in" or "in ref readonly", etc)
verify API declaration and usage from compilation, image, metadata-only image and ref assembly
ref readonly property from metadata with different attributes on getter and property
Where is it allowed or blocked?
outorparams(expect error)ref readonlyis disallowed: in local declarationsref readonly var x = y;, in front of expressionsvar x = ref readonly y;,return ref readonly,foreach (readonly ref i in ...)inandref readonlyin lambdainin delegateinin local functioninin async and iterator methods (disallowed)inorref readonlyat call site (disallowed)refandreadonlydoes matter (what is the error? should we offer a fixer?)inin pattern-based lowering:Deconstructmethod withinparameters is not applicable for deconstructioninin builder typeGetEnumerator(in int count = 10)(see Overload resolution for GetEnumerator fails with optional parameters #19742)OHI
ref/out/in(we only consider types)M(1);withvoid M(ref readonly int i) { }andvoid M(int i) { }in situation of ambiguity (because of inheritance or extension methods)verify
inis invariant (because of CLR limitation, just likeout)Verify that IL for copy versus no-copy
M(someInArg, async M2(), someOtherInArg)-
someInArgcould be local, constant,RefReadonlyM(refReadonlyField),someArray[index],fieldNo writes allowed:
refin S s, thens.Mutate();) (also allowed, operating on a temp copy, verify IL. But no copy ifSis readonly struct)inthrough is okinparameters cannot be captured (lambda/async/iterator)inallowed in indexer and operator parameterstest VB interop (calling blocked because modreq on overridable members and delegate/interface methods, but allowed on non-overridable members)
taking pointer to ref readonly parameter (in unsafe code) is disallowed
passing
nameofexpression asinargument (expect copy?)Ref readonly returns
Spec exists
insyntax disallowed in method declarationref readonlyreturn in async method (no syntax for it)ref readonlyreturn on operator is disallowed (no syntax for it)ref readonlyon indexer (allowed)readonlyis disallowed inreturnstatementsignature needs exact match in OHI
inandrefref-readonly-returning lambda?
calling with a discard (no syntax for it?)
metadata:
IsReadOnlyAttributegets embbeded if not found, disallowed in sourceInAttributemodreq present butIsReadOnlyAttributeis missing, then cannot load metadataIsReadOnlyAttributeis present, butInAttributemodreq is missing, then can load and this absence of modreq will be carried over when overriding.Readonly struct
readonlyon class declaration and other illegal membersreadonlyis floating, butrefmust be next tostructinreadonly ref structpartialmust be beforereforstruct(but what about thereadonly?)instructreadonlyon half a partial struct (allowed, just like other modifiers)Obsoleteattribute given by user wins. There should be a warning.void M(in S s)withM(this), but notvoid M(ref S s).void M(S s)withM(this), but that will make a copy.thiscannot be captured by lambda or otherthisis disallowedRef ternary
(b ? ref x : ref y).M()whereMis ref extension method, regular extension method (error), regular method (error)xoryor both are readonly structs?M(b ? ref x : ref y)wherevoid M(in ...), I expect no temporary.b ? ref M() : ref M2()whereref readonly C M()(and same for M2), expect the ternary is readonlyb ? ref this : ref thiswherethisrefers to a readonly struct, is the ternary readonly?bis known to be constant (compiler knows which branch will be executed)?b ? ref x : yb1 ? ref (b2 ? ref x : ref y) : ref zref x ?? ref yb ? ref x : ref default(disallowed)inargument (M(in condition ? ref x : ref y);)Ref readonly extension methods
ref readonly S Extension(ref readonly this S s) { return ref s; }void RRExtension<T>(ref readonly this T t) { ... }(expect error)42.RRExtension()(ok, but makes temporary)readonlyField.RRExtension()(expect no temporary)refReadonlyParameter.RRExtension()(expect no temporary)M().RRExtension()(expect no temporary)this.RRExtension()(ref readonly ternary).RRExtension()(expect no temporary)Ref-like types and safety (Span)
ref readonly string M(ref readonly string s = "hello") { return ref s; }. Same with value type. (gives a unsafe-to-escape diagnostic)dynamic d = stackalloc int[10];Span<dynamic> d = stackalloc dynamic[10];Misc
(b ? ref x : ref x).foo()x.y.foo() // ref may be inferredIsReadOnlyandIsByRefLikeare disallowed in sourceSee also