Skip to content

Proposal: Allow unconstrained generic type parameters to be nullable references #9967

@bkoelman

Description

@bkoelman

I propose to no longer issue CS0453 on methods with the next signature in the features/NullableReferenceTypes branch:

public static void M3<T>(T? arg) { Console.WriteLine(arg == null ? "null" : arg.ToString()); }

but instead, consider the signature equivalent to:

public static void M3<T>([Nullable] T arg) { Console.WriteLine(arg == null ? "null" : arg.ToString()); }

where [Nullable] refers to the inaccessible compiler-builtin annotation for a nullable reference type.

I believe this will not break existing code, as the construct is currently not allowed:

        // CS6 + Prototype: allowed
        public static void M1<T>(T arg) { Console.WriteLine(arg == null ? "null" : arg.ToString()); }

        // CS6 + Prototype: allowed
        public static void M2<T>(T? arg) where T : struct { Console.WriteLine(arg == null ? "null" : arg.ToString()); }

        // CS6 + Prototype: allowed
        public static void M3<T>(T arg) where T : class { Console.WriteLine(arg == null ? "null" : arg.ToString()); }

        // CS6 + Prototype: CS0453
        public static void M4<T>(T? arg) { Console.WriteLine(arg == null ? "null" : arg.ToString()); }

Applying this proposal would enable callers to pass in value types (which get boxed), nullable value types (get boxed), never-null reference types and nullable reference types.

To clarify: In this proposal, the unconstrained generic type T? is interpreted as [Nullable] T, not Nullable<T>.

This proposal aims to not:

  • Require changes to overload resolution (attributes are not taken into account)
  • Require CLR changes (this is not about unification of types)
  • Require changes in value type boxing (that already happens)

From what I understand, this seems very straightforward. Without asking for alternative proposals (or potential theoretical scenarios), are there any reasons that make doing this impossible?

I am aware this proposal makes it impossible to have these two overloads:

        public static void M<T>(T? arg) { } // => public static void M<T>([Nullable] T arg) { }
        public static void M<T>(T arg) { }

as their signatures (from a CLR perspective) are not overloads. That does not bother me, though.

I would appreciate to center the discussion around the current prototype, not other directions it may or may not take in the future. Therefore, I'd kindly ask you to verify your assumptions against the actual prototype before responding. This prevents the discussion steering somewhere else, based on (incorrect) assumptions.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions