Skip to content

Missing nullable warnings for scenarios involving type constraints that a nullable value type can satisfy. #36005

@AlekseyTs

Description

@AlekseyTs
namespace ns1
{
#nullable enable
    class Test1<T>
    {
        public virtual void M1<S>(S x) where S : T
        { }
        public virtual void M2(T x)
        { }
    }

    class Test2 : Test1<System.ValueType>
    {
        public override void M1<S>(S x)
        {
            object y = x;
            y.ToString();
            System.Console.WriteLine("Test2");
        }

        public override void M2(System.ValueType x)
        {
        }

        public void Test()
        {
            int? x = null;
            M1<int?>(x);
            M2(x);
        }
    }

    class Test3 : Test1<object>
    {
        public override void M1<S>(S x)
        {
            object y = x;
            y.ToString();
            System.Console.WriteLine("Test3");
        }

        public override void M2(object x)
        {
        }

        public void Test()
        {
            int? x = null;
            M1<int?>(x);
            M2(x);
        }

        static void Main()
        {
            new Test2().Test();
            new Test3().Test();
        }
    }
}

Compile the code above.

Observed (no nullable warnings for M1 calls):

.cs(50,16): warning CS8604: Possible null reference argument for parameter 'x' in 'void Test3.M2(object x)'.
.cs(29,16): warning CS8604: Possible null reference argument for parameter 'x' in 'void Test2.M2(ValueType x)'.

Run the code

Unhandled Exception: System.NullReferenceException: Object reference not set to an instance of an object.
   at ns1.Test2.M1[S](S x)
   at ns1.Test2.Test()
   at ns1.Test3.Main()

Expected:
Expected nullable warnings either for M1 invocations (constraint violation), or within M1 implementations. Given the recent design decision around notnull constraint, a constraint violation warning feels more appropriate.

Note that for the code below, we do get warnings inside implementation of M1 and I think we should keep getting them:

    class Test4 : Test1<int?>
    {
        public override void M1<S>(S x)
        {
            object y = x;
            y.ToString();
        }

        public override void M2(int? x)
        {
        }

        public void Test()
        {
            int? x = null;
            M1<int?>(x);
            M2(x);
        }
    }

Metadata

Metadata

Assignees

No one assigned

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions