Roslyn: Improper CS0023 when applying ? to Func<TGenericallyConstrainedTypeParam>

Created on 7 Nov 2020  路  11Comments  路  Source: dotnet/roslyn

    public static IDictionary<TKey, TValue> ToDictionary<T, TKey, TValue, TDictionary>(this IEnumerable<T>? source, Func<T, TKey> keySelector, Func<T, TValue> elementSelector, Func<TDictionary>? factory = null) where TDictionary : IDictionary<TKey, TValue> where TKey : notnull {
      var d1 = factory is null ? new Dictionary<TKey, TValue>() : (IDictionary<TKey, TValue>)factory(); // <-- WORKS!
      var d2 = factory?.Invoke() ?? new Dictionary<TKey, TValue>(); // <-- CS0023: Operator '?' cannot be applied to operand of type 'TDictionary'
      //return something;
    }

In the code above, the complaint from the compiler seems erroneous or incomplete. The ? operator is applied to Func<...>, and _not_ to TDictionary. Both lines d1 = ... and d2 = ... should produce identical results.

Version Info
VS 2019 Community 16.8 Preview 6 (Latest)

.NET SDK (reflecting any global.json):
 Version:   5.0.100-rc.2.20479.15
 Commit:    da7dfa8840

Runtime Environment:
 OS Name:     Windows
 OS Version:  10.0.17134
 OS Platform: Windows
 RID:         win10-x64
 Base Path:   C:\Program Files\dotnet\sdk\5.0.100-rc.2.20479.15\

Host (useful for support):
  Version: 5.0.0-rc.2.20475.5
  Commit:  c5a3f49c88

.NET SDKs installed:
  5.0.100-rc.2.20479.15 [C:\Program Files\dotnet\sdk]

.NET runtimes installed:
  Microsoft.AspNetCore.App 5.0.0-rc.2.20475.17 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
  Microsoft.NETCore.App 5.0.0-rc.2.20475.5 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.WindowsDesktop.App 5.0.0-rc.2.20475.6 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]
Area-Compilers Concept-Diagnostic Clarity

All 11 comments

The error is correct.

      var d1 = factory is null ? 
      var d2 = factory?.Invoke() ?? 

The first line check factory for null. The second line check factory for null using ?. as well as the result of the Invoke method which is required to be nullable for ??. You can add class constraint if that's the intention but these two are not identical.

@alrz Could you clarify this more? I still cannot get the reasoning behind the error in this case (probably the OP too).

The documentation uses a similar example:

https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/operators/member-access-operators#thread-safe-delegate-invocation

What I would expect from factory?.Invoke() is:

  1. If factory is null, the result expression is null.
  2. Otherwise, factory.Invoke() is returned.

The parameter factory is nullable, so ?. operator seems valid to me.

I also that if the error is correct, then it is not clear enough.

error CS0023: Operator '?' cannot be applied to operand of type 'TDictionary'

In the example, the operator ?. is being applied to factory which is of type Func<TDictionary>?. The error message indicates that the operator is being applied to a TDictionary. Not sure why, but this doesn't seem clear to me.

Note that a standalone factory?.Invoke(); compiles, however, when you're using the return value, the compiler tries to make a Nullable<T> if T is a value type or just use T if it's a reference type. but TDictionary is unconstrained, that's why the error is on the ?. operator.

@alrz A standalone factory?.Invoke(); didn't compile with me.

See SharpLab.

A simpler smaller repro:

https://sharplab.io/#v2:EYLgtghgzgLgpgJwDQxAN0QSwGYE8A+AAgEwCMAsAFBUDEAdgK4A2TEwTcABHHWx1VUIBmToVIA2UcU4BhKgG8qnZaJFjJhACycAsgB4AKgD4AFIQCshowH5O2CAGMYAewS5OAXk6MWASiUqipQqIZxoEAicACbSXvZOrrjWAHQAknRozgDWcCa+ANwBygC+ApTFQA==

You're still using the return value.

@alrz Oh I see. In that case, I think the error message should be more clear.

@alrz Thank you for your explanation. The bolded part makes sense. The problem is that I got an error squiggly precisely on ?. and the error message is not making sense there, because clearly the Func itself is nullable, hence the error should have been on the ?? part where it would make sense. So, I still think there is some kind of an issue here.

because clearly the Func itself is nullable

Using ?. to do a func call has two requirements:

  • The target object has clear state of reference type/nullable value type
  • The return value of the call has clear state of reference type/nullable value type

See this code snippet:
https://sharplab.io/#v2:D4AQTAjAsAUCDMACciAaAeAKgPlgb1kSOSU0QDEB7SgCgEpEBebRAEwFMAzAQwFcAbAC4BuWAF9YsBMjCIAwvkLFpIACyIAslmw0MORJQBGAKzpKiBGMWuIAbtwBOiB+wDOAwUwMmA/ADoqWjpRK2IJGDEgA

This is currently of the shorthand of the language, but the compiler is following the spec correctly.

@huoyaoyuan Yep, I think the issue now is more about diagnostic clarity. While it makes sense to have a compile error, the current error message itself doesn't make a lot of sense in my opinion.

Was this page helpful?
0 / 5 - 0 ratings