Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make annotations between Task.Exception and Task.Status=Failed possible to allow usage without the dammit operator #2720

Closed
jnm2 opened this issue Aug 15, 2019 · 8 comments

Comments

@jnm2
Copy link
Contributor

jnm2 commented Aug 15, 2019

There isn't currently a way to annotate Task.Exception in the BCL so that the compiler knows that it is not null if and only if the value of Task.Status is Faulted:

switch (task.Status)
{
    case TaskStatus.RanToCompletion:
        // Do something with:
        _ = task.Result;
        break;
    case TaskStatus.Faulted:
        // Doing something here requires the dammit operator
        _ = completedTask.Exception!.InnerExceptions;
        break;
    case TaskStatus.Canceled:
        // ...
        break;
    default:
        // Was running, use .ContinueWith
        break;
}

This would become possible if the C# spec recognized an addition to the current attributes in System.Diagnostics.CodeAnalysis.

Straw man 1:

[NotNullWhenMemberEquals(nameof(Status), TaskStatus.Faulted)]
public AggregateException Exception { get; }

Straw man 2:

[NotNullWhen(TaskStatus.Faulted, SourceMember = nameof(Status))]
public AggregateException Exception { get; }

This could be generally useful in other types.

@huoyaoyuan
Copy link
Member

I hope some concept like associating value with enum members in Rust can be introduced. That solves the problem better.

@PathogenDavid
Copy link

@huoyaoyuan Not totally on topic, but the concept you're referring to is called discriminated unions. There's a C# X.0 feature suggestion if you're interested in following it or participating in the discussion.

@huoyaoyuan
Copy link
Member

@PathogenDavid If discriminated union has been added, we can expose members of Task following that pattern, instead of adding some complex rules to annotate shape of current members.

@PathogenDavid
Copy link

PathogenDavid commented Aug 15, 2019

Sure we could've, but that ship has sailed. The Task.Exception property has existed since .NET Framework 4.0. I'm sure many other APIs fit this same shape and as such have the same problem. It's too late now, and it'll probably be a while until discriminated unions are a thing.

@PathogenDavid
Copy link

On a more on-topic note: @jcouv has previously stated that this sort of thing would be a whole new feature on top of what already exists for nullable reference types.

@quinmars
Copy link

I think for post conditions like that, code contracts (#105) would be ideal:

public AggregateException Exception
{
    get ensures return != null when Status == TaskStatus.Faulted;
}

@yaakov-h
Copy link
Member

I wouldn't bet on method contracts coming along and solving this problem. The implementation from Microsoft Research had a whole host of problems and very poor performance. Although it predates Roslyn, I have severe doubts it would be able to be integrated into something as performance-sensitive as a compiler.

@jnm2
Copy link
Contributor Author

jnm2 commented Mar 4, 2020

Work on this is underway and tracked at dotnet/roslyn#41438. New API at dotnet/runtime#31877.

@jnm2 jnm2 closed this as completed Mar 4, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants