csharplang/meetings/2018/LDM-2018-04-25.md
2018-05-18 16:04:30 -07:00

4.2 KiB

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

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

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

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
}
... 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>?
static void F<T>(T t) where T : IFoo<string?> { }
IFoo<string> foo;
F(foo); // warning, unless IFoo is covariant

Another kind of clash:

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:

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

static void F<T>(T t) where T : object { }
F(maybeNull); // warning: constraint violated

Need to talk about relationship to value types soon

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

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!:

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.