153 lines
4.2 KiB
Markdown
153 lines
4.2 KiB
Markdown
# C# Language Design Notes for Apr 25, 2018
|
|
|
|
***Warning: These are raw notes, and still need to be cleaned up. Read at your own peril!***
|
|
|
|
## Agenda
|
|
|
|
|
|
# Warn on nullable ref type argument when T has `class` constraint
|
|
|
|
``` c#
|
|
static void F<T>(T t) where T : class { t.ToString(); /* no warning */ }
|
|
F(maybeNull); // warning: string? does not satisfy 'class' constraint
|
|
F<string?>(string.Empty); // warning: string? does not satisfy 'class' constraint
|
|
```
|
|
|
|
This is a real danger, and we should be warning here.
|
|
|
|
Note that in both cases the warning is about constraint checking: even in the second line we infer `string?` and it warns on constraint check.
|
|
|
|
We should probably have the error message say that this is because of nullability.
|
|
|
|
|
|
# Warn on nullable reference type argument when T has non-nullable reference type or interface type constraint
|
|
|
|
``` c#
|
|
static void F<T>(T t) where T : IDisposable { }
|
|
F(maybeNull); // warning: Stream? does not satisfy 'IDisposable' constraint
|
|
```
|
|
Yes, warn for same reasons.
|
|
|
|
We also want to warn (or error) on inconsistent nullability in constraints. Specifically if one constraint is known to be non-nullable and another is known to be nullable.
|
|
|
|
What about
|
|
|
|
``` c#
|
|
class C<T, U, V> where T: class? where U : T, IDisposable where V : T, IDisposable?
|
|
{
|
|
// Fine
|
|
}
|
|
class D<T, U, V> where T: class where U : T, IDisposable where V : T, IDisposable?
|
|
{
|
|
// Warn on V
|
|
}
|
|
```
|
|
|
|
``` c#
|
|
... where T : Node<T>?
|
|
... where T : Node<T?> // both currently allowed, but not where T : Node<T?>?
|
|
... where T : class, INode<T?>
|
|
... where T : class?, INode<T>?
|
|
```
|
|
|
|
``` c#
|
|
static void F<T>(T t) where T : IFoo<string?> { }
|
|
IFoo<string> foo;
|
|
F(foo); // warning, unless IFoo is covariant
|
|
```
|
|
|
|
Another kind of clash:
|
|
|
|
``` c#
|
|
interface IBar : IFoo<string> {}
|
|
static void F<T>(T t) where T : IFoo<string?>, IBar { }
|
|
```
|
|
|
|
We want to warn on this: maybe find shared base interfaces between the two constraints, and check if they have consistent nullability.
|
|
|
|
Other, slightly more complex example, same conclusion:
|
|
``` c#
|
|
interface IBar : IFoo<string?> {}
|
|
static void F<T, U>(T t) where T : IFoo<U>, IBar where U : class{ }
|
|
```
|
|
|
|
|
|
# Allow nullable reference types and interface types as constraints
|
|
|
|
`where T : Person?, IDisposable?`
|
|
|
|
Yes
|
|
|
|
# Allow `class?` constraint
|
|
|
|
Yes
|
|
|
|
How do we express in metadata?
|
|
|
|
Let's put top-level nullability as an attribute on the type parameter itself, rather than on the constraints. We can noodle more on this later.
|
|
|
|
# Allow `object` constraint
|
|
|
|
``` c#
|
|
static void F<T>(T t) where T : object { }
|
|
F(maybeNull); // warning: constraint violated
|
|
```
|
|
|
|
## Need to talk about relationship to value types soon
|
|
|
|
``` c#
|
|
int? i = null;
|
|
object o = i;
|
|
o.ToString();
|
|
```
|
|
|
|
# Allow `object?` explicitly?
|
|
|
|
It's the "most general" constraint that is implied by unconstrained generics. Today we disallow `object` because of that.
|
|
|
|
We don't know that this is a terribly useful restriction today, but we'll keep doing it (now with `object?`) unless we get evidence to the contrary.
|
|
|
|
|
|
# Warn on dereference of T when T is unconstrained?
|
|
|
|
Yes, if a type parameter `T` can be instantiated with a nullable reference type, then we should track null state and warn on unguarded dereference.
|
|
|
|
The warning when occurring on unconstrained generics might suggest using the `object` constraint.
|
|
|
|
|
|
# Warn on assignment to `object` of unconstrained `T`?
|
|
|
|
Yes. This can be a `W` (cosmetic) warning when the `object` variable is a local.
|
|
|
|
|
|
# default(T) with unconstrained T
|
|
|
|
``` c#
|
|
T M<T>()
|
|
{
|
|
T t = default(T); // W warning
|
|
return default(T); // safety warning
|
|
}
|
|
```
|
|
|
|
This is something completely safe. I don't want it to warn!:
|
|
|
|
``` c#
|
|
T M<T>()
|
|
{
|
|
var t = default(T);
|
|
if (something) t = somethingelse;
|
|
if (t != null) WriteLine(t.ToString());
|
|
}
|
|
```
|
|
|
|
This could be an argument for allowing `T?` that is not about special methods. Or it is an argument against having the W warnings at all.
|
|
|
|
Let's keep this example around and revisit. But for now, let's consider `default(T)` to be potentially null, and therefore warn on its unguarded use.
|
|
|
|
|
|
# Annotations
|
|
|
|
We would like to deal with methods with special null semantics, such as string.IsNullOrEmpty, TryGetValue, Assert.NotNull, Object.Equals, Object.ReferenceEquals.
|
|
|
|
We'll come back to this later.
|