Considering example below, one observe that for reference type argument is successfully understood as nullable parameter. For value type conversion to T?/Nullable<T> fails.
T Method<T> ( T? t )
{
// Error CS1503 Argument 1: cannot convert from '<null>' to 'float'
float f1 = Method<float> (null);
// ⇑⇑ float <float>(float) ⇑⇑
// Error CS1503 Argument 1: cannot convert from 'float?' to 'float'
float f2 = Method<float> ((float?) null);
// ⇑⇑ float <float>(float) ⇑⇑
object? o1 = Method<object> (null);
// ⇑⇑ object <object>(object?) ⇑⇑
object o2 = Method<object> ((object?) null);
// ⇑⇑ object <object>(object?) ⇑⇑
return t;
}
Comments show resolved method signature. For value type ? notion is completely ignored. That prevents null usage on input.
It is not so obvious what prevents compiler in understanding when T? should be understood as notion and when as declaration.