# 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\ 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` could produce a warning if the value was accessed without checking for null first. An even more advanced extension would allow `Nullable` 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`, 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) 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.FirstOrDefault()` *Q: Unconstrained T in `List` 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.