csharplang/meetings/2017/LDM-2017-08-16.md
2017-10-05 20:15:39 -07:00

4 KiB

C# Language Design Notes for Aug 16, 2017

Warning: These are raw notes, and still need to be cleaned up. Read at your own peril!

"It's an open question whether we go out with a bang!"

Agenda

The dammit operator

Proposal:

  1. Target typed: e! implicitly converts to T if e does, but without nullability warnings
    • string s = null!;
    • string s = default!
    • string s = GetNameOrNull()!;
    • List<string> l = GetList<string?>()!;
    • List<string?> l = GetList<string>()!;
  2. Inherent type: if the type of e is a nullable reference type T?, then the inherent type of e! is T
    • var s = GetNameOrNull()!;
    • GetNameOrNull()!.Length;
  3. Default expressions: if T is a non-nullable reference type, then default(T)! suppresses the warning normally given by default(T)

For 2, an alternative is to have a dedicated !. and ![...] operator, cousins of ?. and ?[...]. Then you wouldn't get to factor out to a local with var.

3 is a bit of a corner case. Most people would choose to just rewrite it to something else - there are plenty of options. But default(T) is a good strategy for code generators.

We could generalize to ! silencing all nullability warnings even in subexpressions. No.

If ! is applied in a place that yields no nullability warnings, does that lead to a warning? No.

We can make !! and error. If you really want to (we don't believe there's any scenario, other than swearing) you can parenthesize, (e!)!.

An alternative is to make the type of e! oblivious, if we choose to embrace a notion of oblivious. That's attractive in that it makes a type for "something that doesn't yield warnings", but it's also viral - could lead to many things not being checked. Option to be considered in future.

Discussion about var and the type of nullable things known not to be null

T t = ...;

if (t != null)
{
    int s = t.ToString();
}

string? s = ...;
var s1 = s;

if (s != null)
{
    var s2 = s;
	int l = s.Length;
}
string s1 = "Hello";
var s2 = s1; // 

This could be safe:

void M1(ref string s);
void M2(ref string? s);

var s = "foo";

if (s == null);
{
	M1(ref s);
}
else
{
	M2(ref s);
}

Three discussions:

  1. What is the type of the value of a local variable string? s in a scope where it is known to be non-null? Does it contribute the type string to e.g. type inference, or does it remain string?
  2. What is the meaning of var? Does it infer its nullability along with the rest of its type, from the initializer, or does it remain able to be assigned null even when initializer is not-null?
  3. Should we reconsider completely abandoning the notion of nullness being part of a local's type at the top level?
    1. could even consider for value parameters
T M<T>(T t);

string? s = "abc";
var l = M(s).Length;

Type inference

Proposal:

  • Consider nullness an orthogonal aspect to the rest of the type being inferred
  • If any contributing type is nullable, that should contribute nullness to the inference
  • A null literal expression should contribute nullness to the inference, even though it doesn't otherwise contribute to the type

Structs with fields of non-nullable type

Structs can be created without going through a declared constructor, all fields being set to their default value. If those fields are of non-nullable reference type, their default value will still be null!

It seems we can chase this in three ways:

  1. Not at all. We just aren't that ambitious.
  2. We warn on all fields of structs that have non-nullable reference types. That's a lot! How do you "fix" it? Make them nullable? No version of the ! operator works here, since the whole point is you don't control initialization from user code.
  3. We warn whenever a struct that has such fields is created as a default value. In other words, we treat the type the same as a non-null reference type, recursively. (And we warn on passing for a type parameter constrained by new() or struct?)

Dotted names problems