runtime: Nullable must be a readonly struct

Nullable<T> is known to be an immutable struct with sideeffect-free methods. C# compiler certainly relies on the implied purity for the soundness of null-propagating math as well as in various optimizations.

Basically - while for other structs readonly is a good thing to have, if possible. I think the spec for Nullable<T> should demand that Nullable<T> is formally readonly.

In particular we consider methods like HasValue not mutating and therefore callable directly on references to readonly fields (which is currently unverifiable). When verification rules are updated to understand readonly references, we would need to special case Nullable<T> methods. - Unless the type is readonly by the spec, then the behavior would be subsumed by the general treatment or readonly structs.

About this issue

  • Original URL
  • State: closed
  • Created 7 years ago
  • Reactions: 1
  • Comments: 22 (22 by maintainers)

Most upvoted comments

@danmosemsft, the compiler doesn’t necessarily know about the fields. For example, when writing code against DateTime, you’re compiling against the DateTime from ref assembly, at which point there are no fields. And even if it did, the readonly-ness is part of the contract, so whereas it’s not a breaking change to remove readonly from a field, it would be a breaking change if you removed readonly from a field and the compiler had previously used that knowledge in another compilation unit to treat that type as readonly.

Methods that Nullable overrides appears to propagate mutations to inderlying value. I wonder if that is deliberate. I guess that could be to match behavior of fancy boxing, which also exposes underlying to mutations.

I think we should specialcase HasValue, Value, ValueOrDefault as nonmutating in the compiler and tweak proposed verification rules for readonly refs accordingly.

So the following ReadOnlyStruct struct’s implementations are equivalent.

That’s the second part of what I wrote:

the readonly-ness is part of the contract, so whereas it’s not a breaking change to remove readonly from a field, it would be a breaking change if you removed readonly from a field and the compiler had previously used that knowledge in another compilation unit to treat that type as readonly.

If the readonly-ness is implict, then it becomes a breaking change to remove readonly from a field when every other field is also readonly, as someone could have taken a dependency on it even though it wasn’t intended to be a publicly visible fact. This would also wreak havoc on ref assemblies, where every struct could be marked readonly implicitly due to not having any fields.