Skip to content

NullabilityInfoContext does not report the correct nullability for method in generic base class. #63555

Closed

Description

@madelson

Description

Let's say I have the following class defined in nullable enabled context:

private class NonNullableValuesDictionary : Dictionary<string, object> { }

If I try to add a null value to the object, I get a compiler error:

var dictionary = new NonNullableValuesDictionary();
dictionary.Add("a", null); // CS Cannot convert null literal to non-nullable reference type

However, I can't replicate this compiler behavior with NullabilityInfoContext even though it seems to me that the compiler must be reading the same metadata in order to issue that warning.

Reproduction Steps

public static void Main()
{
    var context = new NullabilityInfoContext();
    var addMethod = typeof(NonNullableValuesDictionary).GetMethod("Add")!;
    var info = context.Create(addMethod.GetParameters()[1]);
    Console.WriteLine(info.WriteState); // Nullable
}

private class NonNullableValuesDictionary : Dictionary<string, object> { }

Expected behavior

I would expect the code above to print "NotNull" instead.

Actual behavior

Prints "Nullable".

Regression?

No response

Known Workarounds

No response

Configuration

VS 17.0.2, .NET 6, Windows 10 x64.

I don't have any reason to believe this is configuration specific.

Other information

Some other fun cases:

class DerivedFromNonNullableValuesDictionary : NonNullableValuesDictionary { }
class GenericDerivedFromDerivedFromNonNullableValuesDictionary<T> : DerivedFromNonNullableValuesDictionary { }

class GenericDerivedFromDictionary<T, U> : Dictionary<T, T> { }
class GenericDerivedFromGenericDerivedFromDictionary<U> : GenericDerivedFromDictionary<object> { }

Activity

  1. ghost added
    untriagedNew issue has not been triaged by the area owner
    on Jan 9, 2022
  2. krwq commented on Jan 10, 2022

    @krwq
    Member
  3. buyaa-n commented on Jan 10, 2022

    @buyaa-n
    Contributor

    This is by design, at runtime there is no way to distinguish if the NonNullableValuesDictionary was Dictionary<string, object> or Dictionary<string, object?> at runtime the DeclaringType of refected method var addMethod = typeof(NonNullableValuesDictionary).GetMethod("Add")!; will be just Dictionary<string, object> without any nullability info we could determine object? was null.

    Therefore in order to determine nullability of the Dictionary<string, object>.Add(string, object) we use the original generic definition of the type: Dictionary<TKey, TValue> .Add(TKey, TValue) where TKey not null and TValue is nullable

  4. madelson commented on Jan 10, 2022

    @madelson
    ContributorAuthor

    @buyaa-n if there is no way to tell, how does the compiler differentiate (or can the compiler only do it if the derived type is defined in the same compilation?)?

  5. buyaa-n commented on Jan 10, 2022

    @buyaa-n
    Contributor

    I guess i should have said that there is no way to distinguish that in reflection, because compiler does not provide any nullability info for those generic parameters (reflection uses compiler provided NullableAttribute or NullableContextAttribute to determine nullability, and it is impossible to provide that info current design)

    The compiler nullability analysis and this reflection API are do not work same

  6. madelson commented on Jan 10, 2022

    @madelson
    ContributorAuthor

    @buyaa-n but when I decompile my assembly with dotpeek it looks like the nullable metadata is there:

    Original:

    internal class NonNullableValuesDictionary : Dictionary<string, object> { }
    internal class NullableValuesDictionary : Dictionary<string, object?> { }

    Decompiled:

      [Nullable(new byte[] {(byte) 0, (byte) 1, (byte) 1})]
      internal class NonNullableValuesDictionary : Dictionary<string, object>
      {
      }
    
      [Nullable(new byte[] {(byte) 0, (byte) 1, (byte) 2})]
      internal class NullableValuesDictionary : Dictionary<string, object>
      {
      }

    Aren't those attributes describing the nullability of the generic parameters for Dictionary?

  7. buyaa-n commented on Jan 11, 2022

    @buyaa-n
    Contributor

    Hm, that actually looks feasible compared to, reopening the issue, thanks!

        var nonNull= typeof(Dictionary<string, object>);
        var Nullable = typeof(Dictionary<string, object?>);
  8. removed
    untriagedNew issue has not been triaged by the area owner
    on Jan 11, 2022
  9. added this to the 7.0.0 milestone on Jan 11, 2022
  10. added a commit that references this issue on Jan 22, 2022
    93cb0f6
  11. ghost added
    in-prThere is an active PR which will close this issue when it is merged
    on Jan 22, 2022
  12. ghost removed
    in-prThere is an active PR which will close this issue when it is merged
    on Jan 26, 2022
  13. ghost locked as resolved and limited conversation to collaborators on Feb 26, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions