From 9978f938862ad8191dc5f7209f0cb30e8d74411c Mon Sep 17 00:00:00 2001 From: Paul Chen Date: Mon, 13 Dec 2021 00:48:00 +0800 Subject: [PATCH 01/66] Cache the delegate for static method group conversions. --- .../Portable/Compilation/CSharpCompilation.cs | 2 + .../Portable/Compiler/TypeCompilationState.cs | 2 + .../LocalRewriter/DelegateCacheContainer.cs | 68 + ...CacheRewriter.TypeParameterUsageChecker.cs | 148 + .../LocalRewriter/DelegateCacheRewriter.cs | 145 + .../Lowering/LocalRewriter/LocalRewriter.cs | 11 +- .../LocalRewriter/LocalRewriter_Conversion.cs | 16 +- .../Symbols/Synthesized/GeneratedNameKind.cs | 7 +- .../Symbols/Synthesized/GeneratedNames.cs | 37 + .../AttributeTests_CallerInfoAttributes.cs | 138 +- .../CodeGenConditionalOperatorTests.cs | 44 +- .../Test/Emit/CodeGen/CodeGenLockTests.cs | 43 +- .../CodeGenNullCoalescingAssignmentTests.cs | 63 +- .../EditAndContinue/EditAndContinueTests.cs | 2 +- ...odeGenMethodGroupConversionCachingTests.cs | 4090 +++++++++++++++++ .../Semantic/Semantics/DelegateTypeTests.cs | 186 +- .../Symbol/Symbols/ExtensionMethodTests.cs | 36 +- .../StaticAbstractMembersInInterfacesTests.cs | 42 +- .../Test/WinRT/CodeGen/WinMdEventTests.cs | 108 +- .../Portable/Microsoft.CodeAnalysis.csproj | 1 + .../ExpressionCompilerTests.cs | 36 +- src/Scripting/CSharpTest/ScriptTests.cs | 25 + 22 files changed, 5010 insertions(+), 240 deletions(-) create mode 100644 src/Compilers/CSharp/Portable/Lowering/LocalRewriter/DelegateCacheContainer.cs create mode 100644 src/Compilers/CSharp/Portable/Lowering/LocalRewriter/DelegateCacheRewriter.TypeParameterUsageChecker.cs create mode 100644 src/Compilers/CSharp/Portable/Lowering/LocalRewriter/DelegateCacheRewriter.cs create mode 100644 src/Compilers/CSharp/Test/Emit2/CodeGen/CodeGenMethodGroupConversionCachingTests.cs diff --git a/src/Compilers/CSharp/Portable/Compilation/CSharpCompilation.cs b/src/Compilers/CSharp/Portable/Compilation/CSharpCompilation.cs index b8bf3ced13628..c9fd1c5224c36 100644 --- a/src/Compilers/CSharp/Portable/Compilation/CSharpCompilation.cs +++ b/src/Compilers/CSharp/Portable/Compilation/CSharpCompilation.cs @@ -206,6 +206,8 @@ internal override CommonAnonymousTypeManager CommonAnonymousTypeManager /// internal bool IsPeVerifyCompatEnabled => LanguageVersion < LanguageVersion.CSharp7_2 || Feature("peverify-compat") != null; + internal bool IsStaticMethodGroupDelegateCacheEnabled => Feature("DisableStaticMethodGroupDelegateCache") is null; + /// /// Returns true if nullable analysis is enabled in the text span represented by the syntax node. /// diff --git a/src/Compilers/CSharp/Portable/Compiler/TypeCompilationState.cs b/src/Compilers/CSharp/Portable/Compiler/TypeCompilationState.cs index 9ed8097b9bfca..ac4d6ddcde519 100644 --- a/src/Compilers/CSharp/Portable/Compiler/TypeCompilationState.cs +++ b/src/Compilers/CSharp/Portable/Compiler/TypeCompilationState.cs @@ -71,6 +71,8 @@ internal MethodWithBody(MethodSymbol method, BoundStatement body, ImportChain? i public SynthesizedClosureEnvironment? StaticLambdaFrame; + public DelegateCacheContainer? ConcreteDelegateCacheContainer; + /// /// A graph of method->method references for this(...) constructor initializers. /// Used to detect and report initializer cycles. diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/DelegateCacheContainer.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/DelegateCacheContainer.cs new file mode 100644 index 0000000000000..0a43d3a1ad3de --- /dev/null +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/DelegateCacheContainer.cs @@ -0,0 +1,68 @@ +// 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.Collections.Generic; +using System.Diagnostics; +using Roslyn.Utilities; + +namespace Microsoft.CodeAnalysis.CSharp.Symbols; + +internal sealed class DelegateCacheContainer : SynthesizedContainer +{ + private readonly Symbol _containingSymbol; + private readonly Dictionary<(NamedTypeSymbol, MethodSymbol), FieldSymbol> _delegateFields = new(); + + /// Creates a type scoped concrete delegate cache container. + internal DelegateCacheContainer(TypeSymbol containingType, int generationOrdinal) + : base(GeneratedNames.DelegateCacheContainerType(generationOrdinal), containingMethod: null) + { + Debug.Assert(containingType.IsDefinition); + _containingSymbol = containingType; + } + + /// Creates a method scoped generic delegate cache container. + internal DelegateCacheContainer(MethodSymbol containingMethod, int topLevelMethodOrdinal, int localFunctionOrdinal, int generationOrdinal) + : base(GeneratedNames.DelegateCacheContainerType(generationOrdinal, containingMethod.Name, topLevelMethodOrdinal, localFunctionOrdinal), containingMethod) + { + Debug.Assert(containingMethod.IsDefinition); + _containingSymbol = containingMethod.ContainingType; + } + + public override Symbol ContainingSymbol => _containingSymbol; + + public override bool AreLocalsZeroed => throw ExceptionUtilities.Unreachable; + + public override TypeKind TypeKind => TypeKind.Class; + + public override bool IsStatic => true; + + internal override bool IsRecord => false; + + internal override bool IsRecordStruct => false; + + internal override bool HasPossibleWellKnownCloneMethod() => false; + + internal FieldSymbol GetOrAddCacheField(SyntheticBoundNodeFactory factory, NamedTypeSymbol delegateType, MethodSymbol targetMethod) + { + if (_delegateFields.TryGetValue((delegateType, targetMethod), out var field)) + { + return field; + } + + var fieldType = TypeParameters.IsEmpty ? delegateType : TypeMap.SubstituteType(delegateType).Type; + var fieldName = GeneratedNames.DelegateCacheContainerFieldName(_delegateFields.Count, targetMethod.Name); + + field = new SynthesizedFieldSymbol(this, fieldType, fieldName, isPublic: true, isStatic: true); + factory.AddField(this, field); + + if (!TypeParameters.IsEmpty) + { + field = field.AsMember(Construct(ConstructedFromTypeParameters)); + } + + _delegateFields.Add((delegateType, targetMethod), field); + + return field; + } +} diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/DelegateCacheRewriter.TypeParameterUsageChecker.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/DelegateCacheRewriter.TypeParameterUsageChecker.cs new file mode 100644 index 0000000000000..495e7672f72e1 --- /dev/null +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/DelegateCacheRewriter.TypeParameterUsageChecker.cs @@ -0,0 +1,148 @@ +// 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.Collections.Generic; +using Microsoft.CodeAnalysis.CSharp.Symbols; +using Roslyn.Utilities; + +namespace Microsoft.CodeAnalysis.CSharp; + +partial class DelegateCacheRewriter +{ + /// + /// Checks if a type or a method is constructed with some type that involves the specified generic type parameters. + /// This checker is different with because here we check method symbols, anonmous types, as well as custom modifiers. + /// + sealed class TypeParameterUsageChecker : CSharpSymbolVisitor, bool> + { + public static TypeParameterUsageChecker Instance { get; } = new(); + + public override bool VisitTypeParameter(TypeParameterSymbol symbol, HashSet typeParameters) => typeParameters.Contains(symbol); + + public override bool VisitArrayType(ArrayTypeSymbol symbol, HashSet typeParameters) + { + return VisitTypeWithAnnotations(symbol.ElementTypeWithAnnotations, typeParameters); + } + + public override bool VisitNamedType(NamedTypeSymbol symbol, HashSet typeParameters) + { + if (symbol.IsAnonymousType) + { + switch (symbol.TypeKind) + { + case TypeKind.Class: + { + var anonymousClass = (AnonymousTypeManager.AnonymousTypePublicSymbol)symbol; + + foreach (var property in anonymousClass.Properties) + { + if (VisitTypeWithAnnotations(property.TypeWithAnnotations, typeParameters)) + { + return true; + } + } + } + break; + + case TypeKind.Delegate: + { + var anonymousDelegate = (AnonymousTypeManager.AnonymousDelegatePublicSymbol)symbol; + + foreach (var typeArgumentWithAnnotations in anonymousDelegate.TypeArgumentsWithAnnotationsNoUseSiteDiagnostics) + { + if (VisitTypeWithAnnotations(typeArgumentWithAnnotations, typeParameters)) + { + return true; + } + } + } + break; + + default: + throw ExceptionUtilities.Unreachable; + } + + return false; + } + + if (symbol.IsTupleType) + { + foreach (var elementTypeWithAnnotations in symbol.TupleElementTypesWithAnnotations) + { + if (VisitTypeWithAnnotations(elementTypeWithAnnotations, typeParameters)) + { + return true; + } + } + + return false; + } + + foreach (var typeArgumentWithAnnotations in symbol.TypeArgumentsWithAnnotationsNoUseSiteDiagnostics) + { + if (VisitTypeWithAnnotations(typeArgumentWithAnnotations, typeParameters)) + { + return true; + } + } + + return Visit(symbol.ContainingType, typeParameters); + } + + public override bool VisitMethod(MethodSymbol symbol, HashSet typeParameters) + { + foreach (var typeArgumentWithAnnotations in symbol.TypeArgumentsWithAnnotations) + { + if (VisitTypeWithAnnotations(typeArgumentWithAnnotations, typeParameters)) + { + return true; + } + } + + return Visit(symbol.ContainingType, typeParameters); + } + + public override bool VisitPointerType(PointerTypeSymbol symbol, HashSet typeParameters) + { + // Func is a good example why here is reachable. + return VisitTypeWithAnnotations(symbol.PointedAtTypeWithAnnotations, typeParameters); + } + + private bool VisitTypeWithAnnotations(in TypeWithAnnotations typeWithAnnotations, HashSet typeParameters) + { + if (Visit(typeWithAnnotations.Type, typeParameters)) + { + return true; + } + + foreach (var customModifier in typeWithAnnotations.CustomModifiers) + { + if (Visit((Symbol)customModifier.Modifier, typeParameters)) + { + return true; + } + } + + return false; + } + + public override bool VisitAssembly(AssemblySymbol symbol, HashSet typeParameters) => throw ExceptionUtilities.Unreachable; + + public override bool VisitModule(ModuleSymbol symbol, HashSet typeParameters) => throw ExceptionUtilities.Unreachable; + + public override bool VisitEvent(EventSymbol symbol, HashSet typeParameters) => throw ExceptionUtilities.Unreachable; + + public override bool VisitProperty(PropertySymbol symbol, HashSet typeParameters) => throw ExceptionUtilities.Unreachable; + + public override bool VisitField(FieldSymbol symbol, HashSet typeParameters) => throw ExceptionUtilities.Unreachable; + + public override bool VisitParameter(ParameterSymbol symbol, HashSet typeParameters) => throw ExceptionUtilities.Unreachable; + + public override bool VisitLocal(LocalSymbol symbol, HashSet typeParameters) => throw ExceptionUtilities.Unreachable; + + public override bool VisitRangeVariable(RangeVariableSymbol symbol, HashSet typeParameters) => throw ExceptionUtilities.Unreachable; + + public override bool VisitLabel(LabelSymbol symbol, HashSet typeParameters) => throw ExceptionUtilities.Unreachable; + } +} diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/DelegateCacheRewriter.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/DelegateCacheRewriter.cs new file mode 100644 index 0000000000000..23906296ff97f --- /dev/null +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/DelegateCacheRewriter.cs @@ -0,0 +1,145 @@ +// 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.Collections.Generic; +using System.Diagnostics; +using Microsoft.CodeAnalysis.CSharp.Symbols; +using Microsoft.CodeAnalysis.PooledObjects; +using Roslyn.Utilities; + +namespace Microsoft.CodeAnalysis.CSharp; + +internal sealed partial class DelegateCacheRewriter +{ + private readonly SyntheticBoundNodeFactory _factory; + private readonly MethodSymbol _topLevelMethod; + private readonly int _topLevelMethodOrdinal; + + private Stack<(int LocalFunctionOrdinal, DelegateCacheContainer CacheContainer)>? _genericCacheContainers; + + internal DelegateCacheRewriter(SyntheticBoundNodeFactory factory, int topLevelMethodOrdinal) + { + Debug.Assert(factory.TopLevelMethod is { }); + + _factory = factory; + _topLevelMethod = factory.TopLevelMethod; + _topLevelMethodOrdinal = topLevelMethodOrdinal; + } + + internal static bool CanRewrite(CSharpCompilation compilation, MethodSymbol topLevelMethod, bool inExpressionLambda, BoundConversion boundConversion, MethodSymbol targetMethod) + => targetMethod.IsStatic + && !boundConversion.IsExtensionMethod + && !inExpressionLambda // The tree structure / meaning for expression trees should remain untouched. + && topLevelMethod.MethodKind != MethodKind.StaticConstructor // Avoid caching twice if people do it manually. + && compilation.IsStaticMethodGroupDelegateCacheEnabled + ; + + internal BoundExpression Rewrite(int localFunctionOrdinal, SyntaxNode syntax, BoundExpression receiver, MethodSymbol targetMethod, NamedTypeSymbol delegateType) + { + Debug.Assert(delegateType.IsDelegateType()); + + var oldSyntax = _factory.Syntax; + _factory.Syntax = syntax; + + var cacheContainer = GetOrAddCacheContainer(localFunctionOrdinal, delegateType, targetMethod); + var cacheField = cacheContainer.GetOrAddCacheField(_factory, delegateType, targetMethod); + + var boundCacheField = _factory.Field(null, cacheField); + var boundDelegateCreation = new BoundDelegateCreationExpression(syntax, receiver, targetMethod, isExtensionMethod: false, type: delegateType); + + var rewrittenNode = _factory.Coalesce(boundCacheField, _factory.AssignmentExpression(boundCacheField, boundDelegateCreation)); + + _factory.Syntax = oldSyntax; + + return rewrittenNode; + } + + private DelegateCacheContainer GetOrAddCacheContainer(int localFunctionOrdinal, NamedTypeSymbol delegateType, MethodSymbol targetMethod) + { + Debug.Assert(_factory.ModuleBuilderOpt is { }); + + var typeCompilationState = _factory.CompilationState; + var generation = _factory.ModuleBuilderOpt.CurrentGenerationOrdinal; + + DelegateCacheContainer? container; + + if (AConcreteContainerIsEnough(delegateType, targetMethod)) + { + container = typeCompilationState.ConcreteDelegateCacheContainer; + + if (container is { }) + { + return container; + } + + container = new(typeCompilationState.Type, generation); + typeCompilationState.ConcreteDelegateCacheContainer = container; + } + else + { + var containersStack = _genericCacheContainers ??= new(); + + while (containersStack.Count > 0 && containersStack.Peek().LocalFunctionOrdinal > localFunctionOrdinal) + { + containersStack.Pop(); + } + + if (containersStack.Count > 0 && containersStack.Peek().LocalFunctionOrdinal == localFunctionOrdinal) + { + return containersStack.Peek().CacheContainer; + } + + container = new(_factory.CurrentFunction ?? _topLevelMethod, _topLevelMethodOrdinal, localFunctionOrdinal, generation); + containersStack.Push((localFunctionOrdinal, container)); + } + + _factory.AddNestedType(container); + + return container; + } + + private bool AConcreteContainerIsEnough(NamedTypeSymbol delegateType, MethodSymbol targetMethod) + { + // Possible places for type parameters that can act as type arguments to construct the delegateType or targetMethod: + // 1. containing types + // 2. current method + // 3. local functions + // Since our containers are created within the same enclosing type, we can ignore type parameters from it. + + // Obviously, + if (_factory is { CurrentFunction: null, TopLevelMethod.Arity: 0 }) + { + return true; + } + + var methodTypeParameters = PooledHashSet.GetInstance(); + try + { + methodTypeParameters.AddAll(_topLevelMethod.TypeParameters); + + for (Symbol? s = _factory.CurrentFunction; s is MethodSymbol m; s = s.ContainingSymbol) + { + methodTypeParameters.AddAll(m.TypeParameters); + } + + if (methodTypeParameters.Count == 0) + { + return true; + } + + var checker = TypeParameterUsageChecker.Instance; + + if (checker.Visit(delegateType, methodTypeParameters) || checker.Visit(targetMethod, methodTypeParameters)) + { + return false; + } + } + finally + { + methodTypeParameters.Free(); + } + + return true; + } +} diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter.cs index 4081c24bf0db3..076d0df2686a0 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter.cs @@ -27,6 +27,9 @@ internal sealed partial class LocalRewriter : BoundTreeRewriterWithStackGuard private LoweredDynamicOperationFactory _dynamicFactory; private bool _sawLambdas; private int _availableLocalFunctionOrdinal; + private int _currentLocalFunctionOrdinal; + private readonly int _topLevelMethodOrdinal; + private DelegateCacheRewriter? _lazyDelegateCacheRewriter; private bool _inExpressionLambda; private bool _sawAwait; @@ -57,6 +60,8 @@ private LocalRewriter( _dynamicFactory = new LoweredDynamicOperationFactory(factory, containingMethodOrdinal); _previousSubmissionFields = previousSubmissionFields; _allowOmissionOfConditionalCalls = allowOmissionOfConditionalCalls; + _topLevelMethodOrdinal = containingMethodOrdinal; + _currentLocalFunctionOrdinal = -1; _diagnostics = diagnostics; Debug.Assert(instrumenter != null); @@ -271,7 +276,8 @@ public override BoundNode VisitLambda(BoundLambda node) public override BoundNode VisitLocalFunctionStatement(BoundLocalFunctionStatement node) { - int localFunctionOrdinal = _availableLocalFunctionOrdinal++; + var oldLocalFunctionOrdinal = _currentLocalFunctionOrdinal; + _currentLocalFunctionOrdinal = _availableLocalFunctionOrdinal++; var localFunction = node.Symbol; CheckRefReadOnlySymbols(localFunction); @@ -321,7 +327,7 @@ static bool hasReturnTypeOrParameter(LocalFunctionSymbol localFunction, Func kind is GeneratedNameKind.LambdaDisplayClass or GeneratedNameKind.StateMachineType or GeneratedNameKind.DynamicCallSiteContainerType; + => kind is GeneratedNameKind.LambdaDisplayClass + or GeneratedNameKind.StateMachineType + or GeneratedNameKind.DynamicCallSiteContainerType + or GeneratedNameKind.DelegateCacheContainerType + ; } } diff --git a/src/Compilers/CSharp/Portable/Symbols/Synthesized/GeneratedNames.cs b/src/Compilers/CSharp/Portable/Symbols/Synthesized/GeneratedNames.cs index 51a70440192ed..55f03646955c6 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Synthesized/GeneratedNames.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Synthesized/GeneratedNames.cs @@ -439,6 +439,43 @@ internal static string AsyncBuilderFieldName() return "<>t__builder"; } + internal static string DelegateCacheContainerType(int generation, string? methodName = null, int methodOrdinal = -1, int localFunctionOrdinal = -1) + { + const char NameKind = (char)GeneratedNameKind.DelegateCacheContainerType; + + var result = PooledStringBuilder.GetInstance(); + var builder = result.Builder; + + builder.Append('<').Append(methodName).Append('>').Append(NameKind); + + if (methodOrdinal > -1) + { + builder.Append(GeneratedNameConstants.SuffixSeparator).Append(methodOrdinal); + } + + if (localFunctionOrdinal > -1) + { + builder.Append(IdSeparator).Append(localFunctionOrdinal); + } + + if (generation > 0) + { + builder.Append(GenerationSeparator).Append(generation); + } + + return result.ToStringAndFree(); + } + + internal static string DelegateCacheContainerFieldName(int id, string targetMethod) + { + var result = PooledStringBuilder.GetInstance(); + var builder = result.Builder; + + builder.Append('<').Append(id).Append(">__").Append(targetMethod); + + return result.ToStringAndFree(); + } + internal static string ReusableHoistedLocalFieldName(int number) { Debug.Assert((char)GeneratedNameKind.ReusableHoistedLocalField == '7'); diff --git a/src/Compilers/CSharp/Test/Emit/Attributes/AttributeTests_CallerInfoAttributes.cs b/src/Compilers/CSharp/Test/Emit/Attributes/AttributeTests_CallerInfoAttributes.cs index f9f3b86e81005..031e59638708b 100644 --- a/src/Compilers/CSharp/Test/Emit/Attributes/AttributeTests_CallerInfoAttributes.cs +++ b/src/Compilers/CSharp/Test/Emit/Attributes/AttributeTests_CallerInfoAttributes.cs @@ -68,18 +68,24 @@ public static void Main() // Begin/EndInvoke are not currently supported. CompileAndVerify(compilation).VerifyDiagnostics().VerifyIL("Program.Main", @" { - // Code size 31 (0x1f) + // Code size 46 (0x2e) .maxstack 5 - IL_0000: ldnull - IL_0001: ldftn ""void Program.M(string, string)"" - IL_0007: newobj ""Program.D..ctor(object, System.IntPtr)"" - IL_000c: call ""string Program.GetString()"" - IL_0011: ldstr ""default"" - IL_0016: ldnull - IL_0017: ldnull - IL_0018: callvirt ""System.IAsyncResult Program.D.BeginInvoke(string, string, System.AsyncCallback, object)"" - IL_001d: pop - IL_001e: ret + IL_0000: ldsfld ""Program.D Program.<>O.<0>__M"" + IL_0005: dup + IL_0006: brtrue.s IL_001b + IL_0008: pop + IL_0009: ldnull + IL_000a: ldftn ""void Program.M(string, string)"" + IL_0010: newobj ""Program.D..ctor(object, System.IntPtr)"" + IL_0015: dup + IL_0016: stsfld ""Program.D Program.<>O.<0>__M"" + IL_001b: call ""string Program.GetString()"" + IL_0020: ldstr ""default"" + IL_0025: ldnull + IL_0026: ldnull + IL_0027: callvirt ""System.IAsyncResult Program.D.BeginInvoke(string, string, System.AsyncCallback, object)"" + IL_002c: pop + IL_002d: ret } "); } @@ -134,18 +140,24 @@ public static void Main() // Begin/EndInvoke are not currently supported. CompileAndVerify(compilation).VerifyDiagnostics().VerifyIL("Program.Main", @" { - // Code size 27 (0x1b) + // Code size 42 (0x2a) .maxstack 3 .locals init (string V_0) //s - IL_0000: ldnull - IL_0001: ldftn ""void Program.M(ref string, string)"" - IL_0007: newobj ""Program.D..ctor(object, System.IntPtr)"" - IL_000c: ldsfld ""string string.Empty"" - IL_0011: stloc.0 - IL_0012: ldloca.s V_0 - IL_0014: ldnull - IL_0015: callvirt ""void Program.D.EndInvoke(ref string, System.IAsyncResult)"" - IL_001a: ret + IL_0000: ldsfld ""Program.D Program.<>O.<0>__M"" + IL_0005: dup + IL_0006: brtrue.s IL_001b + IL_0008: pop + IL_0009: ldnull + IL_000a: ldftn ""void Program.M(ref string, string)"" + IL_0010: newobj ""Program.D..ctor(object, System.IntPtr)"" + IL_0015: dup + IL_0016: stsfld ""Program.D Program.<>O.<0>__M"" + IL_001b: ldsfld ""string string.Empty"" + IL_0020: stloc.0 + IL_0021: ldloca.s V_0 + IL_0023: ldnull + IL_0024: callvirt ""void Program.D.EndInvoke(ref string, System.IAsyncResult)"" + IL_0029: ret } "); } @@ -374,18 +386,24 @@ public static void Main() Diagnostic(ErrorCode.WRN_CallerArgumentExpressionAttributeHasInvalidParameterName, "CallerArgumentExpression").WithArguments("s2").WithLocation(29, 33) ).VerifyIL("Program.Main", @" { - // Code size 31 (0x1f) + // Code size 46 (0x2e) .maxstack 5 - IL_0000: ldnull - IL_0001: ldftn ""void Program.M(string, string)"" - IL_0007: newobj ""Program.D..ctor(object, System.IntPtr)"" - IL_000c: call ""string Program.GetString()"" - IL_0011: ldstr ""default"" - IL_0016: ldnull - IL_0017: ldnull - IL_0018: callvirt ""System.IAsyncResult Program.D.BeginInvoke(string, string, System.AsyncCallback, object)"" - IL_001d: pop - IL_001e: ret + IL_0000: ldsfld ""Program.D Program.<>O.<0>__M"" + IL_0005: dup + IL_0006: brtrue.s IL_001b + IL_0008: pop + IL_0009: ldnull + IL_000a: ldftn ""void Program.M(string, string)"" + IL_0010: newobj ""Program.D..ctor(object, System.IntPtr)"" + IL_0015: dup + IL_0016: stsfld ""Program.D Program.<>O.<0>__M"" + IL_001b: call ""string Program.GetString()"" + IL_0020: ldstr ""default"" + IL_0025: ldnull + IL_0026: ldnull + IL_0027: callvirt ""System.IAsyncResult Program.D.BeginInvoke(string, string, System.AsyncCallback, object)"" + IL_002c: pop + IL_002d: ret } "); } @@ -2439,20 +2457,26 @@ public static void Main() var compilation = CreateCompilation(source, targetFramework: TargetFramework.NetCoreApp, options: TestOptions.ReleaseExe, parseOptions: TestOptions.Regular10); CompileAndVerify(compilation).VerifyDiagnostics().VerifyIL("Program.Main", @" { - // Code size 29 (0x1d) + // Code size 44 (0x2c) .maxstack 4 .locals init (string V_0, //s2 string V_1) - IL_0000: ldnull - IL_0001: ldftn ""void Program.M(string, ref string, out string, string)"" - IL_0007: newobj ""Program.D..ctor(object, System.IntPtr)"" - IL_000c: ldstr ""s2-arg"" - IL_0011: stloc.0 - IL_0012: ldloca.s V_0 - IL_0014: ldloca.s V_1 - IL_0016: ldnull - IL_0017: callvirt ""void Program.D.EndInvoke(ref string, out string, System.IAsyncResult)"" - IL_001c: ret + IL_0000: ldsfld ""Program.D Program.<>O.<0>__M"" + IL_0005: dup + IL_0006: brtrue.s IL_001b + IL_0008: pop + IL_0009: ldnull + IL_000a: ldftn ""void Program.M(string, ref string, out string, string)"" + IL_0010: newobj ""Program.D..ctor(object, System.IntPtr)"" + IL_0015: dup + IL_0016: stsfld ""Program.D Program.<>O.<0>__M"" + IL_001b: ldstr ""s2-arg"" + IL_0020: stloc.0 + IL_0021: ldloca.s V_0 + IL_0023: ldloca.s V_1 + IL_0025: ldnull + IL_0026: callvirt ""void Program.D.EndInvoke(ref string, out string, System.IAsyncResult)"" + IL_002b: ret } "); } @@ -2490,20 +2514,26 @@ public static void Main() var compilation = CreateCompilation(source, targetFramework: TargetFramework.NetCoreApp, options: TestOptions.ReleaseExe, parseOptions: TestOptions.Regular10); CompileAndVerify(compilation).VerifyDiagnostics().VerifyIL("Program.Main", @" { - // Code size 29 (0x1d) + // Code size 44 (0x2c) .maxstack 4 .locals init (string V_0, //s2 string V_1) - IL_0000: ldnull - IL_0001: ldftn ""void Program.M(string, ref string, out string, string)"" - IL_0007: newobj ""Program.D..ctor(object, System.IntPtr)"" - IL_000c: ldstr ""s2-arg"" - IL_0011: stloc.0 - IL_0012: ldloca.s V_0 - IL_0014: ldloca.s V_1 - IL_0016: ldnull - IL_0017: callvirt ""void Program.D.EndInvoke(ref string, out string, System.IAsyncResult)"" - IL_001c: ret + IL_0000: ldsfld ""Program.D Program.<>O.<0>__M"" + IL_0005: dup + IL_0006: brtrue.s IL_001b + IL_0008: pop + IL_0009: ldnull + IL_000a: ldftn ""void Program.M(string, ref string, out string, string)"" + IL_0010: newobj ""Program.D..ctor(object, System.IntPtr)"" + IL_0015: dup + IL_0016: stsfld ""Program.D Program.<>O.<0>__M"" + IL_001b: ldstr ""s2-arg"" + IL_0020: stloc.0 + IL_0021: ldloca.s V_0 + IL_0023: ldloca.s V_1 + IL_0025: ldnull + IL_0026: callvirt ""void Program.D.EndInvoke(ref string, out string, System.IAsyncResult)"" + IL_002b: ret } "); } diff --git a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenConditionalOperatorTests.cs b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenConditionalOperatorTests.cs index 2df99e3bcab0f..8afa85a95bb7f 100644 --- a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenConditionalOperatorTests.cs +++ b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenConditionalOperatorTests.cs @@ -1106,7 +1106,7 @@ static int M() comp.VerifyDiagnostics(); comp.VerifyIL("C.Main", @" { - // Code size 55 (0x37) + // Code size 85 (0x55) .maxstack 3 .locals init (System.Func V_0) //f IL_0000: ldc.i4.1 @@ -1115,21 +1115,33 @@ .locals init (System.Func V_0) //f IL_0003: ldloc.0 IL_0004: call ""void System.Console.WriteLine(object)"" IL_0009: dup - IL_000a: brtrue.s IL_001a - IL_000c: ldnull - IL_000d: ldftn ""int C.M()"" - IL_0013: newobj ""System.Func..ctor(object, System.IntPtr)"" - IL_0018: br.s IL_001b - IL_001a: ldloc.0 - IL_001b: call ""void System.Console.WriteLine(object)"" - IL_0020: brtrue.s IL_0025 - IL_0022: ldloc.0 - IL_0023: br.s IL_0031 - IL_0025: ldnull - IL_0026: ldftn ""int C.M()"" - IL_002c: newobj ""System.Func..ctor(object, System.IntPtr)"" - IL_0031: call ""void System.Console.WriteLine(object)"" - IL_0036: ret + IL_000a: brtrue.s IL_0029 + IL_000c: ldsfld ""System.Func C.<>O.<0>__M"" + IL_0011: dup + IL_0012: brtrue.s IL_002a + IL_0014: pop + IL_0015: ldnull + IL_0016: ldftn ""int C.M()"" + IL_001c: newobj ""System.Func..ctor(object, System.IntPtr)"" + IL_0021: dup + IL_0022: stsfld ""System.Func C.<>O.<0>__M"" + IL_0027: br.s IL_002a + IL_0029: ldloc.0 + IL_002a: call ""void System.Console.WriteLine(object)"" + IL_002f: brtrue.s IL_0034 + IL_0031: ldloc.0 + IL_0032: br.s IL_004f + IL_0034: ldsfld ""System.Func C.<>O.<0>__M"" + IL_0039: dup + IL_003a: brtrue.s IL_004f + IL_003c: pop + IL_003d: ldnull + IL_003e: ldftn ""int C.M()"" + IL_0044: newobj ""System.Func..ctor(object, System.IntPtr)"" + IL_0049: dup + IL_004a: stsfld ""System.Func C.<>O.<0>__M"" + IL_004f: call ""void System.Console.WriteLine(object)"" + IL_0054: ret } "); } diff --git a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenLockTests.cs b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenLockTests.cs index 9acbf01575d5e..85a6e48ec2ea6 100644 --- a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenLockTests.cs +++ b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenLockTests.cs @@ -545,33 +545,40 @@ static partial void PM(int p2) CompileAndVerify(text).VerifyIL("Test.Main", @" { - // Code size 36 (0x24) + // Code size 51 (0x33) .maxstack 2 .locals init (D V_0, bool V_1) - IL_0000: ldnull - IL_0001: ldftn ""void Test.PM(int)"" - IL_0007: newobj ""D..ctor(object, System.IntPtr)"" - IL_000c: stloc.0 - IL_000d: ldc.i4.0 - IL_000e: stloc.1 + IL_0000: ldsfld ""D Test.<>O.<0>__PM"" + IL_0005: dup + IL_0006: brtrue.s IL_001b + IL_0008: pop + IL_0009: ldnull + IL_000a: ldftn ""void Test.PM(int)"" + IL_0010: newobj ""D..ctor(object, System.IntPtr)"" + IL_0015: dup + IL_0016: stsfld ""D Test.<>O.<0>__PM"" + IL_001b: stloc.0 + IL_001c: ldc.i4.0 + IL_001d: stloc.1 .try { - IL_000f: ldloc.0 - IL_0010: ldloca.s V_1 - IL_0012: call ""void System.Threading.Monitor.Enter(object, ref bool)"" - IL_0017: leave.s IL_0023 + IL_001e: ldloc.0 + IL_001f: ldloca.s V_1 + IL_0021: call ""void System.Threading.Monitor.Enter(object, ref bool)"" + IL_0026: leave.s IL_0032 } finally { - IL_0019: ldloc.1 - IL_001a: brfalse.s IL_0022 - IL_001c: ldloc.0 - IL_001d: call ""void System.Threading.Monitor.Exit(object)"" - IL_0022: endfinally + IL_0028: ldloc.1 + IL_0029: brfalse.s IL_0031 + IL_002b: ldloc.0 + IL_002c: call ""void System.Threading.Monitor.Exit(object)"" + IL_0031: endfinally } - IL_0023: ret -}"); + IL_0032: ret +} +"); } [Fact] diff --git a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenNullCoalescingAssignmentTests.cs b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenNullCoalescingAssignmentTests.cs index ceb9e3b2f4be8..21ecfeba9bc78 100644 --- a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenNullCoalescingAssignmentTests.cs +++ b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenNullCoalescingAssignmentTests.cs @@ -698,7 +698,7 @@ public class C public static void Main() { Action a = null; - (a ??= TestMethod)(); + (a ??= new Action(TestMethod))(); (a ??= () => {})(); } static void TestMethod() => Console.WriteLine(""In TestMethod""); @@ -744,6 +744,67 @@ .locals init (System.Action V_0) //a "); } + [Fact] + public void ValidRHS1() + { + CompileAndVerify(@" +using System; +public class C +{ + public static void Main() + { + Action a = null; + (a ??= TestMethod)(); + (a ??= () => {})(); + } + static void TestMethod() => Console.WriteLine(""In TestMethod""); +} +", expectedOutput: @" +In TestMethod +In TestMethod +").VerifyIL("C.Main()", @" +{ + // Code size 85 (0x55) + .maxstack 2 + .locals init (System.Action V_0) //a + IL_0000: ldnull + IL_0001: stloc.0 + IL_0002: ldloc.0 + IL_0003: dup + IL_0004: brtrue.s IL_0024 + IL_0006: pop + IL_0007: ldsfld ""System.Action C.<>O.<0>__TestMethod"" + IL_000c: dup + IL_000d: brtrue.s IL_0022 + IL_000f: pop + IL_0010: ldnull + IL_0011: ldftn ""void C.TestMethod()"" + IL_0017: newobj ""System.Action..ctor(object, System.IntPtr)"" + IL_001c: dup + IL_001d: stsfld ""System.Action C.<>O.<0>__TestMethod"" + IL_0022: dup + IL_0023: stloc.0 + IL_0024: callvirt ""void System.Action.Invoke()"" + IL_0029: ldloc.0 + IL_002a: dup + IL_002b: brtrue.s IL_004f + IL_002d: pop + IL_002e: ldsfld ""System.Action C.<>c.<>9__0_0"" + IL_0033: dup + IL_0034: brtrue.s IL_004d + IL_0036: pop + IL_0037: ldsfld ""C.<>c C.<>c.<>9"" + IL_003c: ldftn ""void C.<>c.
b__0_0()"" + IL_0042: newobj ""System.Action..ctor(object, System.IntPtr)"" + IL_0047: dup + IL_0048: stsfld ""System.Action C.<>c.<>9__0_0"" + IL_004d: dup + IL_004e: stloc.0 + IL_004f: callvirt ""void System.Action.Invoke()"" + IL_0054: ret +}"); + } + [Fact] public void InvalidRHS() { diff --git a/src/Compilers/CSharp/Test/Emit/Emit/EditAndContinue/EditAndContinueTests.cs b/src/Compilers/CSharp/Test/Emit/Emit/EditAndContinue/EditAndContinueTests.cs index a46b3a689be66..1f3744ee3903a 100644 --- a/src/Compilers/CSharp/Test/Emit/Emit/EditAndContinue/EditAndContinueTests.cs +++ b/src/Compilers/CSharp/Test/Emit/Emit/EditAndContinue/EditAndContinueTests.cs @@ -8863,7 +8863,7 @@ static void M2(object o) using var md0 = ModuleMetadata.CreateFromImage(bytes0); var reader0 = md0.MetadataReader; - CheckNames(reader0, reader0.GetTypeDefNames(), "", "C`1", "IA", "IC", "S"); + CheckNames(reader0, reader0.GetTypeDefNames(), "", "C`1", "IA", "IC", "S", "<>O"); var generation0 = EmitBaseline.CreateInitialBaseline(md0, methodData0.EncDebugInfoProvider()); diff --git a/src/Compilers/CSharp/Test/Emit2/CodeGen/CodeGenMethodGroupConversionCachingTests.cs b/src/Compilers/CSharp/Test/Emit2/CodeGen/CodeGenMethodGroupConversionCachingTests.cs new file mode 100644 index 0000000000000..365c7f9a26801 --- /dev/null +++ b/src/Compilers/CSharp/Test/Emit2/CodeGen/CodeGenMethodGroupConversionCachingTests.cs @@ -0,0 +1,4090 @@ +// 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; +using System.Diagnostics; +using Microsoft.CodeAnalysis.CSharp.Symbols; +using Microsoft.CodeAnalysis.CSharp.Test.Utilities; +using Xunit; + +namespace Microsoft.CodeAnalysis.CSharp.UnitTests.CodeGen; + +public class CodeGenMethodGroupConversionCachingTests : CSharpTestBase +{ + const string PASS = "PASS"; + + [Fact] + public void Not_DelegateCreations_Static() + { + var source = @" +using System; +delegate void D(); +class C +{ + public static void Main(string[] args) + { + Invoke(new D(Target), new D(Target)); + } + + static void Target() { Console.WriteLine(""FAIL""); } + static void Invoke(D x, D y) { Console.Write(Object.ReferenceEquals(x, y) ? ""FAIL"" : ""PASS""); } +}"; + Action containerValidator = module => + { + Assert.Null(module.GlobalNamespace.GetMember("<>O")); + }; + CompileAndVerify(source, symbolValidator: containerValidator, expectedOutput: PASS); + } + + [Fact] + public void Not_DelegateCreations_Instance() + { + var source = @" +using System; +delegate void D(); +class C +{ + public static void Main(string[] args) + { + var c = new C(); + c.Invoke(new D(c.Target), new D(c.Target)); + } + + void Target() { Console.WriteLine(""FAIL""); } + void Invoke(D x, D y) { Console.Write(Object.ReferenceEquals(x, y) ? ""FAIL"" : ""PASS""); } +}"; + Action containerValidator = module => + { + Assert.Null(module.GlobalNamespace.GetMember("<>O")); + }; + CompileAndVerify(source, symbolValidator: containerValidator, expectedOutput: PASS); + } + + [Fact] + public void Not_DelegateCreations_InstanceExtensionMethod() + { + var source = @" +using System; +delegate void D(); +class C +{ + public static void Main(string[] args) + { + var c = new C(); + c.Invoke(new D(c.Target), new D(c.Target)); + } + + void Invoke(D x, D y) { Console.Write(Object.ReferenceEquals(x, y) ? ""FAIL"" : ""PASS""); } +} +static class E +{ + public static void Target(this C that) { Console.WriteLine(""FAIL""); } +} +"; + Action containerValidator = module => + { + Assert.Null(module.GlobalNamespace.GetMember("<>O")); + }; + CompileAndVerify(source, symbolValidator: containerValidator, expectedOutput: PASS); + } + + [Fact] + public void Not_DelegateCreations_StaticExtensionMethod() + { + var source = @" +using System; +delegate void D(C arg); +class C +{ + public static void Main(string[] args) + { + var c = new C(); + c.Invoke(new D(E.Target), new D(E.Target)); + } + + void Invoke(D x, D y) { Console.Write(Object.ReferenceEquals(x, y) ? ""FAIL"" : ""PASS""); } +} +static class E +{ + public static void Target(this C that) { Console.WriteLine(""FAIL""); } +} +"; + Action containerValidator = module => + { + Assert.Null(module.GlobalNamespace.GetMember("<>O")); + }; + CompileAndVerify(source, symbolValidator: containerValidator, expectedOutput: PASS); + } + + [Fact] + public void Not_InExpressionLamba0() + { + var source = @" +using System; +using System.Linq.Expressions; +class C +{ + public static void Main(string[] args) + { + Expression>> e = x => Target; + } + + static int Target(int x) => 0; +} +"; + Action containerValidator = module => + { + Assert.Null(module.GlobalNamespace.GetMember("<>O")); + }; + CompileAndVerify(source, symbolValidator: containerValidator); + } + + [Fact] + public void Not_InExpressionLamba1() + { + var source = @" +using System; +using System.Linq.Expressions; +class C +{ + public static void Main(string[] args) + { + Func>>> f = x => y => Target; + } + + static int Target(int x) => 0; +} +"; + Action containerValidator = module => + { + Assert.Null(module.GlobalNamespace.GetMember("<>O")); + }; + CompileAndVerify(source, symbolValidator: containerValidator); + } + + [Fact] + public void Not_InStaticConstructor0() + { + var source = @" +using System; +class C +{ + static readonly Action ManualCache = Target; + static void Target() { } +} +"; + Action containerValidator = module => + { + Assert.Null(module.GlobalNamespace.GetMember("<>O")); + }; + CompileAndVerify(source, symbolValidator: containerValidator); + } + + [Fact] + public void Not_InStaticConstructor1() + { + var source = @" +using System; +struct C +{ + static readonly Action ManualCache; + static void Target() { } + + static C() + { + ManualCache = Target; + } +} +"; + Action containerValidator = module => + { + Assert.Null(module.GlobalNamespace.GetMember("<>O")); + }; + CompileAndVerify(source, symbolValidator: containerValidator); + } + + [Fact] + public void CacheExplicitConversions_TypeScoped_CouldBeModuleScoped0() + { + var source = @" +using System; +class C +{ + public static void Main(string[] args) + { + Test((Action)Target); + } + + static void Test(Action action) + { + action(); + } + + static void Target() { Console.WriteLine(""PASS""); } +} +"; + var verifier = CompileAndVerify(source, expectedOutput: PASS); + verifier.VerifyIL("C.Main", @" +{ + // Code size 33 (0x21) + .maxstack 2 + IL_0000: ldsfld ""System.Action C.<>O.<0>__Target"" + IL_0005: dup + IL_0006: brtrue.s IL_001b + IL_0008: pop + IL_0009: ldnull + IL_000a: ldftn ""void C.Target()"" + IL_0010: newobj ""System.Action..ctor(object, System.IntPtr)"" + IL_0015: dup + IL_0016: stsfld ""System.Action C.<>O.<0>__Target"" + IL_001b: call ""void C.Test(System.Action)"" + IL_0020: ret +} +"); + } + + [Fact] + public void CacheExplicitConversions_TypeScoped_CouldBeModuleScoped1() + { + var source = @" +using System; +class C +{ + public static void Main(string[] args) + { + var d = new D(); + d.Test()(); + } + + public static void Target() { Console.WriteLine(""PASS""); } +} +class D +{ + public Action Test() + { + return (Action)C.Target; + } +} +"; + var verifier = CompileAndVerify(source, expectedOutput: PASS); + verifier.VerifyIL("D.Test", @" +{ + // Code size 28 (0x1c) + .maxstack 2 + IL_0000: ldsfld ""System.Action D.<>O.<0>__Target"" + IL_0005: dup + IL_0006: brtrue.s IL_001b + IL_0008: pop + IL_0009: ldnull + IL_000a: ldftn ""void C.Target()"" + IL_0010: newobj ""System.Action..ctor(object, System.IntPtr)"" + IL_0015: dup + IL_0016: stsfld ""System.Action D.<>O.<0>__Target"" + IL_001b: ret +} +"); + } + + [Fact] + public void CacheExplicitConversions_TypeScoped_CouldBeModuleScoped2() + { + var source = @" +using System; +class C +{ + public static void Main(string[] args) + { + (new D()).Test(); + } +} +class D +{ + public void Test() + { + var t = (Action)E.Target; + t(); + } +} +class E +{ + public static void Target() { Console.WriteLine(""PASS""); } +} +"; + var verifier = CompileAndVerify(source, expectedOutput: PASS); + verifier.VerifyIL("D.Test", @" +{ + // Code size 33 (0x21) + .maxstack 2 + IL_0000: ldsfld ""System.Action D.<>O.<0>__Target"" + IL_0005: dup + IL_0006: brtrue.s IL_001b + IL_0008: pop + IL_0009: ldnull + IL_000a: ldftn ""void E.Target()"" + IL_0010: newobj ""System.Action..ctor(object, System.IntPtr)"" + IL_0015: dup + IL_0016: stsfld ""System.Action D.<>O.<0>__Target"" + IL_001b: callvirt ""void System.Action.Invoke()"" + IL_0020: ret +} +"); + } + + [Fact] + public void CacheExplicitConversions_TypeScoped_CouldBeModuleScoped3() + { + var source = @" +using System; +class C +{ + public static void Main(string[] args) + { + (new D()).Test(); + } +} +class D +{ + public void Test() + { + var t = (Action)E.Target; + t(0); + } +} +class E +{ + public static void Target(V v) { Console.WriteLine(""PASS""); } +} +"; + var verifier = CompileAndVerify(source, expectedOutput: PASS); + verifier.VerifyIL("D.Test", @" +{ + // Code size 34 (0x22) + .maxstack 2 + IL_0000: ldsfld ""System.Action D.<>O.<0>__Target"" + IL_0005: dup + IL_0006: brtrue.s IL_001b + IL_0008: pop + IL_0009: ldnull + IL_000a: ldftn ""void E.Target(int)"" + IL_0010: newobj ""System.Action..ctor(object, System.IntPtr)"" + IL_0015: dup + IL_0016: stsfld ""System.Action D.<>O.<0>__Target"" + IL_001b: ldc.i4.0 + IL_001c: callvirt ""void System.Action.Invoke(int)"" + IL_0021: ret +} +"); + } + + [Fact] + public void CacheExplicitConversions_TypeScoped_CouldBeModuleScoped4() + { + var source = @" +using System; +class C +{ + public static void Main(string[] args) + { + (new D()).Test(); + } +} +class D +{ + public void Test() + { + var t = (Action)E.Target; + t(0); + } +} +class E +{ + public static void Target(V v) { Console.WriteLine(""PASS""); } +} +"; + var verifier = CompileAndVerify(source, expectedOutput: PASS); + verifier.VerifyIL("D.Test", @" +{ + // Code size 34 (0x22) + .maxstack 2 + IL_0000: ldsfld ""System.Action D.<>O.<0>__Target"" + IL_0005: dup + IL_0006: brtrue.s IL_001b + IL_0008: pop + IL_0009: ldnull + IL_000a: ldftn ""void E.Target(int)"" + IL_0010: newobj ""System.Action..ctor(object, System.IntPtr)"" + IL_0015: dup + IL_0016: stsfld ""System.Action D.<>O.<0>__Target"" + IL_001b: ldc.i4.0 + IL_001c: callvirt ""void System.Action.Invoke(int)"" + IL_0021: ret +} +"); + } + + [Fact] + public void CacheExplicitConversions_TypeScoped_CouldBeModuleScoped5() + { + var source = @" +using System; +class C +{ + public static void Main(string[] args) + { + (new D()).Test(); + } +} +class D +{ + public void Test() + { + var t = (Func)E.Target; + t(0); + } +} +class E +{ + public static V Target(V v) { Console.WriteLine(""PASS""); return default(V); } +} +"; + var verifier = CompileAndVerify(source, expectedOutput: PASS); + verifier.VerifyIL("D.Test", @" +{ + // Code size 35 (0x23) + .maxstack 2 + IL_0000: ldsfld ""System.Func D.<>O.<0>__Target"" + IL_0005: dup + IL_0006: brtrue.s IL_001b + IL_0008: pop + IL_0009: ldnull + IL_000a: ldftn ""int E.Target(int)"" + IL_0010: newobj ""System.Func..ctor(object, System.IntPtr)"" + IL_0015: dup + IL_0016: stsfld ""System.Func D.<>O.<0>__Target"" + IL_001b: ldc.i4.0 + IL_001c: callvirt ""int System.Func.Invoke(int)"" + IL_0021: pop + IL_0022: ret +} +"); + } + + [Fact] + public void CacheExplicitConversions_TypeScoped0() + { + var source = @" +using System; +class C +{ + public static void Main(string[] args) + { + (new D()).Test(); + } +} +class D +{ + public void Test() + { + var t = (Action)Target; + t(); + } + + static void Target() { Console.WriteLine(""PASS""); } +} +"; + var verifier = CompileAndVerify(source, expectedOutput: PASS); + verifier.VerifyIL("D.Test", @" +{ + // Code size 33 (0x21) + .maxstack 2 + IL_0000: ldsfld ""System.Action D.<>O.<0>__Target"" + IL_0005: dup + IL_0006: brtrue.s IL_001b + IL_0008: pop + IL_0009: ldnull + IL_000a: ldftn ""void D.Target()"" + IL_0010: newobj ""System.Action..ctor(object, System.IntPtr)"" + IL_0015: dup + IL_0016: stsfld ""System.Action D.<>O.<0>__Target"" + IL_001b: callvirt ""void System.Action.Invoke()"" + IL_0020: ret +} +"); + } + + [Fact] + public void CacheExplicitConversions_TypeScoped1() + { + var source = @" +using System; +class C +{ + public static void Main(string[] args) + { + (new D()).Test(); + } +} +class D +{ + public void Test() + { + var t = (Action)Target; + t(); + } + + static void Target() { Console.WriteLine(""PASS""); } +} +"; + var verifier = CompileAndVerify(source, expectedOutput: PASS); + verifier.VerifyIL("D.Test", @" +{ + // Code size 33 (0x21) + .maxstack 2 + IL_0000: ldsfld ""System.Action D.<>O.<0>__Target"" + IL_0005: dup + IL_0006: brtrue.s IL_001b + IL_0008: pop + IL_0009: ldnull + IL_000a: ldftn ""void D.Target()"" + IL_0010: newobj ""System.Action..ctor(object, System.IntPtr)"" + IL_0015: dup + IL_0016: stsfld ""System.Action D.<>O.<0>__Target"" + IL_001b: callvirt ""void System.Action.Invoke()"" + IL_0020: ret +} +"); + } + + [Fact] + public void CacheExplicitConversions_TypeScoped2() + { + var source = @" +using System; +class C +{ + public static void Main(string[] args) + { + (new D()).Test(); + } +} +class D +{ + public void Test() + { + var t = (Action)Target; + t(); + } + + static void Target() { Console.WriteLine(""PASS""); } +} +"; + var verifier = CompileAndVerify(source, expectedOutput: PASS); + verifier.VerifyIL("D.Test", @" +{ + // Code size 33 (0x21) + .maxstack 2 + IL_0000: ldsfld ""System.Action D.<>O.<0>__Target"" + IL_0005: dup + IL_0006: brtrue.s IL_001b + IL_0008: pop + IL_0009: ldnull + IL_000a: ldftn ""void D.Target()"" + IL_0010: newobj ""System.Action..ctor(object, System.IntPtr)"" + IL_0015: dup + IL_0016: stsfld ""System.Action D.<>O.<0>__Target"" + IL_001b: callvirt ""void System.Action.Invoke()"" + IL_0020: ret +} +"); + } + + [Fact] + public void CacheExplicitConversions_TypeScoped3() + { + var source = @" +using System; +class C +{ + public static void Main(string[] args) + { + (new D()).Test(); + } +} +class D +{ + public void Test() + { + var t = (Action)E.Target; + t(); + } +} +class E +{ + public static void Target() { Console.WriteLine(""PASS""); } +} +"; + var verifier = CompileAndVerify(source, expectedOutput: PASS); + verifier.VerifyIL("D.Test", @" +{ + // Code size 33 (0x21) + .maxstack 2 + IL_0000: ldsfld ""System.Action D.<>O.<0>__Target"" + IL_0005: dup + IL_0006: brtrue.s IL_001b + IL_0008: pop + IL_0009: ldnull + IL_000a: ldftn ""void E.Target()"" + IL_0010: newobj ""System.Action..ctor(object, System.IntPtr)"" + IL_0015: dup + IL_0016: stsfld ""System.Action D.<>O.<0>__Target"" + IL_001b: callvirt ""void System.Action.Invoke()"" + IL_0020: ret +} +"); + } + + [Fact] + public void CacheExplicitConversions_TypeScoped4() + { + var source = @" +using System; +class C +{ + public static void Main(string[] args) + { + (new D()).Test(); + } +} +class D +{ + public void Test() + { + var t = (Action)E.Target; + t(); + } +} +class E +{ + public static void Target() { Console.WriteLine(""PASS""); } +} +"; + var verifier = CompileAndVerify(source, expectedOutput: PASS); + verifier.VerifyIL("D.Test", @" +{ + // Code size 33 (0x21) + .maxstack 2 + IL_0000: ldsfld ""System.Action D.<>O.<0>__Target"" + IL_0005: dup + IL_0006: brtrue.s IL_001b + IL_0008: pop + IL_0009: ldnull + IL_000a: ldftn ""void E.Target()"" + IL_0010: newobj ""System.Action..ctor(object, System.IntPtr)"" + IL_0015: dup + IL_0016: stsfld ""System.Action D.<>O.<0>__Target"" + IL_001b: callvirt ""void System.Action.Invoke()"" + IL_0020: ret +} +"); + } + + [Fact] + public void CacheExplicitConversions_TypeScoped5() + { + var source = @" +using System; +class C +{ + public static void Main(string[] args) + { + (new D()).Test(); + } +} +class D +{ + public void Test() + { + var t = (Func)E.Target; + t(); + } +} +class E +{ + public static N Target() { Console.WriteLine(""PASS""); return default(N); } +} +"; + var verifier = CompileAndVerify(source, expectedOutput: PASS); + verifier.VerifyIL("D.Test", @" +{ + // Code size 34 (0x22) + .maxstack 2 + IL_0000: ldsfld ""System.Func D.<>O.<0>__Target"" + IL_0005: dup + IL_0006: brtrue.s IL_001b + IL_0008: pop + IL_0009: ldnull + IL_000a: ldftn ""T E.Target()"" + IL_0010: newobj ""System.Func..ctor(object, System.IntPtr)"" + IL_0015: dup + IL_0016: stsfld ""System.Func D.<>O.<0>__Target"" + IL_001b: callvirt ""T System.Func.Invoke()"" + IL_0020: pop + IL_0021: ret +} +"); + } + + [Fact] + public void CacheExplicitConversions_TypeScoped6() + { + var source = @" +using System; +class C +{ + public static void Main(string[] args) + { + (new D()).Test(); + } +} +class D +{ + public void Test() + { + var t = (Func)E.Target; + t(); + } +} +class E +{ + public static V Target() { Console.WriteLine(""PASS""); return default(V); } +} +"; + var verifier = CompileAndVerify(source, expectedOutput: PASS); + verifier.VerifyIL("D.Test", @" +{ + // Code size 34 (0x22) + .maxstack 2 + IL_0000: ldsfld ""System.Func D.<>O.<0>__Target"" + IL_0005: dup + IL_0006: brtrue.s IL_001b + IL_0008: pop + IL_0009: ldnull + IL_000a: ldftn ""int E.Target()"" + IL_0010: newobj ""System.Func..ctor(object, System.IntPtr)"" + IL_0015: dup + IL_0016: stsfld ""System.Func D.<>O.<0>__Target"" + IL_001b: callvirt ""int System.Func.Invoke()"" + IL_0020: pop + IL_0021: ret +} +"); + } + + [Fact] + public void CacheExplicitConversions_TypeScoped7() + { + var source = @" +using System; +class C +{ + public static void Main(string[] args) + { + (new D()).Test(); + } +} +class D +{ + public void Test() + { + var t = (Func)E.Target; + t(default(T)); + } +} +class E +{ + public static V Target(K k) { Console.WriteLine(""PASS""); return default(V); } +} +"; + var verifier = CompileAndVerify(source, expectedOutput: PASS); + verifier.VerifyIL("D.Test", @" +{ + // Code size 43 (0x2b) + .maxstack 2 + .locals init (T V_0) + IL_0000: ldsfld ""System.Func D.<>O.<0>__Target"" + IL_0005: dup + IL_0006: brtrue.s IL_001b + IL_0008: pop + IL_0009: ldnull + IL_000a: ldftn ""int E.Target(T)"" + IL_0010: newobj ""System.Func..ctor(object, System.IntPtr)"" + IL_0015: dup + IL_0016: stsfld ""System.Func D.<>O.<0>__Target"" + IL_001b: ldloca.s V_0 + IL_001d: initobj ""T"" + IL_0023: ldloc.0 + IL_0024: callvirt ""int System.Func.Invoke(T)"" + IL_0029: pop + IL_002a: ret +} +"); + } + + [Fact] + public void CacheExplicitConversions_MethodScoped0() + { + var source = @" +using System; +class C +{ + public static void Main(string[] args) + { + D.Test(); + } + + public static void Target() { Console.WriteLine(""PASS""); } +} +class D +{ + public static void Test() + { + var t = (Action)C.Target; + t(); + } +} +"; + var verifier = CompileAndVerify(source, expectedOutput: PASS); + verifier.VerifyIL("D.Test", @" +{ + // Code size 33 (0x21) + .maxstack 2 + IL_0000: ldsfld ""System.Action D.O__0.<0>__Target"" + IL_0005: dup + IL_0006: brtrue.s IL_001b + IL_0008: pop + IL_0009: ldnull + IL_000a: ldftn ""void C.Target()"" + IL_0010: newobj ""System.Action..ctor(object, System.IntPtr)"" + IL_0015: dup + IL_0016: stsfld ""System.Action D.O__0.<0>__Target"" + IL_001b: callvirt ""void System.Action.Invoke()"" + IL_0020: ret +} +"); + } + + [Fact] + public void CacheExplicitConversions_MethodScoped1() + { + var source = @" +using System; +class C +{ + public static void Main(string[] args) + { + (new D()).Test(); + } +} +class D +{ + public void Test() + { + var t = (Action)E.Target; + t(); + } +} +class E +{ + public static void Target() { Console.WriteLine(""PASS""); } +} +"; + var verifier = CompileAndVerify(source, expectedOutput: PASS); + verifier.VerifyIL("D.Test", @" +{ + // Code size 33 (0x21) + .maxstack 2 + IL_0000: ldsfld ""System.Action D.O__0.<0>__Target"" + IL_0005: dup + IL_0006: brtrue.s IL_001b + IL_0008: pop + IL_0009: ldnull + IL_000a: ldftn ""void E.Target()"" + IL_0010: newobj ""System.Action..ctor(object, System.IntPtr)"" + IL_0015: dup + IL_0016: stsfld ""System.Action D.O__0.<0>__Target"" + IL_001b: callvirt ""void System.Action.Invoke()"" + IL_0020: ret +} +"); + } + + [Fact] + public void CacheExplicitConversions_MethodScoped2() + { + var source = @" +using System; +class C +{ + public static void Main(string[] args) + { + (new D()).Test(); + } +} +class D +{ + public void Test() + { + var t = (Func)E.Target; + t(default(T)); + } +} +class E +{ + public static V Target(K k) { Console.WriteLine(""PASS""); return default(V); } +} +"; + var verifier = CompileAndVerify(source, expectedOutput: PASS); + verifier.VerifyIL("D.Test", @" +{ + // Code size 43 (0x2b) + .maxstack 2 + .locals init (T V_0) + IL_0000: ldsfld ""System.Func D.O__0.<0>__Target"" + IL_0005: dup + IL_0006: brtrue.s IL_001b + IL_0008: pop + IL_0009: ldnull + IL_000a: ldftn ""int E.Target(T)"" + IL_0010: newobj ""System.Func..ctor(object, System.IntPtr)"" + IL_0015: dup + IL_0016: stsfld ""System.Func D.O__0.<0>__Target"" + IL_001b: ldloca.s V_0 + IL_001d: initobj ""T"" + IL_0023: ldloc.0 + IL_0024: callvirt ""int System.Func.Invoke(T)"" + IL_0029: pop + IL_002a: ret +} +"); + } + + [Fact] + public void CacheExplicitConversions_MethodScoped3() + { + var source = @" +using System; +class C +{ + public static void Main(string[] args) + { + (new D()).Test(); + } +} +class D +{ + public void Test() + { + var t = (Action)E.Target; + t(default(M), default(T)); + } +} +class E +{ + public static void Target(K k, V v) { Console.WriteLine(""PASS""); } +} +"; + var verifier = CompileAndVerify(source, expectedOutput: PASS); + verifier.VerifyIL("D.Test", @" +{ + // Code size 51 (0x33) + .maxstack 3 + .locals init (M V_0, + T V_1) + IL_0000: ldsfld ""System.Action D.O__0.<0>__Target"" + IL_0005: dup + IL_0006: brtrue.s IL_001b + IL_0008: pop + IL_0009: ldnull + IL_000a: ldftn ""void E.Target(M, T)"" + IL_0010: newobj ""System.Action..ctor(object, System.IntPtr)"" + IL_0015: dup + IL_0016: stsfld ""System.Action D.O__0.<0>__Target"" + IL_001b: ldloca.s V_0 + IL_001d: initobj ""M"" + IL_0023: ldloc.0 + IL_0024: ldloca.s V_1 + IL_0026: initobj ""T"" + IL_002c: ldloc.1 + IL_002d: callvirt ""void System.Action.Invoke(M, T)"" + IL_0032: ret +} +"); + } + + [Fact] + public void CacheImplicitConversions_TypeScoped_CouldBeModuleScoped0() + { + var source = @" +using System; +delegate void MyAction(); +class C +{ + public static void Main(string[] args) + { + MyAction t = Target; + t(); + } + + static void Target() { Console.WriteLine(""PASS""); } +} +"; + var verifier = CompileAndVerify(source, expectedOutput: PASS); + verifier.VerifyIL("C.Main", @" +{ + // Code size 33 (0x21) + .maxstack 2 + IL_0000: ldsfld ""MyAction C.<>O.<0>__Target"" + IL_0005: dup + IL_0006: brtrue.s IL_001b + IL_0008: pop + IL_0009: ldnull + IL_000a: ldftn ""void C.Target()"" + IL_0010: newobj ""MyAction..ctor(object, System.IntPtr)"" + IL_0015: dup + IL_0016: stsfld ""MyAction C.<>O.<0>__Target"" + IL_001b: callvirt ""void MyAction.Invoke()"" + IL_0020: ret +} +"); + } + + [Fact] + public void CacheImplicitConversions_TypeScoped_CouldBeModuleScoped1() + { + var source = @" +using System; +class C +{ + public delegate void MyAction(); + + public static void Main(string[] args) + { + MyAction t = Target; + t(); + } + + static void Target() { Console.WriteLine(""PASS""); } +} +"; + var verifier = CompileAndVerify(source, expectedOutput: PASS); + verifier.VerifyIL("C.Main", @" +{ + // Code size 33 (0x21) + .maxstack 2 + IL_0000: ldsfld ""C.MyAction C.<>O.<0>__Target"" + IL_0005: dup + IL_0006: brtrue.s IL_001b + IL_0008: pop + IL_0009: ldnull + IL_000a: ldftn ""void C.Target()"" + IL_0010: newobj ""C.MyAction..ctor(object, System.IntPtr)"" + IL_0015: dup + IL_0016: stsfld ""C.MyAction C.<>O.<0>__Target"" + IL_001b: callvirt ""void C.MyAction.Invoke()"" + IL_0020: ret +} +"); + } + + [Fact] + public void CacheImplicitConversions_TypeScoped0() + { + var source = @" +using System; +delegate void MyAction(); +class C +{ + public static void Main(string[] args) + { + (new D()).Test(); + } +} +class D +{ + public void Test() + { + MyAction t = Target; + t(); + } + + public static void Target() { Console.WriteLine(""PASS""); } +} +"; + var verifier = CompileAndVerify(source, expectedOutput: PASS); + verifier.VerifyIL("D.Test", @" +{ + // Code size 33 (0x21) + .maxstack 2 + IL_0000: ldsfld ""MyAction D.<>O.<0>__Target"" + IL_0005: dup + IL_0006: brtrue.s IL_001b + IL_0008: pop + IL_0009: ldnull + IL_000a: ldftn ""void D.Target()"" + IL_0010: newobj ""MyAction..ctor(object, System.IntPtr)"" + IL_0015: dup + IL_0016: stsfld ""MyAction D.<>O.<0>__Target"" + IL_001b: callvirt ""void MyAction.Invoke()"" + IL_0020: ret +} +"); + } + + [Fact] + public void CacheImplicitConversions_TypeScoped1() + { + var source = @" +using System; +class C +{ + public static void Main(string[] args) + { + (new D()).Test()(); + } +} +class D +{ + public delegate void MyAction(); + + public MyAction Test() + { + return Target; + } + + static void Target() { Console.WriteLine(""PASS""); } +} +"; + var verifier = CompileAndVerify(source, expectedOutput: PASS); + verifier.VerifyIL("D.Test", @" +{ + // Code size 28 (0x1c) + .maxstack 2 + IL_0000: ldsfld ""D.MyAction D.<>O.<0>__Target"" + IL_0005: dup + IL_0006: brtrue.s IL_001b + IL_0008: pop + IL_0009: ldnull + IL_000a: ldftn ""void D.Target()"" + IL_0010: newobj ""D.MyAction..ctor(object, System.IntPtr)"" + IL_0015: dup + IL_0016: stsfld ""D.MyAction D.<>O.<0>__Target"" + IL_001b: ret +} +"); + } + + [Fact] + public void CacheImplicitConversions_TypeScoped2() + { + var source = @" +using System; +class C +{ + public static void Main(string[] args) + { + (new D()).Test(); + } +} +class D +{ + delegate void MyAction(); + + public void Test() + { + MyAction t = Target; + t(); + } + + static void Target() { Console.WriteLine(""PASS""); } +} +"; + var verifier = CompileAndVerify(source, expectedOutput: PASS); + verifier.VerifyIL("D.Test", @" +{ + // Code size 33 (0x21) + .maxstack 2 + IL_0000: ldsfld ""D.MyAction D.<>O.<0>__Target"" + IL_0005: dup + IL_0006: brtrue.s IL_001b + IL_0008: pop + IL_0009: ldnull + IL_000a: ldftn ""void D.Target()"" + IL_0010: newobj ""D.MyAction..ctor(object, System.IntPtr)"" + IL_0015: dup + IL_0016: stsfld ""D.MyAction D.<>O.<0>__Target"" + IL_001b: callvirt ""void D.MyAction.Invoke()"" + IL_0020: ret +} +"); + } + + [Fact] + public void CacheImplicitConversions_TypeScoped3() + { + var source = @" +using System; +class C +{ + public static void Main(string[] args) + { + (new D()).Test(); + } +} +class D +{ + delegate void MyAction(); + + public void Test() + { + MyAction t = E.Target; + t(); + } +} +class E +{ + public static void Target() { Console.WriteLine(""PASS""); } +} +"; + var verifier = CompileAndVerify(source, expectedOutput: PASS); + verifier.VerifyIL("D.Test", @" +{ + // Code size 33 (0x21) + .maxstack 2 + IL_0000: ldsfld ""D.MyAction D.<>O.<0>__Target"" + IL_0005: dup + IL_0006: brtrue.s IL_001b + IL_0008: pop + IL_0009: ldnull + IL_000a: ldftn ""void E.Target()"" + IL_0010: newobj ""D.MyAction..ctor(object, System.IntPtr)"" + IL_0015: dup + IL_0016: stsfld ""D.MyAction D.<>O.<0>__Target"" + IL_001b: callvirt ""void D.MyAction.Invoke()"" + IL_0020: ret +} +"); + } + + [Fact] + public void CacheImplicitConversions_TypeScoped4() + { + var source = @" +using System; +class C +{ + public static void Main(string[] args) + { + (new D()).Test(); + } +} +class D +{ + delegate void MyAction(); + + public void Test() + { + MyAction t = E.Target; + t(); + } +} +class E +{ + public static void Target() { Console.WriteLine(""PASS""); } +} +"; + var verifier = CompileAndVerify(source, expectedOutput: PASS); + verifier.VerifyIL("D.Test", @" +{ + // Code size 33 (0x21) + .maxstack 2 + IL_0000: ldsfld ""D.MyAction D.<>O.<0>__Target"" + IL_0005: dup + IL_0006: brtrue.s IL_001b + IL_0008: pop + IL_0009: ldnull + IL_000a: ldftn ""void E.Target()"" + IL_0010: newobj ""D.MyAction..ctor(object, System.IntPtr)"" + IL_0015: dup + IL_0016: stsfld ""D.MyAction D.<>O.<0>__Target"" + IL_001b: callvirt ""void D.MyAction.Invoke()"" + IL_0020: ret +} +"); + } + + [Fact] + public void CacheImplicitConversions_TypeScoped5() + { + var source = @" +using System; +class C +{ + public static void Main(string[] args) + { + (new D()).Test(); + } +} +class D +{ + delegate T MyFunc(); + + public void Test() + { + MyFunc t = E.Target; + t(); + } +} +class E +{ + public static V Target() { Console.WriteLine(""PASS""); return default(V); } +} +"; + var verifier = CompileAndVerify(source, expectedOutput: PASS); + verifier.VerifyIL("D.Test", @" +{ + // Code size 34 (0x22) + .maxstack 2 + IL_0000: ldsfld ""D.MyFunc D.<>O.<0>__Target"" + IL_0005: dup + IL_0006: brtrue.s IL_001b + IL_0008: pop + IL_0009: ldnull + IL_000a: ldftn ""T E.Target()"" + IL_0010: newobj ""D.MyFunc..ctor(object, System.IntPtr)"" + IL_0015: dup + IL_0016: stsfld ""D.MyFunc D.<>O.<0>__Target"" + IL_001b: callvirt ""T D.MyFunc.Invoke()"" + IL_0020: pop + IL_0021: ret +} +"); + } + + [Fact] + public void CacheImplicitConversions_TypeScoped6() + { + var source = @" +using System; +class C +{ + public static void Main(string[] args) + { + (new D()).Test(); + } +} +class D +{ + delegate int MyFunc(T i); + + public void Test() + { + MyFunc t = E.Target; + t(default(T)); + } +} +class E +{ + public static V Target(K k) { Console.WriteLine(""PASS""); return default(V); } +} +"; + var verifier = CompileAndVerify(source, expectedOutput: PASS); + verifier.VerifyIL("D.Test", @" +{ + // Code size 43 (0x2b) + .maxstack 2 + .locals init (T V_0) + IL_0000: ldsfld ""D.MyFunc D.<>O.<0>__Target"" + IL_0005: dup + IL_0006: brtrue.s IL_001b + IL_0008: pop + IL_0009: ldnull + IL_000a: ldftn ""int E.Target(T)"" + IL_0010: newobj ""D.MyFunc..ctor(object, System.IntPtr)"" + IL_0015: dup + IL_0016: stsfld ""D.MyFunc D.<>O.<0>__Target"" + IL_001b: ldloca.s V_0 + IL_001d: initobj ""T"" + IL_0023: ldloc.0 + IL_0024: callvirt ""int D.MyFunc.Invoke(T)"" + IL_0029: pop + IL_002a: ret +} +"); + } + + [Fact] + public void CacheImplicitConversions_TypeScoped7() + { + var source = @" +using System; +class C +{ + public static void Main(string[] args) + { + (new D()).Test(); + } +} +class D +{ + delegate T MyFunc(M m); + + public void Test() + { + MyFunc t = E.Target; + t(default(M)); + } +} +class E +{ + public static V Target(K k) { Console.WriteLine(""PASS""); return default(V); } +} +"; + var verifier = CompileAndVerify(source, expectedOutput: PASS); + verifier.VerifyIL("D.Test", @" +{ + // Code size 43 (0x2b) + .maxstack 2 + .locals init (M V_0) + IL_0000: ldsfld ""D.MyFunc D.<>O.<0>__Target"" + IL_0005: dup + IL_0006: brtrue.s IL_001b + IL_0008: pop + IL_0009: ldnull + IL_000a: ldftn ""T E.Target(M)"" + IL_0010: newobj ""D.MyFunc..ctor(object, System.IntPtr)"" + IL_0015: dup + IL_0016: stsfld ""D.MyFunc D.<>O.<0>__Target"" + IL_001b: ldloca.s V_0 + IL_001d: initobj ""M"" + IL_0023: ldloc.0 + IL_0024: callvirt ""T D.MyFunc.Invoke(M)"" + IL_0029: pop + IL_002a: ret +} +"); + } + + [Fact] + public void CacheImplicitConversions_TypeScoped8() + { + var source = @" +using System; +class C +{ + delegate void MyAction(); + + public static void Main(string[] args) + { + MyAction t = Target; + t(); + } + + static void Target() { Console.WriteLine(""PASS""); } +} +"; + var verifier = CompileAndVerify(source, expectedOutput: PASS); + verifier.VerifyIL("C.Main", @" +{ + // Code size 33 (0x21) + .maxstack 2 + IL_0000: ldsfld ""C.MyAction C.<>O.<0>__Target"" + IL_0005: dup + IL_0006: brtrue.s IL_001b + IL_0008: pop + IL_0009: ldnull + IL_000a: ldftn ""void C.Target()"" + IL_0010: newobj ""C.MyAction..ctor(object, System.IntPtr)"" + IL_0015: dup + IL_0016: stsfld ""C.MyAction C.<>O.<0>__Target"" + IL_001b: callvirt ""void C.MyAction.Invoke()"" + IL_0020: ret +} +"); + } + + [Fact] + public void CacheImplicitConversions_MethodScoped0() + { + var source = @" +using System; +class C +{ + public static void Main(string[] args) + { + (new D()).Test(); + } + + public static void Target() { Console.WriteLine(""PASS""); } +} +class D +{ + delegate void MyAction(); + + public void Test() + { + MyAction t = C.Target; + t(); + } +} +"; + var verifier = CompileAndVerify(source, expectedOutput: PASS); + verifier.VerifyIL("D.Test", @" +{ + // Code size 33 (0x21) + .maxstack 2 + IL_0000: ldsfld ""D.MyAction D.O__1.<0>__Target"" + IL_0005: dup + IL_0006: brtrue.s IL_001b + IL_0008: pop + IL_0009: ldnull + IL_000a: ldftn ""void C.Target()"" + IL_0010: newobj ""D.MyAction..ctor(object, System.IntPtr)"" + IL_0015: dup + IL_0016: stsfld ""D.MyAction D.O__1.<0>__Target"" + IL_001b: callvirt ""void D.MyAction.Invoke()"" + IL_0020: ret +} +"); + } + + [Fact] + public void CacheImplicitConversions_MethodScoped1() + { + var source = @" +using System; +class C +{ + public static void Main(string[] args) + { + (new D()).Test(); + } + + public static void Target(K k) { Console.WriteLine(""PASS""); } +} +class D +{ + delegate void MyAction(M m); + + public void Test() + { + MyAction t = C.Target; + t(default(V)); + } +} +"; + var verifier = CompileAndVerify(source, expectedOutput: PASS); + verifier.VerifyIL("D.Test", @" +{ + // Code size 42 (0x2a) + .maxstack 2 + .locals init (V V_0) + IL_0000: ldsfld ""D.MyAction D.O__1.<0>__Target"" + IL_0005: dup + IL_0006: brtrue.s IL_001b + IL_0008: pop + IL_0009: ldnull + IL_000a: ldftn ""void C.Target(V)"" + IL_0010: newobj ""D.MyAction..ctor(object, System.IntPtr)"" + IL_0015: dup + IL_0016: stsfld ""D.MyAction D.O__1.<0>__Target"" + IL_001b: ldloca.s V_0 + IL_001d: initobj ""V"" + IL_0023: ldloc.0 + IL_0024: callvirt ""void D.MyAction.Invoke(V)"" + IL_0029: ret +} +"); + } + + [Fact] + public void CacheImplicitConversions_MethodScoped2() + { + var source = @" +using System; +class C +{ + public static void Main(string[] args) + { + (new D()).Test(); + } + + public static N Target(K k, N n) { Console.WriteLine(""PASS""); return default(N); } +} +class D +{ + delegate V MyFunc(M m, V v); + + public void Test() + { + MyFunc t = C.Target; + t(default(T), default(V)); + } +} +"; + var verifier = CompileAndVerify(source, expectedOutput: PASS); + verifier.VerifyIL("D.Test", @" +{ + // Code size 52 (0x34) + .maxstack 3 + .locals init (T V_0, + V V_1) + IL_0000: ldsfld ""D.MyFunc D.O__1.<0>__Target"" + IL_0005: dup + IL_0006: brtrue.s IL_001b + IL_0008: pop + IL_0009: ldnull + IL_000a: ldftn ""V C.Target(T, V)"" + IL_0010: newobj ""D.MyFunc..ctor(object, System.IntPtr)"" + IL_0015: dup + IL_0016: stsfld ""D.MyFunc D.O__1.<0>__Target"" + IL_001b: ldloca.s V_0 + IL_001d: initobj ""T"" + IL_0023: ldloc.0 + IL_0024: ldloca.s V_1 + IL_0026: initobj ""V"" + IL_002c: ldloc.1 + IL_002d: callvirt ""V D.MyFunc.Invoke(T, V)"" + IL_0032: pop + IL_0033: ret +} +"); + } + + [Fact] + public void Where_TypeScoped0() + { + var source = @" +using System; +class C +{ + public static void Main(string[] args) + { + (new D()).Test(); + } +} +class D where T : C +{ + delegate void MyAction(T t); + + public void Test() + { + MyAction t = E.Target; + t(null); + } +} +class E +{ + public static void Target(N n) { Console.WriteLine(""PASS""); } +} +"; + var verifier = CompileAndVerify(source, expectedOutput: PASS); + verifier.VerifyIL("D.Test", @" +{ + // Code size 42 (0x2a) + .maxstack 2 + .locals init (T V_0) + IL_0000: ldsfld ""D.MyAction D.<>O.<0>__Target"" + IL_0005: dup + IL_0006: brtrue.s IL_001b + IL_0008: pop + IL_0009: ldnull + IL_000a: ldftn ""void E.Target(C)"" + IL_0010: newobj ""D.MyAction..ctor(object, System.IntPtr)"" + IL_0015: dup + IL_0016: stsfld ""D.MyAction D.<>O.<0>__Target"" + IL_001b: ldloca.s V_0 + IL_001d: initobj ""T"" + IL_0023: ldloc.0 + IL_0024: callvirt ""void D.MyAction.Invoke(T)"" + IL_0029: ret +} +"); + } + + [Fact] + public void Where_MethodScoped0() + { + var source = @" +using System; +class C +{ + public static void Main(string[] args) + { + (new D()).Test(new C()); + } +} +class D where T : C +{ + delegate T MyFunc(); + + public void Test(M m) where M : T + { + MyFunc t = E.Target; + t(); + } +} +class E +{ + public static N Target() { Console.WriteLine(""PASS""); return default(N); } +} +"; + Action containerValidator = module => + { + var testClass = module.GlobalNamespace.GetTypeMember("D"); + var container = testClass.GetTypeMember("O__1"); + Assert.NotNull(container); Debug.Assert(container is { }); + + var typeParameters = container.TypeParameters; + Assert.Equal(1, container.TypeParameters.Length); + + var m = typeParameters[0]; + Assert.Equal(1, m.ConstraintTypes().Length); + Assert.Equal(testClass.TypeParameters[0], m.ConstraintTypes()[0]); + }; + CompileAndVerify(source, symbolValidator: containerValidator, expectedOutput: PASS).VerifyIL("D.Test", @" +{ + // Code size 34 (0x22) + .maxstack 2 + IL_0000: ldsfld ""D.MyFunc D.O__1.<0>__Target"" + IL_0005: dup + IL_0006: brtrue.s IL_001b + IL_0008: pop + IL_0009: ldnull + IL_000a: ldftn ""M E.Target()"" + IL_0010: newobj ""D.MyFunc..ctor(object, System.IntPtr)"" + IL_0015: dup + IL_0016: stsfld ""D.MyFunc D.O__1.<0>__Target"" + IL_001b: callvirt ""T D.MyFunc.Invoke()"" + IL_0020: pop + IL_0021: ret +} +"); + } + + [Fact] + public void Where_MethodScoped1() + { + var source = @" +using System; +class C +{ + public static void Main(string[] args) + { + (new D()).Test(new C()); + } +} +class D +{ + delegate C MyFunc(); + + public void Test(M m) where M : C + { + MyFunc t = E.Target; + t(); + } +} +class E +{ + public static N Target() { Console.WriteLine(""PASS""); return default(N); } +} +"; + Action containerValidator = module => + { + var globalNs = module.GlobalNamespace; + var mainClass = globalNs.GetTypeMember("C"); + var container = globalNs.GetMember("D.O__1"); + Assert.NotNull(container); Debug.Assert(container is { }); + + var typeParameters = container.TypeParameters; + Assert.Equal(1, container.TypeParameters.Length); + + var m = typeParameters[0]; + Assert.Equal(1, m.ConstraintTypes().Length); + Assert.Equal(mainClass, m.ConstraintTypes()[0]); + }; + CompileAndVerify(source, symbolValidator: containerValidator, expectedOutput: PASS).VerifyIL("D.Test", @" +{ + // Code size 34 (0x22) + .maxstack 2 + IL_0000: ldsfld ""D.MyFunc D.O__1.<0>__Target"" + IL_0005: dup + IL_0006: brtrue.s IL_001b + IL_0008: pop + IL_0009: ldnull + IL_000a: ldftn ""M E.Target()"" + IL_0010: newobj ""D.MyFunc..ctor(object, System.IntPtr)"" + IL_0015: dup + IL_0016: stsfld ""D.MyFunc D.O__1.<0>__Target"" + IL_001b: callvirt ""C D.MyFunc.Invoke()"" + IL_0020: pop + IL_0021: ret +} +"); + } + + [Fact] + public void Where_MethodScoped2() + { + var source = @" +using System; +class C +{ + public static void Main(string[] args) + { + (new D()).Test(0); + } +} +class D +{ + public void Test(M m) where M : struct + { + Func t = E.Target; + t(); + } +} +class E +{ + public static N Target() { Console.WriteLine(""PASS""); return default(N); } +} +"; + Action containerValidator = module => + { + var container = module.GlobalNamespace.GetMember("D.O__0"); + Assert.NotNull(container); Debug.Assert(container is { }); + + var typeParameters = container.TypeParameters; + Assert.Equal(1, container.TypeParameters.Length); + + var m = typeParameters[0]; + Assert.NotNull(m); Debug.Assert(m is { }); + Assert.True(m.IsValueType); + }; + CompileAndVerify(source, symbolValidator: containerValidator, expectedOutput: PASS).VerifyIL("D.Test", @" +{ + // Code size 34 (0x22) + .maxstack 2 + IL_0000: ldsfld ""System.Func D.O__0.<0>__Target"" + IL_0005: dup + IL_0006: brtrue.s IL_001b + IL_0008: pop + IL_0009: ldnull + IL_000a: ldftn ""M? E.Target()"" + IL_0010: newobj ""System.Func..ctor(object, System.IntPtr)"" + IL_0015: dup + IL_0016: stsfld ""System.Func D.O__0.<0>__Target"" + IL_001b: callvirt ""M? System.Func.Invoke()"" + IL_0020: pop + IL_0021: ret +} +"); + } + + [Fact] + public void ExtensionMethod_TypeScoped_CouldBeModuleScoped0() + { + var source = @" +using System; +class C +{ + public static void Main(string[] args) + { + var t = (Action)E.Target; + t(null); + } +} +static class E +{ + public static void Target(this C c) { Console.WriteLine(""PASS""); } +} +"; + CompileAndVerify(source, expectedOutput: PASS).VerifyIL("C.Main", @" +{ + // Code size 34 (0x22) + .maxstack 2 + IL_0000: ldsfld ""System.Action C.<>O.<0>__Target"" + IL_0005: dup + IL_0006: brtrue.s IL_001b + IL_0008: pop + IL_0009: ldnull + IL_000a: ldftn ""void E.Target(C)"" + IL_0010: newobj ""System.Action..ctor(object, System.IntPtr)"" + IL_0015: dup + IL_0016: stsfld ""System.Action C.<>O.<0>__Target"" + IL_001b: ldnull + IL_001c: callvirt ""void System.Action.Invoke(C)"" + IL_0021: ret +} +"); + } + + [Fact] + public void ExtensionMethod_TypeScoped_CouldBeModuleScoped1() + { + var source = @" +using System; +class C +{ + public static void Main(string[] args) + { + Action t = E.Target; + t(null); + } +} +static class E +{ + public static void Target(this T t) { Console.WriteLine(""PASS""); } +} +"; + CompileAndVerify(source, expectedOutput: PASS).VerifyIL("C.Main", @" +{ + // Code size 34 (0x22) + .maxstack 2 + IL_0000: ldsfld ""System.Action C.<>O.<0>__Target"" + IL_0005: dup + IL_0006: brtrue.s IL_001b + IL_0008: pop + IL_0009: ldnull + IL_000a: ldftn ""void E.Target(C)"" + IL_0010: newobj ""System.Action..ctor(object, System.IntPtr)"" + IL_0015: dup + IL_0016: stsfld ""System.Action C.<>O.<0>__Target"" + IL_001b: ldnull + IL_001c: callvirt ""void System.Action.Invoke(C)"" + IL_0021: ret +} +"); + } + + [Fact] + public void ExtensionMethod_TypeScoped_CouldBeModuleScoped2() + { + var source = @" +using System; +static class E +{ + static void Test() + { + Action t = Target; + } + + public static void Target(this T t) { } +} +"; + CompileAndVerify(source).VerifyIL("E.Test", @" +{ + // Code size 25 (0x19) + .maxstack 2 + IL_0000: ldsfld ""System.Action E.<>O.<0>__Target"" + IL_0005: brtrue.s IL_0018 + IL_0007: ldnull + IL_0008: ldftn ""void E.Target(int)"" + IL_000e: newobj ""System.Action..ctor(object, System.IntPtr)"" + IL_0013: stsfld ""System.Action E.<>O.<0>__Target"" + IL_0018: ret +} +"); + } + + [Fact] + public void ExtensionMethod_TypeScoped0() + { + var source = @" +using System; +class C +{ + public static void Main(string[] args) + { + D.Test(); + } +} +class D where T : C +{ + public static void Test() + { + var t = (Action)E.Target; + t(null); + } +} +static class E +{ + public static void Target(this C c) { Console.WriteLine(""PASS""); } +} +"; + CompileAndVerify(source, expectedOutput: PASS).VerifyIL("D.Test", @" +{ + // Code size 42 (0x2a) + .maxstack 2 + .locals init (T V_0) + IL_0000: ldsfld ""System.Action D.<>O.<0>__Target"" + IL_0005: dup + IL_0006: brtrue.s IL_001b + IL_0008: pop + IL_0009: ldnull + IL_000a: ldftn ""void E.Target(C)"" + IL_0010: newobj ""System.Action..ctor(object, System.IntPtr)"" + IL_0015: dup + IL_0016: stsfld ""System.Action D.<>O.<0>__Target"" + IL_001b: ldloca.s V_0 + IL_001d: initobj ""T"" + IL_0023: ldloc.0 + IL_0024: callvirt ""void System.Action.Invoke(T)"" + IL_0029: ret +} +"); + } + + [Fact] + public void ExtensionMethod_TypeScoped1() + { + var source = @" +using System; +class C +{ + public static void Main(string[] args) + { + D.Test(0); + } +} +class D +{ + public static void Test(K k) + { + Action t = E.Target; + t(k); + } +} +static class E +{ + public static void Target(this T t) { Console.WriteLine(""PASS""); } +} +"; + CompileAndVerify(source, expectedOutput: PASS).VerifyIL("D.Test", @" +{ + // Code size 34 (0x22) + .maxstack 2 + IL_0000: ldsfld ""System.Action D.<>O.<0>__Target"" + IL_0005: dup + IL_0006: brtrue.s IL_001b + IL_0008: pop + IL_0009: ldnull + IL_000a: ldftn ""void E.Target(K)"" + IL_0010: newobj ""System.Action..ctor(object, System.IntPtr)"" + IL_0015: dup + IL_0016: stsfld ""System.Action D.<>O.<0>__Target"" + IL_001b: ldarg.0 + IL_001c: callvirt ""void System.Action.Invoke(K)"" + IL_0021: ret +} +"); + } + + [Fact] + public void ExtensionMethod_TypeScoped2() + { + var source = @" +using System; +static class E +{ + class F + { + void Test() + { + Action t = Target; + } + } + + public static void Target(this T t) { } +} +"; + CompileAndVerify(source).VerifyIL("E.F.Test", @" +{ + // Code size 25 (0x19) + .maxstack 2 + IL_0000: ldsfld ""System.Action E.F.<>O.<0>__Target"" + IL_0005: brtrue.s IL_0018 + IL_0007: ldnull + IL_0008: ldftn ""void E.Target(T)"" + IL_000e: newobj ""System.Action..ctor(object, System.IntPtr)"" + IL_0013: stsfld ""System.Action E.F.<>O.<0>__Target"" + IL_0018: ret +} +"); + } + + [Fact] + public void ExtensionMethod_MethodScoped0() + { + var source = @" +using System; +class C +{ + public static void Main(string[] args) + { + Test(0); + } + + static void Test(T arg) + { + var t = (Action)E.Target; + t(arg); + } +} +static class E +{ + public static void Target(this M m) { Console.WriteLine(""PASS""); } +} +"; + CompileAndVerify(source, expectedOutput: PASS).VerifyIL("C.Test", @" +{ + // Code size 34 (0x22) + .maxstack 2 + IL_0000: ldsfld ""System.Action C.O__1.<0>__Target"" + IL_0005: dup + IL_0006: brtrue.s IL_001b + IL_0008: pop + IL_0009: ldnull + IL_000a: ldftn ""void E.Target(T)"" + IL_0010: newobj ""System.Action..ctor(object, System.IntPtr)"" + IL_0015: dup + IL_0016: stsfld ""System.Action C.O__1.<0>__Target"" + IL_001b: ldarg.0 + IL_001c: callvirt ""void System.Action.Invoke(T)"" + IL_0021: ret +} +"); + } + + [Fact] + public void ExtensionMethod_MethodScoped1() + { + var source = @" +using System; +class C +{ + public static void Main(string[] args) + { + Test(); + } + + static void Test() where T : C + { + var t = (Action)E.Target; + t(null); + } +} +static class E +{ + public static void Target(this C c) { Console.WriteLine(""PASS""); } +} +"; + CompileAndVerify(source, expectedOutput: PASS).VerifyIL("C.Test", @" +{ + // Code size 42 (0x2a) + .maxstack 2 + .locals init (T V_0) + IL_0000: ldsfld ""System.Action C.O__1.<0>__Target"" + IL_0005: dup + IL_0006: brtrue.s IL_001b + IL_0008: pop + IL_0009: ldnull + IL_000a: ldftn ""void E.Target(C)"" + IL_0010: newobj ""System.Action..ctor(object, System.IntPtr)"" + IL_0015: dup + IL_0016: stsfld ""System.Action C.O__1.<0>__Target"" + IL_001b: ldloca.s V_0 + IL_001d: initobj ""T"" + IL_0023: ldloc.0 + IL_0024: callvirt ""void System.Action.Invoke(T)"" + IL_0029: ret +} +"); + } + + [Fact] + public void ExtensionMethod_MethodScoped2() + { + var source = @" +using System; +static class E +{ + static void Test() + { + Action t = Target; + } + + public static void Target(this T t) { } +} +"; + CompileAndVerify(source).VerifyIL("E.Test", @" +{ + // Code size 25 (0x19) + .maxstack 2 + IL_0000: ldsfld ""System.Action E.O__0.<0>__Target"" + IL_0005: brtrue.s IL_0018 + IL_0007: ldnull + IL_0008: ldftn ""void E.Target(T)"" + IL_000e: newobj ""System.Action..ctor(object, System.IntPtr)"" + IL_0013: stsfld ""System.Action E.O__0.<0>__Target"" + IL_0018: ret +} +"); + } + + [Fact] + public void Lambda_TypeScoped_CouldBeModuleScoped0() + { + var source = @" +using System; +class C +{ + public static void Main(string[] args) + { + Action test = () => Invoke(Target); + test(); + } + + static void Invoke(Action a) => a(); + + static void Target() => Console.WriteLine(""PASS""); +} +"; + var verifier = CompileAndVerify(source, expectedOutput: PASS); + verifier.VerifyIL("C.<>c.
b__0_0", @" +{ + // Code size 33 (0x21) + .maxstack 2 + IL_0000: ldsfld ""System.Action C.<>O.<0>__Target"" + IL_0005: dup + IL_0006: brtrue.s IL_001b + IL_0008: pop + IL_0009: ldnull + IL_000a: ldftn ""void C.Target()"" + IL_0010: newobj ""System.Action..ctor(object, System.IntPtr)"" + IL_0015: dup + IL_0016: stsfld ""System.Action C.<>O.<0>__Target"" + IL_001b: call ""void C.Invoke(System.Action)"" + IL_0020: ret +} +"); + } + + [Fact] + public void Lambda_TypeScoped_CouldBeModuleScoped1() + { + var source = @" +using System; +class C +{ + public static void Main(string[] args) + { + Action test = () => ((Action)D.Target)(); + test(); + } +} +class D +{ + public static void Target() { Console.WriteLine(""PASS""); } +} +"; + var verifier = CompileAndVerify(source, expectedOutput: PASS); + verifier.VerifyIL("C.<>c.
b__0_0", @" +{ + // Code size 33 (0x21) + .maxstack 2 + IL_0000: ldsfld ""System.Action C.<>O.<0>__Target"" + IL_0005: dup + IL_0006: brtrue.s IL_001b + IL_0008: pop + IL_0009: ldnull + IL_000a: ldftn ""void D.Target()"" + IL_0010: newobj ""System.Action..ctor(object, System.IntPtr)"" + IL_0015: dup + IL_0016: stsfld ""System.Action C.<>O.<0>__Target"" + IL_001b: callvirt ""void System.Action.Invoke()"" + IL_0020: ret +} +"); + } + + [Fact] + public void Lambda_TypeScoped0() + { + var source = @" +using System; +class C +{ + static void Main(string[] args) + { + D.Test(); + } +} +class D +{ + public static void Test() + { + Action test = () => + { + ((Func)Target)(); + }; + test(); + } + + static T Target() { Console.WriteLine(""PASS""); return default(T); } +} +"; + var verifier = CompileAndVerify(source, expectedOutput: PASS); + verifier.VerifyIL("D.<>c.b__0_0", @" +{ + // Code size 34 (0x22) + .maxstack 2 + IL_0000: ldsfld ""System.Func D.<>O.<0>__Target"" + IL_0005: dup + IL_0006: brtrue.s IL_001b + IL_0008: pop + IL_0009: ldnull + IL_000a: ldftn ""T D.Target()"" + IL_0010: newobj ""System.Func..ctor(object, System.IntPtr)"" + IL_0015: dup + IL_0016: stsfld ""System.Func D.<>O.<0>__Target"" + IL_001b: callvirt ""T System.Func.Invoke()"" + IL_0020: pop + IL_0021: ret +} +"); + } + + [Fact] + public void Lambda_TypeScoped1() + { + var source = @" +using System; +class C +{ + static void Main(string[] args) + { + D.Test(); + } +} +class D +{ + delegate T MyFunc(); + public static void Test() + { + Func a = () => E.Target; + a()(); + } +} +class E +{ + public static V Target() { Console.WriteLine(""PASS""); return default(V); } +} +"; + var verifier = CompileAndVerify(source, expectedOutput: PASS); + verifier.VerifyIL("D.<>c.b__1_0", @" +{ + // Code size 28 (0x1c) + .maxstack 2 + IL_0000: ldsfld ""D.MyFunc D.<>O.<0>__Target"" + IL_0005: dup + IL_0006: brtrue.s IL_001b + IL_0008: pop + IL_0009: ldnull + IL_000a: ldftn ""T E.Target()"" + IL_0010: newobj ""D.MyFunc..ctor(object, System.IntPtr)"" + IL_0015: dup + IL_0016: stsfld ""D.MyFunc D.<>O.<0>__Target"" + IL_001b: ret +} +"); + } + + [Fact] + public void Lambda_MethodScoped0() + { + var source = @" +using System; +class C +{ + static void Main(string[] args) + { + D.Test(0); + } +} +class D +{ + public static void Test(G g) + { + Action test = () => + { + ((Func)Target)(); + }; + test(); + } + + static T Target() { Console.WriteLine(""PASS""); return default(T); } +} +"; + var verifier = CompileAndVerify(source, expectedOutput: PASS); + verifier.VerifyIL("D.<>c__0.b__0_0", @" +{ + // Code size 34 (0x22) + .maxstack 2 + IL_0000: ldsfld ""System.Func D.<>O__0.<0>__Target"" + IL_0005: dup + IL_0006: brtrue.s IL_001b + IL_0008: pop + IL_0009: ldnull + IL_000a: ldftn ""T D.Target()"" + IL_0010: newobj ""System.Func..ctor(object, System.IntPtr)"" + IL_0015: dup + IL_0016: stsfld ""System.Func D.<>O__0.<0>__Target"" + IL_001b: callvirt ""T System.Func.Invoke()"" + IL_0020: pop + IL_0021: ret +} +"); + } + + [Fact] + public void Lambda_MethodScoped1() + { + var source = @" +using System; +class C +{ + static void Main(string[] args) + { + D.Test(0); + } +} +class D +{ + public static void Test(G g) + { + Func> a = () => E.Target; + a()(); + } +} +class E +{ + public static V Target() { Console.WriteLine(""PASS""); return default(V); } +} +"; + var verifier = CompileAndVerify(source, expectedOutput: PASS); + verifier.VerifyIL("D.<>c__0.b__0_0", @" +{ + // Code size 28 (0x1c) + .maxstack 2 + IL_0000: ldsfld ""System.Func D.<>O__0.<0>__Target"" + IL_0005: dup + IL_0006: brtrue.s IL_001b + IL_0008: pop + IL_0009: ldnull + IL_000a: ldftn ""G E.Target()"" + IL_0010: newobj ""System.Func..ctor(object, System.IntPtr)"" + IL_0015: dup + IL_0016: stsfld ""System.Func D.<>O__0.<0>__Target"" + IL_001b: ret +} +"); + } + + [Fact] + public void SameTypeAndSymbolResultsSameField_TypeScoped0() + { + var source = @" +using System; +class C +{ + void Test0() { var t = (Action)D.Target; } + void Test1() { Action t = D.Target; } +} +class D +{ + public static void Target() { } +} +"; + Action containerValidator = module => + { + var container = module.GlobalNamespace.GetMember("C.<>O"); + Assert.NotNull(container); Debug.Assert(container is { }); + Assert.Equal(0, container.Arity); + + var members = container.GetMembers(); + Assert.Equal(1, members.Length); + + var field = members[0] as FieldSymbol; + Assert.NotNull(field); Debug.Assert(field is { }); + Assert.Equal("<0>__Target", field.Name); + + var fieldType = field.Type as NamedTypeSymbol; + Assert.NotNull(fieldType); Debug.Assert(fieldType is { }); + Assert.True(fieldType.IsDelegateType()); + Assert.Equal("System", fieldType.ContainingNamespace.Name); + Assert.Equal("Action", fieldType.Name); + Assert.Equal(0, fieldType.Arity); + }; + CompileAndVerify(source, symbolValidator: containerValidator); + } + + [Fact] + public void SameTypeAndSymbolResultsSameField_TypeScoped1() + { + var source = @" +using System; +class C +{ + void Test0() { var t = (Func)Target; } + void Test1() { Func t = Target; } + static V Target() { return default(V); } +} +"; + Action containerValidator = module => + { + var container = module.GlobalNamespace.GetMember("C.<>O"); + Assert.NotNull(container); Debug.Assert(container is { }); + Assert.Equal(0, container.Arity); + + var members = container.GetMembers(); + Assert.Equal(1, members.Length); + + var field = members[0] as FieldSymbol; + Assert.NotNull(field); Debug.Assert(field is { }); + Assert.Equal("<0>__Target", field.Name); + + var fieldType = field.Type as NamedTypeSymbol; + Assert.NotNull(fieldType); Debug.Assert(fieldType is { }); + Assert.True(fieldType.IsDelegateType()); + Assert.Equal("System", fieldType.ContainingNamespace.Name); + Assert.Equal("Func", fieldType.Name); + Assert.Equal(1, fieldType.Arity); + Assert.Equal(module.GlobalNamespace.GetTypeMember("C").TypeParameters[0], fieldType.TypeArguments()[0]); + }; + CompileAndVerify(source, symbolValidator: containerValidator); + } + + [Fact] + public void SameTypeAndSymbolResultsSameField_TypeScoped2() + { + var source = @" +using System; +class C +{ + delegate T MyFunc(); + void Test0() { var t = (MyFunc)Target; } + void Test1() { MyFunc t = Target; } + static T Target() { return default(T); } +} +"; + Action containerValidator = module => + { + var container = module.GlobalNamespace.GetMember("C.<>O"); + Assert.NotNull(container); Debug.Assert(container is { }); + Assert.Equal(0, container.Arity); + + var members = container.GetMembers(); + Assert.Equal(1, members.Length); + + var field = members[0] as FieldSymbol; + Assert.NotNull(field); Debug.Assert(field is { }); + Assert.Equal("<0>__Target", field.Name); + + var fieldType = field.Type as NamedTypeSymbol; + Assert.Equal(module.GlobalNamespace.GetMember("C.MyFunc"), fieldType); + }; + CompileAndVerify(source, symbolValidator: containerValidator); + } + + [Fact] + public void SameTypeAndSymbolResultsSameField_MethodScoped0() + { + var source = @" +using System; +class C +{ + void Test() + { + var t0 = (Action)D.Target; + Action t1 = D.Target; + } +} +class D +{ + public static void Target() { } +} +"; + Action containerValidator = module => + { + var container = module.GlobalNamespace.GetMember("C.O__0"); + Assert.NotNull(container); Debug.Assert(container is { }); + Assert.True(container.IsGenericType); + + var members = container.GetMembers(); + Assert.Equal(1, members.Length); + + var field = members[0] as FieldSymbol; + Assert.NotNull(field); Debug.Assert(field is { }); + Assert.Equal("<0>__Target", field.Name); + + var fieldType = field.Type as NamedTypeSymbol; + Assert.NotNull(fieldType); Debug.Assert(fieldType is { }); + Assert.True(fieldType.IsDelegateType()); + Assert.Equal("System", fieldType.ContainingNamespace.Name); + Assert.Equal("Action", fieldType.Name); + Assert.Equal(0, fieldType.Arity); + }; + CompileAndVerify(source, symbolValidator: containerValidator); + } + + [Fact] + public void SameTypeAndSymbolResultsSameField_MethodScoped1() + { + var source = @" +using System; +class C +{ + void Test() + { + var t0 = (Func)D.Target; + Func t1 = D.Target; + } +} +class D +{ + public static B Target(H h) => default(B); +} +"; + Action containerValidator = module => + { + var testClass = module.GlobalNamespace.GetTypeMember("C"); + var container = testClass.GetTypeMember("O__0"); + Assert.NotNull(container); Debug.Assert(container is { }); + Assert.Equal(1, container.Arity); + + var members = container.GetMembers(); + Assert.Equal(1, members.Length); + + var field = members[0] as FieldSymbol; + Assert.NotNull(field); Debug.Assert(field is { }); + Assert.Equal("<0>__Target", field.Name); + + var fieldType = field.Type as NamedTypeSymbol; + Assert.NotNull(fieldType); Debug.Assert(fieldType is { }); + Assert.True(fieldType.IsDelegateType()); + Assert.Equal("System", fieldType.ContainingNamespace.Name); + Assert.Equal("Func", fieldType.Name); + Assert.Equal(2, fieldType.Arity); + Assert.Equal(testClass.TypeParameters[0], fieldType.TypeArguments()[0]); + Assert.Equal(container.TypeParameters[0], fieldType.TypeArguments()[1]); + }; + CompileAndVerify(source, symbolValidator: containerValidator); + } + + [Fact] + public void SameTypeAndSymbolResultsSameField_MethodScoped2() + { + var source = @" +using System; +class C +{ + delegate O MyFunc(int num); + class O { } + void Test() where V : O + { + var t0 = (MyFunc)D.Target; + MyFunc t1 = D.Target; + } +} +static class D +{ + public static B Target(this int num) => default(B); +} +"; + Action containerValidator = module => + { + var container = module.GlobalNamespace.GetMember("C.O__2"); + Assert.NotNull(container); Debug.Assert(container is { }); + Assert.Equal(1, container.Arity); + + var members = container.GetMembers(); + Assert.Equal(1, members.Length); + + var field = members[0] as FieldSymbol; + Assert.NotNull(field); Debug.Assert(field is { }); + Assert.Equal("<0>__Target", field.Name); + + var fieldType = field.Type as NamedTypeSymbol; + Assert.NotNull(fieldType); Debug.Assert(fieldType is { }); + Assert.True(fieldType.IsDelegateType()); + Assert.Equal(module.GlobalNamespace.GetMember("C.MyFunc"), fieldType); + }; + CompileAndVerify(source, symbolValidator: containerValidator); + } + + [Fact] + public void ContainersCanBeShared_TypeScoped0() + { + var source = @" +using System; +class A +{ + class B + { + void Test0() + { + Action t0 = Target0; + Action t1 = Target1; + } + + void Test1() + { + Action t1 = Target1; + Action t2 = D.Target2; + } + + void Test2() + { + Action t2 = D.Target2; + Action t3 = D.Target3; + } + + void Test3() + { + Action t3 = D.Target3; + Action t4 = D.E.Target4; + } + + void Test4() + { + Action t4 = D.E.Target4; + Action t5 = E.Target5; + } + + void Test5() + { + Action t5t = E.Target5; + Action t5v = E.Target5; + } + + static void Target0(T t) { } + } + + static void Target1(T t) { } +} +class D +{ + public static void Target2(K k) { } + + public static void Target3(K k, M m) { } + + public class E

+ { + public static void Target4(K k, P p) { } + } +} +static class E +{ + public static void Target5(this N n) { } +} +"; + Action containerValidator = module => + { + var A = module.GlobalNamespace.GetTypeMember("A"); + var B = A.GetTypeMember("B"); + var T = A.TypeParameters[0]; + var V = B.TypeParameters[0]; + + var container = B.GetTypeMember("<>O"); + Assert.NotNull(container); Debug.Assert(container is { }); + Assert.Equal(0, container.Arity); + + var members = container.GetMembers(); + Assert.Equal(7, members.Length); + + var field0 = members[0] as FieldSymbol; + Assert.NotNull(field0); Debug.Assert(field0 is { }); + Assert.Equal("<0>__Target0", field0.Name); + + var fieldType0 = field0.Type as NamedTypeSymbol; + Assert.NotNull(fieldType0); Debug.Assert(fieldType0 is { }); + Assert.True(fieldType0.IsDelegateType()); + Assert.Equal("System", fieldType0.ContainingNamespace.Name); + Assert.Equal("Action", fieldType0.Name); + Assert.Equal(1, fieldType0.Arity); + Assert.Equal(T, fieldType0.TypeArguments()[0]); + + var field1 = members[1] as FieldSymbol; + Assert.NotNull(field1); Debug.Assert(field1 is { }); + Assert.Equal("<1>__Target1", field1.Name); + + var fieldType1 = field1.Type as NamedTypeSymbol; + Assert.NotNull(fieldType1); Debug.Assert(fieldType1 is { }); + Assert.True(fieldType1.IsDelegateType()); + Assert.Equal("System", fieldType1.ContainingNamespace.Name); + Assert.Equal("Action", fieldType1.Name); + Assert.Equal(1, fieldType1.Arity); + Assert.Equal(T, fieldType1.TypeArguments()[0]); + + var field2 = members[2] as FieldSymbol; + Assert.NotNull(field2); Debug.Assert(field2 is { }); + Assert.Equal("<2>__Target2", field2.Name); + + var fieldType2 = field2.Type as NamedTypeSymbol; + Assert.NotNull(fieldType2); Debug.Assert(fieldType2 is { }); + Assert.True(fieldType2.IsDelegateType()); + Assert.Equal("System", fieldType2.ContainingNamespace.Name); + Assert.Equal("Action", fieldType2.Name); + Assert.Equal(1, fieldType2.Arity); + Assert.Equal(T, fieldType2.TypeArguments()[0]); + + var field3 = members[3] as FieldSymbol; + Assert.NotNull(field3); Debug.Assert(field3 is { }); + Assert.Equal("<3>__Target3", field3.Name); + + var fieldType3 = field3.Type as NamedTypeSymbol; + Assert.NotNull(fieldType3); Debug.Assert(fieldType3 is { }); + Assert.True(fieldType3.IsDelegateType()); + Assert.Equal("System", fieldType3.ContainingNamespace.Name); + Assert.Equal("Action", fieldType3.Name); + Assert.Equal(2, fieldType3.Arity); + Assert.Equal(T, fieldType3.TypeArguments()[0]); + Assert.Equal(V, fieldType3.TypeArguments()[1]); + + var field4 = members[4] as FieldSymbol; + Assert.NotNull(field4); Debug.Assert(field4 is { }); + Assert.Equal("<4>__Target4", field4.Name); + + var fieldType4 = field4.Type as NamedTypeSymbol; + Assert.NotNull(fieldType4); Debug.Assert(fieldType4 is { }); + Assert.True(fieldType4.IsDelegateType()); + Assert.Equal("System", fieldType4.ContainingNamespace.Name); + Assert.Equal("Action", fieldType4.Name); + Assert.Equal(2, fieldType4.Arity); + Assert.Equal(T, fieldType4.TypeArguments()[0]); + Assert.Equal(V, fieldType4.TypeArguments()[1]); + + var field5 = members[5] as FieldSymbol; + Assert.NotNull(field5); Debug.Assert(field5 is { }); + Assert.Equal("<5>__Target5", field5.Name); + + var fieldType5 = field5.Type as NamedTypeSymbol; + Assert.NotNull(fieldType5); Debug.Assert(fieldType5 is { }); + Assert.True(fieldType5.IsDelegateType()); + Assert.Equal("System", fieldType5.ContainingNamespace.Name); + Assert.Equal("Action", fieldType5.Name); + Assert.Equal(1, fieldType5.Arity); + Assert.Equal(T, fieldType5.TypeArguments()[0]); + + var field6 = members[6] as FieldSymbol; + Assert.NotNull(field6); Debug.Assert(field6 is { }); + Assert.Equal("<6>__Target5", field6.Name); + + var fieldType6 = field6.Type as NamedTypeSymbol; + Assert.NotNull(fieldType6); Debug.Assert(fieldType6 is { }); + Assert.True(fieldType6.IsDelegateType()); + Assert.Equal("System", fieldType6.ContainingNamespace.Name); + Assert.Equal("Action", fieldType6.Name); + Assert.Equal(1, fieldType6.Arity); + Assert.Equal(V, fieldType6.TypeArguments()[0]); + }; + CompileAndVerify(source, symbolValidator: containerValidator); + } + + [Fact] + public void ContainersCanBeShared_MethodScoped0() + { + var source = @" +using System; +class C +{ + void Test() + { + Action a = Target0; + Action b = Target1; + Action c = D.Target2; + Action d = E.Target3; + } + + static void Target0() { } + static void Target1(T t) { } + + class D + { + public static void Target2() { } + } +} +static class E +{ + public static void Target3(this C c) { } +} +"; + Action containerValidator = module => + { + var testClass = module.GlobalNamespace.GetTypeMember("C"); + var container = testClass.GetTypeMember("O__0"); + Assert.NotNull(container); Debug.Assert(container is { }); + + Assert.Equal(1, container.Arity); + var T = container.TypeParameters[0]; + + var members = container.GetMembers(); + Assert.Equal(4, members.Length); + + + var field0 = members[0] as FieldSymbol; + Assert.NotNull(field0); Debug.Assert(field0 is { }); + Assert.Equal("<0>__Target0", field0.Name); + + var fieldType0 = field0.Type as NamedTypeSymbol; + Assert.NotNull(fieldType0); Debug.Assert(fieldType0 is { }); + Assert.True(fieldType0.IsDelegateType()); + Assert.Equal("System", fieldType0.ContainingNamespace.Name); + Assert.Equal("Action", fieldType0.Name); + Assert.Equal(0, fieldType0.Arity); + + var field1 = members[1] as FieldSymbol; + Assert.NotNull(field1); Debug.Assert(field1 is { }); + Assert.Equal("<1>__Target1", field1.Name); + + var fieldType1 = field1.Type as NamedTypeSymbol; + Assert.NotNull(fieldType1); Debug.Assert(fieldType1 is { }); + Assert.True(fieldType1.IsDelegateType()); + Assert.Equal("System", fieldType1.ContainingNamespace.Name); + Assert.Equal("Action", fieldType1.Name); + Assert.Equal(1, fieldType1.Arity); + Assert.Equal(T, fieldType1.TypeArguments()[0]); + + var field2 = members[2] as FieldSymbol; + Assert.NotNull(field2); Debug.Assert(field2 is { }); + Assert.Equal("<2>__Target2", field2.Name); + + var fieldType2 = field2.Type as NamedTypeSymbol; + Assert.NotNull(fieldType2); Debug.Assert(fieldType2 is { }); + Assert.True(fieldType2.IsDelegateType()); + Assert.Equal("System", fieldType2.ContainingNamespace.Name); + Assert.Equal("Action", fieldType2.Name); + Assert.Equal(0, fieldType2.Arity); + + var field3 = members[3] as FieldSymbol; + Assert.NotNull(field3); Debug.Assert(field3 is { }); + Assert.Equal("<3>__Target3", field3.Name); + + var fieldType3 = field3.Type as NamedTypeSymbol; + Assert.NotNull(fieldType3); Debug.Assert(fieldType3 is { }); + Assert.True(fieldType3.IsDelegateType()); + Assert.Equal("System", fieldType3.ContainingNamespace.Name); + Assert.Equal("Action", fieldType3.Name); + Assert.Equal(1, fieldType3.Arity); + Assert.Equal(testClass, fieldType3.TypeArguments()[0]); + }; + CompileAndVerify(source, symbolValidator: containerValidator); + } + + [Fact] + public void ContainersCanBeShared_MethodScoped1() + { + var source = @" +using System; +class C +{ + public static void Target0() { } + public static void Target1(T t) { } + + public class D + { + public static void Target2() { } + } +} +static class E +{ + static void Test() + { + Action a = C.Target0; + Action b = C.Target1; + Action c = C.D.Target2; + Action d = Target3; + } + + public static void Target3(this C c) { } +} +"; + Action containerValidator = module => + { + var testClass = module.GlobalNamespace.GetTypeMember("E"); + var container = testClass.GetTypeMember("O__0"); + Assert.NotNull(container); Debug.Assert(container is { }); + + Assert.Equal(1, container.Arity); + var T = container.TypeParameters[0]; + var C = module.GlobalNamespace.GetTypeMember("C"); + + var members = container.GetMembers(); + Assert.Equal(4, members.Length); + + + var field0 = members[0] as FieldSymbol; + Assert.NotNull(field0); Debug.Assert(field0 is { }); + Assert.Equal("<0>__Target0", field0.Name); + + var fieldType0 = field0.Type as NamedTypeSymbol; + Assert.NotNull(fieldType0); Debug.Assert(fieldType0 is { }); + Assert.True(fieldType0.IsDelegateType()); + Assert.Equal("System", fieldType0.ContainingNamespace.Name); + Assert.Equal("Action", fieldType0.Name); + Assert.Equal(0, fieldType0.Arity); + + var field1 = members[1] as FieldSymbol; + Assert.NotNull(field1); Debug.Assert(field1 is { }); + Assert.Equal("<1>__Target1", field1.Name); + + var fieldType1 = field1.Type as NamedTypeSymbol; + Assert.NotNull(fieldType1); Debug.Assert(fieldType1 is { }); + Assert.True(fieldType1.IsDelegateType()); + Assert.Equal("System", fieldType1.ContainingNamespace.Name); + Assert.Equal("Action", fieldType1.Name); + Assert.Equal(1, fieldType1.Arity); + Assert.Equal(T, fieldType1.TypeArguments()[0]); + + var field2 = members[2] as FieldSymbol; + Assert.NotNull(field2); Debug.Assert(field2 is { }); + Assert.Equal("<2>__Target2", field2.Name); + + var fieldType2 = field2.Type as NamedTypeSymbol; + Assert.NotNull(fieldType2); Debug.Assert(fieldType2 is { }); + Assert.True(fieldType2.IsDelegateType()); + Assert.Equal("System", fieldType2.ContainingNamespace.Name); + Assert.Equal("Action", fieldType2.Name); + Assert.Equal(0, fieldType2.Arity); + + var field3 = members[3] as FieldSymbol; + Assert.NotNull(field3); Debug.Assert(field3 is { }); + Assert.Equal("<3>__Target3", field3.Name); + + var fieldType3 = field3.Type as NamedTypeSymbol; + Assert.NotNull(fieldType3); Debug.Assert(fieldType3 is { }); + Assert.True(fieldType3.IsDelegateType()); + Assert.Equal("System", fieldType3.ContainingNamespace.Name); + Assert.Equal("Action", fieldType3.Name); + Assert.Equal(1, fieldType3.Arity); + Assert.Equal(C, fieldType3.TypeArguments()[0]); + }; + + CompileAndVerify(source, symbolValidator: containerValidator); + } + + [Fact] + public void EventHandlers_TypeScoped_CouldBeModuleScoped0() + { + var source = @" +using System; +using System.Reflection; +class C +{ + void Test() + { + AppDomain.CurrentDomain.AssemblyResolve += Target; + } + + static Assembly Target(object sender, ResolveEventArgs e) => null; +} +"; + CompileAndVerify(source).VerifyIL("C.Test", @" +{ + // Code size 38 (0x26) + .maxstack 3 + IL_0000: call ""System.AppDomain System.AppDomain.CurrentDomain.get"" + IL_0005: ldsfld ""System.ResolveEventHandler C.<>O.<0>__Target"" + IL_000a: dup + IL_000b: brtrue.s IL_0020 + IL_000d: pop + IL_000e: ldnull + IL_000f: ldftn ""System.Reflection.Assembly C.Target(object, System.ResolveEventArgs)"" + IL_0015: newobj ""System.ResolveEventHandler..ctor(object, System.IntPtr)"" + IL_001a: dup + IL_001b: stsfld ""System.ResolveEventHandler C.<>O.<0>__Target"" + IL_0020: callvirt ""void System.AppDomain.AssemblyResolve.add"" + IL_0025: ret +} +"); + } + + [Fact] + public void EventHandlers_TypeScoped_CouldBeModuleScoped1() + { + var source = @" +using System; +class MyEventArgs : EventArgs { } +class C where TEventArgs : EventArgs +{ + event EventHandler SomethingHappened; + + void Test() + { + var c = new C(); + c.SomethingHappened += D.Target; + } + +} +class D +{ + public static void Target(object sender, MyEventArgs e) { } +} +"; + CompileAndVerify(source).VerifyIL("C.Test", @" +{ + // Code size 38 (0x26) + .maxstack 3 + IL_0000: newobj ""C..ctor()"" + IL_0005: ldsfld ""System.EventHandler C.<>O.<0>__Target"" + IL_000a: dup + IL_000b: brtrue.s IL_0020 + IL_000d: pop + IL_000e: ldnull + IL_000f: ldftn ""void D.Target(object, MyEventArgs)"" + IL_0015: newobj ""System.EventHandler..ctor(object, System.IntPtr)"" + IL_001a: dup + IL_001b: stsfld ""System.EventHandler C.<>O.<0>__Target"" + IL_0020: callvirt ""void C.SomethingHappened.add"" + IL_0025: ret +} +"); + } + + [Fact] + public void EventHandlers_TypeScoped0() + { + var source = @" +using System; +class C where TEventArgs : EventArgs +{ + event EventHandler SomethingHappened; + + void Test() + { + this.SomethingHappened += Target; + } + + static void Target(object sender, TEventArgs e) { } +} +"; + CompileAndVerify(source).VerifyIL("C.Test", @" +{ + // Code size 34 (0x22) + .maxstack 3 + IL_0000: ldarg.0 + IL_0001: ldsfld ""System.EventHandler C.<>O.<0>__Target"" + IL_0006: dup + IL_0007: brtrue.s IL_001c + IL_0009: pop + IL_000a: ldnull + IL_000b: ldftn ""void C.Target(object, TEventArgs)"" + IL_0011: newobj ""System.EventHandler..ctor(object, System.IntPtr)"" + IL_0016: dup + IL_0017: stsfld ""System.EventHandler C.<>O.<0>__Target"" + IL_001c: call ""void C.SomethingHappened.add"" + IL_0021: ret +} +"); + } + + [Fact] + public void EventHandlers_MethodScoped0() + { + var source = @" +using System; +class C +{ + void Test() where TEventArgs : EventArgs + { + var d = new D(); + d.SomethingHappened += Target; + } + + static void Target(object sender, TEventArgs e) where TEventArgs : EventArgs { } +} +class D where TEventArgs : EventArgs +{ + public event EventHandler SomethingHappened; +} +"; + CompileAndVerify(source).VerifyIL("C.Test", @" +{ + // Code size 38 (0x26) + .maxstack 3 + IL_0000: newobj ""D..ctor()"" + IL_0005: ldsfld ""System.EventHandler C.O__0.<0>__Target"" + IL_000a: dup + IL_000b: brtrue.s IL_0020 + IL_000d: pop + IL_000e: ldnull + IL_000f: ldftn ""void C.Target(object, TEventArgs)"" + IL_0015: newobj ""System.EventHandler..ctor(object, System.IntPtr)"" + IL_001a: dup + IL_001b: stsfld ""System.EventHandler C.O__0.<0>__Target"" + IL_0020: callvirt ""void D.SomethingHappened.add"" + IL_0025: ret +} +"); + } + + [Fact] + public void AnonymousTypes_TypeScoped_CouldBeModuleScoped0() + { + var source = @" +using System; +class C +{ + static void Main(string[] args) => Invoke(new { x = 0 }, Target); + + static void Invoke(T t, Action f) => f(t); + + static void Target(T t) => Console.WriteLine(t); +} +"; + CompileAndVerify(source, expectedOutput: "{ x = 0 }").VerifyIL("C.Main", @" +{ + // Code size 39 (0x27) + .maxstack 3 + IL_0000: ldc.i4.0 + IL_0001: newobj ""<>f__AnonymousType0..ctor(int)"" + IL_0006: ldsfld ""System.Action<> C.<>O.<0>__Target"" + IL_000b: dup + IL_000c: brtrue.s IL_0021 + IL_000e: pop + IL_000f: ldnull + IL_0010: ldftn ""void C.Target<>()"" + IL_0016: newobj ""System.Action<>..ctor(object, System.IntPtr)"" + IL_001b: dup + IL_001c: stsfld ""System.Action<> C.<>O.<0>__Target"" + IL_0021: call ""void C.Invoke<>(, System.Action<>)"" + IL_0026: ret +} +"); + } + + [Fact] + public void AnonymousTypes_TypeScoped0() + { + var source = @" +using System; +class C +{ + static void Main(string[] args) => (new D()).Test(0); +} +class D +{ + public void Test(G g) => Invoke(new { x = g }, Target); + + static void Invoke(T t, Action f) => f(t); + + static void Target(T t) => Console.WriteLine(t); +} +"; + CompileAndVerify(source, expectedOutput: "{ x = 0 }").VerifyIL("D.Test", @" +{ + // Code size 39 (0x27) + .maxstack 3 + IL_0000: ldarg.1 + IL_0001: newobj ""<>f__AnonymousType0..ctor(G)"" + IL_0006: ldsfld ""System.Action<> D.<>O.<0>__Target"" + IL_000b: dup + IL_000c: brtrue.s IL_0021 + IL_000e: pop + IL_000f: ldnull + IL_0010: ldftn ""void D.Target<>()"" + IL_0016: newobj ""System.Action<>..ctor(object, System.IntPtr)"" + IL_001b: dup + IL_001c: stsfld ""System.Action<> D.<>O.<0>__Target"" + IL_0021: call ""void D.Invoke<>(, System.Action<>)"" + IL_0026: ret +} +"); + } + + [Fact] + public void AnonymousTypes_TypeScoped1() + { + var source = @" +using System; +class C +{ + static void Main(string[] args) => (new D()).Test(0); +} +class D +{ + public void Test(G g) => Invoke(new { x = g }, E.Target); + + static void Invoke(T t, Action f) => f(t); +} +class E +{ + public static void Target(T t) => Console.WriteLine(t); +} +"; + CompileAndVerify(source, expectedOutput: "{ x = 0 }").VerifyIL("D.Test", @" +{ + // Code size 39 (0x27) + .maxstack 3 + IL_0000: ldarg.1 + IL_0001: newobj ""<>f__AnonymousType0..ctor(G)"" + IL_0006: ldsfld ""System.Action<> D.<>O.<0>__Target"" + IL_000b: dup + IL_000c: brtrue.s IL_0021 + IL_000e: pop + IL_000f: ldnull + IL_0010: ldftn ""void E.Target<>()"" + IL_0016: newobj ""System.Action<>..ctor(object, System.IntPtr)"" + IL_001b: dup + IL_001c: stsfld ""System.Action<> D.<>O.<0>__Target"" + IL_0021: call ""void D.Invoke<>(, System.Action<>)"" + IL_0026: ret +} +"); + } + + [Fact] + public void AnonymousTypes_TypeScoped2() + { + var source = @" +using System; +class C +{ + static void Main(string[] args) => D.Test(); +} +class D +{ + delegate void MyAction(T t); + + public static void Test() => Invoke(new { x = 0 }, Target); + + static void Invoke(T t, MyAction f) => f(t); + + static void Target(T t) => Console.WriteLine(t); +} +"; + CompileAndVerify(source, expectedOutput: "{ x = 0 }").VerifyIL("D.Test", @" +{ + // Code size 39 (0x27) + .maxstack 3 + IL_0000: ldc.i4.0 + IL_0001: newobj ""<>f__AnonymousType0..ctor(int)"" + IL_0006: ldsfld ""D.MyAction<> D.<>O.<0>__Target"" + IL_000b: dup + IL_000c: brtrue.s IL_0021 + IL_000e: pop + IL_000f: ldnull + IL_0010: ldftn ""void D.Target<>()"" + IL_0016: newobj ""D.MyAction<>..ctor(object, System.IntPtr)"" + IL_001b: dup + IL_001c: stsfld ""D.MyAction<> D.<>O.<0>__Target"" + IL_0021: call ""void D.Invoke<>(, D.MyAction<>)"" + IL_0026: ret +} +"); + } + + [Fact] + public void AnonymousTypes_MethodScoped0() + { + var source = @" +using System; +class C +{ + static void Main(string[] args) => D.Test(0); +} +class D +{ + public static void Test(T t) => Invoke(new { x = t }, Target); + + static void Invoke(T t, Action f) => f(t); + + static void Target(T t) => Console.WriteLine(t); +} +"; + CompileAndVerify(source, expectedOutput: "{ x = 0 }").VerifyIL("D.Test", @" +{ + // Code size 39 (0x27) + .maxstack 3 + IL_0000: ldarg.0 + IL_0001: newobj ""<>f__AnonymousType0..ctor(T)"" + IL_0006: ldsfld ""System.Action<> D.O__0.<0>__Target"" + IL_000b: dup + IL_000c: brtrue.s IL_0021 + IL_000e: pop + IL_000f: ldnull + IL_0010: ldftn ""void D.Target<>()"" + IL_0016: newobj ""System.Action<>..ctor(object, System.IntPtr)"" + IL_001b: dup + IL_001c: stsfld ""System.Action<> D.O__0.<0>__Target"" + IL_0021: call ""void D.Invoke<>(, System.Action<>)"" + IL_0026: ret +} +"); + } + + [Fact] + public void AnonymousTypes_MethodScoped1() + { + var source = @" +using System; +class C +{ + static void Main(string[] args) => D.Test(0); +} +class D +{ + public static void Test(T t) => Invoke(t, new { x = 0 }, Target); + + static void Invoke(T t, V v, Action f) => f(t, v); + + static void Target(T t, V v) => Console.WriteLine(v); +} +"; + CompileAndVerify(source, expectedOutput: "{ x = 0 }").VerifyIL("D.Test", @" +{ + // Code size 40 (0x28) + .maxstack 4 + IL_0000: ldarg.0 + IL_0001: ldc.i4.0 + IL_0002: newobj ""<>f__AnonymousType0..ctor(int)"" + IL_0007: ldsfld ""System.Action> D.O__0.<0>__Target"" + IL_000c: dup + IL_000d: brtrue.s IL_0022 + IL_000f: pop + IL_0010: ldnull + IL_0011: ldftn ""void D.Target>(T, )"" + IL_0017: newobj ""System.Action>..ctor(object, System.IntPtr)"" + IL_001c: dup + IL_001d: stsfld ""System.Action> D.O__0.<0>__Target"" + IL_0022: call ""void D.Invoke>(T, , System.Action>)"" + IL_0027: ret +} +"); + } + + [Fact] + public void Pointer_TypeScoped_CouldBeModuleScoped0() + { + var source = @" +using System; +class C +{ + unsafe void Test() + { + Func t = Target; + t(); + } + + unsafe static int*[] Target() => default(int*[]); +} +"; + CompileAndVerify(source, options: TestOptions.UnsafeReleaseDll).VerifyIL("C.Test", @" +{ + // Code size 34 (0x22) + .maxstack 2 + IL_0000: ldsfld ""System.Func C.<>O.<0>__Target"" + IL_0005: dup + IL_0006: brtrue.s IL_001b + IL_0008: pop + IL_0009: ldnull + IL_000a: ldftn ""int*[] C.Target()"" + IL_0010: newobj ""System.Func..ctor(object, System.IntPtr)"" + IL_0015: dup + IL_0016: stsfld ""System.Func C.<>O.<0>__Target"" + IL_001b: callvirt ""int*[] System.Func.Invoke()"" + IL_0020: pop + IL_0021: ret +} +"); + } + + [Fact] + public void Pointer_TypeScoped0() + { + var source = @" +using System; +class C +{ + unsafe void Test() + { + Func t = Target; + t(default(T)); + } + + unsafe static int*[] Target(T t) => default(int*[]); +} +"; + CompileAndVerify(source, options: TestOptions.UnsafeReleaseDll).VerifyIL("C.Test", @" +{ + // Code size 43 (0x2b) + .maxstack 2 + .locals init (T V_0) + IL_0000: ldsfld ""System.Func C.<>O.<0>__Target"" + IL_0005: dup + IL_0006: brtrue.s IL_001b + IL_0008: pop + IL_0009: ldnull + IL_000a: ldftn ""int*[] C.Target(T)"" + IL_0010: newobj ""System.Func..ctor(object, System.IntPtr)"" + IL_0015: dup + IL_0016: stsfld ""System.Func C.<>O.<0>__Target"" + IL_001b: ldloca.s V_0 + IL_001d: initobj ""T"" + IL_0023: ldloc.0 + IL_0024: callvirt ""int*[] System.Func.Invoke(T)"" + IL_0029: pop + IL_002a: ret +} +"); + } + + [Fact] + public void Pointer_MethodScoped0() + { + var source = @" +using System; +class C +{ + unsafe void Test(T t) + { + Func f = Target; + f(t); + } + + unsafe static int*[] Target(G g) => default(int*[]); +} +"; + CompileAndVerify(source, options: TestOptions.UnsafeReleaseDll).VerifyIL("C.Test", @" +{ + // Code size 35 (0x23) + .maxstack 2 + IL_0000: ldsfld ""System.Func C.O__0.<0>__Target"" + IL_0005: dup + IL_0006: brtrue.s IL_001b + IL_0008: pop + IL_0009: ldnull + IL_000a: ldftn ""int*[] C.Target(T)"" + IL_0010: newobj ""System.Func..ctor(object, System.IntPtr)"" + IL_0015: dup + IL_0016: stsfld ""System.Func C.O__0.<0>__Target"" + IL_001b: ldarg.1 + IL_001c: callvirt ""int*[] System.Func.Invoke(T)"" + IL_0021: pop + IL_0022: ret +} +"); + } + + [Fact] + public void Dynamic_TypeScoped_CouldBeModuleScoped0() + { + var source = @" +using System; +class C +{ + void Test() + { + Func t = Target; + t(); + } + + static dynamic Target() => 0; +} +"; + CompileAndVerify(source).VerifyIL("C.Test", @" +{ + // Code size 34 (0x22) + .maxstack 2 + IL_0000: ldsfld ""System.Func C.<>O.<0>__Target"" + IL_0005: dup + IL_0006: brtrue.s IL_001b + IL_0008: pop + IL_0009: ldnull + IL_000a: ldftn ""dynamic C.Target()"" + IL_0010: newobj ""System.Func..ctor(object, System.IntPtr)"" + IL_0015: dup + IL_0016: stsfld ""System.Func C.<>O.<0>__Target"" + IL_001b: callvirt ""dynamic System.Func.Invoke()"" + IL_0020: pop + IL_0021: ret +} +"); + } + + [Fact] + public void Dynamic_TypeScoped0() + { + var source = @" +using System; +class C +{ + void Test() + { + Func t = Target; + t(default(T)); + } + + static dynamic Target(T t) => 0; +} +"; + CompileAndVerify(source).VerifyIL("C.Test", @" +{ + // Code size 43 (0x2b) + .maxstack 2 + .locals init (T V_0) + IL_0000: ldsfld ""System.Func C.<>O.<0>__Target"" + IL_0005: dup + IL_0006: brtrue.s IL_001b + IL_0008: pop + IL_0009: ldnull + IL_000a: ldftn ""dynamic C.Target(T)"" + IL_0010: newobj ""System.Func..ctor(object, System.IntPtr)"" + IL_0015: dup + IL_0016: stsfld ""System.Func C.<>O.<0>__Target"" + IL_001b: ldloca.s V_0 + IL_001d: initobj ""T"" + IL_0023: ldloc.0 + IL_0024: callvirt ""dynamic System.Func.Invoke(T)"" + IL_0029: pop + IL_002a: ret +} +"); + } + + [Fact] + public void Dynamic_MethodScoped0() + { + var source = @" +using System; +class C +{ + void Test(T t) + { + Func f = Target; + f(t); + } + + static dynamic Target(G g) => 0; +} +"; + CompileAndVerify(source).VerifyIL("C.Test", @" +{ + // Code size 35 (0x23) + .maxstack 2 + IL_0000: ldsfld ""System.Func C.O__0.<0>__Target"" + IL_0005: dup + IL_0006: brtrue.s IL_001b + IL_0008: pop + IL_0009: ldnull + IL_000a: ldftn ""dynamic C.Target(T)"" + IL_0010: newobj ""System.Func..ctor(object, System.IntPtr)"" + IL_0015: dup + IL_0016: stsfld ""System.Func C.O__0.<0>__Target"" + IL_001b: ldarg.1 + IL_001c: callvirt ""dynamic System.Func.Invoke(T)"" + IL_0021: pop + IL_0022: ret +} +"); + } + + [Fact] + public void SynthesizedAnonymousDelegate_TypeScoped0() + { + var source = @" +using System; +class C +{ + void Test(T t) + { + G(Target); + } + + void G(Delegate d) {} + + static dynamic Target(ref G g) => 0; +} +"; + CompileAndVerify(source).VerifyIL("C.Test", @" +{ + // Code size 34 (0x22) + .maxstack 3 + IL_0000: ldarg.0 + IL_0001: ldsfld "" C.<>O.<0>__Target"" + IL_0006: dup + IL_0007: brtrue.s IL_001c + IL_0009: pop + IL_000a: ldnull + IL_000b: ldftn ""dynamic C.Target(ref int)"" + IL_0011: newobj ""<>F{00000001}..ctor(object, System.IntPtr)"" + IL_0016: dup + IL_0017: stsfld "" C.<>O.<0>__Target"" + IL_001c: call ""void C.G(System.Delegate)"" + IL_0021: ret +} +"); + } + + [Fact] + public void SynthesizedAnonymousDelegate_MethodScoped0() + { + var source = @" +using System; +class C +{ + void Test(T t) + { + G(Target); + } + + void G(Delegate d) {} + + static dynamic Target(ref G g) => 0; +} +"; + CompileAndVerify(source).VerifyIL("C.Test", @" +{ + // Code size 34 (0x22) + .maxstack 3 + IL_0000: ldarg.0 + IL_0001: ldsfld "" C.O__0.<0>__Target"" + IL_0006: dup + IL_0007: brtrue.s IL_001c + IL_0009: pop + IL_000a: ldnull + IL_000b: ldftn ""dynamic C.Target(ref T)"" + IL_0011: newobj ""<>F{00000001}..ctor(object, System.IntPtr)"" + IL_0016: dup + IL_0017: stsfld "" C.O__0.<0>__Target"" + IL_001c: call ""void C.G(System.Delegate)"" + IL_0021: ret +} +"); + } + + [Fact] + public void TopLevel_LocalFunctions_TypeScoped0() + { + var source = @" +using System; +class C +{ + void Test(int t) + { + Func f = Target; + f(t); + + static dynamic Target(G g) => 0; + } +} +"; + CompileAndVerify(source).VerifyIL("C.Test", @" +{ + // Code size 35 (0x23) + .maxstack 2 + IL_0000: ldsfld ""System.Func C.<>O.<0>__Target"" + IL_0005: dup + IL_0006: brtrue.s IL_001b + IL_0008: pop + IL_0009: ldnull + IL_000a: ldftn ""dynamic C.g__Target|0_0(int)"" + IL_0010: newobj ""System.Func..ctor(object, System.IntPtr)"" + IL_0015: dup + IL_0016: stsfld ""System.Func C.<>O.<0>__Target"" + IL_001b: ldarg.1 + IL_001c: callvirt ""dynamic System.Func.Invoke(int)"" + IL_0021: pop + IL_0022: ret +} +"); + } + + [Fact] + public void TopLevel_LocalFunctions_NotStatic() + { + var source = @" +using System; +class C +{ + void Test(int t) + { + Func f = Target; + f(t); + + dynamic Target(G g) => 0; + } +} +"; + CompileAndVerify(source).VerifyIL("C.Test", @" +{ + // Code size 20 (0x14) + .maxstack 2 + IL_0000: ldnull + IL_0001: ldftn ""dynamic C.g__Target|0_0(int)"" + IL_0007: newobj ""System.Func..ctor(object, System.IntPtr)"" + IL_000c: ldarg.1 + IL_000d: callvirt ""dynamic System.Func.Invoke(int)"" + IL_0012: pop + IL_0013: ret +} +"); + } + + [Fact] + public void TopLevel_LocalFunctions_MethodScoped0() + { + var source = @" +using System; +class C +{ + void Test(T t) + { + Func f = Target; + f(t); + + static dynamic Target(G g) => 0; + } +} +"; + CompileAndVerify(source).VerifyIL("C.Test", @" +{ + // Code size 35 (0x23) + .maxstack 2 + IL_0000: ldsfld ""System.Func C.O__0.<0>__Target"" + IL_0005: dup + IL_0006: brtrue.s IL_001b + IL_0008: pop + IL_0009: ldnull + IL_000a: ldftn ""dynamic C.g__Target|0_0(T)"" + IL_0010: newobj ""System.Func..ctor(object, System.IntPtr)"" + IL_0015: dup + IL_0016: stsfld ""System.Func C.O__0.<0>__Target"" + IL_001b: ldarg.1 + IL_001c: callvirt ""dynamic System.Func.Invoke(T)"" + IL_0021: pop + IL_0022: ret +} +"); + } + + [Fact] + public void Local_LocalFunctions_MethodScoped0() + { + var source = @" +using System; +class C +{ + void TopLevel(T t) + { + void Test() + { + Func f = Target; + f(t); + + static dynamic Target(G g) => 0; + } + } +} +"; + CompileAndVerify(source).VerifyIL("C.g__Test|0_0", @" +{ + // Code size 40 (0x28) + .maxstack 2 + IL_0000: ldsfld ""System.Func C.O__0_0.<0>__Target"" + IL_0005: dup + IL_0006: brtrue.s IL_001b + IL_0008: pop + IL_0009: ldnull + IL_000a: ldftn ""dynamic C.g__Target|0_1(T)"" + IL_0010: newobj ""System.Func..ctor(object, System.IntPtr)"" + IL_0015: dup + IL_0016: stsfld ""System.Func C.O__0_0.<0>__Target"" + IL_001b: ldarg.0 + IL_001c: ldfld ""T C.<>c__DisplayClass0_0.t"" + IL_0021: callvirt ""dynamic System.Func.Invoke(T)"" + IL_0026: pop + IL_0027: ret +} +"); + } + + [Fact] + public void Lambda_Local_LocalFunctions_MethodScoped0() + { + var source = @" +using System; +class C +{ + void TopLevel(T t) + { + Action x = () => + { + void Test() + { + Func f = Target; + f(t); + + static dynamic Target(G g) => 0; + } + + Test(); + }; + + x(); + } +} +"; + CompileAndVerify(source).VerifyIL("C.<>c__DisplayClass0_0.g__Test|1", @" +{ + // Code size 40 (0x28) + .maxstack 2 + IL_0000: ldsfld ""System.Func C.O__0_0.<0>__Target"" + IL_0005: dup + IL_0006: brtrue.s IL_001b + IL_0008: pop + IL_0009: ldnull + IL_000a: ldftn ""dynamic C.g__Target|0_2(T)"" + IL_0010: newobj ""System.Func..ctor(object, System.IntPtr)"" + IL_0015: dup + IL_0016: stsfld ""System.Func C.O__0_0.<0>__Target"" + IL_001b: ldarg.0 + IL_001c: ldfld ""T C.<>c__DisplayClass0_0.t"" + IL_0021: callvirt ""dynamic System.Func.Invoke(T)"" + IL_0026: pop + IL_0027: ret +} +"); + } + + [Fact] + public void Lambda_Local_LocalFunctions_MethodScoped1() + { + var source = @" +using System; +class C +{ + void TopLevel(T t) + { + Action y = () => + { + void Test() { /* Test method ordinals in generated names */ } + Test(); + }; + Action x = () => + { + void Test() + { + Func f = Target; + f(t); + + static dynamic Target(G g) => 0; + } + + Test(); + }; + + x(); + y(); + } +} +"; + CompileAndVerify(source).VerifyIL("C.<>c__DisplayClass0_0.g__Test|3", @" +{ + // Code size 40 (0x28) + .maxstack 2 + IL_0000: ldsfld ""System.Func C.O__0_1.<0>__Target"" + IL_0005: dup + IL_0006: brtrue.s IL_001b + IL_0008: pop + IL_0009: ldnull + IL_000a: ldftn ""dynamic C.g__Target|0_4(T)"" + IL_0010: newobj ""System.Func..ctor(object, System.IntPtr)"" + IL_0015: dup + IL_0016: stsfld ""System.Func C.O__0_1.<0>__Target"" + IL_001b: ldarg.0 + IL_001c: ldfld ""T C.<>c__DisplayClass0_0.t"" + IL_0021: callvirt ""dynamic System.Func.Invoke(T)"" + IL_0026: pop + IL_0027: ret +} +"); + } + + [Fact] + public void TestConditionalOperatorMethodGroup() + { + var source = @" +class C +{ + static void Main() + { + bool b = true; + System.Func f = null; + System.Console.WriteLine(f); + System.Func g1 = b ? f : M; + System.Console.WriteLine(g1); + System.Func g2 = b ? M : f; + System.Console.WriteLine(g2); + } + + static int M() + { + return 0; + } +}"; + var comp = CompileAndVerify(source); + comp.VerifyDiagnostics(); + comp.VerifyIL("C.Main", @" +{ + // Code size 85 (0x55) + .maxstack 3 + .locals init (System.Func V_0) //f + IL_0000: ldc.i4.1 + IL_0001: ldnull + IL_0002: stloc.0 + IL_0003: ldloc.0 + IL_0004: call ""void System.Console.WriteLine(object)"" + IL_0009: dup + IL_000a: brtrue.s IL_0029 + IL_000c: ldsfld ""System.Func C.<>O.<0>__M"" + IL_0011: dup + IL_0012: brtrue.s IL_002a + IL_0014: pop + IL_0015: ldnull + IL_0016: ldftn ""int C.M()"" + IL_001c: newobj ""System.Func..ctor(object, System.IntPtr)"" + IL_0021: dup + IL_0022: stsfld ""System.Func C.<>O.<0>__M"" + IL_0027: br.s IL_002a + IL_0029: ldloc.0 + IL_002a: call ""void System.Console.WriteLine(object)"" + IL_002f: brtrue.s IL_0034 + IL_0031: ldloc.0 + IL_0032: br.s IL_004f + IL_0034: ldsfld ""System.Func C.<>O.<0>__M"" + IL_0039: dup + IL_003a: brtrue.s IL_004f + IL_003c: pop + IL_003d: ldnull + IL_003e: ldftn ""int C.M()"" + IL_0044: newobj ""System.Func..ctor(object, System.IntPtr)"" + IL_0049: dup + IL_004a: stsfld ""System.Func C.<>O.<0>__M"" + IL_004f: call ""void System.Console.WriteLine(object)"" + IL_0054: ret +} +"); + } + +} diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/DelegateTypeTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/DelegateTypeTests.cs index 3db35f37d6dfc..154d101277e69 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/DelegateTypeTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/DelegateTypeTests.cs @@ -6967,46 +6967,52 @@ static void Main() System.Action"); verifier.VerifyIL("Program.Main", @"{ - // Code size 100 (0x64) + // Code size 115 (0x73) .maxstack 2 .locals init (System.Action V_0, //d1 System.Action V_1, //d2 System.Action V_2) //d3 IL_0000: nop - IL_0001: ldnull - IL_0002: ldftn ""void Program.Main()"" - IL_0008: newobj ""System.Action..ctor(object, System.IntPtr)"" - IL_000d: stloc.0 - IL_000e: ldloc.0 - IL_000f: call ""void Program.Report(System.Delegate)"" - IL_0014: nop - IL_0015: ldsfld ""System.Action Program.<>c.<>9__0_0"" - IL_001a: dup - IL_001b: brtrue.s IL_0034 - IL_001d: pop - IL_001e: ldsfld ""Program.<>c Program.<>c.<>9"" - IL_0023: ldftn ""void Program.<>c.

b__0_0()"" - IL_0029: newobj ""System.Action..ctor(object, System.IntPtr)"" - IL_002e: dup - IL_002f: stsfld ""System.Action Program.<>c.<>9__0_0"" - IL_0034: stloc.1 - IL_0035: ldloc.1 - IL_0036: call ""void Program.Report(System.Delegate)"" - IL_003b: nop - IL_003c: ldsfld ""System.Action Program.<>c.<>9__0_1"" - IL_0041: dup - IL_0042: brtrue.s IL_005b - IL_0044: pop - IL_0045: ldsfld ""Program.<>c Program.<>c.<>9"" - IL_004a: ldftn ""void Program.<>c.
b__0_1()"" - IL_0050: newobj ""System.Action..ctor(object, System.IntPtr)"" - IL_0055: dup - IL_0056: stsfld ""System.Action Program.<>c.<>9__0_1"" - IL_005b: stloc.2 - IL_005c: ldloc.2 - IL_005d: call ""void Program.Report(System.Delegate)"" - IL_0062: nop - IL_0063: ret + IL_0001: ldsfld ""System.Action Program.<>O.<0>__Main"" + IL_0006: dup + IL_0007: brtrue.s IL_001c + IL_0009: pop + IL_000a: ldnull + IL_000b: ldftn ""void Program.Main()"" + IL_0011: newobj ""System.Action..ctor(object, System.IntPtr)"" + IL_0016: dup + IL_0017: stsfld ""System.Action Program.<>O.<0>__Main"" + IL_001c: stloc.0 + IL_001d: ldloc.0 + IL_001e: call ""void Program.Report(System.Delegate)"" + IL_0023: nop + IL_0024: ldsfld ""System.Action Program.<>c.<>9__0_0"" + IL_0029: dup + IL_002a: brtrue.s IL_0043 + IL_002c: pop + IL_002d: ldsfld ""Program.<>c Program.<>c.<>9"" + IL_0032: ldftn ""void Program.<>c.
b__0_0()"" + IL_0038: newobj ""System.Action..ctor(object, System.IntPtr)"" + IL_003d: dup + IL_003e: stsfld ""System.Action Program.<>c.<>9__0_0"" + IL_0043: stloc.1 + IL_0044: ldloc.1 + IL_0045: call ""void Program.Report(System.Delegate)"" + IL_004a: nop + IL_004b: ldsfld ""System.Action Program.<>c.<>9__0_1"" + IL_0050: dup + IL_0051: brtrue.s IL_006a + IL_0053: pop + IL_0054: ldsfld ""Program.<>c Program.<>c.<>9"" + IL_0059: ldftn ""void Program.<>c.
b__0_1()"" + IL_005f: newobj ""System.Action..ctor(object, System.IntPtr)"" + IL_0064: dup + IL_0065: stsfld ""System.Action Program.<>c.<>9__0_1"" + IL_006a: stloc.2 + IL_006b: ldloc.2 + IL_006c: call ""void Program.Report(System.Delegate)"" + IL_0071: nop + IL_0072: ret }"); } @@ -8070,21 +8076,39 @@ static void Main() "); verifier.VerifyIL("Program.Main", @"{ - // Code size 52 (0x34) + // Code size 97 (0x61) .maxstack 2 - IL_0000: ldnull - IL_0001: ldftn ""int Program.F1()"" - IL_0007: newobj ""System.Func..ctor(object, System.IntPtr)"" - IL_000c: call ""void Program.Report(System.Delegate)"" - IL_0011: ldnull - IL_0012: ldftn ""ref int Program.F2()"" - IL_0018: newobj ""<>F{00000001}..ctor(object, System.IntPtr)"" - IL_001d: call ""void Program.Report(System.Delegate)"" - IL_0022: ldnull - IL_0023: ldftn ""ref readonly int Program.F3()"" - IL_0029: newobj ""<>F{00000003}..ctor(object, System.IntPtr)"" - IL_002e: call ""void Program.Report(System.Delegate)"" - IL_0033: ret + IL_0000: ldsfld ""System.Func Program.<>O.<0>__F1"" + IL_0005: dup + IL_0006: brtrue.s IL_001b + IL_0008: pop + IL_0009: ldnull + IL_000a: ldftn ""int Program.F1()"" + IL_0010: newobj ""System.Func..ctor(object, System.IntPtr)"" + IL_0015: dup + IL_0016: stsfld ""System.Func Program.<>O.<0>__F1"" + IL_001b: call ""void Program.Report(System.Delegate)"" + IL_0020: ldsfld "" Program.<>O.<1>__F2"" + IL_0025: dup + IL_0026: brtrue.s IL_003b + IL_0028: pop + IL_0029: ldnull + IL_002a: ldftn ""ref int Program.F2()"" + IL_0030: newobj ""<>F{00000001}..ctor(object, System.IntPtr)"" + IL_0035: dup + IL_0036: stsfld "" Program.<>O.<1>__F2"" + IL_003b: call ""void Program.Report(System.Delegate)"" + IL_0040: ldsfld "" Program.<>O.<2>__F3"" + IL_0045: dup + IL_0046: brtrue.s IL_005b + IL_0048: pop + IL_0049: ldnull + IL_004a: ldftn ""ref readonly int Program.F3()"" + IL_0050: newobj ""<>F{00000003}..ctor(object, System.IntPtr)"" + IL_0055: dup + IL_0056: stsfld "" Program.<>O.<2>__F3"" + IL_005b: call ""void Program.Report(System.Delegate)"" + IL_0060: ret }"); } @@ -8292,25 +8316,49 @@ static void Main() "); verifier.VerifyIL("Program.Main", @"{ - // Code size 69 (0x45) + // Code size 129 (0x81) .maxstack 2 - IL_0000: ldnull - IL_0001: ldftn ""void Program.M1(int)"" - IL_0007: newobj ""System.Action..ctor(object, System.IntPtr)"" - IL_000c: call ""void Program.Report(System.Delegate)"" - IL_0011: ldnull - IL_0012: ldftn ""void Program.M2(out int)"" - IL_0018: newobj ""<>A{00000002}..ctor(object, System.IntPtr)"" - IL_001d: call ""void Program.Report(System.Delegate)"" - IL_0022: ldnull - IL_0023: ldftn ""void Program.M3(ref int)"" - IL_0029: newobj ""<>A{00000001}..ctor(object, System.IntPtr)"" - IL_002e: call ""void Program.Report(System.Delegate)"" - IL_0033: ldnull - IL_0034: ldftn ""void Program.M4(in int)"" - IL_003a: newobj ""<>A{00000003}..ctor(object, System.IntPtr)"" - IL_003f: call ""void Program.Report(System.Delegate)"" - IL_0044: ret + IL_0000: ldsfld ""System.Action Program.<>O.<0>__M1"" + IL_0005: dup + IL_0006: brtrue.s IL_001b + IL_0008: pop + IL_0009: ldnull + IL_000a: ldftn ""void Program.M1(int)"" + IL_0010: newobj ""System.Action..ctor(object, System.IntPtr)"" + IL_0015: dup + IL_0016: stsfld ""System.Action Program.<>O.<0>__M1"" + IL_001b: call ""void Program.Report(System.Delegate)"" + IL_0020: ldsfld "" Program.<>O.<1>__M2"" + IL_0025: dup + IL_0026: brtrue.s IL_003b + IL_0028: pop + IL_0029: ldnull + IL_002a: ldftn ""void Program.M2(out int)"" + IL_0030: newobj ""<>A{00000002}..ctor(object, System.IntPtr)"" + IL_0035: dup + IL_0036: stsfld "" Program.<>O.<1>__M2"" + IL_003b: call ""void Program.Report(System.Delegate)"" + IL_0040: ldsfld "" Program.<>O.<2>__M3"" + IL_0045: dup + IL_0046: brtrue.s IL_005b + IL_0048: pop + IL_0049: ldnull + IL_004a: ldftn ""void Program.M3(ref int)"" + IL_0050: newobj ""<>A{00000001}..ctor(object, System.IntPtr)"" + IL_0055: dup + IL_0056: stsfld "" Program.<>O.<2>__M3"" + IL_005b: call ""void Program.Report(System.Delegate)"" + IL_0060: ldsfld "" Program.<>O.<3>__M4"" + IL_0065: dup + IL_0066: brtrue.s IL_007b + IL_0068: pop + IL_0069: ldnull + IL_006a: ldftn ""void Program.M4(in int)"" + IL_0070: newobj ""<>A{00000003}..ctor(object, System.IntPtr)"" + IL_0075: dup + IL_0076: stsfld "" Program.<>O.<3>__M4"" + IL_007b: call ""void Program.Report(System.Delegate)"" + IL_0080: ret }"); } @@ -8718,7 +8766,7 @@ static void validator(PEAssembly assembly) var reader = assembly.GetMetadataReader(); var actualTypes = reader.GetTypeDefNames().Select(h => reader.GetString(h)).ToArray(); - string[] expectedTypes = new[] { "", "D", "Program", }; + string[] expectedTypes = new[] { "", "D", "Program", "<>O" }; AssertEx.Equal(expectedTypes, actualTypes); } } @@ -8763,7 +8811,7 @@ static void validator(PEAssembly assembly) var reader = assembly.GetMetadataReader(); var actualTypes = reader.GetTypeDefNames().Select(h => reader.GetString(h)).ToArray(); - string[] expectedTypes = new[] { "", "<>A{00000001}`2", "<>A{00000009}`2", "D2", "D4", "Program", "<>c", }; + string[] expectedTypes = new[] { "", "<>A{00000001}`2", "<>A{00000009}`2", "D2", "D4", "Program", "<>O", "<>c", }; AssertEx.Equal(expectedTypes, actualTypes); } } diff --git a/src/Compilers/CSharp/Test/Symbol/Symbols/ExtensionMethodTests.cs b/src/Compilers/CSharp/Test/Symbol/Symbols/ExtensionMethodTests.cs index b55e4e942dc56..1c2b76d14abc2 100644 --- a/src/Compilers/CSharp/Test/Symbol/Symbols/ExtensionMethodTests.cs +++ b/src/Compilers/CSharp/Test/Symbol/Symbols/ExtensionMethodTests.cs @@ -2240,20 +2240,32 @@ static string Combine(string s1, string s2) }"; var code = @"{ - // Code size 42 (0x2a) + // Code size 72 (0x48) .maxstack 3 - IL_0000: ldarg.0 - IL_0001: ldc.i4.1 + IL_0000: ldarg.0 + IL_0001: ldc.i4.1 IL_0002: call ""System.Collections.Generic.IEnumerable System.Linq.Enumerable.Skip(System.Collections.Generic.IEnumerable, int)"" - IL_0007: ldnull - IL_0008: ldftn ""bool C.Filter(string)"" - IL_000e: newobj ""System.Func..ctor(object, System.IntPtr)"" - IL_0013: call ""System.Collections.Generic.IEnumerable System.Linq.Enumerable.Where(System.Collections.Generic.IEnumerable, System.Func)"" - IL_0018: ldnull - IL_0019: ldftn ""string C.Combine(string, string)"" - IL_001f: newobj ""System.Func..ctor(object, System.IntPtr)"" - IL_0024: call ""string System.Linq.Enumerable.Aggregate(System.Collections.Generic.IEnumerable, System.Func)"" - IL_0029: ret + IL_0007: ldsfld ""System.Func C.<>O.<0>__Filter"" + IL_000c: dup + IL_000d: brtrue.s IL_0022 + IL_000f: pop + IL_0010: ldnull + IL_0011: ldftn ""bool C.Filter(string)"" + IL_0017: newobj ""System.Func..ctor(object, System.IntPtr)"" + IL_001c: dup + IL_001d: stsfld ""System.Func C.<>O.<0>__Filter"" + IL_0022: call ""System.Collections.Generic.IEnumerable System.Linq.Enumerable.Where(System.Collections.Generic.IEnumerable, System.Func)"" + IL_0027: ldsfld ""System.Func C.<>O.<1>__Combine"" + IL_002c: dup + IL_002d: brtrue.s IL_0042 + IL_002f: pop + IL_0030: ldnull + IL_0031: ldftn ""string C.Combine(string, string)"" + IL_0037: newobj ""System.Func..ctor(object, System.IntPtr)"" + IL_003c: dup + IL_003d: stsfld ""System.Func C.<>O.<1>__Combine"" + IL_0042: call ""string System.Linq.Enumerable.Aggregate(System.Collections.Generic.IEnumerable, System.Func)"" + IL_0047: ret }"; var compilation = CompileAndVerify(source, expectedOutput: "orange, apple"); compilation.VerifyIL("C.F", code); diff --git a/src/Compilers/CSharp/Test/Symbol/Symbols/StaticAbstractMembersInInterfacesTests.cs b/src/Compilers/CSharp/Test/Symbol/Symbols/StaticAbstractMembersInInterfacesTests.cs index 8384e21906a84..1c2c2532ba11d 100644 --- a/src/Compilers/CSharp/Test/Symbol/Symbols/StaticAbstractMembersInInterfacesTests.cs +++ b/src/Compilers/CSharp/Test/Symbol/Symbols/StaticAbstractMembersInInterfacesTests.cs @@ -12152,18 +12152,24 @@ static System.Action M02() where T : U where U : I1 verifier.VerifyIL("Test.M02()", @" { - // Code size 24 (0x18) + // Code size 39 (0x27) .maxstack 2 .locals init (System.Action V_0) IL_0000: nop - IL_0001: ldnull - IL_0002: constrained. ""T"" - IL_0008: ldftn ""void I1.M01()"" - IL_000e: newobj ""System.Action..ctor(object, System.IntPtr)"" - IL_0013: stloc.0 - IL_0014: br.s IL_0016 - IL_0016: ldloc.0 - IL_0017: ret + IL_0001: ldsfld ""System.Action Test.<>O.<0>__M01"" + IL_0006: dup + IL_0007: brtrue.s IL_0022 + IL_0009: pop + IL_000a: ldnull + IL_000b: constrained. ""T"" + IL_0011: ldftn ""void I1.M01()"" + IL_0017: newobj ""System.Action..ctor(object, System.IntPtr)"" + IL_001c: dup + IL_001d: stsfld ""System.Action Test.<>O.<0>__M01"" + IL_0022: stloc.0 + IL_0023: br.s IL_0025 + IL_0025: ldloc.0 + IL_0026: ret } "); @@ -12176,13 +12182,19 @@ .locals init (System.Action V_0) verifier.VerifyIL("Test.M02()", @" { - // Code size 19 (0x13) + // Code size 34 (0x22) .maxstack 2 - IL_0000: ldnull - IL_0001: constrained. ""T"" - IL_0007: ldftn ""void I1.M01()"" - IL_000d: newobj ""System.Action..ctor(object, System.IntPtr)"" - IL_0012: ret + IL_0000: ldsfld ""System.Action Test.<>O.<0>__M01"" + IL_0005: dup + IL_0006: brtrue.s IL_0021 + IL_0008: pop + IL_0009: ldnull + IL_000a: constrained. ""T"" + IL_0010: ldftn ""void I1.M01()"" + IL_0016: newobj ""System.Action..ctor(object, System.IntPtr)"" + IL_001b: dup + IL_001c: stsfld ""System.Action Test.<>O.<0>__M01"" + IL_0021: ret } "); diff --git a/src/Compilers/CSharp/Test/WinRT/CodeGen/WinMdEventTests.cs b/src/Compilers/CSharp/Test/WinRT/CodeGen/WinMdEventTests.cs index 0d9339ad85c34..086d92b9d16ea 100644 --- a/src/Compilers/CSharp/Test/WinRT/CodeGen/WinMdEventTests.cs +++ b/src/Compilers/CSharp/Test/WinRT/CodeGen/WinMdEventTests.cs @@ -281,7 +281,7 @@ static void Action() verifier.VerifyIL("D.InstanceAdd", @" { - // Code size 49 (0x31) + // Code size 64 (0x40) .maxstack 4 .locals init (C V_0) IL_0000: ldarg.0 @@ -293,30 +293,42 @@ .locals init (C V_0) IL_0013: ldloc.0 IL_0014: ldftn ""void C.Instance.remove"" IL_001a: newobj ""System.Action..ctor(object, System.IntPtr)"" - IL_001f: ldnull - IL_0020: ldftn ""void D.Action()"" - IL_0026: newobj ""System.Action..ctor(object, System.IntPtr)"" - IL_002b: call ""void System.Runtime.InteropServices.WindowsRuntime.WindowsRuntimeMarshal.AddEventHandler(System.Func, System.Action, System.Action)"" - IL_0030: ret + IL_001f: ldsfld ""System.Action D.<>O.<0>__Action"" + IL_0024: dup + IL_0025: brtrue.s IL_003a + IL_0027: pop + IL_0028: ldnull + IL_0029: ldftn ""void D.Action()"" + IL_002f: newobj ""System.Action..ctor(object, System.IntPtr)"" + IL_0034: dup + IL_0035: stsfld ""System.Action D.<>O.<0>__Action"" + IL_003a: call ""void System.Runtime.InteropServices.WindowsRuntime.WindowsRuntimeMarshal.AddEventHandler(System.Func, System.Action, System.Action)"" + IL_003f: ret }"); verifier.VerifyIL("D.InstanceRemove", @" { - // Code size 35 (0x23) + // Code size 50 (0x32) .maxstack 3 IL_0000: ldarg.0 IL_0001: ldfld ""C D.c"" IL_0006: ldftn ""void C.Instance.remove"" IL_000c: newobj ""System.Action..ctor(object, System.IntPtr)"" - IL_0011: ldnull - IL_0012: ldftn ""void D.Action()"" - IL_0018: newobj ""System.Action..ctor(object, System.IntPtr)"" - IL_001d: call ""void System.Runtime.InteropServices.WindowsRuntime.WindowsRuntimeMarshal.RemoveEventHandler(System.Action, System.Action)"" - IL_0022: ret + IL_0011: ldsfld ""System.Action D.<>O.<0>__Action"" + IL_0016: dup + IL_0017: brtrue.s IL_002c + IL_0019: pop + IL_001a: ldnull + IL_001b: ldftn ""void D.Action()"" + IL_0021: newobj ""System.Action..ctor(object, System.IntPtr)"" + IL_0026: dup + IL_0027: stsfld ""System.Action D.<>O.<0>__Action"" + IL_002c: call ""void System.Runtime.InteropServices.WindowsRuntime.WindowsRuntimeMarshal.RemoveEventHandler(System.Action, System.Action)"" + IL_0031: ret }"); verifier.VerifyIL("D.StaticAdd", @" { - // Code size 42 (0x2a) + // Code size 57 (0x39) .maxstack 4 IL_0000: ldnull IL_0001: ldftn ""System.Runtime.InteropServices.WindowsRuntime.EventRegistrationToken C.Static.add"" @@ -324,25 +336,37 @@ .maxstack 4 IL_000c: ldnull IL_000d: ldftn ""void C.Static.remove"" IL_0013: newobj ""System.Action..ctor(object, System.IntPtr)"" - IL_0018: ldnull - IL_0019: ldftn ""void D.Action()"" - IL_001f: newobj ""System.Action..ctor(object, System.IntPtr)"" - IL_0024: call ""void System.Runtime.InteropServices.WindowsRuntime.WindowsRuntimeMarshal.AddEventHandler(System.Func, System.Action, System.Action)"" - IL_0029: ret + IL_0018: ldsfld ""System.Action D.<>O.<0>__Action"" + IL_001d: dup + IL_001e: brtrue.s IL_0033 + IL_0020: pop + IL_0021: ldnull + IL_0022: ldftn ""void D.Action()"" + IL_0028: newobj ""System.Action..ctor(object, System.IntPtr)"" + IL_002d: dup + IL_002e: stsfld ""System.Action D.<>O.<0>__Action"" + IL_0033: call ""void System.Runtime.InteropServices.WindowsRuntime.WindowsRuntimeMarshal.AddEventHandler(System.Func, System.Action, System.Action)"" + IL_0038: ret }"); verifier.VerifyIL("D.StaticRemove", @" { - // Code size 30 (0x1e) + // Code size 45 (0x2d) .maxstack 3 IL_0000: ldnull IL_0001: ldftn ""void C.Static.remove"" IL_0007: newobj ""System.Action..ctor(object, System.IntPtr)"" - IL_000c: ldnull - IL_000d: ldftn ""void D.Action()"" - IL_0013: newobj ""System.Action..ctor(object, System.IntPtr)"" - IL_0018: call ""void System.Runtime.InteropServices.WindowsRuntime.WindowsRuntimeMarshal.RemoveEventHandler(System.Action, System.Action)"" - IL_001d: ret + IL_000c: ldsfld ""System.Action D.<>O.<0>__Action"" + IL_0011: dup + IL_0012: brtrue.s IL_0027 + IL_0014: pop + IL_0015: ldnull + IL_0016: ldftn ""void D.Action()"" + IL_001c: newobj ""System.Action..ctor(object, System.IntPtr)"" + IL_0021: dup + IL_0022: stsfld ""System.Action D.<>O.<0>__Action"" + IL_0027: call ""void System.Runtime.InteropServices.WindowsRuntime.WindowsRuntimeMarshal.RemoveEventHandler(System.Action, System.Action)"" + IL_002c: ret }"); } @@ -374,7 +398,7 @@ static void Action() verifier.VerifyIL("C.InstanceAssign", @" { - // Code size 59 (0x3b) + // Code size 74 (0x4a) .maxstack 4 IL_0000: ldarg.0 IL_0001: ldftn ""void C.Instance.remove"" @@ -386,16 +410,22 @@ .maxstack 4 IL_001d: ldarg.0 IL_001e: ldftn ""void C.Instance.remove"" IL_0024: newobj ""System.Action..ctor(object, System.IntPtr)"" - IL_0029: ldnull - IL_002a: ldftn ""void C.Action()"" - IL_0030: newobj ""System.Action..ctor(object, System.IntPtr)"" - IL_0035: call ""void System.Runtime.InteropServices.WindowsRuntime.WindowsRuntimeMarshal.AddEventHandler(System.Func, System.Action, System.Action)"" - IL_003a: ret + IL_0029: ldsfld ""System.Action C.<>O.<0>__Action"" + IL_002e: dup + IL_002f: brtrue.s IL_0044 + IL_0031: pop + IL_0032: ldnull + IL_0033: ldftn ""void C.Action()"" + IL_0039: newobj ""System.Action..ctor(object, System.IntPtr)"" + IL_003e: dup + IL_003f: stsfld ""System.Action C.<>O.<0>__Action"" + IL_0044: call ""void System.Runtime.InteropServices.WindowsRuntime.WindowsRuntimeMarshal.AddEventHandler(System.Func, System.Action, System.Action)"" + IL_0049: ret }"); verifier.VerifyIL("C.StaticAssign", @" { - // Code size 59 (0x3b) + // Code size 74 (0x4a) .maxstack 4 IL_0000: ldnull IL_0001: ldftn ""void C.Static.remove"" @@ -407,11 +437,17 @@ .maxstack 4 IL_001d: ldnull IL_001e: ldftn ""void C.Static.remove"" IL_0024: newobj ""System.Action..ctor(object, System.IntPtr)"" - IL_0029: ldnull - IL_002a: ldftn ""void C.Action()"" - IL_0030: newobj ""System.Action..ctor(object, System.IntPtr)"" - IL_0035: call ""void System.Runtime.InteropServices.WindowsRuntime.WindowsRuntimeMarshal.AddEventHandler(System.Func, System.Action, System.Action)"" - IL_003a: ret + IL_0029: ldsfld ""System.Action C.<>O.<0>__Action"" + IL_002e: dup + IL_002f: brtrue.s IL_0044 + IL_0031: pop + IL_0032: ldnull + IL_0033: ldftn ""void C.Action()"" + IL_0039: newobj ""System.Action..ctor(object, System.IntPtr)"" + IL_003e: dup + IL_003f: stsfld ""System.Action C.<>O.<0>__Action"" + IL_0044: call ""void System.Runtime.InteropServices.WindowsRuntime.WindowsRuntimeMarshal.AddEventHandler(System.Func, System.Action, System.Action)"" + IL_0049: ret }"); } diff --git a/src/Compilers/Core/Portable/Microsoft.CodeAnalysis.csproj b/src/Compilers/Core/Portable/Microsoft.CodeAnalysis.csproj index 8535f767ca531..2e8b0ce4a4915 100644 --- a/src/Compilers/Core/Portable/Microsoft.CodeAnalysis.csproj +++ b/src/Compilers/Core/Portable/Microsoft.CodeAnalysis.csproj @@ -62,6 +62,7 @@ + diff --git a/src/ExpressionEvaluator/CSharp/Test/ExpressionCompiler/ExpressionCompilerTests.cs b/src/ExpressionEvaluator/CSharp/Test/ExpressionCompiler/ExpressionCompilerTests.cs index aac4c63c0c555..4dcffa2d271e2 100644 --- a/src/ExpressionEvaluator/CSharp/Test/ExpressionCompiler/ExpressionCompilerTests.cs +++ b/src/ExpressionEvaluator/CSharp/Test/ExpressionCompiler/ExpressionCompilerTests.cs @@ -1764,14 +1764,20 @@ static void M2(int i) { } testData: testData); testData.GetMethodData("<>x.<>m0").VerifyIL( @"{ - // Code size 14 (0xe) + // Code size 29 (0x1d) .maxstack 2 .locals init (object V_0) //o - IL_0000: ldnull - IL_0001: ldftn ""void C.M1()"" - IL_0007: newobj ""System.Action..ctor(object, System.IntPtr)"" - IL_000c: stloc.0 - IL_000d: ret + IL_0000: ldsfld ""System.Action <>x.<>O.<0>__M1"" + IL_0005: dup + IL_0006: brtrue.s IL_001b + IL_0008: pop + IL_0009: ldnull + IL_000a: ldftn ""void C.M1()"" + IL_0010: newobj ""System.Action..ctor(object, System.IntPtr)"" + IL_0015: dup + IL_0016: stsfld ""System.Action <>x.<>O.<0>__M1"" + IL_001b: stloc.0 + IL_001c: ret }"); }); } @@ -4177,13 +4183,19 @@ static void M() expr: "G(F)"); testData.GetMethodData("<>x.<>m0").VerifyIL( @"{ - // Code size 18 (0x12) + // Code size 33 (0x21) .maxstack 2 - IL_0000: ldnull - IL_0001: ldftn ""void C.F()"" - IL_0007: newobj ""D..ctor(object, System.IntPtr)"" - IL_000c: call ""void C.G(D)"" - IL_0011: ret + IL_0000: ldsfld ""D <>x.<>O.<0>__F"" + IL_0005: dup + IL_0006: brtrue.s IL_001b + IL_0008: pop + IL_0009: ldnull + IL_000a: ldftn ""void C.F()"" + IL_0010: newobj ""D..ctor(object, System.IntPtr)"" + IL_0015: dup + IL_0016: stsfld ""D <>x.<>O.<0>__F"" + IL_001b: call ""void C.G(D)"" + IL_0020: ret }"); } diff --git a/src/Scripting/CSharpTest/ScriptTests.cs b/src/Scripting/CSharpTest/ScriptTests.cs index 26a2149230765..4163994b9adc1 100644 --- a/src/Scripting/CSharpTest/ScriptTests.cs +++ b/src/Scripting/CSharpTest/ScriptTests.cs @@ -471,6 +471,31 @@ public async Task TestBranchingSubscripts() Assert.Equal(10, state3.ReturnValue); } + [Fact] + public async Task StaticDelegate0() + { + var state0 = await CSharpScript.RunAsync("static int Add(int x, int y) => x + y;"); + var state1 = await state0.ContinueWithAsync("System.Func adder = Add;"); + var state2 = await state1.ContinueWithAsync("adder(1, 1)"); + Assert.Equal(2, state2.ReturnValue); + } + + [Fact] + public async Task StaticDelegate1() + { + var state0 = await CSharpScript.RunAsync("class Id { static T Core(T t) => t; public static System.Func Get => Core; }"); + var state1 = await state0.ContinueWithAsync("Id.Get(1)"); + Assert.Equal(1, state1.ReturnValue); + } + + [Fact] + public async Task StaticDelegate2() + { + var state0 = await CSharpScript.RunAsync("class Id { static T Core(T t) => t; public static System.Func Get() => Core; }"); + var state1 = await state0.ContinueWithAsync("Id.Get()(1)"); + Assert.Equal(1, state1.ReturnValue); + } + [Fact] public async Task ReturnIntAsObject() { From 0130613745e02f784df7a8606f4d53f583d26829 Mon Sep 17 00:00:00 2001 From: Paul Chen Date: Tue, 14 Dec 2021 17:30:30 +0800 Subject: [PATCH 02/66] Use static local functions for the delegate caching tests. --- ...odeGenMethodGroupConversionCachingTests.cs | 83 +++++++++---------- 1 file changed, 41 insertions(+), 42 deletions(-) diff --git a/src/Compilers/CSharp/Test/Emit2/CodeGen/CodeGenMethodGroupConversionCachingTests.cs b/src/Compilers/CSharp/Test/Emit2/CodeGen/CodeGenMethodGroupConversionCachingTests.cs index 365c7f9a26801..8a18aef9b1ede 100644 --- a/src/Compilers/CSharp/Test/Emit2/CodeGen/CodeGenMethodGroupConversionCachingTests.cs +++ b/src/Compilers/CSharp/Test/Emit2/CodeGen/CodeGenMethodGroupConversionCachingTests.cs @@ -30,10 +30,9 @@ public static void Main(string[] args) static void Target() { Console.WriteLine(""FAIL""); } static void Invoke(D x, D y) { Console.Write(Object.ReferenceEquals(x, y) ? ""FAIL"" : ""PASS""); } }"; - Action containerValidator = module => - { - Assert.Null(module.GlobalNamespace.GetMember("<>O")); - }; + static void containerValidator(ModuleSymbol module) + => Assert.Null(module.GlobalNamespace.GetMember("<>O")); + CompileAndVerify(source, symbolValidator: containerValidator, expectedOutput: PASS); } @@ -54,10 +53,10 @@ public static void Main(string[] args) void Target() { Console.WriteLine(""FAIL""); } void Invoke(D x, D y) { Console.Write(Object.ReferenceEquals(x, y) ? ""FAIL"" : ""PASS""); } }"; - Action containerValidator = module => + static void containerValidator(ModuleSymbol module) { Assert.Null(module.GlobalNamespace.GetMember("<>O")); - }; + } CompileAndVerify(source, symbolValidator: containerValidator, expectedOutput: PASS); } @@ -82,10 +81,10 @@ static class E public static void Target(this C that) { Console.WriteLine(""FAIL""); } } "; - Action containerValidator = module => + static void containerValidator(ModuleSymbol module) { Assert.Null(module.GlobalNamespace.GetMember("<>O")); - }; + } CompileAndVerify(source, symbolValidator: containerValidator, expectedOutput: PASS); } @@ -110,10 +109,10 @@ static class E public static void Target(this C that) { Console.WriteLine(""FAIL""); } } "; - Action containerValidator = module => + static void containerValidator(ModuleSymbol module) { Assert.Null(module.GlobalNamespace.GetMember("<>O")); - }; + } CompileAndVerify(source, symbolValidator: containerValidator, expectedOutput: PASS); } @@ -133,10 +132,10 @@ public static void Main(string[] args) static int Target(int x) => 0; } "; - Action containerValidator = module => + static void containerValidator(ModuleSymbol module) { Assert.Null(module.GlobalNamespace.GetMember("<>O")); - }; + } CompileAndVerify(source, symbolValidator: containerValidator); } @@ -156,10 +155,10 @@ public static void Main(string[] args) static int Target(int x) => 0; } "; - Action containerValidator = module => + static void containerValidator(ModuleSymbol module) { Assert.Null(module.GlobalNamespace.GetMember("<>O")); - }; + } CompileAndVerify(source, symbolValidator: containerValidator); } @@ -174,10 +173,10 @@ class C static void Target() { } } "; - Action containerValidator = module => + static void containerValidator(ModuleSymbol module) { Assert.Null(module.GlobalNamespace.GetMember("<>O")); - }; + } CompileAndVerify(source, symbolValidator: containerValidator); } @@ -197,10 +196,10 @@ static C() } } "; - Action containerValidator = module => + static void containerValidator(ModuleSymbol module) { Assert.Null(module.GlobalNamespace.GetMember("<>O")); - }; + } CompileAndVerify(source, symbolValidator: containerValidator); } @@ -1739,7 +1738,7 @@ class E public static N Target() { Console.WriteLine(""PASS""); return default(N); } } "; - Action containerValidator = module => + static void containerValidator(ModuleSymbol module) { var testClass = module.GlobalNamespace.GetTypeMember("D"); var container = testClass.GetTypeMember("O__1"); @@ -1751,7 +1750,7 @@ class E var m = typeParameters[0]; Assert.Equal(1, m.ConstraintTypes().Length); Assert.Equal(testClass.TypeParameters[0], m.ConstraintTypes()[0]); - }; + } CompileAndVerify(source, symbolValidator: containerValidator, expectedOutput: PASS).VerifyIL("D.Test", @" { // Code size 34 (0x22) @@ -1799,7 +1798,7 @@ class E public static N Target() { Console.WriteLine(""PASS""); return default(N); } } "; - Action containerValidator = module => + static void containerValidator(ModuleSymbol module) { var globalNs = module.GlobalNamespace; var mainClass = globalNs.GetTypeMember("C"); @@ -1812,7 +1811,7 @@ class E var m = typeParameters[0]; Assert.Equal(1, m.ConstraintTypes().Length); Assert.Equal(mainClass, m.ConstraintTypes()[0]); - }; + } CompileAndVerify(source, symbolValidator: containerValidator, expectedOutput: PASS).VerifyIL("D.Test", @" { // Code size 34 (0x22) @@ -1858,7 +1857,7 @@ class E public static N Target() { Console.WriteLine(""PASS""); return default(N); } } "; - Action containerValidator = module => + static void containerValidator(ModuleSymbol module) { var container = module.GlobalNamespace.GetMember("D.O__0"); Assert.NotNull(container); Debug.Assert(container is { }); @@ -1869,7 +1868,7 @@ class E var m = typeParameters[0]; Assert.NotNull(m); Debug.Assert(m is { }); Assert.True(m.IsValueType); - }; + } CompileAndVerify(source, symbolValidator: containerValidator, expectedOutput: PASS).VerifyIL("D.Test", @" { // Code size 34 (0x22) @@ -2515,7 +2514,7 @@ class D public static void Target() { } } "; - Action containerValidator = module => + static void containerValidator(ModuleSymbol module) { var container = module.GlobalNamespace.GetMember("C.<>O"); Assert.NotNull(container); Debug.Assert(container is { }); @@ -2534,7 +2533,7 @@ public static void Target() { } Assert.Equal("System", fieldType.ContainingNamespace.Name); Assert.Equal("Action", fieldType.Name); Assert.Equal(0, fieldType.Arity); - }; + } CompileAndVerify(source, symbolValidator: containerValidator); } @@ -2550,7 +2549,7 @@ class C static V Target() { return default(V); } } "; - Action containerValidator = module => + static void containerValidator(ModuleSymbol module) { var container = module.GlobalNamespace.GetMember("C.<>O"); Assert.NotNull(container); Debug.Assert(container is { }); @@ -2570,7 +2569,7 @@ class C Assert.Equal("Func", fieldType.Name); Assert.Equal(1, fieldType.Arity); Assert.Equal(module.GlobalNamespace.GetTypeMember("C").TypeParameters[0], fieldType.TypeArguments()[0]); - }; + } CompileAndVerify(source, symbolValidator: containerValidator); } @@ -2587,7 +2586,7 @@ class C static T Target() { return default(T); } } "; - Action containerValidator = module => + static void containerValidator(ModuleSymbol module) { var container = module.GlobalNamespace.GetMember("C.<>O"); Assert.NotNull(container); Debug.Assert(container is { }); @@ -2602,7 +2601,7 @@ class C var fieldType = field.Type as NamedTypeSymbol; Assert.Equal(module.GlobalNamespace.GetMember("C.MyFunc"), fieldType); - }; + } CompileAndVerify(source, symbolValidator: containerValidator); } @@ -2624,7 +2623,7 @@ class D public static void Target() { } } "; - Action containerValidator = module => + static void containerValidator(ModuleSymbol module) { var container = module.GlobalNamespace.GetMember("C.O__0"); Assert.NotNull(container); Debug.Assert(container is { }); @@ -2643,7 +2642,7 @@ public static void Target() { } Assert.Equal("System", fieldType.ContainingNamespace.Name); Assert.Equal("Action", fieldType.Name); Assert.Equal(0, fieldType.Arity); - }; + } CompileAndVerify(source, symbolValidator: containerValidator); } @@ -2665,7 +2664,7 @@ class D public static B Target(H h) => default(B); } "; - Action containerValidator = module => + static void containerValidator(ModuleSymbol module) { var testClass = module.GlobalNamespace.GetTypeMember("C"); var container = testClass.GetTypeMember("O__0"); @@ -2687,7 +2686,7 @@ class D Assert.Equal(2, fieldType.Arity); Assert.Equal(testClass.TypeParameters[0], fieldType.TypeArguments()[0]); Assert.Equal(container.TypeParameters[0], fieldType.TypeArguments()[1]); - }; + } CompileAndVerify(source, symbolValidator: containerValidator); } @@ -2711,7 +2710,7 @@ static class D public static B Target(this int num) => default(B); } "; - Action containerValidator = module => + static void containerValidator(ModuleSymbol module) { var container = module.GlobalNamespace.GetMember("C.O__2"); Assert.NotNull(container); Debug.Assert(container is { }); @@ -2728,7 +2727,7 @@ static class D Assert.NotNull(fieldType); Debug.Assert(fieldType is { }); Assert.True(fieldType.IsDelegateType()); Assert.Equal(module.GlobalNamespace.GetMember("C.MyFunc"), fieldType); - }; + } CompileAndVerify(source, symbolValidator: containerValidator); } @@ -2798,7 +2797,7 @@ static class E public static void Target5(this N n) { } } "; - Action containerValidator = module => + static void containerValidator(ModuleSymbol module) { var A = module.GlobalNamespace.GetTypeMember("A"); var B = A.GetTypeMember("B"); @@ -2897,7 +2896,7 @@ public static void Target5(this N n) { } Assert.Equal("Action", fieldType6.Name); Assert.Equal(1, fieldType6.Arity); Assert.Equal(V, fieldType6.TypeArguments()[0]); - }; + } CompileAndVerify(source, symbolValidator: containerValidator); } @@ -2929,7 +2928,7 @@ static class E public static void Target3(this C c) { } } "; - Action containerValidator = module => + static void containerValidator(ModuleSymbol module) { var testClass = module.GlobalNamespace.GetTypeMember("C"); var container = testClass.GetTypeMember("O__0"); @@ -2987,7 +2986,7 @@ public static void Target3(this C c) { } Assert.Equal("Action", fieldType3.Name); Assert.Equal(1, fieldType3.Arity); Assert.Equal(testClass, fieldType3.TypeArguments()[0]); - }; + } CompileAndVerify(source, symbolValidator: containerValidator); } @@ -3019,7 +3018,7 @@ static void Test() public static void Target3(this C c) { } } "; - Action containerValidator = module => + static void containerValidator(ModuleSymbol module) { var testClass = module.GlobalNamespace.GetTypeMember("E"); var container = testClass.GetTypeMember("O__0"); @@ -3078,7 +3077,7 @@ public static void Target3(this C c) { } Assert.Equal("Action", fieldType3.Name); Assert.Equal(1, fieldType3.Arity); Assert.Equal(C, fieldType3.TypeArguments()[0]); - }; + } CompileAndVerify(source, symbolValidator: containerValidator); } From 1daafd8da97ef33dc87af7cd8fb16ba8d952abd0 Mon Sep 17 00:00:00 2001 From: Paul Chen Date: Tue, 14 Dec 2021 20:40:17 +0800 Subject: [PATCH 03/66] Fix a flaw in choosing the container type that targets a local function. --- .../LocalRewriter/DelegateCacheRewriter.cs | 37 +++++-- ...odeGenMethodGroupConversionCachingTests.cs | 98 +++++++++++++++++++ 2 files changed, 127 insertions(+), 8 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/DelegateCacheRewriter.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/DelegateCacheRewriter.cs index 23906296ff97f..8ff72eb80f2be 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/DelegateCacheRewriter.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/DelegateCacheRewriter.cs @@ -58,13 +58,14 @@ internal BoundExpression Rewrite(int localFunctionOrdinal, SyntaxNode syntax, Bo private DelegateCacheContainer GetOrAddCacheContainer(int localFunctionOrdinal, NamedTypeSymbol delegateType, MethodSymbol targetMethod) { Debug.Assert(_factory.ModuleBuilderOpt is { }); + Debug.Assert(_factory.CurrentFunction is { }); var typeCompilationState = _factory.CompilationState; var generation = _factory.ModuleBuilderOpt.CurrentGenerationOrdinal; DelegateCacheContainer? container; - if (AConcreteContainerIsEnough(delegateType, targetMethod)) + if (CanUseConcreteCacheContainer(delegateType, targetMethod)) { container = typeCompilationState.ConcreteDelegateCacheContainer; @@ -90,7 +91,7 @@ private DelegateCacheContainer GetOrAddCacheContainer(int localFunctionOrdinal, return containersStack.Peek().CacheContainer; } - container = new(_factory.CurrentFunction ?? _topLevelMethod, _topLevelMethodOrdinal, localFunctionOrdinal, generation); + container = new(_factory.CurrentFunction, _topLevelMethodOrdinal, localFunctionOrdinal, generation); containersStack.Push((localFunctionOrdinal, container)); } @@ -99,16 +100,18 @@ private DelegateCacheContainer GetOrAddCacheContainer(int localFunctionOrdinal, return container; } - private bool AConcreteContainerIsEnough(NamedTypeSymbol delegateType, MethodSymbol targetMethod) + private bool CanUseConcreteCacheContainer(NamedTypeSymbol delegateType, MethodSymbol targetMethod) { // Possible places for type parameters that can act as type arguments to construct the delegateType or targetMethod: // 1. containing types - // 2. current method + // 2. top level method // 3. local functions - // Since our containers are created within the same enclosing type, we can ignore type parameters from it. + // Since our containers are created within the same enclosing types, we can ignore type parameters from them. + + Debug.Assert(_factory.CurrentFunction is { }); // Obviously, - if (_factory is { CurrentFunction: null, TopLevelMethod.Arity: 0 }) + if (_topLevelMethod.Arity == 0 && (object)_factory.CurrentFunction == _topLevelMethod) { return true; } @@ -116,8 +119,6 @@ private bool AConcreteContainerIsEnough(NamedTypeSymbol delegateType, MethodSymb var methodTypeParameters = PooledHashSet.GetInstance(); try { - methodTypeParameters.AddAll(_topLevelMethod.TypeParameters); - for (Symbol? s = _factory.CurrentFunction; s is MethodSymbol m; s = s.ContainingSymbol) { methodTypeParameters.AddAll(m.TypeParameters); @@ -128,6 +129,26 @@ private bool AConcreteContainerIsEnough(NamedTypeSymbol delegateType, MethodSymb return true; } + if (targetMethod.MethodKind == MethodKind.LocalFunction) + { + // Local functions can reference type parameters from their enclosing methods! + // + // For example: + // void Test() + // { + // var t = Target; + // static object Target() => default(T); + // } + // + // Therefore, unless no method type parameters for the target local function to use, + // we cannot safely use a concrete cache container without some deep analysis. + + // And because we've just found that + Debug.Assert(methodTypeParameters.Count != 0); + + return false; + } + var checker = TypeParameterUsageChecker.Instance; if (checker.Visit(delegateType, methodTypeParameters) || checker.Visit(targetMethod, methodTypeParameters)) diff --git a/src/Compilers/CSharp/Test/Emit2/CodeGen/CodeGenMethodGroupConversionCachingTests.cs b/src/Compilers/CSharp/Test/Emit2/CodeGen/CodeGenMethodGroupConversionCachingTests.cs index 8a18aef9b1ede..efcafd008be96 100644 --- a/src/Compilers/CSharp/Test/Emit2/CodeGen/CodeGenMethodGroupConversionCachingTests.cs +++ b/src/Compilers/CSharp/Test/Emit2/CodeGen/CodeGenMethodGroupConversionCachingTests.cs @@ -4020,6 +4020,104 @@ .maxstack 2 "); } + [Fact] + public void TopLevelStatement_Lambda_Local_LocalFunctions_MethodScoped0() + { + var source = @" +using System; + +Action y = () => +{ + void Test() { /* Test method ordinals in generated names */ } + Test(); +}; +Action x = () => +{ + void Test(T t) + { + Func f = Target; + f(t); + + static dynamic Target(G g) => 0; + } + + Test(0); +}; + +x(); +y(); +"; + CompileAndVerify(source).VerifyIL("Program.<
$>g__Test|0_3", @" +{ + // Code size 35 (0x23) + .maxstack 2 + IL_0000: ldsfld ""System.Func Program.O__0_1.<0>__Target"" + IL_0005: dup + IL_0006: brtrue.s IL_001b + IL_0008: pop + IL_0009: ldnull + IL_000a: ldftn ""dynamic Program.<
$>g__Target|0_4(T)"" + IL_0010: newobj ""System.Func..ctor(object, System.IntPtr)"" + IL_0015: dup + IL_0016: stsfld ""System.Func Program.O__0_1.<0>__Target"" + IL_001b: ldarg.0 + IL_001c: callvirt ""dynamic System.Func.Invoke(T)"" + IL_0021: pop + IL_0022: ret +} +"); + } + + [Fact] + public void TopLevelStatement_Lambda_Local_LocalFunctions_MethodScoped1() + { + var source = @" +var y = () => +{ + void Test() { /* Test method ordinals in generated names */ } + Test(); +}; +var x = () => +{ + void Test(T t) + { + var f = Target; + f(0); + + static dynamic Target(G g) + { + T f = default; + return f; + } + } + + Test(false); +}; + +x(); +y(); +"; + CompileAndVerify(source).VerifyIL("Program.<
$>g__Test|0_3", @" +{ + // Code size 35 (0x23) + .maxstack 2 + IL_0000: ldsfld ""System.Func Program.O__0_1.<0>__Target"" + IL_0005: dup + IL_0006: brtrue.s IL_001b + IL_0008: pop + IL_0009: ldnull + IL_000a: ldftn ""dynamic Program.<
$>g__Target|0_4(int)"" + IL_0010: newobj ""System.Func..ctor(object, System.IntPtr)"" + IL_0015: dup + IL_0016: stsfld ""System.Func Program.O__0_1.<0>__Target"" + IL_001b: ldc.i4.0 + IL_001c: callvirt ""dynamic System.Func.Invoke(int)"" + IL_0021: pop + IL_0022: ret +} +"); + } + [Fact] public void TestConditionalOperatorMethodGroup() { From 08d154469df263280251d4781aaa5ec90631335d Mon Sep 17 00:00:00 2001 From: Paul Chen Date: Wed, 15 Dec 2021 18:41:10 +0800 Subject: [PATCH 04/66] Add some tests for tuples and local functions. --- ...odeGenMethodGroupConversionCachingTests.cs | 253 ++++++++++++++++++ 1 file changed, 253 insertions(+) diff --git a/src/Compilers/CSharp/Test/Emit2/CodeGen/CodeGenMethodGroupConversionCachingTests.cs b/src/Compilers/CSharp/Test/Emit2/CodeGen/CodeGenMethodGroupConversionCachingTests.cs index efcafd008be96..1c74cd924b44b 100644 --- a/src/Compilers/CSharp/Test/Emit2/CodeGen/CodeGenMethodGroupConversionCachingTests.cs +++ b/src/Compilers/CSharp/Test/Emit2/CodeGen/CodeGenMethodGroupConversionCachingTests.cs @@ -4118,6 +4118,259 @@ .maxstack 2 "); } + [Fact] + public void TopLevelStatement_Tuples_LocalFunction_TypeScoped0() + { + var source = @" +(System.Action a, int _) = (Target, 0); +static void Target() { } +"; + CompileAndVerify(source).VerifyIL("", @" +{ + // Code size 29 (0x1d) + .maxstack 2 + IL_0000: ldsfld ""System.Action Program.<>O.<0>__Target"" + IL_0005: dup + IL_0006: brtrue.s IL_001b + IL_0008: pop + IL_0009: ldnull + IL_000a: ldftn ""void Program.<
$>g__Target|0_0()"" + IL_0010: newobj ""System.Action..ctor(object, System.IntPtr)"" + IL_0015: dup + IL_0016: stsfld ""System.Action Program.<>O.<0>__Target"" + IL_001b: pop + IL_001c: ret +} +"); + } + + [Fact] + public void TopLevelStatement_Tuples_LocalFunction_TypeScoped1() + { + var source = @" +var t = Target; +static (int a, int b) Target() => (0, 0); +"; + CompileAndVerify(source).VerifyIL("", @" +{ + // Code size 25 (0x19) + .maxstack 2 + IL_0000: ldsfld ""System.Func> Program.<>O.<0>__Target"" + IL_0005: brtrue.s IL_0018 + IL_0007: ldnull + IL_0008: ldftn ""System.ValueTuple Program.<
$>g__Target|0_0()"" + IL_000e: newobj ""System.Func>..ctor(object, System.IntPtr)"" + IL_0013: stsfld ""System.Func> Program.<>O.<0>__Target"" + IL_0018: ret +} +"); + } + + [Fact] + public void TopLevelStatement_Tuples_LocalFunction_MethodScoped0() + { + var source = @" +Test(0); +static void Test(T t) +{ + (System.Action a, int _) = (Target, 0); + static void Target() { } +} +"; + CompileAndVerify(source).VerifyIL("Program.<
$>g__Test|0_0", @" +{ + // Code size 29 (0x1d) + .maxstack 2 + IL_0000: ldsfld ""System.Action Program.O__0_0.<0>__Target"" + IL_0005: dup + IL_0006: brtrue.s IL_001b + IL_0008: pop + IL_0009: ldnull + IL_000a: ldftn ""void Program.<
$>g__Target|0_1()"" + IL_0010: newobj ""System.Action..ctor(object, System.IntPtr)"" + IL_0015: dup + IL_0016: stsfld ""System.Action Program.O__0_0.<0>__Target"" + IL_001b: pop + IL_001c: ret +} +"); + } + + [Fact] + public void TopLevelStatement_Tuples_LocalFunction_MethodScoped1() + { + var source = @" +Test(0); +static void Test(T t) +{ + (System.Func<(T, int)> a, int _) = (Target, 0); + static (T, int) Target() => (default(T), 0); +} +"; + CompileAndVerify(source).VerifyIL("Program.<
$>g__Test|0_0", @" +{ + // Code size 29 (0x1d) + .maxstack 2 + IL_0000: ldsfld ""System.Func> Program.O__0_0.<0>__Target"" + IL_0005: dup + IL_0006: brtrue.s IL_001b + IL_0008: pop + IL_0009: ldnull + IL_000a: ldftn ""System.ValueTuple Program.<
$>g__Target|0_1()"" + IL_0010: newobj ""System.Func>..ctor(object, System.IntPtr)"" + IL_0015: dup + IL_0016: stsfld ""System.Func> Program.O__0_0.<0>__Target"" + IL_001b: pop + IL_001c: ret +} +"); + } + + [Fact] + public void TopLevelStatement_Tuples_LocalFunction_MethodScoped2() + { + var source = @" +Test(0); +static void Test(T t) +{ + (System.Func a, int _) = (Target, 0); + static (T, G) Target(G g) => (default(T), g); +} +"; + CompileAndVerify(source).VerifyIL("Program.<
$>g__Test|0_0", @" +{ + // Code size 29 (0x1d) + .maxstack 2 + IL_0000: ldsfld ""System.Func> Program.O__0_0.<0>__Target"" + IL_0005: dup + IL_0006: brtrue.s IL_001b + IL_0008: pop + IL_0009: ldnull + IL_000a: ldftn ""System.ValueTuple Program.<
$>g__Target|0_1(T)"" + IL_0010: newobj ""System.Func>..ctor(object, System.IntPtr)"" + IL_0015: dup + IL_0016: stsfld ""System.Func> Program.O__0_0.<0>__Target"" + IL_001b: pop + IL_001c: ret +} +"); + } + + [Fact] + public void Tuples_LocalFunction_TypeScoped0() + { + var source = @" +class C +{ + void Test() + { + var x = Target; + static (T, T) Target(T t) => (t, t); + } +} +"; + CompileAndVerify(source).VerifyIL("C.Test", @" +{ + // Code size 25 (0x19) + .maxstack 2 + IL_0000: ldsfld ""System.Func> C.<>O.<0>__Target"" + IL_0005: brtrue.s IL_0018 + IL_0007: ldnull + IL_0008: ldftn ""System.ValueTuple C.g__Target|0_0(T)"" + IL_000e: newobj ""System.Func>..ctor(object, System.IntPtr)"" + IL_0013: stsfld ""System.Func> C.<>O.<0>__Target"" + IL_0018: ret +} +"); + } + + [Fact] + public void Tuples_LocalFunction_MethodScoped0() + { + var source = @" +class C +{ + void Test() + { + var x = Target; + static (T, G) Target(T t, G g) => (t, g); + } +} +"; + CompileAndVerify(source).VerifyIL("C.Test", @" +{ + // Code size 25 (0x19) + .maxstack 2 + IL_0000: ldsfld ""System.Func> C.O__0.<0>__Target"" + IL_0005: brtrue.s IL_0018 + IL_0007: ldnull + IL_0008: ldftn ""System.ValueTuple C.g__Target|0_0(T, G)"" + IL_000e: newobj ""System.Func>..ctor(object, System.IntPtr)"" + IL_0013: stsfld ""System.Func> C.O__0.<0>__Target"" + IL_0018: ret +} +"); + } + + [Fact] + public void Tuples_LocalFunction_MethodScoped1() + { + var source = @" +class C +{ + void Test() + { + var x = Target; + static (T, G, V) Target(T t, G g, V v) => (t, g, v); + } +} +"; + CompileAndVerify(source).VerifyIL("C.Test", @" +{ + // Code size 25 (0x19) + .maxstack 2 + IL_0000: ldsfld ""System.Func> C.O__0.<0>__Target"" + IL_0005: brtrue.s IL_0018 + IL_0007: ldnull + IL_0008: ldftn ""System.ValueTuple C.g__Target|0_0(T, G, T)"" + IL_000e: newobj ""System.Func>..ctor(object, System.IntPtr)"" + IL_0013: stsfld ""System.Func> C.O__0.<0>__Target"" + IL_0018: ret +} +"); + } + + [Fact] + public void Tuples_LocalFunction_MethodScoped2() + { + var source = @" +class M +{ + void F() + { + void Test() + { + var x = Target; + static (N, I, C, E) Target(N n, I i, C c, E e) => (n, i, c, e); + } + } +} +"; + CompileAndVerify(source).VerifyIL("M.g__Test|0_0", @" +{ + // Code size 25 (0x19) + .maxstack 2 + IL_0000: ldsfld ""System.Func> M.O__0_0.<0>__Target"" + IL_0005: brtrue.s IL_0018 + IL_0007: ldnull + IL_0008: ldftn ""System.ValueTuple M.g__Target|0_1(N, I, C, N)"" + IL_000e: newobj ""System.Func>..ctor(object, System.IntPtr)"" + IL_0013: stsfld ""System.Func> M.O__0_0.<0>__Target"" + IL_0018: ret +} +"); + } + [Fact] public void TestConditionalOperatorMethodGroup() { From 3afc667b6c7155f04f207aec1083eb43c713afa2 Mon Sep 17 00:00:00 2001 From: Paul Chen Date: Thu, 16 Dec 2021 00:15:53 +0800 Subject: [PATCH 05/66] Remove unnecessary stuffs. --- .../LocalRewriter/DelegateCacheContainer.cs | 6 ++++-- ...eCacheRewriter.TypeParameterUsageChecker.cs | 18 +----------------- .../LocalRewriter/DelegateCacheRewriter.cs | 6 +++--- .../LocalRewriter/LocalRewriter_Conversion.cs | 2 +- 4 files changed, 9 insertions(+), 23 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/DelegateCacheContainer.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/DelegateCacheContainer.cs index 0a43d3a1ad3de..29fdb9f1e828c 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/DelegateCacheContainer.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/DelegateCacheContainer.cs @@ -11,7 +11,7 @@ namespace Microsoft.CodeAnalysis.CSharp.Symbols; internal sealed class DelegateCacheContainer : SynthesizedContainer { private readonly Symbol _containingSymbol; - private readonly Dictionary<(NamedTypeSymbol, MethodSymbol), FieldSymbol> _delegateFields = new(); + private readonly Dictionary<(TypeSymbol, MethodSymbol), FieldSymbol> _delegateFields = new(); /// Creates a type scoped concrete delegate cache container. internal DelegateCacheContainer(TypeSymbol containingType, int generationOrdinal) @@ -43,8 +43,10 @@ internal DelegateCacheContainer(MethodSymbol containingMethod, int topLevelMetho internal override bool HasPossibleWellKnownCloneMethod() => false; - internal FieldSymbol GetOrAddCacheField(SyntheticBoundNodeFactory factory, NamedTypeSymbol delegateType, MethodSymbol targetMethod) + internal FieldSymbol GetOrAddCacheField(SyntheticBoundNodeFactory factory, TypeSymbol delegateType, MethodSymbol targetMethod) { + Debug.Assert(delegateType.IsDelegateType()); + if (_delegateFields.TryGetValue((delegateType, targetMethod), out var field)) { return field; diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/DelegateCacheRewriter.TypeParameterUsageChecker.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/DelegateCacheRewriter.TypeParameterUsageChecker.cs index 495e7672f72e1..f367cb3dc9743 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/DelegateCacheRewriter.TypeParameterUsageChecker.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/DelegateCacheRewriter.TypeParameterUsageChecker.cs @@ -127,22 +127,6 @@ private bool VisitTypeWithAnnotations(in TypeWithAnnotations typeWithAnnotations return false; } - public override bool VisitAssembly(AssemblySymbol symbol, HashSet typeParameters) => throw ExceptionUtilities.Unreachable; - - public override bool VisitModule(ModuleSymbol symbol, HashSet typeParameters) => throw ExceptionUtilities.Unreachable; - - public override bool VisitEvent(EventSymbol symbol, HashSet typeParameters) => throw ExceptionUtilities.Unreachable; - - public override bool VisitProperty(PropertySymbol symbol, HashSet typeParameters) => throw ExceptionUtilities.Unreachable; - - public override bool VisitField(FieldSymbol symbol, HashSet typeParameters) => throw ExceptionUtilities.Unreachable; - - public override bool VisitParameter(ParameterSymbol symbol, HashSet typeParameters) => throw ExceptionUtilities.Unreachable; - - public override bool VisitLocal(LocalSymbol symbol, HashSet typeParameters) => throw ExceptionUtilities.Unreachable; - - public override bool VisitRangeVariable(RangeVariableSymbol symbol, HashSet typeParameters) => throw ExceptionUtilities.Unreachable; - - public override bool VisitLabel(LabelSymbol symbol, HashSet typeParameters) => throw ExceptionUtilities.Unreachable; + public override bool DefaultVisit(Symbol symbol, HashSet argument) => throw ExceptionUtilities.Unreachable; } } diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/DelegateCacheRewriter.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/DelegateCacheRewriter.cs index 8ff72eb80f2be..7c8818b42bf04 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/DelegateCacheRewriter.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/DelegateCacheRewriter.cs @@ -35,7 +35,7 @@ internal static bool CanRewrite(CSharpCompilation compilation, MethodSymbol topL && compilation.IsStaticMethodGroupDelegateCacheEnabled ; - internal BoundExpression Rewrite(int localFunctionOrdinal, SyntaxNode syntax, BoundExpression receiver, MethodSymbol targetMethod, NamedTypeSymbol delegateType) + internal BoundExpression Rewrite(int localFunctionOrdinal, SyntaxNode syntax, BoundExpression receiver, MethodSymbol targetMethod, TypeSymbol delegateType) { Debug.Assert(delegateType.IsDelegateType()); @@ -55,7 +55,7 @@ internal BoundExpression Rewrite(int localFunctionOrdinal, SyntaxNode syntax, Bo return rewrittenNode; } - private DelegateCacheContainer GetOrAddCacheContainer(int localFunctionOrdinal, NamedTypeSymbol delegateType, MethodSymbol targetMethod) + private DelegateCacheContainer GetOrAddCacheContainer(int localFunctionOrdinal, TypeSymbol delegateType, MethodSymbol targetMethod) { Debug.Assert(_factory.ModuleBuilderOpt is { }); Debug.Assert(_factory.CurrentFunction is { }); @@ -100,7 +100,7 @@ private DelegateCacheContainer GetOrAddCacheContainer(int localFunctionOrdinal, return container; } - private bool CanUseConcreteCacheContainer(NamedTypeSymbol delegateType, MethodSymbol targetMethod) + private bool CanUseConcreteCacheContainer(TypeSymbol delegateType, MethodSymbol targetMethod) { // Possible places for type parameters that can act as type arguments to construct the delegateType or targetMethod: // 1. containing types diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_Conversion.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_Conversion.cs index 57263e2d488ae..11822008b1b1e 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_Conversion.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_Conversion.cs @@ -410,7 +410,7 @@ private BoundExpression MakeConversionNodeCore( if (DelegateCacheRewriter.CanRewrite(_factory.Compilation, _factory.TopLevelMethod, _inExpressionLambda, oldNodeOpt, method)) { var cacheRewriter = _lazyDelegateCacheRewriter ??= new(_factory, _topLevelMethodOrdinal); - return cacheRewriter.Rewrite(_currentLocalFunctionOrdinal, syntax, receiver, method, (NamedTypeSymbol)rewrittenType); + return cacheRewriter.Rewrite(_currentLocalFunctionOrdinal, syntax, receiver, method, rewrittenType); } else { From e03960f0bad1be590135187d1fc42e046d5b0fee Mon Sep 17 00:00:00 2001 From: Paul Chen Date: Tue, 21 Dec 2021 11:24:40 +0800 Subject: [PATCH 06/66] Feedback: Avoid targeted new for places where types are not immediately clear. --- .../Lowering/LocalRewriter/DelegateCacheRewriter.cs | 6 +++--- .../Lowering/LocalRewriter/LocalRewriter_Conversion.cs | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/DelegateCacheRewriter.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/DelegateCacheRewriter.cs index 7c8818b42bf04..1b8f0f6a35959 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/DelegateCacheRewriter.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/DelegateCacheRewriter.cs @@ -74,12 +74,12 @@ private DelegateCacheContainer GetOrAddCacheContainer(int localFunctionOrdinal, return container; } - container = new(typeCompilationState.Type, generation); + container = new DelegateCacheContainer(typeCompilationState.Type, generation); typeCompilationState.ConcreteDelegateCacheContainer = container; } else { - var containersStack = _genericCacheContainers ??= new(); + var containersStack = _genericCacheContainers ??= new Stack<(int LocalFunctionOrdinal, DelegateCacheContainer CacheContainer)>(); while (containersStack.Count > 0 && containersStack.Peek().LocalFunctionOrdinal > localFunctionOrdinal) { @@ -91,7 +91,7 @@ private DelegateCacheContainer GetOrAddCacheContainer(int localFunctionOrdinal, return containersStack.Peek().CacheContainer; } - container = new(_factory.CurrentFunction, _topLevelMethodOrdinal, localFunctionOrdinal, generation); + container = new DelegateCacheContainer(_factory.CurrentFunction, _topLevelMethodOrdinal, localFunctionOrdinal, generation); containersStack.Push((localFunctionOrdinal, container)); } diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_Conversion.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_Conversion.cs index 11822008b1b1e..701cf4ef0d186 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_Conversion.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_Conversion.cs @@ -409,7 +409,7 @@ private BoundExpression MakeConversionNodeCore( Debug.Assert(_factory.TopLevelMethod is { }); if (DelegateCacheRewriter.CanRewrite(_factory.Compilation, _factory.TopLevelMethod, _inExpressionLambda, oldNodeOpt, method)) { - var cacheRewriter = _lazyDelegateCacheRewriter ??= new(_factory, _topLevelMethodOrdinal); + var cacheRewriter = _lazyDelegateCacheRewriter ??= new DelegateCacheRewriter(_factory, _topLevelMethodOrdinal); return cacheRewriter.Rewrite(_currentLocalFunctionOrdinal, syntax, receiver, method, rewrittenType); } else From 846f16342d80acbbc2ab4580bc838487b85cd29c Mon Sep 17 00:00:00 2001 From: Paul Chen Date: Tue, 21 Dec 2021 11:31:19 +0800 Subject: [PATCH 07/66] Feedback: Add doc comments. --- .../Portable/Lowering/LocalRewriter/DelegateCacheContainer.cs | 3 +++ .../Portable/Lowering/LocalRewriter/DelegateCacheRewriter.cs | 3 +++ 2 files changed, 6 insertions(+) diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/DelegateCacheContainer.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/DelegateCacheContainer.cs index 29fdb9f1e828c..1c7a0cd610033 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/DelegateCacheContainer.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/DelegateCacheContainer.cs @@ -8,6 +8,9 @@ namespace Microsoft.CodeAnalysis.CSharp.Symbols; +/// +/// This type is synthesized to hold the cached delegates that target static method groups. +/// internal sealed class DelegateCacheContainer : SynthesizedContainer { private readonly Symbol _containingSymbol; diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/DelegateCacheRewriter.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/DelegateCacheRewriter.cs index 1b8f0f6a35959..3b6ec058b9719 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/DelegateCacheRewriter.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/DelegateCacheRewriter.cs @@ -10,6 +10,9 @@ namespace Microsoft.CodeAnalysis.CSharp; +/// +/// This type helps rewrite the delegate creations that target static method groups to use a cached instance of delegate. +/// internal sealed partial class DelegateCacheRewriter { private readonly SyntheticBoundNodeFactory _factory; From 6f9c4ab2306f0a25e254e6f2747f498d20167a6a Mon Sep 17 00:00:00 2001 From: Paul Chen Date: Tue, 21 Dec 2021 11:47:42 +0800 Subject: [PATCH 08/66] Feedback: Use shared instances. --- .../Lowering/LocalRewriter/DelegateCacheContainer.cs | 6 +++++- .../Lowering/LocalRewriter/DelegateCacheRewriter.cs | 4 +--- .../Lowering/LocalRewriter/LocalRewriter_Conversion.cs | 8 +++++--- 3 files changed, 11 insertions(+), 7 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/DelegateCacheContainer.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/DelegateCacheContainer.cs index 1c7a0cd610033..5472f10425295 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/DelegateCacheContainer.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/DelegateCacheContainer.cs @@ -14,6 +14,7 @@ namespace Microsoft.CodeAnalysis.CSharp.Symbols; internal sealed class DelegateCacheContainer : SynthesizedContainer { private readonly Symbol _containingSymbol; + private readonly NamedTypeSymbol? _constructedContainer; private readonly Dictionary<(TypeSymbol, MethodSymbol), FieldSymbol> _delegateFields = new(); /// Creates a type scoped concrete delegate cache container. @@ -30,6 +31,7 @@ internal DelegateCacheContainer(MethodSymbol containingMethod, int topLevelMetho { Debug.Assert(containingMethod.IsDefinition); _containingSymbol = containingMethod.ContainingType; + _constructedContainer = Construct(ConstructedFromTypeParameters); } public override Symbol ContainingSymbol => _containingSymbol; @@ -63,7 +65,9 @@ internal FieldSymbol GetOrAddCacheField(SyntheticBoundNodeFactory factory, TypeS if (!TypeParameters.IsEmpty) { - field = field.AsMember(Construct(ConstructedFromTypeParameters)); + Debug.Assert(_constructedContainer is { }); + + field = field.AsMember(_constructedContainer); } _delegateFields.Add((delegateType, targetMethod), field); diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/DelegateCacheRewriter.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/DelegateCacheRewriter.cs index 3b6ec058b9719..eb4e5f4504f48 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/DelegateCacheRewriter.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/DelegateCacheRewriter.cs @@ -38,7 +38,7 @@ internal static bool CanRewrite(CSharpCompilation compilation, MethodSymbol topL && compilation.IsStaticMethodGroupDelegateCacheEnabled ; - internal BoundExpression Rewrite(int localFunctionOrdinal, SyntaxNode syntax, BoundExpression receiver, MethodSymbol targetMethod, TypeSymbol delegateType) + internal BoundExpression Rewrite(int localFunctionOrdinal, SyntaxNode syntax, BoundDelegateCreationExpression boundDelegateCreation, MethodSymbol targetMethod, TypeSymbol delegateType) { Debug.Assert(delegateType.IsDelegateType()); @@ -49,8 +49,6 @@ internal BoundExpression Rewrite(int localFunctionOrdinal, SyntaxNode syntax, Bo var cacheField = cacheContainer.GetOrAddCacheField(_factory, delegateType, targetMethod); var boundCacheField = _factory.Field(null, cacheField); - var boundDelegateCreation = new BoundDelegateCreationExpression(syntax, receiver, targetMethod, isExtensionMethod: false, type: delegateType); - var rewrittenNode = _factory.Coalesce(boundCacheField, _factory.AssignmentExpression(boundCacheField, boundDelegateCreation)); _factory.Syntax = oldSyntax; diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_Conversion.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_Conversion.cs index 701cf4ef0d186..524a788f38ba0 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_Conversion.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_Conversion.cs @@ -406,16 +406,18 @@ private BoundExpression MakeConversionNodeCore( Debug.Assert(receiver is { }); _factory.Syntax = oldSyntax; + var boundDelegateCreation = new BoundDelegateCreationExpression(syntax, argument: receiver, methodOpt: method, + isExtensionMethod: oldNodeOpt.IsExtensionMethod, type: rewrittenType); + Debug.Assert(_factory.TopLevelMethod is { }); if (DelegateCacheRewriter.CanRewrite(_factory.Compilation, _factory.TopLevelMethod, _inExpressionLambda, oldNodeOpt, method)) { var cacheRewriter = _lazyDelegateCacheRewriter ??= new DelegateCacheRewriter(_factory, _topLevelMethodOrdinal); - return cacheRewriter.Rewrite(_currentLocalFunctionOrdinal, syntax, receiver, method, rewrittenType); + return cacheRewriter.Rewrite(_currentLocalFunctionOrdinal, syntax, boundDelegateCreation, method, rewrittenType); } else { - return new BoundDelegateCreationExpression(syntax, argument: receiver, methodOpt: method, - isExtensionMethod: oldNodeOpt.IsExtensionMethod, type: rewrittenType); + return boundDelegateCreation; } } From dc5c5bd80cde06f8f0acf57d6e67c1758cc0a60e Mon Sep 17 00:00:00 2001 From: Paul Chen Date: Tue, 21 Dec 2021 12:19:29 +0800 Subject: [PATCH 09/66] Feedback: Rename and optimize. --- .../LocalRewriter/DelegateCacheRewriter.cs | 56 ++++++++----------- 1 file changed, 24 insertions(+), 32 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/DelegateCacheRewriter.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/DelegateCacheRewriter.cs index eb4e5f4504f48..8f979b5671df0 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/DelegateCacheRewriter.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/DelegateCacheRewriter.cs @@ -66,7 +66,7 @@ private DelegateCacheContainer GetOrAddCacheContainer(int localFunctionOrdinal, DelegateCacheContainer? container; - if (CanUseConcreteCacheContainer(delegateType, targetMethod)) + if (CanUseTypeScopedConcreteCacheContainer(delegateType, targetMethod)) { container = typeCompilationState.ConcreteDelegateCacheContainer; @@ -101,7 +101,7 @@ private DelegateCacheContainer GetOrAddCacheContainer(int localFunctionOrdinal, return container; } - private bool CanUseConcreteCacheContainer(TypeSymbol delegateType, MethodSymbol targetMethod) + private bool CanUseTypeScopedConcreteCacheContainer(TypeSymbol delegateType, MethodSymbol targetMethod) { // Possible places for type parameters that can act as type arguments to construct the delegateType or targetMethod: // 1. containing types @@ -109,20 +109,32 @@ private bool CanUseConcreteCacheContainer(TypeSymbol delegateType, MethodSymbol // 3. local functions // Since our containers are created within the same enclosing types, we can ignore type parameters from them. - Debug.Assert(_factory.CurrentFunction is { }); - - // Obviously, - if (_topLevelMethod.Arity == 0 && (object)_factory.CurrentFunction == _topLevelMethod) - { - return true; - } - var methodTypeParameters = PooledHashSet.GetInstance(); try { - for (Symbol? s = _factory.CurrentFunction; s is MethodSymbol m; s = s.ContainingSymbol) + for (Symbol? enclosingSymbol = _factory.CurrentFunction; enclosingSymbol is MethodSymbol enclosingMethod; enclosingSymbol = enclosingSymbol.ContainingSymbol) { - methodTypeParameters.AddAll(m.TypeParameters); + if (enclosingMethod.Arity > 0) + { + if (targetMethod.MethodKind == MethodKind.LocalFunction) + { + // Local functions can reference type parameters from their enclosing methods! + // + // For example: + // void Test() + // { + // var t = Target; + // static object Target() => default(T); + // } + // + // Therefore, unless no method type parameters for the target local function to use, + // we cannot safely use a type scoped concrete cache container without some deep analysis. + + return false; + } + + methodTypeParameters.AddAll(enclosingMethod.TypeParameters); + } } if (methodTypeParameters.Count == 0) @@ -130,26 +142,6 @@ private bool CanUseConcreteCacheContainer(TypeSymbol delegateType, MethodSymbol return true; } - if (targetMethod.MethodKind == MethodKind.LocalFunction) - { - // Local functions can reference type parameters from their enclosing methods! - // - // For example: - // void Test() - // { - // var t = Target; - // static object Target() => default(T); - // } - // - // Therefore, unless no method type parameters for the target local function to use, - // we cannot safely use a concrete cache container without some deep analysis. - - // And because we've just found that - Debug.Assert(methodTypeParameters.Count != 0); - - return false; - } - var checker = TypeParameterUsageChecker.Instance; if (checker.Visit(delegateType, methodTypeParameters) || checker.Visit(targetMethod, methodTypeParameters)) From 346f1c0a99ef19796f84c5df6fc93401d9f98fa8 Mon Sep 17 00:00:00 2001 From: Paul Chen Date: Tue, 21 Dec 2021 13:04:26 +0800 Subject: [PATCH 10/66] Rename. --- ...ateCreationRewriter.TypeParameterUsageChecker.cs} | 2 +- ...eCacheRewriter.cs => DelegateCreationRewriter.cs} | 12 +++++------- .../Portable/Lowering/LocalRewriter/LocalRewriter.cs | 2 +- .../LocalRewriter/LocalRewriter_Conversion.cs | 6 +++--- 4 files changed, 10 insertions(+), 12 deletions(-) rename src/Compilers/CSharp/Portable/Lowering/LocalRewriter/{DelegateCacheRewriter.TypeParameterUsageChecker.cs => DelegateCreationRewriter.TypeParameterUsageChecker.cs} (99%) rename src/Compilers/CSharp/Portable/Lowering/LocalRewriter/{DelegateCacheRewriter.cs => DelegateCreationRewriter.cs} (89%) diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/DelegateCacheRewriter.TypeParameterUsageChecker.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/DelegateCreationRewriter.TypeParameterUsageChecker.cs similarity index 99% rename from src/Compilers/CSharp/Portable/Lowering/LocalRewriter/DelegateCacheRewriter.TypeParameterUsageChecker.cs rename to src/Compilers/CSharp/Portable/Lowering/LocalRewriter/DelegateCreationRewriter.TypeParameterUsageChecker.cs index f367cb3dc9743..923129b04620f 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/DelegateCacheRewriter.TypeParameterUsageChecker.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/DelegateCreationRewriter.TypeParameterUsageChecker.cs @@ -8,7 +8,7 @@ namespace Microsoft.CodeAnalysis.CSharp; -partial class DelegateCacheRewriter +partial class DelegateCreationRewriter { /// /// Checks if a type or a method is constructed with some type that involves the specified generic type parameters. diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/DelegateCacheRewriter.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/DelegateCreationRewriter.cs similarity index 89% rename from src/Compilers/CSharp/Portable/Lowering/LocalRewriter/DelegateCacheRewriter.cs rename to src/Compilers/CSharp/Portable/Lowering/LocalRewriter/DelegateCreationRewriter.cs index 8f979b5671df0..72ba2265ed956 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/DelegateCacheRewriter.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/DelegateCreationRewriter.cs @@ -13,24 +13,22 @@ namespace Microsoft.CodeAnalysis.CSharp; /// /// This type helps rewrite the delegate creations that target static method groups to use a cached instance of delegate. /// -internal sealed partial class DelegateCacheRewriter +internal sealed partial class DelegateCreationRewriter { private readonly SyntheticBoundNodeFactory _factory; - private readonly MethodSymbol _topLevelMethod; private readonly int _topLevelMethodOrdinal; private Stack<(int LocalFunctionOrdinal, DelegateCacheContainer CacheContainer)>? _genericCacheContainers; - internal DelegateCacheRewriter(SyntheticBoundNodeFactory factory, int topLevelMethodOrdinal) + internal DelegateCreationRewriter(SyntheticBoundNodeFactory factory, int topLevelMethodOrdinal) { Debug.Assert(factory.TopLevelMethod is { }); _factory = factory; - _topLevelMethod = factory.TopLevelMethod; _topLevelMethodOrdinal = topLevelMethodOrdinal; } - internal static bool CanRewrite(CSharpCompilation compilation, MethodSymbol topLevelMethod, bool inExpressionLambda, BoundConversion boundConversion, MethodSymbol targetMethod) + internal static bool AllowCaching(CSharpCompilation compilation, MethodSymbol topLevelMethod, bool inExpressionLambda, BoundConversion boundConversion, MethodSymbol targetMethod) => targetMethod.IsStatic && !boundConversion.IsExtensionMethod && !inExpressionLambda // The tree structure / meaning for expression trees should remain untouched. @@ -38,12 +36,12 @@ internal static bool CanRewrite(CSharpCompilation compilation, MethodSymbol topL && compilation.IsStaticMethodGroupDelegateCacheEnabled ; - internal BoundExpression Rewrite(int localFunctionOrdinal, SyntaxNode syntax, BoundDelegateCreationExpression boundDelegateCreation, MethodSymbol targetMethod, TypeSymbol delegateType) + internal BoundExpression Rewrite(BoundDelegateCreationExpression boundDelegateCreation, int localFunctionOrdinal, MethodSymbol targetMethod, TypeSymbol delegateType) { Debug.Assert(delegateType.IsDelegateType()); var oldSyntax = _factory.Syntax; - _factory.Syntax = syntax; + _factory.Syntax = boundDelegateCreation.Syntax; var cacheContainer = GetOrAddCacheContainer(localFunctionOrdinal, delegateType, targetMethod); var cacheField = cacheContainer.GetOrAddCacheField(_factory, delegateType, targetMethod); diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter.cs index 076d0df2686a0..ee74d35fa241d 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter.cs @@ -29,7 +29,7 @@ internal sealed partial class LocalRewriter : BoundTreeRewriterWithStackGuard private int _availableLocalFunctionOrdinal; private int _currentLocalFunctionOrdinal; private readonly int _topLevelMethodOrdinal; - private DelegateCacheRewriter? _lazyDelegateCacheRewriter; + private DelegateCreationRewriter? _lazyDelegateCacheRewriter; private bool _inExpressionLambda; private bool _sawAwait; diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_Conversion.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_Conversion.cs index 524a788f38ba0..2f029c3f033e3 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_Conversion.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_Conversion.cs @@ -410,10 +410,10 @@ private BoundExpression MakeConversionNodeCore( isExtensionMethod: oldNodeOpt.IsExtensionMethod, type: rewrittenType); Debug.Assert(_factory.TopLevelMethod is { }); - if (DelegateCacheRewriter.CanRewrite(_factory.Compilation, _factory.TopLevelMethod, _inExpressionLambda, oldNodeOpt, method)) + if (DelegateCreationRewriter.AllowCaching(_factory.Compilation, _factory.TopLevelMethod, _inExpressionLambda, oldNodeOpt, method)) { - var cacheRewriter = _lazyDelegateCacheRewriter ??= new DelegateCacheRewriter(_factory, _topLevelMethodOrdinal); - return cacheRewriter.Rewrite(_currentLocalFunctionOrdinal, syntax, boundDelegateCreation, method, rewrittenType); + var rewriter = _lazyDelegateCacheRewriter ??= new DelegateCreationRewriter(_factory, _topLevelMethodOrdinal); + return rewriter.Rewrite(boundDelegateCreation, _currentLocalFunctionOrdinal, method, rewrittenType); } else { From 9ec9776019894166adf795728df9fc320e291d4a Mon Sep 17 00:00:00 2001 From: Paul Chen Date: Tue, 21 Dec 2021 15:46:56 +0800 Subject: [PATCH 11/66] Feedback: Share containers for functions with zero arity within the same generic ancestor function. --- .../LocalRewriter/DelegateCacheContainer.cs | 4 +- .../LocalRewriter/DelegateCreationRewriter.cs | 50 +++++-- .../Lowering/LocalRewriter/LocalRewriter.cs | 8 +- .../LocalRewriter/LocalRewriter_Conversion.cs | 2 +- .../Symbols/Synthesized/GeneratedNames.cs | 6 +- ...odeGenMethodGroupConversionCachingTests.cs | 132 +++++++++--------- 6 files changed, 113 insertions(+), 89 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/DelegateCacheContainer.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/DelegateCacheContainer.cs index 5472f10425295..62926f54565da 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/DelegateCacheContainer.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/DelegateCacheContainer.cs @@ -26,8 +26,8 @@ internal DelegateCacheContainer(TypeSymbol containingType, int generationOrdinal } /// Creates a method scoped generic delegate cache container. - internal DelegateCacheContainer(MethodSymbol containingMethod, int topLevelMethodOrdinal, int localFunctionOrdinal, int generationOrdinal) - : base(GeneratedNames.DelegateCacheContainerType(generationOrdinal, containingMethod.Name, topLevelMethodOrdinal, localFunctionOrdinal), containingMethod) + internal DelegateCacheContainer(MethodSymbol containingMethod, int topLevelMethodOrdinal, int ownerUniqueId, int generationOrdinal) + : base(GeneratedNames.DelegateCacheContainerType(generationOrdinal, containingMethod.Name, topLevelMethodOrdinal, ownerUniqueId), containingMethod) { Debug.Assert(containingMethod.IsDefinition); _containingSymbol = containingMethod.ContainingType; diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/DelegateCreationRewriter.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/DelegateCreationRewriter.cs index 72ba2265ed956..bedb6df721239 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/DelegateCreationRewriter.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/DelegateCreationRewriter.cs @@ -18,7 +18,7 @@ internal sealed partial class DelegateCreationRewriter private readonly SyntheticBoundNodeFactory _factory; private readonly int _topLevelMethodOrdinal; - private Stack<(int LocalFunctionOrdinal, DelegateCacheContainer CacheContainer)>? _genericCacheContainers; + private Dictionary? _genericCacheContainers; internal DelegateCreationRewriter(SyntheticBoundNodeFactory factory, int topLevelMethodOrdinal) { @@ -36,14 +36,14 @@ internal static bool AllowCaching(CSharpCompilation compilation, MethodSymbol to && compilation.IsStaticMethodGroupDelegateCacheEnabled ; - internal BoundExpression Rewrite(BoundDelegateCreationExpression boundDelegateCreation, int localFunctionOrdinal, MethodSymbol targetMethod, TypeSymbol delegateType) + internal BoundExpression Rewrite(BoundDelegateCreationExpression boundDelegateCreation, MethodSymbol targetMethod, TypeSymbol delegateType) { Debug.Assert(delegateType.IsDelegateType()); var oldSyntax = _factory.Syntax; _factory.Syntax = boundDelegateCreation.Syntax; - var cacheContainer = GetOrAddCacheContainer(localFunctionOrdinal, delegateType, targetMethod); + var cacheContainer = GetOrAddCacheContainer(delegateType, targetMethod); var cacheField = cacheContainer.GetOrAddCacheField(_factory, delegateType, targetMethod); var boundCacheField = _factory.Field(null, cacheField); @@ -54,7 +54,7 @@ internal BoundExpression Rewrite(BoundDelegateCreationExpression boundDelegateCr return rewrittenNode; } - private DelegateCacheContainer GetOrAddCacheContainer(int localFunctionOrdinal, TypeSymbol delegateType, MethodSymbol targetMethod) + private DelegateCacheContainer GetOrAddCacheContainer(TypeSymbol delegateType, MethodSymbol targetMethod) { Debug.Assert(_factory.ModuleBuilderOpt is { }); Debug.Assert(_factory.CurrentFunction is { }); @@ -78,20 +78,48 @@ private DelegateCacheContainer GetOrAddCacheContainer(int localFunctionOrdinal, } else { - var containersStack = _genericCacheContainers ??= new Stack<(int LocalFunctionOrdinal, DelegateCacheContainer CacheContainer)>(); + // We don't need to synthesize a container for each and every function. + // For functions with zero arity, we can share the container with the closest generic ancestor function. + // + // For example: + // void LF1() + // { + // void LF2() + // { + // Func d = SomeMethod; + // static void LF4 () { Func d = SomeMethod; } + // } + // + // void LF3() + // { + // Func d = SomeMethod; + // } + // } + // + // In the above case, only one cached delegate is necessary, and it could be assigned to the container 'owned' by LF1. + + MethodSymbol? ownerFunction = null; - while (containersStack.Count > 0 && containersStack.Peek().LocalFunctionOrdinal > localFunctionOrdinal) + for (Symbol? enclosingSymbol = _factory.CurrentFunction; enclosingSymbol is MethodSymbol enclosingMethod; enclosingSymbol = enclosingSymbol.ContainingSymbol) { - containersStack.Pop(); + if (enclosingMethod.Arity > 0) + { + ownerFunction = enclosingMethod; + break; + } } - if (containersStack.Count > 0 && containersStack.Peek().LocalFunctionOrdinal == localFunctionOrdinal) + Debug.Assert(ownerFunction is { }); + + var containers = _genericCacheContainers ??= new Dictionary(); + + if (containers.TryGetValue(ownerFunction, out container)) { - return containersStack.Peek().CacheContainer; + return container; } - container = new DelegateCacheContainer(_factory.CurrentFunction, _topLevelMethodOrdinal, localFunctionOrdinal, generation); - containersStack.Push((localFunctionOrdinal, container)); + container = new DelegateCacheContainer(ownerFunction, _topLevelMethodOrdinal, containers.Count, generation); + containers.Add(ownerFunction, container); } _factory.AddNestedType(container); diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter.cs index ee74d35fa241d..18df4ba97bf0a 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter.cs @@ -27,7 +27,6 @@ internal sealed partial class LocalRewriter : BoundTreeRewriterWithStackGuard private LoweredDynamicOperationFactory _dynamicFactory; private bool _sawLambdas; private int _availableLocalFunctionOrdinal; - private int _currentLocalFunctionOrdinal; private readonly int _topLevelMethodOrdinal; private DelegateCreationRewriter? _lazyDelegateCacheRewriter; private bool _inExpressionLambda; @@ -61,7 +60,6 @@ private LocalRewriter( _previousSubmissionFields = previousSubmissionFields; _allowOmissionOfConditionalCalls = allowOmissionOfConditionalCalls; _topLevelMethodOrdinal = containingMethodOrdinal; - _currentLocalFunctionOrdinal = -1; _diagnostics = diagnostics; Debug.Assert(instrumenter != null); @@ -276,8 +274,7 @@ public override BoundNode VisitLambda(BoundLambda node) public override BoundNode VisitLocalFunctionStatement(BoundLocalFunctionStatement node) { - var oldLocalFunctionOrdinal = _currentLocalFunctionOrdinal; - _currentLocalFunctionOrdinal = _availableLocalFunctionOrdinal++; + int localFunctionOrdinal = _availableLocalFunctionOrdinal++; var localFunction = node.Symbol; CheckRefReadOnlySymbols(localFunction); @@ -327,7 +324,7 @@ static bool hasReturnTypeOrParameter(LocalFunctionSymbol localFunction, Funct__builder"; } - internal static string DelegateCacheContainerType(int generation, string? methodName = null, int methodOrdinal = -1, int localFunctionOrdinal = -1) + internal static string DelegateCacheContainerType(int generation, string? methodName = null, int methodOrdinal = -1, int ownerUniqueId = -1) { const char NameKind = (char)GeneratedNameKind.DelegateCacheContainerType; @@ -453,9 +453,9 @@ internal static string DelegateCacheContainerType(int generation, string? method builder.Append(GeneratedNameConstants.SuffixSeparator).Append(methodOrdinal); } - if (localFunctionOrdinal > -1) + if (ownerUniqueId > -1) { - builder.Append(IdSeparator).Append(localFunctionOrdinal); + builder.Append(IdSeparator).Append(ownerUniqueId); } if (generation > 0) diff --git a/src/Compilers/CSharp/Test/Emit2/CodeGen/CodeGenMethodGroupConversionCachingTests.cs b/src/Compilers/CSharp/Test/Emit2/CodeGen/CodeGenMethodGroupConversionCachingTests.cs index 1c74cd924b44b..f7c9ad26c3b63 100644 --- a/src/Compilers/CSharp/Test/Emit2/CodeGen/CodeGenMethodGroupConversionCachingTests.cs +++ b/src/Compilers/CSharp/Test/Emit2/CodeGen/CodeGenMethodGroupConversionCachingTests.cs @@ -858,7 +858,7 @@ public static void Test() { // Code size 33 (0x21) .maxstack 2 - IL_0000: ldsfld ""System.Action D.O__0.<0>__Target"" + IL_0000: ldsfld ""System.Action D.O__0_0.<0>__Target"" IL_0005: dup IL_0006: brtrue.s IL_001b IL_0008: pop @@ -866,7 +866,7 @@ .maxstack 2 IL_000a: ldftn ""void C.Target()"" IL_0010: newobj ""System.Action..ctor(object, System.IntPtr)"" IL_0015: dup - IL_0016: stsfld ""System.Action D.O__0.<0>__Target"" + IL_0016: stsfld ""System.Action D.O__0_0.<0>__Target"" IL_001b: callvirt ""void System.Action.Invoke()"" IL_0020: ret } @@ -903,7 +903,7 @@ class E { // Code size 33 (0x21) .maxstack 2 - IL_0000: ldsfld ""System.Action D.O__0.<0>__Target"" + IL_0000: ldsfld ""System.Action D.O__0_0.<0>__Target"" IL_0005: dup IL_0006: brtrue.s IL_001b IL_0008: pop @@ -911,7 +911,7 @@ .maxstack 2 IL_000a: ldftn ""void E.Target()"" IL_0010: newobj ""System.Action..ctor(object, System.IntPtr)"" IL_0015: dup - IL_0016: stsfld ""System.Action D.O__0.<0>__Target"" + IL_0016: stsfld ""System.Action D.O__0_0.<0>__Target"" IL_001b: callvirt ""void System.Action.Invoke()"" IL_0020: ret } @@ -949,7 +949,7 @@ class E // Code size 43 (0x2b) .maxstack 2 .locals init (T V_0) - IL_0000: ldsfld ""System.Func D.O__0.<0>__Target"" + IL_0000: ldsfld ""System.Func D.O__0_0.<0>__Target"" IL_0005: dup IL_0006: brtrue.s IL_001b IL_0008: pop @@ -957,7 +957,7 @@ .locals init (T V_0) IL_000a: ldftn ""int E.Target(T)"" IL_0010: newobj ""System.Func..ctor(object, System.IntPtr)"" IL_0015: dup - IL_0016: stsfld ""System.Func D.O__0.<0>__Target"" + IL_0016: stsfld ""System.Func D.O__0_0.<0>__Target"" IL_001b: ldloca.s V_0 IL_001d: initobj ""T"" IL_0023: ldloc.0 @@ -1000,7 +1000,7 @@ class E .maxstack 3 .locals init (M V_0, T V_1) - IL_0000: ldsfld ""System.Action D.O__0.<0>__Target"" + IL_0000: ldsfld ""System.Action D.O__0_0.<0>__Target"" IL_0005: dup IL_0006: brtrue.s IL_001b IL_0008: pop @@ -1008,7 +1008,7 @@ .locals init (M V_0, IL_000a: ldftn ""void E.Target(M, T)"" IL_0010: newobj ""System.Action..ctor(object, System.IntPtr)"" IL_0015: dup - IL_0016: stsfld ""System.Action D.O__0.<0>__Target"" + IL_0016: stsfld ""System.Action D.O__0_0.<0>__Target"" IL_001b: ldloca.s V_0 IL_001d: initobj ""M"" IL_0023: ldloc.0 @@ -1542,7 +1542,7 @@ public void Test() { // Code size 33 (0x21) .maxstack 2 - IL_0000: ldsfld ""D.MyAction D.O__1.<0>__Target"" + IL_0000: ldsfld ""D.MyAction D.O__1_0.<0>__Target"" IL_0005: dup IL_0006: brtrue.s IL_001b IL_0008: pop @@ -1550,7 +1550,7 @@ .maxstack 2 IL_000a: ldftn ""void C.Target()"" IL_0010: newobj ""D.MyAction..ctor(object, System.IntPtr)"" IL_0015: dup - IL_0016: stsfld ""D.MyAction D.O__1.<0>__Target"" + IL_0016: stsfld ""D.MyAction D.O__1_0.<0>__Target"" IL_001b: callvirt ""void D.MyAction.Invoke()"" IL_0020: ret } @@ -1588,7 +1588,7 @@ public void Test() // Code size 42 (0x2a) .maxstack 2 .locals init (V V_0) - IL_0000: ldsfld ""D.MyAction D.O__1.<0>__Target"" + IL_0000: ldsfld ""D.MyAction D.O__1_0.<0>__Target"" IL_0005: dup IL_0006: brtrue.s IL_001b IL_0008: pop @@ -1596,7 +1596,7 @@ .locals init (V V_0) IL_000a: ldftn ""void C.Target(V)"" IL_0010: newobj ""D.MyAction..ctor(object, System.IntPtr)"" IL_0015: dup - IL_0016: stsfld ""D.MyAction D.O__1.<0>__Target"" + IL_0016: stsfld ""D.MyAction D.O__1_0.<0>__Target"" IL_001b: ldloca.s V_0 IL_001d: initobj ""V"" IL_0023: ldloc.0 @@ -1638,7 +1638,7 @@ public void Test() .maxstack 3 .locals init (T V_0, V V_1) - IL_0000: ldsfld ""D.MyFunc D.O__1.<0>__Target"" + IL_0000: ldsfld ""D.MyFunc D.O__1_0.<0>__Target"" IL_0005: dup IL_0006: brtrue.s IL_001b IL_0008: pop @@ -1646,7 +1646,7 @@ .locals init (T V_0, IL_000a: ldftn ""V C.Target(T, V)"" IL_0010: newobj ""D.MyFunc..ctor(object, System.IntPtr)"" IL_0015: dup - IL_0016: stsfld ""D.MyFunc D.O__1.<0>__Target"" + IL_0016: stsfld ""D.MyFunc D.O__1_0.<0>__Target"" IL_001b: ldloca.s V_0 IL_001d: initobj ""T"" IL_0023: ldloc.0 @@ -1741,7 +1741,7 @@ class E static void containerValidator(ModuleSymbol module) { var testClass = module.GlobalNamespace.GetTypeMember("D"); - var container = testClass.GetTypeMember("O__1"); + var container = testClass.GetTypeMember("O__1_0"); Assert.NotNull(container); Debug.Assert(container is { }); var typeParameters = container.TypeParameters; @@ -1755,7 +1755,7 @@ static void containerValidator(ModuleSymbol module) { // Code size 34 (0x22) .maxstack 2 - IL_0000: ldsfld ""D.MyFunc D.O__1.<0>__Target"" + IL_0000: ldsfld ""D.MyFunc D.O__1_0.<0>__Target"" IL_0005: dup IL_0006: brtrue.s IL_001b IL_0008: pop @@ -1763,7 +1763,7 @@ .maxstack 2 IL_000a: ldftn ""M E.Target()"" IL_0010: newobj ""D.MyFunc..ctor(object, System.IntPtr)"" IL_0015: dup - IL_0016: stsfld ""D.MyFunc D.O__1.<0>__Target"" + IL_0016: stsfld ""D.MyFunc D.O__1_0.<0>__Target"" IL_001b: callvirt ""T D.MyFunc.Invoke()"" IL_0020: pop IL_0021: ret @@ -1802,7 +1802,7 @@ static void containerValidator(ModuleSymbol module) { var globalNs = module.GlobalNamespace; var mainClass = globalNs.GetTypeMember("C"); - var container = globalNs.GetMember("D.O__1"); + var container = globalNs.GetMember("D.O__1_0"); Assert.NotNull(container); Debug.Assert(container is { }); var typeParameters = container.TypeParameters; @@ -1816,7 +1816,7 @@ static void containerValidator(ModuleSymbol module) { // Code size 34 (0x22) .maxstack 2 - IL_0000: ldsfld ""D.MyFunc D.O__1.<0>__Target"" + IL_0000: ldsfld ""D.MyFunc D.O__1_0.<0>__Target"" IL_0005: dup IL_0006: brtrue.s IL_001b IL_0008: pop @@ -1824,7 +1824,7 @@ .maxstack 2 IL_000a: ldftn ""M E.Target()"" IL_0010: newobj ""D.MyFunc..ctor(object, System.IntPtr)"" IL_0015: dup - IL_0016: stsfld ""D.MyFunc D.O__1.<0>__Target"" + IL_0016: stsfld ""D.MyFunc D.O__1_0.<0>__Target"" IL_001b: callvirt ""C D.MyFunc.Invoke()"" IL_0020: pop IL_0021: ret @@ -1859,7 +1859,7 @@ class E "; static void containerValidator(ModuleSymbol module) { - var container = module.GlobalNamespace.GetMember("D.O__0"); + var container = module.GlobalNamespace.GetMember("D.O__0_0"); Assert.NotNull(container); Debug.Assert(container is { }); var typeParameters = container.TypeParameters; @@ -1873,7 +1873,7 @@ static void containerValidator(ModuleSymbol module) { // Code size 34 (0x22) .maxstack 2 - IL_0000: ldsfld ""System.Func D.O__0.<0>__Target"" + IL_0000: ldsfld ""System.Func D.O__0_0.<0>__Target"" IL_0005: dup IL_0006: brtrue.s IL_001b IL_0008: pop @@ -1881,7 +1881,7 @@ .maxstack 2 IL_000a: ldftn ""M? E.Target()"" IL_0010: newobj ""System.Func..ctor(object, System.IntPtr)"" IL_0015: dup - IL_0016: stsfld ""System.Func D.O__0.<0>__Target"" + IL_0016: stsfld ""System.Func D.O__0_0.<0>__Target"" IL_001b: callvirt ""M? System.Func.Invoke()"" IL_0020: pop IL_0021: ret @@ -2148,7 +2148,7 @@ static class E { // Code size 34 (0x22) .maxstack 2 - IL_0000: ldsfld ""System.Action C.O__1.<0>__Target"" + IL_0000: ldsfld ""System.Action C.O__1_0.<0>__Target"" IL_0005: dup IL_0006: brtrue.s IL_001b IL_0008: pop @@ -2156,7 +2156,7 @@ .maxstack 2 IL_000a: ldftn ""void E.Target(T)"" IL_0010: newobj ""System.Action..ctor(object, System.IntPtr)"" IL_0015: dup - IL_0016: stsfld ""System.Action C.O__1.<0>__Target"" + IL_0016: stsfld ""System.Action C.O__1_0.<0>__Target"" IL_001b: ldarg.0 IL_001c: callvirt ""void System.Action.Invoke(T)"" IL_0021: ret @@ -2192,7 +2192,7 @@ static class E // Code size 42 (0x2a) .maxstack 2 .locals init (T V_0) - IL_0000: ldsfld ""System.Action C.O__1.<0>__Target"" + IL_0000: ldsfld ""System.Action C.O__1_0.<0>__Target"" IL_0005: dup IL_0006: brtrue.s IL_001b IL_0008: pop @@ -2200,7 +2200,7 @@ .locals init (T V_0) IL_000a: ldftn ""void E.Target(C)"" IL_0010: newobj ""System.Action..ctor(object, System.IntPtr)"" IL_0015: dup - IL_0016: stsfld ""System.Action C.O__1.<0>__Target"" + IL_0016: stsfld ""System.Action C.O__1_0.<0>__Target"" IL_001b: ldloca.s V_0 IL_001d: initobj ""T"" IL_0023: ldloc.0 @@ -2229,12 +2229,12 @@ public static void Target(this T t) { } { // Code size 25 (0x19) .maxstack 2 - IL_0000: ldsfld ""System.Action E.O__0.<0>__Target"" + IL_0000: ldsfld ""System.Action E.O__0_0.<0>__Target"" IL_0005: brtrue.s IL_0018 IL_0007: ldnull IL_0008: ldftn ""void E.Target(T)"" IL_000e: newobj ""System.Action..ctor(object, System.IntPtr)"" - IL_0013: stsfld ""System.Action E.O__0.<0>__Target"" + IL_0013: stsfld ""System.Action E.O__0_0.<0>__Target"" IL_0018: ret } "); @@ -2439,7 +2439,7 @@ public static void Test(G g) { // Code size 34 (0x22) .maxstack 2 - IL_0000: ldsfld ""System.Func D.<>O__0.<0>__Target"" + IL_0000: ldsfld ""System.Func D.O__0_0.<0>__Target"" IL_0005: dup IL_0006: brtrue.s IL_001b IL_0008: pop @@ -2447,7 +2447,7 @@ .maxstack 2 IL_000a: ldftn ""T D.Target()"" IL_0010: newobj ""System.Func..ctor(object, System.IntPtr)"" IL_0015: dup - IL_0016: stsfld ""System.Func D.<>O__0.<0>__Target"" + IL_0016: stsfld ""System.Func D.O__0_0.<0>__Target"" IL_001b: callvirt ""T System.Func.Invoke()"" IL_0020: pop IL_0021: ret @@ -2485,7 +2485,7 @@ class E { // Code size 28 (0x1c) .maxstack 2 - IL_0000: ldsfld ""System.Func D.<>O__0.<0>__Target"" + IL_0000: ldsfld ""System.Func D.O__0_0.<0>__Target"" IL_0005: dup IL_0006: brtrue.s IL_001b IL_0008: pop @@ -2493,7 +2493,7 @@ .maxstack 2 IL_000a: ldftn ""G E.Target()"" IL_0010: newobj ""System.Func..ctor(object, System.IntPtr)"" IL_0015: dup - IL_0016: stsfld ""System.Func D.<>O__0.<0>__Target"" + IL_0016: stsfld ""System.Func D.O__0_0.<0>__Target"" IL_001b: ret } "); @@ -2625,7 +2625,7 @@ public static void Target() { } "; static void containerValidator(ModuleSymbol module) { - var container = module.GlobalNamespace.GetMember("C.O__0"); + var container = module.GlobalNamespace.GetMember("C.O__0_0"); Assert.NotNull(container); Debug.Assert(container is { }); Assert.True(container.IsGenericType); @@ -2667,7 +2667,7 @@ class D static void containerValidator(ModuleSymbol module) { var testClass = module.GlobalNamespace.GetTypeMember("C"); - var container = testClass.GetTypeMember("O__0"); + var container = testClass.GetTypeMember("O__0_0"); Assert.NotNull(container); Debug.Assert(container is { }); Assert.Equal(1, container.Arity); @@ -2712,7 +2712,7 @@ static class D "; static void containerValidator(ModuleSymbol module) { - var container = module.GlobalNamespace.GetMember("C.O__2"); + var container = module.GlobalNamespace.GetMember("C.O__2_0"); Assert.NotNull(container); Debug.Assert(container is { }); Assert.Equal(1, container.Arity); @@ -2931,7 +2931,7 @@ public static void Target3(this C c) { } static void containerValidator(ModuleSymbol module) { var testClass = module.GlobalNamespace.GetTypeMember("C"); - var container = testClass.GetTypeMember("O__0"); + var container = testClass.GetTypeMember("O__0_0"); Assert.NotNull(container); Debug.Assert(container is { }); Assert.Equal(1, container.Arity); @@ -3021,7 +3021,7 @@ public static void Target3(this C c) { } static void containerValidator(ModuleSymbol module) { var testClass = module.GlobalNamespace.GetTypeMember("E"); - var container = testClass.GetTypeMember("O__0"); + var container = testClass.GetTypeMember("O__0_0"); Assert.NotNull(container); Debug.Assert(container is { }); Assert.Equal(1, container.Arity); @@ -3222,7 +3222,7 @@ class D where TEventArgs : EventArgs // Code size 38 (0x26) .maxstack 3 IL_0000: newobj ""D..ctor()"" - IL_0005: ldsfld ""System.EventHandler C.O__0.<0>__Target"" + IL_0005: ldsfld ""System.EventHandler C.O__0_0.<0>__Target"" IL_000a: dup IL_000b: brtrue.s IL_0020 IL_000d: pop @@ -3230,7 +3230,7 @@ .maxstack 3 IL_000f: ldftn ""void C.Target(object, TEventArgs)"" IL_0015: newobj ""System.EventHandler..ctor(object, System.IntPtr)"" IL_001a: dup - IL_001b: stsfld ""System.EventHandler C.O__0.<0>__Target"" + IL_001b: stsfld ""System.EventHandler C.O__0_0.<0>__Target"" IL_0020: callvirt ""void D.SomethingHappened.add"" IL_0025: ret } @@ -3417,7 +3417,7 @@ class D .maxstack 3 IL_0000: ldarg.0 IL_0001: newobj ""<>f__AnonymousType0..ctor(T)"" - IL_0006: ldsfld ""System.Action<> D.O__0.<0>__Target"" + IL_0006: ldsfld ""System.Action<> D.O__0_0.<0>__Target"" IL_000b: dup IL_000c: brtrue.s IL_0021 IL_000e: pop @@ -3425,7 +3425,7 @@ .maxstack 3 IL_0010: ldftn ""void D.Target<>()"" IL_0016: newobj ""System.Action<>..ctor(object, System.IntPtr)"" IL_001b: dup - IL_001c: stsfld ""System.Action<> D.O__0.<0>__Target"" + IL_001c: stsfld ""System.Action<> D.O__0_0.<0>__Target"" IL_0021: call ""void D.Invoke<>(, System.Action<>)"" IL_0026: ret } @@ -3457,7 +3457,7 @@ .maxstack 4 IL_0000: ldarg.0 IL_0001: ldc.i4.0 IL_0002: newobj ""<>f__AnonymousType0..ctor(int)"" - IL_0007: ldsfld ""System.Action> D.O__0.<0>__Target"" + IL_0007: ldsfld ""System.Action> D.O__0_0.<0>__Target"" IL_000c: dup IL_000d: brtrue.s IL_0022 IL_000f: pop @@ -3465,7 +3465,7 @@ .maxstack 4 IL_0011: ldftn ""void D.Target>(T, )"" IL_0017: newobj ""System.Action>..ctor(object, System.IntPtr)"" IL_001c: dup - IL_001d: stsfld ""System.Action> D.O__0.<0>__Target"" + IL_001d: stsfld ""System.Action> D.O__0_0.<0>__Target"" IL_0022: call ""void D.Invoke>(T, , System.Action>)"" IL_0027: ret } @@ -3568,7 +3568,7 @@ unsafe void Test(T t) { // Code size 35 (0x23) .maxstack 2 - IL_0000: ldsfld ""System.Func C.O__0.<0>__Target"" + IL_0000: ldsfld ""System.Func C.O__0_0.<0>__Target"" IL_0005: dup IL_0006: brtrue.s IL_001b IL_0008: pop @@ -3576,7 +3576,7 @@ .maxstack 2 IL_000a: ldftn ""int*[] C.Target(T)"" IL_0010: newobj ""System.Func..ctor(object, System.IntPtr)"" IL_0015: dup - IL_0016: stsfld ""System.Func C.O__0.<0>__Target"" + IL_0016: stsfld ""System.Func C.O__0_0.<0>__Target"" IL_001b: ldarg.1 IL_001c: callvirt ""int*[] System.Func.Invoke(T)"" IL_0021: pop @@ -3681,7 +3681,7 @@ void Test(T t) { // Code size 35 (0x23) .maxstack 2 - IL_0000: ldsfld ""System.Func C.O__0.<0>__Target"" + IL_0000: ldsfld ""System.Func C.O__0_0.<0>__Target"" IL_0005: dup IL_0006: brtrue.s IL_001b IL_0008: pop @@ -3689,7 +3689,7 @@ .maxstack 2 IL_000a: ldftn ""dynamic C.Target(T)"" IL_0010: newobj ""System.Func..ctor(object, System.IntPtr)"" IL_0015: dup - IL_0016: stsfld ""System.Func C.O__0.<0>__Target"" + IL_0016: stsfld ""System.Func C.O__0_0.<0>__Target"" IL_001b: ldarg.1 IL_001c: callvirt ""dynamic System.Func.Invoke(T)"" IL_0021: pop @@ -3757,7 +3757,7 @@ void G(Delegate d) {} // Code size 34 (0x22) .maxstack 3 IL_0000: ldarg.0 - IL_0001: ldsfld "" C.O__0.<0>__Target"" + IL_0001: ldsfld "" C.O__0_0.<0>__Target"" IL_0006: dup IL_0007: brtrue.s IL_001c IL_0009: pop @@ -3765,7 +3765,7 @@ .maxstack 3 IL_000b: ldftn ""dynamic C.Target(ref T)"" IL_0011: newobj ""<>F{00000001}..ctor(object, System.IntPtr)"" IL_0016: dup - IL_0017: stsfld "" C.O__0.<0>__Target"" + IL_0017: stsfld "" C.O__0_0.<0>__Target"" IL_001c: call ""void C.G(System.Delegate)"" IL_0021: ret } @@ -3860,7 +3860,7 @@ void Test(T t) { // Code size 35 (0x23) .maxstack 2 - IL_0000: ldsfld ""System.Func C.O__0.<0>__Target"" + IL_0000: ldsfld ""System.Func C.O__0_0.<0>__Target"" IL_0005: dup IL_0006: brtrue.s IL_001b IL_0008: pop @@ -3868,7 +3868,7 @@ .maxstack 2 IL_000a: ldftn ""dynamic C.g__Target|0_0(T)"" IL_0010: newobj ""System.Func..ctor(object, System.IntPtr)"" IL_0015: dup - IL_0016: stsfld ""System.Func C.O__0.<0>__Target"" + IL_0016: stsfld ""System.Func C.O__0_0.<0>__Target"" IL_001b: ldarg.1 IL_001c: callvirt ""dynamic System.Func.Invoke(T)"" IL_0021: pop @@ -3900,7 +3900,7 @@ void Test() { // Code size 40 (0x28) .maxstack 2 - IL_0000: ldsfld ""System.Func C.O__0_0.<0>__Target"" + IL_0000: ldsfld ""System.Func C.O__0_0.<0>__Target"" IL_0005: dup IL_0006: brtrue.s IL_001b IL_0008: pop @@ -3908,7 +3908,7 @@ .maxstack 2 IL_000a: ldftn ""dynamic C.g__Target|0_1(T)"" IL_0010: newobj ""System.Func..ctor(object, System.IntPtr)"" IL_0015: dup - IL_0016: stsfld ""System.Func C.O__0_0.<0>__Target"" + IL_0016: stsfld ""System.Func C.O__0_0.<0>__Target"" IL_001b: ldarg.0 IL_001c: ldfld ""T C.<>c__DisplayClass0_0.t"" IL_0021: callvirt ""dynamic System.Func.Invoke(T)"" @@ -3948,7 +3948,7 @@ void Test() { // Code size 40 (0x28) .maxstack 2 - IL_0000: ldsfld ""System.Func C.O__0_0.<0>__Target"" + IL_0000: ldsfld ""System.Func C.O__0_0.<0>__Target"" IL_0005: dup IL_0006: brtrue.s IL_001b IL_0008: pop @@ -3956,7 +3956,7 @@ .maxstack 2 IL_000a: ldftn ""dynamic C.g__Target|0_2(T)"" IL_0010: newobj ""System.Func..ctor(object, System.IntPtr)"" IL_0015: dup - IL_0016: stsfld ""System.Func C.O__0_0.<0>__Target"" + IL_0016: stsfld ""System.Func C.O__0_0.<0>__Target"" IL_001b: ldarg.0 IL_001c: ldfld ""T C.<>c__DisplayClass0_0.t"" IL_0021: callvirt ""dynamic System.Func.Invoke(T)"" @@ -4002,7 +4002,7 @@ void Test() { // Code size 40 (0x28) .maxstack 2 - IL_0000: ldsfld ""System.Func C.O__0_1.<0>__Target"" + IL_0000: ldsfld ""System.Func C.O__0_0.<0>__Target"" IL_0005: dup IL_0006: brtrue.s IL_001b IL_0008: pop @@ -4010,7 +4010,7 @@ .maxstack 2 IL_000a: ldftn ""dynamic C.g__Target|0_4(T)"" IL_0010: newobj ""System.Func..ctor(object, System.IntPtr)"" IL_0015: dup - IL_0016: stsfld ""System.Func C.O__0_1.<0>__Target"" + IL_0016: stsfld ""System.Func C.O__0_0.<0>__Target"" IL_001b: ldarg.0 IL_001c: ldfld ""T C.<>c__DisplayClass0_0.t"" IL_0021: callvirt ""dynamic System.Func.Invoke(T)"" @@ -4051,7 +4051,7 @@ void Test(T t) { // Code size 35 (0x23) .maxstack 2 - IL_0000: ldsfld ""System.Func Program.O__0_1.<0>__Target"" + IL_0000: ldsfld ""System.Func Program.O__0_0.<0>__Target"" IL_0005: dup IL_0006: brtrue.s IL_001b IL_0008: pop @@ -4059,7 +4059,7 @@ .maxstack 2 IL_000a: ldftn ""dynamic Program.<
$>g__Target|0_4(T)"" IL_0010: newobj ""System.Func..ctor(object, System.IntPtr)"" IL_0015: dup - IL_0016: stsfld ""System.Func Program.O__0_1.<0>__Target"" + IL_0016: stsfld ""System.Func Program.O__0_0.<0>__Target"" IL_001b: ldarg.0 IL_001c: callvirt ""dynamic System.Func.Invoke(T)"" IL_0021: pop @@ -4101,7 +4101,7 @@ static dynamic Target(G g) { // Code size 35 (0x23) .maxstack 2 - IL_0000: ldsfld ""System.Func Program.O__0_1.<0>__Target"" + IL_0000: ldsfld ""System.Func Program.O__0_0.<0>__Target"" IL_0005: dup IL_0006: brtrue.s IL_001b IL_0008: pop @@ -4109,7 +4109,7 @@ .maxstack 2 IL_000a: ldftn ""dynamic Program.<
$>g__Target|0_4(int)"" IL_0010: newobj ""System.Func..ctor(object, System.IntPtr)"" IL_0015: dup - IL_0016: stsfld ""System.Func Program.O__0_1.<0>__Target"" + IL_0016: stsfld ""System.Func Program.O__0_0.<0>__Target"" IL_001b: ldc.i4.0 IL_001c: callvirt ""dynamic System.Func.Invoke(int)"" IL_0021: pop @@ -4301,12 +4301,12 @@ void Test() { // Code size 25 (0x19) .maxstack 2 - IL_0000: ldsfld ""System.Func> C.O__0.<0>__Target"" + IL_0000: ldsfld ""System.Func> C.O__0_0.<0>__Target"" IL_0005: brtrue.s IL_0018 IL_0007: ldnull IL_0008: ldftn ""System.ValueTuple C.g__Target|0_0(T, G)"" IL_000e: newobj ""System.Func>..ctor(object, System.IntPtr)"" - IL_0013: stsfld ""System.Func> C.O__0.<0>__Target"" + IL_0013: stsfld ""System.Func> C.O__0_0.<0>__Target"" IL_0018: ret } "); @@ -4329,12 +4329,12 @@ void Test() { // Code size 25 (0x19) .maxstack 2 - IL_0000: ldsfld ""System.Func> C.O__0.<0>__Target"" + IL_0000: ldsfld ""System.Func> C.O__0_0.<0>__Target"" IL_0005: brtrue.s IL_0018 IL_0007: ldnull IL_0008: ldftn ""System.ValueTuple C.g__Target|0_0(T, G, T)"" IL_000e: newobj ""System.Func>..ctor(object, System.IntPtr)"" - IL_0013: stsfld ""System.Func> C.O__0.<0>__Target"" + IL_0013: stsfld ""System.Func> C.O__0_0.<0>__Target"" IL_0018: ret } "); From af2f823bb82601c7c7de94cc37376de9f715c902 Mon Sep 17 00:00:00 2001 From: Paul Chen Date: Tue, 21 Dec 2021 21:50:48 +0800 Subject: [PATCH 12/66] Use MessageID for feature enabling. --- .../Portable/Compilation/CSharpCompilation.cs | 2 - .../CSharp/Portable/Errors/MessageID.cs | 3 + .../LocalRewriter/DelegateCreationRewriter.cs | 2 +- .../ExpressionCompilerTests.cs | 115 +++++++++++++----- 4 files changed, 90 insertions(+), 32 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Compilation/CSharpCompilation.cs b/src/Compilers/CSharp/Portable/Compilation/CSharpCompilation.cs index c9fd1c5224c36..b8bf3ced13628 100644 --- a/src/Compilers/CSharp/Portable/Compilation/CSharpCompilation.cs +++ b/src/Compilers/CSharp/Portable/Compilation/CSharpCompilation.cs @@ -206,8 +206,6 @@ internal override CommonAnonymousTypeManager CommonAnonymousTypeManager ///
internal bool IsPeVerifyCompatEnabled => LanguageVersion < LanguageVersion.CSharp7_2 || Feature("peverify-compat") != null; - internal bool IsStaticMethodGroupDelegateCacheEnabled => Feature("DisableStaticMethodGroupDelegateCache") is null; - /// /// Returns true if nullable analysis is enabled in the text span represented by the syntax node. /// diff --git a/src/Compilers/CSharp/Portable/Errors/MessageID.cs b/src/Compilers/CSharp/Portable/Errors/MessageID.cs index 9afceff25cbcb..ee4263caed018 100644 --- a/src/Compilers/CSharp/Portable/Errors/MessageID.cs +++ b/src/Compilers/CSharp/Portable/Errors/MessageID.cs @@ -240,6 +240,8 @@ internal enum MessageID IDS_FeatureNewLinesInInterpolations = MessageBase + 12813, IDS_FeatureListPattern = MessageBase + 12814, + + IDS_FeatureCacheStaticMethodGroupConversion = MessageBase + 12815, } // Message IDs may refer to strings that need to be localized. @@ -353,6 +355,7 @@ internal static LanguageVersion RequiredVersion(this MessageID feature) case MessageID.IDS_FeatureGenericAttributes: // semantic check case MessageID.IDS_FeatureNewLinesInInterpolations: // semantic check case MessageID.IDS_FeatureListPattern: // semantic check + case MessageID.IDS_FeatureCacheStaticMethodGroupConversion: // lowering check return LanguageVersion.Preview; // C# 10.0 features. diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/DelegateCreationRewriter.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/DelegateCreationRewriter.cs index bedb6df721239..e94fdad5c320b 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/DelegateCreationRewriter.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/DelegateCreationRewriter.cs @@ -33,7 +33,7 @@ internal static bool AllowCaching(CSharpCompilation compilation, MethodSymbol to && !boundConversion.IsExtensionMethod && !inExpressionLambda // The tree structure / meaning for expression trees should remain untouched. && topLevelMethod.MethodKind != MethodKind.StaticConstructor // Avoid caching twice if people do it manually. - && compilation.IsStaticMethodGroupDelegateCacheEnabled + && compilation.LanguageVersion >= MessageID.IDS_FeatureCacheStaticMethodGroupConversion.RequiredVersion() ; internal BoundExpression Rewrite(BoundDelegateCreationExpression boundDelegateCreation, MethodSymbol targetMethod, TypeSymbol delegateType) diff --git a/src/ExpressionEvaluator/CSharp/Test/ExpressionCompiler/ExpressionCompilerTests.cs b/src/ExpressionEvaluator/CSharp/Test/ExpressionCompiler/ExpressionCompilerTests.cs index 4dcffa2d271e2..43efe1cce9d90 100644 --- a/src/ExpressionEvaluator/CSharp/Test/ExpressionCompiler/ExpressionCompilerTests.cs +++ b/src/ExpressionEvaluator/CSharp/Test/ExpressionCompiler/ExpressionCompilerTests.cs @@ -1742,7 +1742,7 @@ static void M1() static void M2() { } static void M2(int i) { } }"; - var compilation0 = CreateCompilation(source, options: TestOptions.DebugDll); + var compilation0 = CreateCompilation(source, options: TestOptions.DebugDll, parseOptions: TestOptions.RegularPreview); WithRuntimeInstance(compilation0, runtime => { var context = CreateMethodContext( @@ -1756,29 +1756,58 @@ static void M2(int i) { } error: out error, testData: testData); Assert.Equal("error CS8917: The delegate type could not be inferred.", error); + testData = new CompilationTestData(); - context.CompileAssignment( - target: "o", - expr: "M1", - error: out error, - testData: testData); - testData.GetMethodData("<>x.<>m0").VerifyIL( + + // To remove the else block, please fix https://github.com/dotnet/roslyn/issues/58449 + if (context.Compilation.LanguageVersion == compilation0.LanguageVersion) + { + Assert.True(false); + + context.CompileAssignment( + target: "o", + expr: "M1", + error: out error, + testData: testData); + testData.GetMethodData("<>x.<>m0").VerifyIL( @"{ - // Code size 29 (0x1d) - .maxstack 2 - .locals init (object V_0) //o - IL_0000: ldsfld ""System.Action <>x.<>O.<0>__M1"" - IL_0005: dup - IL_0006: brtrue.s IL_001b - IL_0008: pop - IL_0009: ldnull - IL_000a: ldftn ""void C.M1()"" - IL_0010: newobj ""System.Action..ctor(object, System.IntPtr)"" - IL_0015: dup - IL_0016: stsfld ""System.Action <>x.<>O.<0>__M1"" - IL_001b: stloc.0 - IL_001c: ret + // Code size 29 (0x1d) + .maxstack 2 + .locals init (object V_0) //o + IL_0000: ldsfld ""System.Action <>x.<>O.<0>__M1"" + IL_0005: dup + IL_0006: brtrue.s IL_001b + IL_0008: pop + IL_0009: ldnull + IL_000a: ldftn ""void C.M1()"" + IL_0010: newobj ""System.Action..ctor(object, System.IntPtr)"" + IL_0015: dup + IL_0016: stsfld ""System.Action <>x.<>O.<0>__M1"" + IL_001b: stloc.0 + IL_001c: ret +}"); + } + else + { + Assert.False(false); + + context.CompileAssignment( + target: "o", + expr: "M1", + error: out error, + testData: testData); + testData.GetMethodData("<>x.<>m0").VerifyIL( +@"{ + // Code size 14 (0xe) + .maxstack 2 + .locals init (object V_0) //o + IL_0000: ldnull + IL_0001: ldftn ""void C.M1()"" + IL_0007: newobj ""System.Action..ctor(object, System.IntPtr)"" + IL_000c: stloc.0 + IL_000d: ret }"); + } }); } @@ -4176,13 +4205,22 @@ static void M() { } }"; - var testData = Evaluate( - source, - OutputKind.DynamicallyLinkedLibrary, - methodName: "C.M", - expr: "G(F)"); - testData.GetMethodData("<>x.<>m0").VerifyIL( -@"{ + var compilation = CreateCompilation(source, options: TestOptions.DebugDll, parseOptions: TestOptions.RegularPreview); + WithRuntimeInstance(compilation, runtime => + { + var context = CreateMethodContext( + runtime, + methodName: "C.M"); + var testData = new CompilationTestData(); + var result = context.CompileExpression("G(F)", out var error, testData); + + // To remove the else block, please fix https://github.com/dotnet/roslyn/issues/58449 + if (context.Compilation.LanguageVersion == compilation.LanguageVersion) + { + Assert.True(false); + + testData.GetMethodData("<>x.<>m0").VerifyIL(@" +{ // Code size 33 (0x21) .maxstack 2 IL_0000: ldsfld ""D <>x.<>O.<0>__F"" @@ -4196,7 +4234,26 @@ .maxstack 2 IL_0016: stsfld ""D <>x.<>O.<0>__F"" IL_001b: call ""void C.G(D)"" IL_0020: ret -}"); +} +"); + } + else + { + Assert.False(false); + + testData.GetMethodData("<>x.<>m0").VerifyIL(@" +{ + // Code size 18 (0x12) + .maxstack 2 + IL_0000: ldnull + IL_0001: ldftn ""void C.F()"" + IL_0007: newobj ""D..ctor(object, System.IntPtr)"" + IL_000c: call ""void C.G(D)"" + IL_0011: ret +} +"); + } + }); } [Fact] From f0b45f93167bcdde027aa7034bc2eb687658a901 Mon Sep 17 00:00:00 2001 From: Paul Chen Date: Tue, 21 Dec 2021 23:04:04 +0800 Subject: [PATCH 13/66] Fix test options. --- .../Attributes/AttributeTests_CallerInfoAttributes.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Compilers/CSharp/Test/Emit/Attributes/AttributeTests_CallerInfoAttributes.cs b/src/Compilers/CSharp/Test/Emit/Attributes/AttributeTests_CallerInfoAttributes.cs index 031e59638708b..0de94077f5338 100644 --- a/src/Compilers/CSharp/Test/Emit/Attributes/AttributeTests_CallerInfoAttributes.cs +++ b/src/Compilers/CSharp/Test/Emit/Attributes/AttributeTests_CallerInfoAttributes.cs @@ -64,7 +64,7 @@ public static void Main() } "; - var compilation = CreateCompilation(source, targetFramework: TargetFramework.NetCoreApp, options: TestOptions.ReleaseExe, parseOptions: TestOptions.Regular10); + var compilation = CreateCompilation(source, targetFramework: TargetFramework.NetCoreApp, options: TestOptions.ReleaseExe, parseOptions: TestOptions.RegularPreview); // Begin/EndInvoke are not currently supported. CompileAndVerify(compilation).VerifyDiagnostics().VerifyIL("Program.Main", @" { @@ -136,7 +136,7 @@ public static void Main() } "; - var compilation = CreateCompilation(source, targetFramework: TargetFramework.NetCoreApp, options: TestOptions.ReleaseExe, parseOptions: TestOptions.Regular10); + var compilation = CreateCompilation(source, targetFramework: TargetFramework.NetCoreApp, options: TestOptions.ReleaseExe, parseOptions: TestOptions.RegularPreview); // Begin/EndInvoke are not currently supported. CompileAndVerify(compilation).VerifyDiagnostics().VerifyIL("Program.Main", @" { @@ -379,7 +379,7 @@ public static void Main() } "; - var compilation = CreateCompilation(source, targetFramework: TargetFramework.NetCoreApp, options: TestOptions.ReleaseExe, parseOptions: TestOptions.Regular10); + var compilation = CreateCompilation(source, targetFramework: TargetFramework.NetCoreApp, options: TestOptions.ReleaseExe, parseOptions: TestOptions.RegularPreview); CompileAndVerify(compilation).VerifyDiagnostics( // (29,33): warning CS8963: The CallerArgumentExpressionAttribute applied to parameter 's2' will have no effect. It is applied with an invalid parameter name. // delegate void D(string s1, [CallerArgumentExpression(callback)] [Optional] [DefaultParameterValue("default")] string s2); @@ -2454,7 +2454,7 @@ public static void Main() } "; - var compilation = CreateCompilation(source, targetFramework: TargetFramework.NetCoreApp, options: TestOptions.ReleaseExe, parseOptions: TestOptions.Regular10); + var compilation = CreateCompilation(source, targetFramework: TargetFramework.NetCoreApp, options: TestOptions.ReleaseExe, parseOptions: TestOptions.RegularPreview); CompileAndVerify(compilation).VerifyDiagnostics().VerifyIL("Program.Main", @" { // Code size 44 (0x2c) @@ -2511,7 +2511,7 @@ public static void Main() } "; - var compilation = CreateCompilation(source, targetFramework: TargetFramework.NetCoreApp, options: TestOptions.ReleaseExe, parseOptions: TestOptions.Regular10); + var compilation = CreateCompilation(source, targetFramework: TargetFramework.NetCoreApp, options: TestOptions.ReleaseExe, parseOptions: TestOptions.RegularPreview); CompileAndVerify(compilation).VerifyDiagnostics().VerifyIL("Program.Main", @" { // Code size 44 (0x2c) From bb135c17f6e3633a133ad02c714f4e06284e850e Mon Sep 17 00:00:00 2001 From: Paul Chen Date: Tue, 21 Dec 2021 23:48:28 +0800 Subject: [PATCH 14/66] Revert file rename. --- ...cker.cs => DelegateCacheRewriter.TypeParameterUsageChecker.cs} | 0 .../{DelegateCreationRewriter.cs => DelegateCacheRewriter.cs} | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename src/Compilers/CSharp/Portable/Lowering/LocalRewriter/{DelegateCreationRewriter.TypeParameterUsageChecker.cs => DelegateCacheRewriter.TypeParameterUsageChecker.cs} (100%) rename src/Compilers/CSharp/Portable/Lowering/LocalRewriter/{DelegateCreationRewriter.cs => DelegateCacheRewriter.cs} (100%) diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/DelegateCreationRewriter.TypeParameterUsageChecker.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/DelegateCacheRewriter.TypeParameterUsageChecker.cs similarity index 100% rename from src/Compilers/CSharp/Portable/Lowering/LocalRewriter/DelegateCreationRewriter.TypeParameterUsageChecker.cs rename to src/Compilers/CSharp/Portable/Lowering/LocalRewriter/DelegateCacheRewriter.TypeParameterUsageChecker.cs diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/DelegateCreationRewriter.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/DelegateCacheRewriter.cs similarity index 100% rename from src/Compilers/CSharp/Portable/Lowering/LocalRewriter/DelegateCreationRewriter.cs rename to src/Compilers/CSharp/Portable/Lowering/LocalRewriter/DelegateCacheRewriter.cs From 357883bb0c862e6c46bd2a14094e3a79d1565aa8 Mon Sep 17 00:00:00 2001 From: Paul Chen Date: Wed, 22 Dec 2021 00:15:05 +0800 Subject: [PATCH 15/66] Keep existing tests that explicitly target older language versions. --- .../AttributeTests_CallerInfoAttributes.cs | 148 +++++++----------- 1 file changed, 59 insertions(+), 89 deletions(-) diff --git a/src/Compilers/CSharp/Test/Emit/Attributes/AttributeTests_CallerInfoAttributes.cs b/src/Compilers/CSharp/Test/Emit/Attributes/AttributeTests_CallerInfoAttributes.cs index 0de94077f5338..f9f3b86e81005 100644 --- a/src/Compilers/CSharp/Test/Emit/Attributes/AttributeTests_CallerInfoAttributes.cs +++ b/src/Compilers/CSharp/Test/Emit/Attributes/AttributeTests_CallerInfoAttributes.cs @@ -64,28 +64,22 @@ public static void Main() } "; - var compilation = CreateCompilation(source, targetFramework: TargetFramework.NetCoreApp, options: TestOptions.ReleaseExe, parseOptions: TestOptions.RegularPreview); + var compilation = CreateCompilation(source, targetFramework: TargetFramework.NetCoreApp, options: TestOptions.ReleaseExe, parseOptions: TestOptions.Regular10); // Begin/EndInvoke are not currently supported. CompileAndVerify(compilation).VerifyDiagnostics().VerifyIL("Program.Main", @" { - // Code size 46 (0x2e) + // Code size 31 (0x1f) .maxstack 5 - IL_0000: ldsfld ""Program.D Program.<>O.<0>__M"" - IL_0005: dup - IL_0006: brtrue.s IL_001b - IL_0008: pop - IL_0009: ldnull - IL_000a: ldftn ""void Program.M(string, string)"" - IL_0010: newobj ""Program.D..ctor(object, System.IntPtr)"" - IL_0015: dup - IL_0016: stsfld ""Program.D Program.<>O.<0>__M"" - IL_001b: call ""string Program.GetString()"" - IL_0020: ldstr ""default"" - IL_0025: ldnull - IL_0026: ldnull - IL_0027: callvirt ""System.IAsyncResult Program.D.BeginInvoke(string, string, System.AsyncCallback, object)"" - IL_002c: pop - IL_002d: ret + IL_0000: ldnull + IL_0001: ldftn ""void Program.M(string, string)"" + IL_0007: newobj ""Program.D..ctor(object, System.IntPtr)"" + IL_000c: call ""string Program.GetString()"" + IL_0011: ldstr ""default"" + IL_0016: ldnull + IL_0017: ldnull + IL_0018: callvirt ""System.IAsyncResult Program.D.BeginInvoke(string, string, System.AsyncCallback, object)"" + IL_001d: pop + IL_001e: ret } "); } @@ -136,28 +130,22 @@ public static void Main() } "; - var compilation = CreateCompilation(source, targetFramework: TargetFramework.NetCoreApp, options: TestOptions.ReleaseExe, parseOptions: TestOptions.RegularPreview); + var compilation = CreateCompilation(source, targetFramework: TargetFramework.NetCoreApp, options: TestOptions.ReleaseExe, parseOptions: TestOptions.Regular10); // Begin/EndInvoke are not currently supported. CompileAndVerify(compilation).VerifyDiagnostics().VerifyIL("Program.Main", @" { - // Code size 42 (0x2a) + // Code size 27 (0x1b) .maxstack 3 .locals init (string V_0) //s - IL_0000: ldsfld ""Program.D Program.<>O.<0>__M"" - IL_0005: dup - IL_0006: brtrue.s IL_001b - IL_0008: pop - IL_0009: ldnull - IL_000a: ldftn ""void Program.M(ref string, string)"" - IL_0010: newobj ""Program.D..ctor(object, System.IntPtr)"" - IL_0015: dup - IL_0016: stsfld ""Program.D Program.<>O.<0>__M"" - IL_001b: ldsfld ""string string.Empty"" - IL_0020: stloc.0 - IL_0021: ldloca.s V_0 - IL_0023: ldnull - IL_0024: callvirt ""void Program.D.EndInvoke(ref string, System.IAsyncResult)"" - IL_0029: ret + IL_0000: ldnull + IL_0001: ldftn ""void Program.M(ref string, string)"" + IL_0007: newobj ""Program.D..ctor(object, System.IntPtr)"" + IL_000c: ldsfld ""string string.Empty"" + IL_0011: stloc.0 + IL_0012: ldloca.s V_0 + IL_0014: ldnull + IL_0015: callvirt ""void Program.D.EndInvoke(ref string, System.IAsyncResult)"" + IL_001a: ret } "); } @@ -379,31 +367,25 @@ public static void Main() } "; - var compilation = CreateCompilation(source, targetFramework: TargetFramework.NetCoreApp, options: TestOptions.ReleaseExe, parseOptions: TestOptions.RegularPreview); + var compilation = CreateCompilation(source, targetFramework: TargetFramework.NetCoreApp, options: TestOptions.ReleaseExe, parseOptions: TestOptions.Regular10); CompileAndVerify(compilation).VerifyDiagnostics( // (29,33): warning CS8963: The CallerArgumentExpressionAttribute applied to parameter 's2' will have no effect. It is applied with an invalid parameter name. // delegate void D(string s1, [CallerArgumentExpression(callback)] [Optional] [DefaultParameterValue("default")] string s2); Diagnostic(ErrorCode.WRN_CallerArgumentExpressionAttributeHasInvalidParameterName, "CallerArgumentExpression").WithArguments("s2").WithLocation(29, 33) ).VerifyIL("Program.Main", @" { - // Code size 46 (0x2e) + // Code size 31 (0x1f) .maxstack 5 - IL_0000: ldsfld ""Program.D Program.<>O.<0>__M"" - IL_0005: dup - IL_0006: brtrue.s IL_001b - IL_0008: pop - IL_0009: ldnull - IL_000a: ldftn ""void Program.M(string, string)"" - IL_0010: newobj ""Program.D..ctor(object, System.IntPtr)"" - IL_0015: dup - IL_0016: stsfld ""Program.D Program.<>O.<0>__M"" - IL_001b: call ""string Program.GetString()"" - IL_0020: ldstr ""default"" - IL_0025: ldnull - IL_0026: ldnull - IL_0027: callvirt ""System.IAsyncResult Program.D.BeginInvoke(string, string, System.AsyncCallback, object)"" - IL_002c: pop - IL_002d: ret + IL_0000: ldnull + IL_0001: ldftn ""void Program.M(string, string)"" + IL_0007: newobj ""Program.D..ctor(object, System.IntPtr)"" + IL_000c: call ""string Program.GetString()"" + IL_0011: ldstr ""default"" + IL_0016: ldnull + IL_0017: ldnull + IL_0018: callvirt ""System.IAsyncResult Program.D.BeginInvoke(string, string, System.AsyncCallback, object)"" + IL_001d: pop + IL_001e: ret } "); } @@ -2454,29 +2436,23 @@ public static void Main() } "; - var compilation = CreateCompilation(source, targetFramework: TargetFramework.NetCoreApp, options: TestOptions.ReleaseExe, parseOptions: TestOptions.RegularPreview); + var compilation = CreateCompilation(source, targetFramework: TargetFramework.NetCoreApp, options: TestOptions.ReleaseExe, parseOptions: TestOptions.Regular10); CompileAndVerify(compilation).VerifyDiagnostics().VerifyIL("Program.Main", @" { - // Code size 44 (0x2c) + // Code size 29 (0x1d) .maxstack 4 .locals init (string V_0, //s2 string V_1) - IL_0000: ldsfld ""Program.D Program.<>O.<0>__M"" - IL_0005: dup - IL_0006: brtrue.s IL_001b - IL_0008: pop - IL_0009: ldnull - IL_000a: ldftn ""void Program.M(string, ref string, out string, string)"" - IL_0010: newobj ""Program.D..ctor(object, System.IntPtr)"" - IL_0015: dup - IL_0016: stsfld ""Program.D Program.<>O.<0>__M"" - IL_001b: ldstr ""s2-arg"" - IL_0020: stloc.0 - IL_0021: ldloca.s V_0 - IL_0023: ldloca.s V_1 - IL_0025: ldnull - IL_0026: callvirt ""void Program.D.EndInvoke(ref string, out string, System.IAsyncResult)"" - IL_002b: ret + IL_0000: ldnull + IL_0001: ldftn ""void Program.M(string, ref string, out string, string)"" + IL_0007: newobj ""Program.D..ctor(object, System.IntPtr)"" + IL_000c: ldstr ""s2-arg"" + IL_0011: stloc.0 + IL_0012: ldloca.s V_0 + IL_0014: ldloca.s V_1 + IL_0016: ldnull + IL_0017: callvirt ""void Program.D.EndInvoke(ref string, out string, System.IAsyncResult)"" + IL_001c: ret } "); } @@ -2511,29 +2487,23 @@ public static void Main() } "; - var compilation = CreateCompilation(source, targetFramework: TargetFramework.NetCoreApp, options: TestOptions.ReleaseExe, parseOptions: TestOptions.RegularPreview); + var compilation = CreateCompilation(source, targetFramework: TargetFramework.NetCoreApp, options: TestOptions.ReleaseExe, parseOptions: TestOptions.Regular10); CompileAndVerify(compilation).VerifyDiagnostics().VerifyIL("Program.Main", @" { - // Code size 44 (0x2c) + // Code size 29 (0x1d) .maxstack 4 .locals init (string V_0, //s2 string V_1) - IL_0000: ldsfld ""Program.D Program.<>O.<0>__M"" - IL_0005: dup - IL_0006: brtrue.s IL_001b - IL_0008: pop - IL_0009: ldnull - IL_000a: ldftn ""void Program.M(string, ref string, out string, string)"" - IL_0010: newobj ""Program.D..ctor(object, System.IntPtr)"" - IL_0015: dup - IL_0016: stsfld ""Program.D Program.<>O.<0>__M"" - IL_001b: ldstr ""s2-arg"" - IL_0020: stloc.0 - IL_0021: ldloca.s V_0 - IL_0023: ldloca.s V_1 - IL_0025: ldnull - IL_0026: callvirt ""void Program.D.EndInvoke(ref string, out string, System.IAsyncResult)"" - IL_002b: ret + IL_0000: ldnull + IL_0001: ldftn ""void Program.M(string, ref string, out string, string)"" + IL_0007: newobj ""Program.D..ctor(object, System.IntPtr)"" + IL_000c: ldstr ""s2-arg"" + IL_0011: stloc.0 + IL_0012: ldloca.s V_0 + IL_0014: ldloca.s V_1 + IL_0016: ldnull + IL_0017: callvirt ""void Program.D.EndInvoke(ref string, out string, System.IAsyncResult)"" + IL_001c: ret } "); } From fef8b7af946200f97e412b2f55590d4001b20140 Mon Sep 17 00:00:00 2001 From: Paul Chen Date: Wed, 22 Dec 2021 00:52:01 +0800 Subject: [PATCH 16/66] Feedback: Improve readability. --- .../Lowering/LocalRewriter/DelegateCacheRewriter.cs | 9 ++------- .../Lowering/LocalRewriter/LocalRewriter_Conversion.cs | 6 +++++- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/DelegateCacheRewriter.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/DelegateCacheRewriter.cs index e94fdad5c320b..b61450a7f514b 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/DelegateCacheRewriter.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/DelegateCacheRewriter.cs @@ -28,13 +28,8 @@ internal DelegateCreationRewriter(SyntheticBoundNodeFactory factory, int topLeve _topLevelMethodOrdinal = topLevelMethodOrdinal; } - internal static bool AllowCaching(CSharpCompilation compilation, MethodSymbol topLevelMethod, bool inExpressionLambda, BoundConversion boundConversion, MethodSymbol targetMethod) - => targetMethod.IsStatic - && !boundConversion.IsExtensionMethod - && !inExpressionLambda // The tree structure / meaning for expression trees should remain untouched. - && topLevelMethod.MethodKind != MethodKind.StaticConstructor // Avoid caching twice if people do it manually. - && compilation.LanguageVersion >= MessageID.IDS_FeatureCacheStaticMethodGroupConversion.RequiredVersion() - ; + internal static bool CanRewrite(MethodSymbol targetMethod, BoundConversion boundConversion) + => targetMethod.IsStatic && !boundConversion.IsExtensionMethod; internal BoundExpression Rewrite(BoundDelegateCreationExpression boundDelegateCreation, MethodSymbol targetMethod, TypeSymbol delegateType) { diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_Conversion.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_Conversion.cs index 31f2c2b2d996c..b155201ca3f96 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_Conversion.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_Conversion.cs @@ -410,7 +410,11 @@ private BoundExpression MakeConversionNodeCore( isExtensionMethod: oldNodeOpt.IsExtensionMethod, type: rewrittenType); Debug.Assert(_factory.TopLevelMethod is { }); - if (DelegateCreationRewriter.AllowCaching(_factory.Compilation, _factory.TopLevelMethod, _inExpressionLambda, oldNodeOpt, method)) + + if (_factory.Compilation.LanguageVersion >= MessageID.IDS_FeatureCacheStaticMethodGroupConversion.RequiredVersion() + && !_inExpressionLambda // The tree structure / meaning for expression trees should remain untouched. + && _factory.TopLevelMethod.MethodKind != MethodKind.StaticConstructor // Avoid caching twice if people do it manually. + && DelegateCreationRewriter.CanRewrite(method, oldNodeOpt)) { var rewriter = _lazyDelegateCacheRewriter ??= new DelegateCreationRewriter(_factory, _topLevelMethodOrdinal); return rewriter.Rewrite(boundDelegateCreation, method, rewrittenType); From 797e281eb5e45c194bebe6393218fea338b2ca06 Mon Sep 17 00:00:00 2001 From: Paul Chen Date: Wed, 22 Dec 2021 16:00:13 +0800 Subject: [PATCH 17/66] Feedback: Pin affected pre-existing tests to C# 10. --- .../CodeGenConditionalOperatorTests.cs | 46 +-- .../Test/Emit/CodeGen/CodeGenLockTests.cs | 45 +-- .../CodeGenNullCoalescingAssignmentTests.cs | 65 +--- .../EditAndContinue/EditAndContinueTests.cs | 4 +- ...odeGenMethodGroupConversionCachingTests.cs | 342 ++++++++++++++++++ .../Semantic/Semantics/DelegateTypeTests.cs | 191 +++++----- .../Symbol/Symbols/ExtensionMethodTests.cs | 36 +- .../Test/WinRT/CodeGen/WinMdEventTests.cs | 112 ++---- .../ExpressionCompilerTests.cs | 83 +---- 9 files changed, 546 insertions(+), 378 deletions(-) diff --git a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenConditionalOperatorTests.cs b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenConditionalOperatorTests.cs index 8afa85a95bb7f..3daeccaa04c99 100644 --- a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenConditionalOperatorTests.cs +++ b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenConditionalOperatorTests.cs @@ -1102,11 +1102,11 @@ static int M() return 0; } }"; - var comp = CompileAndVerify(source); + var comp = CompileAndVerify(source, parseOptions: TestOptions.Regular10); comp.VerifyDiagnostics(); comp.VerifyIL("C.Main", @" { - // Code size 85 (0x55) + // Code size 55 (0x37) .maxstack 3 .locals init (System.Func V_0) //f IL_0000: ldc.i4.1 @@ -1115,33 +1115,21 @@ .locals init (System.Func V_0) //f IL_0003: ldloc.0 IL_0004: call ""void System.Console.WriteLine(object)"" IL_0009: dup - IL_000a: brtrue.s IL_0029 - IL_000c: ldsfld ""System.Func C.<>O.<0>__M"" - IL_0011: dup - IL_0012: brtrue.s IL_002a - IL_0014: pop - IL_0015: ldnull - IL_0016: ldftn ""int C.M()"" - IL_001c: newobj ""System.Func..ctor(object, System.IntPtr)"" - IL_0021: dup - IL_0022: stsfld ""System.Func C.<>O.<0>__M"" - IL_0027: br.s IL_002a - IL_0029: ldloc.0 - IL_002a: call ""void System.Console.WriteLine(object)"" - IL_002f: brtrue.s IL_0034 - IL_0031: ldloc.0 - IL_0032: br.s IL_004f - IL_0034: ldsfld ""System.Func C.<>O.<0>__M"" - IL_0039: dup - IL_003a: brtrue.s IL_004f - IL_003c: pop - IL_003d: ldnull - IL_003e: ldftn ""int C.M()"" - IL_0044: newobj ""System.Func..ctor(object, System.IntPtr)"" - IL_0049: dup - IL_004a: stsfld ""System.Func C.<>O.<0>__M"" - IL_004f: call ""void System.Console.WriteLine(object)"" - IL_0054: ret + IL_000a: brtrue.s IL_001a + IL_000c: ldnull + IL_000d: ldftn ""int C.M()"" + IL_0013: newobj ""System.Func..ctor(object, System.IntPtr)"" + IL_0018: br.s IL_001b + IL_001a: ldloc.0 + IL_001b: call ""void System.Console.WriteLine(object)"" + IL_0020: brtrue.s IL_0025 + IL_0022: ldloc.0 + IL_0023: br.s IL_0031 + IL_0025: ldnull + IL_0026: ldftn ""int C.M()"" + IL_002c: newobj ""System.Func..ctor(object, System.IntPtr)"" + IL_0031: call ""void System.Console.WriteLine(object)"" + IL_0036: ret } "); } diff --git a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenLockTests.cs b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenLockTests.cs index 85a6e48ec2ea6..f873d1ef0c65b 100644 --- a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenLockTests.cs +++ b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenLockTests.cs @@ -543,42 +543,35 @@ static partial void PM(int p2) } "; - CompileAndVerify(text).VerifyIL("Test.Main", @" + CompileAndVerify(text, parseOptions: TestOptions.Regular10).VerifyIL("Test.Main", @" { - // Code size 51 (0x33) + // Code size 36 (0x24) .maxstack 2 .locals init (D V_0, bool V_1) - IL_0000: ldsfld ""D Test.<>O.<0>__PM"" - IL_0005: dup - IL_0006: brtrue.s IL_001b - IL_0008: pop - IL_0009: ldnull - IL_000a: ldftn ""void Test.PM(int)"" - IL_0010: newobj ""D..ctor(object, System.IntPtr)"" - IL_0015: dup - IL_0016: stsfld ""D Test.<>O.<0>__PM"" - IL_001b: stloc.0 - IL_001c: ldc.i4.0 - IL_001d: stloc.1 + IL_0000: ldnull + IL_0001: ldftn ""void Test.PM(int)"" + IL_0007: newobj ""D..ctor(object, System.IntPtr)"" + IL_000c: stloc.0 + IL_000d: ldc.i4.0 + IL_000e: stloc.1 .try { - IL_001e: ldloc.0 - IL_001f: ldloca.s V_1 - IL_0021: call ""void System.Threading.Monitor.Enter(object, ref bool)"" - IL_0026: leave.s IL_0032 + IL_000f: ldloc.0 + IL_0010: ldloca.s V_1 + IL_0012: call ""void System.Threading.Monitor.Enter(object, ref bool)"" + IL_0017: leave.s IL_0023 } finally { - IL_0028: ldloc.1 - IL_0029: brfalse.s IL_0031 - IL_002b: ldloc.0 - IL_002c: call ""void System.Threading.Monitor.Exit(object)"" - IL_0031: endfinally + IL_0019: ldloc.1 + IL_001a: brfalse.s IL_0022 + IL_001c: ldloc.0 + IL_001d: call ""void System.Threading.Monitor.Exit(object)"" + IL_0022: endfinally } - IL_0032: ret -} -"); + IL_0023: ret +}"); } [Fact] diff --git a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenNullCoalescingAssignmentTests.cs b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenNullCoalescingAssignmentTests.cs index 21ecfeba9bc78..0c30cfd082de4 100644 --- a/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenNullCoalescingAssignmentTests.cs +++ b/src/Compilers/CSharp/Test/Emit/CodeGen/CodeGenNullCoalescingAssignmentTests.cs @@ -698,12 +698,12 @@ public class C public static void Main() { Action a = null; - (a ??= new Action(TestMethod))(); + (a ??= TestMethod)(); (a ??= () => {})(); } static void TestMethod() => Console.WriteLine(""In TestMethod""); } -", expectedOutput: @" +", parseOptions: TestOptions.Regular10, expectedOutput: @" In TestMethod In TestMethod ").VerifyIL("C.Main()", @" @@ -744,67 +744,6 @@ .locals init (System.Action V_0) //a "); } - [Fact] - public void ValidRHS1() - { - CompileAndVerify(@" -using System; -public class C -{ - public static void Main() - { - Action a = null; - (a ??= TestMethod)(); - (a ??= () => {})(); - } - static void TestMethod() => Console.WriteLine(""In TestMethod""); -} -", expectedOutput: @" -In TestMethod -In TestMethod -").VerifyIL("C.Main()", @" -{ - // Code size 85 (0x55) - .maxstack 2 - .locals init (System.Action V_0) //a - IL_0000: ldnull - IL_0001: stloc.0 - IL_0002: ldloc.0 - IL_0003: dup - IL_0004: brtrue.s IL_0024 - IL_0006: pop - IL_0007: ldsfld ""System.Action C.<>O.<0>__TestMethod"" - IL_000c: dup - IL_000d: brtrue.s IL_0022 - IL_000f: pop - IL_0010: ldnull - IL_0011: ldftn ""void C.TestMethod()"" - IL_0017: newobj ""System.Action..ctor(object, System.IntPtr)"" - IL_001c: dup - IL_001d: stsfld ""System.Action C.<>O.<0>__TestMethod"" - IL_0022: dup - IL_0023: stloc.0 - IL_0024: callvirt ""void System.Action.Invoke()"" - IL_0029: ldloc.0 - IL_002a: dup - IL_002b: brtrue.s IL_004f - IL_002d: pop - IL_002e: ldsfld ""System.Action C.<>c.<>9__0_0"" - IL_0033: dup - IL_0034: brtrue.s IL_004d - IL_0036: pop - IL_0037: ldsfld ""C.<>c C.<>c.<>9"" - IL_003c: ldftn ""void C.<>c.
b__0_0()"" - IL_0042: newobj ""System.Action..ctor(object, System.IntPtr)"" - IL_0047: dup - IL_0048: stsfld ""System.Action C.<>c.<>9__0_0"" - IL_004d: dup - IL_004e: stloc.0 - IL_004f: callvirt ""void System.Action.Invoke()"" - IL_0054: ret -}"); - } - [Fact] public void InvalidRHS() { diff --git a/src/Compilers/CSharp/Test/Emit/Emit/EditAndContinue/EditAndContinueTests.cs b/src/Compilers/CSharp/Test/Emit/Emit/EditAndContinue/EditAndContinueTests.cs index 1f3744ee3903a..083f7694649bd 100644 --- a/src/Compilers/CSharp/Test/Emit/Emit/EditAndContinue/EditAndContinueTests.cs +++ b/src/Compilers/CSharp/Test/Emit/Emit/EditAndContinue/EditAndContinueTests.cs @@ -8849,7 +8849,7 @@ static void M2(object o) }"; var compilationPIA = CreateCompilation(sourcePIA, options: TestOptions.DebugDll); var referencePIA = compilationPIA.EmitToImageReference(embedInteropTypes: true); - var compilation0 = CreateCompilation(source0, options: TestOptions.DebugDll, references: new MetadataReference[] { referencePIA, CSharpRef }); + var compilation0 = CreateCompilation(source0, parseOptions: TestOptions.Regular10, options: TestOptions.DebugDll, references: new MetadataReference[] { referencePIA, CSharpRef }); var compilation1A = compilation0.WithSource(source1A); var compilation1B = compilation0.WithSource(source1B); @@ -8863,7 +8863,7 @@ static void M2(object o) using var md0 = ModuleMetadata.CreateFromImage(bytes0); var reader0 = md0.MetadataReader; - CheckNames(reader0, reader0.GetTypeDefNames(), "", "C`1", "IA", "IC", "S", "<>O"); + CheckNames(reader0, reader0.GetTypeDefNames(), "", "C`1", "IA", "IC", "S"); var generation0 = EmitBaseline.CreateInitialBaseline(md0, methodData0.EncDebugInfoProvider()); diff --git a/src/Compilers/CSharp/Test/Emit2/CodeGen/CodeGenMethodGroupConversionCachingTests.cs b/src/Compilers/CSharp/Test/Emit2/CodeGen/CodeGenMethodGroupConversionCachingTests.cs index f7c9ad26c3b63..8cf095622c84a 100644 --- a/src/Compilers/CSharp/Test/Emit2/CodeGen/CodeGenMethodGroupConversionCachingTests.cs +++ b/src/Compilers/CSharp/Test/Emit2/CodeGen/CodeGenMethodGroupConversionCachingTests.cs @@ -4437,4 +4437,346 @@ .locals init (System.Func V_0) //f "); } + [Fact] + public void WinMdEventAssignment() + { + var source = @" +class C +{ + public event System.Action Instance; + public static event System.Action Static; +} + +class D +{ + C c; + + void InstanceAdd() + { + c.Instance += Action; + } + + void InstanceRemove() + { + c.Instance -= Action; + } + + static void StaticAdd() + { + C.Static += Action; + } + + static void StaticRemove() + { + C.Static -= Action; + } + + static void Action() + { + } +} +"; + var verifier = CompileAndVerifyWithWinRt(source, options: TestOptions.ReleaseWinMD); + + verifier.VerifyIL("D.InstanceAdd", @" +{ + // Code size 64 (0x40) + .maxstack 4 + .locals init (C V_0) + IL_0000: ldarg.0 + IL_0001: ldfld ""C D.c"" + IL_0006: stloc.0 + IL_0007: ldloc.0 + IL_0008: ldftn ""System.Runtime.InteropServices.WindowsRuntime.EventRegistrationToken C.Instance.add"" + IL_000e: newobj ""System.Func..ctor(object, System.IntPtr)"" + IL_0013: ldloc.0 + IL_0014: ldftn ""void C.Instance.remove"" + IL_001a: newobj ""System.Action..ctor(object, System.IntPtr)"" + IL_001f: ldsfld ""System.Action D.<>O.<0>__Action"" + IL_0024: dup + IL_0025: brtrue.s IL_003a + IL_0027: pop + IL_0028: ldnull + IL_0029: ldftn ""void D.Action()"" + IL_002f: newobj ""System.Action..ctor(object, System.IntPtr)"" + IL_0034: dup + IL_0035: stsfld ""System.Action D.<>O.<0>__Action"" + IL_003a: call ""void System.Runtime.InteropServices.WindowsRuntime.WindowsRuntimeMarshal.AddEventHandler(System.Func, System.Action, System.Action)"" + IL_003f: ret +}"); + + verifier.VerifyIL("D.InstanceRemove", @" +{ + // Code size 50 (0x32) + .maxstack 3 + IL_0000: ldarg.0 + IL_0001: ldfld ""C D.c"" + IL_0006: ldftn ""void C.Instance.remove"" + IL_000c: newobj ""System.Action..ctor(object, System.IntPtr)"" + IL_0011: ldsfld ""System.Action D.<>O.<0>__Action"" + IL_0016: dup + IL_0017: brtrue.s IL_002c + IL_0019: pop + IL_001a: ldnull + IL_001b: ldftn ""void D.Action()"" + IL_0021: newobj ""System.Action..ctor(object, System.IntPtr)"" + IL_0026: dup + IL_0027: stsfld ""System.Action D.<>O.<0>__Action"" + IL_002c: call ""void System.Runtime.InteropServices.WindowsRuntime.WindowsRuntimeMarshal.RemoveEventHandler(System.Action, System.Action)"" + IL_0031: ret +}"); + verifier.VerifyIL("D.StaticAdd", @" +{ + // Code size 57 (0x39) + .maxstack 4 + IL_0000: ldnull + IL_0001: ldftn ""System.Runtime.InteropServices.WindowsRuntime.EventRegistrationToken C.Static.add"" + IL_0007: newobj ""System.Func..ctor(object, System.IntPtr)"" + IL_000c: ldnull + IL_000d: ldftn ""void C.Static.remove"" + IL_0013: newobj ""System.Action..ctor(object, System.IntPtr)"" + IL_0018: ldsfld ""System.Action D.<>O.<0>__Action"" + IL_001d: dup + IL_001e: brtrue.s IL_0033 + IL_0020: pop + IL_0021: ldnull + IL_0022: ldftn ""void D.Action()"" + IL_0028: newobj ""System.Action..ctor(object, System.IntPtr)"" + IL_002d: dup + IL_002e: stsfld ""System.Action D.<>O.<0>__Action"" + IL_0033: call ""void System.Runtime.InteropServices.WindowsRuntime.WindowsRuntimeMarshal.AddEventHandler(System.Func, System.Action, System.Action)"" + IL_0038: ret +}"); + + verifier.VerifyIL("D.StaticRemove", @" +{ + // Code size 45 (0x2d) + .maxstack 3 + IL_0000: ldnull + IL_0001: ldftn ""void C.Static.remove"" + IL_0007: newobj ""System.Action..ctor(object, System.IntPtr)"" + IL_000c: ldsfld ""System.Action D.<>O.<0>__Action"" + IL_0011: dup + IL_0012: brtrue.s IL_0027 + IL_0014: pop + IL_0015: ldnull + IL_0016: ldftn ""void D.Action()"" + IL_001c: newobj ""System.Action..ctor(object, System.IntPtr)"" + IL_0021: dup + IL_0022: stsfld ""System.Action D.<>O.<0>__Action"" + IL_0027: call ""void System.Runtime.InteropServices.WindowsRuntime.WindowsRuntimeMarshal.RemoveEventHandler(System.Action, System.Action)"" + IL_002c: ret +}"); + } + + [Fact] + public void WinMdEventFieldAssignment() + { + var source = @" +class C +{ + public event System.Action Instance; + public static event System.Action Static; + + void InstanceAssign() + { + Instance = Action; + } + + static void StaticAssign() + { + Static = Action; + } + + static void Action() + { + } +} +"; + var verifier = CompileAndVerifyWithWinRt(source, options: TestOptions.ReleaseWinMD); + + verifier.VerifyIL("C.InstanceAssign", @" +{ + // Code size 74 (0x4a) + .maxstack 4 + IL_0000: ldarg.0 + IL_0001: ldftn ""void C.Instance.remove"" + IL_0007: newobj ""System.Action..ctor(object, System.IntPtr)"" + IL_000c: call ""void System.Runtime.InteropServices.WindowsRuntime.WindowsRuntimeMarshal.RemoveAllEventHandlers(System.Action)"" + IL_0011: ldarg.0 + IL_0012: ldftn ""System.Runtime.InteropServices.WindowsRuntime.EventRegistrationToken C.Instance.add"" + IL_0018: newobj ""System.Func..ctor(object, System.IntPtr)"" + IL_001d: ldarg.0 + IL_001e: ldftn ""void C.Instance.remove"" + IL_0024: newobj ""System.Action..ctor(object, System.IntPtr)"" + IL_0029: ldsfld ""System.Action C.<>O.<0>__Action"" + IL_002e: dup + IL_002f: brtrue.s IL_0044 + IL_0031: pop + IL_0032: ldnull + IL_0033: ldftn ""void C.Action()"" + IL_0039: newobj ""System.Action..ctor(object, System.IntPtr)"" + IL_003e: dup + IL_003f: stsfld ""System.Action C.<>O.<0>__Action"" + IL_0044: call ""void System.Runtime.InteropServices.WindowsRuntime.WindowsRuntimeMarshal.AddEventHandler(System.Func, System.Action, System.Action)"" + IL_0049: ret +}"); + + verifier.VerifyIL("C.StaticAssign", @" +{ + // Code size 74 (0x4a) + .maxstack 4 + IL_0000: ldnull + IL_0001: ldftn ""void C.Static.remove"" + IL_0007: newobj ""System.Action..ctor(object, System.IntPtr)"" + IL_000c: call ""void System.Runtime.InteropServices.WindowsRuntime.WindowsRuntimeMarshal.RemoveAllEventHandlers(System.Action)"" + IL_0011: ldnull + IL_0012: ldftn ""System.Runtime.InteropServices.WindowsRuntime.EventRegistrationToken C.Static.add"" + IL_0018: newobj ""System.Func..ctor(object, System.IntPtr)"" + IL_001d: ldnull + IL_001e: ldftn ""void C.Static.remove"" + IL_0024: newobj ""System.Action..ctor(object, System.IntPtr)"" + IL_0029: ldsfld ""System.Action C.<>O.<0>__Action"" + IL_002e: dup + IL_002f: brtrue.s IL_0044 + IL_0031: pop + IL_0032: ldnull + IL_0033: ldftn ""void C.Action()"" + IL_0039: newobj ""System.Action..ctor(object, System.IntPtr)"" + IL_003e: dup + IL_003f: stsfld ""System.Action C.<>O.<0>__Action"" + IL_0044: call ""void System.Runtime.InteropServices.WindowsRuntime.WindowsRuntimeMarshal.AddEventHandler(System.Func, System.Action, System.Action)"" + IL_0049: ret +}"); + } + + [Fact] + public void LockDelegate() + { + var text = +@" +delegate void D(string p1); +partial class Test +{ + public static void Main() + { + D d1; + lock (d1 = PM) + { + d1(""PASS""); + } + } + static partial void PM(string p2); + static partial void PM(string p2) + { + System.Console.WriteLine(p2); + } +} +"; + + CompileAndVerify(text, expectedOutput: PASS).VerifyIL("Test.Main", @" +{ + // Code size 64 (0x40) + .maxstack 2 + .locals init (D V_0, //d1 + D V_1, + bool V_2) + IL_0000: ldsfld ""D Test.<>O.<0>__PM"" + IL_0005: dup + IL_0006: brtrue.s IL_001b + IL_0008: pop + IL_0009: ldnull + IL_000a: ldftn ""void Test.PM(string)"" + IL_0010: newobj ""D..ctor(object, System.IntPtr)"" + IL_0015: dup + IL_0016: stsfld ""D Test.<>O.<0>__PM"" + IL_001b: dup + IL_001c: stloc.0 + IL_001d: stloc.1 + IL_001e: ldc.i4.0 + IL_001f: stloc.2 + .try + { + IL_0020: ldloc.1 + IL_0021: ldloca.s V_2 + IL_0023: call ""void System.Threading.Monitor.Enter(object, ref bool)"" + IL_0028: ldloc.0 + IL_0029: ldstr ""PASS"" + IL_002e: callvirt ""void D.Invoke(string)"" + IL_0033: leave.s IL_003f + } + finally + { + IL_0035: ldloc.2 + IL_0036: brfalse.s IL_003e + IL_0038: ldloc.1 + IL_0039: call ""void System.Threading.Monitor.Exit(object)"" + IL_003e: endfinally + } + IL_003f: ret +} +"); + } + + [Fact] + public void NullCoalescingAssignmentValidRHS() + { + CompileAndVerify(@" +using System; +public class C +{ + public static void Main() + { + Action a = null; + (a ??= TestMethod)(); + (a ??= () => {})(); + } + static void TestMethod() => Console.WriteLine(""In TestMethod""); +} +", expectedOutput: @" +In TestMethod +In TestMethod +").VerifyIL("C.Main()", @" +{ + // Code size 85 (0x55) + .maxstack 2 + .locals init (System.Action V_0) //a + IL_0000: ldnull + IL_0001: stloc.0 + IL_0002: ldloc.0 + IL_0003: dup + IL_0004: brtrue.s IL_0024 + IL_0006: pop + IL_0007: ldsfld ""System.Action C.<>O.<0>__TestMethod"" + IL_000c: dup + IL_000d: brtrue.s IL_0022 + IL_000f: pop + IL_0010: ldnull + IL_0011: ldftn ""void C.TestMethod()"" + IL_0017: newobj ""System.Action..ctor(object, System.IntPtr)"" + IL_001c: dup + IL_001d: stsfld ""System.Action C.<>O.<0>__TestMethod"" + IL_0022: dup + IL_0023: stloc.0 + IL_0024: callvirt ""void System.Action.Invoke()"" + IL_0029: ldloc.0 + IL_002a: dup + IL_002b: brtrue.s IL_004f + IL_002d: pop + IL_002e: ldsfld ""System.Action C.<>c.<>9__0_0"" + IL_0033: dup + IL_0034: brtrue.s IL_004d + IL_0036: pop + IL_0037: ldsfld ""C.<>c C.<>c.<>9"" + IL_003c: ldftn ""void C.<>c.
b__0_0()"" + IL_0042: newobj ""System.Action..ctor(object, System.IntPtr)"" + IL_0047: dup + IL_0048: stsfld ""System.Action C.<>c.<>9__0_0"" + IL_004d: dup + IL_004e: stloc.0 + IL_004f: callvirt ""void System.Action.Invoke()"" + IL_0054: ret +}"); + } + } diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/DelegateTypeTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/DelegateTypeTests.cs index 154d101277e69..e7d6a41120f9f 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/DelegateTypeTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/DelegateTypeTests.cs @@ -706,15 +706,15 @@ static void Main() public static IEnumerable GetMethodGroupImplicitConversionData() { return GetMethodGroupData((methodGroupExpression, methodGroupOnly) => - { - int offset = methodGroupExpression.Length - methodGroupOnly.Length; - return new[] - { + { + int offset = methodGroupExpression.Length - methodGroupOnly.Length; + return new[] + { // (6,29): error CS8917: The delegate type could not be inferred. // System.Delegate d = F; Diagnostic(ErrorCode.ERR_CannotInferDelegateType, methodGroupOnly).WithLocation(6, 29 + offset) }; - }); + }); } [Theory] @@ -752,15 +752,15 @@ static void Main() public static IEnumerable GetMethodGroupExplicitConversionData() { return GetMethodGroupData((methodGroupExpression, methodGroupOnly) => - { - int offset = methodGroupExpression.Length - methodGroupOnly.Length; - return new[] - { + { + int offset = methodGroupExpression.Length - methodGroupOnly.Length; + return new[] + { // (6,20): error CS0030: Cannot convert type 'method' to 'Delegate' // object o = (System.Delegate)F; Diagnostic(ErrorCode.ERR_NoExplicitConv, $"(System.Delegate){methodGroupExpression}").WithArguments("method", "System.Delegate").WithLocation(6, 20) }; - }); + }); } [Theory] @@ -6958,12 +6958,63 @@ static void Main() // var d3 = delegate () { }; Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion9, "delegate () { }").WithArguments("inferred delegate type", "10.0").WithLocation(10, 18)); - comp = CreateCompilation(new[] { source, s_utils }, options: TestOptions.DebugExe); + comp = CreateCompilation(new[] { source, s_utils }, parseOptions: TestOptions.Regular10, options: TestOptions.DebugExe); comp.VerifyDiagnostics(); var verifier = CompileAndVerify(comp, expectedOutput: @"System.Action System.Action +System.Action"); + verifier.VerifyIL("Program.Main", +@"{ + // Code size 100 (0x64) + .maxstack 2 + .locals init (System.Action V_0, //d1 + System.Action V_1, //d2 + System.Action V_2) //d3 + IL_0000: nop + IL_0001: ldnull + IL_0002: ldftn ""void Program.Main()"" + IL_0008: newobj ""System.Action..ctor(object, System.IntPtr)"" + IL_000d: stloc.0 + IL_000e: ldloc.0 + IL_000f: call ""void Program.Report(System.Delegate)"" + IL_0014: nop + IL_0015: ldsfld ""System.Action Program.<>c.<>9__0_0"" + IL_001a: dup + IL_001b: brtrue.s IL_0034 + IL_001d: pop + IL_001e: ldsfld ""Program.<>c Program.<>c.<>9"" + IL_0023: ldftn ""void Program.<>c.
b__0_0()"" + IL_0029: newobj ""System.Action..ctor(object, System.IntPtr)"" + IL_002e: dup + IL_002f: stsfld ""System.Action Program.<>c.<>9__0_0"" + IL_0034: stloc.1 + IL_0035: ldloc.1 + IL_0036: call ""void Program.Report(System.Delegate)"" + IL_003b: nop + IL_003c: ldsfld ""System.Action Program.<>c.<>9__0_1"" + IL_0041: dup + IL_0042: brtrue.s IL_005b + IL_0044: pop + IL_0045: ldsfld ""Program.<>c Program.<>c.<>9"" + IL_004a: ldftn ""void Program.<>c.
b__0_1()"" + IL_0050: newobj ""System.Action..ctor(object, System.IntPtr)"" + IL_0055: dup + IL_0056: stsfld ""System.Action Program.<>c.<>9__0_1"" + IL_005b: stloc.2 + IL_005c: ldloc.2 + IL_005d: call ""void Program.Report(System.Delegate)"" + IL_0062: nop + IL_0063: ret +}"); + + comp = CreateCompilation(new[] { source, s_utils }, options: TestOptions.DebugExe); + comp.VerifyDiagnostics(); + + verifier = CompileAndVerify(comp, expectedOutput: +@"System.Action +System.Action System.Action"); verifier.VerifyIL("Program.Main", @"{ @@ -8066,7 +8117,7 @@ static void Main() static void Report(Delegate d) => Console.WriteLine(d.GetType()); }"; - var comp = CreateCompilation(source, options: TestOptions.ReleaseExe); + var comp = CreateCompilation(source, parseOptions: TestOptions.Regular10, options: TestOptions.ReleaseExe); comp.VerifyDiagnostics(); var verifier = CompileAndVerify(comp, expectedOutput: @@ -8076,39 +8127,21 @@ static void Main() "); verifier.VerifyIL("Program.Main", @"{ - // Code size 97 (0x61) + // Code size 52 (0x34) .maxstack 2 - IL_0000: ldsfld ""System.Func Program.<>O.<0>__F1"" - IL_0005: dup - IL_0006: brtrue.s IL_001b - IL_0008: pop - IL_0009: ldnull - IL_000a: ldftn ""int Program.F1()"" - IL_0010: newobj ""System.Func..ctor(object, System.IntPtr)"" - IL_0015: dup - IL_0016: stsfld ""System.Func Program.<>O.<0>__F1"" - IL_001b: call ""void Program.Report(System.Delegate)"" - IL_0020: ldsfld "" Program.<>O.<1>__F2"" - IL_0025: dup - IL_0026: brtrue.s IL_003b - IL_0028: pop - IL_0029: ldnull - IL_002a: ldftn ""ref int Program.F2()"" - IL_0030: newobj ""<>F{00000001}..ctor(object, System.IntPtr)"" - IL_0035: dup - IL_0036: stsfld "" Program.<>O.<1>__F2"" - IL_003b: call ""void Program.Report(System.Delegate)"" - IL_0040: ldsfld "" Program.<>O.<2>__F3"" - IL_0045: dup - IL_0046: brtrue.s IL_005b - IL_0048: pop - IL_0049: ldnull - IL_004a: ldftn ""ref readonly int Program.F3()"" - IL_0050: newobj ""<>F{00000003}..ctor(object, System.IntPtr)"" - IL_0055: dup - IL_0056: stsfld "" Program.<>O.<2>__F3"" - IL_005b: call ""void Program.Report(System.Delegate)"" - IL_0060: ret + IL_0000: ldnull + IL_0001: ldftn ""int Program.F1()"" + IL_0007: newobj ""System.Func..ctor(object, System.IntPtr)"" + IL_000c: call ""void Program.Report(System.Delegate)"" + IL_0011: ldnull + IL_0012: ldftn ""ref int Program.F2()"" + IL_0018: newobj ""<>F{00000001}..ctor(object, System.IntPtr)"" + IL_001d: call ""void Program.Report(System.Delegate)"" + IL_0022: ldnull + IL_0023: ldftn ""ref readonly int Program.F3()"" + IL_0029: newobj ""<>F{00000003}..ctor(object, System.IntPtr)"" + IL_002e: call ""void Program.Report(System.Delegate)"" + IL_0033: ret }"); } @@ -8305,7 +8338,7 @@ static void Main() static void Report(Delegate d) => Console.WriteLine(d.GetType()); }"; - var comp = CreateCompilation(source, options: TestOptions.ReleaseExe); + var comp = CreateCompilation(source, parseOptions: TestOptions.Regular10, options: TestOptions.ReleaseExe); comp.VerifyDiagnostics(); var verifier = CompileAndVerify(comp, expectedOutput: @@ -8316,49 +8349,25 @@ static void Main() "); verifier.VerifyIL("Program.Main", @"{ - // Code size 129 (0x81) + // Code size 69 (0x45) .maxstack 2 - IL_0000: ldsfld ""System.Action Program.<>O.<0>__M1"" - IL_0005: dup - IL_0006: brtrue.s IL_001b - IL_0008: pop - IL_0009: ldnull - IL_000a: ldftn ""void Program.M1(int)"" - IL_0010: newobj ""System.Action..ctor(object, System.IntPtr)"" - IL_0015: dup - IL_0016: stsfld ""System.Action Program.<>O.<0>__M1"" - IL_001b: call ""void Program.Report(System.Delegate)"" - IL_0020: ldsfld "" Program.<>O.<1>__M2"" - IL_0025: dup - IL_0026: brtrue.s IL_003b - IL_0028: pop - IL_0029: ldnull - IL_002a: ldftn ""void Program.M2(out int)"" - IL_0030: newobj ""<>A{00000002}..ctor(object, System.IntPtr)"" - IL_0035: dup - IL_0036: stsfld "" Program.<>O.<1>__M2"" - IL_003b: call ""void Program.Report(System.Delegate)"" - IL_0040: ldsfld "" Program.<>O.<2>__M3"" - IL_0045: dup - IL_0046: brtrue.s IL_005b - IL_0048: pop - IL_0049: ldnull - IL_004a: ldftn ""void Program.M3(ref int)"" - IL_0050: newobj ""<>A{00000001}..ctor(object, System.IntPtr)"" - IL_0055: dup - IL_0056: stsfld "" Program.<>O.<2>__M3"" - IL_005b: call ""void Program.Report(System.Delegate)"" - IL_0060: ldsfld "" Program.<>O.<3>__M4"" - IL_0065: dup - IL_0066: brtrue.s IL_007b - IL_0068: pop - IL_0069: ldnull - IL_006a: ldftn ""void Program.M4(in int)"" - IL_0070: newobj ""<>A{00000003}..ctor(object, System.IntPtr)"" - IL_0075: dup - IL_0076: stsfld "" Program.<>O.<3>__M4"" - IL_007b: call ""void Program.Report(System.Delegate)"" - IL_0080: ret + IL_0000: ldnull + IL_0001: ldftn ""void Program.M1(int)"" + IL_0007: newobj ""System.Action..ctor(object, System.IntPtr)"" + IL_000c: call ""void Program.Report(System.Delegate)"" + IL_0011: ldnull + IL_0012: ldftn ""void Program.M2(out int)"" + IL_0018: newobj ""<>A{00000002}..ctor(object, System.IntPtr)"" + IL_001d: call ""void Program.Report(System.Delegate)"" + IL_0022: ldnull + IL_0023: ldftn ""void Program.M3(ref int)"" + IL_0029: newobj ""<>A{00000001}..ctor(object, System.IntPtr)"" + IL_002e: call ""void Program.Report(System.Delegate)"" + IL_0033: ldnull + IL_0034: ldftn ""void Program.M4(in int)"" + IL_003a: newobj ""<>A{00000003}..ctor(object, System.IntPtr)"" + IL_003f: call ""void Program.Report(System.Delegate)"" + IL_0044: ret }"); } @@ -8758,7 +8767,7 @@ static void Main() static void Report(Delegate d) => Console.WriteLine(d.GetType()); }"; - var comp = CreateCompilation(source, options: TestOptions.ReleaseExe); + var comp = CreateCompilation(source, parseOptions: TestOptions.Regular10, options: TestOptions.ReleaseExe); var verifier = CompileAndVerify(comp, validator: validator, expectedOutput: "D"); static void validator(PEAssembly assembly) @@ -8766,7 +8775,7 @@ static void validator(PEAssembly assembly) var reader = assembly.GetMetadataReader(); var actualTypes = reader.GetTypeDefNames().Select(h => reader.GetString(h)).ToArray(); - string[] expectedTypes = new[] { "", "D", "Program", "<>O" }; + string[] expectedTypes = new[] { "", "D", "Program", }; AssertEx.Equal(expectedTypes, actualTypes); } } @@ -8799,7 +8808,7 @@ static void Main() static void Report(Delegate d) => Console.WriteLine(d.GetType()); }"; - var comp = CreateCompilation(source, options: TestOptions.ReleaseExe); + var comp = CreateCompilation(source, parseOptions: TestOptions.Regular10, options: TestOptions.ReleaseExe); var verifier = CompileAndVerify(comp, validator: validator, expectedOutput: @"<>A{00000001}`2[System.Object,System.Object] D2 @@ -8811,7 +8820,7 @@ static void validator(PEAssembly assembly) var reader = assembly.GetMetadataReader(); var actualTypes = reader.GetTypeDefNames().Select(h => reader.GetString(h)).ToArray(); - string[] expectedTypes = new[] { "", "<>A{00000001}`2", "<>A{00000009}`2", "D2", "D4", "Program", "<>O", "<>c", }; + string[] expectedTypes = new[] { "", "<>A{00000001}`2", "<>A{00000009}`2", "D2", "D4", "Program", "<>c", }; AssertEx.Equal(expectedTypes, actualTypes); } } diff --git a/src/Compilers/CSharp/Test/Symbol/Symbols/ExtensionMethodTests.cs b/src/Compilers/CSharp/Test/Symbol/Symbols/ExtensionMethodTests.cs index 1c2b76d14abc2..22ccf8861adc4 100644 --- a/src/Compilers/CSharp/Test/Symbol/Symbols/ExtensionMethodTests.cs +++ b/src/Compilers/CSharp/Test/Symbol/Symbols/ExtensionMethodTests.cs @@ -2240,34 +2240,22 @@ static string Combine(string s1, string s2) }"; var code = @"{ - // Code size 72 (0x48) + // Code size 42 (0x2a) .maxstack 3 IL_0000: ldarg.0 IL_0001: ldc.i4.1 IL_0002: call ""System.Collections.Generic.IEnumerable System.Linq.Enumerable.Skip(System.Collections.Generic.IEnumerable, int)"" - IL_0007: ldsfld ""System.Func C.<>O.<0>__Filter"" - IL_000c: dup - IL_000d: brtrue.s IL_0022 - IL_000f: pop - IL_0010: ldnull - IL_0011: ldftn ""bool C.Filter(string)"" - IL_0017: newobj ""System.Func..ctor(object, System.IntPtr)"" - IL_001c: dup - IL_001d: stsfld ""System.Func C.<>O.<0>__Filter"" - IL_0022: call ""System.Collections.Generic.IEnumerable System.Linq.Enumerable.Where(System.Collections.Generic.IEnumerable, System.Func)"" - IL_0027: ldsfld ""System.Func C.<>O.<1>__Combine"" - IL_002c: dup - IL_002d: brtrue.s IL_0042 - IL_002f: pop - IL_0030: ldnull - IL_0031: ldftn ""string C.Combine(string, string)"" - IL_0037: newobj ""System.Func..ctor(object, System.IntPtr)"" - IL_003c: dup - IL_003d: stsfld ""System.Func C.<>O.<1>__Combine"" - IL_0042: call ""string System.Linq.Enumerable.Aggregate(System.Collections.Generic.IEnumerable, System.Func)"" - IL_0047: ret -}"; - var compilation = CompileAndVerify(source, expectedOutput: "orange, apple"); + IL_0007: ldnull + IL_0008: ldftn ""bool C.Filter(string)"" + IL_000e: newobj ""System.Func..ctor(object, System.IntPtr)"" + IL_0013: call ""System.Collections.Generic.IEnumerable System.Linq.Enumerable.Where(System.Collections.Generic.IEnumerable, System.Func)"" + IL_0018: ldnull + IL_0019: ldftn ""string C.Combine(string, string)"" + IL_001f: newobj ""System.Func..ctor(object, System.IntPtr)"" + IL_0024: call ""string System.Linq.Enumerable.Aggregate(System.Collections.Generic.IEnumerable, System.Func)"" + IL_0029: ret +}"; + var compilation = CompileAndVerify(source, parseOptions: TestOptions.Regular10, expectedOutput: "orange, apple"); compilation.VerifyIL("C.F", code); compilation.VerifyIL("C.G", code); } diff --git a/src/Compilers/CSharp/Test/WinRT/CodeGen/WinMdEventTests.cs b/src/Compilers/CSharp/Test/WinRT/CodeGen/WinMdEventTests.cs index 086d92b9d16ea..10b823874e0c7 100644 --- a/src/Compilers/CSharp/Test/WinRT/CodeGen/WinMdEventTests.cs +++ b/src/Compilers/CSharp/Test/WinRT/CodeGen/WinMdEventTests.cs @@ -277,11 +277,11 @@ static void Action() } } "; - var verifier = CompileAndVerifyWithWinRt(source, options: TestOptions.ReleaseWinMD); + var verifier = CompileAndVerifyWithWinRt(source, parseOptions: TestOptions.Regular10, options: TestOptions.ReleaseWinMD); verifier.VerifyIL("D.InstanceAdd", @" { - // Code size 64 (0x40) + // Code size 49 (0x31) .maxstack 4 .locals init (C V_0) IL_0000: ldarg.0 @@ -293,42 +293,30 @@ .locals init (C V_0) IL_0013: ldloc.0 IL_0014: ldftn ""void C.Instance.remove"" IL_001a: newobj ""System.Action..ctor(object, System.IntPtr)"" - IL_001f: ldsfld ""System.Action D.<>O.<0>__Action"" - IL_0024: dup - IL_0025: brtrue.s IL_003a - IL_0027: pop - IL_0028: ldnull - IL_0029: ldftn ""void D.Action()"" - IL_002f: newobj ""System.Action..ctor(object, System.IntPtr)"" - IL_0034: dup - IL_0035: stsfld ""System.Action D.<>O.<0>__Action"" - IL_003a: call ""void System.Runtime.InteropServices.WindowsRuntime.WindowsRuntimeMarshal.AddEventHandler(System.Func, System.Action, System.Action)"" - IL_003f: ret + IL_001f: ldnull + IL_0020: ldftn ""void D.Action()"" + IL_0026: newobj ""System.Action..ctor(object, System.IntPtr)"" + IL_002b: call ""void System.Runtime.InteropServices.WindowsRuntime.WindowsRuntimeMarshal.AddEventHandler(System.Func, System.Action, System.Action)"" + IL_0030: ret }"); verifier.VerifyIL("D.InstanceRemove", @" { - // Code size 50 (0x32) + // Code size 35 (0x23) .maxstack 3 IL_0000: ldarg.0 IL_0001: ldfld ""C D.c"" IL_0006: ldftn ""void C.Instance.remove"" IL_000c: newobj ""System.Action..ctor(object, System.IntPtr)"" - IL_0011: ldsfld ""System.Action D.<>O.<0>__Action"" - IL_0016: dup - IL_0017: brtrue.s IL_002c - IL_0019: pop - IL_001a: ldnull - IL_001b: ldftn ""void D.Action()"" - IL_0021: newobj ""System.Action..ctor(object, System.IntPtr)"" - IL_0026: dup - IL_0027: stsfld ""System.Action D.<>O.<0>__Action"" - IL_002c: call ""void System.Runtime.InteropServices.WindowsRuntime.WindowsRuntimeMarshal.RemoveEventHandler(System.Action, System.Action)"" - IL_0031: ret + IL_0011: ldnull + IL_0012: ldftn ""void D.Action()"" + IL_0018: newobj ""System.Action..ctor(object, System.IntPtr)"" + IL_001d: call ""void System.Runtime.InteropServices.WindowsRuntime.WindowsRuntimeMarshal.RemoveEventHandler(System.Action, System.Action)"" + IL_0022: ret }"); verifier.VerifyIL("D.StaticAdd", @" { - // Code size 57 (0x39) + // Code size 42 (0x2a) .maxstack 4 IL_0000: ldnull IL_0001: ldftn ""System.Runtime.InteropServices.WindowsRuntime.EventRegistrationToken C.Static.add"" @@ -336,37 +324,25 @@ .maxstack 4 IL_000c: ldnull IL_000d: ldftn ""void C.Static.remove"" IL_0013: newobj ""System.Action..ctor(object, System.IntPtr)"" - IL_0018: ldsfld ""System.Action D.<>O.<0>__Action"" - IL_001d: dup - IL_001e: brtrue.s IL_0033 - IL_0020: pop - IL_0021: ldnull - IL_0022: ldftn ""void D.Action()"" - IL_0028: newobj ""System.Action..ctor(object, System.IntPtr)"" - IL_002d: dup - IL_002e: stsfld ""System.Action D.<>O.<0>__Action"" - IL_0033: call ""void System.Runtime.InteropServices.WindowsRuntime.WindowsRuntimeMarshal.AddEventHandler(System.Func, System.Action, System.Action)"" - IL_0038: ret + IL_0018: ldnull + IL_0019: ldftn ""void D.Action()"" + IL_001f: newobj ""System.Action..ctor(object, System.IntPtr)"" + IL_0024: call ""void System.Runtime.InteropServices.WindowsRuntime.WindowsRuntimeMarshal.AddEventHandler(System.Func, System.Action, System.Action)"" + IL_0029: ret }"); verifier.VerifyIL("D.StaticRemove", @" { - // Code size 45 (0x2d) + // Code size 30 (0x1e) .maxstack 3 IL_0000: ldnull IL_0001: ldftn ""void C.Static.remove"" IL_0007: newobj ""System.Action..ctor(object, System.IntPtr)"" - IL_000c: ldsfld ""System.Action D.<>O.<0>__Action"" - IL_0011: dup - IL_0012: brtrue.s IL_0027 - IL_0014: pop - IL_0015: ldnull - IL_0016: ldftn ""void D.Action()"" - IL_001c: newobj ""System.Action..ctor(object, System.IntPtr)"" - IL_0021: dup - IL_0022: stsfld ""System.Action D.<>O.<0>__Action"" - IL_0027: call ""void System.Runtime.InteropServices.WindowsRuntime.WindowsRuntimeMarshal.RemoveEventHandler(System.Action, System.Action)"" - IL_002c: ret + IL_000c: ldnull + IL_000d: ldftn ""void D.Action()"" + IL_0013: newobj ""System.Action..ctor(object, System.IntPtr)"" + IL_0018: call ""void System.Runtime.InteropServices.WindowsRuntime.WindowsRuntimeMarshal.RemoveEventHandler(System.Action, System.Action)"" + IL_001d: ret }"); } @@ -394,11 +370,11 @@ static void Action() } } "; - var verifier = CompileAndVerifyWithWinRt(source, options: TestOptions.ReleaseWinMD); + var verifier = CompileAndVerifyWithWinRt(source, parseOptions: TestOptions.Regular10, options: TestOptions.ReleaseWinMD); verifier.VerifyIL("C.InstanceAssign", @" { - // Code size 74 (0x4a) + // Code size 59 (0x3b) .maxstack 4 IL_0000: ldarg.0 IL_0001: ldftn ""void C.Instance.remove"" @@ -410,22 +386,16 @@ .maxstack 4 IL_001d: ldarg.0 IL_001e: ldftn ""void C.Instance.remove"" IL_0024: newobj ""System.Action..ctor(object, System.IntPtr)"" - IL_0029: ldsfld ""System.Action C.<>O.<0>__Action"" - IL_002e: dup - IL_002f: brtrue.s IL_0044 - IL_0031: pop - IL_0032: ldnull - IL_0033: ldftn ""void C.Action()"" - IL_0039: newobj ""System.Action..ctor(object, System.IntPtr)"" - IL_003e: dup - IL_003f: stsfld ""System.Action C.<>O.<0>__Action"" - IL_0044: call ""void System.Runtime.InteropServices.WindowsRuntime.WindowsRuntimeMarshal.AddEventHandler(System.Func, System.Action, System.Action)"" - IL_0049: ret + IL_0029: ldnull + IL_002a: ldftn ""void C.Action()"" + IL_0030: newobj ""System.Action..ctor(object, System.IntPtr)"" + IL_0035: call ""void System.Runtime.InteropServices.WindowsRuntime.WindowsRuntimeMarshal.AddEventHandler(System.Func, System.Action, System.Action)"" + IL_003a: ret }"); verifier.VerifyIL("C.StaticAssign", @" { - // Code size 74 (0x4a) + // Code size 59 (0x3b) .maxstack 4 IL_0000: ldnull IL_0001: ldftn ""void C.Static.remove"" @@ -437,17 +407,11 @@ .maxstack 4 IL_001d: ldnull IL_001e: ldftn ""void C.Static.remove"" IL_0024: newobj ""System.Action..ctor(object, System.IntPtr)"" - IL_0029: ldsfld ""System.Action C.<>O.<0>__Action"" - IL_002e: dup - IL_002f: brtrue.s IL_0044 - IL_0031: pop - IL_0032: ldnull - IL_0033: ldftn ""void C.Action()"" - IL_0039: newobj ""System.Action..ctor(object, System.IntPtr)"" - IL_003e: dup - IL_003f: stsfld ""System.Action C.<>O.<0>__Action"" - IL_0044: call ""void System.Runtime.InteropServices.WindowsRuntime.WindowsRuntimeMarshal.AddEventHandler(System.Func, System.Action, System.Action)"" - IL_0049: ret + IL_0029: ldnull + IL_002a: ldftn ""void C.Action()"" + IL_0030: newobj ""System.Action..ctor(object, System.IntPtr)"" + IL_0035: call ""void System.Runtime.InteropServices.WindowsRuntime.WindowsRuntimeMarshal.AddEventHandler(System.Func, System.Action, System.Action)"" + IL_003a: ret }"); } diff --git a/src/ExpressionEvaluator/CSharp/Test/ExpressionCompiler/ExpressionCompilerTests.cs b/src/ExpressionEvaluator/CSharp/Test/ExpressionCompiler/ExpressionCompilerTests.cs index 43efe1cce9d90..9f072a66cb3e8 100644 --- a/src/ExpressionEvaluator/CSharp/Test/ExpressionCompiler/ExpressionCompilerTests.cs +++ b/src/ExpressionEvaluator/CSharp/Test/ExpressionCompiler/ExpressionCompilerTests.cs @@ -1742,7 +1742,7 @@ static void M1() static void M2() { } static void M2(int i) { } }"; - var compilation0 = CreateCompilation(source, options: TestOptions.DebugDll, parseOptions: TestOptions.RegularPreview); + var compilation0 = CreateCompilation(source, parseOptions: TestOptions.Regular10, options: TestOptions.DebugDll); WithRuntimeInstance(compilation0, runtime => { var context = CreateMethodContext( @@ -1759,44 +1759,15 @@ static void M2(int i) { } testData = new CompilationTestData(); - // To remove the else block, please fix https://github.com/dotnet/roslyn/issues/58449 - if (context.Compilation.LanguageVersion == compilation0.LanguageVersion) - { - Assert.True(false); - - context.CompileAssignment( - target: "o", - expr: "M1", - error: out error, - testData: testData); - testData.GetMethodData("<>x.<>m0").VerifyIL( -@"{ - // Code size 29 (0x1d) - .maxstack 2 - .locals init (object V_0) //o - IL_0000: ldsfld ""System.Action <>x.<>O.<0>__M1"" - IL_0005: dup - IL_0006: brtrue.s IL_001b - IL_0008: pop - IL_0009: ldnull - IL_000a: ldftn ""void C.M1()"" - IL_0010: newobj ""System.Action..ctor(object, System.IntPtr)"" - IL_0015: dup - IL_0016: stsfld ""System.Action <>x.<>O.<0>__M1"" - IL_001b: stloc.0 - IL_001c: ret -}"); - } - else - { - Assert.False(false); - - context.CompileAssignment( - target: "o", - expr: "M1", - error: out error, - testData: testData); - testData.GetMethodData("<>x.<>m0").VerifyIL( + // If you see this failing, please fix https://github.com/dotnet/roslyn/issues/58449 + Assert.Equal(compilation0.LanguageVersion, context.Compilation.LanguageVersion); + + context.CompileAssignment( + target: "o", + expr: "M1", + error: out error, + testData: testData); + testData.GetMethodData("<>x.<>m0").VerifyIL( @"{ // Code size 14 (0xe) .maxstack 2 @@ -1807,7 +1778,6 @@ .locals init (object V_0) //o IL_000c: stloc.0 IL_000d: ret }"); - } }); } @@ -4205,7 +4175,7 @@ static void M() { } }"; - var compilation = CreateCompilation(source, options: TestOptions.DebugDll, parseOptions: TestOptions.RegularPreview); + var compilation = CreateCompilation(source, parseOptions: TestOptions.Regular10, options: TestOptions.DebugDll); WithRuntimeInstance(compilation, runtime => { var context = CreateMethodContext( @@ -4214,34 +4184,10 @@ static void M() var testData = new CompilationTestData(); var result = context.CompileExpression("G(F)", out var error, testData); - // To remove the else block, please fix https://github.com/dotnet/roslyn/issues/58449 - if (context.Compilation.LanguageVersion == compilation.LanguageVersion) - { - Assert.True(false); - - testData.GetMethodData("<>x.<>m0").VerifyIL(@" -{ - // Code size 33 (0x21) - .maxstack 2 - IL_0000: ldsfld ""D <>x.<>O.<0>__F"" - IL_0005: dup - IL_0006: brtrue.s IL_001b - IL_0008: pop - IL_0009: ldnull - IL_000a: ldftn ""void C.F()"" - IL_0010: newobj ""D..ctor(object, System.IntPtr)"" - IL_0015: dup - IL_0016: stsfld ""D <>x.<>O.<0>__F"" - IL_001b: call ""void C.G(D)"" - IL_0020: ret -} -"); - } - else - { - Assert.False(false); + // If you see this failing, please fix https://github.com/dotnet/roslyn/issues/58449 + Assert.Equal(compilation.LanguageVersion, context.Compilation.LanguageVersion); - testData.GetMethodData("<>x.<>m0").VerifyIL(@" + testData.GetMethodData("<>x.<>m0").VerifyIL(@" { // Code size 18 (0x12) .maxstack 2 @@ -4252,7 +4198,6 @@ .maxstack 2 IL_0011: ret } "); - } }); } From 17e0056316ff108413cd8ebf2c9d9c0e182db40a Mon Sep 17 00:00:00 2001 From: Paul Chen Date: Wed, 22 Dec 2021 16:19:51 +0800 Subject: [PATCH 18/66] Restore original comments and whitespaces. --- .../LocalRewriter/LocalRewriter_Conversion.cs | 1 + .../Semantic/Semantics/DelegateTypeTests.cs | 20 +++++++++---------- .../Symbol/Symbols/ExtensionMethodTests.cs | 6 +++--- .../ExpressionCompilerTests.cs | 16 +++++++-------- 4 files changed, 22 insertions(+), 21 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_Conversion.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_Conversion.cs index b155201ca3f96..31c67fbec50e4 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_Conversion.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_Conversion.cs @@ -396,6 +396,7 @@ private BoundExpression MakeConversionNodeCore( case ConversionKind.MethodGroup: { + // we eliminate the method group conversion entirely from the bound nodes following local lowering Debug.Assert(oldNodeOpt is { }); var mg = (BoundMethodGroup)rewrittenOperand; var method = oldNodeOpt.SymbolOpt; diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/DelegateTypeTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/DelegateTypeTests.cs index e7d6a41120f9f..6155c0a46f0b4 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/DelegateTypeTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/DelegateTypeTests.cs @@ -706,15 +706,15 @@ static void Main() public static IEnumerable GetMethodGroupImplicitConversionData() { return GetMethodGroupData((methodGroupExpression, methodGroupOnly) => - { - int offset = methodGroupExpression.Length - methodGroupOnly.Length; - return new[] - { + { + int offset = methodGroupExpression.Length - methodGroupOnly.Length; + return new[] + { // (6,29): error CS8917: The delegate type could not be inferred. // System.Delegate d = F; Diagnostic(ErrorCode.ERR_CannotInferDelegateType, methodGroupOnly).WithLocation(6, 29 + offset) }; - }); + }); } [Theory] @@ -752,15 +752,15 @@ static void Main() public static IEnumerable GetMethodGroupExplicitConversionData() { return GetMethodGroupData((methodGroupExpression, methodGroupOnly) => - { - int offset = methodGroupExpression.Length - methodGroupOnly.Length; - return new[] - { + { + int offset = methodGroupExpression.Length - methodGroupOnly.Length; + return new[] + { // (6,20): error CS0030: Cannot convert type 'method' to 'Delegate' // object o = (System.Delegate)F; Diagnostic(ErrorCode.ERR_NoExplicitConv, $"(System.Delegate){methodGroupExpression}").WithArguments("method", "System.Delegate").WithLocation(6, 20) }; - }); + }); } [Theory] diff --git a/src/Compilers/CSharp/Test/Symbol/Symbols/ExtensionMethodTests.cs b/src/Compilers/CSharp/Test/Symbol/Symbols/ExtensionMethodTests.cs index 22ccf8861adc4..685720e450387 100644 --- a/src/Compilers/CSharp/Test/Symbol/Symbols/ExtensionMethodTests.cs +++ b/src/Compilers/CSharp/Test/Symbol/Symbols/ExtensionMethodTests.cs @@ -2242,8 +2242,8 @@ static string Combine(string s1, string s2) @"{ // Code size 42 (0x2a) .maxstack 3 - IL_0000: ldarg.0 - IL_0001: ldc.i4.1 + IL_0000: ldarg.0 + IL_0001: ldc.i4.1 IL_0002: call ""System.Collections.Generic.IEnumerable System.Linq.Enumerable.Skip(System.Collections.Generic.IEnumerable, int)"" IL_0007: ldnull IL_0008: ldftn ""bool C.Filter(string)"" @@ -2253,7 +2253,7 @@ .maxstack 3 IL_0019: ldftn ""string C.Combine(string, string)"" IL_001f: newobj ""System.Func..ctor(object, System.IntPtr)"" IL_0024: call ""string System.Linq.Enumerable.Aggregate(System.Collections.Generic.IEnumerable, System.Func)"" - IL_0029: ret + IL_0029: ret }"; var compilation = CompileAndVerify(source, parseOptions: TestOptions.Regular10, expectedOutput: "orange, apple"); compilation.VerifyIL("C.F", code); diff --git a/src/ExpressionEvaluator/CSharp/Test/ExpressionCompiler/ExpressionCompilerTests.cs b/src/ExpressionEvaluator/CSharp/Test/ExpressionCompiler/ExpressionCompilerTests.cs index 9f072a66cb3e8..deba1106a7a49 100644 --- a/src/ExpressionEvaluator/CSharp/Test/ExpressionCompiler/ExpressionCompilerTests.cs +++ b/src/ExpressionEvaluator/CSharp/Test/ExpressionCompiler/ExpressionCompilerTests.cs @@ -1769,14 +1769,14 @@ static void M2(int i) { } testData: testData); testData.GetMethodData("<>x.<>m0").VerifyIL( @"{ - // Code size 14 (0xe) - .maxstack 2 - .locals init (object V_0) //o - IL_0000: ldnull - IL_0001: ldftn ""void C.M1()"" - IL_0007: newobj ""System.Action..ctor(object, System.IntPtr)"" - IL_000c: stloc.0 - IL_000d: ret + // Code size 14 (0xe) + .maxstack 2 + .locals init (object V_0) //o + IL_0000: ldnull + IL_0001: ldftn ""void C.M1()"" + IL_0007: newobj ""System.Action..ctor(object, System.IntPtr)"" + IL_000c: stloc.0 + IL_000d: ret }"); }); } From 97acc82fc2c24b586264eef6291af19d4820ed5f Mon Sep 17 00:00:00 2001 From: Paul Chen Date: Wed, 22 Dec 2021 16:53:08 +0800 Subject: [PATCH 19/66] Move 10 vs preview to the new test file. --- ...odeGenMethodGroupConversionCachingTests.cs | 128 ++++++++++++++++++ .../Semantic/Semantics/DelegateTypeTests.cs | 57 -------- 2 files changed, 128 insertions(+), 57 deletions(-) diff --git a/src/Compilers/CSharp/Test/Emit2/CodeGen/CodeGenMethodGroupConversionCachingTests.cs b/src/Compilers/CSharp/Test/Emit2/CodeGen/CodeGenMethodGroupConversionCachingTests.cs index 8cf095622c84a..62dca8afdd2b7 100644 --- a/src/Compilers/CSharp/Test/Emit2/CodeGen/CodeGenMethodGroupConversionCachingTests.cs +++ b/src/Compilers/CSharp/Test/Emit2/CodeGen/CodeGenMethodGroupConversionCachingTests.cs @@ -4779,4 +4779,132 @@ .locals init (System.Action V_0) //a }"); } + [Fact] + public void ImplicitlyTypedVariables_01() + { + var source = +@"using System; +class Program +{ + static void Main() + { + var d1 = Main; + Report(d1); + var d2 = () => { }; + Report(d2); + var d3 = delegate () { }; + Report(d3); + } + static void Report(Delegate d) => Console.WriteLine($""{d.GetType().Namespace}.{d.GetType().Name}""); +}"; + + var comp = CreateCompilation(source, parseOptions: TestOptions.Regular10, options: TestOptions.DebugExe); + comp.VerifyDiagnostics(); + + var verifier = CompileAndVerify(comp, expectedOutput: +@"System.Action +System.Action +System.Action"); + verifier.VerifyIL("Program.Main", +@"{ + // Code size 100 (0x64) + .maxstack 2 + .locals init (System.Action V_0, //d1 + System.Action V_1, //d2 + System.Action V_2) //d3 + IL_0000: nop + IL_0001: ldnull + IL_0002: ldftn ""void Program.Main()"" + IL_0008: newobj ""System.Action..ctor(object, System.IntPtr)"" + IL_000d: stloc.0 + IL_000e: ldloc.0 + IL_000f: call ""void Program.Report(System.Delegate)"" + IL_0014: nop + IL_0015: ldsfld ""System.Action Program.<>c.<>9__0_0"" + IL_001a: dup + IL_001b: brtrue.s IL_0034 + IL_001d: pop + IL_001e: ldsfld ""Program.<>c Program.<>c.<>9"" + IL_0023: ldftn ""void Program.<>c.
b__0_0()"" + IL_0029: newobj ""System.Action..ctor(object, System.IntPtr)"" + IL_002e: dup + IL_002f: stsfld ""System.Action Program.<>c.<>9__0_0"" + IL_0034: stloc.1 + IL_0035: ldloc.1 + IL_0036: call ""void Program.Report(System.Delegate)"" + IL_003b: nop + IL_003c: ldsfld ""System.Action Program.<>c.<>9__0_1"" + IL_0041: dup + IL_0042: brtrue.s IL_005b + IL_0044: pop + IL_0045: ldsfld ""Program.<>c Program.<>c.<>9"" + IL_004a: ldftn ""void Program.<>c.
b__0_1()"" + IL_0050: newobj ""System.Action..ctor(object, System.IntPtr)"" + IL_0055: dup + IL_0056: stsfld ""System.Action Program.<>c.<>9__0_1"" + IL_005b: stloc.2 + IL_005c: ldloc.2 + IL_005d: call ""void Program.Report(System.Delegate)"" + IL_0062: nop + IL_0063: ret +}"); + + comp = CreateCompilation(source, options: TestOptions.DebugExe); + comp.VerifyDiagnostics(); + + verifier = CompileAndVerify(comp, expectedOutput: +@"System.Action +System.Action +System.Action"); + verifier.VerifyIL("Program.Main", +@"{ + // Code size 115 (0x73) + .maxstack 2 + .locals init (System.Action V_0, //d1 + System.Action V_1, //d2 + System.Action V_2) //d3 + IL_0000: nop + IL_0001: ldsfld ""System.Action Program.<>O.<0>__Main"" + IL_0006: dup + IL_0007: brtrue.s IL_001c + IL_0009: pop + IL_000a: ldnull + IL_000b: ldftn ""void Program.Main()"" + IL_0011: newobj ""System.Action..ctor(object, System.IntPtr)"" + IL_0016: dup + IL_0017: stsfld ""System.Action Program.<>O.<0>__Main"" + IL_001c: stloc.0 + IL_001d: ldloc.0 + IL_001e: call ""void Program.Report(System.Delegate)"" + IL_0023: nop + IL_0024: ldsfld ""System.Action Program.<>c.<>9__0_0"" + IL_0029: dup + IL_002a: brtrue.s IL_0043 + IL_002c: pop + IL_002d: ldsfld ""Program.<>c Program.<>c.<>9"" + IL_0032: ldftn ""void Program.<>c.
b__0_0()"" + IL_0038: newobj ""System.Action..ctor(object, System.IntPtr)"" + IL_003d: dup + IL_003e: stsfld ""System.Action Program.<>c.<>9__0_0"" + IL_0043: stloc.1 + IL_0044: ldloc.1 + IL_0045: call ""void Program.Report(System.Delegate)"" + IL_004a: nop + IL_004b: ldsfld ""System.Action Program.<>c.<>9__0_1"" + IL_0050: dup + IL_0051: brtrue.s IL_006a + IL_0053: pop + IL_0054: ldsfld ""Program.<>c Program.<>c.<>9"" + IL_0059: ldftn ""void Program.<>c.
b__0_1()"" + IL_005f: newobj ""System.Action..ctor(object, System.IntPtr)"" + IL_0064: dup + IL_0065: stsfld ""System.Action Program.<>c.<>9__0_1"" + IL_006a: stloc.2 + IL_006b: ldloc.2 + IL_006c: call ""void Program.Report(System.Delegate)"" + IL_0071: nop + IL_0072: ret +}"); + } + } diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/DelegateTypeTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/DelegateTypeTests.cs index 6155c0a46f0b4..0ac5b948cb1eb 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/DelegateTypeTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/DelegateTypeTests.cs @@ -7008,63 +7008,6 @@ .locals init (System.Action V_0, //d1 IL_0062: nop IL_0063: ret }"); - - comp = CreateCompilation(new[] { source, s_utils }, options: TestOptions.DebugExe); - comp.VerifyDiagnostics(); - - verifier = CompileAndVerify(comp, expectedOutput: -@"System.Action -System.Action -System.Action"); - verifier.VerifyIL("Program.Main", -@"{ - // Code size 115 (0x73) - .maxstack 2 - .locals init (System.Action V_0, //d1 - System.Action V_1, //d2 - System.Action V_2) //d3 - IL_0000: nop - IL_0001: ldsfld ""System.Action Program.<>O.<0>__Main"" - IL_0006: dup - IL_0007: brtrue.s IL_001c - IL_0009: pop - IL_000a: ldnull - IL_000b: ldftn ""void Program.Main()"" - IL_0011: newobj ""System.Action..ctor(object, System.IntPtr)"" - IL_0016: dup - IL_0017: stsfld ""System.Action Program.<>O.<0>__Main"" - IL_001c: stloc.0 - IL_001d: ldloc.0 - IL_001e: call ""void Program.Report(System.Delegate)"" - IL_0023: nop - IL_0024: ldsfld ""System.Action Program.<>c.<>9__0_0"" - IL_0029: dup - IL_002a: brtrue.s IL_0043 - IL_002c: pop - IL_002d: ldsfld ""Program.<>c Program.<>c.<>9"" - IL_0032: ldftn ""void Program.<>c.
b__0_0()"" - IL_0038: newobj ""System.Action..ctor(object, System.IntPtr)"" - IL_003d: dup - IL_003e: stsfld ""System.Action Program.<>c.<>9__0_0"" - IL_0043: stloc.1 - IL_0044: ldloc.1 - IL_0045: call ""void Program.Report(System.Delegate)"" - IL_004a: nop - IL_004b: ldsfld ""System.Action Program.<>c.<>9__0_1"" - IL_0050: dup - IL_0051: brtrue.s IL_006a - IL_0053: pop - IL_0054: ldsfld ""Program.<>c Program.<>c.<>9"" - IL_0059: ldftn ""void Program.<>c.
b__0_1()"" - IL_005f: newobj ""System.Action..ctor(object, System.IntPtr)"" - IL_0064: dup - IL_0065: stsfld ""System.Action Program.<>c.<>9__0_1"" - IL_006a: stloc.2 - IL_006b: ldloc.2 - IL_006c: call ""void Program.Report(System.Delegate)"" - IL_0071: nop - IL_0072: ret -}"); } [Fact] From 87c448b791c13c9a5fba74cdc8b64872fb15b1ac Mon Sep 17 00:00:00 2001 From: Paul Chen Date: Thu, 23 Dec 2021 12:51:21 +0800 Subject: [PATCH 20/66] Feedback: Remove duplicate information. --- .../LocalRewriter/DelegateCacheRewriter.cs | 17 +++++++++++++---- .../LocalRewriter/LocalRewriter_Conversion.cs | 4 ++-- 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/DelegateCacheRewriter.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/DelegateCacheRewriter.cs index b61450a7f514b..0bfde395f9e9e 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/DelegateCacheRewriter.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/DelegateCacheRewriter.cs @@ -28,12 +28,21 @@ internal DelegateCreationRewriter(SyntheticBoundNodeFactory factory, int topLeve _topLevelMethodOrdinal = topLevelMethodOrdinal; } - internal static bool CanRewrite(MethodSymbol targetMethod, BoundConversion boundConversion) - => targetMethod.IsStatic && !boundConversion.IsExtensionMethod; + internal static bool CanRewrite(BoundDelegateCreationExpression boundDelegateCreation) + { + var targetMethod = boundDelegateCreation.MethodOpt; + + Debug.Assert(targetMethod is { }); - internal BoundExpression Rewrite(BoundDelegateCreationExpression boundDelegateCreation, MethodSymbol targetMethod, TypeSymbol delegateType) + return targetMethod.IsStatic && !boundDelegateCreation.IsExtensionMethod; + } + + internal BoundExpression Rewrite(BoundDelegateCreationExpression boundDelegateCreation) { - Debug.Assert(delegateType.IsDelegateType()); + var targetMethod = boundDelegateCreation.MethodOpt; + var delegateType = boundDelegateCreation.Type; + + Debug.Assert(targetMethod is { }); var oldSyntax = _factory.Syntax; _factory.Syntax = boundDelegateCreation.Syntax; diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_Conversion.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_Conversion.cs index 31c67fbec50e4..ed5c694731399 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_Conversion.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_Conversion.cs @@ -415,10 +415,10 @@ private BoundExpression MakeConversionNodeCore( if (_factory.Compilation.LanguageVersion >= MessageID.IDS_FeatureCacheStaticMethodGroupConversion.RequiredVersion() && !_inExpressionLambda // The tree structure / meaning for expression trees should remain untouched. && _factory.TopLevelMethod.MethodKind != MethodKind.StaticConstructor // Avoid caching twice if people do it manually. - && DelegateCreationRewriter.CanRewrite(method, oldNodeOpt)) + && DelegateCreationRewriter.CanRewrite(boundDelegateCreation)) { var rewriter = _lazyDelegateCacheRewriter ??= new DelegateCreationRewriter(_factory, _topLevelMethodOrdinal); - return rewriter.Rewrite(boundDelegateCreation, method, rewrittenType); + return rewriter.Rewrite(boundDelegateCreation); } else { From 55b01713e3f9cde1eff9edb1b3bc9f400d07f241 Mon Sep 17 00:00:00 2001 From: Paul Chen Date: Sun, 26 Dec 2021 17:45:33 +0800 Subject: [PATCH 21/66] Feedback: Adjust TypeSymbol.VisitType and remove the visitor. --- ...CacheRewriter.TypeParameterUsageChecker.cs | 132 ----------------- .../LocalRewriter/DelegateCacheRewriter.cs | 30 +++- .../Portable/Symbols/TypeSymbolExtensions.cs | 137 ++++++++++++++---- 3 files changed, 137 insertions(+), 162 deletions(-) delete mode 100644 src/Compilers/CSharp/Portable/Lowering/LocalRewriter/DelegateCacheRewriter.TypeParameterUsageChecker.cs diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/DelegateCacheRewriter.TypeParameterUsageChecker.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/DelegateCacheRewriter.TypeParameterUsageChecker.cs deleted file mode 100644 index 923129b04620f..0000000000000 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/DelegateCacheRewriter.TypeParameterUsageChecker.cs +++ /dev/null @@ -1,132 +0,0 @@ -// 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.Collections.Generic; -using Microsoft.CodeAnalysis.CSharp.Symbols; -using Roslyn.Utilities; - -namespace Microsoft.CodeAnalysis.CSharp; - -partial class DelegateCreationRewriter -{ - /// - /// Checks if a type or a method is constructed with some type that involves the specified generic type parameters. - /// This checker is different with because here we check method symbols, anonmous types, as well as custom modifiers. - /// - sealed class TypeParameterUsageChecker : CSharpSymbolVisitor, bool> - { - public static TypeParameterUsageChecker Instance { get; } = new(); - - public override bool VisitTypeParameter(TypeParameterSymbol symbol, HashSet typeParameters) => typeParameters.Contains(symbol); - - public override bool VisitArrayType(ArrayTypeSymbol symbol, HashSet typeParameters) - { - return VisitTypeWithAnnotations(symbol.ElementTypeWithAnnotations, typeParameters); - } - - public override bool VisitNamedType(NamedTypeSymbol symbol, HashSet typeParameters) - { - if (symbol.IsAnonymousType) - { - switch (symbol.TypeKind) - { - case TypeKind.Class: - { - var anonymousClass = (AnonymousTypeManager.AnonymousTypePublicSymbol)symbol; - - foreach (var property in anonymousClass.Properties) - { - if (VisitTypeWithAnnotations(property.TypeWithAnnotations, typeParameters)) - { - return true; - } - } - } - break; - - case TypeKind.Delegate: - { - var anonymousDelegate = (AnonymousTypeManager.AnonymousDelegatePublicSymbol)symbol; - - foreach (var typeArgumentWithAnnotations in anonymousDelegate.TypeArgumentsWithAnnotationsNoUseSiteDiagnostics) - { - if (VisitTypeWithAnnotations(typeArgumentWithAnnotations, typeParameters)) - { - return true; - } - } - } - break; - - default: - throw ExceptionUtilities.Unreachable; - } - - return false; - } - - if (symbol.IsTupleType) - { - foreach (var elementTypeWithAnnotations in symbol.TupleElementTypesWithAnnotations) - { - if (VisitTypeWithAnnotations(elementTypeWithAnnotations, typeParameters)) - { - return true; - } - } - - return false; - } - - foreach (var typeArgumentWithAnnotations in symbol.TypeArgumentsWithAnnotationsNoUseSiteDiagnostics) - { - if (VisitTypeWithAnnotations(typeArgumentWithAnnotations, typeParameters)) - { - return true; - } - } - - return Visit(symbol.ContainingType, typeParameters); - } - - public override bool VisitMethod(MethodSymbol symbol, HashSet typeParameters) - { - foreach (var typeArgumentWithAnnotations in symbol.TypeArgumentsWithAnnotations) - { - if (VisitTypeWithAnnotations(typeArgumentWithAnnotations, typeParameters)) - { - return true; - } - } - - return Visit(symbol.ContainingType, typeParameters); - } - - public override bool VisitPointerType(PointerTypeSymbol symbol, HashSet typeParameters) - { - // Func is a good example why here is reachable. - return VisitTypeWithAnnotations(symbol.PointedAtTypeWithAnnotations, typeParameters); - } - - private bool VisitTypeWithAnnotations(in TypeWithAnnotations typeWithAnnotations, HashSet typeParameters) - { - if (Visit(typeWithAnnotations.Type, typeParameters)) - { - return true; - } - - foreach (var customModifier in typeWithAnnotations.CustomModifiers) - { - if (Visit((Symbol)customModifier.Modifier, typeParameters)) - { - return true; - } - } - - return false; - } - - public override bool DefaultVisit(Symbol symbol, HashSet argument) => throw ExceptionUtilities.Unreachable; - } -} diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/DelegateCacheRewriter.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/DelegateCacheRewriter.cs index 0bfde395f9e9e..37a0ce00a973b 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/DelegateCacheRewriter.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/DelegateCacheRewriter.cs @@ -172,9 +172,7 @@ private bool CanUseTypeScopedConcreteCacheContainer(TypeSymbol delegateType, Met return true; } - var checker = TypeParameterUsageChecker.Instance; - - if (checker.Visit(delegateType, methodTypeParameters) || checker.Visit(targetMethod, methodTypeParameters)) + if (ContainsTypeParameters(targetMethod, methodTypeParameters) || delegateType.ContainsTypeParameters(methodTypeParameters, visitCustomModifiers: true)) { return false; } @@ -186,4 +184,30 @@ private bool CanUseTypeScopedConcreteCacheContainer(TypeSymbol delegateType, Met return true; } + + private static bool ContainsTypeParameters(MethodSymbol method, HashSet typeParameters) + { + if (method.ContainingType.ContainsTypeParameters(typeParameters, visitCustomModifiers: true)) + { + return true; + } + + foreach (var typeArgumentWithAnnotations in method.TypeArgumentsWithAnnotations) + { + if (typeArgumentWithAnnotations.Type.ContainsTypeParameters(typeParameters)) + { + return true; + } + + foreach (var customModifier in typeArgumentWithAnnotations.CustomModifiers) + { + if (((NamedTypeSymbol)customModifier.Modifier).ContainsTypeParameters(typeParameters)) + { + return true; + } + } + } + + return false; + } } diff --git a/src/Compilers/CSharp/Portable/Symbols/TypeSymbolExtensions.cs b/src/Compilers/CSharp/Portable/Symbols/TypeSymbolExtensions.cs index 6b0f2459f47c4..4575b58703bcf 100644 --- a/src/Compilers/CSharp/Portable/Symbols/TypeSymbolExtensions.cs +++ b/src/Compilers/CSharp/Portable/Symbols/TypeSymbolExtensions.cs @@ -637,7 +637,8 @@ private static bool IsTypeLessVisibleThan(TypeSymbol type, Symbol sym, ref Compo this TypeSymbol type, Func predicate, T arg, - bool canDigThroughNullable = false) + bool canDigThroughNullable = false, + bool visitCustomModifiers = false) { return VisitType( typeWithAnnotationsOpt: default, @@ -666,7 +667,8 @@ private static bool IsTypeLessVisibleThan(TypeSymbol type, Symbol sym, ref Compo Func? typePredicate, T arg, bool canDigThroughNullable = false, - bool useDefaultType = false) + bool useDefaultType = false, + bool visitCustomModifiers = false) { RoslynDebug.Assert(typeWithAnnotationsOpt.HasType == (type is null)); RoslynDebug.Assert(canDigThroughNullable == false || useDefaultType == false, "digging through nullable will cause early resolution of nullable types"); @@ -693,7 +695,7 @@ private static bool IsTypeLessVisibleThan(TypeSymbol type, Symbol sym, ref Compo if ((object)containingType != null) { isNestedNamedType = true; - var result = VisitType(default, containingType, typeWithAnnotationsPredicate, typePredicate, arg, canDigThroughNullable, useDefaultType); + var result = VisitType(default, containingType, typeWithAnnotationsPredicate, typePredicate, arg, canDigThroughNullable, useDefaultType, visitCustomModifiers); if (result is object) { return result; @@ -737,32 +739,67 @@ private static bool IsTypeLessVisibleThan(TypeSymbol type, Symbol sym, ref Compo case TypeKind.Struct: case TypeKind.Interface: case TypeKind.Delegate: - var typeArguments = ((NamedTypeSymbol)current).TypeArgumentsWithAnnotationsNoUseSiteDiagnostics; - if (typeArguments.IsEmpty) + + if (current.IsAnonymousType && current.TypeKind == TypeKind.Class) { - return null; + var anonymousClass = (AnonymousTypeManager.AnonymousTypePublicSymbol)current; + var properties = anonymousClass.Properties; + + int i; + for (i = 0; i < properties.Length - 1; i++) + { + if (visitTypeWithAnnotations( + properties[i].TypeWithAnnotations, + typeWithAnnotationsPredicate, typePredicate, arg, + useDefaultType, canDigThroughNullable, visitCustomModifiers) is TypeSymbol result) + { + return result; + } + } + + next = properties[i].TypeWithAnnotations; } + else if (current.IsTupleType) + { + var elementTypesWithAnnotations = current.TupleElementTypesWithAnnotations; - int i; - for (i = 0; i < typeArguments.Length - 1; i++) + int i; + for (i = 0; i < elementTypesWithAnnotations.Length - 1; i++) + { + if (visitTypeWithAnnotations( + elementTypesWithAnnotations[i], + typeWithAnnotationsPredicate, typePredicate, arg, + useDefaultType, canDigThroughNullable, visitCustomModifiers) is TypeSymbol result) + { + return result; + } + } + + next = elementTypesWithAnnotations[i]; + } + else { - // Let's try to avoid early resolution of nullable types - (TypeWithAnnotations nextTypeWithAnnotations, TypeSymbol? nextType) = getNextIterationElements(typeArguments[i], canDigThroughNullable); - var result = VisitType( - typeWithAnnotationsOpt: nextTypeWithAnnotations, - type: nextType, - typeWithAnnotationsPredicate, - typePredicate, - arg, - canDigThroughNullable, - useDefaultType); - if (result is object) + var typeArguments = ((NamedTypeSymbol)current).TypeArgumentsWithAnnotationsNoUseSiteDiagnostics; + if (typeArguments.IsEmpty) { - return result; + return null; + } + + int i; + for (i = 0; i < typeArguments.Length - 1; i++) + { + if (visitTypeWithAnnotations( + typeArguments[i], + typeWithAnnotationsPredicate, typePredicate, arg, + useDefaultType, canDigThroughNullable, visitCustomModifiers) is TypeSymbol result) + { + return result; + } } + + next = typeArguments[i]; } - next = typeArguments[i]; break; case TypeKind.Array: @@ -775,7 +812,7 @@ private static bool IsTypeLessVisibleThan(TypeSymbol type, Symbol sym, ref Compo case TypeKind.FunctionPointer: { - var result = visitFunctionPointerType((FunctionPointerTypeSymbol)current, typeWithAnnotationsPredicate, typePredicate, arg, useDefaultType, canDigThroughNullable, out next); + var result = visitFunctionPointerType((FunctionPointerTypeSymbol)current, typeWithAnnotationsPredicate, typePredicate, arg, useDefaultType, canDigThroughNullable, visitCustomModifiers, out next); if (result is object) { return result; @@ -796,7 +833,51 @@ private static bool IsTypeLessVisibleThan(TypeSymbol type, Symbol sym, ref Compo static (TypeWithAnnotations, TypeSymbol?) getNextIterationElements(TypeWithAnnotations type, bool canDigThroughNullable) => canDigThroughNullable ? (default(TypeWithAnnotations), type.NullableUnderlyingTypeOrSelf) : (type, null); - static TypeSymbol? visitFunctionPointerType(FunctionPointerTypeSymbol type, Func? typeWithAnnotationsPredicate, Func? typePredicate, T arg, bool useDefaultType, bool canDigThroughNullable, out TypeWithAnnotations next) + static TypeSymbol? visitTypeWithAnnotations(TypeWithAnnotations typeWithAnnotations, Func? typeWithAnnotationsPredicate, Func? typePredicate, T arg, bool useDefaultType, bool canDigThroughNullable, bool visitCustomModifiers) + { + // Let's try to avoid early resolution of nullable types + (TypeWithAnnotations nextTypeWithAnnotations, TypeSymbol? nextType) = getNextIterationElements(typeWithAnnotations, canDigThroughNullable); + + var result = VisitType( + typeWithAnnotationsOpt: nextTypeWithAnnotations, + type: nextType, + typeWithAnnotationsPredicate, + typePredicate, + arg, + canDigThroughNullable, + useDefaultType, + visitCustomModifiers); + + if (result is object) + { + return result; + } + + if (visitCustomModifiers) + { + foreach (var customModifier in typeWithAnnotations.CustomModifiers) + { + result = VisitType( + typeWithAnnotationsOpt: default, + type: (NamedTypeSymbol)customModifier.Modifier, + typeWithAnnotationsPredicate, + typePredicate, + arg, + canDigThroughNullable, + useDefaultType, + visitCustomModifiers: false); + + if (result is object) + { + return result; + } + } + } + + return null; + } + + static TypeSymbol? visitFunctionPointerType(FunctionPointerTypeSymbol type, Func? typeWithAnnotationsPredicate, Func? typePredicate, T arg, bool useDefaultType, bool canDigThroughNullable, bool visitCustomModifiers, out TypeWithAnnotations next) { MethodSymbol currentPointer = type.Signature; if (currentPointer.ParameterCount == 0) @@ -812,7 +893,8 @@ private static bool IsTypeLessVisibleThan(TypeSymbol type, Symbol sym, ref Compo typePredicate, arg, canDigThroughNullable, - useDefaultType); + useDefaultType, + visitCustomModifiers); if (result is object) { next = default; @@ -830,7 +912,8 @@ private static bool IsTypeLessVisibleThan(TypeSymbol type, Symbol sym, ref Compo typePredicate, arg, canDigThroughNullable, - useDefaultType); + useDefaultType, + visitCustomModifiers); if (result is object) { next = default; @@ -1059,9 +1142,9 @@ public static bool ContainsTypeParameter(this TypeSymbol type, MethodSymbol para private static readonly Func s_isTypeParameterWithSpecificContainerPredicate = (type, parameterContainer, unused) => type.TypeKind == TypeKind.TypeParameter && (object)type.ContainingSymbol == (object)parameterContainer; - public static bool ContainsTypeParameters(this TypeSymbol type, HashSet parameters) + public static bool ContainsTypeParameters(this TypeSymbol type, HashSet parameters, bool visitCustomModifiers = false) { - var result = type.VisitType(s_containsTypeParametersPredicate, parameters); + var result = type.VisitType(s_containsTypeParametersPredicate, parameters, visitCustomModifiers: visitCustomModifiers); return result is object; } From 1a75a7fa909457e1f6514e4e486546d0d178c3c5 Mon Sep 17 00:00:00 2001 From: Paul Chen Date: Sun, 26 Dec 2021 18:43:49 +0800 Subject: [PATCH 22/66] Fix IndexOutOfRangeException. --- .../CSharp/Portable/Symbols/TypeSymbolExtensions.cs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/Compilers/CSharp/Portable/Symbols/TypeSymbolExtensions.cs b/src/Compilers/CSharp/Portable/Symbols/TypeSymbolExtensions.cs index 4575b58703bcf..b19dc29c21eb2 100644 --- a/src/Compilers/CSharp/Portable/Symbols/TypeSymbolExtensions.cs +++ b/src/Compilers/CSharp/Portable/Symbols/TypeSymbolExtensions.cs @@ -745,6 +745,11 @@ private static bool IsTypeLessVisibleThan(TypeSymbol type, Symbol sym, ref Compo var anonymousClass = (AnonymousTypeManager.AnonymousTypePublicSymbol)current; var properties = anonymousClass.Properties; + if (properties.IsEmpty) + { + return null; + } + int i; for (i = 0; i < properties.Length - 1; i++) { @@ -763,6 +768,11 @@ private static bool IsTypeLessVisibleThan(TypeSymbol type, Symbol sym, ref Compo { var elementTypesWithAnnotations = current.TupleElementTypesWithAnnotations; + if (elementTypesWithAnnotations.IsEmpty) + { + return null; + } + int i; for (i = 0; i < elementTypesWithAnnotations.Length - 1; i++) { @@ -780,6 +790,7 @@ private static bool IsTypeLessVisibleThan(TypeSymbol type, Symbol sym, ref Compo else { var typeArguments = ((NamedTypeSymbol)current).TypeArgumentsWithAnnotationsNoUseSiteDiagnostics; + if (typeArguments.IsEmpty) { return null; From 656466a06aaa78dd275d17c5e7e83cda25e124e7 Mon Sep 17 00:00:00 2001 From: Paul Chen Date: Sun, 26 Dec 2021 19:48:51 +0800 Subject: [PATCH 23/66] Remove tuple branch. --- .../Portable/Symbols/TypeSymbolExtensions.cs | 23 ------------------- 1 file changed, 23 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Symbols/TypeSymbolExtensions.cs b/src/Compilers/CSharp/Portable/Symbols/TypeSymbolExtensions.cs index b19dc29c21eb2..dcb95d907140e 100644 --- a/src/Compilers/CSharp/Portable/Symbols/TypeSymbolExtensions.cs +++ b/src/Compilers/CSharp/Portable/Symbols/TypeSymbolExtensions.cs @@ -764,29 +764,6 @@ private static bool IsTypeLessVisibleThan(TypeSymbol type, Symbol sym, ref Compo next = properties[i].TypeWithAnnotations; } - else if (current.IsTupleType) - { - var elementTypesWithAnnotations = current.TupleElementTypesWithAnnotations; - - if (elementTypesWithAnnotations.IsEmpty) - { - return null; - } - - int i; - for (i = 0; i < elementTypesWithAnnotations.Length - 1; i++) - { - if (visitTypeWithAnnotations( - elementTypesWithAnnotations[i], - typeWithAnnotationsPredicate, typePredicate, arg, - useDefaultType, canDigThroughNullable, visitCustomModifiers) is TypeSymbol result) - { - return result; - } - } - - next = elementTypesWithAnnotations[i]; - } else { var typeArguments = ((NamedTypeSymbol)current).TypeArgumentsWithAnnotationsNoUseSiteDiagnostics; From f5bdc79176f4d061ab7ee3cb4168233719f03ca0 Mon Sep 17 00:00:00 2001 From: Paul Chen Date: Mon, 27 Dec 2021 12:56:40 +0800 Subject: [PATCH 24/66] Feedback: Add asserts. --- .../Lowering/LocalRewriter/DelegateCacheContainer.cs | 11 +++++++---- .../CSharp/Portable/Symbols/TypeSymbolExtensions.cs | 4 +++- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/DelegateCacheContainer.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/DelegateCacheContainer.cs index 62926f54565da..42eec060ee9fb 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/DelegateCacheContainer.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/DelegateCacheContainer.cs @@ -22,15 +22,18 @@ internal DelegateCacheContainer(TypeSymbol containingType, int generationOrdinal : base(GeneratedNames.DelegateCacheContainerType(generationOrdinal), containingMethod: null) { Debug.Assert(containingType.IsDefinition); + _containingSymbol = containingType; } /// Creates a method scoped generic delegate cache container. - internal DelegateCacheContainer(MethodSymbol containingMethod, int topLevelMethodOrdinal, int ownerUniqueId, int generationOrdinal) - : base(GeneratedNames.DelegateCacheContainerType(generationOrdinal, containingMethod.Name, topLevelMethodOrdinal, ownerUniqueId), containingMethod) + internal DelegateCacheContainer(MethodSymbol ownerMethod, int topLevelMethodOrdinal, int ownerUniqueId, int generationOrdinal) + : base(GeneratedNames.DelegateCacheContainerType(generationOrdinal, ownerMethod.Name, topLevelMethodOrdinal, ownerUniqueId), ownerMethod) { - Debug.Assert(containingMethod.IsDefinition); - _containingSymbol = containingMethod.ContainingType; + Debug.Assert(ownerMethod.IsDefinition); + Debug.Assert(ownerMethod.Arity > 0); + + _containingSymbol = ownerMethod.ContainingType; _constructedContainer = Construct(ConstructedFromTypeParameters); } diff --git a/src/Compilers/CSharp/Portable/Symbols/TypeSymbolExtensions.cs b/src/Compilers/CSharp/Portable/Symbols/TypeSymbolExtensions.cs index dcb95d907140e..939ac367d16fd 100644 --- a/src/Compilers/CSharp/Portable/Symbols/TypeSymbolExtensions.cs +++ b/src/Compilers/CSharp/Portable/Symbols/TypeSymbolExtensions.cs @@ -646,7 +646,8 @@ private static bool IsTypeLessVisibleThan(TypeSymbol type, Symbol sym, ref Compo typeWithAnnotationsPredicate: null, typePredicate: predicate, arg, - canDigThroughNullable); + canDigThroughNullable, + visitCustomModifiers); } /// @@ -672,6 +673,7 @@ private static bool IsTypeLessVisibleThan(TypeSymbol type, Symbol sym, ref Compo { RoslynDebug.Assert(typeWithAnnotationsOpt.HasType == (type is null)); RoslynDebug.Assert(canDigThroughNullable == false || useDefaultType == false, "digging through nullable will cause early resolution of nullable types"); + RoslynDebug.Assert(canDigThroughNullable == false || visitCustomModifiers == false); // In order to handle extremely "deep" types like "int[][][][][][][][][]...[]" // or int*****************...* we implement manual tail recursion rather than From c244c5129722dcf43cb83fa7b3fff8948734c1b0 Mon Sep 17 00:00:00 2001 From: Paul Chen Date: Mon, 27 Dec 2021 15:23:17 +0800 Subject: [PATCH 25/66] Feedback: Adjust VisitType. --- .../Portable/Symbols/TypeSymbolExtensions.cs | 126 +++++++----------- 1 file changed, 48 insertions(+), 78 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Symbols/TypeSymbolExtensions.cs b/src/Compilers/CSharp/Portable/Symbols/TypeSymbolExtensions.cs index 939ac367d16fd..0b5fe6e4df58c 100644 --- a/src/Compilers/CSharp/Portable/Symbols/TypeSymbolExtensions.cs +++ b/src/Compilers/CSharp/Portable/Symbols/TypeSymbolExtensions.cs @@ -726,6 +726,21 @@ private static bool IsTypeLessVisibleThan(TypeSymbol type, Symbol sym, ref Compo } } + if (visitCustomModifiers && typeWithAnnotationsOpt.HasType) + { + foreach (var customModifier in typeWithAnnotationsOpt.CustomModifiers) + { + var result = VisitType( + typeWithAnnotationsOpt: default, type: (NamedTypeSymbol)customModifier.Modifier, + typeWithAnnotationsPredicate, typePredicate, arg, + canDigThroughNullable, useDefaultType, visitCustomModifiers); + if (result is object) + { + return result; + } + } + } + TypeWithAnnotations next; switch (current.TypeKind) @@ -742,54 +757,53 @@ private static bool IsTypeLessVisibleThan(TypeSymbol type, Symbol sym, ref Compo case TypeKind.Interface: case TypeKind.Delegate: - if (current.IsAnonymousType && current.TypeKind == TypeKind.Class) + ImmutableArray typesWithAnnotations; + + if (current.IsAnonymousType) { - var anonymousClass = (AnonymousTypeManager.AnonymousTypePublicSymbol)current; - var properties = anonymousClass.Properties; + var anonymous = (AnonymousTypeManager.AnonymousTypeOrDelegatePublicSymbol)current; + var fields = anonymous.TypeDescriptor.Fields; + var builder = ArrayBuilder.GetInstance(); - if (properties.IsEmpty) + foreach (var field in fields) { - return null; + builder.Add(field.TypeWithAnnotations); } - int i; - for (i = 0; i < properties.Length - 1; i++) - { - if (visitTypeWithAnnotations( - properties[i].TypeWithAnnotations, - typeWithAnnotationsPredicate, typePredicate, arg, - useDefaultType, canDigThroughNullable, visitCustomModifiers) is TypeSymbol result) - { - return result; - } - } - - next = properties[i].TypeWithAnnotations; + typesWithAnnotations = builder.ToImmutableAndFree(); } else { - var typeArguments = ((NamedTypeSymbol)current).TypeArgumentsWithAnnotationsNoUseSiteDiagnostics; + typesWithAnnotations = ((NamedTypeSymbol)current).TypeArgumentsWithAnnotationsNoUseSiteDiagnostics; + } - if (typeArguments.IsEmpty) - { - return null; - } + if (typesWithAnnotations.IsEmpty) + { + return null; + } - int i; - for (i = 0; i < typeArguments.Length - 1; i++) + int i; + for (i = 0; i < typesWithAnnotations.Length - 1; i++) + { + // Let's try to avoid early resolution of nullable types + (TypeWithAnnotations nextTypeWithAnnotations, TypeSymbol? nextType) = getNextIterationElements(typesWithAnnotations[i], canDigThroughNullable); + var result = VisitType( + typeWithAnnotationsOpt: nextTypeWithAnnotations, + type: nextType, + typeWithAnnotationsPredicate, + typePredicate, + arg, + canDigThroughNullable, + useDefaultType, + visitCustomModifiers); + if (result is object) { - if (visitTypeWithAnnotations( - typeArguments[i], - typeWithAnnotationsPredicate, typePredicate, arg, - useDefaultType, canDigThroughNullable, visitCustomModifiers) is TypeSymbol result) - { - return result; - } + return result; } - - next = typeArguments[i]; } + next = typesWithAnnotations[i]; + break; case TypeKind.Array: @@ -823,50 +837,6 @@ private static bool IsTypeLessVisibleThan(TypeSymbol type, Symbol sym, ref Compo static (TypeWithAnnotations, TypeSymbol?) getNextIterationElements(TypeWithAnnotations type, bool canDigThroughNullable) => canDigThroughNullable ? (default(TypeWithAnnotations), type.NullableUnderlyingTypeOrSelf) : (type, null); - static TypeSymbol? visitTypeWithAnnotations(TypeWithAnnotations typeWithAnnotations, Func? typeWithAnnotationsPredicate, Func? typePredicate, T arg, bool useDefaultType, bool canDigThroughNullable, bool visitCustomModifiers) - { - // Let's try to avoid early resolution of nullable types - (TypeWithAnnotations nextTypeWithAnnotations, TypeSymbol? nextType) = getNextIterationElements(typeWithAnnotations, canDigThroughNullable); - - var result = VisitType( - typeWithAnnotationsOpt: nextTypeWithAnnotations, - type: nextType, - typeWithAnnotationsPredicate, - typePredicate, - arg, - canDigThroughNullable, - useDefaultType, - visitCustomModifiers); - - if (result is object) - { - return result; - } - - if (visitCustomModifiers) - { - foreach (var customModifier in typeWithAnnotations.CustomModifiers) - { - result = VisitType( - typeWithAnnotationsOpt: default, - type: (NamedTypeSymbol)customModifier.Modifier, - typeWithAnnotationsPredicate, - typePredicate, - arg, - canDigThroughNullable, - useDefaultType, - visitCustomModifiers: false); - - if (result is object) - { - return result; - } - } - } - - return null; - } - static TypeSymbol? visitFunctionPointerType(FunctionPointerTypeSymbol type, Func? typeWithAnnotationsPredicate, Func? typePredicate, T arg, bool useDefaultType, bool canDigThroughNullable, bool visitCustomModifiers, out TypeWithAnnotations next) { MethodSymbol currentPointer = type.Signature; From 8c197fe641e2abbfe3c47ced43ee9078b5f4e658 Mon Sep 17 00:00:00 2001 From: Paul Chen Date: Tue, 28 Dec 2021 19:37:50 +0800 Subject: [PATCH 26/66] Feedback: Use ancestor cache containers (less arity) when possible. --- .../LocalRewriter/DelegateCacheRewriter.cs | 175 +++++++++--------- .../Portable/Symbols/TypeSymbolExtensions.cs | 4 +- ...odeGenMethodGroupConversionCachingTests.cs | 132 +++++++++++++ 3 files changed, 221 insertions(+), 90 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/DelegateCacheRewriter.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/DelegateCacheRewriter.cs index 37a0ce00a973b..b1b483dcb1074 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/DelegateCacheRewriter.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/DelegateCacheRewriter.cs @@ -3,10 +3,11 @@ // See the LICENSE file in the project root for more information. using System.Collections.Generic; +using System.Collections.Immutable; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp.Symbols; using Microsoft.CodeAnalysis.PooledObjects; -using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CSharp; @@ -63,13 +64,36 @@ private DelegateCacheContainer GetOrAddCacheContainer(TypeSymbol delegateType, M Debug.Assert(_factory.ModuleBuilderOpt is { }); Debug.Assert(_factory.CurrentFunction is { }); - var typeCompilationState = _factory.CompilationState; var generation = _factory.ModuleBuilderOpt.CurrentGenerationOrdinal; DelegateCacheContainer? container; - if (CanUseTypeScopedConcreteCacheContainer(delegateType, targetMethod)) + // We don't need to synthesize a container for each and every function. + // For functions with zero arity, we can share the container with the closest generic ancestor function. + // + // For example: + // void LF1() + // { + // void LF2() + // { + // void LF3() + // { + // Func d = SomeMethod; + // static void LF4 () { Func d = SomeMethod; } + // } + // + // void LF5() + // { + // Func d = SomeMethod; + // } + // } + // } + // + // In the above case, only one cached delegate is necessary, and it could be assigned to the container 'owned' by LF1. + + if (!TryGetOwnerFunction(_factory.CurrentFunction, delegateType, targetMethod, out var ownerFunction)) { + var typeCompilationState = _factory.CompilationState; container = typeCompilationState.ConcreteDelegateCacheContainer; if (container is { }) @@ -82,39 +106,6 @@ private DelegateCacheContainer GetOrAddCacheContainer(TypeSymbol delegateType, M } else { - // We don't need to synthesize a container for each and every function. - // For functions with zero arity, we can share the container with the closest generic ancestor function. - // - // For example: - // void LF1() - // { - // void LF2() - // { - // Func d = SomeMethod; - // static void LF4 () { Func d = SomeMethod; } - // } - // - // void LF3() - // { - // Func d = SomeMethod; - // } - // } - // - // In the above case, only one cached delegate is necessary, and it could be assigned to the container 'owned' by LF1. - - MethodSymbol? ownerFunction = null; - - for (Symbol? enclosingSymbol = _factory.CurrentFunction; enclosingSymbol is MethodSymbol enclosingMethod; enclosingSymbol = enclosingSymbol.ContainingSymbol) - { - if (enclosingMethod.Arity > 0) - { - ownerFunction = enclosingMethod; - break; - } - } - - Debug.Assert(ownerFunction is { }); - var containers = _genericCacheContainers ??= new Dictionary(); if (containers.TryGetValue(ownerFunction, out container)) @@ -131,81 +122,89 @@ private DelegateCacheContainer GetOrAddCacheContainer(TypeSymbol delegateType, M return container; } - private bool CanUseTypeScopedConcreteCacheContainer(TypeSymbol delegateType, MethodSymbol targetMethod) + private static bool TryGetOwnerFunction(MethodSymbol currentFunction, TypeSymbol delegateType, MethodSymbol targetMethod, [NotNullWhen(true)] out MethodSymbol? ownerFunction) { - // Possible places for type parameters that can act as type arguments to construct the delegateType or targetMethod: - // 1. containing types - // 2. top level method - // 3. local functions - // Since our containers are created within the same enclosing types, we can ignore type parameters from them. - - var methodTypeParameters = PooledHashSet.GetInstance(); - try + if (targetMethod.MethodKind == MethodKind.LocalFunction) { - for (Symbol? enclosingSymbol = _factory.CurrentFunction; enclosingSymbol is MethodSymbol enclosingMethod; enclosingSymbol = enclosingSymbol.ContainingSymbol) + // Local functions can use type parameters from their enclosing methods! + // + // For example: + // void Test() + // { + // var t = Target; + // static object Target() => default(T); + // } + // + // Therefore, without too much analysis, we select the closest generic enclosing function as the cache container owner. + + for (Symbol? enclosingSymbol = currentFunction; enclosingSymbol is MethodSymbol enclosingMethod; enclosingSymbol = enclosingSymbol.ContainingSymbol) { if (enclosingMethod.Arity > 0) { - if (targetMethod.MethodKind == MethodKind.LocalFunction) - { - // Local functions can reference type parameters from their enclosing methods! - // - // For example: - // void Test() - // { - // var t = Target; - // static object Target() => default(T); - // } - // - // Therefore, unless no method type parameters for the target local function to use, - // we cannot safely use a type scoped concrete cache container without some deep analysis. - - return false; - } - - methodTypeParameters.AddAll(enclosingMethod.TypeParameters); + ownerFunction = enclosingMethod; + return true; } } - if (methodTypeParameters.Count == 0) - { - return true; - } + ownerFunction = null; + return false; + } - if (ContainsTypeParameters(targetMethod, methodTypeParameters) || delegateType.ContainsTypeParameters(methodTypeParameters, visitCustomModifiers: true)) + var usedTypeParameters = PooledHashSet.GetInstance(); + try + { + FindTypeParameters(delegateType, usedTypeParameters); + FindTypeParameters(targetMethod, usedTypeParameters); + + for (Symbol? enclosingSymbol = currentFunction; enclosingSymbol is MethodSymbol enclosingMethod; enclosingSymbol = enclosingSymbol.ContainingSymbol) { - return false; + if (usedTypeParametersContains(usedTypeParameters, enclosingMethod.TypeParameters)) + { + ownerFunction = enclosingMethod; + return true; + } } + + ownerFunction = null; + return false; } finally { - methodTypeParameters.Free(); + usedTypeParameters.Free(); } - return true; + static bool usedTypeParametersContains(HashSet used, ImmutableArray typeParameters) + { + foreach (var typeParameter in typeParameters) + { + if (used.Contains(typeParameter)) + { + return true; + } + } + + return false; + } } - private static bool ContainsTypeParameters(MethodSymbol method, HashSet typeParameters) + private static void FindTypeParameters(TypeSymbol type, HashSet result) + => type.VisitType(TypeParameterSymbolCollector, result, visitCustomModifiers: true); + + private static void FindTypeParameters(MethodSymbol method, HashSet result) { - if (method.ContainingType.ContainsTypeParameters(typeParameters, visitCustomModifiers: true)) + FindTypeParameters(method.ContainingType, result); + + foreach (var typeArgument in method.TypeArgumentsWithAnnotations) { - return true; + typeArgument.VisitType(type: null, typeWithAnnotationsPredicate: null, TypeParameterSymbolCollector, result, visitCustomModifiers: true); } + } - foreach (var typeArgumentWithAnnotations in method.TypeArgumentsWithAnnotations) + private static bool TypeParameterSymbolCollector(TypeSymbol typeSymbol, HashSet result, bool _) + { + if (typeSymbol is TypeParameterSymbol typeParameter) { - if (typeArgumentWithAnnotations.Type.ContainsTypeParameters(typeParameters)) - { - return true; - } - - foreach (var customModifier in typeArgumentWithAnnotations.CustomModifiers) - { - if (((NamedTypeSymbol)customModifier.Modifier).ContainsTypeParameters(typeParameters)) - { - return true; - } - } + result.Add(typeParameter); } return false; diff --git a/src/Compilers/CSharp/Portable/Symbols/TypeSymbolExtensions.cs b/src/Compilers/CSharp/Portable/Symbols/TypeSymbolExtensions.cs index 0b5fe6e4df58c..f037bb36b20ad 100644 --- a/src/Compilers/CSharp/Portable/Symbols/TypeSymbolExtensions.cs +++ b/src/Compilers/CSharp/Portable/Symbols/TypeSymbolExtensions.cs @@ -1102,9 +1102,9 @@ public static bool ContainsTypeParameter(this TypeSymbol type, MethodSymbol para private static readonly Func s_isTypeParameterWithSpecificContainerPredicate = (type, parameterContainer, unused) => type.TypeKind == TypeKind.TypeParameter && (object)type.ContainingSymbol == (object)parameterContainer; - public static bool ContainsTypeParameters(this TypeSymbol type, HashSet parameters, bool visitCustomModifiers = false) + public static bool ContainsTypeParameters(this TypeSymbol type, HashSet parameters) { - var result = type.VisitType(s_containsTypeParametersPredicate, parameters, visitCustomModifiers: visitCustomModifiers); + var result = type.VisitType(s_containsTypeParametersPredicate, parameters); return result is object; } diff --git a/src/Compilers/CSharp/Test/Emit2/CodeGen/CodeGenMethodGroupConversionCachingTests.cs b/src/Compilers/CSharp/Test/Emit2/CodeGen/CodeGenMethodGroupConversionCachingTests.cs index 62dca8afdd2b7..e7e6ececf87fd 100644 --- a/src/Compilers/CSharp/Test/Emit2/CodeGen/CodeGenMethodGroupConversionCachingTests.cs +++ b/src/Compilers/CSharp/Test/Emit2/CodeGen/CodeGenMethodGroupConversionCachingTests.cs @@ -3082,6 +3082,138 @@ static void containerValidator(ModuleSymbol module) CompileAndVerify(source, symbolValidator: containerValidator); } + [Fact] + public void ContainersCanBeShared_SkippingUnused() + { + var source = @" +using System; +class C +{ + public static void Target(T t) { } +} +static class E +{ + static void Test() + { + void LF2() + { + void LF3() + { + Action d = C.Target; + static void LF4 () { Action d = C.Target; } + + LF4(); + } + + void LF5() + { + Action d = C.Target; + } + + LF3(); LF5(); + } + + LF2(); + } +} +"; + static void containerValidator(ModuleSymbol module) + { + var testClass = module.GlobalNamespace.GetTypeMember("E"); + var container = testClass.GetTypeMember("O__0_0"); + Assert.NotNull(container); Debug.Assert(container is { }); + + Assert.Equal(1, container.Arity); + + var members = container.GetMembers(); + Assert.Equal(1, members.Length); + + var field0 = members[0] as FieldSymbol; + Assert.NotNull(field0); Debug.Assert(field0 is { }); + Assert.Equal("<0>__Target", field0.Name); + + var fieldType0 = field0.Type as NamedTypeSymbol; + Assert.NotNull(fieldType0); Debug.Assert(fieldType0 is { }); + Assert.True(fieldType0.IsDelegateType()); + Assert.Equal("System", fieldType0.ContainingNamespace.Name); + Assert.Equal("Action", fieldType0.Name); + Assert.Equal(1, fieldType0.Arity); + } + + CompileAndVerify(source, symbolValidator: containerValidator); + } + + [Fact] + public void ContainersCanBeShared_LocalFunctions() + { + var source = @" +using System; +static class E +{ + static void Test() + { + void Owner() + { + void LF1() + { + Action d = LF2; + static void LF2() { } + + LF2(); + } + + void LF3() + { + Action d = LF2; + static void LF2() { } + + LF2(); + } + + LF1(); LF3(); + } + + Owner(); + } +} +"; + static void containerValidator(ModuleSymbol module) + { + var testClass = module.GlobalNamespace.GetTypeMember("E"); + var container = testClass.GetTypeMember("O__0_0"); + Assert.NotNull(container); Debug.Assert(container is { }); + + Assert.Equal(2, container.Arity); + + var members = container.GetMembers(); + Assert.Equal(2, members.Length); + + var field0 = members[0] as FieldSymbol; + Assert.NotNull(field0); Debug.Assert(field0 is { }); + Assert.Equal("<0>__LF2", field0.Name); + + var fieldType0 = field0.Type as NamedTypeSymbol; + Assert.NotNull(fieldType0); Debug.Assert(fieldType0 is { }); + Assert.True(fieldType0.IsDelegateType()); + Assert.Equal("System", fieldType0.ContainingNamespace.Name); + Assert.Equal("Action", fieldType0.Name); + Assert.Equal(0, fieldType0.Arity); + + var field1 = members[1] as FieldSymbol; + Assert.NotNull(field1); Debug.Assert(field1 is { }); + Assert.Equal("<1>__LF2", field1.Name); + + var fieldType1 = field1.Type as NamedTypeSymbol; + Assert.NotNull(fieldType1); Debug.Assert(fieldType1 is { }); + Assert.True(fieldType1.IsDelegateType()); + Assert.Equal("System", fieldType1.ContainingNamespace.Name); + Assert.Equal("Action", fieldType1.Name); + Assert.Equal(0, fieldType1.Arity); + } + + CompileAndVerify(source, symbolValidator: containerValidator); + } + [Fact] public void EventHandlers_TypeScoped_CouldBeModuleScoped0() { From 8f85ed65295582d4b22039a35c1049b4408375b8 Mon Sep 17 00:00:00 2001 From: Paul Chen Date: Wed, 29 Dec 2021 00:18:32 +0800 Subject: [PATCH 27/66] Add tests for annoymous delegate in anonymous class. --- ...odeGenMethodGroupConversionCachingTests.cs | 108 ++++++++++++++++++ 1 file changed, 108 insertions(+) diff --git a/src/Compilers/CSharp/Test/Emit2/CodeGen/CodeGenMethodGroupConversionCachingTests.cs b/src/Compilers/CSharp/Test/Emit2/CodeGen/CodeGenMethodGroupConversionCachingTests.cs index e7e6ececf87fd..ee1bfa0eca03d 100644 --- a/src/Compilers/CSharp/Test/Emit2/CodeGen/CodeGenMethodGroupConversionCachingTests.cs +++ b/src/Compilers/CSharp/Test/Emit2/CodeGen/CodeGenMethodGroupConversionCachingTests.cs @@ -3604,6 +3604,114 @@ .maxstack 4 "); } + [Fact] + public void AnonymousClass_AnonymousDelegate0() + { + var source = @" +using System; +class D +{ + public void Owner() + { + static void Test(T t) + { + var f = F; + var a = new { x = f }; + + Invoke(a, Target); + } + } + + static void F(ref T t) { } + + static void Invoke(T t, Action f) { } + + static void Target(T t) { } +} +"; + CompileAndVerify(source).VerifyIL("D.g__Test|0_0", @" +{ + // Code size 65 (0x41) + .maxstack 3 + IL_0000: ldsfld "" D.O__0_0.<0>__F"" + IL_0005: dup + IL_0006: brtrue.s IL_001b + IL_0008: pop + IL_0009: ldnull + IL_000a: ldftn ""void D.F(ref T)"" + IL_0010: newobj ""<>A{00000001}..ctor(object, System.IntPtr)"" + IL_0015: dup + IL_0016: stsfld "" D.O__0_0.<0>__F"" + IL_001b: newobj ""<>f__AnonymousType0<>..ctor()"" + IL_0020: ldsfld ""System.Action< x>> D.O__0_0.<1>__Target"" + IL_0025: dup + IL_0026: brtrue.s IL_003b + IL_0028: pop + IL_0029: ldnull + IL_002a: ldftn ""void D.Target< x>>( x>)"" + IL_0030: newobj ""System.Action< x>>..ctor(object, System.IntPtr)"" + IL_0035: dup + IL_0036: stsfld ""System.Action< x>> D.O__0_0.<1>__Target"" + IL_003b: call ""void D.Invoke< x>>( x>, System.Action< x>>)"" + IL_0040: ret +} +"); + } + + [Fact] + public void AnonymousClass_AnonymousDelegate1() + { + var source = @" +using System; +class D +{ + public void Top() + { + static void Test(T t) + { + var f = F; + var a = new { x = f }; + + Invoke(a, Target); + } + } + + static void F(ref T t) { } + + static void Invoke(G t, Action f) { } + + static void Target(G t) { } +} +"; + CompileAndVerify(source).VerifyIL("D.g__Test|0_0", @" +{ + // Code size 65 (0x41) + .maxstack 3 + IL_0000: ldsfld "" D.<>O.<0>__F"" + IL_0005: dup + IL_0006: brtrue.s IL_001b + IL_0008: pop + IL_0009: ldnull + IL_000a: ldftn ""void D.F(ref T)"" + IL_0010: newobj ""<>A{00000001}..ctor(object, System.IntPtr)"" + IL_0015: dup + IL_0016: stsfld "" D.<>O.<0>__F"" + IL_001b: newobj ""<>f__AnonymousType0<>..ctor()"" + IL_0020: ldsfld ""System.Action< x>> D.<>O.<1>__Target"" + IL_0025: dup + IL_0026: brtrue.s IL_003b + IL_0028: pop + IL_0029: ldnull + IL_002a: ldftn ""void D.Target< x>>( x>)"" + IL_0030: newobj ""System.Action< x>>..ctor(object, System.IntPtr)"" + IL_0035: dup + IL_0036: stsfld ""System.Action< x>> D.<>O.<1>__Target"" + IL_003b: call ""void D.Invoke< x>>( x>, System.Action< x>>)"" + IL_0040: ret +} +"); + } + [Fact] public void Pointer_TypeScoped_CouldBeModuleScoped0() { From 0758ddcbc1ba7a621aa000266703821c6c987881 Mon Sep 17 00:00:00 2001 From: Paul Chen Date: Wed, 29 Dec 2021 13:12:55 +0800 Subject: [PATCH 28/66] Feedback: Remove confusing comments. Add an assert. --- .../Portable/Lowering/LocalRewriter/DelegateCacheRewriter.cs | 1 - src/Compilers/CSharp/Portable/Symbols/TypeSymbolExtensions.cs | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/DelegateCacheRewriter.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/DelegateCacheRewriter.cs index b1b483dcb1074..51852ce4d5452 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/DelegateCacheRewriter.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/DelegateCacheRewriter.cs @@ -69,7 +69,6 @@ private DelegateCacheContainer GetOrAddCacheContainer(TypeSymbol delegateType, M DelegateCacheContainer? container; // We don't need to synthesize a container for each and every function. - // For functions with zero arity, we can share the container with the closest generic ancestor function. // // For example: // void LF1() diff --git a/src/Compilers/CSharp/Portable/Symbols/TypeSymbolExtensions.cs b/src/Compilers/CSharp/Portable/Symbols/TypeSymbolExtensions.cs index f037bb36b20ad..9afedfeaf2482 100644 --- a/src/Compilers/CSharp/Portable/Symbols/TypeSymbolExtensions.cs +++ b/src/Compilers/CSharp/Portable/Symbols/TypeSymbolExtensions.cs @@ -674,6 +674,7 @@ private static bool IsTypeLessVisibleThan(TypeSymbol type, Symbol sym, ref Compo RoslynDebug.Assert(typeWithAnnotationsOpt.HasType == (type is null)); RoslynDebug.Assert(canDigThroughNullable == false || useDefaultType == false, "digging through nullable will cause early resolution of nullable types"); RoslynDebug.Assert(canDigThroughNullable == false || visitCustomModifiers == false); + RoslynDebug.Assert(!visitCustomModifiers || (visitCustomModifiers && typePredicate is { })); // In order to handle extremely "deep" types like "int[][][][][][][][][]...[]" // or int*****************...* we implement manual tail recursion rather than From 49c74207a7890a970e4aab968db53aa9ccc1937f Mon Sep 17 00:00:00 2001 From: Paul Chen Date: Wed, 29 Dec 2021 14:36:30 +0800 Subject: [PATCH 29/66] Feedback: Update assert. Use static field. --- .../Lowering/LocalRewriter/DelegateCacheRewriter.cs | 9 +++++---- .../CSharp/Portable/Symbols/TypeSymbolExtensions.cs | 2 +- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/DelegateCacheRewriter.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/DelegateCacheRewriter.cs index 51852ce4d5452..df9da43067357 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/DelegateCacheRewriter.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/DelegateCacheRewriter.cs @@ -2,6 +2,7 @@ // 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; using System.Collections.Generic; using System.Collections.Immutable; using System.Diagnostics; @@ -187,7 +188,7 @@ static bool usedTypeParametersContains(HashSet used, Immuta } private static void FindTypeParameters(TypeSymbol type, HashSet result) - => type.VisitType(TypeParameterSymbolCollector, result, visitCustomModifiers: true); + => type.VisitType(s_typeParameterSymbolCollector, result, visitCustomModifiers: true); private static void FindTypeParameters(MethodSymbol method, HashSet result) { @@ -195,11 +196,11 @@ private static void FindTypeParameters(MethodSymbol method, HashSet result, bool _) + private static readonly Func, bool, bool> s_typeParameterSymbolCollector = (typeSymbol, result, _) => { if (typeSymbol is TypeParameterSymbol typeParameter) { @@ -207,5 +208,5 @@ private static bool TypeParameterSymbolCollector(TypeSymbol typeSymbol, HashSet< } return false; - } + }; } diff --git a/src/Compilers/CSharp/Portable/Symbols/TypeSymbolExtensions.cs b/src/Compilers/CSharp/Portable/Symbols/TypeSymbolExtensions.cs index 9afedfeaf2482..cf479b36330f8 100644 --- a/src/Compilers/CSharp/Portable/Symbols/TypeSymbolExtensions.cs +++ b/src/Compilers/CSharp/Portable/Symbols/TypeSymbolExtensions.cs @@ -674,7 +674,7 @@ private static bool IsTypeLessVisibleThan(TypeSymbol type, Symbol sym, ref Compo RoslynDebug.Assert(typeWithAnnotationsOpt.HasType == (type is null)); RoslynDebug.Assert(canDigThroughNullable == false || useDefaultType == false, "digging through nullable will cause early resolution of nullable types"); RoslynDebug.Assert(canDigThroughNullable == false || visitCustomModifiers == false); - RoslynDebug.Assert(!visitCustomModifiers || (visitCustomModifiers && typePredicate is { })); + RoslynDebug.Assert(visitCustomModifiers == false || typePredicate is { }); // In order to handle extremely "deep" types like "int[][][][][][][][][]...[]" // or int*****************...* we implement manual tail recursion rather than From 67429d3eff9317f319e12bcbd0981c3f1bea3e84 Mon Sep 17 00:00:00 2001 From: Paul Chen Date: Wed, 29 Dec 2021 14:45:22 +0800 Subject: [PATCH 30/66] Feedback: Use CSharpCustomModifier.ModifierSymbol. --- src/Compilers/CSharp/Portable/Symbols/TypeSymbolExtensions.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Compilers/CSharp/Portable/Symbols/TypeSymbolExtensions.cs b/src/Compilers/CSharp/Portable/Symbols/TypeSymbolExtensions.cs index cf479b36330f8..4b3be2f2060cc 100644 --- a/src/Compilers/CSharp/Portable/Symbols/TypeSymbolExtensions.cs +++ b/src/Compilers/CSharp/Portable/Symbols/TypeSymbolExtensions.cs @@ -732,7 +732,7 @@ private static bool IsTypeLessVisibleThan(TypeSymbol type, Symbol sym, ref Compo foreach (var customModifier in typeWithAnnotationsOpt.CustomModifiers) { var result = VisitType( - typeWithAnnotationsOpt: default, type: (NamedTypeSymbol)customModifier.Modifier, + typeWithAnnotationsOpt: default, type: ((CSharpCustomModifier)customModifier).ModifierSymbol, typeWithAnnotationsPredicate, typePredicate, arg, canDigThroughNullable, useDefaultType, visitCustomModifiers); if (result is object) From d0912a9bc778dd7c80687d85f83a2d6a4d44b6f7 Mon Sep 17 00:00:00 2001 From: Paul Chen Date: Wed, 29 Dec 2021 15:07:11 +0800 Subject: [PATCH 31/66] Feedback: Avoid ImmutableArray allocation. --- .../Portable/Symbols/TypeSymbolExtensions.cs | 77 ++++++++++++------- 1 file changed, 48 insertions(+), 29 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Symbols/TypeSymbolExtensions.cs b/src/Compilers/CSharp/Portable/Symbols/TypeSymbolExtensions.cs index 4b3be2f2060cc..dc0d96149d317 100644 --- a/src/Compilers/CSharp/Portable/Symbols/TypeSymbolExtensions.cs +++ b/src/Compilers/CSharp/Portable/Symbols/TypeSymbolExtensions.cs @@ -758,52 +758,71 @@ private static bool IsTypeLessVisibleThan(TypeSymbol type, Symbol sym, ref Compo case TypeKind.Interface: case TypeKind.Delegate: - ImmutableArray typesWithAnnotations; + ImmutableArray typeArguments; if (current.IsAnonymousType) { var anonymous = (AnonymousTypeManager.AnonymousTypeOrDelegatePublicSymbol)current; var fields = anonymous.TypeDescriptor.Fields; - var builder = ArrayBuilder.GetInstance(); - foreach (var field in fields) + if (fields.IsEmpty) { - builder.Add(field.TypeWithAnnotations); + return null; } - typesWithAnnotations = builder.ToImmutableAndFree(); + int i; + for (i = 0; i < fields.Length - 1; i++) + { + // Let's try to avoid early resolution of nullable types + (TypeWithAnnotations nextTypeWithAnnotations, TypeSymbol? nextType) = getNextIterationElements(fields[i].TypeWithAnnotations, canDigThroughNullable); + var result = VisitType( + typeWithAnnotationsOpt: nextTypeWithAnnotations, + type: nextType, + typeWithAnnotationsPredicate, + typePredicate, + arg, + canDigThroughNullable, + useDefaultType, + visitCustomModifiers); + if (result is object) + { + return result; + } + } + + next = fields[i].TypeWithAnnotations; } else { - typesWithAnnotations = ((NamedTypeSymbol)current).TypeArgumentsWithAnnotationsNoUseSiteDiagnostics; - } + typeArguments = ((NamedTypeSymbol)current).TypeArgumentsWithAnnotationsNoUseSiteDiagnostics; - if (typesWithAnnotations.IsEmpty) - { - return null; - } + if (typeArguments.IsEmpty) + { + return null; + } - int i; - for (i = 0; i < typesWithAnnotations.Length - 1; i++) - { - // Let's try to avoid early resolution of nullable types - (TypeWithAnnotations nextTypeWithAnnotations, TypeSymbol? nextType) = getNextIterationElements(typesWithAnnotations[i], canDigThroughNullable); - var result = VisitType( - typeWithAnnotationsOpt: nextTypeWithAnnotations, - type: nextType, - typeWithAnnotationsPredicate, - typePredicate, - arg, - canDigThroughNullable, - useDefaultType, - visitCustomModifiers); - if (result is object) + int i; + for (i = 0; i < typeArguments.Length - 1; i++) { - return result; + // Let's try to avoid early resolution of nullable types + (TypeWithAnnotations nextTypeWithAnnotations, TypeSymbol? nextType) = getNextIterationElements(typeArguments[i], canDigThroughNullable); + var result = VisitType( + typeWithAnnotationsOpt: nextTypeWithAnnotations, + type: nextType, + typeWithAnnotationsPredicate, + typePredicate, + arg, + canDigThroughNullable, + useDefaultType, + visitCustomModifiers); + if (result is object) + { + return result; + } } - } - next = typesWithAnnotations[i]; + next = typeArguments[i]; + } break; From 48c5cd7412458f913e4d8a8583aba613afc6ddb8 Mon Sep 17 00:00:00 2001 From: Paul Chen Date: Wed, 29 Dec 2021 15:14:22 +0800 Subject: [PATCH 32/66] Fix var scope. --- src/Compilers/CSharp/Portable/Symbols/TypeSymbolExtensions.cs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Symbols/TypeSymbolExtensions.cs b/src/Compilers/CSharp/Portable/Symbols/TypeSymbolExtensions.cs index dc0d96149d317..3c0d7abe119c9 100644 --- a/src/Compilers/CSharp/Portable/Symbols/TypeSymbolExtensions.cs +++ b/src/Compilers/CSharp/Portable/Symbols/TypeSymbolExtensions.cs @@ -758,8 +758,6 @@ private static bool IsTypeLessVisibleThan(TypeSymbol type, Symbol sym, ref Compo case TypeKind.Interface: case TypeKind.Delegate: - ImmutableArray typeArguments; - if (current.IsAnonymousType) { var anonymous = (AnonymousTypeManager.AnonymousTypeOrDelegatePublicSymbol)current; @@ -794,7 +792,7 @@ private static bool IsTypeLessVisibleThan(TypeSymbol type, Symbol sym, ref Compo } else { - typeArguments = ((NamedTypeSymbol)current).TypeArgumentsWithAnnotationsNoUseSiteDiagnostics; + var typeArguments = ((NamedTypeSymbol)current).TypeArgumentsWithAnnotationsNoUseSiteDiagnostics; if (typeArguments.IsEmpty) { From 3784e4aafb0ee8a3054d4459e1c265b38cd891dd Mon Sep 17 00:00:00 2001 From: Paul Chen Date: Wed, 29 Dec 2021 18:06:31 +0800 Subject: [PATCH 33/66] Feedback: Add direct tests for VisitType. --- .../Portable/Symbols/TypeSymbolExtensions.cs | 2 +- .../Symbol/Symbols/SymbolExtensionTests.cs | 195 ++++++++++++++++++ 2 files changed, 196 insertions(+), 1 deletion(-) diff --git a/src/Compilers/CSharp/Portable/Symbols/TypeSymbolExtensions.cs b/src/Compilers/CSharp/Portable/Symbols/TypeSymbolExtensions.cs index 3c0d7abe119c9..a83da25243d79 100644 --- a/src/Compilers/CSharp/Portable/Symbols/TypeSymbolExtensions.cs +++ b/src/Compilers/CSharp/Portable/Symbols/TypeSymbolExtensions.cs @@ -647,7 +647,7 @@ private static bool IsTypeLessVisibleThan(TypeSymbol type, Symbol sym, ref Compo typePredicate: predicate, arg, canDigThroughNullable, - visitCustomModifiers); + visitCustomModifiers: visitCustomModifiers); } /// diff --git a/src/Compilers/CSharp/Test/Symbol/Symbols/SymbolExtensionTests.cs b/src/Compilers/CSharp/Test/Symbol/Symbols/SymbolExtensionTests.cs index 6c032e4930553..51374fee9ea55 100644 --- a/src/Compilers/CSharp/Test/Symbol/Symbols/SymbolExtensionTests.cs +++ b/src/Compilers/CSharp/Test/Symbol/Symbols/SymbolExtensionTests.cs @@ -5,9 +5,12 @@ #nullable disable using Microsoft.CodeAnalysis.CSharp.Symbols; +using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.CSharp.Test.Utilities; +using Microsoft.CodeAnalysis.PooledObjects; using Roslyn.Test.Utilities; using System; +using System.Linq; using Xunit; namespace Microsoft.CodeAnalysis.CSharp.UnitTests.Symbols @@ -84,6 +87,198 @@ class C { } HasNameQualifierCore(namespaceNames, compilation.GetMember("NB.C"), "NB"); } + [Fact] + public void VisitType_AnonymousDelegate() + { + var source = @" +void F(T t) +{ + var f = (ref T x) => 0; +} +"; + var compilation = CreateCompilation(source, options: TestOptions.DebugDll); + var tree = compilation.SyntaxTrees.Single(); + var identifier = tree.GetRoot().DescendantNodes().OfType().First(id => id.Identifier.Text == "var"); + var model = compilation.GetSemanticModel(tree); + var anonymousType = model.GetSymbolInfo(identifier).Symbol.GetSymbol(); + + Assert.True(anonymousType.ContainsTypeParameter()); + } + + [Fact] + public void VisitType_AnonymousDelegateWithAnonymousClass() + { + var source = @" +void F(T t) +{ + var f = (ref int x) => new { t }; +} +"; + var compilation = CreateCompilation(source, options: TestOptions.DebugDll); + var tree = compilation.SyntaxTrees.Single(); + var identifier = tree.GetRoot().DescendantNodes().OfType().First(id => id.Identifier.Text == "var"); + var model = compilation.GetSemanticModel(tree); + var anonymousType = model.GetSymbolInfo(identifier).Symbol.GetSymbol(); + + Assert.True(anonymousType.ContainsTypeParameter()); + } + + [Fact] + public void VisitType_AnonymousClass() + { + var source = @" +void F(T t) +{ + var f = new { t }; +} +"; + var compilation = CreateCompilation(source, options: TestOptions.DebugDll); + var tree = compilation.SyntaxTrees.Single(); + var identifier = tree.GetRoot().DescendantNodes().OfType().First(id => id.Identifier.Text == "var"); + var model = compilation.GetSemanticModel(tree); + var anonymousType = model.GetSymbolInfo(identifier).Symbol.GetSymbol(); + + Assert.True(anonymousType.ContainsTypeParameter()); + } + + [Fact] + public void VisitType_AnonymousClassWithAnonymousDelegate() + { + var source = @" +void F(T t) +{ + var f = (ref int x) => t; + var g = new { f }; +} +"; + var compilation = CreateCompilation(source, options: TestOptions.DebugDll); + var tree = compilation.SyntaxTrees.Single(); + var identifier = tree.GetRoot().DescendantNodes().OfType().Last(id => id.Identifier.Text == "var"); + var model = compilation.GetSemanticModel(tree); + var anonymousType = model.GetSymbolInfo(identifier).Symbol.GetSymbol(); + + Assert.True(anonymousType.ContainsTypeParameter()); + } + + [Fact] + public void VisitType_CustomModifiers() + { + var ilSource = @" +.class public auto ansi beforefieldinit C1`1 + extends System.Object +{ + // Methods + .method public hidebysig static + string Method () cil managed + { + // Method begins at RVA 0x2050 + // Code size 6 (0x6) + .maxstack 8 + + IL_0000: ldstr ""Method"" + IL_0005: ret + } // end of method C1`1::Method + + .method public hidebysig specialname rtspecialname + instance void .ctor () cil managed + { + // Method begins at RVA 0x2057 + // Code size 7 (0x7) + .maxstack 8 + + IL_0000: ldarg.0 + IL_0001: call instance void System.Object::.ctor() + IL_0006: ret + } // end of method C1`1::.ctor + +} // end of class C1`1 + +.class public auto ansi beforefieldinit C2`1 + extends System.Object +{ + // Methods + .method public hidebysig specialname rtspecialname + instance void .ctor () cil managed + { + // Method begins at RVA 0x2057 + // Code size 7 (0x7) + .maxstack 8 + + IL_0000: ldarg.0 + IL_0001: call instance void System.Object::.ctor() + IL_0006: ret + } // end of method C2`1::.ctor + +} // end of class C2`1 + +.class public auto ansi beforefieldinit C3`1 + extends class C1`1)> +{ + // Methods + .method public hidebysig specialname rtspecialname + instance void .ctor () cil managed + { + // Method begins at RVA 0x205f + // Code size 7 (0x7) + .maxstack 8 + + IL_0000: ldarg.0 + IL_0001: call instance void class C1`1::.ctor() + IL_0006: ret + } // end of method C3`1::.ctor + +} // end of class C3`1 +"; + + var source = @" +class Test +{ + static void Main() + { + M(); + } + + static void M() + { + System.Func x = C3.Method; + System.Console.WriteLine(x()); + } +} +"; + var compilation = CreateCompilationWithIL(source, ilSource, options: TestOptions.ReleaseExe); + + var tree = compilation.SyntaxTrees.Single(); + var model = compilation.GetSemanticModel(tree); + var method = model.GetSymbolInfo(tree.GetRoot().DescendantNodes().OfType().Where(id => id.Identifier.ValueText == "Method").Single()).Symbol.GetSymbol(); + + AssertEx.Equal("System.String C1)>.Method()", method.ToTestDisplayString()); + + var typeParameters = PooledHashSet.GetInstance(); + try + { + method.ContainingType.VisitType(static (typeSymbol, typeParameters, _) => + { + if (typeSymbol is TypeParameterSymbol typeParameter) + { + typeParameters.Add(typeParameter); + } + + return false; + }, + typeParameters, visitCustomModifiers: true); + + var typeParameter = typeParameters.Single(); + Assert.Equal("G", typeParameter.Name); + Assert.Equal("M", typeParameter.ContainingSymbol.Name); + } + finally + { + typeParameters.Free(); + } + + //CompileAndVerify(compilation, expectedOutput: @"Method"); + } + private void HasNameQualifierCore(string[] namespaceNames, NamedTypeSymbol type, string expectedName) { Assert.True(Array.IndexOf(namespaceNames, expectedName) >= 0); From 5113cdcab1ab49cd42254315a105bdc83c53e307 Mon Sep 17 00:00:00 2001 From: Paul Chen Date: Thu, 30 Dec 2021 11:28:47 +0800 Subject: [PATCH 34/66] Feedback: Remove commented code. --- .../CSharp/Test/Symbol/Symbols/SymbolExtensionTests.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/Compilers/CSharp/Test/Symbol/Symbols/SymbolExtensionTests.cs b/src/Compilers/CSharp/Test/Symbol/Symbols/SymbolExtensionTests.cs index 51374fee9ea55..8cd545b5dd241 100644 --- a/src/Compilers/CSharp/Test/Symbol/Symbols/SymbolExtensionTests.cs +++ b/src/Compilers/CSharp/Test/Symbol/Symbols/SymbolExtensionTests.cs @@ -275,8 +275,6 @@ static void M() { typeParameters.Free(); } - - //CompileAndVerify(compilation, expectedOutput: @"Method"); } private void HasNameQualifierCore(string[] namespaceNames, NamedTypeSymbol type, string expectedName) From 1d98b32f03f8046dde9a542a1f677d827e37d9e5 Mon Sep 17 00:00:00 2001 From: Paul Chen Date: Thu, 30 Dec 2021 16:08:04 +0800 Subject: [PATCH 35/66] Add test with custom modifiers with type parameter. --- ...odeGenMethodGroupConversionCachingTests.cs | 256 ++++++++++++++++++ 1 file changed, 256 insertions(+) diff --git a/src/Compilers/CSharp/Test/Emit2/CodeGen/CodeGenMethodGroupConversionCachingTests.cs b/src/Compilers/CSharp/Test/Emit2/CodeGen/CodeGenMethodGroupConversionCachingTests.cs index ee1bfa0eca03d..ed96393f3d2de 100644 --- a/src/Compilers/CSharp/Test/Emit2/CodeGen/CodeGenMethodGroupConversionCachingTests.cs +++ b/src/Compilers/CSharp/Test/Emit2/CodeGen/CodeGenMethodGroupConversionCachingTests.cs @@ -6,6 +6,7 @@ using System.Diagnostics; using Microsoft.CodeAnalysis.CSharp.Symbols; using Microsoft.CodeAnalysis.CSharp.Test.Utilities; +using Roslyn.Test.Utilities; using Xunit; namespace Microsoft.CodeAnalysis.CSharp.UnitTests.CodeGen; @@ -5147,4 +5148,259 @@ .locals init (System.Action V_0, //d1 }"); } + [Fact] + public void CustomModifiers_Method() + { + var ilSource = @" +.class public auto ansi beforefieldinit C1`1 + extends System.Object +{ + // Methods + .method public hidebysig static + string Method () cil managed + { + // Method begins at RVA 0x2050 + // Code size 6 (0x6) + .maxstack 8 + + IL_0000: ldstr ""PASS"" + IL_0005: ret + } // end of method C1`1::Method + + .method public hidebysig specialname rtspecialname + instance void .ctor () cil managed + { + // Method begins at RVA 0x2057 + // Code size 7 (0x7) + .maxstack 8 + + IL_0000: ldarg.0 + IL_0001: call instance void System.Object::.ctor() + IL_0006: ret + } // end of method C1`1::.ctor + +} // end of class C1`1 + +.class public auto ansi beforefieldinit C2`1 + extends System.Object +{ + // Methods + .method public hidebysig specialname rtspecialname + instance void .ctor () cil managed + { + // Method begins at RVA 0x2057 + // Code size 7 (0x7) + .maxstack 8 + + IL_0000: ldarg.0 + IL_0001: call instance void System.Object::.ctor() + IL_0006: ret + } // end of method C2`1::.ctor + +} // end of class C2`1 + +.class public auto ansi beforefieldinit C3`1 + extends class C1`1)> +{ + // Methods + .method public hidebysig specialname rtspecialname + instance void .ctor () cil managed + { + // Method begins at RVA 0x205f + // Code size 7 (0x7) + .maxstack 8 + + IL_0000: ldarg.0 + IL_0001: call instance void class C1`1::.ctor() + IL_0006: ret + } // end of method C3`1::.ctor + +} // end of class C3`1 +"; + var source = @" +class Test +{ + static void Main() + { + M(); + } + + static void M() + { + System.Func x = C3.Method; + System.Console.WriteLine(x()); + } +} +"; + var compilation = CreateCompilationWithIL(source, ilSource, options: TestOptions.ReleaseExe); + var verifier = CompileAndVerify(compilation, expectedOutput: PASS); + verifier.VerifyIL("Test.M", @" +{ + // Code size 38 (0x26) + .maxstack 2 + IL_0000: ldsfld ""System.Func Test.O__1_0.<0>__Method"" + IL_0005: dup + IL_0006: brtrue.s IL_001b + IL_0008: pop + IL_0009: ldnull + IL_000a: ldftn ""string C1.Method()"" + IL_0010: newobj ""System.Func..ctor(object, System.IntPtr)"" + IL_0015: dup + IL_0016: stsfld ""System.Func Test.O__1_0.<0>__Method"" + IL_001b: callvirt ""string System.Func.Invoke()"" + IL_0020: call ""void System.Console.WriteLine(string)"" + IL_0025: ret +} +"); + } + + [Fact] + public void CustomModifiers_Delegate() + { + var ilSource = @" +.class public auto ansi beforefieldinit C1`1 + extends [mscorlib]System.Object +{ + // Nested Types + .class nested public auto ansi sealed F + extends [mscorlib]System.MulticastDelegate + { + // Methods + .method public hidebysig specialname rtspecialname + instance void .ctor ( + object 'object', + native int 'method' + ) runtime managed + { + } // end of method F::.ctor + + .method public hidebysig newslot virtual + instance string Invoke () runtime managed + { + } // end of method F::Invoke + + .method public hidebysig newslot virtual + instance class [mscorlib]System.IAsyncResult BeginInvoke ( + class [mscorlib]System.AsyncCallback callback, + object 'object' + ) runtime managed + { + } // end of method F::BeginInvoke + + .method public hidebysig newslot virtual + instance string EndInvoke ( + class [mscorlib]System.IAsyncResult result + ) runtime managed + { + } // end of method F::EndInvoke + + } // end of class F + + + // Methods + .method public hidebysig + instance string Method () cil managed + { + // Method begins at RVA 0x2118 + // Code size 6 (0x6) + .maxstack 8 + + IL_0000: ldstr ""PASS"" + IL_0005: ret + } // end of method C1`1::Method + + .method public hidebysig specialname rtspecialname + instance void .ctor () cil managed + { + // Method begins at RVA 0x211f + // Code size 8 (0x8) + .maxstack 8 + + IL_0000: ldarg.0 + IL_0001: call instance void [mscorlib]System.Object::.ctor() + IL_0006: nop + IL_0007: ret + } // end of method C1`1::.ctor + +} // end of class C1`1 + +.class public auto ansi beforefieldinit C2`1 + extends System.Object +{ + // Methods + .method public hidebysig specialname rtspecialname + instance void .ctor () cil managed + { + // Method begins at RVA 0x2057 + // Code size 7 (0x7) + .maxstack 8 + + IL_0000: ldarg.0 + IL_0001: call instance void System.Object::.ctor() + IL_0006: ret + } // end of method C2`1::.ctor + +} // end of class C2`1 + +.class public auto ansi beforefieldinit C3`1 + extends class C1`1)> +{ + // Methods + .method public hidebysig specialname rtspecialname + instance void .ctor () cil managed + { + // Method begins at RVA 0x205f + // Code size 7 (0x7) + .maxstack 8 + + IL_0000: ldarg.0 + IL_0001: call instance void class C1`1::.ctor() + IL_0006: ret + } // end of method C3`1::.ctor + +} // end of class C3`1 +"; + var source = @" +class Test +{ + static void Main() + { + M(); + } + + static void M() + { + C3.F x = Method; + System.Console.WriteLine(x()); + } + + static string Method() => ""PASS""; +} +"; + var compilation = CreateCompilationWithIL(source, ilSource, options: TestOptions.DebugExe); + var verifier = CompileAndVerify(compilation, expectedOutput: PASS); + verifier.VerifyIL("Test.M", @" +{ + // Code size 42 (0x2a) + .maxstack 2 + .locals init (C1.F V_0) //x + IL_0000: nop + IL_0001: ldsfld ""C1.F Test.O__1_0.<0>__Method"" + IL_0006: dup + IL_0007: brtrue.s IL_001c + IL_0009: pop + IL_000a: ldnull + IL_000b: ldftn ""string Test.Method()"" + IL_0011: newobj ""C1.F..ctor(object, System.IntPtr)"" + IL_0016: dup + IL_0017: stsfld ""C1.F Test.O__1_0.<0>__Method"" + IL_001c: stloc.0 + IL_001d: ldloc.0 + IL_001e: callvirt ""string C1.F.Invoke()"" + IL_0023: call ""void System.Console.WriteLine(string)"" + IL_0028: nop + IL_0029: ret +} +"); + } } From fcb0159216f3217466281bb16ffcce0df073bce1 Mon Sep 17 00:00:00 2001 From: Paul Chen Date: Fri, 31 Dec 2021 00:23:07 +0800 Subject: [PATCH 36/66] Simplify container validations when possible. Add type parameter verification for custom modifiers. --- ...odeGenMethodGroupConversionCachingTests.cs | 536 +++--------------- 1 file changed, 90 insertions(+), 446 deletions(-) diff --git a/src/Compilers/CSharp/Test/Emit2/CodeGen/CodeGenMethodGroupConversionCachingTests.cs b/src/Compilers/CSharp/Test/Emit2/CodeGen/CodeGenMethodGroupConversionCachingTests.cs index ed96393f3d2de..3d2705c97dca9 100644 --- a/src/Compilers/CSharp/Test/Emit2/CodeGen/CodeGenMethodGroupConversionCachingTests.cs +++ b/src/Compilers/CSharp/Test/Emit2/CodeGen/CodeGenMethodGroupConversionCachingTests.cs @@ -3,6 +3,7 @@ // See the LICENSE file in the project root for more information. using System; +using System.Collections.Generic; using System.Diagnostics; using Microsoft.CodeAnalysis.CSharp.Symbols; using Microsoft.CodeAnalysis.CSharp.Test.Utilities; @@ -31,10 +32,8 @@ public static void Main(string[] args) static void Target() { Console.WriteLine(""FAIL""); } static void Invoke(D x, D y) { Console.Write(Object.ReferenceEquals(x, y) ? ""FAIL"" : ""PASS""); } }"; - static void containerValidator(ModuleSymbol module) - => Assert.Null(module.GlobalNamespace.GetMember("<>O")); - - CompileAndVerify(source, symbolValidator: containerValidator, expectedOutput: PASS); + var verifier = CompileAndVerify(source, expectedOutput: PASS); + Assert.Empty(verifier.TestData.Module!.GetAllSynthesizedMembers()); } [Fact] @@ -54,11 +53,8 @@ public static void Main(string[] args) void Target() { Console.WriteLine(""FAIL""); } void Invoke(D x, D y) { Console.Write(Object.ReferenceEquals(x, y) ? ""FAIL"" : ""PASS""); } }"; - static void containerValidator(ModuleSymbol module) - { - Assert.Null(module.GlobalNamespace.GetMember("<>O")); - } - CompileAndVerify(source, symbolValidator: containerValidator, expectedOutput: PASS); + var verifier = CompileAndVerify(source, expectedOutput: PASS); + Assert.Empty(verifier.TestData.Module!.GetAllSynthesizedMembers()); } [Fact] @@ -82,11 +78,8 @@ static class E public static void Target(this C that) { Console.WriteLine(""FAIL""); } } "; - static void containerValidator(ModuleSymbol module) - { - Assert.Null(module.GlobalNamespace.GetMember("<>O")); - } - CompileAndVerify(source, symbolValidator: containerValidator, expectedOutput: PASS); + var verifier = CompileAndVerify(source, expectedOutput: PASS); + Assert.Empty(verifier.TestData.Module!.GetAllSynthesizedMembers()); } [Fact] @@ -110,11 +103,8 @@ static class E public static void Target(this C that) { Console.WriteLine(""FAIL""); } } "; - static void containerValidator(ModuleSymbol module) - { - Assert.Null(module.GlobalNamespace.GetMember("<>O")); - } - CompileAndVerify(source, symbolValidator: containerValidator, expectedOutput: PASS); + var verifier = CompileAndVerify(source, expectedOutput: PASS); + Assert.Empty(verifier.TestData.Module!.GetAllSynthesizedMembers()); } [Fact] @@ -133,11 +123,8 @@ public static void Main(string[] args) static int Target(int x) => 0; } "; - static void containerValidator(ModuleSymbol module) - { - Assert.Null(module.GlobalNamespace.GetMember("<>O")); - } - CompileAndVerify(source, symbolValidator: containerValidator); + var verifier = CompileAndVerify(source); + Assert.DoesNotContain(verifier.TestData.Module!.GetAllSynthesizedMembers(), s => s.Key.Name.Contains("<>O")); } [Fact] @@ -156,11 +143,8 @@ public static void Main(string[] args) static int Target(int x) => 0; } "; - static void containerValidator(ModuleSymbol module) - { - Assert.Null(module.GlobalNamespace.GetMember("<>O")); - } - CompileAndVerify(source, symbolValidator: containerValidator); + var verifier = CompileAndVerify(source); + Assert.DoesNotContain(verifier.TestData.Module!.GetAllSynthesizedMembers(), s => s.Key.Name.Contains("<>O")); } [Fact] @@ -174,11 +158,8 @@ class C static void Target() { } } "; - static void containerValidator(ModuleSymbol module) - { - Assert.Null(module.GlobalNamespace.GetMember("<>O")); - } - CompileAndVerify(source, symbolValidator: containerValidator); + var verifier = CompileAndVerify(source); + Assert.Empty(verifier.TestData.Module!.GetAllSynthesizedMembers()); } [Fact] @@ -197,11 +178,8 @@ static C() } } "; - static void containerValidator(ModuleSymbol module) - { - Assert.Null(module.GlobalNamespace.GetMember("<>O")); - } - CompileAndVerify(source, symbolValidator: containerValidator); + var verifier = CompileAndVerify(source); + Assert.Empty(verifier.TestData.Module!.GetAllSynthesizedMembers()); } [Fact] @@ -2515,27 +2493,8 @@ class D public static void Target() { } } "; - static void containerValidator(ModuleSymbol module) - { - var container = module.GlobalNamespace.GetMember("C.<>O"); - Assert.NotNull(container); Debug.Assert(container is { }); - Assert.Equal(0, container.Arity); - - var members = container.GetMembers(); - Assert.Equal(1, members.Length); - - var field = members[0] as FieldSymbol; - Assert.NotNull(field); Debug.Assert(field is { }); - Assert.Equal("<0>__Target", field.Name); - - var fieldType = field.Type as NamedTypeSymbol; - Assert.NotNull(fieldType); Debug.Assert(fieldType is { }); - Assert.True(fieldType.IsDelegateType()); - Assert.Equal("System", fieldType.ContainingNamespace.Name); - Assert.Equal("Action", fieldType.Name); - Assert.Equal(0, fieldType.Arity); - } - CompileAndVerify(source, symbolValidator: containerValidator); + var verifier = CompileAndVerify(source); + verifier.VerifySynthesizedFields("C.<>O", "System.Action <0>__Target"); } [Fact] @@ -2550,28 +2509,8 @@ class C static V Target() { return default(V); } } "; - static void containerValidator(ModuleSymbol module) - { - var container = module.GlobalNamespace.GetMember("C.<>O"); - Assert.NotNull(container); Debug.Assert(container is { }); - Assert.Equal(0, container.Arity); - - var members = container.GetMembers(); - Assert.Equal(1, members.Length); - - var field = members[0] as FieldSymbol; - Assert.NotNull(field); Debug.Assert(field is { }); - Assert.Equal("<0>__Target", field.Name); - - var fieldType = field.Type as NamedTypeSymbol; - Assert.NotNull(fieldType); Debug.Assert(fieldType is { }); - Assert.True(fieldType.IsDelegateType()); - Assert.Equal("System", fieldType.ContainingNamespace.Name); - Assert.Equal("Func", fieldType.Name); - Assert.Equal(1, fieldType.Arity); - Assert.Equal(module.GlobalNamespace.GetTypeMember("C").TypeParameters[0], fieldType.TypeArguments()[0]); - } - CompileAndVerify(source, symbolValidator: containerValidator); + var verifier = CompileAndVerify(source); + verifier.VerifySynthesizedFields("C.<>O", "System.Func <0>__Target"); } [Fact] @@ -2587,23 +2526,8 @@ class C static T Target() { return default(T); } } "; - static void containerValidator(ModuleSymbol module) - { - var container = module.GlobalNamespace.GetMember("C.<>O"); - Assert.NotNull(container); Debug.Assert(container is { }); - Assert.Equal(0, container.Arity); - - var members = container.GetMembers(); - Assert.Equal(1, members.Length); - - var field = members[0] as FieldSymbol; - Assert.NotNull(field); Debug.Assert(field is { }); - Assert.Equal("<0>__Target", field.Name); - - var fieldType = field.Type as NamedTypeSymbol; - Assert.Equal(module.GlobalNamespace.GetMember("C.MyFunc"), fieldType); - } - CompileAndVerify(source, symbolValidator: containerValidator); + var verifier = CompileAndVerify(source); + verifier.VerifySynthesizedFields("C.<>O", "C.MyFunc <0>__Target"); } [Fact] @@ -2624,27 +2548,8 @@ class D public static void Target() { } } "; - static void containerValidator(ModuleSymbol module) - { - var container = module.GlobalNamespace.GetMember("C.O__0_0"); - Assert.NotNull(container); Debug.Assert(container is { }); - Assert.True(container.IsGenericType); - - var members = container.GetMembers(); - Assert.Equal(1, members.Length); - - var field = members[0] as FieldSymbol; - Assert.NotNull(field); Debug.Assert(field is { }); - Assert.Equal("<0>__Target", field.Name); - - var fieldType = field.Type as NamedTypeSymbol; - Assert.NotNull(fieldType); Debug.Assert(fieldType is { }); - Assert.True(fieldType.IsDelegateType()); - Assert.Equal("System", fieldType.ContainingNamespace.Name); - Assert.Equal("Action", fieldType.Name); - Assert.Equal(0, fieldType.Arity); - } - CompileAndVerify(source, symbolValidator: containerValidator); + var verifier = CompileAndVerify(source); + verifier.VerifySynthesizedFields("C.O__0_0", "System.Action <0>__Target"); } [Fact] @@ -2665,30 +2570,8 @@ class D public static B Target(H h) => default(B); } "; - static void containerValidator(ModuleSymbol module) - { - var testClass = module.GlobalNamespace.GetTypeMember("C"); - var container = testClass.GetTypeMember("O__0_0"); - Assert.NotNull(container); Debug.Assert(container is { }); - Assert.Equal(1, container.Arity); - - var members = container.GetMembers(); - Assert.Equal(1, members.Length); - - var field = members[0] as FieldSymbol; - Assert.NotNull(field); Debug.Assert(field is { }); - Assert.Equal("<0>__Target", field.Name); - - var fieldType = field.Type as NamedTypeSymbol; - Assert.NotNull(fieldType); Debug.Assert(fieldType is { }); - Assert.True(fieldType.IsDelegateType()); - Assert.Equal("System", fieldType.ContainingNamespace.Name); - Assert.Equal("Func", fieldType.Name); - Assert.Equal(2, fieldType.Arity); - Assert.Equal(testClass.TypeParameters[0], fieldType.TypeArguments()[0]); - Assert.Equal(container.TypeParameters[0], fieldType.TypeArguments()[1]); - } - CompileAndVerify(source, symbolValidator: containerValidator); + var verifier = CompileAndVerify(source); + verifier.VerifySynthesizedFields("C.O__0_0", "System.Func <0>__Target"); } [Fact] @@ -2711,25 +2594,8 @@ static class D public static B Target(this int num) => default(B); } "; - static void containerValidator(ModuleSymbol module) - { - var container = module.GlobalNamespace.GetMember("C.O__2_0"); - Assert.NotNull(container); Debug.Assert(container is { }); - Assert.Equal(1, container.Arity); - - var members = container.GetMembers(); - Assert.Equal(1, members.Length); - - var field = members[0] as FieldSymbol; - Assert.NotNull(field); Debug.Assert(field is { }); - Assert.Equal("<0>__Target", field.Name); - - var fieldType = field.Type as NamedTypeSymbol; - Assert.NotNull(fieldType); Debug.Assert(fieldType is { }); - Assert.True(fieldType.IsDelegateType()); - Assert.Equal(module.GlobalNamespace.GetMember("C.MyFunc"), fieldType); - } - CompileAndVerify(source, symbolValidator: containerValidator); + var verifier = CompileAndVerify(source); + verifier.VerifySynthesizedFields("C.O__2_0", "C.MyFunc <0>__Target"); } [Fact] @@ -2798,107 +2664,17 @@ static class E public static void Target5(this N n) { } } "; - static void containerValidator(ModuleSymbol module) - { - var A = module.GlobalNamespace.GetTypeMember("A"); - var B = A.GetTypeMember("B"); - var T = A.TypeParameters[0]; - var V = B.TypeParameters[0]; - - var container = B.GetTypeMember("<>O"); - Assert.NotNull(container); Debug.Assert(container is { }); - Assert.Equal(0, container.Arity); - - var members = container.GetMembers(); - Assert.Equal(7, members.Length); - - var field0 = members[0] as FieldSymbol; - Assert.NotNull(field0); Debug.Assert(field0 is { }); - Assert.Equal("<0>__Target0", field0.Name); - - var fieldType0 = field0.Type as NamedTypeSymbol; - Assert.NotNull(fieldType0); Debug.Assert(fieldType0 is { }); - Assert.True(fieldType0.IsDelegateType()); - Assert.Equal("System", fieldType0.ContainingNamespace.Name); - Assert.Equal("Action", fieldType0.Name); - Assert.Equal(1, fieldType0.Arity); - Assert.Equal(T, fieldType0.TypeArguments()[0]); - - var field1 = members[1] as FieldSymbol; - Assert.NotNull(field1); Debug.Assert(field1 is { }); - Assert.Equal("<1>__Target1", field1.Name); - - var fieldType1 = field1.Type as NamedTypeSymbol; - Assert.NotNull(fieldType1); Debug.Assert(fieldType1 is { }); - Assert.True(fieldType1.IsDelegateType()); - Assert.Equal("System", fieldType1.ContainingNamespace.Name); - Assert.Equal("Action", fieldType1.Name); - Assert.Equal(1, fieldType1.Arity); - Assert.Equal(T, fieldType1.TypeArguments()[0]); - - var field2 = members[2] as FieldSymbol; - Assert.NotNull(field2); Debug.Assert(field2 is { }); - Assert.Equal("<2>__Target2", field2.Name); - - var fieldType2 = field2.Type as NamedTypeSymbol; - Assert.NotNull(fieldType2); Debug.Assert(fieldType2 is { }); - Assert.True(fieldType2.IsDelegateType()); - Assert.Equal("System", fieldType2.ContainingNamespace.Name); - Assert.Equal("Action", fieldType2.Name); - Assert.Equal(1, fieldType2.Arity); - Assert.Equal(T, fieldType2.TypeArguments()[0]); - - var field3 = members[3] as FieldSymbol; - Assert.NotNull(field3); Debug.Assert(field3 is { }); - Assert.Equal("<3>__Target3", field3.Name); - - var fieldType3 = field3.Type as NamedTypeSymbol; - Assert.NotNull(fieldType3); Debug.Assert(fieldType3 is { }); - Assert.True(fieldType3.IsDelegateType()); - Assert.Equal("System", fieldType3.ContainingNamespace.Name); - Assert.Equal("Action", fieldType3.Name); - Assert.Equal(2, fieldType3.Arity); - Assert.Equal(T, fieldType3.TypeArguments()[0]); - Assert.Equal(V, fieldType3.TypeArguments()[1]); - - var field4 = members[4] as FieldSymbol; - Assert.NotNull(field4); Debug.Assert(field4 is { }); - Assert.Equal("<4>__Target4", field4.Name); - - var fieldType4 = field4.Type as NamedTypeSymbol; - Assert.NotNull(fieldType4); Debug.Assert(fieldType4 is { }); - Assert.True(fieldType4.IsDelegateType()); - Assert.Equal("System", fieldType4.ContainingNamespace.Name); - Assert.Equal("Action", fieldType4.Name); - Assert.Equal(2, fieldType4.Arity); - Assert.Equal(T, fieldType4.TypeArguments()[0]); - Assert.Equal(V, fieldType4.TypeArguments()[1]); - - var field5 = members[5] as FieldSymbol; - Assert.NotNull(field5); Debug.Assert(field5 is { }); - Assert.Equal("<5>__Target5", field5.Name); - - var fieldType5 = field5.Type as NamedTypeSymbol; - Assert.NotNull(fieldType5); Debug.Assert(fieldType5 is { }); - Assert.True(fieldType5.IsDelegateType()); - Assert.Equal("System", fieldType5.ContainingNamespace.Name); - Assert.Equal("Action", fieldType5.Name); - Assert.Equal(1, fieldType5.Arity); - Assert.Equal(T, fieldType5.TypeArguments()[0]); - - var field6 = members[6] as FieldSymbol; - Assert.NotNull(field6); Debug.Assert(field6 is { }); - Assert.Equal("<6>__Target5", field6.Name); - - var fieldType6 = field6.Type as NamedTypeSymbol; - Assert.NotNull(fieldType6); Debug.Assert(fieldType6 is { }); - Assert.True(fieldType6.IsDelegateType()); - Assert.Equal("System", fieldType6.ContainingNamespace.Name); - Assert.Equal("Action", fieldType6.Name); - Assert.Equal(1, fieldType6.Arity); - Assert.Equal(V, fieldType6.TypeArguments()[0]); - } - CompileAndVerify(source, symbolValidator: containerValidator); + var verifier = CompileAndVerify(source); + verifier.VerifySynthesizedFields( + "A.B.<>O" + , "System.Action <0>__Target0" + , "System.Action <1>__Target1" + , "System.Action <2>__Target2" + , "System.Action <3>__Target3" + , "System.Action <4>__Target4" + , "System.Action <5>__Target5" + , "System.Action <6>__Target5" + ); } [Fact] @@ -2929,66 +2705,14 @@ static class E public static void Target3(this C c) { } } "; - static void containerValidator(ModuleSymbol module) - { - var testClass = module.GlobalNamespace.GetTypeMember("C"); - var container = testClass.GetTypeMember("O__0_0"); - Assert.NotNull(container); Debug.Assert(container is { }); - - Assert.Equal(1, container.Arity); - var T = container.TypeParameters[0]; - - var members = container.GetMembers(); - Assert.Equal(4, members.Length); - - - var field0 = members[0] as FieldSymbol; - Assert.NotNull(field0); Debug.Assert(field0 is { }); - Assert.Equal("<0>__Target0", field0.Name); - - var fieldType0 = field0.Type as NamedTypeSymbol; - Assert.NotNull(fieldType0); Debug.Assert(fieldType0 is { }); - Assert.True(fieldType0.IsDelegateType()); - Assert.Equal("System", fieldType0.ContainingNamespace.Name); - Assert.Equal("Action", fieldType0.Name); - Assert.Equal(0, fieldType0.Arity); - - var field1 = members[1] as FieldSymbol; - Assert.NotNull(field1); Debug.Assert(field1 is { }); - Assert.Equal("<1>__Target1", field1.Name); - - var fieldType1 = field1.Type as NamedTypeSymbol; - Assert.NotNull(fieldType1); Debug.Assert(fieldType1 is { }); - Assert.True(fieldType1.IsDelegateType()); - Assert.Equal("System", fieldType1.ContainingNamespace.Name); - Assert.Equal("Action", fieldType1.Name); - Assert.Equal(1, fieldType1.Arity); - Assert.Equal(T, fieldType1.TypeArguments()[0]); - - var field2 = members[2] as FieldSymbol; - Assert.NotNull(field2); Debug.Assert(field2 is { }); - Assert.Equal("<2>__Target2", field2.Name); - - var fieldType2 = field2.Type as NamedTypeSymbol; - Assert.NotNull(fieldType2); Debug.Assert(fieldType2 is { }); - Assert.True(fieldType2.IsDelegateType()); - Assert.Equal("System", fieldType2.ContainingNamespace.Name); - Assert.Equal("Action", fieldType2.Name); - Assert.Equal(0, fieldType2.Arity); - - var field3 = members[3] as FieldSymbol; - Assert.NotNull(field3); Debug.Assert(field3 is { }); - Assert.Equal("<3>__Target3", field3.Name); - - var fieldType3 = field3.Type as NamedTypeSymbol; - Assert.NotNull(fieldType3); Debug.Assert(fieldType3 is { }); - Assert.True(fieldType3.IsDelegateType()); - Assert.Equal("System", fieldType3.ContainingNamespace.Name); - Assert.Equal("Action", fieldType3.Name); - Assert.Equal(1, fieldType3.Arity); - Assert.Equal(testClass, fieldType3.TypeArguments()[0]); - } - CompileAndVerify(source, symbolValidator: containerValidator); + var verifier = CompileAndVerify(source); + verifier.VerifySynthesizedFields( + "C.O__0_0" + , "System.Action <0>__Target0" + , "System.Action <1>__Target1" + , "System.Action <2>__Target2" + , "System.Action <3>__Target3" + ); } [Fact] @@ -3019,68 +2743,14 @@ static void Test() public static void Target3(this C c) { } } "; - static void containerValidator(ModuleSymbol module) - { - var testClass = module.GlobalNamespace.GetTypeMember("E"); - var container = testClass.GetTypeMember("O__0_0"); - Assert.NotNull(container); Debug.Assert(container is { }); - - Assert.Equal(1, container.Arity); - var T = container.TypeParameters[0]; - var C = module.GlobalNamespace.GetTypeMember("C"); - - var members = container.GetMembers(); - Assert.Equal(4, members.Length); - - - var field0 = members[0] as FieldSymbol; - Assert.NotNull(field0); Debug.Assert(field0 is { }); - Assert.Equal("<0>__Target0", field0.Name); - - var fieldType0 = field0.Type as NamedTypeSymbol; - Assert.NotNull(fieldType0); Debug.Assert(fieldType0 is { }); - Assert.True(fieldType0.IsDelegateType()); - Assert.Equal("System", fieldType0.ContainingNamespace.Name); - Assert.Equal("Action", fieldType0.Name); - Assert.Equal(0, fieldType0.Arity); - - var field1 = members[1] as FieldSymbol; - Assert.NotNull(field1); Debug.Assert(field1 is { }); - Assert.Equal("<1>__Target1", field1.Name); - - var fieldType1 = field1.Type as NamedTypeSymbol; - Assert.NotNull(fieldType1); Debug.Assert(fieldType1 is { }); - Assert.True(fieldType1.IsDelegateType()); - Assert.Equal("System", fieldType1.ContainingNamespace.Name); - Assert.Equal("Action", fieldType1.Name); - Assert.Equal(1, fieldType1.Arity); - Assert.Equal(T, fieldType1.TypeArguments()[0]); - - var field2 = members[2] as FieldSymbol; - Assert.NotNull(field2); Debug.Assert(field2 is { }); - Assert.Equal("<2>__Target2", field2.Name); - - var fieldType2 = field2.Type as NamedTypeSymbol; - Assert.NotNull(fieldType2); Debug.Assert(fieldType2 is { }); - Assert.True(fieldType2.IsDelegateType()); - Assert.Equal("System", fieldType2.ContainingNamespace.Name); - Assert.Equal("Action", fieldType2.Name); - Assert.Equal(0, fieldType2.Arity); - - var field3 = members[3] as FieldSymbol; - Assert.NotNull(field3); Debug.Assert(field3 is { }); - Assert.Equal("<3>__Target3", field3.Name); - - var fieldType3 = field3.Type as NamedTypeSymbol; - Assert.NotNull(fieldType3); Debug.Assert(fieldType3 is { }); - Assert.True(fieldType3.IsDelegateType()); - Assert.Equal("System", fieldType3.ContainingNamespace.Name); - Assert.Equal("Action", fieldType3.Name); - Assert.Equal(1, fieldType3.Arity); - Assert.Equal(C, fieldType3.TypeArguments()[0]); - } - - CompileAndVerify(source, symbolValidator: containerValidator); + var verifier = CompileAndVerify(source); + verifier.VerifySynthesizedFields( + "E.O__0_0" + , "System.Action <0>__Target0" + , "System.Action <1>__Target1" + , "System.Action <2>__Target2" + , "System.Action <3>__Target3" + ); } [Fact] @@ -3118,30 +2788,11 @@ void LF5() } } "; - static void containerValidator(ModuleSymbol module) - { - var testClass = module.GlobalNamespace.GetTypeMember("E"); - var container = testClass.GetTypeMember("O__0_0"); - Assert.NotNull(container); Debug.Assert(container is { }); - - Assert.Equal(1, container.Arity); - - var members = container.GetMembers(); - Assert.Equal(1, members.Length); - - var field0 = members[0] as FieldSymbol; - Assert.NotNull(field0); Debug.Assert(field0 is { }); - Assert.Equal("<0>__Target", field0.Name); - - var fieldType0 = field0.Type as NamedTypeSymbol; - Assert.NotNull(fieldType0); Debug.Assert(fieldType0 is { }); - Assert.True(fieldType0.IsDelegateType()); - Assert.Equal("System", fieldType0.ContainingNamespace.Name); - Assert.Equal("Action", fieldType0.Name); - Assert.Equal(1, fieldType0.Arity); - } - - CompileAndVerify(source, symbolValidator: containerValidator); + var verifier = CompileAndVerify(source); + verifier.VerifySynthesizedFields( + "E.O__0_0" + , "System.Action <0>__Target" + ); } [Fact] @@ -3178,41 +2829,12 @@ static void LF2() { } } } "; - static void containerValidator(ModuleSymbol module) - { - var testClass = module.GlobalNamespace.GetTypeMember("E"); - var container = testClass.GetTypeMember("O__0_0"); - Assert.NotNull(container); Debug.Assert(container is { }); - - Assert.Equal(2, container.Arity); - - var members = container.GetMembers(); - Assert.Equal(2, members.Length); - - var field0 = members[0] as FieldSymbol; - Assert.NotNull(field0); Debug.Assert(field0 is { }); - Assert.Equal("<0>__LF2", field0.Name); - - var fieldType0 = field0.Type as NamedTypeSymbol; - Assert.NotNull(fieldType0); Debug.Assert(fieldType0 is { }); - Assert.True(fieldType0.IsDelegateType()); - Assert.Equal("System", fieldType0.ContainingNamespace.Name); - Assert.Equal("Action", fieldType0.Name); - Assert.Equal(0, fieldType0.Arity); - - var field1 = members[1] as FieldSymbol; - Assert.NotNull(field1); Debug.Assert(field1 is { }); - Assert.Equal("<1>__LF2", field1.Name); - - var fieldType1 = field1.Type as NamedTypeSymbol; - Assert.NotNull(fieldType1); Debug.Assert(fieldType1 is { }); - Assert.True(fieldType1.IsDelegateType()); - Assert.Equal("System", fieldType1.ContainingNamespace.Name); - Assert.Equal("Action", fieldType1.Name); - Assert.Equal(0, fieldType1.Arity); - } - - CompileAndVerify(source, symbolValidator: containerValidator); + var verifier = CompileAndVerify(source); + verifier.VerifySynthesizedFields( + "E.O__0_0" + , "System.Action <0>__LF2" + , "System.Action <1>__LF2" + ); } [Fact] @@ -5377,8 +4999,30 @@ static void M() static string Method() => ""PASS""; } "; + static void containerValidator(ModuleSymbol module) + { + var container = module.GlobalNamespace.GetMember("Test.O__1_0"); + var field = Assert.Single(container.GetMembers()) as FieldSymbol; + Assert.NotNull(field); Debug.Assert(field is { }); + + var typeParameters = new List(); + field.Type.VisitType(static (typeSymbol, typeParameters, _) => + { + if (typeSymbol is TypeParameterSymbol typeParameter) + { + typeParameters.Add(typeParameter); + } + + return false; + }, + typeParameters, visitCustomModifiers: true); + + var typeParameter = Assert.Single(typeParameters); + Assert.Equal("G", typeParameter.Name); + Assert.Equal("O__1_0", typeParameter.ContainingSymbol.Name); + } var compilation = CreateCompilationWithIL(source, ilSource, options: TestOptions.DebugExe); - var verifier = CompileAndVerify(compilation, expectedOutput: PASS); + var verifier = CompileAndVerify(compilation, expectedOutput: PASS, symbolValidator: containerValidator); verifier.VerifyIL("Test.M", @" { // Code size 42 (0x2a) From 60b72d6f6865712811bfeea42190b0d7d386e035 Mon Sep 17 00:00:00 2001 From: Paul Chen Date: Fri, 31 Dec 2021 00:31:05 +0800 Subject: [PATCH 37/66] Feedback: Remove unused method. --- .../CodeGenMethodGroupConversionCachingTests.cs | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/src/Compilers/CSharp/Test/Emit2/CodeGen/CodeGenMethodGroupConversionCachingTests.cs b/src/Compilers/CSharp/Test/Emit2/CodeGen/CodeGenMethodGroupConversionCachingTests.cs index 3d2705c97dca9..5dc083dec616c 100644 --- a/src/Compilers/CSharp/Test/Emit2/CodeGen/CodeGenMethodGroupConversionCachingTests.cs +++ b/src/Compilers/CSharp/Test/Emit2/CodeGen/CodeGenMethodGroupConversionCachingTests.cs @@ -4920,17 +4920,6 @@ class [mscorlib]System.IAsyncResult result // Methods - .method public hidebysig - instance string Method () cil managed - { - // Method begins at RVA 0x2118 - // Code size 6 (0x6) - .maxstack 8 - - IL_0000: ldstr ""PASS"" - IL_0005: ret - } // end of method C1`1::Method - .method public hidebysig specialname rtspecialname instance void .ctor () cil managed { From ee75f3ced82745e8ad44cd55f6dc6d54369e7924 Mon Sep 17 00:00:00 2001 From: Paul Chen Date: Fri, 31 Dec 2021 11:48:19 +0800 Subject: [PATCH 38/66] Use AssertEx.NotNull. --- .../CodeGenMethodGroupConversionCachingTests.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Compilers/CSharp/Test/Emit2/CodeGen/CodeGenMethodGroupConversionCachingTests.cs b/src/Compilers/CSharp/Test/Emit2/CodeGen/CodeGenMethodGroupConversionCachingTests.cs index 5dc083dec616c..97c492d0882b8 100644 --- a/src/Compilers/CSharp/Test/Emit2/CodeGen/CodeGenMethodGroupConversionCachingTests.cs +++ b/src/Compilers/CSharp/Test/Emit2/CodeGen/CodeGenMethodGroupConversionCachingTests.cs @@ -1721,7 +1721,7 @@ static void containerValidator(ModuleSymbol module) { var testClass = module.GlobalNamespace.GetTypeMember("D"); var container = testClass.GetTypeMember("O__1_0"); - Assert.NotNull(container); Debug.Assert(container is { }); + AssertEx.NotNull(container); var typeParameters = container.TypeParameters; Assert.Equal(1, container.TypeParameters.Length); @@ -1782,7 +1782,7 @@ static void containerValidator(ModuleSymbol module) var globalNs = module.GlobalNamespace; var mainClass = globalNs.GetTypeMember("C"); var container = globalNs.GetMember("D.O__1_0"); - Assert.NotNull(container); Debug.Assert(container is { }); + AssertEx.NotNull(container); var typeParameters = container.TypeParameters; Assert.Equal(1, container.TypeParameters.Length); @@ -1839,13 +1839,13 @@ class E static void containerValidator(ModuleSymbol module) { var container = module.GlobalNamespace.GetMember("D.O__0_0"); - Assert.NotNull(container); Debug.Assert(container is { }); + AssertEx.NotNull(container); var typeParameters = container.TypeParameters; Assert.Equal(1, container.TypeParameters.Length); var m = typeParameters[0]; - Assert.NotNull(m); Debug.Assert(m is { }); + AssertEx.NotNull(m); Assert.True(m.IsValueType); } CompileAndVerify(source, symbolValidator: containerValidator, expectedOutput: PASS).VerifyIL("D.Test", @" @@ -4992,7 +4992,7 @@ static void containerValidator(ModuleSymbol module) { var container = module.GlobalNamespace.GetMember("Test.O__1_0"); var field = Assert.Single(container.GetMembers()) as FieldSymbol; - Assert.NotNull(field); Debug.Assert(field is { }); + AssertEx.NotNull(field); var typeParameters = new List(); field.Type.VisitType(static (typeSymbol, typeParameters, _) => From 764c86308d02a97b5a16af51f3891a0e738349fc Mon Sep 17 00:00:00 2001 From: Paul Chen Date: Fri, 31 Dec 2021 15:59:45 +0800 Subject: [PATCH 39/66] Add IL verification for cases we shouldn't cache. --- ...odeGenMethodGroupConversionCachingTests.cs | 151 ++++++++++++++++++ 1 file changed, 151 insertions(+) diff --git a/src/Compilers/CSharp/Test/Emit2/CodeGen/CodeGenMethodGroupConversionCachingTests.cs b/src/Compilers/CSharp/Test/Emit2/CodeGen/CodeGenMethodGroupConversionCachingTests.cs index 97c492d0882b8..7c4a7a4054b9b 100644 --- a/src/Compilers/CSharp/Test/Emit2/CodeGen/CodeGenMethodGroupConversionCachingTests.cs +++ b/src/Compilers/CSharp/Test/Emit2/CodeGen/CodeGenMethodGroupConversionCachingTests.cs @@ -34,6 +34,20 @@ public static void Main(string[] args) }"; var verifier = CompileAndVerify(source, expectedOutput: PASS); Assert.Empty(verifier.TestData.Module!.GetAllSynthesizedMembers()); + verifier.VerifyIL("C.Main", @" +{ + // Code size 30 (0x1e) + .maxstack 3 + IL_0000: ldnull + IL_0001: ldftn ""void C.Target()"" + IL_0007: newobj ""D..ctor(object, System.IntPtr)"" + IL_000c: ldnull + IL_000d: ldftn ""void C.Target()"" + IL_0013: newobj ""D..ctor(object, System.IntPtr)"" + IL_0018: call ""void C.Invoke(D, D)"" + IL_001d: ret +} +"); } [Fact] @@ -55,6 +69,24 @@ public static void Main(string[] args) }"; var verifier = CompileAndVerify(source, expectedOutput: PASS); Assert.Empty(verifier.TestData.Module!.GetAllSynthesizedMembers()); + verifier.VerifyIL("C.Main", @" +{ + // Code size 37 (0x25) + .maxstack 4 + .locals init (C V_0) //c + IL_0000: newobj ""C..ctor()"" + IL_0005: stloc.0 + IL_0006: ldloc.0 + IL_0007: ldloc.0 + IL_0008: ldftn ""void C.Target()"" + IL_000e: newobj ""D..ctor(object, System.IntPtr)"" + IL_0013: ldloc.0 + IL_0014: ldftn ""void C.Target()"" + IL_001a: newobj ""D..ctor(object, System.IntPtr)"" + IL_001f: callvirt ""void C.Invoke(D, D)"" + IL_0024: ret +} +"); } [Fact] @@ -80,6 +112,24 @@ static class E "; var verifier = CompileAndVerify(source, expectedOutput: PASS); Assert.Empty(verifier.TestData.Module!.GetAllSynthesizedMembers()); + verifier.VerifyIL("C.Main", @" +{ + // Code size 37 (0x25) + .maxstack 4 + .locals init (C V_0) //c + IL_0000: newobj ""C..ctor()"" + IL_0005: stloc.0 + IL_0006: ldloc.0 + IL_0007: ldloc.0 + IL_0008: ldftn ""void E.Target(C)"" + IL_000e: newobj ""D..ctor(object, System.IntPtr)"" + IL_0013: ldloc.0 + IL_0014: ldftn ""void E.Target(C)"" + IL_001a: newobj ""D..ctor(object, System.IntPtr)"" + IL_001f: callvirt ""void C.Invoke(D, D)"" + IL_0024: ret +} +"); } [Fact] @@ -105,6 +155,21 @@ static class E "; var verifier = CompileAndVerify(source, expectedOutput: PASS); Assert.Empty(verifier.TestData.Module!.GetAllSynthesizedMembers()); + verifier.VerifyIL("C.Main", @" +{ + // Code size 35 (0x23) + .maxstack 4 + IL_0000: newobj ""C..ctor()"" + IL_0005: ldnull + IL_0006: ldftn ""void E.Target(C)"" + IL_000c: newobj ""D..ctor(object, System.IntPtr)"" + IL_0011: ldnull + IL_0012: ldftn ""void E.Target(C)"" + IL_0018: newobj ""D..ctor(object, System.IntPtr)"" + IL_001d: callvirt ""void C.Invoke(D, D)"" + IL_0022: ret +} +"); } [Fact] @@ -125,6 +190,57 @@ public static void Main(string[] args) "; var verifier = CompileAndVerify(source); Assert.DoesNotContain(verifier.TestData.Module!.GetAllSynthesizedMembers(), s => s.Key.Name.Contains("<>O")); + verifier.VerifyIL("C.Main", @" +{ + // Code size 156 (0x9c) + .maxstack 7 + .locals init (System.Linq.Expressions.ParameterExpression V_0) + IL_0000: ldtoken ""int"" + IL_0005: call ""System.Type System.Type.GetTypeFromHandle(System.RuntimeTypeHandle)"" + IL_000a: ldstr ""x"" + IL_000f: call ""System.Linq.Expressions.ParameterExpression System.Linq.Expressions.Expression.Parameter(System.Type, string)"" + IL_0014: stloc.0 + IL_0015: ldtoken ""int C.Target(int)"" + IL_001a: call ""System.Reflection.MethodBase System.Reflection.MethodBase.GetMethodFromHandle(System.RuntimeMethodHandle)"" + IL_001f: castclass ""System.Reflection.MethodInfo"" + IL_0024: ldtoken ""System.Reflection.MethodInfo"" + IL_0029: call ""System.Type System.Type.GetTypeFromHandle(System.RuntimeTypeHandle)"" + IL_002e: call ""System.Linq.Expressions.ConstantExpression System.Linq.Expressions.Expression.Constant(object, System.Type)"" + IL_0033: ldtoken ""System.Delegate System.Reflection.MethodInfo.CreateDelegate(System.Type, object)"" + IL_0038: call ""System.Reflection.MethodBase System.Reflection.MethodBase.GetMethodFromHandle(System.RuntimeMethodHandle)"" + IL_003d: castclass ""System.Reflection.MethodInfo"" + IL_0042: ldc.i4.2 + IL_0043: newarr ""System.Linq.Expressions.Expression"" + IL_0048: dup + IL_0049: ldc.i4.0 + IL_004a: ldtoken ""System.Func"" + IL_004f: call ""System.Type System.Type.GetTypeFromHandle(System.RuntimeTypeHandle)"" + IL_0054: ldtoken ""System.Type"" + IL_0059: call ""System.Type System.Type.GetTypeFromHandle(System.RuntimeTypeHandle)"" + IL_005e: call ""System.Linq.Expressions.ConstantExpression System.Linq.Expressions.Expression.Constant(object, System.Type)"" + IL_0063: stelem.ref + IL_0064: dup + IL_0065: ldc.i4.1 + IL_0066: ldnull + IL_0067: ldtoken ""object"" + IL_006c: call ""System.Type System.Type.GetTypeFromHandle(System.RuntimeTypeHandle)"" + IL_0071: call ""System.Linq.Expressions.ConstantExpression System.Linq.Expressions.Expression.Constant(object, System.Type)"" + IL_0076: stelem.ref + IL_0077: call ""System.Linq.Expressions.MethodCallExpression System.Linq.Expressions.Expression.Call(System.Linq.Expressions.Expression, System.Reflection.MethodInfo, params System.Linq.Expressions.Expression[])"" + IL_007c: ldtoken ""System.Func"" + IL_0081: call ""System.Type System.Type.GetTypeFromHandle(System.RuntimeTypeHandle)"" + IL_0086: call ""System.Linq.Expressions.UnaryExpression System.Linq.Expressions.Expression.Convert(System.Linq.Expressions.Expression, System.Type)"" + IL_008b: ldc.i4.1 + IL_008c: newarr ""System.Linq.Expressions.ParameterExpression"" + IL_0091: dup + IL_0092: ldc.i4.0 + IL_0093: ldloc.0 + IL_0094: stelem.ref + IL_0095: call ""System.Linq.Expressions.Expression>> System.Linq.Expressions.Expression.Lambda>>(System.Linq.Expressions.Expression, params System.Linq.Expressions.ParameterExpression[])"" + IL_009a: pop + IL_009b: ret +} +"); } [Fact] @@ -145,6 +261,19 @@ public static void Main(string[] args) "; var verifier = CompileAndVerify(source); Assert.DoesNotContain(verifier.TestData.Module!.GetAllSynthesizedMembers(), s => s.Key.Name.Contains("<>O")); + verifier.VerifyIL("C.Main", @" +{ + // Code size 29 (0x1d) + .maxstack 2 + IL_0000: ldsfld ""System.Func>>> C.<>c.<>9__0_0"" + IL_0005: brtrue.s IL_001c + IL_0007: ldsfld ""C.<>c C.<>c.<>9"" + IL_000c: ldftn ""System.Linq.Expressions.Expression>> C.<>c.
b__0_0(int)"" + IL_0012: newobj ""System.Func>>>..ctor(object, System.IntPtr)"" + IL_0017: stsfld ""System.Func>>> C.<>c.<>9__0_0"" + IL_001c: ret +} +"); } [Fact] @@ -160,6 +289,17 @@ static void Target() { } "; var verifier = CompileAndVerify(source); Assert.Empty(verifier.TestData.Module!.GetAllSynthesizedMembers()); + verifier.VerifyIL("C..cctor", @" +{ + // Code size 18 (0x12) + .maxstack 2 + IL_0000: ldnull + IL_0001: ldftn ""void C.Target()"" + IL_0007: newobj ""System.Action..ctor(object, System.IntPtr)"" + IL_000c: stsfld ""System.Action C.ManualCache"" + IL_0011: ret +} +"); } [Fact] @@ -180,6 +320,17 @@ static C() "; var verifier = CompileAndVerify(source); Assert.Empty(verifier.TestData.Module!.GetAllSynthesizedMembers()); + verifier.VerifyIL("C..cctor", @" +{ + // Code size 18 (0x12) + .maxstack 2 + IL_0000: ldnull + IL_0001: ldftn ""void C.Target()"" + IL_0007: newobj ""System.Action..ctor(object, System.IntPtr)"" + IL_000c: stsfld ""System.Action C.ManualCache"" + IL_0011: ret +} +"); } [Fact] From cb66dafa6de1a1fa1f89789297bc291ba9c7c489 Mon Sep 17 00:00:00 2001 From: Paul Chen Date: Fri, 31 Dec 2021 16:56:33 +0800 Subject: [PATCH 40/66] Test the actual interested method. --- ...odeGenMethodGroupConversionCachingTests.cs | 57 +++++++++++++++---- 1 file changed, 47 insertions(+), 10 deletions(-) diff --git a/src/Compilers/CSharp/Test/Emit2/CodeGen/CodeGenMethodGroupConversionCachingTests.cs b/src/Compilers/CSharp/Test/Emit2/CodeGen/CodeGenMethodGroupConversionCachingTests.cs index 7c4a7a4054b9b..051fdc2557051 100644 --- a/src/Compilers/CSharp/Test/Emit2/CodeGen/CodeGenMethodGroupConversionCachingTests.cs +++ b/src/Compilers/CSharp/Test/Emit2/CodeGen/CodeGenMethodGroupConversionCachingTests.cs @@ -261,17 +261,54 @@ public static void Main(string[] args) "; var verifier = CompileAndVerify(source); Assert.DoesNotContain(verifier.TestData.Module!.GetAllSynthesizedMembers(), s => s.Key.Name.Contains("<>O")); - verifier.VerifyIL("C.Main", @" + verifier.VerifyIL("C.<>c.
b__0_0", @" { - // Code size 29 (0x1d) - .maxstack 2 - IL_0000: ldsfld ""System.Func>>> C.<>c.<>9__0_0"" - IL_0005: brtrue.s IL_001c - IL_0007: ldsfld ""C.<>c C.<>c.<>9"" - IL_000c: ldftn ""System.Linq.Expressions.Expression>> C.<>c.
b__0_0(int)"" - IL_0012: newobj ""System.Func>>>..ctor(object, System.IntPtr)"" - IL_0017: stsfld ""System.Func>>> C.<>c.<>9__0_0"" - IL_001c: ret + // Code size 155 (0x9b) + .maxstack 7 + .locals init (System.Linq.Expressions.ParameterExpression V_0) + IL_0000: ldtoken ""int"" + IL_0005: call ""System.Type System.Type.GetTypeFromHandle(System.RuntimeTypeHandle)"" + IL_000a: ldstr ""y"" + IL_000f: call ""System.Linq.Expressions.ParameterExpression System.Linq.Expressions.Expression.Parameter(System.Type, string)"" + IL_0014: stloc.0 + IL_0015: ldtoken ""int C.Target(int)"" + IL_001a: call ""System.Reflection.MethodBase System.Reflection.MethodBase.GetMethodFromHandle(System.RuntimeMethodHandle)"" + IL_001f: castclass ""System.Reflection.MethodInfo"" + IL_0024: ldtoken ""System.Reflection.MethodInfo"" + IL_0029: call ""System.Type System.Type.GetTypeFromHandle(System.RuntimeTypeHandle)"" + IL_002e: call ""System.Linq.Expressions.ConstantExpression System.Linq.Expressions.Expression.Constant(object, System.Type)"" + IL_0033: ldtoken ""System.Delegate System.Reflection.MethodInfo.CreateDelegate(System.Type, object)"" + IL_0038: call ""System.Reflection.MethodBase System.Reflection.MethodBase.GetMethodFromHandle(System.RuntimeMethodHandle)"" + IL_003d: castclass ""System.Reflection.MethodInfo"" + IL_0042: ldc.i4.2 + IL_0043: newarr ""System.Linq.Expressions.Expression"" + IL_0048: dup + IL_0049: ldc.i4.0 + IL_004a: ldtoken ""System.Func"" + IL_004f: call ""System.Type System.Type.GetTypeFromHandle(System.RuntimeTypeHandle)"" + IL_0054: ldtoken ""System.Type"" + IL_0059: call ""System.Type System.Type.GetTypeFromHandle(System.RuntimeTypeHandle)"" + IL_005e: call ""System.Linq.Expressions.ConstantExpression System.Linq.Expressions.Expression.Constant(object, System.Type)"" + IL_0063: stelem.ref + IL_0064: dup + IL_0065: ldc.i4.1 + IL_0066: ldnull + IL_0067: ldtoken ""object"" + IL_006c: call ""System.Type System.Type.GetTypeFromHandle(System.RuntimeTypeHandle)"" + IL_0071: call ""System.Linq.Expressions.ConstantExpression System.Linq.Expressions.Expression.Constant(object, System.Type)"" + IL_0076: stelem.ref + IL_0077: call ""System.Linq.Expressions.MethodCallExpression System.Linq.Expressions.Expression.Call(System.Linq.Expressions.Expression, System.Reflection.MethodInfo, params System.Linq.Expressions.Expression[])"" + IL_007c: ldtoken ""System.Func"" + IL_0081: call ""System.Type System.Type.GetTypeFromHandle(System.RuntimeTypeHandle)"" + IL_0086: call ""System.Linq.Expressions.UnaryExpression System.Linq.Expressions.Expression.Convert(System.Linq.Expressions.Expression, System.Type)"" + IL_008b: ldc.i4.1 + IL_008c: newarr ""System.Linq.Expressions.ParameterExpression"" + IL_0091: dup + IL_0092: ldc.i4.0 + IL_0093: ldloc.0 + IL_0094: stelem.ref + IL_0095: call ""System.Linq.Expressions.Expression>> System.Linq.Expressions.Expression.Lambda>>(System.Linq.Expressions.Expression, params System.Linq.Expressions.ParameterExpression[])"" + IL_009a: ret } "); } From 194e3208e018fafe352eefa943c2dd2be36e6358 Mon Sep 17 00:00:00 2001 From: Paul Chen Date: Fri, 31 Dec 2021 18:48:56 +0800 Subject: [PATCH 41/66] Add an overload of VerifySynthesizedFields that also perform validations against the emitted assembly. --- ...odeGenMethodGroupConversionCachingTests.cs | 41 +++++++++++-------- .../Test/Core/CompilationVerifier.cs | 20 +++++++++ 2 files changed, 45 insertions(+), 16 deletions(-) diff --git a/src/Compilers/CSharp/Test/Emit2/CodeGen/CodeGenMethodGroupConversionCachingTests.cs b/src/Compilers/CSharp/Test/Emit2/CodeGen/CodeGenMethodGroupConversionCachingTests.cs index 051fdc2557051..335eaa9a2c136 100644 --- a/src/Compilers/CSharp/Test/Emit2/CodeGen/CodeGenMethodGroupConversionCachingTests.cs +++ b/src/Compilers/CSharp/Test/Emit2/CodeGen/CodeGenMethodGroupConversionCachingTests.cs @@ -2682,7 +2682,9 @@ public static void Target() { } } "; var verifier = CompileAndVerify(source); - verifier.VerifySynthesizedFields("C.<>O", "System.Action <0>__Target"); + verifier.VerifySynthesizedFields("C.<>O", ContainerFinder("C.<>O"), arity: 0 + , "System.Action <0>__Target" + ); } [Fact] @@ -2698,7 +2700,9 @@ class C } "; var verifier = CompileAndVerify(source); - verifier.VerifySynthesizedFields("C.<>O", "System.Func <0>__Target"); + verifier.VerifySynthesizedFields("C.<>O", ContainerFinder("C.<>O"), arity: 0 + , "System.Func <0>__Target" + ); } [Fact] @@ -2715,7 +2719,9 @@ class C } "; var verifier = CompileAndVerify(source); - verifier.VerifySynthesizedFields("C.<>O", "C.MyFunc <0>__Target"); + verifier.VerifySynthesizedFields("C.<>O", ContainerFinder("C.<>O"), arity: 0 + , "C.MyFunc <0>__Target" + ); } [Fact] @@ -2737,7 +2743,9 @@ public static void Target() { } } "; var verifier = CompileAndVerify(source); - verifier.VerifySynthesizedFields("C.O__0_0", "System.Action <0>__Target"); + verifier.VerifySynthesizedFields("C.O__0_0", ContainerFinder("C.O__0_0"), arity: 1 + , "System.Action <0>__Target" + ); } [Fact] @@ -2759,7 +2767,9 @@ class D } "; var verifier = CompileAndVerify(source); - verifier.VerifySynthesizedFields("C.O__0_0", "System.Func <0>__Target"); + verifier.VerifySynthesizedFields("C.O__0_0", ContainerFinder("C.O__0_0"), arity: 1 + , "System.Func <0>__Target" + ); } [Fact] @@ -2783,7 +2793,9 @@ static class D } "; var verifier = CompileAndVerify(source); - verifier.VerifySynthesizedFields("C.O__2_0", "C.MyFunc <0>__Target"); + verifier.VerifySynthesizedFields("C.O__2_0", ContainerFinder("C.O__2_0"), arity: 1 + , "C.MyFunc <0>__Target" + ); } [Fact] @@ -2853,8 +2865,7 @@ public static void Target5(this N n) { } } "; var verifier = CompileAndVerify(source); - verifier.VerifySynthesizedFields( - "A.B.<>O" + verifier.VerifySynthesizedFields("A.B.<>O", ContainerFinder("A.B.<>O"), arity: 0 , "System.Action <0>__Target0" , "System.Action <1>__Target1" , "System.Action <2>__Target2" @@ -2894,8 +2905,7 @@ public static void Target3(this C c) { } } "; var verifier = CompileAndVerify(source); - verifier.VerifySynthesizedFields( - "C.O__0_0" + verifier.VerifySynthesizedFields("C.O__0_0", ContainerFinder("C.O__0_0"), arity: 1 , "System.Action <0>__Target0" , "System.Action <1>__Target1" , "System.Action <2>__Target2" @@ -2932,8 +2942,7 @@ public static void Target3(this C c) { } } "; var verifier = CompileAndVerify(source); - verifier.VerifySynthesizedFields( - "E.O__0_0" + verifier.VerifySynthesizedFields("E.O__0_0", ContainerFinder("E.O__0_0"), arity: 1 , "System.Action <0>__Target0" , "System.Action <1>__Target1" , "System.Action <2>__Target2" @@ -2977,8 +2986,7 @@ void LF5() } "; var verifier = CompileAndVerify(source); - verifier.VerifySynthesizedFields( - "E.O__0_0" + verifier.VerifySynthesizedFields("E.O__0_0", ContainerFinder("E.O__0_0"), arity: 1 , "System.Action <0>__Target" ); } @@ -3018,8 +3026,7 @@ static void LF2() { } } "; var verifier = CompileAndVerify(source); - verifier.VerifySynthesizedFields( - "E.O__0_0" + verifier.VerifySynthesizedFields("E.O__0_0", ContainerFinder("E.O__0_0"), arity: 2 , "System.Action <0>__LF2" , "System.Action <1>__LF2" ); @@ -5224,4 +5231,6 @@ .locals init (C1.F V_0) //x } "); } + + private static Func ContainerFinder(string name) => module => module.GlobalNamespace.GetMember(name); } diff --git a/src/Compilers/Test/Core/CompilationVerifier.cs b/src/Compilers/Test/Core/CompilationVerifier.cs index 1ed62d8b828c8..deb410741f33d 100644 --- a/src/Compilers/Test/Core/CompilationVerifier.cs +++ b/src/Compilers/Test/Core/CompilationVerifier.cs @@ -446,5 +446,25 @@ public void VerifySynthesizedFields(string containingTypeName, params string[] e .ToList(); AssertEx.SetEqual(expectedFields, members); } + + /// + /// Useful for verifying the expected variables are hoisted for closures, async, and iterator methods. + /// Additionally this overload performs validations against the produced assembly. + /// + public void VerifySynthesizedFields(string containingTypeName, Func findContainer, int arity, params string[] expectedFields) + { + VerifySynthesizedFields(containingTypeName, expectedFields); + + CommonTestBase.RunValidators(this, assemblyValidator: null, symbolValidator: module => + { + var container = findContainer(module); + AssertEx.NotNull(container); + Assert.Equal(arity, container.Arity); + + var fields = container.GetMembers().OfType().Select(field => $"{field.Type} {field.Name}").ToArray(); + AssertEx.SetEqual(expectedFields, fields); + }); + } + } } From 7c7589225a2de581a73b14eb78e88371bed941c6 Mon Sep 17 00:00:00 2001 From: Paul Chen Date: Sat, 1 Jan 2022 14:55:50 +0800 Subject: [PATCH 42/66] Add IL verifications show that the container and fields are used. --- ...odeGenMethodGroupConversionCachingTests.cs | 397 +++++++++++++++++- 1 file changed, 392 insertions(+), 5 deletions(-) diff --git a/src/Compilers/CSharp/Test/Emit2/CodeGen/CodeGenMethodGroupConversionCachingTests.cs b/src/Compilers/CSharp/Test/Emit2/CodeGen/CodeGenMethodGroupConversionCachingTests.cs index 335eaa9a2c136..1499b91636fc5 100644 --- a/src/Compilers/CSharp/Test/Emit2/CodeGen/CodeGenMethodGroupConversionCachingTests.cs +++ b/src/Compilers/CSharp/Test/Emit2/CodeGen/CodeGenMethodGroupConversionCachingTests.cs @@ -2685,6 +2685,32 @@ public static void Target() { } verifier.VerifySynthesizedFields("C.<>O", ContainerFinder("C.<>O"), arity: 0 , "System.Action <0>__Target" ); + verifier.VerifyIL("C.Test0", @" +{ + // Code size 25 (0x19) + .maxstack 2 + IL_0000: ldsfld ""System.Action C.<>O.<0>__Target"" + IL_0005: brtrue.s IL_0018 + IL_0007: ldnull + IL_0008: ldftn ""void D.Target()"" + IL_000e: newobj ""System.Action..ctor(object, System.IntPtr)"" + IL_0013: stsfld ""System.Action C.<>O.<0>__Target"" + IL_0018: ret +} +"); + verifier.VerifyIL("C.Test1", @" +{ + // Code size 25 (0x19) + .maxstack 2 + IL_0000: ldsfld ""System.Action C.<>O.<0>__Target"" + IL_0005: brtrue.s IL_0018 + IL_0007: ldnull + IL_0008: ldftn ""void D.Target()"" + IL_000e: newobj ""System.Action..ctor(object, System.IntPtr)"" + IL_0013: stsfld ""System.Action C.<>O.<0>__Target"" + IL_0018: ret +} +"); } [Fact] @@ -2703,6 +2729,32 @@ class C verifier.VerifySynthesizedFields("C.<>O", ContainerFinder("C.<>O"), arity: 0 , "System.Func <0>__Target" ); + verifier.VerifyIL("C.Test0", @" +{ + // Code size 25 (0x19) + .maxstack 2 + IL_0000: ldsfld ""System.Func C.<>O.<0>__Target"" + IL_0005: brtrue.s IL_0018 + IL_0007: ldnull + IL_0008: ldftn ""T C.Target()"" + IL_000e: newobj ""System.Func..ctor(object, System.IntPtr)"" + IL_0013: stsfld ""System.Func C.<>O.<0>__Target"" + IL_0018: ret +} +"); + verifier.VerifyIL("C.Test1", @" +{ + // Code size 25 (0x19) + .maxstack 2 + IL_0000: ldsfld ""System.Func C.<>O.<0>__Target"" + IL_0005: brtrue.s IL_0018 + IL_0007: ldnull + IL_0008: ldftn ""T C.Target()"" + IL_000e: newobj ""System.Func..ctor(object, System.IntPtr)"" + IL_0013: stsfld ""System.Func C.<>O.<0>__Target"" + IL_0018: ret +} +"); } [Fact] @@ -2722,6 +2774,32 @@ class C verifier.VerifySynthesizedFields("C.<>O", ContainerFinder("C.<>O"), arity: 0 , "C.MyFunc <0>__Target" ); + verifier.VerifyIL("C.Test0", @" +{ + // Code size 25 (0x19) + .maxstack 2 + IL_0000: ldsfld ""C.MyFunc C.<>O.<0>__Target"" + IL_0005: brtrue.s IL_0018 + IL_0007: ldnull + IL_0008: ldftn ""T C.Target()"" + IL_000e: newobj ""C.MyFunc..ctor(object, System.IntPtr)"" + IL_0013: stsfld ""C.MyFunc C.<>O.<0>__Target"" + IL_0018: ret +} +"); + verifier.VerifyIL("C.Test1", @" +{ + // Code size 25 (0x19) + .maxstack 2 + IL_0000: ldsfld ""C.MyFunc C.<>O.<0>__Target"" + IL_0005: brtrue.s IL_0018 + IL_0007: ldnull + IL_0008: ldftn ""T C.Target()"" + IL_000e: newobj ""C.MyFunc..ctor(object, System.IntPtr)"" + IL_0013: stsfld ""C.MyFunc C.<>O.<0>__Target"" + IL_0018: ret +} +"); } [Fact] @@ -2746,6 +2824,25 @@ public static void Target() { } verifier.VerifySynthesizedFields("C.O__0_0", ContainerFinder("C.O__0_0"), arity: 1 , "System.Action <0>__Target" ); + verifier.VerifyIL("C.Test", @" +{ + // Code size 49 (0x31) + .maxstack 2 + IL_0000: ldsfld ""System.Action C.O__0_0.<0>__Target"" + IL_0005: brtrue.s IL_0018 + IL_0007: ldnull + IL_0008: ldftn ""void D.Target()"" + IL_000e: newobj ""System.Action..ctor(object, System.IntPtr)"" + IL_0013: stsfld ""System.Action C.O__0_0.<0>__Target"" + IL_0018: ldsfld ""System.Action C.O__0_0.<0>__Target"" + IL_001d: brtrue.s IL_0030 + IL_001f: ldnull + IL_0020: ldftn ""void D.Target()"" + IL_0026: newobj ""System.Action..ctor(object, System.IntPtr)"" + IL_002b: stsfld ""System.Action C.O__0_0.<0>__Target"" + IL_0030: ret +} +"); } [Fact] @@ -2770,6 +2867,25 @@ class D verifier.VerifySynthesizedFields("C.O__0_0", ContainerFinder("C.O__0_0"), arity: 1 , "System.Func <0>__Target" ); + verifier.VerifyIL("C.Test", @" +{ + // Code size 49 (0x31) + .maxstack 2 + IL_0000: ldsfld ""System.Func C.O__0_0.<0>__Target"" + IL_0005: brtrue.s IL_0018 + IL_0007: ldnull + IL_0008: ldftn ""V D.Target(T)"" + IL_000e: newobj ""System.Func..ctor(object, System.IntPtr)"" + IL_0013: stsfld ""System.Func C.O__0_0.<0>__Target"" + IL_0018: ldsfld ""System.Func C.O__0_0.<0>__Target"" + IL_001d: brtrue.s IL_0030 + IL_001f: ldnull + IL_0020: ldftn ""V D.Target(T)"" + IL_0026: newobj ""System.Func..ctor(object, System.IntPtr)"" + IL_002b: stsfld ""System.Func C.O__0_0.<0>__Target"" + IL_0030: ret +} +"); } [Fact] @@ -2796,6 +2912,25 @@ static class D verifier.VerifySynthesizedFields("C.O__2_0", ContainerFinder("C.O__2_0"), arity: 1 , "C.MyFunc <0>__Target" ); + verifier.VerifyIL("C.Test", @" +{ + // Code size 49 (0x31) + .maxstack 2 + IL_0000: ldsfld ""C.MyFunc C.O__2_0.<0>__Target"" + IL_0005: brtrue.s IL_0018 + IL_0007: ldnull + IL_0008: ldftn ""V D.Target(int)"" + IL_000e: newobj ""C.MyFunc..ctor(object, System.IntPtr)"" + IL_0013: stsfld ""C.MyFunc C.O__2_0.<0>__Target"" + IL_0018: ldsfld ""C.MyFunc C.O__2_0.<0>__Target"" + IL_001d: brtrue.s IL_0030 + IL_001f: ldnull + IL_0020: ldftn ""V D.Target(int)"" + IL_0026: newobj ""C.MyFunc..ctor(object, System.IntPtr)"" + IL_002b: stsfld ""C.MyFunc C.O__2_0.<0>__Target"" + IL_0030: ret +} +"); } [Fact] @@ -2874,6 +3009,120 @@ public static void Target5(this N n) { } , "System.Action <5>__Target5" , "System.Action <6>__Target5" ); + verifier.VerifyIL("A.B.Test0", @" +{ + // Code size 49 (0x31) + .maxstack 2 + IL_0000: ldsfld ""System.Action A.B.<>O.<0>__Target0"" + IL_0005: brtrue.s IL_0018 + IL_0007: ldnull + IL_0008: ldftn ""void A.B.Target0(T)"" + IL_000e: newobj ""System.Action..ctor(object, System.IntPtr)"" + IL_0013: stsfld ""System.Action A.B.<>O.<0>__Target0"" + IL_0018: ldsfld ""System.Action A.B.<>O.<1>__Target1"" + IL_001d: brtrue.s IL_0030 + IL_001f: ldnull + IL_0020: ldftn ""void A.Target1(T)"" + IL_0026: newobj ""System.Action..ctor(object, System.IntPtr)"" + IL_002b: stsfld ""System.Action A.B.<>O.<1>__Target1"" + IL_0030: ret +} +"); + verifier.VerifyIL("A.B.Test1", @" +{ + // Code size 49 (0x31) + .maxstack 2 + IL_0000: ldsfld ""System.Action A.B.<>O.<1>__Target1"" + IL_0005: brtrue.s IL_0018 + IL_0007: ldnull + IL_0008: ldftn ""void A.Target1(T)"" + IL_000e: newobj ""System.Action..ctor(object, System.IntPtr)"" + IL_0013: stsfld ""System.Action A.B.<>O.<1>__Target1"" + IL_0018: ldsfld ""System.Action A.B.<>O.<2>__Target2"" + IL_001d: brtrue.s IL_0030 + IL_001f: ldnull + IL_0020: ldftn ""void D.Target2(T)"" + IL_0026: newobj ""System.Action..ctor(object, System.IntPtr)"" + IL_002b: stsfld ""System.Action A.B.<>O.<2>__Target2"" + IL_0030: ret +} +"); + verifier.VerifyIL("A.B.Test2", @" +{ + // Code size 49 (0x31) + .maxstack 2 + IL_0000: ldsfld ""System.Action A.B.<>O.<2>__Target2"" + IL_0005: brtrue.s IL_0018 + IL_0007: ldnull + IL_0008: ldftn ""void D.Target2(T)"" + IL_000e: newobj ""System.Action..ctor(object, System.IntPtr)"" + IL_0013: stsfld ""System.Action A.B.<>O.<2>__Target2"" + IL_0018: ldsfld ""System.Action A.B.<>O.<3>__Target3"" + IL_001d: brtrue.s IL_0030 + IL_001f: ldnull + IL_0020: ldftn ""void D.Target3(T, V)"" + IL_0026: newobj ""System.Action..ctor(object, System.IntPtr)"" + IL_002b: stsfld ""System.Action A.B.<>O.<3>__Target3"" + IL_0030: ret +} +"); + verifier.VerifyIL("A.B.Test3", @" +{ + // Code size 49 (0x31) + .maxstack 2 + IL_0000: ldsfld ""System.Action A.B.<>O.<3>__Target3"" + IL_0005: brtrue.s IL_0018 + IL_0007: ldnull + IL_0008: ldftn ""void D.Target3(T, V)"" + IL_000e: newobj ""System.Action..ctor(object, System.IntPtr)"" + IL_0013: stsfld ""System.Action A.B.<>O.<3>__Target3"" + IL_0018: ldsfld ""System.Action A.B.<>O.<4>__Target4"" + IL_001d: brtrue.s IL_0030 + IL_001f: ldnull + IL_0020: ldftn ""void D.E.Target4(T, V)"" + IL_0026: newobj ""System.Action..ctor(object, System.IntPtr)"" + IL_002b: stsfld ""System.Action A.B.<>O.<4>__Target4"" + IL_0030: ret +} +"); + verifier.VerifyIL("A.B.Test4", @" +{ + // Code size 49 (0x31) + .maxstack 2 + IL_0000: ldsfld ""System.Action A.B.<>O.<4>__Target4"" + IL_0005: brtrue.s IL_0018 + IL_0007: ldnull + IL_0008: ldftn ""void D.E.Target4(T, V)"" + IL_000e: newobj ""System.Action..ctor(object, System.IntPtr)"" + IL_0013: stsfld ""System.Action A.B.<>O.<4>__Target4"" + IL_0018: ldsfld ""System.Action A.B.<>O.<5>__Target5"" + IL_001d: brtrue.s IL_0030 + IL_001f: ldnull + IL_0020: ldftn ""void E.Target5(T)"" + IL_0026: newobj ""System.Action..ctor(object, System.IntPtr)"" + IL_002b: stsfld ""System.Action A.B.<>O.<5>__Target5"" + IL_0030: ret +} +"); + verifier.VerifyIL("A.B.Test5", @" +{ + // Code size 49 (0x31) + .maxstack 2 + IL_0000: ldsfld ""System.Action A.B.<>O.<5>__Target5"" + IL_0005: brtrue.s IL_0018 + IL_0007: ldnull + IL_0008: ldftn ""void E.Target5(T)"" + IL_000e: newobj ""System.Action..ctor(object, System.IntPtr)"" + IL_0013: stsfld ""System.Action A.B.<>O.<5>__Target5"" + IL_0018: ldsfld ""System.Action A.B.<>O.<6>__Target5"" + IL_001d: brtrue.s IL_0030 + IL_001f: ldnull + IL_0020: ldftn ""void E.Target5(V)"" + IL_0026: newobj ""System.Action..ctor(object, System.IntPtr)"" + IL_002b: stsfld ""System.Action A.B.<>O.<6>__Target5"" + IL_0030: ret +} +"); } [Fact] @@ -2911,6 +3160,37 @@ public static void Target3(this C c) { } , "System.Action <2>__Target2" , "System.Action <3>__Target3" ); + verifier.VerifyIL("C.Test", @" +{ + // Code size 97 (0x61) + .maxstack 2 + IL_0000: ldsfld ""System.Action C.O__0_0.<0>__Target0"" + IL_0005: brtrue.s IL_0018 + IL_0007: ldnull + IL_0008: ldftn ""void C.Target0()"" + IL_000e: newobj ""System.Action..ctor(object, System.IntPtr)"" + IL_0013: stsfld ""System.Action C.O__0_0.<0>__Target0"" + IL_0018: ldsfld ""System.Action C.O__0_0.<1>__Target1"" + IL_001d: brtrue.s IL_0030 + IL_001f: ldnull + IL_0020: ldftn ""void C.Target1(T)"" + IL_0026: newobj ""System.Action..ctor(object, System.IntPtr)"" + IL_002b: stsfld ""System.Action C.O__0_0.<1>__Target1"" + IL_0030: ldsfld ""System.Action C.O__0_0.<2>__Target2"" + IL_0035: brtrue.s IL_0048 + IL_0037: ldnull + IL_0038: ldftn ""void C.D.Target2()"" + IL_003e: newobj ""System.Action..ctor(object, System.IntPtr)"" + IL_0043: stsfld ""System.Action C.O__0_0.<2>__Target2"" + IL_0048: ldsfld ""System.Action C.O__0_0.<3>__Target3"" + IL_004d: brtrue.s IL_0060 + IL_004f: ldnull + IL_0050: ldftn ""void E.Target3(C)"" + IL_0056: newobj ""System.Action..ctor(object, System.IntPtr)"" + IL_005b: stsfld ""System.Action C.O__0_0.<3>__Target3"" + IL_0060: ret +} +"); } [Fact] @@ -2948,6 +3228,37 @@ public static void Target3(this C c) { } , "System.Action <2>__Target2" , "System.Action <3>__Target3" ); + verifier.VerifyIL("E.Test", @" +{ + // Code size 97 (0x61) + .maxstack 2 + IL_0000: ldsfld ""System.Action E.O__0_0.<0>__Target0"" + IL_0005: brtrue.s IL_0018 + IL_0007: ldnull + IL_0008: ldftn ""void C.Target0()"" + IL_000e: newobj ""System.Action..ctor(object, System.IntPtr)"" + IL_0013: stsfld ""System.Action E.O__0_0.<0>__Target0"" + IL_0018: ldsfld ""System.Action E.O__0_0.<1>__Target1"" + IL_001d: brtrue.s IL_0030 + IL_001f: ldnull + IL_0020: ldftn ""void C.Target1(T)"" + IL_0026: newobj ""System.Action..ctor(object, System.IntPtr)"" + IL_002b: stsfld ""System.Action E.O__0_0.<1>__Target1"" + IL_0030: ldsfld ""System.Action E.O__0_0.<2>__Target2"" + IL_0035: brtrue.s IL_0048 + IL_0037: ldnull + IL_0038: ldftn ""void C.D.Target2()"" + IL_003e: newobj ""System.Action..ctor(object, System.IntPtr)"" + IL_0043: stsfld ""System.Action E.O__0_0.<2>__Target2"" + IL_0048: ldsfld ""System.Action E.O__0_0.<3>__Target3"" + IL_004d: brtrue.s IL_0060 + IL_004f: ldnull + IL_0050: ldftn ""void E.Target3(C)"" + IL_0056: newobj ""System.Action..ctor(object, System.IntPtr)"" + IL_005b: stsfld ""System.Action E.O__0_0.<3>__Target3"" + IL_0060: ret +} +"); } [Fact] @@ -2989,6 +3300,46 @@ void LF5() verifier.VerifySynthesizedFields("E.O__0_0", ContainerFinder("E.O__0_0"), arity: 1 , "System.Action <0>__Target" ); + verifier.VerifyIL("E.g__LF3|0_1", @" +{ + // Code size 30 (0x1e) + .maxstack 2 + IL_0000: ldsfld ""System.Action E.O__0_0.<0>__Target"" + IL_0005: brtrue.s IL_0018 + IL_0007: ldnull + IL_0008: ldftn ""void C.Target(T)"" + IL_000e: newobj ""System.Action..ctor(object, System.IntPtr)"" + IL_0013: stsfld ""System.Action E.O__0_0.<0>__Target"" + IL_0018: call ""void E.g__LF4|0_3()"" + IL_001d: ret +} +"); + verifier.VerifyIL("E.g__LF4|0_3", @" +{ + // Code size 25 (0x19) + .maxstack 2 + IL_0000: ldsfld ""System.Action E.O__0_0.<0>__Target"" + IL_0005: brtrue.s IL_0018 + IL_0007: ldnull + IL_0008: ldftn ""void C.Target(T)"" + IL_000e: newobj ""System.Action..ctor(object, System.IntPtr)"" + IL_0013: stsfld ""System.Action E.O__0_0.<0>__Target"" + IL_0018: ret +} +"); + verifier.VerifyIL("E.g__LF5|0_2", @" +{ + // Code size 25 (0x19) + .maxstack 2 + IL_0000: ldsfld ""System.Action E.O__0_0.<0>__Target"" + IL_0005: brtrue.s IL_0018 + IL_0007: ldnull + IL_0008: ldftn ""void C.Target(T)"" + IL_000e: newobj ""System.Action..ctor(object, System.IntPtr)"" + IL_0013: stsfld ""System.Action E.O__0_0.<0>__Target"" + IL_0018: ret +} +"); } [Fact] @@ -3005,17 +3356,17 @@ void Owner() void LF1() { Action d = LF2; - static void LF2() { } + static void LF2() { Console.Write(""PA""); } - LF2(); + d(); } void LF3() { Action d = LF2; - static void LF2() { } + static void LF2() { Console.Write(""SS""); } - LF2(); + d(); } LF1(); LF3(); @@ -3023,13 +3374,49 @@ static void LF2() { } Owner(); } + + static void Main(string[] args) { Test(); } } "; - var verifier = CompileAndVerify(source); + var verifier = CompileAndVerify(source, expectedOutput: PASS); verifier.VerifySynthesizedFields("E.O__0_0", ContainerFinder("E.O__0_0"), arity: 2 , "System.Action <0>__LF2" , "System.Action <1>__LF2" ); + verifier.VerifyIL("E.g__LF1|0_1", @" +{ + // Code size 33 (0x21) + .maxstack 2 + IL_0000: ldsfld ""System.Action E.O__0_0.<0>__LF2"" + IL_0005: dup + IL_0006: brtrue.s IL_001b + IL_0008: pop + IL_0009: ldnull + IL_000a: ldftn ""void E.g__LF2|0_3()"" + IL_0010: newobj ""System.Action..ctor(object, System.IntPtr)"" + IL_0015: dup + IL_0016: stsfld ""System.Action E.O__0_0.<0>__LF2"" + IL_001b: callvirt ""void System.Action.Invoke()"" + IL_0020: ret +} +"); + verifier.VerifyIL("E.g__LF3|0_2", @" +{ + // Code size 33 (0x21) + .maxstack 2 + IL_0000: ldsfld ""System.Action E.O__0_0.<1>__LF2"" + IL_0005: dup + IL_0006: brtrue.s IL_001b + IL_0008: pop + IL_0009: ldnull + IL_000a: ldftn ""void E.g__LF2|0_4()"" + IL_0010: newobj ""System.Action..ctor(object, System.IntPtr)"" + IL_0015: dup + IL_0016: stsfld ""System.Action E.O__0_0.<1>__LF2"" + IL_001b: callvirt ""void System.Action.Invoke()"" + IL_0020: ret +} +"); } [Fact] From fd2d72761cee1110c539f43c86ce07b15fd068cb Mon Sep 17 00:00:00 2001 From: Paul Chen Date: Sat, 1 Jan 2022 15:52:15 +0800 Subject: [PATCH 43/66] Prepare for EnC tests. --- .../EditAndContinueDelegateCacheTests.cs | 23 +++++++++++++++++++ ...CodeAnalysis.CSharp.Emit2.UnitTests.csproj | 6 +++++ 2 files changed, 29 insertions(+) create mode 100644 src/Compilers/CSharp/Test/Emit2/Emit/EditAndContinue/EditAndContinueDelegateCacheTests.cs diff --git a/src/Compilers/CSharp/Test/Emit2/Emit/EditAndContinue/EditAndContinueDelegateCacheTests.cs b/src/Compilers/CSharp/Test/Emit2/Emit/EditAndContinue/EditAndContinueDelegateCacheTests.cs new file mode 100644 index 0000000000000..fe565e1eb3fb9 --- /dev/null +++ b/src/Compilers/CSharp/Test/Emit2/Emit/EditAndContinue/EditAndContinueDelegateCacheTests.cs @@ -0,0 +1,23 @@ +// 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; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.CodeGen; +using Microsoft.CodeAnalysis.CSharp.Symbols; +using Microsoft.CodeAnalysis.CSharp.Test.Utilities; +using Microsoft.CodeAnalysis.CSharp.UnitTests; +using Microsoft.CodeAnalysis.Emit; +using Microsoft.CodeAnalysis.Test.Utilities; +using Roslyn.Test.Utilities; +using Xunit; + +namespace Microsoft.CodeAnalysis.CSharp.EditAndContinue.UnitTests; + +public class EditAndContinueDelegateCacheTests : EditAndContinueTestBase +{ +} diff --git a/src/Compilers/CSharp/Test/Emit2/Microsoft.CodeAnalysis.CSharp.Emit2.UnitTests.csproj b/src/Compilers/CSharp/Test/Emit2/Microsoft.CodeAnalysis.CSharp.Emit2.UnitTests.csproj index 238c253b9cb1c..2396636716797 100644 --- a/src/Compilers/CSharp/Test/Emit2/Microsoft.CodeAnalysis.CSharp.Emit2.UnitTests.csproj +++ b/src/Compilers/CSharp/Test/Emit2/Microsoft.CodeAnalysis.CSharp.Emit2.UnitTests.csproj @@ -17,6 +17,12 @@ + + Emit\EditAndContinue\EditAndContinueTestBase.cs + + + Emit\EditAndContinue\SemanticEditDescription.cs + Emit\MvidReader.cs From b6c09d6b1d22646be5c8e7668c16ca6750979d8f Mon Sep 17 00:00:00 2001 From: Paul Chen Date: Sat, 1 Jan 2022 17:32:25 +0800 Subject: [PATCH 44/66] Pass the previous ParseOptions to the next compilation. --- .../Test/Emit/Emit/EditAndContinue/EditAndContinueTestBase.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Compilers/CSharp/Test/Emit/Emit/EditAndContinue/EditAndContinueTestBase.cs b/src/Compilers/CSharp/Test/Emit/Emit/EditAndContinue/EditAndContinueTestBase.cs index db4b878a56567..f51708a8bf74b 100644 --- a/src/Compilers/CSharp/Test/Emit/Emit/EditAndContinue/EditAndContinueTestBase.cs +++ b/src/Compilers/CSharp/Test/Emit/Emit/EditAndContinue/EditAndContinueTestBase.cs @@ -276,7 +276,8 @@ public static class EditAndContinueTestExtensions { internal static CSharpCompilation WithSource(this CSharpCompilation compilation, CSharpTestSource newSource) { - return compilation.RemoveAllSyntaxTrees().AddSyntaxTrees(newSource.GetSyntaxTrees(TestOptions.Regular)); + var previousParseOptions = compilation.SyntaxTrees.FirstOrDefault()?.Options as CSharpParseOptions; + return compilation.RemoveAllSyntaxTrees().AddSyntaxTrees(newSource.GetSyntaxTrees(previousParseOptions)); } internal static CSharpCompilation WithSource(this CSharpCompilation compilation, SyntaxTree newTree) From b26d098711a1216cd83b02bba63d7e0dcd790950 Mon Sep 17 00:00:00 2001 From: Paul Chen Date: Sat, 1 Jan 2022 17:32:45 +0800 Subject: [PATCH 45/66] Add EnC tests. --- .../EditAndContinueDelegateCacheTests.cs | 321 ++++++++++++++++++ 1 file changed, 321 insertions(+) diff --git a/src/Compilers/CSharp/Test/Emit2/Emit/EditAndContinue/EditAndContinueDelegateCacheTests.cs b/src/Compilers/CSharp/Test/Emit2/Emit/EditAndContinue/EditAndContinueDelegateCacheTests.cs index fe565e1eb3fb9..3b50141864d50 100644 --- a/src/Compilers/CSharp/Test/Emit2/Emit/EditAndContinue/EditAndContinueDelegateCacheTests.cs +++ b/src/Compilers/CSharp/Test/Emit2/Emit/EditAndContinue/EditAndContinueDelegateCacheTests.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; +using System.Collections.Immutable; using System.Linq; using System.Text; using System.Threading.Tasks; @@ -20,4 +21,324 @@ namespace Microsoft.CodeAnalysis.CSharp.EditAndContinue.UnitTests; public class EditAndContinueDelegateCacheTests : EditAndContinueTestBase { + [Fact] + public void TargetChanged0() + { + var source0 = @" +class C +{ + static int Target0() => 0; + static int Target1() => 1; + + System.Func F() => Target0; +} +"; + var source1 = @" +class C +{ + static int Target0() => 0; + static int Target1() => 1; + + System.Func F() => Target1; +} +"; + var compilation0 = CreateCompilation(source0); + var compilation1 = compilation0.WithSource(source1); + + Assert.Equal(compilation0.LanguageVersion, compilation1.LanguageVersion); + + var f0 = compilation0.GetMember("C.F"); + var f1 = compilation1.GetMember("C.F"); + + var v0 = CompileAndVerify(compilation0); + using var moduleData0 = ModuleMetadata.CreateFromImage(v0.EmittedAssemblyData); + var methodData0 = v0.TestData.GetMethodData("C.F"); + + var generation0 = EmitBaseline.CreateInitialBaseline(moduleData0, methodData0.EncDebugInfoProvider()); + var diff1 = compilation1.EmitDifference( + generation0, + ImmutableArray.Create( + SemanticEdit.Create(SemanticEditKind.Update, f0, f1, preserveLocalVariables: true))); + + diff1.EmitResult.Diagnostics.Verify(); + diff1.VerifyIL("C.F", @" +{ + // Code size 28 (0x1c) + .maxstack 2 + IL_0000: ldsfld ""System.Func C.<>O#1.<0>__Target1"" + IL_0005: dup + IL_0006: brtrue.s IL_001b + IL_0008: pop + IL_0009: ldnull + IL_000a: ldftn ""int C.Target1()"" + IL_0010: newobj ""System.Func..ctor(object, System.IntPtr)"" + IL_0015: dup + IL_0016: stsfld ""System.Func C.<>O#1.<0>__Target1"" + IL_001b: ret +} +"); + + var reader0 = moduleData0.MetadataReader; + var reader1 = diff1.GetMetadata().Reader; + + CheckNames(reader0, reader0.GetTypeDefNames(), "", "C", "<>O"); + CheckNames(new[] { reader0, reader1 }, reader1.GetTypeDefNames(), "<>O#1"); + } + + [Fact] + public void TargetChanged1() + { + var source0 = @" +class C +{ + static int Target0() => 0; + static int Target1() => 1; + + System.Func F() => Target0; +} +"; + var source1 = @" +class C +{ + static int Target0() => 0; + static int Target1() => 1; + + System.Func F() => Target1; +} +"; + var compilation0 = CreateCompilation(source0); + var compilation1 = compilation0.WithSource(source1); + + Assert.Equal(compilation0.LanguageVersion, compilation1.LanguageVersion); + + var f0 = compilation0.GetMember("C.F"); + var f1 = compilation1.GetMember("C.F"); + + var v0 = CompileAndVerify(compilation0); + using var moduleData0 = ModuleMetadata.CreateFromImage(v0.EmittedAssemblyData); + var methodData0 = v0.TestData.GetMethodData("C.F"); + + var generation0 = EmitBaseline.CreateInitialBaseline(moduleData0, methodData0.EncDebugInfoProvider()); + var diff1 = compilation1.EmitDifference( + generation0, + ImmutableArray.Create( + SemanticEdit.Create(SemanticEditKind.Update, f0, f1, preserveLocalVariables: true))); + + diff1.EmitResult.Diagnostics.Verify(); + diff1.VerifyIL("C.F", @" +{ + // Code size 28 (0x1c) + .maxstack 2 + IL_0000: ldsfld ""System.Func C.<>O#1.<0>__Target1"" + IL_0005: dup + IL_0006: brtrue.s IL_001b + IL_0008: pop + IL_0009: ldnull + IL_000a: ldftn ""int C.Target1()"" + IL_0010: newobj ""System.Func..ctor(object, System.IntPtr)"" + IL_0015: dup + IL_0016: stsfld ""System.Func C.<>O#1.<0>__Target1"" + IL_001b: ret +} +"); + + var reader0 = moduleData0.MetadataReader; + var reader1 = diff1.GetMetadata().Reader; + + CheckNames(reader0, reader0.GetTypeDefNames(), "", "C`1", "<>O"); + CheckNames(new[] { reader0, reader1 }, reader1.GetTypeDefNames(), "<>O#1"); + } + + [Fact] + public void TargetChanged2() + { + var source0 = @" +class C +{ + static int Target0() => 0; + static int Target1() => 1; + + System.Func F() => Target0; +} +"; + var source1 = @" +class C +{ + static int Target0() => 0; + static int Target1() => 1; + + System.Func F() => Target1; +} +"; + var compilation0 = CreateCompilation(source0); + var compilation1 = compilation0.WithSource(source1); + + Assert.Equal(compilation0.LanguageVersion, compilation1.LanguageVersion); + + var f0 = compilation0.GetMember("C.F"); + var f1 = compilation1.GetMember("C.F"); + + var v0 = CompileAndVerify(compilation0); + using var moduleData0 = ModuleMetadata.CreateFromImage(v0.EmittedAssemblyData); + var methodData0 = v0.TestData.GetMethodData("C.F"); + + var generation0 = EmitBaseline.CreateInitialBaseline(moduleData0, methodData0.EncDebugInfoProvider()); + var diff1 = compilation1.EmitDifference( + generation0, + ImmutableArray.Create( + SemanticEdit.Create(SemanticEditKind.Update, f0, f1, preserveLocalVariables: true))); + + diff1.EmitResult.Diagnostics.Verify(); + diff1.VerifyIL("C.F", @" +{ + // Code size 28 (0x1c) + .maxstack 2 + IL_0000: ldsfld ""System.Func C.<>O#1.<0>__Target1"" + IL_0005: dup + IL_0006: brtrue.s IL_001b + IL_0008: pop + IL_0009: ldnull + IL_000a: ldftn ""int C.Target1()"" + IL_0010: newobj ""System.Func..ctor(object, System.IntPtr)"" + IL_0015: dup + IL_0016: stsfld ""System.Func C.<>O#1.<0>__Target1"" + IL_001b: ret +} +"); + + var reader0 = moduleData0.MetadataReader; + var reader1 = diff1.GetMetadata().Reader; + + CheckNames(reader0, reader0.GetTypeDefNames(), "", "C`1", "<>O"); + CheckNames(new[] { reader0, reader1 }, reader1.GetTypeDefNames(), "<>O#1"); + } + + [Fact] + public void TargetChanged3() + { + var source0 = @" +class C +{ + static int Target0() => 0; + static int Target1() => 1; + + System.Func F() => Target0; +} +"; + var source1 = @" +class C +{ + static int Target0() => 0; + static int Target1() => 1; + + System.Func F() => Target1; +} +"; + var compilation0 = CreateCompilation(source0); + var compilation1 = compilation0.WithSource(source1); + + Assert.Equal(compilation0.LanguageVersion, compilation1.LanguageVersion); + + var f0 = compilation0.GetMember("C.F"); + var f1 = compilation1.GetMember("C.F"); + + var v0 = CompileAndVerify(compilation0); + using var moduleData0 = ModuleMetadata.CreateFromImage(v0.EmittedAssemblyData); + var methodData0 = v0.TestData.GetMethodData("C.F"); + + var generation0 = EmitBaseline.CreateInitialBaseline(moduleData0, methodData0.EncDebugInfoProvider()); + var diff1 = compilation1.EmitDifference( + generation0, + ImmutableArray.Create( + SemanticEdit.Create(SemanticEditKind.Update, f0, f1, preserveLocalVariables: true))); + + diff1.EmitResult.Diagnostics.Verify(); + diff1.VerifyIL("C.F", @" +{ + // Code size 28 (0x1c) + .maxstack 2 + IL_0000: ldsfld ""System.Func C.O__2_0#1.<0>__Target1"" + IL_0005: dup + IL_0006: brtrue.s IL_001b + IL_0008: pop + IL_0009: ldnull + IL_000a: ldftn ""int C.Target1()"" + IL_0010: newobj ""System.Func..ctor(object, System.IntPtr)"" + IL_0015: dup + IL_0016: stsfld ""System.Func C.O__2_0#1.<0>__Target1"" + IL_001b: ret +} +"); + + var reader0 = moduleData0.MetadataReader; + var reader1 = diff1.GetMetadata().Reader; + + CheckNames(reader0, reader0.GetTypeDefNames(), "", "C`1", "<>O"); + CheckNames(new[] { reader0, reader1 }, reader1.GetTypeDefNames(), "O__2_0#1`1"); + } + + [Fact] + public void TargetChanged4() + { + var source0 = @" +class C +{ + static int Target0() => 0; + static int Target1() => 1; + + System.Func F() => Target0; +} +"; + var source1 = @" +class C +{ + static int Target0() => 0; + static int Target1() => 1; + + System.Func F() => Target1; +} +"; + var compilation0 = CreateCompilation(source0); + var compilation1 = compilation0.WithSource(source1); + + Assert.Equal(compilation0.LanguageVersion, compilation1.LanguageVersion); + + var f0 = compilation0.GetMember("C.F"); + var f1 = compilation1.GetMember("C.F"); + + var v0 = CompileAndVerify(compilation0); + using var moduleData0 = ModuleMetadata.CreateFromImage(v0.EmittedAssemblyData); + var methodData0 = v0.TestData.GetMethodData("C.F"); + + var generation0 = EmitBaseline.CreateInitialBaseline(moduleData0, methodData0.EncDebugInfoProvider()); + var diff1 = compilation1.EmitDifference( + generation0, + ImmutableArray.Create( + SemanticEdit.Create(SemanticEditKind.Update, f0, f1, preserveLocalVariables: true))); + + diff1.EmitResult.Diagnostics.Verify(); + diff1.VerifyIL("C.F", @" +{ + // Code size 28 (0x1c) + .maxstack 2 + IL_0000: ldsfld ""System.Func C.O__2_0#1.<0>__Target1"" + IL_0005: dup + IL_0006: brtrue.s IL_001b + IL_0008: pop + IL_0009: ldnull + IL_000a: ldftn ""int C.Target1()"" + IL_0010: newobj ""System.Func..ctor(object, System.IntPtr)"" + IL_0015: dup + IL_0016: stsfld ""System.Func C.O__2_0#1.<0>__Target1"" + IL_001b: ret +} +"); + + var reader0 = moduleData0.MetadataReader; + var reader1 = diff1.GetMetadata().Reader; + + CheckNames(reader0, reader0.GetTypeDefNames(), "", "C`1", "O__2_0`1"); + CheckNames(new[] { reader0, reader1 }, reader1.GetTypeDefNames(), "O__2_0#1`1"); + } + } From ef662144c2edee6439ef00b5acc593ed0bfe6745 Mon Sep 17 00:00:00 2001 From: Paul Chen Date: Wed, 5 Jan 2022 15:27:14 +0800 Subject: [PATCH 46/66] Add VerifyNoCacheContainers that also verifies the produced assembly. Add tests for target typed new. --- ...odeGenMethodGroupConversionCachingTests.cs | 83 +++++++++++++++++-- 1 file changed, 74 insertions(+), 9 deletions(-) diff --git a/src/Compilers/CSharp/Test/Emit2/CodeGen/CodeGenMethodGroupConversionCachingTests.cs b/src/Compilers/CSharp/Test/Emit2/CodeGen/CodeGenMethodGroupConversionCachingTests.cs index 1499b91636fc5..6a0cdfc176d19 100644 --- a/src/Compilers/CSharp/Test/Emit2/CodeGen/CodeGenMethodGroupConversionCachingTests.cs +++ b/src/Compilers/CSharp/Test/Emit2/CodeGen/CodeGenMethodGroupConversionCachingTests.cs @@ -4,7 +4,6 @@ using System; using System.Collections.Generic; -using System.Diagnostics; using Microsoft.CodeAnalysis.CSharp.Symbols; using Microsoft.CodeAnalysis.CSharp.Test.Utilities; using Roslyn.Test.Utilities; @@ -33,7 +32,7 @@ public static void Main(string[] args) static void Invoke(D x, D y) { Console.Write(Object.ReferenceEquals(x, y) ? ""FAIL"" : ""PASS""); } }"; var verifier = CompileAndVerify(source, expectedOutput: PASS); - Assert.Empty(verifier.TestData.Module!.GetAllSynthesizedMembers()); + VerifyNoCacheContainers(verifier); verifier.VerifyIL("C.Main", @" { // Code size 30 (0x1e) @@ -68,7 +67,7 @@ public static void Main(string[] args) void Invoke(D x, D y) { Console.Write(Object.ReferenceEquals(x, y) ? ""FAIL"" : ""PASS""); } }"; var verifier = CompileAndVerify(source, expectedOutput: PASS); - Assert.Empty(verifier.TestData.Module!.GetAllSynthesizedMembers()); + VerifyNoCacheContainers(verifier); verifier.VerifyIL("C.Main", @" { // Code size 37 (0x25) @@ -111,7 +110,7 @@ static class E } "; var verifier = CompileAndVerify(source, expectedOutput: PASS); - Assert.Empty(verifier.TestData.Module!.GetAllSynthesizedMembers()); + VerifyNoCacheContainers(verifier); verifier.VerifyIL("C.Main", @" { // Code size 37 (0x25) @@ -154,7 +153,7 @@ static class E } "; var verifier = CompileAndVerify(source, expectedOutput: PASS); - Assert.Empty(verifier.TestData.Module!.GetAllSynthesizedMembers()); + VerifyNoCacheContainers(verifier); verifier.VerifyIL("C.Main", @" { // Code size 35 (0x23) @@ -189,7 +188,7 @@ public static void Main(string[] args) } "; var verifier = CompileAndVerify(source); - Assert.DoesNotContain(verifier.TestData.Module!.GetAllSynthesizedMembers(), s => s.Key.Name.Contains("<>O")); + VerifyNoCacheContainers(verifier); verifier.VerifyIL("C.Main", @" { // Code size 156 (0x9c) @@ -260,7 +259,7 @@ public static void Main(string[] args) } "; var verifier = CompileAndVerify(source); - Assert.DoesNotContain(verifier.TestData.Module!.GetAllSynthesizedMembers(), s => s.Key.Name.Contains("<>O")); + VerifyNoCacheContainers(verifier); verifier.VerifyIL("C.<>c.
b__0_0", @" { // Code size 155 (0x9b) @@ -325,7 +324,7 @@ static void Target() { } } "; var verifier = CompileAndVerify(source); - Assert.Empty(verifier.TestData.Module!.GetAllSynthesizedMembers()); + VerifyNoCacheContainers(verifier); verifier.VerifyIL("C..cctor", @" { // Code size 18 (0x12) @@ -356,7 +355,7 @@ static C() } "; var verifier = CompileAndVerify(source); - Assert.Empty(verifier.TestData.Module!.GetAllSynthesizedMembers()); + VerifyNoCacheContainers(verifier); verifier.VerifyIL("C..cctor", @" { // Code size 18 (0x12) @@ -370,6 +369,72 @@ .maxstack 2 "); } + [Fact] + public void Not_TargetTypedNew0() + { + var source = @" +using System; + +Action f = new(Target); +f(); + +static void Target() { Console.WriteLine(""PASS""); } +"; + var verifier = CompileAndVerify(source, expectedOutput: PASS); + VerifyNoCacheContainers(verifier); + verifier.VerifyIL("", @" +{ + // Code size 18 (0x12) + .maxstack 2 + IL_0000: ldnull + IL_0001: ldftn ""void Program.<
$>g__Target|0_0()"" + IL_0007: newobj ""System.Action..ctor(object, System.IntPtr)"" + IL_000c: callvirt ""void System.Action.Invoke()"" + IL_0011: ret +} +"); + } + + [Fact] + public void Not_TargetTypedNew1() + { + var source = @" +#nullable enable +using System; + +Action? f = new(Target); +f(); + +static void Target() { Console.WriteLine(""PASS""); } +"; + var verifier = CompileAndVerify(source, expectedOutput: PASS); + VerifyNoCacheContainers(verifier); + verifier.VerifyIL("", @" +{ + // Code size 18 (0x12) + .maxstack 2 + IL_0000: ldnull + IL_0001: ldftn ""void Program.<
$>g__Target|0_0()"" + IL_0007: newobj ""System.Action..ctor(object, System.IntPtr)"" + IL_000c: callvirt ""void System.Action.Invoke()"" + IL_0011: ret +} +"); + } + + private static void VerifyNoCacheContainers(CodeAnalysis.Test.Utilities.CompilationVerifier verifier) + { + Assert.DoesNotContain(verifier.TestData.Module!.GetAllSynthesizedMembers(), s => s.Key.Name.Contains(">O")); + + RunValidators(verifier, assemblyValidator: null, symbolValidator: static iModule => + { + var module = iModule.GetSymbol(); + var types = module.GlobalNamespace.GetTypeMembers(); + + Assert.DoesNotContain(types, t => t.Name.Contains(">O")); + }); + } + [Fact] public void CacheExplicitConversions_TypeScoped_CouldBeModuleScoped0() { From 1eed89b8d4e26c2b05252f11e035d04f124f8a48 Mon Sep 17 00:00:00 2001 From: Paul Chen Date: Sun, 9 Jan 2022 13:46:35 +0800 Subject: [PATCH 47/66] Feedback: Use regular type cast and move to a shared library. --- .../Microsoft.CodeAnalysis.CSharp.Emit2.UnitTests.csproj | 6 ------ .../Utilities/CSharp}/EditAndContinueTestBase.cs | 2 +- .../Utilities/CSharp}/SemanticEditDescription.cs | 0 3 files changed, 1 insertion(+), 7 deletions(-) rename src/Compilers/{CSharp/Test/Emit/Emit/EditAndContinue => Test/Utilities/CSharp}/EditAndContinueTestBase.cs (99%) rename src/Compilers/{CSharp/Test/Emit/Emit/EditAndContinue => Test/Utilities/CSharp}/SemanticEditDescription.cs (100%) diff --git a/src/Compilers/CSharp/Test/Emit2/Microsoft.CodeAnalysis.CSharp.Emit2.UnitTests.csproj b/src/Compilers/CSharp/Test/Emit2/Microsoft.CodeAnalysis.CSharp.Emit2.UnitTests.csproj index 2396636716797..238c253b9cb1c 100644 --- a/src/Compilers/CSharp/Test/Emit2/Microsoft.CodeAnalysis.CSharp.Emit2.UnitTests.csproj +++ b/src/Compilers/CSharp/Test/Emit2/Microsoft.CodeAnalysis.CSharp.Emit2.UnitTests.csproj @@ -17,12 +17,6 @@ - - Emit\EditAndContinue\EditAndContinueTestBase.cs - - - Emit\EditAndContinue\SemanticEditDescription.cs - Emit\MvidReader.cs diff --git a/src/Compilers/CSharp/Test/Emit/Emit/EditAndContinue/EditAndContinueTestBase.cs b/src/Compilers/Test/Utilities/CSharp/EditAndContinueTestBase.cs similarity index 99% rename from src/Compilers/CSharp/Test/Emit/Emit/EditAndContinue/EditAndContinueTestBase.cs rename to src/Compilers/Test/Utilities/CSharp/EditAndContinueTestBase.cs index f51708a8bf74b..0690435a76ff7 100644 --- a/src/Compilers/CSharp/Test/Emit/Emit/EditAndContinue/EditAndContinueTestBase.cs +++ b/src/Compilers/Test/Utilities/CSharp/EditAndContinueTestBase.cs @@ -276,7 +276,7 @@ public static class EditAndContinueTestExtensions { internal static CSharpCompilation WithSource(this CSharpCompilation compilation, CSharpTestSource newSource) { - var previousParseOptions = compilation.SyntaxTrees.FirstOrDefault()?.Options as CSharpParseOptions; + var previousParseOptions = (CSharpParseOptions)compilation.SyntaxTrees.FirstOrDefault()?.Options; return compilation.RemoveAllSyntaxTrees().AddSyntaxTrees(newSource.GetSyntaxTrees(previousParseOptions)); } diff --git a/src/Compilers/CSharp/Test/Emit/Emit/EditAndContinue/SemanticEditDescription.cs b/src/Compilers/Test/Utilities/CSharp/SemanticEditDescription.cs similarity index 100% rename from src/Compilers/CSharp/Test/Emit/Emit/EditAndContinue/SemanticEditDescription.cs rename to src/Compilers/Test/Utilities/CSharp/SemanticEditDescription.cs From c985538036f0578d33035e3f285afe57e72a3317 Mon Sep 17 00:00:00 2001 From: Paul Chen Date: Sun, 9 Jan 2022 14:34:52 +0800 Subject: [PATCH 48/66] Feedback: Use regular type cast. Revert "Move to a shared library." This reverts commit 1eed89b8d4e26c2b05252f11e035d04f124f8a48. --- .../Emit/Emit/EditAndContinue}/EditAndContinueTestBase.cs | 0 .../Emit/Emit/EditAndContinue}/SemanticEditDescription.cs | 0 .../Microsoft.CodeAnalysis.CSharp.Emit2.UnitTests.csproj | 6 ++++++ 3 files changed, 6 insertions(+) rename src/Compilers/{Test/Utilities/CSharp => CSharp/Test/Emit/Emit/EditAndContinue}/EditAndContinueTestBase.cs (100%) rename src/Compilers/{Test/Utilities/CSharp => CSharp/Test/Emit/Emit/EditAndContinue}/SemanticEditDescription.cs (100%) diff --git a/src/Compilers/Test/Utilities/CSharp/EditAndContinueTestBase.cs b/src/Compilers/CSharp/Test/Emit/Emit/EditAndContinue/EditAndContinueTestBase.cs similarity index 100% rename from src/Compilers/Test/Utilities/CSharp/EditAndContinueTestBase.cs rename to src/Compilers/CSharp/Test/Emit/Emit/EditAndContinue/EditAndContinueTestBase.cs diff --git a/src/Compilers/Test/Utilities/CSharp/SemanticEditDescription.cs b/src/Compilers/CSharp/Test/Emit/Emit/EditAndContinue/SemanticEditDescription.cs similarity index 100% rename from src/Compilers/Test/Utilities/CSharp/SemanticEditDescription.cs rename to src/Compilers/CSharp/Test/Emit/Emit/EditAndContinue/SemanticEditDescription.cs diff --git a/src/Compilers/CSharp/Test/Emit2/Microsoft.CodeAnalysis.CSharp.Emit2.UnitTests.csproj b/src/Compilers/CSharp/Test/Emit2/Microsoft.CodeAnalysis.CSharp.Emit2.UnitTests.csproj index 238c253b9cb1c..2396636716797 100644 --- a/src/Compilers/CSharp/Test/Emit2/Microsoft.CodeAnalysis.CSharp.Emit2.UnitTests.csproj +++ b/src/Compilers/CSharp/Test/Emit2/Microsoft.CodeAnalysis.CSharp.Emit2.UnitTests.csproj @@ -17,6 +17,12 @@ + + Emit\EditAndContinue\EditAndContinueTestBase.cs + + + Emit\EditAndContinue\SemanticEditDescription.cs + Emit\MvidReader.cs From eecb17b3be151260409713f1cad5f6dcde00becb Mon Sep 17 00:00:00 2001 From: Paul Chen Date: Sun, 9 Jan 2022 17:36:34 +0800 Subject: [PATCH 49/66] Feedback: Use the symbolValidator parameter. --- ...odeGenMethodGroupConversionCachingTests.cs | 124 ++++++++---------- .../Test/Core/CompilationVerifier.cs | 20 --- 2 files changed, 57 insertions(+), 87 deletions(-) diff --git a/src/Compilers/CSharp/Test/Emit2/CodeGen/CodeGenMethodGroupConversionCachingTests.cs b/src/Compilers/CSharp/Test/Emit2/CodeGen/CodeGenMethodGroupConversionCachingTests.cs index 6a0cdfc176d19..6bec15ef03827 100644 --- a/src/Compilers/CSharp/Test/Emit2/CodeGen/CodeGenMethodGroupConversionCachingTests.cs +++ b/src/Compilers/CSharp/Test/Emit2/CodeGen/CodeGenMethodGroupConversionCachingTests.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; +using System.Linq; using Microsoft.CodeAnalysis.CSharp.Symbols; using Microsoft.CodeAnalysis.CSharp.Test.Utilities; using Roslyn.Test.Utilities; @@ -31,8 +32,7 @@ public static void Main(string[] args) static void Target() { Console.WriteLine(""FAIL""); } static void Invoke(D x, D y) { Console.Write(Object.ReferenceEquals(x, y) ? ""FAIL"" : ""PASS""); } }"; - var verifier = CompileAndVerify(source, expectedOutput: PASS); - VerifyNoCacheContainers(verifier); + var verifier = CompileAndVerify(source, expectedOutput: PASS, symbolValidator: NoCacheContainers("C")); verifier.VerifyIL("C.Main", @" { // Code size 30 (0x1e) @@ -66,8 +66,7 @@ public static void Main(string[] args) void Target() { Console.WriteLine(""FAIL""); } void Invoke(D x, D y) { Console.Write(Object.ReferenceEquals(x, y) ? ""FAIL"" : ""PASS""); } }"; - var verifier = CompileAndVerify(source, expectedOutput: PASS); - VerifyNoCacheContainers(verifier); + var verifier = CompileAndVerify(source, expectedOutput: PASS, symbolValidator: NoCacheContainers("C")); verifier.VerifyIL("C.Main", @" { // Code size 37 (0x25) @@ -109,8 +108,7 @@ static class E public static void Target(this C that) { Console.WriteLine(""FAIL""); } } "; - var verifier = CompileAndVerify(source, expectedOutput: PASS); - VerifyNoCacheContainers(verifier); + var verifier = CompileAndVerify(source, expectedOutput: PASS, symbolValidator: NoCacheContainers("C")); verifier.VerifyIL("C.Main", @" { // Code size 37 (0x25) @@ -152,8 +150,7 @@ static class E public static void Target(this C that) { Console.WriteLine(""FAIL""); } } "; - var verifier = CompileAndVerify(source, expectedOutput: PASS); - VerifyNoCacheContainers(verifier); + var verifier = CompileAndVerify(source, expectedOutput: PASS, symbolValidator: NoCacheContainers("C")); verifier.VerifyIL("C.Main", @" { // Code size 35 (0x23) @@ -187,8 +184,7 @@ public static void Main(string[] args) static int Target(int x) => 0; } "; - var verifier = CompileAndVerify(source); - VerifyNoCacheContainers(verifier); + var verifier = CompileAndVerify(source, symbolValidator: NoCacheContainers("C")); verifier.VerifyIL("C.Main", @" { // Code size 156 (0x9c) @@ -258,8 +254,7 @@ public static void Main(string[] args) static int Target(int x) => 0; } "; - var verifier = CompileAndVerify(source); - VerifyNoCacheContainers(verifier); + var verifier = CompileAndVerify(source, symbolValidator: NoCacheContainers("C")); verifier.VerifyIL("C.<>c.
b__0_0", @" { // Code size 155 (0x9b) @@ -323,8 +318,7 @@ class C static void Target() { } } "; - var verifier = CompileAndVerify(source); - VerifyNoCacheContainers(verifier); + var verifier = CompileAndVerify(source, symbolValidator: NoCacheContainers("C")); verifier.VerifyIL("C..cctor", @" { // Code size 18 (0x12) @@ -354,8 +348,7 @@ static C() } } "; - var verifier = CompileAndVerify(source); - VerifyNoCacheContainers(verifier); + var verifier = CompileAndVerify(source, symbolValidator: NoCacheContainers("C")); verifier.VerifyIL("C..cctor", @" { // Code size 18 (0x12) @@ -380,8 +373,7 @@ public void Not_TargetTypedNew0() static void Target() { Console.WriteLine(""PASS""); } "; - var verifier = CompileAndVerify(source, expectedOutput: PASS); - VerifyNoCacheContainers(verifier); + var verifier = CompileAndVerify(source, expectedOutput: PASS, symbolValidator: NoCacheContainers("Program")); verifier.VerifyIL("", @" { // Code size 18 (0x12) @@ -407,8 +399,7 @@ public void Not_TargetTypedNew1() static void Target() { Console.WriteLine(""PASS""); } "; - var verifier = CompileAndVerify(source, expectedOutput: PASS); - VerifyNoCacheContainers(verifier); + var verifier = CompileAndVerify(source, expectedOutput: PASS, symbolValidator: NoCacheContainers("Program")); verifier.VerifyIL("", @" { // Code size 18 (0x12) @@ -422,19 +413,6 @@ .maxstack 2 "); } - private static void VerifyNoCacheContainers(CodeAnalysis.Test.Utilities.CompilationVerifier verifier) - { - Assert.DoesNotContain(verifier.TestData.Module!.GetAllSynthesizedMembers(), s => s.Key.Name.Contains(">O")); - - RunValidators(verifier, assemblyValidator: null, symbolValidator: static iModule => - { - var module = iModule.GetSymbol(); - var types = module.GlobalNamespace.GetTypeMembers(); - - Assert.DoesNotContain(types, t => t.Name.Contains(">O")); - }); - } - [Fact] public void CacheExplicitConversions_TypeScoped_CouldBeModuleScoped0() { @@ -2746,10 +2724,9 @@ class D public static void Target() { } } "; - var verifier = CompileAndVerify(source); - verifier.VerifySynthesizedFields("C.<>O", ContainerFinder("C.<>O"), arity: 0 + var verifier = CompileAndVerify(source, symbolValidator: CacheContainer("C.<>O", arity: 0 , "System.Action <0>__Target" - ); + )); verifier.VerifyIL("C.Test0", @" { // Code size 25 (0x19) @@ -2790,10 +2767,9 @@ class C static V Target() { return default(V); } } "; - var verifier = CompileAndVerify(source); - verifier.VerifySynthesizedFields("C.<>O", ContainerFinder("C.<>O"), arity: 0 + var verifier = CompileAndVerify(source, symbolValidator: CacheContainer("C.<>O", arity: 0 , "System.Func <0>__Target" - ); + )); verifier.VerifyIL("C.Test0", @" { // Code size 25 (0x19) @@ -2835,10 +2811,9 @@ class C static T Target() { return default(T); } } "; - var verifier = CompileAndVerify(source); - verifier.VerifySynthesizedFields("C.<>O", ContainerFinder("C.<>O"), arity: 0 + var verifier = CompileAndVerify(source, symbolValidator: CacheContainer("C.<>O", arity: 0 , "C.MyFunc <0>__Target" - ); + )); verifier.VerifyIL("C.Test0", @" { // Code size 25 (0x19) @@ -2885,10 +2860,9 @@ class D public static void Target() { } } "; - var verifier = CompileAndVerify(source); - verifier.VerifySynthesizedFields("C.O__0_0", ContainerFinder("C.O__0_0"), arity: 1 + var verifier = CompileAndVerify(source, symbolValidator: CacheContainer("C.O__0_0", arity: 1 , "System.Action <0>__Target" - ); + )); verifier.VerifyIL("C.Test", @" { // Code size 49 (0x31) @@ -2928,10 +2902,9 @@ class D public static B Target(H h) => default(B); } "; - var verifier = CompileAndVerify(source); - verifier.VerifySynthesizedFields("C.O__0_0", ContainerFinder("C.O__0_0"), arity: 1 + var verifier = CompileAndVerify(source, symbolValidator: CacheContainer("C.O__0_0", arity: 1 , "System.Func <0>__Target" - ); + )); verifier.VerifyIL("C.Test", @" { // Code size 49 (0x31) @@ -2973,10 +2946,9 @@ static class D public static B Target(this int num) => default(B); } "; - var verifier = CompileAndVerify(source); - verifier.VerifySynthesizedFields("C.O__2_0", ContainerFinder("C.O__2_0"), arity: 1 + var verifier = CompileAndVerify(source, symbolValidator: CacheContainer("C.O__2_0", arity: 1 , "C.MyFunc <0>__Target" - ); + )); verifier.VerifyIL("C.Test", @" { // Code size 49 (0x31) @@ -3064,8 +3036,7 @@ static class E public static void Target5(this N n) { } } "; - var verifier = CompileAndVerify(source); - verifier.VerifySynthesizedFields("A.B.<>O", ContainerFinder("A.B.<>O"), arity: 0 + var verifier = CompileAndVerify(source, symbolValidator: CacheContainer("A.B.<>O", arity: 0 , "System.Action <0>__Target0" , "System.Action <1>__Target1" , "System.Action <2>__Target2" @@ -3073,7 +3044,7 @@ public static void Target5(this N n) { } , "System.Action <4>__Target4" , "System.Action <5>__Target5" , "System.Action <6>__Target5" - ); + )); verifier.VerifyIL("A.B.Test0", @" { // Code size 49 (0x31) @@ -3218,13 +3189,12 @@ static class E public static void Target3(this C c) { } } "; - var verifier = CompileAndVerify(source); - verifier.VerifySynthesizedFields("C.O__0_0", ContainerFinder("C.O__0_0"), arity: 1 + var verifier = CompileAndVerify(source, symbolValidator: CacheContainer("C.O__0_0", arity: 1 , "System.Action <0>__Target0" , "System.Action <1>__Target1" , "System.Action <2>__Target2" , "System.Action <3>__Target3" - ); + )); verifier.VerifyIL("C.Test", @" { // Code size 97 (0x61) @@ -3286,13 +3256,12 @@ static void Test() public static void Target3(this C c) { } } "; - var verifier = CompileAndVerify(source); - verifier.VerifySynthesizedFields("E.O__0_0", ContainerFinder("E.O__0_0"), arity: 1 + var verifier = CompileAndVerify(source, symbolValidator: CacheContainer("E.O__0_0", arity: 1 , "System.Action <0>__Target0" , "System.Action <1>__Target1" , "System.Action <2>__Target2" , "System.Action <3>__Target3" - ); + )); verifier.VerifyIL("E.Test", @" { // Code size 97 (0x61) @@ -3361,10 +3330,9 @@ void LF5() } } "; - var verifier = CompileAndVerify(source); - verifier.VerifySynthesizedFields("E.O__0_0", ContainerFinder("E.O__0_0"), arity: 1 + var verifier = CompileAndVerify(source, symbolValidator: CacheContainer("E.O__0_0", arity: 1 , "System.Action <0>__Target" - ); + )); verifier.VerifyIL("E.g__LF3|0_1", @" { // Code size 30 (0x1e) @@ -3443,11 +3411,10 @@ void LF3() static void Main(string[] args) { Test(); } } "; - var verifier = CompileAndVerify(source, expectedOutput: PASS); - verifier.VerifySynthesizedFields("E.O__0_0", ContainerFinder("E.O__0_0"), arity: 2 + var verifier = CompileAndVerify(source, expectedOutput: PASS, symbolValidator: CacheContainer("E.O__0_0", arity: 2 , "System.Action <0>__LF2" , "System.Action <1>__LF2" - ); + )); verifier.VerifyIL("E.g__LF1|0_1", @" { // Code size 33 (0x21) @@ -5684,5 +5651,28 @@ .locals init (C1.F V_0) //x "); } - private static Func ContainerFinder(string name) => module => module.GlobalNamespace.GetMember(name); + private static Action CacheContainer(string typeName, int arity, params string[] expectedFields) + { + return module => + { + var container = module.GlobalNamespace.GetMember(typeName); + AssertEx.NotNull(container); + Assert.Equal(arity, container.Arity); + + var fields = container.GetMembers().OfType().Select(field => $"{field.Type.ToTestDisplayString()} {field.Name}").ToArray(); + AssertEx.SetEqual(expectedFields, fields); + }; + } + + private static Action NoCacheContainers(string containingTypeName) + { + return module => + { + var containingType = module.GlobalNamespace.GetMember(containingTypeName); + var nestedTypes = containingType.GetTypeMembers(); + + Assert.DoesNotContain(nestedTypes, t => t.Name.StartsWith("<") && t.Name.Contains(">O")); + }; + } + } diff --git a/src/Compilers/Test/Core/CompilationVerifier.cs b/src/Compilers/Test/Core/CompilationVerifier.cs index deb410741f33d..1ed62d8b828c8 100644 --- a/src/Compilers/Test/Core/CompilationVerifier.cs +++ b/src/Compilers/Test/Core/CompilationVerifier.cs @@ -446,25 +446,5 @@ public void VerifySynthesizedFields(string containingTypeName, params string[] e .ToList(); AssertEx.SetEqual(expectedFields, members); } - - /// - /// Useful for verifying the expected variables are hoisted for closures, async, and iterator methods. - /// Additionally this overload performs validations against the produced assembly. - /// - public void VerifySynthesizedFields(string containingTypeName, Func findContainer, int arity, params string[] expectedFields) - { - VerifySynthesizedFields(containingTypeName, expectedFields); - - CommonTestBase.RunValidators(this, assemblyValidator: null, symbolValidator: module => - { - var container = findContainer(module); - AssertEx.NotNull(container); - Assert.Equal(arity, container.Arity); - - var fields = container.GetMembers().OfType().Select(field => $"{field.Type} {field.Name}").ToArray(); - AssertEx.SetEqual(expectedFields, fields); - }); - } - } } From 67dfa96d69546e0325928f4805750bbd7636db5f Mon Sep 17 00:00:00 2001 From: Paul Chen Date: Sun, 9 Jan 2022 18:36:39 +0800 Subject: [PATCH 50/66] Add tests for conversions from instances. --- ...odeGenMethodGroupConversionCachingTests.cs | 80 +++++++++++++++++++ 1 file changed, 80 insertions(+) diff --git a/src/Compilers/CSharp/Test/Emit2/CodeGen/CodeGenMethodGroupConversionCachingTests.cs b/src/Compilers/CSharp/Test/Emit2/CodeGen/CodeGenMethodGroupConversionCachingTests.cs index 6bec15ef03827..e2b0848b2a7c6 100644 --- a/src/Compilers/CSharp/Test/Emit2/CodeGen/CodeGenMethodGroupConversionCachingTests.cs +++ b/src/Compilers/CSharp/Test/Emit2/CodeGen/CodeGenMethodGroupConversionCachingTests.cs @@ -87,6 +87,44 @@ .locals init (C V_0) //c "); } + [Fact] + public void Not_Conversions_Instance() + { + var source = @" +using System; +delegate void D(); +class C +{ + public static void Main(string[] args) + { + var c = new C(); + c.Invoke(c.Target, c.Target); + } + + void Target() { Console.WriteLine(""FAIL""); } + void Invoke(D x, D y) { Console.Write(Object.ReferenceEquals(x, y) ? ""FAIL"" : ""PASS""); } +}"; + var verifier = CompileAndVerify(source, expectedOutput: PASS, symbolValidator: NoCacheContainers("C")); + verifier.VerifyIL("C.Main", @" +{ + // Code size 37 (0x25) + .maxstack 4 + .locals init (C V_0) //c + IL_0000: newobj ""C..ctor()"" + IL_0005: stloc.0 + IL_0006: ldloc.0 + IL_0007: ldloc.0 + IL_0008: ldftn ""void C.Target()"" + IL_000e: newobj ""D..ctor(object, System.IntPtr)"" + IL_0013: ldloc.0 + IL_0014: ldftn ""void C.Target()"" + IL_001a: newobj ""D..ctor(object, System.IntPtr)"" + IL_001f: callvirt ""void C.Invoke(D, D)"" + IL_0024: ret +} +"); + } + [Fact] public void Not_DelegateCreations_InstanceExtensionMethod() { @@ -129,6 +167,48 @@ .locals init (C V_0) //c "); } + [Fact] + public void Not_Conversions_InstanceExtensionMethod() + { + var source = @" +using System; +delegate void D(); +class C +{ + public static void Main(string[] args) + { + var c = new C(); + c.Invoke(c.Target, c.Target); + } + + void Invoke(D x, D y) { Console.Write(Object.ReferenceEquals(x, y) ? ""FAIL"" : ""PASS""); } +} +static class E +{ + public static void Target(this C that) { Console.WriteLine(""FAIL""); } +} +"; + var verifier = CompileAndVerify(source, expectedOutput: PASS, symbolValidator: NoCacheContainers("C")); + verifier.VerifyIL("C.Main", @" +{ + // Code size 37 (0x25) + .maxstack 4 + .locals init (C V_0) //c + IL_0000: newobj ""C..ctor()"" + IL_0005: stloc.0 + IL_0006: ldloc.0 + IL_0007: ldloc.0 + IL_0008: ldftn ""void E.Target(C)"" + IL_000e: newobj ""D..ctor(object, System.IntPtr)"" + IL_0013: ldloc.0 + IL_0014: ldftn ""void E.Target(C)"" + IL_001a: newobj ""D..ctor(object, System.IntPtr)"" + IL_001f: callvirt ""void C.Invoke(D, D)"" + IL_0024: ret +} +"); + } + [Fact] public void Not_DelegateCreations_StaticExtensionMethod() { From 35de0aa8edbfdacab022ec03b630ab51ca5997d1 Mon Sep 17 00:00:00 2001 From: Paul Chen Date: Sun, 9 Jan 2022 18:44:41 +0800 Subject: [PATCH 51/66] Feedback: Remove a duplicated C# 10 test case. --- ...odeGenMethodGroupConversionCachingTests.cs | 60 ++----------------- 1 file changed, 5 insertions(+), 55 deletions(-) diff --git a/src/Compilers/CSharp/Test/Emit2/CodeGen/CodeGenMethodGroupConversionCachingTests.cs b/src/Compilers/CSharp/Test/Emit2/CodeGen/CodeGenMethodGroupConversionCachingTests.cs index e2b0848b2a7c6..c7d233ffe6226 100644 --- a/src/Compilers/CSharp/Test/Emit2/CodeGen/CodeGenMethodGroupConversionCachingTests.cs +++ b/src/Compilers/CSharp/Test/Emit2/CodeGen/CodeGenMethodGroupConversionCachingTests.cs @@ -5355,66 +5355,15 @@ static void Main() static void Report(Delegate d) => Console.WriteLine($""{d.GetType().Namespace}.{d.GetType().Name}""); }"; - var comp = CreateCompilation(source, parseOptions: TestOptions.Regular10, options: TestOptions.DebugExe); + var comp = CreateCompilation(source, options: TestOptions.DebugExe); comp.VerifyDiagnostics(); var verifier = CompileAndVerify(comp, expectedOutput: @"System.Action System.Action System.Action"); - verifier.VerifyIL("Program.Main", -@"{ - // Code size 100 (0x64) - .maxstack 2 - .locals init (System.Action V_0, //d1 - System.Action V_1, //d2 - System.Action V_2) //d3 - IL_0000: nop - IL_0001: ldnull - IL_0002: ldftn ""void Program.Main()"" - IL_0008: newobj ""System.Action..ctor(object, System.IntPtr)"" - IL_000d: stloc.0 - IL_000e: ldloc.0 - IL_000f: call ""void Program.Report(System.Delegate)"" - IL_0014: nop - IL_0015: ldsfld ""System.Action Program.<>c.<>9__0_0"" - IL_001a: dup - IL_001b: brtrue.s IL_0034 - IL_001d: pop - IL_001e: ldsfld ""Program.<>c Program.<>c.<>9"" - IL_0023: ldftn ""void Program.<>c.
b__0_0()"" - IL_0029: newobj ""System.Action..ctor(object, System.IntPtr)"" - IL_002e: dup - IL_002f: stsfld ""System.Action Program.<>c.<>9__0_0"" - IL_0034: stloc.1 - IL_0035: ldloc.1 - IL_0036: call ""void Program.Report(System.Delegate)"" - IL_003b: nop - IL_003c: ldsfld ""System.Action Program.<>c.<>9__0_1"" - IL_0041: dup - IL_0042: brtrue.s IL_005b - IL_0044: pop - IL_0045: ldsfld ""Program.<>c Program.<>c.<>9"" - IL_004a: ldftn ""void Program.<>c.
b__0_1()"" - IL_0050: newobj ""System.Action..ctor(object, System.IntPtr)"" - IL_0055: dup - IL_0056: stsfld ""System.Action Program.<>c.<>9__0_1"" - IL_005b: stloc.2 - IL_005c: ldloc.2 - IL_005d: call ""void Program.Report(System.Delegate)"" - IL_0062: nop - IL_0063: ret -}"); - - comp = CreateCompilation(source, options: TestOptions.DebugExe); - comp.VerifyDiagnostics(); - - verifier = CompileAndVerify(comp, expectedOutput: -@"System.Action -System.Action -System.Action"); - verifier.VerifyIL("Program.Main", -@"{ + verifier.VerifyIL("Program.Main", @" +{ // Code size 115 (0x73) .maxstack 2 .locals init (System.Action V_0, //d1 @@ -5461,7 +5410,8 @@ .locals init (System.Action V_0, //d1 IL_006c: call ""void Program.Report(System.Delegate)"" IL_0071: nop IL_0072: ret -}"); +} +"); } [Fact] From 75a11ccc4072159a217749bbe3ad3d938563c714 Mon Sep 17 00:00:00 2001 From: Paul Chen Date: Sun, 9 Jan 2022 19:05:21 +0800 Subject: [PATCH 52/66] Feedback: Print the expression tree. --- .../CodeGenMethodGroupConversionCachingTests.cs | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/src/Compilers/CSharp/Test/Emit2/CodeGen/CodeGenMethodGroupConversionCachingTests.cs b/src/Compilers/CSharp/Test/Emit2/CodeGen/CodeGenMethodGroupConversionCachingTests.cs index c7d233ffe6226..5bc9bfb00a871 100644 --- a/src/Compilers/CSharp/Test/Emit2/CodeGen/CodeGenMethodGroupConversionCachingTests.cs +++ b/src/Compilers/CSharp/Test/Emit2/CodeGen/CodeGenMethodGroupConversionCachingTests.cs @@ -259,15 +259,18 @@ class C public static void Main(string[] args) { Expression>> e = x => Target; + Console.WriteLine(e); } static int Target(int x) => 0; } "; - var verifier = CompileAndVerify(source, symbolValidator: NoCacheContainers("C")); + var verifier = CompileAndVerify(source + , expectedOutput: "x => Convert(Int32 Target(Int32).CreateDelegate(System.Func`2[System.Int32,System.Int32], null), Func`2)" + , symbolValidator: NoCacheContainers("C")); verifier.VerifyIL("C.Main", @" { - // Code size 156 (0x9c) + // Code size 160 (0xa0) .maxstack 7 .locals init (System.Linq.Expressions.ParameterExpression V_0) IL_0000: ldtoken ""int"" @@ -312,8 +315,8 @@ .locals init (System.Linq.Expressions.ParameterExpression V_0) IL_0093: ldloc.0 IL_0094: stelem.ref IL_0095: call ""System.Linq.Expressions.Expression>> System.Linq.Expressions.Expression.Lambda>>(System.Linq.Expressions.Expression, params System.Linq.Expressions.ParameterExpression[])"" - IL_009a: pop - IL_009b: ret + IL_009a: call ""void System.Console.WriteLine(object)"" + IL_009f: ret } "); } @@ -329,12 +332,15 @@ class C public static void Main(string[] args) { Func>>> f = x => y => Target; + Console.WriteLine(f(0)); } static int Target(int x) => 0; } "; - var verifier = CompileAndVerify(source, symbolValidator: NoCacheContainers("C")); + var verifier = CompileAndVerify(source + , expectedOutput: "y => Convert(Int32 Target(Int32).CreateDelegate(System.Func`2[System.Int32,System.Int32], null), Func`2)" + , symbolValidator: NoCacheContainers("C")); verifier.VerifyIL("C.<>c.
b__0_0", @" { // Code size 155 (0x9b) From 35a9f647d3c2b6b0c8ca336375c9c8db0033942e Mon Sep 17 00:00:00 2001 From: Paul Chen Date: Sun, 9 Jan 2022 20:41:33 +0800 Subject: [PATCH 53/66] Feedback: Add name ambiguity tests. --- ...odeGenMethodGroupConversionCachingTests.cs | 364 ++++++++++++++++++ 1 file changed, 364 insertions(+) diff --git a/src/Compilers/CSharp/Test/Emit2/CodeGen/CodeGenMethodGroupConversionCachingTests.cs b/src/Compilers/CSharp/Test/Emit2/CodeGen/CodeGenMethodGroupConversionCachingTests.cs index 5bc9bfb00a871..4e91f17b2cdfd 100644 --- a/src/Compilers/CSharp/Test/Emit2/CodeGen/CodeGenMethodGroupConversionCachingTests.cs +++ b/src/Compilers/CSharp/Test/Emit2/CodeGen/CodeGenMethodGroupConversionCachingTests.cs @@ -3537,6 +3537,370 @@ .maxstack 2 "); } + [Fact] + public void NameAmbiguity_Containers0() + { + var source = @" +void Owner(int i) +{ + var f = Target; +} +void X() +{ + void Owner(string s) + { + var f = Target; + } +} +static void Target() { } +"; + var verifier = CompileAndVerify(source, symbolValidator: static module => + { + CacheContainer("Program.O__0_0", arity: 1, "System.Action <0>__Target")(module); + CacheContainer("Program.O__0_1", arity: 1, "System.Action <0>__Target")(module); + }); + verifier.VerifyIL("Program.<
$>g__Owner|0_0", @" +{ + // Code size 25 (0x19) + .maxstack 2 + IL_0000: ldsfld ""System.Action Program.O__0_0.<0>__Target"" + IL_0005: brtrue.s IL_0018 + IL_0007: ldnull + IL_0008: ldftn ""void Program.<
$>g__Target|0_2()"" + IL_000e: newobj ""System.Action..ctor(object, System.IntPtr)"" + IL_0013: stsfld ""System.Action Program.O__0_0.<0>__Target"" + IL_0018: ret +} +"); + verifier.VerifyIL("Program.<
$>g__Owner|0_3", @" +{ + // Code size 25 (0x19) + .maxstack 2 + IL_0000: ldsfld ""System.Action Program.O__0_1.<0>__Target"" + IL_0005: brtrue.s IL_0018 + IL_0007: ldnull + IL_0008: ldftn ""void Program.<
$>g__Target|0_2()"" + IL_000e: newobj ""System.Action..ctor(object, System.IntPtr)"" + IL_0013: stsfld ""System.Action Program.O__0_1.<0>__Target"" + IL_0018: ret +} +"); + } + + [Fact] + public void NameAmbiguity_Containers1() + { + var source = @" +class C +{ + void Owner(int i) + { + var f = Target; + } + void Owner(string s) + { + var f = Target; + } + static void Target() { } +} +"; + var verifier = CompileAndVerify(source, symbolValidator: static module => + { + CacheContainer("C.O__0_0", arity: 1, "System.Action <0>__Target")(module); + CacheContainer("C.O__1_0", arity: 1, "System.Action <0>__Target")(module); + }); + verifier.VerifyIL("C.Owner(int)", @" +{ + // Code size 25 (0x19) + .maxstack 2 + IL_0000: ldsfld ""System.Action C.O__0_0.<0>__Target"" + IL_0005: brtrue.s IL_0018 + IL_0007: ldnull + IL_0008: ldftn ""void C.Target()"" + IL_000e: newobj ""System.Action..ctor(object, System.IntPtr)"" + IL_0013: stsfld ""System.Action C.O__0_0.<0>__Target"" + IL_0018: ret +} +"); + verifier.VerifyIL("C.Owner(string)", @" +{ + // Code size 25 (0x19) + .maxstack 2 + IL_0000: ldsfld ""System.Action C.O__1_0.<0>__Target"" + IL_0005: brtrue.s IL_0018 + IL_0007: ldnull + IL_0008: ldftn ""void C.Target()"" + IL_000e: newobj ""System.Action..ctor(object, System.IntPtr)"" + IL_0013: stsfld ""System.Action C.O__1_0.<0>__Target"" + IL_0018: ret +} +"); + } + + [Fact] + public void NameAmbiguity_Containers2() + { + var source = @" +class C +{ + void Owner(int i) + { + var f = Target; + } + void F() + { + void Owner(string s) + { + var f = Target; + } + } + static void Target() { } +} +"; + var verifier = CompileAndVerify(source, symbolValidator: static module => + { + CacheContainer("C.O__0_0", arity: 1, "System.Action <0>__Target")(module); + CacheContainer("C.O__1_0", arity: 1, "System.Action <0>__Target")(module); + }); + verifier.VerifyIL("C.Owner(int)", @" +{ + // Code size 25 (0x19) + .maxstack 2 + IL_0000: ldsfld ""System.Action C.O__0_0.<0>__Target"" + IL_0005: brtrue.s IL_0018 + IL_0007: ldnull + IL_0008: ldftn ""void C.Target()"" + IL_000e: newobj ""System.Action..ctor(object, System.IntPtr)"" + IL_0013: stsfld ""System.Action C.O__0_0.<0>__Target"" + IL_0018: ret +} +"); + verifier.VerifyIL("C.g__Owner|1_0", @" +{ + // Code size 25 (0x19) + .maxstack 2 + IL_0000: ldsfld ""System.Action C.O__1_0.<0>__Target"" + IL_0005: brtrue.s IL_0018 + IL_0007: ldnull + IL_0008: ldftn ""void C.Target()"" + IL_000e: newobj ""System.Action..ctor(object, System.IntPtr)"" + IL_0013: stsfld ""System.Action C.O__1_0.<0>__Target"" + IL_0018: ret +} +"); + } + + [Fact] + public void NameAmbiguity_Fields0() + { + var source = @" +void F0() +{ + var f = Target; + static void Target() { } +} +void F1() +{ + var f = Target; + static void Target() { } +} +"; + var verifier = CompileAndVerify(source, symbolValidator: CacheContainer("Program.<>O", arity: 0 + , "System.Action <0>__Target" + , "System.Action <1>__Target" + )); + verifier.VerifyIL("Program.<
$>g__F0|0_0", @" +{ + // Code size 25 (0x19) + .maxstack 2 + IL_0000: ldsfld ""System.Action Program.<>O.<0>__Target"" + IL_0005: brtrue.s IL_0018 + IL_0007: ldnull + IL_0008: ldftn ""void Program.<
$>g__Target|0_2()"" + IL_000e: newobj ""System.Action..ctor(object, System.IntPtr)"" + IL_0013: stsfld ""System.Action Program.<>O.<0>__Target"" + IL_0018: ret +} +"); + verifier.VerifyIL("Program.<
$>g__F1|0_1", @" +{ + // Code size 25 (0x19) + .maxstack 2 + IL_0000: ldsfld ""System.Action Program.<>O.<1>__Target"" + IL_0005: brtrue.s IL_0018 + IL_0007: ldnull + IL_0008: ldftn ""void Program.<
$>g__Target|0_3()"" + IL_000e: newobj ""System.Action..ctor(object, System.IntPtr)"" + IL_0013: stsfld ""System.Action Program.<>O.<1>__Target"" + IL_0018: ret +} +"); + } + + [Fact] + public void NameAmbiguity_Fields2() + { + var source = @" +class C +{ + void F() + { + void Owner() + { + void F0() + { + var f = Target; + static void Target() { } + } + void F1() + { + var f = Target; + static void Target() { } + } + } + } +} +"; + var verifier = CompileAndVerify(source, symbolValidator: CacheContainer("C.O__0_0", arity: 1 + , "System.Action <0>__Target" + , "System.Action <1>__Target" + )); + verifier.VerifyIL("C.g__F0|0_1", @" +{ + // Code size 25 (0x19) + .maxstack 2 + IL_0000: ldsfld ""System.Action C.O__0_0.<0>__Target"" + IL_0005: brtrue.s IL_0018 + IL_0007: ldnull + IL_0008: ldftn ""void C.g__Target|0_3()"" + IL_000e: newobj ""System.Action..ctor(object, System.IntPtr)"" + IL_0013: stsfld ""System.Action C.O__0_0.<0>__Target"" + IL_0018: ret +} +"); + verifier.VerifyIL("C.g__F1|0_2", @" +{ + // Code size 25 (0x19) + .maxstack 2 + IL_0000: ldsfld ""System.Action C.O__0_0.<1>__Target"" + IL_0005: brtrue.s IL_0018 + IL_0007: ldnull + IL_0008: ldftn ""void C.g__Target|0_4()"" + IL_000e: newobj ""System.Action..ctor(object, System.IntPtr)"" + IL_0013: stsfld ""System.Action C.O__0_0.<1>__Target"" + IL_0018: ret +} +"); + } + + [Fact] + public void NameAmbiguity_Fields3() + { + var source = @" +using System; +class C +{ + void F() + { + void Owner() + { + Action f = E.Target; + + void F1() + { + Action f = E.Target; + } + } + } +} +class E +{ + public static void Target(int i) { } + public static void Target(string i) { } +} +"; + var verifier = CompileAndVerify(source, symbolValidator: CacheContainer("C.O__0_0", arity: 1 + , "System.Action <0>__Target" + , "System.Action <1>__Target" + )); + verifier.VerifyIL("C.g__Owner|0_0", @" +{ + // Code size 25 (0x19) + .maxstack 2 + IL_0000: ldsfld ""System.Action C.O__0_0.<0>__Target"" + IL_0005: brtrue.s IL_0018 + IL_0007: ldnull + IL_0008: ldftn ""void E.Target(int)"" + IL_000e: newobj ""System.Action..ctor(object, System.IntPtr)"" + IL_0013: stsfld ""System.Action C.O__0_0.<0>__Target"" + IL_0018: ret +} +"); + verifier.VerifyIL("C.g__F1|0_1", @" +{ + // Code size 25 (0x19) + .maxstack 2 + IL_0000: ldsfld ""System.Action C.O__0_0.<1>__Target"" + IL_0005: brtrue.s IL_0018 + IL_0007: ldnull + IL_0008: ldftn ""void E.Target(string)"" + IL_000e: newobj ""System.Action..ctor(object, System.IntPtr)"" + IL_0013: stsfld ""System.Action C.O__0_0.<1>__Target"" + IL_0018: ret +} +"); + } + + [Fact] + public void NameAmbiguity_Fields1() + { + var source = @" +class C +{ + void F0() + { + var f = Target; + static void Target() { } + } + void F1() + { + var f = Target; + static void Target() { } + } +} +"; + var verifier = CompileAndVerify(source, symbolValidator: CacheContainer("C.<>O", arity: 0 + , "System.Action <0>__Target" + , "System.Action <1>__Target" + )); + verifier.VerifyIL("C.F0", @" +{ + // Code size 25 (0x19) + .maxstack 2 + IL_0000: ldsfld ""System.Action C.<>O.<0>__Target"" + IL_0005: brtrue.s IL_0018 + IL_0007: ldnull + IL_0008: ldftn ""void C.g__Target|0_0()"" + IL_000e: newobj ""System.Action..ctor(object, System.IntPtr)"" + IL_0013: stsfld ""System.Action C.<>O.<0>__Target"" + IL_0018: ret +} +"); + verifier.VerifyIL("C.F1", @" +{ + // Code size 25 (0x19) + .maxstack 2 + IL_0000: ldsfld ""System.Action C.<>O.<1>__Target"" + IL_0005: brtrue.s IL_0018 + IL_0007: ldnull + IL_0008: ldftn ""void C.g__Target|1_0()"" + IL_000e: newobj ""System.Action..ctor(object, System.IntPtr)"" + IL_0013: stsfld ""System.Action C.<>O.<1>__Target"" + IL_0018: ret +} +"); + } + [Fact] public void EventHandlers_TypeScoped_CouldBeModuleScoped0() { From 0ed19da1e4366d71cd5f1657a2c95e4f7c07933c Mon Sep 17 00:00:00 2001 From: Paul Chen Date: Sun, 9 Jan 2022 21:11:22 +0800 Subject: [PATCH 54/66] Fix expectedOutput for expression lamba. --- .../CodeGen/CodeGenMethodGroupConversionCachingTests.cs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/Compilers/CSharp/Test/Emit2/CodeGen/CodeGenMethodGroupConversionCachingTests.cs b/src/Compilers/CSharp/Test/Emit2/CodeGen/CodeGenMethodGroupConversionCachingTests.cs index 4e91f17b2cdfd..ff95f7f1e659e 100644 --- a/src/Compilers/CSharp/Test/Emit2/CodeGen/CodeGenMethodGroupConversionCachingTests.cs +++ b/src/Compilers/CSharp/Test/Emit2/CodeGen/CodeGenMethodGroupConversionCachingTests.cs @@ -266,7 +266,11 @@ public static void Main(string[] args) } "; var verifier = CompileAndVerify(source +#if NETFRAMEWORK + , expectedOutput: "x => Convert(Int32 Target(Int32).CreateDelegate(System.Func`2[System.Int32,System.Int32], null))" +#else , expectedOutput: "x => Convert(Int32 Target(Int32).CreateDelegate(System.Func`2[System.Int32,System.Int32], null), Func`2)" +#endif , symbolValidator: NoCacheContainers("C")); verifier.VerifyIL("C.Main", @" { @@ -339,7 +343,11 @@ public static void Main(string[] args) } "; var verifier = CompileAndVerify(source +#if NETFRAMEWORK + , expectedOutput: "y => Convert(Int32 Target(Int32).CreateDelegate(System.Func`2[System.Int32,System.Int32], null))" +#else , expectedOutput: "y => Convert(Int32 Target(Int32).CreateDelegate(System.Func`2[System.Int32,System.Int32], null), Func`2)" +#endif , symbolValidator: NoCacheContainers("C")); verifier.VerifyIL("C.<>c.
b__0_0", @" { From e9cb8a95194d4bfc59911c19436cdc82f5d9813a Mon Sep 17 00:00:00 2001 From: Paul Chen Date: Sun, 9 Jan 2022 21:58:33 +0800 Subject: [PATCH 55/66] Add an assert. --- .../Emit2/CodeGen/CodeGenMethodGroupConversionCachingTests.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Compilers/CSharp/Test/Emit2/CodeGen/CodeGenMethodGroupConversionCachingTests.cs b/src/Compilers/CSharp/Test/Emit2/CodeGen/CodeGenMethodGroupConversionCachingTests.cs index ff95f7f1e659e..04ac5f423ff87 100644 --- a/src/Compilers/CSharp/Test/Emit2/CodeGen/CodeGenMethodGroupConversionCachingTests.cs +++ b/src/Compilers/CSharp/Test/Emit2/CodeGen/CodeGenMethodGroupConversionCachingTests.cs @@ -6077,8 +6077,9 @@ private static Action NoCacheContainers(string containingTypeName) return module => { var containingType = module.GlobalNamespace.GetMember(containingTypeName); - var nestedTypes = containingType.GetTypeMembers(); + AssertEx.NotNull(containingType); + var nestedTypes = containingType.GetTypeMembers(); Assert.DoesNotContain(nestedTypes, t => t.Name.StartsWith("<") && t.Name.Contains(">O")); }; } From e659853aeb7c7d27c1b4204300a60db505add140 Mon Sep 17 00:00:00 2001 From: Paul Chen Date: Sun, 9 Jan 2022 23:29:46 +0800 Subject: [PATCH 56/66] Rename helper methods. --- ...odeGenMethodGroupConversionCachingTests.cs | 70 +++++++++---------- 1 file changed, 35 insertions(+), 35 deletions(-) diff --git a/src/Compilers/CSharp/Test/Emit2/CodeGen/CodeGenMethodGroupConversionCachingTests.cs b/src/Compilers/CSharp/Test/Emit2/CodeGen/CodeGenMethodGroupConversionCachingTests.cs index 04ac5f423ff87..3152b0a4adeb6 100644 --- a/src/Compilers/CSharp/Test/Emit2/CodeGen/CodeGenMethodGroupConversionCachingTests.cs +++ b/src/Compilers/CSharp/Test/Emit2/CodeGen/CodeGenMethodGroupConversionCachingTests.cs @@ -32,7 +32,7 @@ public static void Main(string[] args) static void Target() { Console.WriteLine(""FAIL""); } static void Invoke(D x, D y) { Console.Write(Object.ReferenceEquals(x, y) ? ""FAIL"" : ""PASS""); } }"; - var verifier = CompileAndVerify(source, expectedOutput: PASS, symbolValidator: NoCacheContainers("C")); + var verifier = CompileAndVerify(source, expectedOutput: PASS, symbolValidator: VerifyNoCacheContainersIn("C")); verifier.VerifyIL("C.Main", @" { // Code size 30 (0x1e) @@ -66,7 +66,7 @@ public static void Main(string[] args) void Target() { Console.WriteLine(""FAIL""); } void Invoke(D x, D y) { Console.Write(Object.ReferenceEquals(x, y) ? ""FAIL"" : ""PASS""); } }"; - var verifier = CompileAndVerify(source, expectedOutput: PASS, symbolValidator: NoCacheContainers("C")); + var verifier = CompileAndVerify(source, expectedOutput: PASS, symbolValidator: VerifyNoCacheContainersIn("C")); verifier.VerifyIL("C.Main", @" { // Code size 37 (0x25) @@ -104,7 +104,7 @@ public static void Main(string[] args) void Target() { Console.WriteLine(""FAIL""); } void Invoke(D x, D y) { Console.Write(Object.ReferenceEquals(x, y) ? ""FAIL"" : ""PASS""); } }"; - var verifier = CompileAndVerify(source, expectedOutput: PASS, symbolValidator: NoCacheContainers("C")); + var verifier = CompileAndVerify(source, expectedOutput: PASS, symbolValidator: VerifyNoCacheContainersIn("C")); verifier.VerifyIL("C.Main", @" { // Code size 37 (0x25) @@ -146,7 +146,7 @@ static class E public static void Target(this C that) { Console.WriteLine(""FAIL""); } } "; - var verifier = CompileAndVerify(source, expectedOutput: PASS, symbolValidator: NoCacheContainers("C")); + var verifier = CompileAndVerify(source, expectedOutput: PASS, symbolValidator: VerifyNoCacheContainersIn("C")); verifier.VerifyIL("C.Main", @" { // Code size 37 (0x25) @@ -188,7 +188,7 @@ static class E public static void Target(this C that) { Console.WriteLine(""FAIL""); } } "; - var verifier = CompileAndVerify(source, expectedOutput: PASS, symbolValidator: NoCacheContainers("C")); + var verifier = CompileAndVerify(source, expectedOutput: PASS, symbolValidator: VerifyNoCacheContainersIn("C")); verifier.VerifyIL("C.Main", @" { // Code size 37 (0x25) @@ -230,7 +230,7 @@ static class E public static void Target(this C that) { Console.WriteLine(""FAIL""); } } "; - var verifier = CompileAndVerify(source, expectedOutput: PASS, symbolValidator: NoCacheContainers("C")); + var verifier = CompileAndVerify(source, expectedOutput: PASS, symbolValidator: VerifyNoCacheContainersIn("C")); verifier.VerifyIL("C.Main", @" { // Code size 35 (0x23) @@ -271,7 +271,7 @@ public static void Main(string[] args) #else , expectedOutput: "x => Convert(Int32 Target(Int32).CreateDelegate(System.Func`2[System.Int32,System.Int32], null), Func`2)" #endif - , symbolValidator: NoCacheContainers("C")); + , symbolValidator: VerifyNoCacheContainersIn("C")); verifier.VerifyIL("C.Main", @" { // Code size 160 (0xa0) @@ -348,7 +348,7 @@ public static void Main(string[] args) #else , expectedOutput: "y => Convert(Int32 Target(Int32).CreateDelegate(System.Func`2[System.Int32,System.Int32], null), Func`2)" #endif - , symbolValidator: NoCacheContainers("C")); + , symbolValidator: VerifyNoCacheContainersIn("C")); verifier.VerifyIL("C.<>c.
b__0_0", @" { // Code size 155 (0x9b) @@ -412,7 +412,7 @@ class C static void Target() { } } "; - var verifier = CompileAndVerify(source, symbolValidator: NoCacheContainers("C")); + var verifier = CompileAndVerify(source, symbolValidator: VerifyNoCacheContainersIn("C")); verifier.VerifyIL("C..cctor", @" { // Code size 18 (0x12) @@ -442,7 +442,7 @@ static C() } } "; - var verifier = CompileAndVerify(source, symbolValidator: NoCacheContainers("C")); + var verifier = CompileAndVerify(source, symbolValidator: VerifyNoCacheContainersIn("C")); verifier.VerifyIL("C..cctor", @" { // Code size 18 (0x12) @@ -467,7 +467,7 @@ public void Not_TargetTypedNew0() static void Target() { Console.WriteLine(""PASS""); } "; - var verifier = CompileAndVerify(source, expectedOutput: PASS, symbolValidator: NoCacheContainers("Program")); + var verifier = CompileAndVerify(source, expectedOutput: PASS, symbolValidator: VerifyNoCacheContainersIn("Program")); verifier.VerifyIL("", @" { // Code size 18 (0x12) @@ -493,7 +493,7 @@ public void Not_TargetTypedNew1() static void Target() { Console.WriteLine(""PASS""); } "; - var verifier = CompileAndVerify(source, expectedOutput: PASS, symbolValidator: NoCacheContainers("Program")); + var verifier = CompileAndVerify(source, expectedOutput: PASS, symbolValidator: VerifyNoCacheContainersIn("Program")); verifier.VerifyIL("", @" { // Code size 18 (0x12) @@ -2818,7 +2818,7 @@ class D public static void Target() { } } "; - var verifier = CompileAndVerify(source, symbolValidator: CacheContainer("C.<>O", arity: 0 + var verifier = CompileAndVerify(source, symbolValidator: VerifyCacheContainer("C.<>O", arity: 0 , "System.Action <0>__Target" )); verifier.VerifyIL("C.Test0", @" @@ -2861,7 +2861,7 @@ class C static V Target() { return default(V); } } "; - var verifier = CompileAndVerify(source, symbolValidator: CacheContainer("C.<>O", arity: 0 + var verifier = CompileAndVerify(source, symbolValidator: VerifyCacheContainer("C.<>O", arity: 0 , "System.Func <0>__Target" )); verifier.VerifyIL("C.Test0", @" @@ -2905,7 +2905,7 @@ class C static T Target() { return default(T); } } "; - var verifier = CompileAndVerify(source, symbolValidator: CacheContainer("C.<>O", arity: 0 + var verifier = CompileAndVerify(source, symbolValidator: VerifyCacheContainer("C.<>O", arity: 0 , "C.MyFunc <0>__Target" )); verifier.VerifyIL("C.Test0", @" @@ -2954,7 +2954,7 @@ class D public static void Target() { } } "; - var verifier = CompileAndVerify(source, symbolValidator: CacheContainer("C.O__0_0", arity: 1 + var verifier = CompileAndVerify(source, symbolValidator: VerifyCacheContainer("C.O__0_0", arity: 1 , "System.Action <0>__Target" )); verifier.VerifyIL("C.Test", @" @@ -2996,7 +2996,7 @@ class D public static B Target(H h) => default(B); } "; - var verifier = CompileAndVerify(source, symbolValidator: CacheContainer("C.O__0_0", arity: 1 + var verifier = CompileAndVerify(source, symbolValidator: VerifyCacheContainer("C.O__0_0", arity: 1 , "System.Func <0>__Target" )); verifier.VerifyIL("C.Test", @" @@ -3040,7 +3040,7 @@ static class D public static B Target(this int num) => default(B); } "; - var verifier = CompileAndVerify(source, symbolValidator: CacheContainer("C.O__2_0", arity: 1 + var verifier = CompileAndVerify(source, symbolValidator: VerifyCacheContainer("C.O__2_0", arity: 1 , "C.MyFunc <0>__Target" )); verifier.VerifyIL("C.Test", @" @@ -3130,7 +3130,7 @@ static class E public static void Target5(this N n) { } } "; - var verifier = CompileAndVerify(source, symbolValidator: CacheContainer("A.B.<>O", arity: 0 + var verifier = CompileAndVerify(source, symbolValidator: VerifyCacheContainer("A.B.<>O", arity: 0 , "System.Action <0>__Target0" , "System.Action <1>__Target1" , "System.Action <2>__Target2" @@ -3283,7 +3283,7 @@ static class E public static void Target3(this C c) { } } "; - var verifier = CompileAndVerify(source, symbolValidator: CacheContainer("C.O__0_0", arity: 1 + var verifier = CompileAndVerify(source, symbolValidator: VerifyCacheContainer("C.O__0_0", arity: 1 , "System.Action <0>__Target0" , "System.Action <1>__Target1" , "System.Action <2>__Target2" @@ -3350,7 +3350,7 @@ static void Test() public static void Target3(this C c) { } } "; - var verifier = CompileAndVerify(source, symbolValidator: CacheContainer("E.O__0_0", arity: 1 + var verifier = CompileAndVerify(source, symbolValidator: VerifyCacheContainer("E.O__0_0", arity: 1 , "System.Action <0>__Target0" , "System.Action <1>__Target1" , "System.Action <2>__Target2" @@ -3424,7 +3424,7 @@ void LF5() } } "; - var verifier = CompileAndVerify(source, symbolValidator: CacheContainer("E.O__0_0", arity: 1 + var verifier = CompileAndVerify(source, symbolValidator: VerifyCacheContainer("E.O__0_0", arity: 1 , "System.Action <0>__Target" )); verifier.VerifyIL("E.g__LF3|0_1", @" @@ -3505,7 +3505,7 @@ void LF3() static void Main(string[] args) { Test(); } } "; - var verifier = CompileAndVerify(source, expectedOutput: PASS, symbolValidator: CacheContainer("E.O__0_0", arity: 2 + var verifier = CompileAndVerify(source, expectedOutput: PASS, symbolValidator: VerifyCacheContainer("E.O__0_0", arity: 2 , "System.Action <0>__LF2" , "System.Action <1>__LF2" )); @@ -3564,8 +3564,8 @@ static void Target() { } "; var verifier = CompileAndVerify(source, symbolValidator: static module => { - CacheContainer("Program.O__0_0", arity: 1, "System.Action <0>__Target")(module); - CacheContainer("Program.O__0_1", arity: 1, "System.Action <0>__Target")(module); + VerifyCacheContainer("Program.O__0_0", arity: 1, "System.Action <0>__Target")(module); + VerifyCacheContainer("Program.O__0_1", arity: 1, "System.Action <0>__Target")(module); }); verifier.VerifyIL("Program.<
$>g__Owner|0_0", @" { @@ -3614,8 +3614,8 @@ static void Target() { } "; var verifier = CompileAndVerify(source, symbolValidator: static module => { - CacheContainer("C.O__0_0", arity: 1, "System.Action <0>__Target")(module); - CacheContainer("C.O__1_0", arity: 1, "System.Action <0>__Target")(module); + VerifyCacheContainer("C.O__0_0", arity: 1, "System.Action <0>__Target")(module); + VerifyCacheContainer("C.O__1_0", arity: 1, "System.Action <0>__Target")(module); }); verifier.VerifyIL("C.Owner(int)", @" { @@ -3667,8 +3667,8 @@ static void Target() { } "; var verifier = CompileAndVerify(source, symbolValidator: static module => { - CacheContainer("C.O__0_0", arity: 1, "System.Action <0>__Target")(module); - CacheContainer("C.O__1_0", arity: 1, "System.Action <0>__Target")(module); + VerifyCacheContainer("C.O__0_0", arity: 1, "System.Action <0>__Target")(module); + VerifyCacheContainer("C.O__1_0", arity: 1, "System.Action <0>__Target")(module); }); verifier.VerifyIL("C.Owner(int)", @" { @@ -3713,7 +3713,7 @@ void F1() static void Target() { } } "; - var verifier = CompileAndVerify(source, symbolValidator: CacheContainer("Program.<>O", arity: 0 + var verifier = CompileAndVerify(source, symbolValidator: VerifyCacheContainer("Program.<>O", arity: 0 , "System.Action <0>__Target" , "System.Action <1>__Target" )); @@ -3769,7 +3769,7 @@ static void Target() { } } } "; - var verifier = CompileAndVerify(source, symbolValidator: CacheContainer("C.O__0_0", arity: 1 + var verifier = CompileAndVerify(source, symbolValidator: VerifyCacheContainer("C.O__0_0", arity: 1 , "System.Action <0>__Target" , "System.Action <1>__Target" )); @@ -3827,7 +3827,7 @@ public static void Target(int i) { } public static void Target(string i) { } } "; - var verifier = CompileAndVerify(source, symbolValidator: CacheContainer("C.O__0_0", arity: 1 + var verifier = CompileAndVerify(source, symbolValidator: VerifyCacheContainer("C.O__0_0", arity: 1 , "System.Action <0>__Target" , "System.Action <1>__Target" )); @@ -3877,7 +3877,7 @@ static void Target() { } } } "; - var verifier = CompileAndVerify(source, symbolValidator: CacheContainer("C.<>O", arity: 0 + var verifier = CompileAndVerify(source, symbolValidator: VerifyCacheContainer("C.<>O", arity: 0 , "System.Action <0>__Target" , "System.Action <1>__Target" )); @@ -6059,7 +6059,7 @@ .locals init (C1.F V_0) //x "); } - private static Action CacheContainer(string typeName, int arity, params string[] expectedFields) + private static Action VerifyCacheContainer(string typeName, int arity, params string[] expectedFields) { return module => { @@ -6072,7 +6072,7 @@ private static Action CacheContainer(string typeName, int arity, p }; } - private static Action NoCacheContainers(string containingTypeName) + private static Action VerifyNoCacheContainersIn(string containingTypeName) { return module => { From ffe1c99d2d0a26845920809ac0ef22afd80d5ab1 Mon Sep 17 00:00:00 2001 From: Paul Chen Date: Tue, 11 Jan 2022 09:45:32 +0800 Subject: [PATCH 57/66] Feedback: Move EnC tests from Emit2 to Emit. --- .../EditAndContinue/EditAndContinueDelegateCacheTests.cs | 0 .../Microsoft.CodeAnalysis.CSharp.Emit2.UnitTests.csproj | 6 ------ 2 files changed, 6 deletions(-) rename src/Compilers/CSharp/Test/{Emit2 => Emit}/Emit/EditAndContinue/EditAndContinueDelegateCacheTests.cs (100%) diff --git a/src/Compilers/CSharp/Test/Emit2/Emit/EditAndContinue/EditAndContinueDelegateCacheTests.cs b/src/Compilers/CSharp/Test/Emit/Emit/EditAndContinue/EditAndContinueDelegateCacheTests.cs similarity index 100% rename from src/Compilers/CSharp/Test/Emit2/Emit/EditAndContinue/EditAndContinueDelegateCacheTests.cs rename to src/Compilers/CSharp/Test/Emit/Emit/EditAndContinue/EditAndContinueDelegateCacheTests.cs diff --git a/src/Compilers/CSharp/Test/Emit2/Microsoft.CodeAnalysis.CSharp.Emit2.UnitTests.csproj b/src/Compilers/CSharp/Test/Emit2/Microsoft.CodeAnalysis.CSharp.Emit2.UnitTests.csproj index 2396636716797..238c253b9cb1c 100644 --- a/src/Compilers/CSharp/Test/Emit2/Microsoft.CodeAnalysis.CSharp.Emit2.UnitTests.csproj +++ b/src/Compilers/CSharp/Test/Emit2/Microsoft.CodeAnalysis.CSharp.Emit2.UnitTests.csproj @@ -17,12 +17,6 @@ - - Emit\EditAndContinue\EditAndContinueTestBase.cs - - - Emit\EditAndContinue\SemanticEditDescription.cs - Emit\MvidReader.cs From c0f729cffb76eb8a9ec06e8d30ebe9d7fad74179 Mon Sep 17 00:00:00 2001 From: Paul Chen Date: Tue, 11 Jan 2022 12:44:25 +0800 Subject: [PATCH 58/66] Feedback: Only compare CLR signature for the cache fields. --- .../LocalRewriter/DelegateCacheContainer.cs | 21 ++- ...odeGenMethodGroupConversionCachingTests.cs | 129 ++++++++++++++++++ 2 files changed, 149 insertions(+), 1 deletion(-) diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/DelegateCacheContainer.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/DelegateCacheContainer.cs index 42eec060ee9fb..79244083511d4 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/DelegateCacheContainer.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/DelegateCacheContainer.cs @@ -15,7 +15,7 @@ internal sealed class DelegateCacheContainer : SynthesizedContainer { private readonly Symbol _containingSymbol; private readonly NamedTypeSymbol? _constructedContainer; - private readonly Dictionary<(TypeSymbol, MethodSymbol), FieldSymbol> _delegateFields = new(); + private readonly Dictionary<(TypeSymbol, MethodSymbol), FieldSymbol> _delegateFields = new(ConversionCLRSignatureComparer.Instance); /// Creates a type scoped concrete delegate cache container. internal DelegateCacheContainer(TypeSymbol containingType, int generationOrdinal) @@ -77,4 +77,23 @@ internal FieldSymbol GetOrAddCacheField(SyntheticBoundNodeFactory factory, TypeS return field; } + + private sealed class ConversionCLRSignatureComparer : IEqualityComparer<(TypeSymbol delegateType, MethodSymbol targetMethod)> + { + public static readonly ConversionCLRSignatureComparer Instance = new(); + + public bool Equals((TypeSymbol delegateType, MethodSymbol targetMethod) x, (TypeSymbol delegateType, MethodSymbol targetMethod) y) + { + var symbolComparer = SymbolEqualityComparer.CLRSignature; + + return symbolComparer.Equals(x.delegateType, y.delegateType) && symbolComparer.Equals(x.targetMethod, y.targetMethod); + } + + public int GetHashCode((TypeSymbol delegateType, MethodSymbol targetMethod) conversion) + { + var symbolComparer = SymbolEqualityComparer.CLRSignature; + + return Hash.Combine(symbolComparer.GetHashCode(conversion.delegateType), symbolComparer.GetHashCode(conversion.targetMethod)); + } + } } diff --git a/src/Compilers/CSharp/Test/Emit2/CodeGen/CodeGenMethodGroupConversionCachingTests.cs b/src/Compilers/CSharp/Test/Emit2/CodeGen/CodeGenMethodGroupConversionCachingTests.cs index 3152b0a4adeb6..db7be3b4b32f6 100644 --- a/src/Compilers/CSharp/Test/Emit2/CodeGen/CodeGenMethodGroupConversionCachingTests.cs +++ b/src/Compilers/CSharp/Test/Emit2/CodeGen/CodeGenMethodGroupConversionCachingTests.cs @@ -2936,6 +2936,93 @@ .maxstack 2 "); } + [Fact] + public void SameTypeAndSymbolResultsSameField_TypeScoped3_CLRSignature() + { + var source = @" +#nullable enable +using System; +class C +{ + void Test0() { var t = (Func)Target; } + void Test1() { Func t = Target; } + static V Target() { return default(V); } +} +"; + var verifier = CompileAndVerify(source, symbolValidator: VerifyCacheContainer("C.<>O", arity: 0 + , "System.Func <0>__Target" + )); + verifier.VerifyIL("C.Test0", @" +{ + // Code size 25 (0x19) + .maxstack 2 + IL_0000: ldsfld ""System.Func C.<>O.<0>__Target"" + IL_0005: brtrue.s IL_0018 + IL_0007: ldnull + IL_0008: ldftn ""T C.Target()"" + IL_000e: newobj ""System.Func..ctor(object, System.IntPtr)"" + IL_0013: stsfld ""System.Func C.<>O.<0>__Target"" + IL_0018: ret +} +"); + verifier.VerifyIL("C.Test1", @" +{ + // Code size 25 (0x19) + .maxstack 2 + IL_0000: ldsfld ""System.Func C.<>O.<0>__Target"" + IL_0005: brtrue.s IL_0018 + IL_0007: ldnull + IL_0008: ldftn ""T C.Target()"" + IL_000e: newobj ""System.Func..ctor(object, System.IntPtr)"" + IL_0013: stsfld ""System.Func C.<>O.<0>__Target"" + IL_0018: ret +} +"); + } + + [Fact] + public void SameTypeAndSymbolResultsSameField_TypeScoped4_CLRSignature() + { + var source = @" +using System; +class C +{ + void Test0() { var t = (Func<(T x, T y)>)Target<(T x, T y)>; } + void Test1() { Func<(T a, T b)> t = Target<(T c, T d)>; } + static V Target() { return default(V); } +} +"; + var verifier = CompileAndVerify(source, symbolValidator: VerifyCacheContainer("C.<>O", arity: 0 + , "System.Func<(T x, T y)> <0>__Target" + )); + verifier.VerifyIL("C.Test0", @" +{ + // Code size 25 (0x19) + .maxstack 2 + IL_0000: ldsfld ""System.Func> C.<>O.<0>__Target"" + IL_0005: brtrue.s IL_0018 + IL_0007: ldnull + IL_0008: ldftn ""System.ValueTuple C.Target>()"" + IL_000e: newobj ""System.Func>..ctor(object, System.IntPtr)"" + IL_0013: stsfld ""System.Func> C.<>O.<0>__Target"" + IL_0018: ret +} +"); + verifier.VerifyIL("C.Test1", @" +{ + // Code size 25 (0x19) + .maxstack 2 + IL_0000: ldsfld ""System.Func> C.<>O.<0>__Target"" + IL_0005: brtrue.s IL_0018 + IL_0007: ldnull + IL_0008: ldftn ""System.ValueTuple C.Target>()"" + IL_000e: newobj ""System.Func>..ctor(object, System.IntPtr)"" + IL_0013: stsfld ""System.Func> C.<>O.<0>__Target"" + IL_0018: ret +} +"); + } + [Fact] public void SameTypeAndSymbolResultsSameField_MethodScoped0() { @@ -3064,6 +3151,48 @@ .maxstack 2 "); } + [Fact] + public void SameTypeAndSymbolResultsSameField_MethodScoped3_CLRSignature() + { + var source = @" +using System; +class C +{ + void Test() + { + var t0 = (Func)D.Target; + Func t1 = D.Target; + } +} +class D +{ + public static B Target(dynamic o, H h) => default(B); +} +"; + var verifier = CompileAndVerify(source, symbolValidator: VerifyCacheContainer("C.O__0_0", arity: 1 + , "System.Func <0>__Target" + )); + verifier.VerifyIL("C.Test", @" +{ + // Code size 49 (0x31) + .maxstack 2 + IL_0000: ldsfld ""System.Func C.O__0_0.<0>__Target"" + IL_0005: brtrue.s IL_0018 + IL_0007: ldnull + IL_0008: ldftn ""V D.Target(dynamic, T)"" + IL_000e: newobj ""System.Func..ctor(object, System.IntPtr)"" + IL_0013: stsfld ""System.Func C.O__0_0.<0>__Target"" + IL_0018: ldsfld ""System.Func C.O__0_0.<0>__Target"" + IL_001d: brtrue.s IL_0030 + IL_001f: ldnull + IL_0020: ldftn ""V D.Target(dynamic, T)"" + IL_0026: newobj ""System.Func..ctor(object, System.IntPtr)"" + IL_002b: stsfld ""System.Func C.O__0_0.<0>__Target"" + IL_0030: ret +} +"); + } + [Fact] public void ContainersCanBeShared_TypeScoped0() { From 336c95bc142cc8b9e23a039f38ce97d17a2acc63 Mon Sep 17 00:00:00 2001 From: Paul Chen Date: Tue, 11 Jan 2022 15:08:08 +0800 Subject: [PATCH 59/66] Address some feedback. --- .../Lowering/LocalRewriter/DelegateCacheContainer.cs | 4 ++-- .../Portable/Lowering/LocalRewriter/DelegateCacheRewriter.cs | 2 +- .../CSharp/Portable/Symbols/Synthesized/GeneratedNames.cs | 5 +---- 3 files changed, 4 insertions(+), 7 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/DelegateCacheContainer.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/DelegateCacheContainer.cs index 79244083511d4..c5af2604519f9 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/DelegateCacheContainer.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/DelegateCacheContainer.cs @@ -17,7 +17,7 @@ internal sealed class DelegateCacheContainer : SynthesizedContainer private readonly NamedTypeSymbol? _constructedContainer; private readonly Dictionary<(TypeSymbol, MethodSymbol), FieldSymbol> _delegateFields = new(ConversionCLRSignatureComparer.Instance); - /// Creates a type scoped concrete delegate cache container. + /// Creates a type-scope concrete delegate cache container. internal DelegateCacheContainer(TypeSymbol containingType, int generationOrdinal) : base(GeneratedNames.DelegateCacheContainerType(generationOrdinal), containingMethod: null) { @@ -26,7 +26,7 @@ internal DelegateCacheContainer(TypeSymbol containingType, int generationOrdinal _containingSymbol = containingType; } - /// Creates a method scoped generic delegate cache container. + /// Creates a method-scope generic delegate cache container. internal DelegateCacheContainer(MethodSymbol ownerMethod, int topLevelMethodOrdinal, int ownerUniqueId, int generationOrdinal) : base(GeneratedNames.DelegateCacheContainerType(generationOrdinal, ownerMethod.Name, topLevelMethodOrdinal, ownerUniqueId), ownerMethod) { diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/DelegateCacheRewriter.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/DelegateCacheRewriter.cs index df9da43067357..493e0d1b18703 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/DelegateCacheRewriter.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/DelegateCacheRewriter.cs @@ -52,7 +52,7 @@ internal BoundExpression Rewrite(BoundDelegateCreationExpression boundDelegateCr var cacheContainer = GetOrAddCacheContainer(delegateType, targetMethod); var cacheField = cacheContainer.GetOrAddCacheField(_factory, delegateType, targetMethod); - var boundCacheField = _factory.Field(null, cacheField); + var boundCacheField = _factory.Field(receiver: null, cacheField); var rewrittenNode = _factory.Coalesce(boundCacheField, _factory.AssignmentExpression(boundCacheField, boundDelegateCreation)); _factory.Syntax = oldSyntax; diff --git a/src/Compilers/CSharp/Portable/Symbols/Synthesized/GeneratedNames.cs b/src/Compilers/CSharp/Portable/Symbols/Synthesized/GeneratedNames.cs index 0bb350c9c4a76..a50b9ccb9b9f6 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Synthesized/GeneratedNames.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Synthesized/GeneratedNames.cs @@ -458,10 +458,7 @@ internal static string DelegateCacheContainerType(int generation, string? method builder.Append(IdSeparator).Append(ownerUniqueId); } - if (generation > 0) - { - builder.Append(GenerationSeparator).Append(generation); - } + AppendOptionalGeneration(builder, generation); return result.ToStringAndFree(); } From e673d057b07e53bc2a116ad2cd4ef4b1e6778cfa Mon Sep 17 00:00:00 2001 From: Paul Chen Date: Tue, 11 Jan 2022 15:40:08 +0800 Subject: [PATCH 60/66] Feedback: Explicitly target some tests to C# Next. --- .../CodeGenMethodGroupConversionCachingTests.cs | 12 ++++++------ src/Scripting/CSharpTest/ScriptTests.cs | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/Compilers/CSharp/Test/Emit2/CodeGen/CodeGenMethodGroupConversionCachingTests.cs b/src/Compilers/CSharp/Test/Emit2/CodeGen/CodeGenMethodGroupConversionCachingTests.cs index db7be3b4b32f6..ad9f9225d2fbc 100644 --- a/src/Compilers/CSharp/Test/Emit2/CodeGen/CodeGenMethodGroupConversionCachingTests.cs +++ b/src/Compilers/CSharp/Test/Emit2/CodeGen/CodeGenMethodGroupConversionCachingTests.cs @@ -5457,7 +5457,7 @@ static int M() return 0; } }"; - var comp = CompileAndVerify(source); + var comp = CompileAndVerify(source, parseOptions: TestOptions.RegularNext); comp.VerifyDiagnostics(); comp.VerifyIL("C.Main", @" { @@ -5540,7 +5540,7 @@ static void Action() } } "; - var verifier = CompileAndVerifyWithWinRt(source, options: TestOptions.ReleaseWinMD); + var verifier = CompileAndVerifyWithWinRt(source, parseOptions: TestOptions.RegularNext, options: TestOptions.ReleaseWinMD); verifier.VerifyIL("D.InstanceAdd", @" { @@ -5657,7 +5657,7 @@ static void Action() } } "; - var verifier = CompileAndVerifyWithWinRt(source, options: TestOptions.ReleaseWinMD); + var verifier = CompileAndVerifyWithWinRt(source, parseOptions: TestOptions.RegularNext, options: TestOptions.ReleaseWinMD); verifier.VerifyIL("C.InstanceAssign", @" { @@ -5738,7 +5738,7 @@ static partial void PM(string p2) } "; - CompileAndVerify(text, expectedOutput: PASS).VerifyIL("Test.Main", @" + CompileAndVerify(text, parseOptions: TestOptions.RegularNext, expectedOutput: PASS).VerifyIL("Test.Main", @" { // Code size 64 (0x40) .maxstack 2 @@ -5797,7 +5797,7 @@ public static void Main() } static void TestMethod() => Console.WriteLine(""In TestMethod""); } -", expectedOutput: @" +", parseOptions: TestOptions.RegularNext, expectedOutput: @" In TestMethod In TestMethod ").VerifyIL("C.Main()", @" @@ -5862,7 +5862,7 @@ static void Main() static void Report(Delegate d) => Console.WriteLine($""{d.GetType().Namespace}.{d.GetType().Name}""); }"; - var comp = CreateCompilation(source, options: TestOptions.DebugExe); + var comp = CreateCompilation(source, parseOptions: TestOptions.RegularNext, options: TestOptions.DebugExe); comp.VerifyDiagnostics(); var verifier = CompileAndVerify(comp, expectedOutput: diff --git a/src/Scripting/CSharpTest/ScriptTests.cs b/src/Scripting/CSharpTest/ScriptTests.cs index 4163994b9adc1..290d9a4505eb1 100644 --- a/src/Scripting/CSharpTest/ScriptTests.cs +++ b/src/Scripting/CSharpTest/ScriptTests.cs @@ -474,7 +474,7 @@ public async Task TestBranchingSubscripts() [Fact] public async Task StaticDelegate0() { - var state0 = await CSharpScript.RunAsync("static int Add(int x, int y) => x + y;"); + var state0 = await CSharpScript.RunAsync("static int Add(int x, int y) => x + y;", options: ScriptOptions.Default.WithLanguageVersion(LanguageVersion.Preview)); var state1 = await state0.ContinueWithAsync("System.Func adder = Add;"); var state2 = await state1.ContinueWithAsync("adder(1, 1)"); Assert.Equal(2, state2.ReturnValue); From 367933002020c8cc92520b0a829788ceeba5cb9f Mon Sep 17 00:00:00 2001 From: Paul Chen Date: Tue, 11 Jan 2022 21:49:43 +0800 Subject: [PATCH 61/66] Add a test where the target is a local function outside of the current function. --- ...odeGenMethodGroupConversionCachingTests.cs | 36 +++++++++++++++++-- 1 file changed, 33 insertions(+), 3 deletions(-) diff --git a/src/Compilers/CSharp/Test/Emit2/CodeGen/CodeGenMethodGroupConversionCachingTests.cs b/src/Compilers/CSharp/Test/Emit2/CodeGen/CodeGenMethodGroupConversionCachingTests.cs index ad9f9225d2fbc..cda9e8791c245 100644 --- a/src/Compilers/CSharp/Test/Emit2/CodeGen/CodeGenMethodGroupConversionCachingTests.cs +++ b/src/Compilers/CSharp/Test/Emit2/CodeGen/CodeGenMethodGroupConversionCachingTests.cs @@ -4837,7 +4837,7 @@ .maxstack 3 } [Fact] - public void TopLevel_LocalFunctions_TypeScoped0() + public void TopLevelMethod_LocalFunctions_TypeScoped0() { var source = @" using System; @@ -4874,7 +4874,7 @@ .maxstack 2 } [Fact] - public void TopLevel_LocalFunctions_NotStatic() + public void TopLevelMethod_LocalFunctions_NotStatic() { var source = @" using System; @@ -4905,7 +4905,7 @@ .maxstack 2 } [Fact] - public void TopLevel_LocalFunctions_MethodScoped0() + public void TopLevelMethod_LocalFunctions_MethodScoped0() { var source = @" using System; @@ -5320,6 +5320,36 @@ .maxstack 2 "); } + [Fact] + public void TopLevelStatement_Tuples_LocalFunction_MethodScoped3() + { + var source = @" +Test(0); +static void Test(T t) +{ + (System.Func a, int _) = (Target, 0); +} +static (T, G) Target(G g) => (default(T), g); +"; + CompileAndVerify(source).VerifyIL("Program.<
$>g__Test|0_0", @" +{ + // Code size 29 (0x1d) + .maxstack 2 + IL_0000: ldsfld ""System.Func> Program.O__0_0.<0>__Target"" + IL_0005: dup + IL_0006: brtrue.s IL_001b + IL_0008: pop + IL_0009: ldnull + IL_000a: ldftn ""System.ValueTuple Program.<
$>g__Target|0_1(T)"" + IL_0010: newobj ""System.Func>..ctor(object, System.IntPtr)"" + IL_0015: dup + IL_0016: stsfld ""System.Func> Program.O__0_0.<0>__Target"" + IL_001b: pop + IL_001c: ret +} +"); + } + [Fact] public void Tuples_LocalFunction_TypeScoped0() { From af2d17938b57ca0d86f8ef0a18301e9d399d7d9e Mon Sep 17 00:00:00 2001 From: Paul Chen Date: Tue, 11 Jan 2022 22:17:55 +0800 Subject: [PATCH 62/66] Feedback: Add an assert. --- .../Portable/Lowering/LocalRewriter/DelegateCacheRewriter.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/DelegateCacheRewriter.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/DelegateCacheRewriter.cs index 493e0d1b18703..2157151707b62 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/DelegateCacheRewriter.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/DelegateCacheRewriter.cs @@ -150,6 +150,8 @@ private static bool TryGetOwnerFunction(MethodSymbol currentFunction, TypeSymbol return false; } + Debug.Assert(targetMethod.MethodKind == MethodKind.Ordinary); + var usedTypeParameters = PooledHashSet.GetInstance(); try { From ebb945e3aab2ac32467bda213724162af779e313 Mon Sep 17 00:00:00 2001 From: Paul Chen Date: Tue, 11 Jan 2022 22:38:08 +0800 Subject: [PATCH 63/66] Feedback: Rename the custom comparer. --- .../Lowering/LocalRewriter/DelegateCacheContainer.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/DelegateCacheContainer.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/DelegateCacheContainer.cs index c5af2604519f9..21d49a0ec2fdc 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/DelegateCacheContainer.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/DelegateCacheContainer.cs @@ -15,7 +15,7 @@ internal sealed class DelegateCacheContainer : SynthesizedContainer { private readonly Symbol _containingSymbol; private readonly NamedTypeSymbol? _constructedContainer; - private readonly Dictionary<(TypeSymbol, MethodSymbol), FieldSymbol> _delegateFields = new(ConversionCLRSignatureComparer.Instance); + private readonly Dictionary<(TypeSymbol, MethodSymbol), FieldSymbol> _delegateFields = new(CLRSignatureComparer.Instance); /// Creates a type-scope concrete delegate cache container. internal DelegateCacheContainer(TypeSymbol containingType, int generationOrdinal) @@ -78,9 +78,9 @@ internal FieldSymbol GetOrAddCacheField(SyntheticBoundNodeFactory factory, TypeS return field; } - private sealed class ConversionCLRSignatureComparer : IEqualityComparer<(TypeSymbol delegateType, MethodSymbol targetMethod)> + private sealed class CLRSignatureComparer : IEqualityComparer<(TypeSymbol delegateType, MethodSymbol targetMethod)> { - public static readonly ConversionCLRSignatureComparer Instance = new(); + public static readonly CLRSignatureComparer Instance = new(); public bool Equals((TypeSymbol delegateType, MethodSymbol targetMethod) x, (TypeSymbol delegateType, MethodSymbol targetMethod) y) { From aaa2621b8a5528853375bca6a0ffcd1d9b92da30 Mon Sep 17 00:00:00 2001 From: Paul Chen Date: Tue, 11 Jan 2022 22:56:06 +0800 Subject: [PATCH 64/66] Feedback: Use ReferenceEqualityComparer. --- .../Portable/Lowering/LocalRewriter/DelegateCacheRewriter.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/DelegateCacheRewriter.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/DelegateCacheRewriter.cs index 2157151707b62..b1ff4cd96d531 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/DelegateCacheRewriter.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/DelegateCacheRewriter.cs @@ -9,6 +9,7 @@ using System.Diagnostics.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp.Symbols; using Microsoft.CodeAnalysis.PooledObjects; +using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CSharp; @@ -106,7 +107,7 @@ private DelegateCacheContainer GetOrAddCacheContainer(TypeSymbol delegateType, M } else { - var containers = _genericCacheContainers ??= new Dictionary(); + var containers = _genericCacheContainers ??= new Dictionary(ReferenceEqualityComparer.Instance); if (containers.TryGetValue(ownerFunction, out container)) { From 28ffe2776d9c0146e6b5e021f170768229b206f3 Mon Sep 17 00:00:00 2001 From: Paul Chen Date: Wed, 12 Jan 2022 00:22:25 +0800 Subject: [PATCH 65/66] Feedback: Add a test for C# 10. Add tests for cache field sharing the same anonymous delegate. --- ...odeGenMethodGroupConversionCachingTests.cs | 130 ++++++++++++++++++ 1 file changed, 130 insertions(+) diff --git a/src/Compilers/CSharp/Test/Emit2/CodeGen/CodeGenMethodGroupConversionCachingTests.cs b/src/Compilers/CSharp/Test/Emit2/CodeGen/CodeGenMethodGroupConversionCachingTests.cs index cda9e8791c245..50cc12f67f61a 100644 --- a/src/Compilers/CSharp/Test/Emit2/CodeGen/CodeGenMethodGroupConversionCachingTests.cs +++ b/src/Compilers/CSharp/Test/Emit2/CodeGen/CodeGenMethodGroupConversionCachingTests.cs @@ -507,6 +507,28 @@ .maxstack 2 "); } + [Fact] + public void Not_CSharp10() + { + var source = @" +var f = Target; +f(); +static void Target() { } +"; + var verifier = CompileAndVerify(source, parseOptions: TestOptions.Regular10, symbolValidator: VerifyNoCacheContainersIn("Program")); + verifier.VerifyIL("", @" +{ + // Code size 18 (0x12) + .maxstack 2 + IL_0000: ldnull + IL_0001: ldftn ""void Program.<
$>g__Target|0_0()"" + IL_0007: newobj ""System.Action..ctor(object, System.IntPtr)"" + IL_000c: callvirt ""void System.Action.Invoke()"" + IL_0011: ret +} +"); + } + [Fact] public void CacheExplicitConversions_TypeScoped_CouldBeModuleScoped0() { @@ -3023,6 +3045,62 @@ .maxstack 2 "); } + [Fact] + public void SameTypeAndSymbolResultsSameField_TypeScoped5_AnonymousDelegate() + { + var source = @" +class C +{ + void Test0(T t) { G0(Target); } + void Test1(T t) { G1(Target); } + + void G0(System.Delegate d) { } + void G1(System.Delegate d) { } + + static dynamic Target(ref G g) => 0; +} +"; + var verifier = CompileAndVerify(source, symbolValidator: VerifyCacheContainer("C.<>O", arity: 0 + , "<>F{00000001} <0>__Target" + )); + verifier.VerifyIL("C.Test0", @" +{ + // Code size 34 (0x22) + .maxstack 3 + IL_0000: ldarg.0 + IL_0001: ldsfld "" C.<>O.<0>__Target"" + IL_0006: dup + IL_0007: brtrue.s IL_001c + IL_0009: pop + IL_000a: ldnull + IL_000b: ldftn ""dynamic C.Target(ref int)"" + IL_0011: newobj ""<>F{00000001}..ctor(object, System.IntPtr)"" + IL_0016: dup + IL_0017: stsfld "" C.<>O.<0>__Target"" + IL_001c: call ""void C.G0(System.Delegate)"" + IL_0021: ret +} +"); + verifier.VerifyIL("C.Test1", @" +{ + // Code size 34 (0x22) + .maxstack 3 + IL_0000: ldarg.0 + IL_0001: ldsfld "" C.<>O.<0>__Target"" + IL_0006: dup + IL_0007: brtrue.s IL_001c + IL_0009: pop + IL_000a: ldnull + IL_000b: ldftn ""dynamic C.Target(ref int)"" + IL_0011: newobj ""<>F{00000001}..ctor(object, System.IntPtr)"" + IL_0016: dup + IL_0017: stsfld "" C.<>O.<0>__Target"" + IL_001c: call ""void C.G1(System.Delegate)"" + IL_0021: ret +} +"); + } + [Fact] public void SameTypeAndSymbolResultsSameField_MethodScoped0() { @@ -3193,6 +3271,58 @@ .maxstack 2 "); } + [Fact] + public void SameTypeAndSymbolResultsSameField_MethodScoped4_AnonymousDelegate() + { + var source = @" +class C +{ + void Test(T t) + { + G0(Target); + G1(Target); + } + + void G0(System.Delegate d) { } + void G1(System.Delegate d) { } + + static dynamic Target(ref G g) => 0; +} +"; + var verifier = CompileAndVerify(source, symbolValidator: VerifyCacheContainer("C.O__0_0", arity: 1 + , "<>F{00000001} <0>__Target" + )); + verifier.VerifyIL("C.Test", @" +{ + // Code size 67 (0x43) + .maxstack 3 + IL_0000: ldarg.0 + IL_0001: ldsfld "" C.O__0_0.<0>__Target"" + IL_0006: dup + IL_0007: brtrue.s IL_001c + IL_0009: pop + IL_000a: ldnull + IL_000b: ldftn ""dynamic C.Target(ref T)"" + IL_0011: newobj ""<>F{00000001}..ctor(object, System.IntPtr)"" + IL_0016: dup + IL_0017: stsfld "" C.O__0_0.<0>__Target"" + IL_001c: call ""void C.G0(System.Delegate)"" + IL_0021: ldarg.0 + IL_0022: ldsfld "" C.O__0_0.<0>__Target"" + IL_0027: dup + IL_0028: brtrue.s IL_003d + IL_002a: pop + IL_002b: ldnull + IL_002c: ldftn ""dynamic C.Target(ref T)"" + IL_0032: newobj ""<>F{00000001}..ctor(object, System.IntPtr)"" + IL_0037: dup + IL_0038: stsfld "" C.O__0_0.<0>__Target"" + IL_003d: call ""void C.G1(System.Delegate)"" + IL_0042: ret +} +"); + } + [Fact] public void ContainersCanBeShared_TypeScoped0() { From 84551f100f73d0e4e6b3f78a7fdb8a08b8cbe9aa Mon Sep 17 00:00:00 2001 From: Paul Chen Date: Wed, 12 Jan 2022 09:23:38 +0800 Subject: [PATCH 66/66] Address feedback. --- .../Lowering/LocalRewriter/DelegateCacheRewriter.cs | 10 ++++++++-- .../Portable/Lowering/LocalRewriter/LocalRewriter.cs | 2 +- .../Lowering/LocalRewriter/LocalRewriter_Conversion.cs | 4 ++-- 3 files changed, 11 insertions(+), 5 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/DelegateCacheRewriter.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/DelegateCacheRewriter.cs index b1ff4cd96d531..a4c8eb97dad58 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/DelegateCacheRewriter.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/DelegateCacheRewriter.cs @@ -16,14 +16,14 @@ namespace Microsoft.CodeAnalysis.CSharp; /// /// This type helps rewrite the delegate creations that target static method groups to use a cached instance of delegate. /// -internal sealed partial class DelegateCreationRewriter +internal sealed class DelegateCacheRewriter { private readonly SyntheticBoundNodeFactory _factory; private readonly int _topLevelMethodOrdinal; private Dictionary? _genericCacheContainers; - internal DelegateCreationRewriter(SyntheticBoundNodeFactory factory, int topLevelMethodOrdinal) + internal DelegateCacheRewriter(SyntheticBoundNodeFactory factory, int topLevelMethodOrdinal) { Debug.Assert(factory.TopLevelMethod is { }); @@ -151,6 +151,12 @@ private static bool TryGetOwnerFunction(MethodSymbol currentFunction, TypeSymbol return false; } + // @AlekseyTs: It is Ok to create delegates for other method kinds as well. + // @jcouv: We'd likely want to pay attention to this code if this happens. + // What we really cared above was, + // - "Are there any type parameters from the target method that we cannot discover simply from it's signature?" + // As of C# 10, we only observe local functions could potentially answer yes, so we used that. + // If this is hit, feel free to change but please also add tests. Debug.Assert(targetMethod.MethodKind == MethodKind.Ordinary); var usedTypeParameters = PooledHashSet.GetInstance(); diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter.cs index f90a04318e97b..d39ee387058da 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter.cs @@ -28,7 +28,7 @@ internal sealed partial class LocalRewriter : BoundTreeRewriterWithStackGuard private bool _sawLambdas; private int _availableLocalFunctionOrdinal; private readonly int _topLevelMethodOrdinal; - private DelegateCreationRewriter? _lazyDelegateCacheRewriter; + private DelegateCacheRewriter? _lazyDelegateCacheRewriter; private bool _inExpressionLambda; private bool _sawAwait; diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_Conversion.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_Conversion.cs index 1cf95c97c81a9..e088ad6227c04 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_Conversion.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_Conversion.cs @@ -434,9 +434,9 @@ private BoundExpression MakeConversionNodeCore( if (_factory.Compilation.LanguageVersion >= MessageID.IDS_FeatureCacheStaticMethodGroupConversion.RequiredVersion() && !_inExpressionLambda // The tree structure / meaning for expression trees should remain untouched. && _factory.TopLevelMethod.MethodKind != MethodKind.StaticConstructor // Avoid caching twice if people do it manually. - && DelegateCreationRewriter.CanRewrite(boundDelegateCreation)) + && DelegateCacheRewriter.CanRewrite(boundDelegateCreation)) { - var rewriter = _lazyDelegateCacheRewriter ??= new DelegateCreationRewriter(_factory, _topLevelMethodOrdinal); + var rewriter = _lazyDelegateCacheRewriter ??= new DelegateCacheRewriter(_factory, _topLevelMethodOrdinal); return rewriter.Rewrite(boundDelegateCreation); } else