roslyn: [MaybeNull]: Cannot implement a generic method with a "default" return value with nullable references

I’m trying to implement methods that return an optional of a given type T but get a compiler error. Changing return type to T? only changes the error.

I understand why compiler provides that error, it makes sense, but I have no idea how I can get around it in the world of nullable references. Should I change the design of such methods?

Version Used: Roslyn_Nullable_References_Preview_09112018

Steps to Reproduce:

        public static T FirstOrDefault<T>(this IList<T> list)
        {
            if (list.Count == 0)
            {
                return default; // CS8625
            }
            return list[0];
        }

Expected Behavior: The code compiles fine without nullable references. I’d expect it to work the same.

Actual Behavior: Compiler error CS8625: Cannot convert null literal to non-nullable reference or unconstrained type parameter.

About this issue

  • Original URL
  • State: closed
  • Created 6 years ago
  • Reactions: 8
  • Comments: 18 (11 by maintainers)

Commits related to this issue

Most upvoted comments

Wow, one can apply attributes to generic types. I didn’t know it)

public static async Task<T> GetAsync<[MaybeNull]T>() {
    await Task.Yield();
    return default!;
}

Only why does MaybeNullAttribute not have AttributeTargets.GenericParameter ? It could work. But it doesn’t (( Hope in C# 9 you will come up with something.

@Denis535 There is not way to return default from an async method without warning or suppression at the moment. We are planning to allow a T?? syntax that could be used in this example in Task<T??> (meaning maybe-null even if the type is non-nullable). Tracked by https://github.com/dotnet/roslyn/issues/29146

For the more general question, unconstrained generic types could be nullable or not-nullable. If you consume one, you need to be careful as it may be nullable. If you return one, you need to be careful as it may be not-nullable.

As I wrote in dotnet/csharplang#2194, it seems extremely problematic to change the meaning of T? to mean anything other than Nullable<T> for value types, in any context.

Methods like FirstOrDefault<T> could be simply be annotated with the [MaybeNull] proposed here for the same effect.

Thanks. This is a known issue.

T? is currently only valid when T is constrained to be a reference type. For unconstrained type parameters, the current plan from LDM is to use an attribute, [MaybeNull], to indicate that the value may be null when the type is a reference type. But there are still language design issues with this: T local = list.FirstOrDefault(); would still produce a warning and attributes are not allowed on local declarations…

Tagging @cston since he was looking into this recently.

@ssg generic type constraints aren’t part of the method signature, so this can’t work (see https://github.com/dotnet/csharplang/issues/2013).

Our current plan is for nullability attributes like [return: MaybeNull] not to affect method bodies. So return default; would still warn for now. We will look at refining this post C# 8 RTM. This is tracked by https://github.com/dotnet/roslyn/issues/36039 (Should nullability attributes affect method bodies and OHI?)

@YohDeadfall The LDM writeup is here. It’s part of a wider suite of attributes.

For whatever it’s worth, I was able to implement this without any compiler errors on .NET Core 3.0 preview 5 and it seems to be working fine (except for non-nullable references, of course):

    static class A
    {
        public static T FirstOrDefault<T>(this IList<T> list) where T: struct
        {
            if (list.Count == 0)
            {
                return default;
            }
            return list[0];
        }
    }

    static class B
    {
        public static T? FirstOrDefault<T>(this IList<T?> list) where T: class
        {
            if (list.Count == 0)
            {
                return default;
            }
            return list[0];
        }
    }

When you use it, it uses the correct overload depending on the type of the list item.