Add design notes

This commit is contained in:
Mads Torgersen 2017-08-04 16:50:53 -07:00
parent 4663883c3b
commit bd52a5bbf1
2 changed files with 123 additions and 0 deletions

View file

@ -0,0 +1,112 @@
# C# Language Design Notes for Jul 24 and 26, 2017
## Agenda
We started putting a series of stakes in the ground for nullable reference types, based on the evolving strawman proposal [here](https://github.com/dotnet/csharplang/issues/790). We're doing our first implementation of the feature based on this, and can then refine as we learn things from usage.
1. Goals
2. Nullable reference types
3. Rarely-null members
# Goals
The goals make sense.
## Goal 3: Incrementality
Are our existing mechanisms good enough, or is more needed? Currently the proposal has an in-language approach to gradualness, where it mainly comes from the gradual addition of `?`s where nullability is intended, and dealing with the fallout of those one by one. We'll go in on the assumption that this is incremental enough, but trying the feature out may prove us wrong.
## Goal 4: No semantic impact
Should this only yield warnings, or should some of them be errors? It should at least be configurable, and we can change our mind about the default settings.
If they are errors *in the language*, that affects overload resolution. "Warn as errors" is better because it doesn't affect overload resolution.
There may be an opportunity for optimizations based on the compiler's reasoning about nullability; however it would be hampered by all the places where we aren't 100% sure. Worth looking into at some point.
## Goal 5: Library upgrades
Should libraries continue to do null checks?
Good question. We can't really trust our callers to not pass null. But maybe we can slide more in the direction of just letting the method body fail however it may, if people violate the contract, not checking at every boundary. The Midori experience was that this doesn't make a huge impact to performance, so it might not be worth changing guidelines away from explicit null checks.
It's very similar to when we added iterators. Some people will want to have a very high bar, others may be able to relax.
We have an accompanying feature proposal (not yet in the strawman) for a short hand null checking syntax for parameters:
``` c#
public void M(string name!) { ... }
// equivalent to
public void M(string name)
{
if (name is null) throw new ArgumentNullException(nameof(name));
...
}
```
# Nullable reference types
The strawman proposes an always-on, non-breaking nullable reference types feature, where a postfix `?` annotation on reference types leads to warnings if values thereof are dereferenced directly or indirectly without (what the compiler recognizes as) a null check.
## Warning on conversion
A key point of the proposal is that it mandates a warning on conversion from nullable to not-nullable reference type. This even though there is nothing wrong with having null in not-nullable reference types *until* you opt in to not-null checks.
This may seem controversial or overly aggressive, but we ended up approving it for a couple of reasons:
- If nullable reference types are to protect their values from being dereferenced even indirectly, they can only do so by warning when you take those values out of the nullable space.
- This leads to better incrementality, because you get these warnings piecemeal every time you add a `?`, instead of in a flood when you turn on non-null checks.
- These warnings aren't breaking. There's no reason to protect a feature behind a switch if it isn't breaking.
## Tracked variables
The strawman allows the null state of dotted chains `x.y.z` to be tracked by the flow analysis. Is this too lax? Imagine an await in the middle. There would absolutely be cases where this would lead us to consider code null safe even when there's a null at runtime.
We're assuming people are already checking for null, and are trying to help find where they forgot. We are *not* trying to add new idioms and features, we are trying to lower the bar for getting people to where the warnings they get are useful.
Stake in the ground: we will allow dotted chains. We may want to offer suggestions here, or a knob of aggression.
## Local variable declarations
The strawman proposes that we make a sort of exception for local variable declarations. There'll be a lot of local variable declarations in existing code using an explicit type (which in existing code never has a `?` on it). Their use of it may be completely null safe, but when something they get assigned gets a nullable annotations, those assignments would still generate a warning.
The concern is that such warnings will drown out warnings about actual null-unsafe code:
``` c#
void M(string s);
M(p.MiddleName);
string s = p.MiddleName; // warning here
M(s); // but the problem is really here
```
The strawman therefore proposes to *infer* the nullability of a local variable *even* when it is declared with an explicit type.
We ended up not agreeing to this. There's a worry about whether people understand the feature if type declarations mean different things in locals versus members.
The proposal is too preemptive. We're assuming people will be put off by warnings on this, but is it that bad? The cure may be worse than the disease. When people do not use `var` it is because they *want* to be explicit about the type, and inferring part of it anyway rubs right against that.
Instead we will try to mitigate the "drowning out" by having "fix all" options in the IDE, etc.
## Cross-assembly
Do we need per-assembly granularity? Concern: heading towards too much complexity.
Stake in the ground:
- Assemblies opt in with an attribute to having annotations. By default (no attribute) they don't. By default, however, the new compiler will put the attribute.
- You can opt out per assembly if you don't want their annotations heeded.
# Rarely-null members
The compiler code base has a property (`BoundExpression.Type`) that's non-null in 95% of the cases. In a few places under very specific and well-defined circumstances it is allowed to be null.
What's the guidance for this kind of property?
For this kind of member you want the burden to be put on the few, not the many. It should be declared as not null, and the few places that will use it as null anyway should explicitly circumvent the annotation. Those places are no worse off than today, and are in fact helped a little bit by warnings reminding them to circumvent the non-nullness (e.g. with a `!` operator).

View file

@ -268,3 +268,14 @@ Triage of features in the C# 7.2 milestone. They don't all fit: which should be
11. Readonly locals and params *(X.X)*
12. ref structs in tuples *(don't)*
13. Overload resolution tie breakers with long tuples *(use underlying generics)*
## Jul 26, 2017
[C# Language Design Notes for Jul 24 and 26, 2017](LDM-2017-07-26.md)
We started putting a series of stakes in the ground for nullable reference types, based on the evolving strawman proposal [here](https://github.com/dotnet/csharplang/issues/790). We're doing our first implementation of the feature based on this, and can then refine as we learn things from usage.
1. Goals
2. Nullable reference types
3. Rarely-null members