Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Codegen for module initializers feature #43281

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
8c28e24
Decode ModuleInitializerAttribute to SourceMethodSymbolWithAttributes…
jnm2 Apr 11, 2020
223ef89
Check feature availability
jnm2 Apr 11, 2020
a51bac2
Test for simple success case
jnm2 Apr 11, 2020
44d207d
Method is not specific to PrivateImplementationDetails
jnm2 Apr 11, 2020
ee873ab
Simplify RootModuleType.cs
jnm2 Apr 11, 2020
f5fe948
Compile synthesized methods that the root module type may have
jnm2 Apr 11, 2020
4e4889a
Emit root module type constructor calling the module initializer methods
jnm2 Apr 11, 2020
46e61ef
Fix the <Module> method full names in the CompilationTestData that th…
jnm2 Apr 11, 2020
e509e89
Freeze the root module type since it is processed when emitting metad…
jnm2 Apr 12, 2020
b4d607b
Apply suggestions from code review
jnm2 Apr 13, 2020
7308772
Inline now and extract as private method later
jnm2 Apr 17, 2020
f50aa4c
Add suggested comment where features are mapped to versions
jnm2 Apr 17, 2020
5908d80
Revert in preparation for taking an approach that is not based on syn…
jnm2 Apr 17, 2020
0290b4c
Store decoded ModuleInitializerAttribute state directly on the compil…
jnm2 Apr 18, 2020
a64bb21
Emit without synthesizing symbols
jnm2 Apr 19, 2020
f144ce2
Assert that SetStaticConstructorBody is not called after GetMethods
jnm2 Apr 24, 2020
11c64a8
Remove unnecessary GetModuleInitializerMethods method
jnm2 Apr 24, 2020
2960b8b
fixup! Emit without synthesizing symbols
jnm2 Apr 24, 2020
0d0936d
Remove unnecessary dependency on SourceMethodSymbolWithAttributes.Syn…
jnm2 Apr 24, 2020
a83e6df
Use ConcurrentSet to prevent duplicates
jnm2 Apr 24, 2020
8fb31ed
Allow any MethodSymbol to be added as a module initializer for the co…
jnm2 Apr 24, 2020
34dd24a
Assert expected metadata flags
jnm2 Apr 24, 2020
771e905
Add specific test that C# 9 is the first version supporting module in…
jnm2 Apr 25, 2020
1f34d13
Merge branch 'features/module-initializers' of github.com:dotnet/rosl…
RikkiGibson Apr 27, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions src/Compilers/CSharp/Portable/CSharpResources.resx
Original file line number Diff line number Diff line change
Expand Up @@ -6070,4 +6070,7 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ
<data name="WRN_GeneratorFailedDuringInitialization_Title" xml:space="preserve">
<value>Generator failed to initialize.</value>
</data>
<data name="IDS_FeatureModuleInitializers" xml:space="preserve">
<value>module initializers</value>
</data>
</root>
36 changes: 36 additions & 0 deletions src/Compilers/CSharp/Portable/Compilation/CSharpCompilation.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
using System.Reflection.Metadata;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CodeGen;
using Microsoft.CodeAnalysis.CSharp.Emit;
using Microsoft.CodeAnalysis.CSharp.Symbols;
Expand Down Expand Up @@ -2015,6 +2016,14 @@ private protected override bool IsSymbolAccessibleWithinCore(
throw new NotImplementedException();
}

private ConcurrentSet<MethodSymbol>? _moduleInitializerMethods;

internal void AddModuleInitializerMethod(MethodSymbol method)
{
Debug.Assert(!_declarationDiagnosticsFrozen);
LazyInitializer.EnsureInitialized(ref _moduleInitializerMethods).Add(method);
}

#endregion

#region Binding
Expand Down Expand Up @@ -2802,6 +2811,8 @@ internal override bool CompileMethods(
filterOpt: filterOpt,
cancellationToken: cancellationToken);

GenerateModuleInitializer(moduleBeingBuilt, methodBodyDiagnosticBag);
jnm2 marked this conversation as resolved.
Show resolved Hide resolved

bool hasMethodBodyError = !FilterAndAppendAndFreeDiagnostics(diagnostics, ref methodBodyDiagnosticBag);

if (hasDeclarationErrors || hasMethodBodyError)
Expand All @@ -2813,6 +2824,31 @@ internal override bool CompileMethods(
return true;
}

private void GenerateModuleInitializer(PEModuleBuilder moduleBeingBuilt, DiagnosticBag methodBodyDiagnosticBag)
{
Debug.Assert(_declarationDiagnosticsFrozen);

if (_moduleInitializerMethods is object)
{
var ilBuilder = new ILBuilder(moduleBeingBuilt, new LocalSlotManager(slotAllocator: null), OptimizationLevel.Release, areLocalsZeroed: false);

// PROTOTYPE(module-initializers): require deterministic order
foreach (var method in _moduleInitializerMethods)
{
ilBuilder.EmitOpCode(ILOpCode.Call, stackAdjustment: 0);

ilBuilder.EmitToken(
moduleBeingBuilt.Translate(method, methodBodyDiagnosticBag, needDeclaration: true),
CSharpSyntaxTree.Dummy.GetRoot(),
methodBodyDiagnosticBag);
}

ilBuilder.EmitRet(isVoid: true);
ilBuilder.Realize();
moduleBeingBuilt.RootModuleType.SetStaticConstructorBody(ilBuilder.RealizedIL);
}
}

internal override bool GenerateResourcesAndDocumentationComments(
CommonPEModuleBuilder moduleBuilder,
Stream? xmlDocStream,
Expand Down
2 changes: 2 additions & 0 deletions src/Compilers/CSharp/Portable/Errors/MessageID.cs
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,7 @@ internal enum MessageID
IDS_FeatureMemberNotNull = MessageBase + 12768,
IDS_FeatureNativeInt = MessageBase + 12769,
IDS_FeatureTargetTypedObjectCreation = MessageBase + 12770,
IDS_FeatureModuleInitializers = MessageBase + 12771,
}

// Message IDs may refer to strings that need to be localized.
Expand Down Expand Up @@ -305,6 +306,7 @@ internal static LanguageVersion RequiredVersion(this MessageID feature)
case MessageID.IDS_FeatureTargetTypedObjectCreation: // syntax check
case MessageID.IDS_FeatureMemberNotNull:
case MessageID.IDS_FeatureNativeInt:
case MessageID.IDS_FeatureModuleInitializers: // semantic check on method attribute
return LanguageVersion.Preview;

