- Where can #nullable go?
- Open issues with pattern matching
Q: Does #nullable
appear in metadata in any way?
It may be useful to minimize the amount of extra metadata by putting it on
containers (like types) and letting the context flow down. However, this
becomes complex when considering things like partial
. In general, we don't
want to let the implementation decide the language support and we would
prefer to limit things only if it improves the language experience, not makes
implementation easier.
Instead, we'll start by where it makes sense to allow #nullable
for the
language.
Q: If we were to allow it anywhere, where does it span?
One proposal is the last token in the type syntax. So
Dictionary<string,
#nullable disable
string>
#nullable enable
specifies the nullable context for the second string
and the context for
Dictionary<string, string>
.
Q: If we believe that the least restrictive version is a reasonable design, what makes sense from an implementation perspective?
Basing the context on the end type token is a reasonable design for implementation. If we decide that this causes significant extra metadata to be generated, we can provide optimizations at emit time without changing the semantics of the language.
Conclusion
Basing the nullable context on the last token in the pragma region actually makes a lot of sense. Let's go ahead with that as the current plan.
For warnings, we would base it entirely on diagnostic locations, which is what we do for pragma suppression right now.
Proposal: Do not allow nullable on the right of a using alias
Accepted.
Q: What about typeof
and nameof
?
Same behavior as nullable value types (allowed in typeof
, not in nameof
).
Proposal:
- If the type is a tuple type (any arity >1; see below) then use tuple semantics
- If the type has no accessible instance Deconstruct and satisfies the
ITuple
deconstruct constraints, useITuple
semantics - Otherwise attempt
Deconstruct
semantics (instance or extension)
Arguments against:
- Exhaustiveness gets better with an extension Deconstruct
- Different behavior between pattern matching and deconstruction
- But the extension and ITuple implementation should be semantically equivalent
- Boxes in the case of no instance Deconstruct and an explicit ITuple implementation
- But pattern matching boxes anyway for everything except a concrete value type
Arguments for:
- We don't have to add complex rules about which extensions are applicable
Alternative Proposal:
- If the type is a tuple type (any arity >1; see below) then use tuple semantics
- If "binding" a Deconstruct invocation finds one or more applicable methods, use Deconstruct.
- If the type satisfies the
ITuple
deconstruct constraints, useITuple
Conclusion
We like the alternative more, but very slightly.
Proposal:
Permit pattern-matching tuple patterns with 0 and 1 elements (appropriately disambiguated as previously decided)
if (e is ()) ...
if (e is (1) _) ...
if (e is (x: 1)) ...
if (e is (1, 2)) ...
The primary concern here is that these disambiguations are impossible in deconstructing declarations and deconstructing assignments. However, we think that's a fine limitation, since there are simple workarounds in all of those contexts. Pattern matching, however, has no such workaround.
Conclusion
Accepted.
Proposal:
Consider System.ValueTuple
and System.ValueTuple<T>
to be tuple types. No syntax changes.
Conclusion
Accepted.