100 lines
3.5 KiB
Markdown
100 lines
3.5 KiB
Markdown
|
|
||
|
# C# Language Design Notes for Oct 24, 2018
|
||
|
|
||
|
## Agenda
|
||
|
|
||
|
1. [Adding Nullable Reference Type features to Nullable Value Types](https://github.com/dotnet/csharplang/issues/1865)
|
||
|
2. Open issues with pattern matching
|
||
|
|
||
|
## Discussion
|
||
|
|
||
|
### Tracking null state for `Nullable<T>`
|
||
|
|
||
|
Note that the proposed null tracking would not warn on `GetValueOrDefault`,
|
||
|
which is legal on null values.
|
||
|
|
||
|
*Q: Do we want to prohibit `!` on assigning to a `Nullable<T>`?*
|
||
|
|
||
|
`Nullable<T>` is fully type-safe and the analysis is precise. The only cases
|
||
|
we can't prove are complicated uses/checks that the compiler can't prove. If
|
||
|
we force the user to rewrite their code such that the compiler can prove
|
||
|
safety, we may not need `!`.
|
||
|
|
||
|
**Conclusion**
|
||
|
|
||
|
Let's extend the tracking for nullable reference types to `Nullable<T>`.
|
||
|
|
||
|
For banning, `!`, we're worried this is too restrictive and there still may
|
||
|
be places where you want the "easy out" of `!`.
|
||
|
|
||
|
Note: For the flow analysis, we will consider a value type that is accessed
|
||
|
as though it were non-null to be non-null thereafter. There is no situation
|
||
|
for the reverse for value types. We will never treat a non-Nullable value
|
||
|
type as a Nullable value type, regardless of how you treat it.
|
||
|
|
||
|
### Using nullable values as non-nullable values
|
||
|
|
||
|
*Q: Would we regard the new uses of the underlying members as implicitly turning
|
||
|
on nullable warnings?*
|
||
|
|
||
|
**A:** Yes, probably.
|
||
|
|
||
|
Pros:
|
||
|
- It's more convenient
|
||
|
- It's safe because you'll get a warning
|
||
|
|
||
|
Cons:
|
||
|
- The checking is not precise, because we allow `!`
|
||
|
- Type analysis is less precise, but easier to understand. We adopted the
|
||
|
flow analysis mainly because we had back compat concerns with existing
|
||
|
null checking code for reference types. Here we don't have to deal with
|
||
|
backwards compatibility.
|
||
|
- This would also contravene the information provide the declaration site.
|
||
|
This figures into seeing the annotation at the declaration vs the use site
|
||
|
|
||
|
**Conclusion:**
|
||
|
|
||
|
We're not going to do this.
|
||
|
|
||
|
### Pattern matching open issues
|
||
|
|
||
|
#### Is a pattern permitted to match a pointer type?
|
||
|
|
||
|
You can't explicitly match a pointer because you can't write a pointer type
|
||
|
as a pattern (`*` is used for multiplication in this context).
|
||
|
|
||
|
However, it would be weird to make an exception for discard and `var`, so it
|
||
|
will be allowed for those use cases.
|
||
|
|
||
|
*Q: What about `ptr is null`? Or `ptr is {}`?*
|
||
|
|
||
|
Allow `ptr is null`. No `ptr is {}` or anything else.
|
||
|
|
||
|
#### ITuple vs unconstrained type parameter
|
||
|
|
||
|
Let's keep it an error for now. We may relax the restriction later.
|
||
|
|
||
|
### Matching ITuple in the presence of extension Deconstruct
|
||
|
|
||
|
This has some similarities in dynamic/static lookup conflicts in
|
||
|
GetEnumerator/IEnumerable. Currently, if we see a struct GetEnumerator that
|
||
|
doesn't match the pattern we provide an error, even if there is a valid
|
||
|
IEnumerable interface underneath. Here the Deconstruct is an extension
|
||
|
method, so the analogy is not perfect.
|
||
|
|
||
|
Using the Deconstruct method seems more consistent with what we do for
|
||
|
instance methods, although this would be different based on whether or not
|
||
|
the extension method is in scope.
|
||
|
|
||
|
However, it seems difficult to actually implement the check for extension
|
||
|
Deconstruct, because it's not clear whether none of the extension Deconstruct
|
||
|
methods match because they are not meant for the given receiver, or if they
|
||
|
were simply incorrectly written.
|
||
|
|
||
|
If we disregard Deconstruct, this would create a difference between the
|
||
|
behavior for instance Deconstruct methods and extension Deconstruct methods.
|
||
|
|
||
|
**Conclusion**
|
||
|
|
||
|
None. Let's look at the implementation in more detail and come back with a
|
||
|
proposal.
|