From fcb71555db5ebf6552ac79ad8a75efb9f9fe6098 Mon Sep 17 00:00:00 2001 From: Jan Jones Date: Tue, 27 Aug 2024 15:18:10 +0200 Subject: [PATCH] Avoid synthesizing incorrect `allows ref struct` delegate type parameters --- .../AnonymousType.DelegateTemplateSymbol.cs | 17 +- .../AnonymousType.TemplateSymbol.cs | 2 +- .../AnonymousType.TypeParameterSymbol.cs | 12 +- .../Test/Emit3/RefStructInterfacesTests.cs | 402 ++++++++++++++++++ 4 files changed, 418 insertions(+), 15 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Symbols/AnonymousTypes/SynthesizedSymbols/AnonymousType.DelegateTemplateSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/AnonymousTypes/SynthesizedSymbols/AnonymousType.DelegateTemplateSymbol.cs index c38b16243af25..b7749ddcd7867 100644 --- a/src/Compilers/CSharp/Portable/Symbols/AnonymousTypes/SynthesizedSymbols/AnonymousType.DelegateTemplateSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/AnonymousTypes/SynthesizedSymbols/AnonymousType.DelegateTemplateSymbol.cs @@ -40,7 +40,7 @@ internal AnonymousDelegateTemplateSymbol( Debug.Assert(refKinds.IsNull || parameterCount == refKinds.Capacity - (voidReturnTypeOpt is { } ? 0 : 1)); HasIndexedName = false; - TypeParameters = CreateTypeParameters(this, parameterCount, returnsVoid: voidReturnTypeOpt is { }); + TypeParameters = CreateTypeParameters(this, parameterCount, returnsVoid: voidReturnTypeOpt is { }, hasParamsArray: false); NameAndIndex = new NameAndIndex(name, index: 0); var constructor = new SynthesizedDelegateConstructor(this, objectType, intPtrType); @@ -70,17 +70,20 @@ static SynthesizedDelegateInvokeMethod createInvokeMethod(AnonymousDelegateTempl } } - private static ImmutableArray CreateTypeParameters(AnonymousDelegateTemplateSymbol containingType, int parameterCount, bool returnsVoid) + private static ImmutableArray CreateTypeParameters(AnonymousDelegateTemplateSymbol containingType, int parameterCount, bool returnsVoid, bool hasParamsArray) { + var allowRefLikeTypes = containingType.ContainingAssembly.RuntimeSupportsByRefLikeGenerics; + var typeParameters = ArrayBuilder.GetInstance(parameterCount + (returnsVoid ? 0 : 1)); for (int i = 0; i < parameterCount; i++) { - typeParameters.Add(new AnonymousTypeManager.AnonymousTypeParameterSymbol(containingType, i, "T" + (i + 1))); + typeParameters.Add(new AnonymousTypeManager.AnonymousTypeParameterSymbol(containingType, i, "T" + (i + 1), + allowsRefLikeType: allowRefLikeTypes && (!hasParamsArray || i != parameterCount - 1))); } if (!returnsVoid) { - typeParameters.Add(new AnonymousTypeManager.AnonymousTypeParameterSymbol(containingType, parameterCount, "TResult")); + typeParameters.Add(new AnonymousTypeManager.AnonymousTypeParameterSymbol(containingType, parameterCount, "TResult", allowsRefLikeType: allowRefLikeTypes)); } return typeParameters.ToImmutableAndFree(); @@ -103,7 +106,8 @@ internal AnonymousDelegateTemplateSymbol(AnonymousTypeManager manager, Anonymous TypeParameters = CreateTypeParameters( this, parameterCount: typeDescr.Fields.Length - 1, - returnsVoid: typeDescr.Fields[^1].Type.IsVoidType()); + returnsVoid: typeDescr.Fields[^1].Type.IsVoidType(), + hasParamsArray: typeDescr.Fields is [.., { IsParams: true }, _]); var constructor = new SynthesizedDelegateConstructor(this, manager.System_Object, manager.System_IntPtr); // https://github.com/dotnet/roslyn/issues/56808: Synthesized delegates should include BeginInvoke() and EndInvoke(). @@ -172,7 +176,8 @@ internal AnonymousDelegateTemplateSymbol(AnonymousTypeManager manager, Anonymous var typeParameters = ArrayBuilder.GetInstance(typeParameterCount); for (int i = 0; i < typeParameterCount; i++) { - typeParameters.Add(new AnonymousTypeParameterSymbol(this, i, "T" + (i + 1))); + typeParameters.Add(new AnonymousTypeParameterSymbol(this, i, "T" + (i + 1), + allowsRefLikeType: typeParametersToSubstitute[i].AllowsRefLikeType)); } TypeParameters = typeParameters.ToImmutableAndFree(); typeMap = new TypeMap(typeParametersToSubstitute, TypeParameters, allowAlpha: true); diff --git a/src/Compilers/CSharp/Portable/Symbols/AnonymousTypes/SynthesizedSymbols/AnonymousType.TemplateSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/AnonymousTypes/SynthesizedSymbols/AnonymousType.TemplateSymbol.cs index 5c3dce114585a..ce0f158b7a4a4 100644 --- a/src/Compilers/CSharp/Portable/Symbols/AnonymousTypes/SynthesizedSymbols/AnonymousType.TemplateSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/AnonymousTypes/SynthesizedSymbols/AnonymousType.TemplateSymbol.cs @@ -56,7 +56,7 @@ internal AnonymousTypeTemplateSymbol(AnonymousTypeManager manager, AnonymousType // Add a type parameter AnonymousTypeParameterSymbol typeParameter = - new AnonymousTypeParameterSymbol(this, fieldIndex, GeneratedNames.MakeAnonymousTypeParameterName(field.Name)); + new AnonymousTypeParameterSymbol(this, fieldIndex, GeneratedNames.MakeAnonymousTypeParameterName(field.Name), allowsRefLikeType: false); typeParametersBuilder.Add(typeParameter); // Add a property diff --git a/src/Compilers/CSharp/Portable/Symbols/AnonymousTypes/SynthesizedSymbols/AnonymousType.TypeParameterSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/AnonymousTypes/SynthesizedSymbols/AnonymousType.TypeParameterSymbol.cs index b7a14cc207bbf..3f3e9f125b2d0 100644 --- a/src/Compilers/CSharp/Portable/Symbols/AnonymousTypes/SynthesizedSymbols/AnonymousType.TypeParameterSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/AnonymousTypes/SynthesizedSymbols/AnonymousType.TypeParameterSymbol.cs @@ -20,8 +20,9 @@ internal sealed class AnonymousTypeParameterSymbol : TypeParameterSymbol private readonly AnonymousTypeOrDelegateTemplateSymbol _container; private readonly int _ordinal; private readonly string _name; + private readonly bool _allowsRefLikeType; - public AnonymousTypeParameterSymbol(AnonymousTypeOrDelegateTemplateSymbol container, int ordinal, string name) + public AnonymousTypeParameterSymbol(AnonymousTypeOrDelegateTemplateSymbol container, int ordinal, string name, bool allowsRefLikeType) { Debug.Assert((object)container != null); Debug.Assert(!string.IsNullOrEmpty(name)); @@ -29,6 +30,7 @@ public AnonymousTypeParameterSymbol(AnonymousTypeOrDelegateTemplateSymbol contai _container = container; _ordinal = ordinal; _name = name; + _allowsRefLikeType = allowsRefLikeType; } public override TypeParameterKind TypeParameterKind @@ -91,13 +93,7 @@ public override bool HasValueTypeConstraint get { return false; } } - public override bool AllowsRefLikeType - { - get - { - return _container.IsDelegateType() && ContainingAssembly.RuntimeSupportsByRefLikeGenerics; - } - } + public override bool AllowsRefLikeType => _allowsRefLikeType; public override bool IsValueTypeFromConstraintTypes { diff --git a/src/Compilers/CSharp/Test/Emit3/RefStructInterfacesTests.cs b/src/Compilers/CSharp/Test/Emit3/RefStructInterfacesTests.cs index 95d967b8755d4..0c993511a7d1e 100644 --- a/src/Compilers/CSharp/Test/Emit3/RefStructInterfacesTests.cs +++ b/src/Compilers/CSharp/Test/Emit3/RefStructInterfacesTests.cs @@ -22788,6 +22788,408 @@ namespace System ).VerifyDiagnostics(); } + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/74823")] + public void AnonymousDelegateType_09_ParamsArray() + { + var source = """ + var d = M; + System.Console.WriteLine(d.GetType()); + + void M(params int[] arr) { } + """; + var verifier = CompileAndVerify(source, + targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, + symbolValidator: validate, + verify: Verification.FailsPEVerify, + expectedOutput: ExecutionConditionUtil.IsMonoOrCoreClr ? "<>f__AnonymousDelegate0`1[System.Int32]" : null); + verifier.VerifyDiagnostics(); + + static void validate(ModuleSymbol module) + { + var m = module.GlobalNamespace.GetMember("<>f__AnonymousDelegate0.Invoke"); + AssertEx.Equal("void <>f__AnonymousDelegate0.Invoke(params T1[] arg)", m.ToTestDisplayString()); + Assert.False(m.ContainingType.TypeParameters.Single().AllowsRefLikeType); + } + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/74823")] + public void AnonymousDelegateType_10_ParamsArray() + { + var source = """ + var d = M; + System.Console.WriteLine(d.GetType()); + + void M(string s, params int[] arr) { } + """; + var verifier = CompileAndVerify(source, + targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, + symbolValidator: validate, + verify: Verification.FailsPEVerify, + expectedOutput: ExecutionConditionUtil.IsMonoOrCoreClr ? "<>f__AnonymousDelegate0`2[System.String,System.Int32]" : null); + verifier.VerifyDiagnostics(); + + static void validate(ModuleSymbol module) + { + var m = module.GlobalNamespace.GetMember("<>f__AnonymousDelegate0.Invoke"); + AssertEx.Equal("void <>f__AnonymousDelegate0.Invoke(T1 arg1, params T2[] arg2)", m.ToTestDisplayString()); + Assert.Equal([true, false], m.ContainingType.TypeParameters.Select(t => t.AllowsRefLikeType)); + } + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/74823")] + public void AnonymousDelegateType_11_ParamsArray() + { + var source = """ + unsafe + { + var d = M; + System.Console.WriteLine(d.GetType()); + + void M(int* p, params int[] arr) { } + } + """; + var verifier = CompileAndVerify(source, + options: TestOptions.UnsafeReleaseExe, + targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, + symbolValidator: validate, + verify: Verification.FailsPEVerify, + expectedOutput: ExecutionConditionUtil.IsMonoOrCoreClr ? "<>f__AnonymousDelegate0" : null); + verifier.VerifyDiagnostics(); + + static void validate(ModuleSymbol module) + { + var m = module.GlobalNamespace.GetMember("<>f__AnonymousDelegate0.Invoke"); + AssertEx.Equal("void <>f__AnonymousDelegate0.Invoke(System.Int32* arg1, params System.Int32[] arg2)", m.ToTestDisplayString()); + } + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/74823")] + public void AnonymousDelegateType_12_ParamsArray() + { + var source = """ + M(); + unsafe static void M() + { + var d = C.M; + System.Console.WriteLine(d.GetType()); + } + unsafe static class C + { + public static void M(int* p, T x, params int[] arr) { } + } + """; + var verifier = CompileAndVerify(source, + options: TestOptions.UnsafeReleaseExe, + targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, + symbolValidator: validate, + verify: Verification.FailsPEVerify, + expectedOutput: ExecutionConditionUtil.IsMonoOrCoreClr ? "<>f__AnonymousDelegate0`1[System.String]" : null); + verifier.VerifyDiagnostics(); + + static void validate(ModuleSymbol module) + { + var m = module.GlobalNamespace.GetMember("<>f__AnonymousDelegate0.Invoke"); + AssertEx.Equal("void <>f__AnonymousDelegate0.Invoke(System.Int32* arg1, T1 arg2, params System.Int32[] arg3)", m.ToTestDisplayString()); + Assert.False(m.ContainingType.TypeParameters.Single().AllowsRefLikeType); + } + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/74823")] + public void AnonymousDelegateType_13_ParamsArray() + { + var source = """ + M(); + unsafe static void M() where T : allows ref struct + { + var d = C.M; + System.Console.WriteLine(d.GetType()); + } + unsafe static class C where T : allows ref struct + { + public static void M(int* p, T x, params int[] arr) { } + } + """; + var verifier = CompileAndVerify(source, + options: TestOptions.UnsafeReleaseExe, + targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, + symbolValidator: validate, + verify: Verification.FailsPEVerify, + expectedOutput: ExecutionConditionUtil.IsMonoOrCoreClr ? "<>f__AnonymousDelegate0`1[System.String]" : null); + verifier.VerifyDiagnostics(); + + static void validate(ModuleSymbol module) + { + var m = module.GlobalNamespace.GetMember("<>f__AnonymousDelegate0.Invoke"); + AssertEx.Equal("void <>f__AnonymousDelegate0.Invoke(System.Int32* arg1, T1 arg2, params System.Int32[] arg3)", m.ToTestDisplayString()); + Assert.True(m.ContainingType.TypeParameters.Single().AllowsRefLikeType); + } + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/74823")] + public void AnonymousDelegateType_14_ParamsArray() + { + var source = """ + M(); + unsafe static void M() + { + var d = C.M; + System.Console.WriteLine(d.GetType()); + } + unsafe static class C + { + public static void M(int* p, T x, params T[] arr) { } + } + """; + var verifier = CompileAndVerify(source, + options: TestOptions.UnsafeReleaseExe, + targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, + symbolValidator: validate, + verify: Verification.FailsPEVerify, + expectedOutput: ExecutionConditionUtil.IsMonoOrCoreClr ? "<>f__AnonymousDelegate0`1[System.String]" : null); + verifier.VerifyDiagnostics(); + + static void validate(ModuleSymbol module) + { + var m = module.GlobalNamespace.GetMember("<>f__AnonymousDelegate0.Invoke"); + AssertEx.Equal("void <>f__AnonymousDelegate0.Invoke(System.Int32* arg1, T1 arg2, params T1[] arg3)", m.ToTestDisplayString()); + Assert.False(m.ContainingType.TypeParameters.Single().AllowsRefLikeType); + } + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/74823")] + public void AnonymousDelegateType_15_ParamsArray() + { + var source = """ + M(); + unsafe static void M() + { + var d = C.M; + System.Console.WriteLine(d.GetType()); + } + unsafe static class C + { + public static void M(int* p, params T[] arr) { } + } + """; + var verifier = CompileAndVerify(source, + options: TestOptions.UnsafeReleaseExe, + targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, + symbolValidator: validate, + verify: Verification.FailsPEVerify, + expectedOutput: ExecutionConditionUtil.IsMonoOrCoreClr ? "<>f__AnonymousDelegate0`1[System.String]" : null); + verifier.VerifyDiagnostics(); + + static void validate(ModuleSymbol module) + { + var m = module.GlobalNamespace.GetMember("<>f__AnonymousDelegate0.Invoke"); + AssertEx.Equal("void <>f__AnonymousDelegate0.Invoke(System.Int32* arg1, params T1[] arg2)", m.ToTestDisplayString()); + Assert.False(m.ContainingType.TypeParameters.Single().AllowsRefLikeType); + } + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/74823")] + public void AnonymousDelegateType_16_ParamsArray() + { + var source = """ + #nullable enable + M(); + unsafe static void M() + { + var d = C.M; + System.Console.WriteLine(d.GetType()); + } + unsafe static class C + { + public static void M(int* p, params T?[] arr) { } + } + """; + var verifier = CompileAndVerify(source, + options: TestOptions.UnsafeReleaseExe, + targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, + symbolValidator: validate, + verify: Verification.FailsPEVerify, + expectedOutput: ExecutionConditionUtil.IsMonoOrCoreClr ? "<>f__AnonymousDelegate0`1[System.String]" : null); + verifier.VerifyDiagnostics(); + + static void validate(ModuleSymbol module) + { + var m = module.GlobalNamespace.GetMember("<>f__AnonymousDelegate0.Invoke"); + AssertEx.Equal("void <>f__AnonymousDelegate0.Invoke(System.Int32* arg1, params T1?[] arg2)", m.ToTestDisplayString()); + Assert.False(m.ContainingType.TypeParameters.Single().AllowsRefLikeType); + } + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/74823")] + public void AnonymousDelegateType_17_ParamsArray() + { + var source = """ + M(); + unsafe static void M() where T : struct + { + var d = C.M; + System.Console.WriteLine(d.GetType()); + } + unsafe static class C where T : struct + { + public static void M(int* p, params T?[] arr) { } + } + """; + var verifier = CompileAndVerify(source, + options: TestOptions.UnsafeReleaseExe, + targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, + symbolValidator: validate, + verify: Verification.FailsPEVerify, + expectedOutput: ExecutionConditionUtil.IsMonoOrCoreClr ? "<>f__AnonymousDelegate0`1[System.Int16]" : null); + verifier.VerifyDiagnostics(); + + static void validate(ModuleSymbol module) + { + var m = module.GlobalNamespace.GetMember("<>f__AnonymousDelegate0.Invoke"); + AssertEx.Equal("void <>f__AnonymousDelegate0.Invoke(System.Int32* arg1, params T1?[] arg2)", m.ToTestDisplayString()); + Assert.False(m.ContainingType.TypeParameters.Single().AllowsRefLikeType); + } + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/74823")] + public void AnonymousDelegateType_18_ParamsArray() + { + var source = """ + M(); + unsafe static void M() + { + var d = C.M; + System.Console.WriteLine(d.GetType()); + } + unsafe static class C + { + public static void M(int* p, params T[][] arr) { } + } + """; + var verifier = CompileAndVerify(source, + options: TestOptions.UnsafeReleaseExe, + targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, + symbolValidator: validate, + verify: Verification.FailsPEVerify, + expectedOutput: ExecutionConditionUtil.IsMonoOrCoreClr ? "<>f__AnonymousDelegate0`1[System.Int16]" : null); + verifier.VerifyDiagnostics(); + + static void validate(ModuleSymbol module) + { + var m = module.GlobalNamespace.GetMember("<>f__AnonymousDelegate0.Invoke"); + AssertEx.Equal("void <>f__AnonymousDelegate0.Invoke(System.Int32* arg1, params T1[][] arg2)", m.ToTestDisplayString()); + Assert.False(m.ContainingType.TypeParameters.Single().AllowsRefLikeType); + } + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/74823")] + public void AnonymousDelegateType_19_ParamsCollection() + { + var source = """ + var d = M; + System.Console.WriteLine(d.GetType()); + + void M(params System.Collections.Generic.IEnumerable e) { } + """; + var verifier = CompileAndVerify(source, + targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, + symbolValidator: validate, + verify: Verification.FailsPEVerify, + expectedOutput: ExecutionConditionUtil.IsMonoOrCoreClr ? "<>f__AnonymousDelegate0" : null); + verifier.VerifyDiagnostics(); + + static void validate(ModuleSymbol module) + { + var m = module.GlobalNamespace.GetMember("<>f__AnonymousDelegate0.Invoke"); + AssertEx.Equal("void <>f__AnonymousDelegate0.Invoke(params System.Collections.Generic.IEnumerable arg)", m.ToTestDisplayString()); + } + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/74823")] + public void AnonymousDelegateType_20() + { + var source = """ + M(); + unsafe static void M() + { + var d = C.M; + System.Console.WriteLine(d.GetType()); + } + unsafe static class C + { + public static void M(int* p, T[] arr) { } + } + """; + var verifier = CompileAndVerify(source, + options: TestOptions.UnsafeReleaseExe, + targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, + symbolValidator: validate, + verify: Verification.FailsPEVerify, + expectedOutput: ExecutionConditionUtil.IsMonoOrCoreClr ? "<>f__AnonymousDelegate0`1[System.Int16]" : null); + verifier.VerifyDiagnostics(); + + static void validate(ModuleSymbol module) + { + var m = module.GlobalNamespace.GetMember("<>f__AnonymousDelegate0.Invoke"); + AssertEx.Equal("void <>f__AnonymousDelegate0.Invoke(System.Int32* arg1, T1[] arg2)", m.ToTestDisplayString()); + Assert.False(m.ContainingType.TypeParameters.Single().AllowsRefLikeType); + } + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/74823")] + public void AnonymousDelegateType_21() + { + var source = """ + M(); + unsafe static void M() where T : allows ref struct + { + var d = C.M; + System.Console.WriteLine(d.GetType()); + } + unsafe static class C where T : allows ref struct + { + public static void M(int* p, T t) { } + } + """; + var verifier = CompileAndVerify(source, + options: TestOptions.UnsafeReleaseExe, + targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, + symbolValidator: validate, + verify: Verification.FailsPEVerify, + expectedOutput: ExecutionConditionUtil.IsMonoOrCoreClr ? "<>f__AnonymousDelegate0`1[System.Int16]" : null); + verifier.VerifyDiagnostics(); + + static void validate(ModuleSymbol module) + { + var m = module.GlobalNamespace.GetMember("<>f__AnonymousDelegate0.Invoke"); + AssertEx.Equal("void <>f__AnonymousDelegate0.Invoke(System.Int32* arg1, T1 arg2)", m.ToTestDisplayString()); + Assert.True(m.ContainingType.TypeParameters.Single().AllowsRefLikeType); + } + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/74823")] + public void AnonymousDelegateType_22() + { + var source = """ + var d = M; + System.Console.WriteLine(d.GetType()); + + void M(ref short p, int[] arr) { } + """; + var verifier = CompileAndVerify(source, + targetFramework: s_targetFrameworkSupportingByRefLikeGenerics, + symbolValidator: validate, + verify: Verification.FailsPEVerify, + expectedOutput: ExecutionConditionUtil.IsMonoOrCoreClr ? "<>A{00000001}`2[System.Int16,System.Int32[]]" : null); + verifier.VerifyDiagnostics(); + + static void validate(ModuleSymbol module) + { + var m = module.GlobalNamespace.GetMember("<>A{00000001}.Invoke"); + AssertEx.Equal("void <>A{00000001}.Invoke(ref T1 arg1, T2 arg2)", m.ToTestDisplayString()); + Assert.Equal([true, true], m.ContainingType.TypeParameters.Select(t => t.AllowsRefLikeType)); + } + } + [Fact] public void ExpressionTree_01() {