// C# 8.0 features.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -482,6 +482,12 @@ private void DecodeWellKnownAttributeAppliedToMethod(ref DecodeWellKnownAttribut
MessageID.IDS_FeatureMemberNotNull.CheckFeatureAvailability(arguments.Diagnostics, arguments.AttributeSyntaxOpt);
CSharpAttributeData.DecodeMemberNotNullWhenAttribute<MethodWellKnownAttributeData>(ContainingType, ref arguments);
}
else if (attribute.IsTargetAttribute(this, AttributeDescription.ModuleInitializerAttribute))
{
MessageID.IDS_FeatureModuleInitializers.CheckFeatureAvailability(arguments.Diagnostics, arguments.AttributeSyntaxOpt);
// PROTOTYPE(module-initializers): diagnostics
DeclaringCompilation.AddModuleInitializerMethod(this);
}
else
{
var compilation = this.DeclaringCompilation;
Expand Down
5 changes: 5 additions & 0 deletions src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf
Original file line number Diff line number Diff line change
Expand Up @@ -944,6 +944,11 @@
<target state="new">MemberNotNull attribute</target>
<note />
</trans-unit>
<trans-unit id="IDS_FeatureModuleInitializers">
<source>module initializers</source>
<target state="new">module initializers</target>
<note />
</trans-unit>
<trans-unit id="IDS_FeatureNameShadowingInNestedFunctions">
<source>name shadowing in nested functions</source>
<target state="translated">skrývání názvů ve vnořených funkcích</target>
Expand Down
5 changes: 5 additions & 0 deletions src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf
Original file line number Diff line number Diff line change
Expand Up @@ -944,6 +944,11 @@
<target state="new">MemberNotNull attribute</target>
<note />
</trans-unit>
<trans-unit id="IDS_FeatureModuleInitializers">
<source>module initializers</source>
<target state="new">module initializers</target>
<note />
</trans-unit>
<trans-unit id="IDS_FeatureNameShadowingInNestedFunctions">
<source>name shadowing in nested functions</source>
<target state="translated">Namensshadowing in geschachtelten Funktionen</target>
Expand Down
5 changes: 5 additions & 0 deletions src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf
Original file line number Diff line number Diff line change
Expand Up @@ -944,6 +944,11 @@
<target state="new">MemberNotNull attribute</target>
<note />
</trans-unit>
<trans-unit id="IDS_FeatureModuleInitializers">
<source>module initializers</source>
<target state="new">module initializers</target>
<note />
</trans-unit>
<trans-unit id="IDS_FeatureNameShadowingInNestedFunctions">
<source>name shadowing in nested functions</source>
<target state="translated">sombreado de nombres en funciones anidadas</target>
Expand Down
5 changes: 5 additions & 0 deletions src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf
Original file line number Diff line number Diff line change
Expand Up @@ -944,6 +944,11 @@
<target state="new">MemberNotNull attribute</target>
<note />
</trans-unit>
<trans-unit id="IDS_FeatureModuleInitializers">
<source>module initializers</source>
<target state="new">module initializers</target>
<note />
</trans-unit>
<trans-unit id="IDS_FeatureNameShadowingInNestedFunctions">
<source>name shadowing in nested functions</source>
<target state="translated">ombrage des noms dans les fonctions imbriquées</target>
Expand Down
5 changes: 5 additions & 0 deletions src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf
Original file line number Diff line number Diff line change
Expand Up @@ -944,6 +944,11 @@
<target state="new">MemberNotNull attribute</target>
<note />
</trans-unit>
<trans-unit id="IDS_FeatureModuleInitializers">
<source>module initializers</source>
<target state="new">module initializers</target>
<note />
</trans-unit>
<trans-unit id="IDS_FeatureNameShadowingInNestedFunctions">
<source>name shadowing in nested functions</source>
<target state="translated">shadowing dei nomi nelle funzioni annidate</target>
Expand Down
5 changes: 5 additions & 0 deletions src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf
Original file line number Diff line number Diff line change
Expand Up @@ -944,6 +944,11 @@
<target state="new">MemberNotNull attribute</target>
<note />
</trans-unit>
<trans-unit id="IDS_FeatureModuleInitializers">
<source>module initializers</source>
<target state="new">module initializers</target>
<note />
</trans-unit>
<trans-unit id="IDS_FeatureNameShadowingInNestedFunctions">
<source>name shadowing in nested functions</source>
<target state="translated">入れ子になった関数での名前シャドウイング</target>
Expand Down
5 changes: 5 additions & 0 deletions src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf
Original file line number Diff line number Diff line change
Expand Up @@ -944,6 +944,11 @@
<target state="new">MemberNotNull attribute</target>
<note />
</trans-unit>
<trans-unit id="IDS_FeatureModuleInitializers">
<source>module initializers</source>
<target state="new">module initializers</target>
<note />
</trans-unit>
<trans-unit id="IDS_FeatureNameShadowingInNestedFunctions">
<source>name shadowing in nested functions</source>
<target state="translated">중첩된 함수의 이름 섀도잉</target>
Expand Down
5 changes: 5 additions & 0 deletions src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf
Original file line number Diff line number Diff line change
Expand Up @@ -944,6 +944,11 @@
<target state="new">MemberNotNull attribute</target>
<note />
</trans-unit>
<trans-unit id="IDS_FeatureModuleInitializers">
<source>module initializers</source>
<target state="new">module initializers</target>
<note />
</trans-unit>
<trans-unit id="IDS_FeatureNameShadowingInNestedFunctions">
<source>name shadowing in nested functions</source>
<target state="translated">zasłanianie nazw w funkcjach zagnieżdżonych</target>
Expand Down
5 changes: 5 additions & 0 deletions src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf
Original file line number Diff line number Diff line change
Expand Up @@ -942,6 +942,11 @@
<target state="new">MemberNotNull attribute</target>
<note />
</trans-unit>
<trans-unit id="IDS_FeatureModuleInitializers">
<source>module initializers</source>
<target state="new">module initializers</target>
<note />
</trans-unit>
<trans-unit id="IDS_FeatureNameShadowingInNestedFunctions">
<source>name shadowing in nested functions</source>
<target state="translated">sombreamento de nome em funções aninhadas</target>
Expand Down
5 changes: 5 additions & 0 deletions src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf
Original file line number Diff line number Diff line change
Expand Up @@ -944,6 +944,11 @@
<target state="new">MemberNotNull attribute</target>
<note />
</trans-unit>
<trans-unit id="IDS_FeatureModuleInitializers">
<source>module initializers</source>
<target state="new">module initializers</target>
<note />
</trans-unit>
<trans-unit id="IDS_FeatureNameShadowingInNestedFunctions">
<source>name shadowing in nested functions</source>
<target state="translated">скрытие имен во вложенных функциях</target>
Expand Down
5 changes: 5 additions & 0 deletions src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf
Original file line number Diff line number Diff line change
Expand Up @@ -944,6 +944,11 @@
<target state="new">MemberNotNull attribute</target>
<note />
</trans-unit>
<trans-unit id="IDS_FeatureModuleInitializers">
<source>module initializers</source>
<target state="new">module initializers</target>
<note />
</trans-unit>
<trans-unit id="IDS_FeatureNameShadowingInNestedFunctions">
<source>name shadowing in nested functions</source>
<target state="translated">iç içe işlevlerde ad gölgeleme</target>
Expand Down
5 changes: 5 additions & 0 deletions src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf
Original file line number Diff line number Diff line change
Expand Up @@ -944,6 +944,11 @@
<target state="new">MemberNotNull attribute</target>
<note />
</trans-unit>
<trans-unit id="IDS_FeatureModuleInitializers">
<source>module initializers</source>
<target state="new">module initializers</target>
<note />
</trans-unit>
<trans-unit id="IDS_FeatureNameShadowingInNestedFunctions">
<source>name shadowing in nested functions</source>
<target state="translated">在嵌套函数中的名称映射</target>
Expand Down
5 changes: 5 additions & 0 deletions src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf
Original file line number Diff line number Diff line change
Expand Up @@ -944,6 +944,11 @@
<target state="new">MemberNotNull attribute</target>
<note />
</trans-unit>
<trans-unit id="IDS_FeatureModuleInitializers">
<source>module initializers</source>
<target state="new">module initializers</target>
<note />
</trans-unit>
<trans-unit id="IDS_FeatureNameShadowingInNestedFunctions">
<source>name shadowing in nested functions</source>
<target state="translated">巢狀函式中的名稱鏡像處理</target>
Expand Down
141 changes: 141 additions & 0 deletions src/Compilers/CSharp/Test/Symbol/Symbols/ModuleInitializersTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using System.Reflection;
using Microsoft.CodeAnalysis.CSharp.Symbols;
using Microsoft.CodeAnalysis.CSharp.Symbols.Metadata.PE;
using Microsoft.CodeAnalysis.CSharp.Test.Utilities;
using Microsoft.CodeAnalysis.Test.Utilities;
using Xunit;

namespace Microsoft.CodeAnalysis.CSharp.UnitTests.Symbols
{
[CompilerTrait(CompilerFeature.ModuleInitializers)]
public sealed class ModuleInitializersTests : CSharpTestBase
{
private static readonly CSharpParseOptions s_parseOptions = TestOptions.RegularPreview;

[Fact]
public static void LastLanguageVersionNotSupportingModuleInitializersIs8()
{
var source =
@"using System.Runtime.CompilerServices;

class C
{
[ModuleInitializer]
internal static void M() { }
}

namespace System.Runtime.CompilerServices { class ModuleInitializerAttribute : System.Attribute { } }
";
var compilation = CreateCompilation(source, parseOptions: TestOptions.Regular8);

compilation.VerifyDiagnostics(
// (5,6): error CS8652: The feature 'module initializers' is currently in Preview and *unsupported*. To use Preview features, use the 'preview' language version.
// [ModuleInitializer]
Diagnostic(ErrorCode.ERR_FeatureInPreview, "ModuleInitializer").WithArguments("module initializers").WithLocation(5, 6)
);
}

[Fact]
public static void FirstLanguageVersionSupportingModuleInitializersIs9()
{
var source =
@"using System.Runtime.CompilerServices;

class C
{
[ModuleInitializer]
internal static void M() { }
}

namespace System.Runtime.CompilerServices { class ModuleInitializerAttribute : System.Attribute { } }
";
var compilation = CreateCompilation(source, parseOptions: TestOptions.RegularPreview);

compilation.VerifyDiagnostics();
}

[Fact]
public void ModuleTypeStaticConstructorIsNotEmittedWhenNoMethodIsMarkedWithModuleInitializerAttribute()
{
string source = @"
using System;
using System.Runtime.CompilerServices;

class C
{
internal static void M() => Console.WriteLine(""C.M"");
}

class Program
{
static void Main() => Console.WriteLine(""Program.Main"");
}

namespace System.Runtime.CompilerServices { class ModuleInitializerAttribute : System.Attribute { } }
";

CompileAndVerify(
source,
parseOptions: s_parseOptions,
options: new CSharpCompilationOptions(OutputKind.ConsoleApplication, metadataImportOptions: MetadataImportOptions.All),
jnm2 marked this conversation as resolved.
Show resolved Hide resolved
symbolValidator: module =>
{
var rootModuleType = (TypeSymbol)module.GlobalNamespace.GetMember("<Module>");
Assert.Null(rootModuleType.GetMember(".cctor"));
},
expectedOutput: @"
Program.Main");
}

[Fact]
public void ModuleTypeStaticConstructorCallsMethodMarkedWithModuleInitializerAttribute()
{
string source = @"
using System;
using System.Runtime.CompilerServices;

class C
{
[ModuleInitializer]
internal static void M() => Console.WriteLine(""C.M"");
}

class Program
{
static void Main() => Console.WriteLine(""Program.Main"");
}

namespace System.Runtime.CompilerServices { class ModuleInitializerAttribute : System.Attribute { } }
";

CompileAndVerify(
source,
parseOptions: s_parseOptions,
options: TestOptions.DebugExe.WithMetadataImportOptions(MetadataImportOptions.All),
symbolValidator: module =>
{
var rootModuleType = (TypeSymbol)module.GlobalNamespace.GetMember("<Module>");
var staticConstructor = (PEMethodSymbol)rootModuleType.GetMember(".cctor");

Assert.NotNull(staticConstructor);
Assert.Equal(MethodKind.StaticConstructor, staticConstructor.MethodKind);

var expectedFlags =
MethodAttributes.Private
| MethodAttributes.Static
| MethodAttributes.SpecialName
| MethodAttributes.RTSpecialName
| MethodAttributes.HideBySig;

Assert.Equal(expectedFlags, staticConstructor.Flags);
},
expectedOutput: @"
C.M
Program.Main");
}
}
}
Loading