# C# Language Design Notes for August 20, 2018

## Agenda

Nullable open issues:

1. Remaining questions on [suppression operator](https://na01.safelinks.protection.outlook.com/?url=https%3A%2F%2Fgithub.com%2Fdotnet%2Froslyn%2Fissues%2F28271&data=02%7C01%7C%7C6defe1e21ab54cce8d0008d606be5d23%7C72f988bf86f141af91ab2d7cd011db47%7C1%7C0%7C636703812006445395&sdata=DAdh5dev1mnr%2F5zxtvuJVcHP%2Bzewrzz4z9iuGkl%2BUHg%3D&reserved=0) (and possibly cast)
2. Does a dereference update the null-state?
3. Null contract attributes
4. Expanding the feature
5. Is T? where T : class? allowed or meaningful?
6. Typing judgments containing oblivious types
7. Unconstrained T in List\<T> then `FirstOrDefault()`. What attribute to annotate `FirstOrDefault`?

# Discussion

# Remaining questions on suppression operator

## 1.1 Suppression of nested nullability

*Q: Should `!` suppress warnings for nested nullability?*

There's a question here about the interplay of casting and the `!` operator,
since they do somewhat similar things. The problem with the current design is
that neither casting nor `!` give any warnings/errors at either compile time
or runtime for nested nullability. We think it would be useful to have at
least one mechanism that provides more safety around nullable.

**Conclusion**

The user should get a warning if you cast away nested nullability. No warning
if you use '!'. This provides a safety mechanism for casts and still allows
for an "I Know Better" command, which was the primary motivation for `!`.

## 1.2 Meaningless `!` operators

*Q: Should `nonNull!` result in a warning for unnecessary `!`?*

**Conclusion**

These constructs are meaningless, but unlikely to do something the user
didn't want. No compiler warnings for `!!...` or `nonNull!`. Maybe an IDE
feature.

## Result of `obliviousValue!`

**Conclusion**

Top-level non-null, suppressed warnings for nested.

# Dereference of nullable types

Consider the following example:

```C#
string? x = y;
var z = x.Substring(1);
...
```

**Conclusion**

There's clearly a problem with this example that will generate a warning: `x`
is a nullable reference type, but it's being dereferenced without a null
check. The first question is: what is the type of `x` after the call? The
answer is non-nullable `string`, but the more detailed answer is this results
in a split state. The specification recognizes that dereferencing a null
value results in a NullReferenceException, so state should be split into
exceptional and non-exceptional flow.

To spell it out in a more detailed example:

```C#
try
{
    string? x = y;
    var z = x.Substring(1);
    ...
}
catch
{
    ...
}
```

After the `Substring` call, `x` is non-nullable, but in the catch block, `x`
is nullable, as there was a potential NullReferenceException if `x` was null.
Additionally, like other flow control warnings, we will only provide a warning
about dereference of nullable type once. So if `x` is dereferenced again in
the catch block, another warning will not be produced.

## Null contract attributes

We've started building a list of attributes that are useful for annotating
existing code to note when null or non-null types are produced. For instance,
`string.IsNullOrEmpty()` will always be true if the receiver was null. It would
be useful to mark this method with an attribute which can indicate this to the
compiler, so

```C#
if (!x.IsNullOrEmpty())
{
    ...
}
```

should let the compiler know that `x` is not null within the `if` block.

*Q: Do we want to do a larger review of all these attributes?*

**Conclusion**

Let's take all the current attributes and start prototyping. We'll adapt
as we move forward.

## Expanding the feature

We have two potential extensions here.

The first is possibly allowing a `!` annotation on a parameter *name*, which
indicates that the compiler should produce a dynamic null check on entry to
the method. For instance, a method `void M(string s!) { }` would not only
indicate that `s` is meant to be a non-nullable reference type if the
nullable reference type feature is fully enabled, it would also insert code
at the beginning of the method to throw an `ArgumentNullException` if `null`
is passed anyway.

The second is how to treat nullable value types. There are two extensions we
are considering for nullable value types. The first is about extending the
analysis. This is as simple as extending flow analysis to update null state
of nullable value types based on information of null checks. For instance,
accessing `.Value` on a `Nullable<T>` could produce a warning if the value
was accessed without checking for null first. An even more advanced extension
would allow `Nullable<T>` values to be accessed without going through `.Value`
if the variable is proved to not be null.

**Conclusion**

Jared's going to write up a proposal on the compiler-inserted dynamic checks.

For `Nullable<T>`, we agree with extending the analysis. We're not sure about
the silent call of `.Value` or automatic conversion.

## `class?` constraint

*Q: Is `void M<T>(T? t) where T : class?` allowed?*

**Conclusion**

Rule: you can only use `?` on types you know to be non-nullable.

`T : class?` is possibly nullable, so you can't use `T?`.

## Typing judgments containing oblivious types

Mainly comes down to the type of `x` in `var x = oblivious;`.

We need more time for this. Return later.

## Annotating `List<T>.FirstOrDefault()`

*Q: Unconstrained T in `List<T>` then `FirstOrDefault()`. What attribute is
*used to annotate `FirstOrDefault`?*

**Conclusion**

[MaybeNull], since `T` is unconstrained and could either be a nullable or
non-nullable type. More information may be available after type substitution.
