roslyn: CS8619 False positive on Task instantiated with different nullability

Version Used

Visual Studio 2019 (16.5.0 Preview 1.0)

Steps to Reproduce

using System.Threading.Tasks;
#nullable enable
public class C {
    public Task<(bool Result, string? Extras)> M() {
        return Task.FromResult((true, ""));
    }
}

SharpLab

Expected Behavior

Since string is a valid string?, no warning should be presented.

Actual Behavior

Warning CS8619: Nullability of reference types in value of type ‘Task<(bool, string)>’ doesn’t match target type ‘Task<(bool Result, string? Extras)>’.

About this issue

  • Original URL
  • State: open
  • Created 4 years ago
  • Reactions: 3
  • Comments: 18 (11 by maintainers)

Most upvoted comments

@inf9144

You are welcome to take part in this conversation instead of just putting thumbs down emojis on someones post 😉

People can engage however they want (within the rules) 😃

If you see this as variance issue it would be a problem to just declare in or out on the generic type parameters like you can do on interfaces, because this would not be what you would like to express. You dont want the class working/castable/assignable for T<XBase> or T<XDerived>. This would also come with the cost that every contravariance or covariance in C# brings - you cannot use the generic type in all situations on the generic class/struct anymore. (Return Values, Method Parameters, …). Can you understand why i dont match this problem to type variance but to nullability? If this is a variance problem, you would need to have the ability to explicitly define the variance in means of nullability. So having an invariant type which is nullable co-/contravariant. Only then you could relax those restrictions that normally come with covariance/contravariance in general, because (yes on runtime level) you can be sure you have the very same types (in case of reference types).

@Logerfo You are welcome to take part in this conversation instead of just putting thumbs down emojis on someones post 😉

//EDIT: And it would not be nice to only solve this for Task<T> / ValueTask<T> because this hits also custom structs that have those generics and behave like ValueHolder, Optionals or …

It’s a bug in my eye, no matter if we talk about variance here.

Sorry, variance is very significant. Just because something is assignable doesn’t mean you can replace one with the other. For example, all strings are objects. But that doesn’t make all lists of strings into lists of objects.

Have a method and cant make an overload for this

Correct, and that will always be the case (and is unrelated to this issue). Those types are identical as far as the runtime is concerned. So they literally cannot both exist at the same time.

Anything new here? Got my self in this situation, not only with Task<T> but also with MyType<T>,

Have a method and cant make an overload for this because string is a reference type and those types are identical: void Xyz<TFrom>(Expression<Func<TFrom, MyType<string>>> param); void Xyz<TFrom>(Expression<Func<TFrom, MyType<string?>>> param);

My hacky workaround would be something like

void Xyz<TFrom>(Expression<Func<TFrom, MyType<TString>>> param)
  where TString: IEnumerable<char>?

This should really be fixed because it affects all generic types and their usage as parameters… string is fullfilling string? so i cant get why this warning appears on the direction of notnull to nullable assignements. It’s a bug in my eye, no matter if we talk about variance here.

https://github.com/dotnet/csharplang/issues/3950

today and we’re going to look into adding some special treatment for Task<T> and possibly ValueTask<T> to treat them as covariant.

Woudl be great ot have an attribute to allow other types to play here IMO 😃

This is currently by-design, as the type parameter of Task is invariant. That said, LDM discussed this today and we’re going to look into adding some special treatment for Task<T> and possibly ValueTask<T> to treat them as covariant.

Relates to https://github.com/dotnet/roslyn/issues/40759 FYI @cston