dotnet/roslyn

System.Runtime.CompilerServices.IsExternalInit not recognized in .NET 5 project

SetTrend opened this issue ยท 13 comments

I just created a fresh WPF class library project utilizing .NET 5, still the known CS0518 Predefined type 'System.Runtime.CompilerServices.IsExternalInit' is not defined or imported error occurs.

Version Used:

3.8.0-5.20604.10 (9ed4b77)

Steps to Reproduce:

  1. Create a fresh WPF class library project, utilizing .NET 5
  2. Add some C#9 code, like public ICommand AddCmd { get; init; }

Here's the .csproj file's content:

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <TargetFramework>net5.0-windows</TargetFramework>
    <UseWPF>true</UseWPF>
    <Nullable>enable</Nullable>
  </PropertyGroup>

  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
    <DebugType>none</DebugType>
    <DebugSymbols>false</DebugSymbols>
    <TreatWarningsAsErrors>true</TreatWarningsAsErrors>
  </PropertyGroup>

  <ItemGroup>
    <ProjectReference Include="..\DataRepository\DataRepository.csproj" />
  </ItemGroup>

</Project>

Expected Behavior:

This code should compile flawlessly.

Actual Behavior:

It's required to manually add a dummy class to the project:

namespace System.Runtime.CompilerServices
{
  public class IsExternalInit { }
}

I can't reproduce this behavior. I created a directory with the following two files:

wpf.cs

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <TargetFramework>net5.0-windows</TargetFramework>
    <UseWPF>true</UseWPF>
    <Nullable>enable</Nullable>
  </PropertyGroup>

</Project>

example.cs

record Point(int X, int Y);

In that directory I run dotnet build it succeeds. This will exercise the IsExternalInit code path because records generate it for it's positional properties. To double check though I opened up the binary in ILSpy and verified that it has the expected reference:

image

This seems like it's an issue specific to your setup in some way: either something with the machine or with the build environment. Possibly for example you have a Directory.Build.props / Directory.Build.targets that is changing the inputs here somehow.

Probably the easiest way to dig a bit here is use the /bl option which will produce a binary build log. Looking through that we can likely see quickly if the references that are passed in the stock projects are being passed here.

Actually, I cannot tell what precisely is going wrong. It's Visual Studio 2019 that's raising the error with the project file content I gave above:

Visual Studio 2019

@SetTrend Frpm tge screenshot, it appears the error is occuring in a class lib not the wpf project.

Can you double check the target framework for the class lib project?

Absolutely. It's a class library generated by a Visual Studio 2019 project template. The project template is called "WPF Class Library". The project file's content I gave above.

@SetTrend The default .NET WPF class library template (called "WPF library (.NET)") is creating .NET Core 3.1 app. When I change it to .NET 5.0, I get no errors. Can you share a reproducible project in which you get the error and/or a binary log using dotnet build /bl? That should help investigating the issue.

@Youssef1313: Sure, I'll gladly do that. At this time, my project doesn't compile as I just added two new view-models. I guess I can upload my repository on Monday. Please bear with me.

I remember now, that for this particular project in the solution I manually upgraded from a standard .NET 5 class library to WPF .NET 5 class library:

https://stackoverflow.com/questions/65846137/how-can-i-add-a-reference-to-presentationcore-in-visual-studio-2019/65846554

Perhaps the description therein wasn't sufficient?

I finished my sample project now and uploaded it to Github. Please find the corresponding repository here.

@SetTrend Thanks for the repro. As far as I can tell, the issue is related to referencing the .NET Standard project which defines IsExternalInit in itself.

I think there is conflict between choosing the one defined in the .NET 5.0 runtime and the other one defined in your .NET Standard project. There is a room to make the diagnostic more clear here.

To fix the issue, either make IsExternalInit internal class instead of public class in your .NET Standard project; or compile it conditionally based on the target framework. I'll let @jaredpar or any Microsoft employee guide you about what's the more appropriate solution.

Here is the minimal repro:

https://github.com/Youssef1313/IsExternalInitRepro

I'd suggesting debugging around:

result = this.Assembly.GetTypeByMetadataName(
mdName, includeReferences: true, useCLSCompliantNameArityEncoding: true, isWellKnownType: true, conflicts: out conflicts,
warnings: legacyWarnings, ignoreCorLibraryDuplicatedTypes: ignoreCorLibraryDuplicatedTypes);

and see if any conflicts are discovered, then add a new error similar to:

<data name="ERR_PredefinedValueTupleTypeAmbiguous3" xml:space="preserve">
<value>Predefined type '{0}' is declared in multiple referenced assemblies: '{1}' and '{2}'</value>
</data>

Thank you so much, @Youssef1313, for taking your valuable time and providing me with your expertise!

Your last message wasn't addressed to me, was it?

I think there is conflict between choosing the one defined in the .NET 5.0 runtime and the other one defined in your .NET Standard project. There is a room to make the diagnostic more clear here.

Agree. This seems to be the issue.

After some discussion we are going to modify the lookup rules for IsExternalInit when creating a new init accessor to be the following (logically):

  1. Prefer IsExternalInit from source
  2. Prefer IsExternalInit from corelib
  3. General lookup in all referenced assemblies and succeed if one instance is found
  4. Error when zero or more than one found