
# C# Language Design Notes for Apr. 15, 2019

## Agenda

1. CancellationToken in iterators
2. Implied nullable constraints in nullable disabled code
3. Inheriting constraints in nullable disabled code
4. Declarations with constraints declared in #nullable disabled code
5. Result type of `??=` expression
6. Use annotation context to compute the annotations?
7. Follow-up decisions for pattern-based Index/Range

## Discussion

### Proposal for CancellationToken in iterator method

The proposal is that we allow a parameter of type CancellationToken
to an iterator to be decorated with an attribute that specifies
that the parameter is to be used in the state machine for cancellation
if none is provided.

**Conclusion**

Approved. We will also allow the token in WithCancellation to override the token provided to the
iterator. We will mitigate potentially surprising behavior by making the attribute named
`DefaultCancellation` or similar.

### Implied constraints of type parameters in #nullable disabled code

Consider the following declaration:

```C#
#nullable disable
interface I
{
    T M<T>(T arg);
}
```

If you provide an implementation,

```C#
#nullable enable
class A : I
{
    public T M<T>(T arg) where T : object
    {
    }
}
```

this will provide a warning because currently the implied constraint on `I.M<T>` is `object?`.
The proposal is that the implicit constraint should instead be "oblivous `object`", meaning that
an implicit interface implementation will not provide a warning, and an explicit interface
implementation will inherit the oblivious constraint.

**Conclusion**

Yes, the implied constraint in a disabled context is oblivious. We previously did not consider
context when deciding the implicit constraint, but seeing this example, we have decided
differently for disabled context.

### Inheriting constraints in #nullable disabled code

```C#
#nullable enable
public class A
{
    public virtual void F2<T>(T y) where T : class?
    {
    }
}

class B : A
{
#nullable disable
    public override void F2<U>(U y)
    {
        ...
    }
}
```

What is the implied constraint for the type parameter `U`?

**Conclusion**

The constraint is inherited from the definition, which is `class?`. Since you cannot write a new
constraint in the override, there's no point in doing anything but inheriting.

### Declarations with constraints declared in #nullable disabled code

```C#
#nullable disable
class A<T1, T2, T3> where T2 : class where T3 : object
{
    #nullable enable
    void M2()
    {
        T1 x2 = default; // warning?
        T2 y2 = default; // warning?
        T3 z2 = default; // warning?
    }
}
```

Proposal 1: The declarations using the type parameters are treated as unconstrained. All lines
require a warning by previous decisions.

Proposal 2: The declarations using the type parameters are treated as oblivious. None of the
lines produce warnings.

**Conclusion**

Proposal 2 seems to fit with the philosophy of oblivious, where we do not provide warnings and
trust the user to know the correct nullability of their types. Proposal 2 accepted.

### Result type of `??=` expression

Right now the result type of `??=` is always the type of the LHS of the assignment. This is
consistent with all other assignment operators in the language. However, there's been some desire
for a closer analogy to the `??` instead of to the assignment operators.

That change would mean the following:

```C#
int? b = null;
var c = b ??= 5;
// b is int?
// c is int
```

**Conclusion**

We will accept the design change to be closer to the behavior of `??`. This means that we will no
longer maintain the principle that an assignment can be replaced with the variable being assigned
in an expression context. While that is unfortunate, we think the use cases are good enough to
justify the drawback. The result of the `??=` expression will be the result of an implicit
conversion from the RHS to the underlying type of the LHS, if one exists. Spec note: if possible
it would be cleaner to specify this in terms of `??`.

### Use annotation context to compute the annotations?

The current LDM decisions have a tentative decision to prohibit inferring oblivious in type
inference, as a part of the principle that type inference does not infer "unspeakable" types.

The proposal is that we change the spec to remove this requirement. We do not seem to have any
definitive examples of where this rule would reduce confusion or provide a lot of value, and it
seems to be fairly complicated both to implement and also to spec the results of all of the
situations in which oblivious states can enter into type inference.

**Conclusion**

While the principle is valuable, it doesn't seem to carry over to this area completely.
It's agreed that we will remove this part of the spec and keep our current implementation.

### Follow-up decisions for pattern-based Index/Range

A small note that there were the following changes or refinements for pattern-based Index/Range:

- All members in the pattern must be instance members 
- If a Length method is found but it has
the wrong return type, continue looking for Count 
- The indexer used for the Index pattern must
have exactly one int parameter 
- The Slice method used for the Range pattern must have exactly
two int parameters - When looking for the pattern members, we look for original definitions, not
constructed members
