From 41e69a48a814438d2b645bfac5723d5fc3e129a7 Mon Sep 17 00:00:00 2001 From: Fredric Silberberg Date: Fri, 22 Mar 2024 11:30:35 -0700 Subject: [PATCH 1/6] Disallow dynamic calls on ref struct receivers Fixes https://github.com/dotnet/roslyn/issues/72606. Note that the behavior in the bug has slightly changed from the sharplab build used in the repro due to https://github.com/dotnet/roslyn/pull/71421. --- .../Portable/Binder/Binder_Invocation.cs | 7 + .../CSharp/Portable/CSharpResources.resx | 5 +- .../CSharp/Portable/Errors/ErrorCode.cs | 1 + .../Portable/xlf/CSharpResources.cs.xlf | 5 + .../Portable/xlf/CSharpResources.de.xlf | 5 + .../Portable/xlf/CSharpResources.es.xlf | 5 + .../Portable/xlf/CSharpResources.fr.xlf | 5 + .../Portable/xlf/CSharpResources.it.xlf | 5 + .../Portable/xlf/CSharpResources.ja.xlf | 5 + .../Portable/xlf/CSharpResources.ko.xlf | 5 + .../Portable/xlf/CSharpResources.pl.xlf | 5 + .../Portable/xlf/CSharpResources.pt-BR.xlf | 5 + .../Portable/xlf/CSharpResources.ru.xlf | 5 + .../Portable/xlf/CSharpResources.tr.xlf | 5 + .../Portable/xlf/CSharpResources.zh-Hans.xlf | 5 + .../Portable/xlf/CSharpResources.zh-Hant.xlf | 5 + .../Test/Semantic/Semantics/DynamicTests.cs | 178 ++++++++++++++ .../Semantic/Semantics/InterpolationTests.cs | 224 ++++++++++++++++++ 18 files changed, 479 insertions(+), 1 deletion(-) diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Invocation.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Invocation.cs index 366a0e05659ac..b89f2f56c6818 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Invocation.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Invocation.cs @@ -459,6 +459,13 @@ private BoundExpression BindDynamicInvocation( methodGroup.ResultKind); break; } + + if (receiver.Type.IsRefLikeType) + { + // Cannot perform a dynamic call on a ref struct '{0}'. + Error(diagnostics, ErrorCode.ERR_CannotDynamicInvokeOnRefStruct, receiver.Syntax, receiver.Type); + hasErrors = true; + } } } else diff --git a/src/Compilers/CSharp/Portable/CSharpResources.resx b/src/Compilers/CSharp/Portable/CSharpResources.resx index 1c597710b3960..ec8903768920f 100644 --- a/src/Compilers/CSharp/Portable/CSharpResources.resx +++ b/src/Compilers/CSharp/Portable/CSharpResources.resx @@ -7905,4 +7905,7 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ Modifiers cannot be placed on using declarations - \ No newline at end of file + + Cannot perform a dynamic call on a ref struct '{0}'. + + diff --git a/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs b/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs index 70c3ed6d8b861..ff90dd90be955 100644 --- a/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs +++ b/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs @@ -2301,6 +2301,7 @@ internal enum ErrorCode ERR_ParamsCollectionMissingConstructor = 9228, ERR_NoModifiersOnUsing = 9229, + ERR_CannotDynamicInvokeOnRefStruct = 9230, #endregion diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf index 52bc7a06848ef..563399d19796e 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf @@ -327,6 +327,11 @@ Skupina &metody {0} se nedá převést na typ delegáta {1}. + + Cannot perform a dynamic call on a ref struct '{0}'. + Cannot perform a dynamic call on a ref struct '{0}'. + + The delegate type could not be inferred. Nedal se odvodit typ delegáta. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf index 31fdb8ad5d94e..283dbabbc55a8 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf @@ -327,6 +327,11 @@ Die &Methodengruppe "{0}" kann nicht in den Delegattyp "{1}" konvertiert werden. + + Cannot perform a dynamic call on a ref struct '{0}'. + Cannot perform a dynamic call on a ref struct '{0}'. + + The delegate type could not be inferred. Der Delegattyp konnte nicht abgeleitet werden. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf index 46d2302cf07e3..9b00c930804c5 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf @@ -327,6 +327,11 @@ No se puede convertir el grupo de métodos '{0}' al tipo delegado '{1}'. + + Cannot perform a dynamic call on a ref struct '{0}'. + Cannot perform a dynamic call on a ref struct '{0}'. + + The delegate type could not be inferred. El tipo de delegado no se puede deducir. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf index 21d95c543a69c..e0946c37cc443 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf @@ -327,6 +327,11 @@ Impossible de convertir le groupe de &méthodes '{0}' en type délégué '{1}'. + + Cannot perform a dynamic call on a ref struct '{0}'. + Cannot perform a dynamic call on a ref struct '{0}'. + + The delegate type could not be inferred. Impossible de déduire le type délégué. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf index 4b6f9d4393503..9a950ec32b622 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf @@ -327,6 +327,11 @@ Non è possibile convertire il gruppo di &metodi '{0}' nel tipo delegato '{1}'. + + Cannot perform a dynamic call on a ref struct '{0}'. + Cannot perform a dynamic call on a ref struct '{0}'. + + The delegate type could not be inferred. Non è possibile dedurre il tipo di delegato. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf index d22e46da8f2c8..6c13875244498 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf @@ -327,6 +327,11 @@ メソッド グループ '{0}' をデリゲート型 '{1}' に変換することはできません。(&M) + + Cannot perform a dynamic call on a ref struct '{0}'. + Cannot perform a dynamic call on a ref struct '{0}'. + + The delegate type could not be inferred. デリゲート型を推論できませんでした。 diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf index 3c3e4232b34f0..1af328925dc67 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf @@ -327,6 +327,11 @@ 메서드 그룹 '{0}'을(를) 대리자 형식 '{1}'(으)로 변환할 수 없습니다(&M). + + Cannot perform a dynamic call on a ref struct '{0}'. + Cannot perform a dynamic call on a ref struct '{0}'. + + The delegate type could not be inferred. 대리자 형식을 유추할 수 없습니다. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf index 236da182cee77..0682b5d52f5f4 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf @@ -327,6 +327,11 @@ Nie można przekonwertować &grupy metod „{0}” na typ delegata „{1}”. + + Cannot perform a dynamic call on a ref struct '{0}'. + Cannot perform a dynamic call on a ref struct '{0}'. + + The delegate type could not be inferred. Nie można wywnioskować typu delegowania. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf index ed4a82cea33ad..693e11d10d0cd 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf @@ -327,6 +327,11 @@ Não é possível converter o grupo &método '{0}' para o tipo delegado '{1}'. + + Cannot perform a dynamic call on a ref struct '{0}'. + Cannot perform a dynamic call on a ref struct '{0}'. + + The delegate type could not be inferred. O tipo de representante não pôde ser inferido. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf index 197239919e8d6..ce344cbf626cb 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf @@ -327,6 +327,11 @@ Невозможно преобразовать &группу методов "{0}" в тип делегата "{1}". + + Cannot perform a dynamic call on a ref struct '{0}'. + Cannot perform a dynamic call on a ref struct '{0}'. + + The delegate type could not be inferred. Не удалось вывести тип делегата. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf index 44f14a30f0b80..d1ae28725efab 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf @@ -327,6 +327,11 @@ '{0}' &metot grubu, '{1}' temsilci türüne dönüştürülemiyor. + + Cannot perform a dynamic call on a ref struct '{0}'. + Cannot perform a dynamic call on a ref struct '{0}'. + + The delegate type could not be inferred. Temsilci türü çıkarsanamadı. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf index 0983b4f8fc93c..876d483c1cea4 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf @@ -327,6 +327,11 @@ 无法将方法组“{0}”转换为委托类型“{1}”(&M)。 + + Cannot perform a dynamic call on a ref struct '{0}'. + Cannot perform a dynamic call on a ref struct '{0}'. + + The delegate type could not be inferred. 无法推断委托类型。 diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf index ac7f52744cd5c..72951a5e21845 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf @@ -327,6 +327,11 @@ 無法將方法群組 '{0}' 轉換成委派類型 '{1}'(&M)。 + + Cannot perform a dynamic call on a ref struct '{0}'. + Cannot perform a dynamic call on a ref struct '{0}'. + + The delegate type could not be inferred. 無法推斷委派類型。 diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/DynamicTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/DynamicTests.cs index c965d1742584e..bd73da765f858 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/DynamicTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/DynamicTests.cs @@ -4545,5 +4545,183 @@ class C2 : C1 CompileAndVerify(comp, expectedOutput: "int").VerifyDiagnostics(); } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/72606")] + public void RefStructReceiver01() + { + var code = """ + var s = new S(); + dynamic d = null; + + s.M(d); + + ref struct S + { + public void M(T t) { } + } + """; + + CreateCompilation(code).VerifyDiagnostics( + // (4,1): error CS9230: Cannot perform a dynamic call on a ref struct 'S'. + // s.M(d); + Diagnostic(ErrorCode.ERR_CannotDynamicInvokeOnRefStruct, "s").WithArguments("S").WithLocation(4, 1) + ); + } + + [Theory, WorkItem("https://github.com/dotnet/roslyn/issues/72606")] + [InlineData("object")] + [InlineData("dynamic")] + public void RefStructReceiver02(string argType) + { + var code = $$""" + var s = new S(); + dynamic d = "Hello world"; + + s.M(d); + + ref struct S + { + public void M({{argType}} o) => System.Console.WriteLine(o); + } + """; + + var verifier = CompileAndVerify(code, expectedOutput: "Hello world", targetFramework: TargetFramework.StandardAndCSharp); + verifier.VerifyDiagnostics(); + verifier.VerifyIL("", $$""" + { + // Code size 23 (0x17) + .maxstack 2 + .locals init (S V_0, //s + object V_1) //d + IL_0000: ldloca.s V_0 + IL_0002: initobj "S" + IL_0008: ldstr "Hello world" + IL_000d: stloc.1 + IL_000e: ldloca.s V_0 + IL_0010: ldloc.1 + IL_0011: call "void S.M({{argType}})" + IL_0016: ret + } + """); + } + + [Theory, WorkItem("https://github.com/dotnet/roslyn/issues/72606")] + [InlineData("string")] + [InlineData("int")] + public void RefStructReceiver03(string argType) + { + var code = $$""" + var s = new S(); + dynamic d = "Hello world"; + + try + { + s.M(d); + } + catch + { + System.Console.WriteLine("Caught exception"); + } + + ref struct S + { + public void M({{argType}} o) => System.Console.WriteLine(o); + } + """; + + var verifier = CompileAndVerify(code, expectedOutput: argType == "string" ? "Hello world" : "Caught exception", targetFramework: TargetFramework.StandardAndCSharp); + verifier.VerifyDiagnostics(); + verifier.VerifyIL("", $$""" + { + // Code size 101 (0x65) + .maxstack 4 + .locals init (S V_0, //s + object V_1) //d + IL_0000: ldloca.s V_0 + IL_0002: initobj "S" + IL_0008: ldstr "Hello world" + IL_000d: stloc.1 + .try + { + IL_000e: ldloca.s V_0 + IL_0010: ldsfld "System.Runtime.CompilerServices.CallSite> Program.<>o__0.<>p__0" + IL_0015: brtrue.s IL_003b + IL_0017: ldc.i4.0 + IL_0018: ldtoken "{{argType}}" + IL_001d: call "System.Type System.Type.GetTypeFromHandle(System.RuntimeTypeHandle)" + IL_0022: ldtoken "Program" + IL_0027: call "System.Type System.Type.GetTypeFromHandle(System.RuntimeTypeHandle)" + IL_002c: call "System.Runtime.CompilerServices.CallSiteBinder Microsoft.CSharp.RuntimeBinder.Binder.Convert(Microsoft.CSharp.RuntimeBinder.CSharpBinderFlags, System.Type, System.Type)" + IL_0031: call "System.Runtime.CompilerServices.CallSite> System.Runtime.CompilerServices.CallSite>.Create(System.Runtime.CompilerServices.CallSiteBinder)" + IL_0036: stsfld "System.Runtime.CompilerServices.CallSite> Program.<>o__0.<>p__0" + IL_003b: ldsfld "System.Runtime.CompilerServices.CallSite> Program.<>o__0.<>p__0" + IL_0040: ldfld "System.Func System.Runtime.CompilerServices.CallSite>.Target" + IL_0045: ldsfld "System.Runtime.CompilerServices.CallSite> Program.<>o__0.<>p__0" + IL_004a: ldloc.1 + IL_004b: callvirt "{{argType}} System.Func.Invoke(System.Runtime.CompilerServices.CallSite, dynamic)" + IL_0050: call "void S.M({{argType}})" + IL_0055: leave.s IL_0064 + } + catch object + { + IL_0057: pop + IL_0058: ldstr "Caught exception" + IL_005d: call "void System.Console.WriteLine(string)" + IL_0062: leave.s IL_0064 + } + IL_0064: ret + } + """); + } + + [Theory, WorkItem("https://github.com/dotnet/roslyn/issues/72606")] + [InlineData("object")] + [InlineData("dynamic")] + public void RefStructReceiver04(string argType) + { + var code = $$""" + var s = new S(); + dynamic d = "Hello world"; + + s.M(d); + + ref struct S + { + public void M({{argType}} o) => System.Console.WriteLine(o); + public void M(string s) => System.Console.WriteLine(s); + } + """; + + CreateCompilation(code).VerifyDiagnostics( + // (4,1): error CS9230: Cannot perform a dynamic call on a ref struct 'S'. + // s.M(d); + Diagnostic(ErrorCode.ERR_CannotDynamicInvokeOnRefStruct, "s").WithArguments("S").WithLocation(4, 1) + ); + } + + [Theory, WorkItem("https://github.com/dotnet/roslyn/issues/72606")] + [InlineData("object")] + [InlineData("dynamic")] + public void RefStructReceiver05(string argType) + { + var code = $$""" + var s = new S(); + dynamic d = "Hello world"; + + s.M(d); + + ref struct S + { + public void M({{argType}} o) => System.Console.WriteLine(o); + public void M(T t) => System.Console.WriteLine(t); + } + """; + + CreateCompilation(code).VerifyDiagnostics( + // (4,1): error CS9230: Cannot perform a dynamic call on a ref struct 'S'. + // s.M(d); + Diagnostic(ErrorCode.ERR_CannotDynamicInvokeOnRefStruct, "s").WithArguments("S").WithLocation(4, 1) + ); + } } } diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/InterpolationTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/InterpolationTests.cs index 44237e0c5c671..1eb63e8f2f29a 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/InterpolationTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/InterpolationTests.cs @@ -14376,6 +14376,230 @@ .locals init (object V_0, //d "); } + [Theory, CombinatorialData, WorkItem("https://github.com/dotnet/roslyn/issues/72606")] + public void DynamicConstruction10( + [CombinatorialValues(@"$""literal{d}""", @"$""literal"" + $""{d}""")] string expression, + [CombinatorialValues("object", "dynamic", "string")] string type) + { + var source = $$""" + using System; + using System.Runtime.CompilerServices; + using System.Text; + + dynamic d = "Hello world!"; + + Console.WriteLine(Interpolate({{expression}})); + + static string Interpolate(CustomInterpolationHandler text) + { + return text.ToString(); + } + + [InterpolatedStringHandler] + public ref struct CustomInterpolationHandler + { + private StringBuilder StringBuilder; + + public CustomInterpolationHandler(int literalLength, int formattedCount) + { + StringBuilder = new StringBuilder(); + } + + public void AppendLiteral(string text) + { + StringBuilder.Append(text); + } + + public void AppendFormatted({{type}} item) + { + StringBuilder.Append(item); + } + + public override string ToString() + { + return StringBuilder.ToString(); + } + } + """; + + var verifier = CompileAndVerify([source, InterpolatedStringHandlerAttribute], expectedOutput: "literalHello world!", targetFramework: TargetFramework.StandardAndCSharp); + verifier.VerifyDiagnostics(); + + verifier.VerifyIL("", type switch + { + "string" => """ + { + // Code size 110 (0x6e) + .maxstack 4 + .locals init (object V_0, //d + CustomInterpolationHandler V_1) + IL_0000: ldstr "Hello world!" + IL_0005: stloc.0 + IL_0006: ldloca.s V_1 + IL_0008: ldc.i4.7 + IL_0009: ldc.i4.1 + IL_000a: call "CustomInterpolationHandler..ctor(int, int)" + IL_000f: ldloca.s V_1 + IL_0011: ldstr "literal" + IL_0016: call "void CustomInterpolationHandler.AppendLiteral(string)" + IL_001b: ldloca.s V_1 + IL_001d: ldsfld "System.Runtime.CompilerServices.CallSite> Program.<>o__0.<>p__0" + IL_0022: brtrue.s IL_0048 + IL_0024: ldc.i4.0 + IL_0025: ldtoken "string" + IL_002a: call "System.Type System.Type.GetTypeFromHandle(System.RuntimeTypeHandle)" + IL_002f: ldtoken "Program" + IL_0034: call "System.Type System.Type.GetTypeFromHandle(System.RuntimeTypeHandle)" + IL_0039: call "System.Runtime.CompilerServices.CallSiteBinder Microsoft.CSharp.RuntimeBinder.Binder.Convert(Microsoft.CSharp.RuntimeBinder.CSharpBinderFlags, System.Type, System.Type)" + IL_003e: call "System.Runtime.CompilerServices.CallSite> System.Runtime.CompilerServices.CallSite>.Create(System.Runtime.CompilerServices.CallSiteBinder)" + IL_0043: stsfld "System.Runtime.CompilerServices.CallSite> Program.<>o__0.<>p__0" + IL_0048: ldsfld "System.Runtime.CompilerServices.CallSite> Program.<>o__0.<>p__0" + IL_004d: ldfld "System.Func System.Runtime.CompilerServices.CallSite>.Target" + IL_0052: ldsfld "System.Runtime.CompilerServices.CallSite> Program.<>o__0.<>p__0" + IL_0057: ldloc.0 + IL_0058: callvirt "string System.Func.Invoke(System.Runtime.CompilerServices.CallSite, dynamic)" + IL_005d: call "void CustomInterpolationHandler.AppendFormatted(string)" + IL_0062: ldloc.1 + IL_0063: call "string Program.<
$>g__Interpolate|0_0(CustomInterpolationHandler)" + IL_0068: call "void System.Console.WriteLine(string)" + IL_006d: ret + } + """, + _ => $$""" + { + // Code size 47 (0x2f) + .maxstack 3 + .locals init (object V_0, //d + CustomInterpolationHandler V_1) + IL_0000: ldstr "Hello world!" + IL_0005: stloc.0 + IL_0006: ldloca.s V_1 + IL_0008: ldc.i4.7 + IL_0009: ldc.i4.1 + IL_000a: call "CustomInterpolationHandler..ctor(int, int)" + IL_000f: ldloca.s V_1 + IL_0011: ldstr "literal" + IL_0016: call "void CustomInterpolationHandler.AppendLiteral(string)" + IL_001b: ldloca.s V_1 + IL_001d: ldloc.0 + IL_001e: call "void CustomInterpolationHandler.AppendFormatted({{type}})" + IL_0023: ldloc.1 + IL_0024: call "string Program.<
$>g__Interpolate|0_0(CustomInterpolationHandler)" + IL_0029: call "void System.Console.WriteLine(string)" + IL_002e: ret + } + """, + }); + } + + [Theory, CombinatorialData, WorkItem("https://github.com/dotnet/roslyn/issues/72606")] + public void DynamicConstruction11([CombinatorialValues(@"$""literal{d}""", @"$""literal"" + $""{d}""")] string expression) + { + var source = $$""" + using System; + using System.Runtime.CompilerServices; + using System.Text; + + dynamic d = "Hello world!"; + + Console.WriteLine(Interpolate({{expression}})); + + static string Interpolate(CustomInterpolationHandler text) + { + return text.ToString(); + } + + [InterpolatedStringHandler] + public ref struct CustomInterpolationHandler + { + private StringBuilder StringBuilder; + + public CustomInterpolationHandler(int literalLength, int formattedCount) + { + StringBuilder = new StringBuilder(); + } + + public void AppendLiteral(string text) + { + StringBuilder.Append(text); + } + + public void AppendFormatted(T item) + { + StringBuilder.Append(item); + } + + public override string ToString() + { + return StringBuilder.ToString(); + } + } + """; + + CreateCompilation(source, targetFramework: TargetFramework.NetCoreApp).VerifyDiagnostics( + // (7,31): error CS9230: Cannot perform a dynamic call on a ref struct 'CustomInterpolationHandler'. + // Console.WriteLine(Interpolate($"literal" + $"{d}")); + Diagnostic(ErrorCode.ERR_CannotDynamicInvokeOnRefStruct, expression).WithArguments("CustomInterpolationHandler").WithLocation(7, 31) + + ); + } + + [Theory, CombinatorialData, WorkItem("https://github.com/dotnet/roslyn/issues/72606")] + public void DynamicConstruction12([CombinatorialValues(@"$""literal{d}""", @"$""literal"" + $""{d}""")] string expression) + { + var source = $$""" + using System; + using System.Runtime.CompilerServices; + using System.Text; + + dynamic d = "Hello world!"; + + Console.WriteLine(Interpolate({{expression}})); + + static string Interpolate(CustomInterpolationHandler text) + { + return text.ToString(); + } + + [InterpolatedStringHandler] + public ref struct CustomInterpolationHandler + { + private StringBuilder StringBuilder; + + public CustomInterpolationHandler(int literalLength, int formattedCount) + { + StringBuilder = new StringBuilder(); + } + + public void AppendLiteral(string text) + { + StringBuilder.Append(text); + } + + public void AppendFormatted(object item) + { + StringBuilder.Append(item); + } + + public void AppendFormatted(string item) + { + StringBuilder.Append(item); + } + + public override string ToString() + { + return StringBuilder.ToString(); + } + } + """; + + CreateCompilation(source, targetFramework: TargetFramework.NetCoreApp).VerifyDiagnostics( + // (7,31): error CS9230: Cannot perform a dynamic call on a ref struct 'CustomInterpolationHandler'. + // Console.WriteLine(Interpolate($"literal" + $"{d}")); + Diagnostic(ErrorCode.ERR_CannotDynamicInvokeOnRefStruct, expression).WithArguments("CustomInterpolationHandler").WithLocation(7, 31) + ); + } + [Theory] [InlineData(@"$""{s}""")] [InlineData(@"$""{s}"" + $""""")] From 7b8937a3b203a6690649fe2624c2472cd8a03e11 Mon Sep 17 00:00:00 2001 From: Fredric Silberberg Date: Fri, 22 Mar 2024 12:21:10 -0700 Subject: [PATCH 2/6] IsBuildOnlyDiagnostic --- src/Compilers/CSharp/Portable/Errors/ErrorCode.cs | 4 ++++ src/Compilers/CSharp/Portable/Errors/ErrorFacts.cs | 1 + 2 files changed, 5 insertions(+) diff --git a/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs b/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs index ff90dd90be955..f0d27e16e4f0e 100644 --- a/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs +++ b/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs @@ -2305,7 +2305,11 @@ internal enum ErrorCode #endregion + // Note: you will need to do the following after adding errors: + // 1) Update ErrorFacts.IsBuildOnlyDiagnostic (src/Compilers/CSharp/Portable/Errors/ErrorFacts.cs) + // Note: you will need to do the following after adding warnings: // 1) Re-generate compiler code (eng\generate-compiler-code.cmd). + // 2) Update ErrorFacts.IsBuildOnlyDiagnostic (src/Compilers/CSharp/Portable/Errors/ErrorFacts.cs) } } diff --git a/src/Compilers/CSharp/Portable/Errors/ErrorFacts.cs b/src/Compilers/CSharp/Portable/Errors/ErrorFacts.cs index b4eaef25f8e7a..38771d635f18b 100644 --- a/src/Compilers/CSharp/Portable/Errors/ErrorFacts.cs +++ b/src/Compilers/CSharp/Portable/Errors/ErrorFacts.cs @@ -2431,6 +2431,7 @@ internal static bool IsBuildOnlyDiagnostic(ErrorCode code) case ErrorCode.ERR_ParamsCollectionExtensionAddMethod: case ErrorCode.ERR_ParamsCollectionMissingConstructor: case ErrorCode.ERR_NoModifiersOnUsing: + case ErrorCode.ERR_CannotDynamicInvokeOnRefStruct: return false; default: // NOTE: All error codes must be explicitly handled in this switch statement From 0ea0eeb3c25ed6bcf07e3f3055681e0bc96ce149 Mon Sep 17 00:00:00 2001 From: Fredric Silberberg Date: Mon, 25 Mar 2024 17:43:16 -0700 Subject: [PATCH 3/6] Block dynamic indexing as well. --- .../Portable/Binder/Binder_Expressions.cs | 7 + .../Test/Semantic/Semantics/DynamicTests.cs | 237 ++++++++++++++++++ 2 files changed, 244 insertions(+) diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs index c577341e48531..f4923f33f380a 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs @@ -9278,6 +9278,13 @@ private BoundExpression BindDynamicIndexer( receiver = ReplaceTypeOrValueReceiver(typeOrValue, useType, diagnostics); } + if (receiver.Type.IsRefLikeType) + { + // Cannot perform a dynamic call on a ref struct '{0}'. + Error(diagnostics, ErrorCode.ERR_CannotDynamicInvokeOnRefStruct, receiver.Syntax, receiver.Type); + hasErrors = true; + } + var argArray = BuildArgumentsForDynamicInvocation(arguments, diagnostics); var refKindsArray = arguments.RefKinds.ToImmutableOrNull(); diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/DynamicTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/DynamicTests.cs index bd73da765f858..9a676463cb946 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/DynamicTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/DynamicTests.cs @@ -4723,5 +4723,242 @@ ref struct S Diagnostic(ErrorCode.ERR_CannotDynamicInvokeOnRefStruct, "s").WithArguments("S").WithLocation(4, 1) ); } + + [Theory, WorkItem("https://github.com/dotnet/roslyn/issues/72606")] + [InlineData("object")] + [InlineData("dynamic")] + public void RefStructReceiver06(string argType) + { + var code = $$""" + var s = new S(); + dynamic d = "Hello world"; + + _ = s[d]; + s[d] = 1; + + ref struct S + { + public int this[{{argType}} o] + { + get + { + System.Console.WriteLine(o); + return 0; + } + set => System.Console.WriteLine(o); + } + } + """; + + var verifier = CompileAndVerify(code, expectedOutput: """ + Hello world + Hello world + """, targetFramework: TargetFramework.StandardAndCSharp); + verifier.VerifyDiagnostics(); + verifier.VerifyIL("", $$""" + { + // Code size 33 (0x21) + .maxstack 3 + .locals init (S V_0, //s + object V_1) //d + IL_0000: ldloca.s V_0 + IL_0002: initobj "S" + IL_0008: ldstr "Hello world" + IL_000d: stloc.1 + IL_000e: ldloca.s V_0 + IL_0010: ldloc.1 + IL_0011: call "int S.this[{{argType}}].get" + IL_0016: pop + IL_0017: ldloca.s V_0 + IL_0019: ldloc.1 + IL_001a: ldc.i4.1 + IL_001b: call "void S.this[{{argType}}].set" + IL_0020: ret + } + """); + } + + [Theory, WorkItem("https://github.com/dotnet/roslyn/issues/72606")] + [InlineData("string")] + [InlineData("int")] + public void RefStructReceiver07(string argType) + { + var code = $$""" + dynamic d = "Hello world"; + + get(); + set(); + + void get() + { + var s = new S(); + try + { + _ = s[d]; + } + catch + { + System.Console.WriteLine("Caught exception"); + } + } + + void set() + { + var s = new S(); + try + { + s[d] = 1; + } + catch + { + System.Console.WriteLine("Caught exception"); + } + } + + ref struct S + { + public int this[{{argType}} o] + { + get + { + System.Console.WriteLine(o); + return 0; + } + set => System.Console.WriteLine(o); + } + } + """; + + var verifier = CompileAndVerify(code, expectedOutput: argType == "string" + ? """ + Hello world + Hello world + """ + : """ + Caught exception + Caught exception + """, targetFramework: TargetFramework.StandardAndCSharp); + verifier.VerifyDiagnostics(); + + verifier.VerifyIL("Program.<
$>g__get|0_0(ref Program.<>c__DisplayClass0_0)", $$""" + { + // Code size 101 (0x65) + .maxstack 4 + .locals init (S V_0) //s + IL_0000: ldloca.s V_0 + IL_0002: initobj "S" + .try + { + IL_0008: ldloca.s V_0 + IL_000a: ldsfld "System.Runtime.CompilerServices.CallSite> Program.<>o__0.<>p__0" + IL_000f: brtrue.s IL_0035 + IL_0011: ldc.i4.0 + IL_0012: ldtoken "{{argType}}" + IL_0017: call "System.Type System.Type.GetTypeFromHandle(System.RuntimeTypeHandle)" + IL_001c: ldtoken "Program" + IL_0021: call "System.Type System.Type.GetTypeFromHandle(System.RuntimeTypeHandle)" + IL_0026: call "System.Runtime.CompilerServices.CallSiteBinder Microsoft.CSharp.RuntimeBinder.Binder.Convert(Microsoft.CSharp.RuntimeBinder.CSharpBinderFlags, System.Type, System.Type)" + IL_002b: call "System.Runtime.CompilerServices.CallSite> System.Runtime.CompilerServices.CallSite>.Create(System.Runtime.CompilerServices.CallSiteBinder)" + IL_0030: stsfld "System.Runtime.CompilerServices.CallSite> Program.<>o__0.<>p__0" + IL_0035: ldsfld "System.Runtime.CompilerServices.CallSite> Program.<>o__0.<>p__0" + IL_003a: ldfld "System.Func System.Runtime.CompilerServices.CallSite>.Target" + IL_003f: ldsfld "System.Runtime.CompilerServices.CallSite> Program.<>o__0.<>p__0" + IL_0044: ldarg.0 + IL_0045: ldfld "dynamic Program.<>c__DisplayClass0_0.d" + IL_004a: callvirt "{{argType}} System.Func.Invoke(System.Runtime.CompilerServices.CallSite, dynamic)" + IL_004f: call "int S.this[{{argType}}].get" + IL_0054: pop + IL_0055: leave.s IL_0064 + } + catch object + { + IL_0057: pop + IL_0058: ldstr "Caught exception" + IL_005d: call "void System.Console.WriteLine(string)" + IL_0062: leave.s IL_0064 + } + IL_0064: ret + } + """); + + verifier.VerifyIL("Program.<
$>g__set|0_1(ref Program.<>c__DisplayClass0_0)", $$""" + { + // Code size 101 (0x65) + .maxstack 4 + .locals init (S V_0) //s + IL_0000: ldloca.s V_0 + IL_0002: initobj "S" + .try + { + IL_0008: ldloca.s V_0 + IL_000a: ldsfld "System.Runtime.CompilerServices.CallSite> Program.<>o__0.<>p__1" + IL_000f: brtrue.s IL_0035 + IL_0011: ldc.i4.0 + IL_0012: ldtoken "{{argType}}" + IL_0017: call "System.Type System.Type.GetTypeFromHandle(System.RuntimeTypeHandle)" + IL_001c: ldtoken "Program" + IL_0021: call "System.Type System.Type.GetTypeFromHandle(System.RuntimeTypeHandle)" + IL_0026: call "System.Runtime.CompilerServices.CallSiteBinder Microsoft.CSharp.RuntimeBinder.Binder.Convert(Microsoft.CSharp.RuntimeBinder.CSharpBinderFlags, System.Type, System.Type)" + IL_002b: call "System.Runtime.CompilerServices.CallSite> System.Runtime.CompilerServices.CallSite>.Create(System.Runtime.CompilerServices.CallSiteBinder)" + IL_0030: stsfld "System.Runtime.CompilerServices.CallSite> Program.<>o__0.<>p__1" + IL_0035: ldsfld "System.Runtime.CompilerServices.CallSite> Program.<>o__0.<>p__1" + IL_003a: ldfld "System.Func System.Runtime.CompilerServices.CallSite>.Target" + IL_003f: ldsfld "System.Runtime.CompilerServices.CallSite> Program.<>o__0.<>p__1" + IL_0044: ldarg.0 + IL_0045: ldfld "dynamic Program.<>c__DisplayClass0_0.d" + IL_004a: callvirt "{{argType}} System.Func.Invoke(System.Runtime.CompilerServices.CallSite, dynamic)" + IL_004f: ldc.i4.1 + IL_0050: call "void S.this[{{argType}}].set" + IL_0055: leave.s IL_0064 + } + catch object + { + IL_0057: pop + IL_0058: ldstr "Caught exception" + IL_005d: call "void System.Console.WriteLine(string)" + IL_0062: leave.s IL_0064 + } + IL_0064: ret + } + """); + } + + [Theory, WorkItem("https://github.com/dotnet/roslyn/issues/72606")] + [InlineData("object")] + [InlineData("dynamic")] + public void RefStructReceiver08(string argType) + { + var code = $$""" + var s = new S(); + dynamic d = "Hello world"; + + _ = s[d]; + s[d] = 1; + + ref struct S + { + public int this[{{argType}} o] + { + get => 0; + set => System.Console.WriteLine(o); + } + + public int this[string s] + { + get => 0; + set => System.Console.WriteLine(s); + } + } + """; + + CreateCompilation(code).VerifyDiagnostics( + // (4,5): error CS9230: Cannot perform a dynamic call on a ref struct 'S'. + // _ = s[d]; + Diagnostic(ErrorCode.ERR_CannotDynamicInvokeOnRefStruct, "s").WithArguments("S").WithLocation(4, 5), + // (5,1): error CS9230: Cannot perform a dynamic call on a ref struct 'S'. + // s[d] = 1; + Diagnostic(ErrorCode.ERR_CannotDynamicInvokeOnRefStruct, "s").WithArguments("S").WithLocation(5, 1) + ); + } } } From 46ffaf77cc7190fd3275167b579b515f392569e8 Mon Sep 17 00:00:00 2001 From: Fredric Silberberg Date: Wed, 27 Mar 2024 14:01:03 -0700 Subject: [PATCH 4/6] Address PR feedback --- .../Portable/Binder/Binder_Conversions.cs | 5 ++-- .../Portable/Binder/Binder_Expressions.cs | 13 ++------- .../Portable/Binder/Binder_Invocation.cs | 29 +++++++++++++------ .../Semantics/CollectionExpressionTests.cs | 23 +++++++++++++++ 4 files changed, 49 insertions(+), 21 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Conversions.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Conversions.cs index 6b07dc19faac5..3d804f239c031 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Conversions.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Conversions.cs @@ -1051,13 +1051,14 @@ static bool bindInvocationExpression( static bool bindDynamicInvocation( Binder addMethodBinder, SyntaxNode node, + BoundExpression? receiver, AnalyzedArguments arguments, BindingDiagnosticBag diagnostics) { ImmutableArray argArray = addMethodBinder.BuildArgumentsForDynamicInvocation(arguments, diagnostics); var refKindsArray = arguments.RefKinds.ToImmutableOrNull(); - return !ReportBadDynamicArguments(node, argArray, refKindsArray, diagnostics, queryClause: null); + return !ReportBadDynamicArguments(node, receiver, argArray, refKindsArray, diagnostics, queryClause: null); } // This is what BindMethodGroupInvocation is doing in terms of reporting diagnostics and detecting a failure @@ -1152,7 +1153,7 @@ static bool bindMethodGroupInvocation( addMethodBinder.ReportDynamicInvocationWarnings(syntax, methodGroup, diagnostics, resolution, finalApplicableCandidates); addMethods = finalApplicableCandidates.SelectAsArray(r => r.Member); - result = bindDynamicInvocation(addMethodBinder, syntax, resolution.AnalyzedArguments, diagnostics); + result = bindDynamicInvocation(addMethodBinder, syntax, methodGroup.ReceiverOpt, resolution.AnalyzedArguments, diagnostics); } } } diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs index f4923f33f380a..3da3fa044ccb7 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs @@ -5954,7 +5954,7 @@ BoundExpression bindCollectionInitializerElementAddMethod( if (implicitReceiver.Type.IsDynamic()) { - var hasErrors = ReportBadDynamicArguments(elementInitializer, boundElementInitializerExpressions, refKinds: default, diagnostics, queryClause: null); + var hasErrors = ReportBadDynamicArguments(elementInitializer, implicitReceiver, boundElementInitializerExpressions, refKinds: default, diagnostics, queryClause: null); return new BoundDynamicCollectionElementInitializer( elementInitializer, @@ -6233,7 +6233,7 @@ protected BoundExpression BindClassCreationExpression( var argArray = BuildArgumentsForDynamicInvocation(analyzedArguments, diagnostics); var refKindsArray = analyzedArguments.RefKinds.ToImmutableOrNull(); - hasErrors &= ReportBadDynamicArguments(node, argArray, refKindsArray, diagnostics, queryClause: null); + hasErrors &= ReportBadDynamicArguments(node, receiver: null, argArray, refKindsArray, diagnostics, queryClause: null); BoundObjectInitializerExpressionBase boundInitializerOpt; boundInitializerOpt = MakeBoundInitializerOpt(typeNode, type, initializerSyntaxOpt, initializerTypeOpt, diagnostics); @@ -9278,17 +9278,10 @@ private BoundExpression BindDynamicIndexer( receiver = ReplaceTypeOrValueReceiver(typeOrValue, useType, diagnostics); } - if (receiver.Type.IsRefLikeType) - { - // Cannot perform a dynamic call on a ref struct '{0}'. - Error(diagnostics, ErrorCode.ERR_CannotDynamicInvokeOnRefStruct, receiver.Syntax, receiver.Type); - hasErrors = true; - } - var argArray = BuildArgumentsForDynamicInvocation(arguments, diagnostics); var refKindsArray = arguments.RefKinds.ToImmutableOrNull(); - hasErrors &= ReportBadDynamicArguments(syntax, argArray, refKindsArray, diagnostics, queryClause: null); + hasErrors &= ReportBadDynamicArguments(syntax, receiver, argArray, refKindsArray, diagnostics, queryClause: null); return new BoundDynamicIndexerAccess( syntax, diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Invocation.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Invocation.cs index b89f2f56c6818..e752a41ba708a 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Invocation.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Invocation.cs @@ -399,10 +399,11 @@ private BoundExpression BindDynamicInvocation( CheckNamedArgumentsForDynamicInvocation(arguments, diagnostics); bool hasErrors = false; + BoundExpression? receiver; if (expression.Kind == BoundKind.MethodGroup) { BoundMethodGroup methodGroup = (BoundMethodGroup)expression; - BoundExpression receiver = methodGroup.ReceiverOpt; + receiver = methodGroup.ReceiverOpt; // receiver is null if we are calling a static method declared on an outer class via its simple name: if (receiver != null) @@ -459,24 +460,26 @@ private BoundExpression BindDynamicInvocation( methodGroup.ResultKind); break; } - - if (receiver.Type.IsRefLikeType) - { - // Cannot perform a dynamic call on a ref struct '{0}'. - Error(diagnostics, ErrorCode.ERR_CannotDynamicInvokeOnRefStruct, receiver.Syntax, receiver.Type); - hasErrors = true; - } } } else { expression = BindToNaturalType(expression, diagnostics); + + if (expression is BoundDynamicMemberAccess memberAccess) + { + receiver = memberAccess.Receiver; + } + else + { + receiver = expression; + } } ImmutableArray argArray = BuildArgumentsForDynamicInvocation(arguments, diagnostics); var refKindsArray = arguments.RefKinds.ToImmutableOrNull(); - hasErrors &= ReportBadDynamicArguments(node, argArray, refKindsArray, diagnostics, queryClause); + hasErrors &= ReportBadDynamicArguments(node, receiver, argArray, refKindsArray, diagnostics, queryClause); return new BoundDynamicInvocation( node, @@ -536,6 +539,7 @@ private ImmutableArray BuildArgumentsForDynamicInvocation(Analy // Returns true if there were errors. private static bool ReportBadDynamicArguments( SyntaxNode node, + BoundExpression? receiver, ImmutableArray arguments, ImmutableArray refKinds, BindingDiagnosticBag diagnostics, @@ -544,6 +548,13 @@ private static bool ReportBadDynamicArguments( bool hasErrors = false; bool reportedBadQuery = false; + if (receiver != null && !IsLegalDynamicOperand(receiver)) + { + // Cannot perform a dynamic call on a ref struct '{0}'. + Error(diagnostics, ErrorCode.ERR_CannotDynamicInvokeOnRefStruct, receiver.Syntax, receiver.Type); + hasErrors = true; + } + if (!refKinds.IsDefault) { for (int argIndex = 0; argIndex < refKinds.Length; argIndex++) diff --git a/src/Compilers/CSharp/Test/Emit2/Semantics/CollectionExpressionTests.cs b/src/Compilers/CSharp/Test/Emit2/Semantics/CollectionExpressionTests.cs index 892925207389d..0835e1324bd32 100644 --- a/src/Compilers/CSharp/Test/Emit2/Semantics/CollectionExpressionTests.cs +++ b/src/Compilers/CSharp/Test/Emit2/Semantics/CollectionExpressionTests.cs @@ -20434,6 +20434,29 @@ static void Main() CompileAndVerify(new[] { source, s_collectionExtensions }, verify: Verification.Skipped, expectedOutput: "[0, 1], "); } + [Fact(Skip = "https://github.com/dotnet/roslyn/issues/72762")] + public void RefStruct_04() + { + var source = """ + using System.Collections; + using System.Collections.Generic; + + dynamic d = null; + S s = [d]; + + ref struct S : IEnumerable + { + public IEnumerator GetEnumerator() => throw null; + IEnumerator IEnumerable.GetEnumerator() => throw null; + public void Add(T t) => throw null; + } + """; + + // https://github.com/dotnet/roslyn/issues/72762 - There should be diagnostics for S implementing an interface, and + // there should also be a diagnostic that `s` cannot be used a recevier for a dynamic call. + CreateCompilation(source).VerifyDiagnostics(); + } + [CombinatorialData] [Theory] public void RefSafety_Return_01([CombinatorialValues(TargetFramework.Net70, TargetFramework.Net80)] TargetFramework targetFramework) From 0c53f111e6816bd339302b3ca95176ec23c018f2 Mon Sep 17 00:00:00 2001 From: Fredric Silberberg Date: Tue, 2 Apr 2024 17:44:14 -0700 Subject: [PATCH 5/6] Nullable enable to address warnings, reword error message to include more things that it will error on. --- .../Portable/Binder/Binder_Invocation.cs | 14 +++++++++---- .../CSharp/Portable/CSharpResources.resx | 4 ++-- .../CSharp/Portable/Errors/ErrorCode.cs | 2 +- .../CSharp/Portable/Errors/ErrorFacts.cs | 2 +- .../Portable/xlf/CSharpResources.cs.xlf | 8 ++++---- .../Portable/xlf/CSharpResources.de.xlf | 8 ++++---- .../Portable/xlf/CSharpResources.es.xlf | 8 ++++---- .../Portable/xlf/CSharpResources.fr.xlf | 8 ++++---- .../Portable/xlf/CSharpResources.it.xlf | 8 ++++---- .../Portable/xlf/CSharpResources.ja.xlf | 8 ++++---- .../Portable/xlf/CSharpResources.ko.xlf | 8 ++++---- .../Portable/xlf/CSharpResources.pl.xlf | 8 ++++---- .../Portable/xlf/CSharpResources.pt-BR.xlf | 8 ++++---- .../Portable/xlf/CSharpResources.ru.xlf | 8 ++++---- .../Portable/xlf/CSharpResources.tr.xlf | 8 ++++---- .../Portable/xlf/CSharpResources.zh-Hans.xlf | 8 ++++---- .../Portable/xlf/CSharpResources.zh-Hant.xlf | 8 ++++---- .../Test/Semantic/Semantics/DynamicTests.cs | 20 +++++++++---------- .../Semantic/Semantics/InterpolationTests.cs | 8 ++++---- 19 files changed, 80 insertions(+), 74 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Invocation.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Invocation.cs index e752a41ba708a..5314be7a5b914 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Invocation.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Invocation.cs @@ -380,6 +380,7 @@ private BoundExpression BindInvocationExpression( return result; } +#nullable enable private BoundExpression BindDynamicInvocation( SyntaxNode node, BoundExpression expression, @@ -423,6 +424,7 @@ private BoundExpression BindDynamicInvocation( // the runtime binder would ignore the receiver, but in a ctor initializer we can't read "this" before // the base constructor is called. We need to handle this as a type qualified static method call. // Also applicable to things like field initializers, which run before the ctor initializer. + Debug.Assert(ContainingType is not null); expression = methodGroup.Update( methodGroup.TypeArgumentsOpt, methodGroup.Name, @@ -491,6 +493,7 @@ private BoundExpression BindDynamicInvocation( type: Compilation.DynamicType, hasErrors: hasErrors); } +#nullable disable private void CheckNamedArgumentsForDynamicInvocation(AnalyzedArguments arguments, BindingDiagnosticBag diagnostics) { @@ -537,21 +540,23 @@ private ImmutableArray BuildArgumentsForDynamicInvocation(Analy } // Returns true if there were errors. +#nullable enable private static bool ReportBadDynamicArguments( SyntaxNode node, BoundExpression? receiver, ImmutableArray arguments, ImmutableArray refKinds, BindingDiagnosticBag diagnostics, - CSharpSyntaxNode queryClause) + CSharpSyntaxNode? queryClause) { bool hasErrors = false; bool reportedBadQuery = false; if (receiver != null && !IsLegalDynamicOperand(receiver)) { - // Cannot perform a dynamic call on a ref struct '{0}'. - Error(diagnostics, ErrorCode.ERR_CannotDynamicInvokeOnRefStruct, receiver.Syntax, receiver.Type); + // Cannot perform a dynamic invocation on an expression with type '{0}'. + Debug.Assert(receiver.Type is not null); + Error(diagnostics, ErrorCode.ERR_CannotDynamicInvokeOnExpression, receiver.Syntax, receiver.Type); hasErrors = true; } @@ -602,7 +607,7 @@ private static bool ReportBadDynamicArguments( { // Lambdas,anonymous methods and method groups are the typeless expressions that // are not usable as dynamic arguments; if we get here then the expression must have a type. - Debug.Assert((object)arg.Type != null); + Debug.Assert((object?)arg.Type != null); // error CS1978: Cannot use an expression of type 'int*' as an argument to a dynamically dispatched operation Error(diagnostics, ErrorCode.ERR_BadDynamicMethodArg, arg.Syntax, arg.Type); @@ -612,6 +617,7 @@ private static bool ReportBadDynamicArguments( } return hasErrors; } +#nullable disable private BoundExpression BindDelegateInvocation( SyntaxNode node, diff --git a/src/Compilers/CSharp/Portable/CSharpResources.resx b/src/Compilers/CSharp/Portable/CSharpResources.resx index ec8903768920f..6440e5877788a 100644 --- a/src/Compilers/CSharp/Portable/CSharpResources.resx +++ b/src/Compilers/CSharp/Portable/CSharpResources.resx @@ -7905,7 +7905,7 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ Modifiers cannot be placed on using declarations - - Cannot perform a dynamic call on a ref struct '{0}'. + + Cannot perform a dynamic invocation on an expression with type '{0}'. diff --git a/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs b/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs index f0d27e16e4f0e..0b141c96fe9f6 100644 --- a/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs +++ b/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs @@ -2301,7 +2301,7 @@ internal enum ErrorCode ERR_ParamsCollectionMissingConstructor = 9228, ERR_NoModifiersOnUsing = 9229, - ERR_CannotDynamicInvokeOnRefStruct = 9230, + ERR_CannotDynamicInvokeOnExpression = 9230, #endregion diff --git a/src/Compilers/CSharp/Portable/Errors/ErrorFacts.cs b/src/Compilers/CSharp/Portable/Errors/ErrorFacts.cs index 38771d635f18b..2342c333dfb1f 100644 --- a/src/Compilers/CSharp/Portable/Errors/ErrorFacts.cs +++ b/src/Compilers/CSharp/Portable/Errors/ErrorFacts.cs @@ -2431,7 +2431,7 @@ internal static bool IsBuildOnlyDiagnostic(ErrorCode code) case ErrorCode.ERR_ParamsCollectionExtensionAddMethod: case ErrorCode.ERR_ParamsCollectionMissingConstructor: case ErrorCode.ERR_NoModifiersOnUsing: - case ErrorCode.ERR_CannotDynamicInvokeOnRefStruct: + case ErrorCode.ERR_CannotDynamicInvokeOnExpression: return false; default: // NOTE: All error codes must be explicitly handled in this switch statement diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf index 563399d19796e..967e0b13e562a 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf @@ -327,9 +327,9 @@ Skupina &metody {0} se nedá převést na typ delegáta {1}. - - Cannot perform a dynamic call on a ref struct '{0}'. - Cannot perform a dynamic call on a ref struct '{0}'. + + Cannot perform a dynamic invocation on an expression with type '{0}'. + Cannot perform a dynamic invocation on an expression with type '{0}'. @@ -13011,4 +13011,4 @@ Pokud chcete odstranit toto varování, můžete místo toho použít /reference - \ No newline at end of file + diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf index 283dbabbc55a8..ce61deae0070c 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf @@ -327,9 +327,9 @@ Die &Methodengruppe "{0}" kann nicht in den Delegattyp "{1}" konvertiert werden. - - Cannot perform a dynamic call on a ref struct '{0}'. - Cannot perform a dynamic call on a ref struct '{0}'. + + Cannot perform a dynamic invocation on an expression with type '{0}'. + Cannot perform a dynamic invocation on an expression with type '{0}'. @@ -13011,4 +13011,4 @@ Um die Warnung zu beheben, können Sie stattdessen /reference verwenden (Einbett - \ No newline at end of file + diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf index 9b00c930804c5..74a97dcc504cf 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf @@ -327,9 +327,9 @@ No se puede convertir el grupo de métodos '{0}' al tipo delegado '{1}'. - - Cannot perform a dynamic call on a ref struct '{0}'. - Cannot perform a dynamic call on a ref struct '{0}'. + + Cannot perform a dynamic invocation on an expression with type '{0}'. + Cannot perform a dynamic invocation on an expression with type '{0}'. @@ -13011,4 +13011,4 @@ Para eliminar la advertencia puede usar /reference (establezca la propiedad Embe - \ No newline at end of file + diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf index e0946c37cc443..1889230abd22d 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf @@ -327,9 +327,9 @@ Impossible de convertir le groupe de &méthodes '{0}' en type délégué '{1}'. - - Cannot perform a dynamic call on a ref struct '{0}'. - Cannot perform a dynamic call on a ref struct '{0}'. + + Cannot perform a dynamic invocation on an expression with type '{0}'. + Cannot perform a dynamic invocation on an expression with type '{0}'. @@ -13011,4 +13011,4 @@ Pour supprimer l'avertissement, vous pouvez utiliser la commande /reference (dé - \ No newline at end of file + diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf index 9a950ec32b622..6d08108ae813a 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf @@ -327,9 +327,9 @@ Non è possibile convertire il gruppo di &metodi '{0}' nel tipo delegato '{1}'. - - Cannot perform a dynamic call on a ref struct '{0}'. - Cannot perform a dynamic call on a ref struct '{0}'. + + Cannot perform a dynamic invocation on an expression with type '{0}'. + Cannot perform a dynamic invocation on an expression with type '{0}'. @@ -13011,4 +13011,4 @@ Per rimuovere l'avviso, è invece possibile usare /reference (impostare la propr - \ No newline at end of file + diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf index 6c13875244498..35540a24dd000 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf @@ -327,9 +327,9 @@ メソッド グループ '{0}' をデリゲート型 '{1}' に変換することはできません。(&M) - - Cannot perform a dynamic call on a ref struct '{0}'. - Cannot perform a dynamic call on a ref struct '{0}'. + + Cannot perform a dynamic invocation on an expression with type '{0}'. + Cannot perform a dynamic invocation on an expression with type '{0}'. @@ -13011,4 +13011,4 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ - \ No newline at end of file + diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf index 1af328925dc67..84181d36d88a3 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf @@ -327,9 +327,9 @@ 메서드 그룹 '{0}'을(를) 대리자 형식 '{1}'(으)로 변환할 수 없습니다(&M). - - Cannot perform a dynamic call on a ref struct '{0}'. - Cannot perform a dynamic call on a ref struct '{0}'. + + Cannot perform a dynamic invocation on an expression with type '{0}'. + Cannot perform a dynamic invocation on an expression with type '{0}'. @@ -13011,4 +13011,4 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ - \ No newline at end of file + diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf index 0682b5d52f5f4..c9ff6a2bb9ca6 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf @@ -327,9 +327,9 @@ Nie można przekonwertować &grupy metod „{0}” na typ delegata „{1}”. - - Cannot perform a dynamic call on a ref struct '{0}'. - Cannot perform a dynamic call on a ref struct '{0}'. + + Cannot perform a dynamic invocation on an expression with type '{0}'. + Cannot perform a dynamic invocation on an expression with type '{0}'. @@ -13011,4 +13011,4 @@ Aby usunąć ostrzeżenie, możesz zamiast tego użyć opcji /reference (ustaw w - \ No newline at end of file + diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf index 693e11d10d0cd..8d098c835cfaa 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf @@ -327,9 +327,9 @@ Não é possível converter o grupo &método '{0}' para o tipo delegado '{1}'. - - Cannot perform a dynamic call on a ref struct '{0}'. - Cannot perform a dynamic call on a ref struct '{0}'. + + Cannot perform a dynamic invocation on an expression with type '{0}'. + Cannot perform a dynamic invocation on an expression with type '{0}'. @@ -13011,4 +13011,4 @@ Para incorporar informações de tipo de interoperabilidade para os dois assembl - \ No newline at end of file + diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf index ce344cbf626cb..c0408330befc1 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf @@ -327,9 +327,9 @@ Невозможно преобразовать &группу методов "{0}" в тип делегата "{1}". - - Cannot perform a dynamic call on a ref struct '{0}'. - Cannot perform a dynamic call on a ref struct '{0}'. + + Cannot perform a dynamic invocation on an expression with type '{0}'. + Cannot perform a dynamic invocation on an expression with type '{0}'. @@ -13012,4 +13012,4 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ - \ No newline at end of file + diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf index d1ae28725efab..5af1205aab436 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf @@ -327,9 +327,9 @@ '{0}' &metot grubu, '{1}' temsilci türüne dönüştürülemiyor. - - Cannot perform a dynamic call on a ref struct '{0}'. - Cannot perform a dynamic call on a ref struct '{0}'. + + Cannot perform a dynamic invocation on an expression with type '{0}'. + Cannot perform a dynamic invocation on an expression with type '{0}'. @@ -13011,4 +13011,4 @@ Uyarıyı kaldırmak için, /reference kullanabilirsiniz (Birlikte Çalışma T - \ No newline at end of file + diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf index 876d483c1cea4..083f084732eb5 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf @@ -327,9 +327,9 @@ 无法将方法组“{0}”转换为委托类型“{1}”(&M)。 - - Cannot perform a dynamic call on a ref struct '{0}'. - Cannot perform a dynamic call on a ref struct '{0}'. + + Cannot perform a dynamic invocation on an expression with type '{0}'. + Cannot perform a dynamic invocation on an expression with type '{0}'. @@ -13011,4 +13011,4 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ - \ No newline at end of file + diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf index 72951a5e21845..57774ad95b96a 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf @@ -327,9 +327,9 @@ 無法將方法群組 '{0}' 轉換成委派類型 '{1}'(&M)。 - - Cannot perform a dynamic call on a ref struct '{0}'. - Cannot perform a dynamic call on a ref struct '{0}'. + + Cannot perform a dynamic invocation on an expression with type '{0}'. + Cannot perform a dynamic invocation on an expression with type '{0}'. @@ -13011,4 +13011,4 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ - \ No newline at end of file + diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/DynamicTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/DynamicTests.cs index 9a676463cb946..ea675e195a4cd 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/DynamicTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/DynamicTests.cs @@ -4562,9 +4562,9 @@ public void M(T t) { } """; CreateCompilation(code).VerifyDiagnostics( - // (4,1): error CS9230: Cannot perform a dynamic call on a ref struct 'S'. + // (4,1): error CS9230: Cannot perform a dynamic invocation on an expression with type 'S'. // s.M(d); - Diagnostic(ErrorCode.ERR_CannotDynamicInvokeOnRefStruct, "s").WithArguments("S").WithLocation(4, 1) + Diagnostic(ErrorCode.ERR_CannotDynamicInvokeOnExpression, "s").WithArguments("S").WithLocation(4, 1) ); } @@ -4693,9 +4693,9 @@ ref struct S """; CreateCompilation(code).VerifyDiagnostics( - // (4,1): error CS9230: Cannot perform a dynamic call on a ref struct 'S'. + // (4,1): error CS9230: Cannot perform a dynamic invocation on an expression with type 'S'. // s.M(d); - Diagnostic(ErrorCode.ERR_CannotDynamicInvokeOnRefStruct, "s").WithArguments("S").WithLocation(4, 1) + Diagnostic(ErrorCode.ERR_CannotDynamicInvokeOnExpression, "s").WithArguments("S").WithLocation(4, 1) ); } @@ -4718,9 +4718,9 @@ ref struct S """; CreateCompilation(code).VerifyDiagnostics( - // (4,1): error CS9230: Cannot perform a dynamic call on a ref struct 'S'. + // (4,1): error CS9230: Cannot perform a dynamic invocation on an expression with type 'S'. // s.M(d); - Diagnostic(ErrorCode.ERR_CannotDynamicInvokeOnRefStruct, "s").WithArguments("S").WithLocation(4, 1) + Diagnostic(ErrorCode.ERR_CannotDynamicInvokeOnExpression, "s").WithArguments("S").WithLocation(4, 1) ); } @@ -4952,12 +4952,12 @@ public int this[string s] """; CreateCompilation(code).VerifyDiagnostics( - // (4,5): error CS9230: Cannot perform a dynamic call on a ref struct 'S'. + // (4,5): error CS9230: Cannot perform a dynamic invocation on an expression with type 'S'. // _ = s[d]; - Diagnostic(ErrorCode.ERR_CannotDynamicInvokeOnRefStruct, "s").WithArguments("S").WithLocation(4, 5), - // (5,1): error CS9230: Cannot perform a dynamic call on a ref struct 'S'. + Diagnostic(ErrorCode.ERR_CannotDynamicInvokeOnExpression, "s").WithArguments("S").WithLocation(4, 5), + // (5,1): error CS9230: Cannot perform a dynamic invocation on an expression with type 'S'. // s[d] = 1; - Diagnostic(ErrorCode.ERR_CannotDynamicInvokeOnRefStruct, "s").WithArguments("S").WithLocation(5, 1) + Diagnostic(ErrorCode.ERR_CannotDynamicInvokeOnExpression, "s").WithArguments("S").WithLocation(5, 1) ); } } diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/InterpolationTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/InterpolationTests.cs index 1eb63e8f2f29a..2d3ef41415b5f 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/InterpolationTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/InterpolationTests.cs @@ -14537,9 +14537,9 @@ public override string ToString() """; CreateCompilation(source, targetFramework: TargetFramework.NetCoreApp).VerifyDiagnostics( - // (7,31): error CS9230: Cannot perform a dynamic call on a ref struct 'CustomInterpolationHandler'. + // (7,31): error CS9230: Cannot perform a dynamic invocation on an expression with type 'CustomInterpolationHandler'. // Console.WriteLine(Interpolate($"literal" + $"{d}")); - Diagnostic(ErrorCode.ERR_CannotDynamicInvokeOnRefStruct, expression).WithArguments("CustomInterpolationHandler").WithLocation(7, 31) + Diagnostic(ErrorCode.ERR_CannotDynamicInvokeOnExpression, expression).WithArguments("CustomInterpolationHandler").WithLocation(7, 31) ); } @@ -14594,9 +14594,9 @@ public override string ToString() """; CreateCompilation(source, targetFramework: TargetFramework.NetCoreApp).VerifyDiagnostics( - // (7,31): error CS9230: Cannot perform a dynamic call on a ref struct 'CustomInterpolationHandler'. + // (7,31): error CS9230: Cannot perform a dynamic invocation on an expression with type 'CustomInterpolationHandler'. // Console.WriteLine(Interpolate($"literal" + $"{d}")); - Diagnostic(ErrorCode.ERR_CannotDynamicInvokeOnRefStruct, expression).WithArguments("CustomInterpolationHandler").WithLocation(7, 31) + Diagnostic(ErrorCode.ERR_CannotDynamicInvokeOnExpression, expression).WithArguments("CustomInterpolationHandler").WithLocation(7, 31) ); } From 0bae4342e0bc83903de000b08d598fe7e18f8d32 Mon Sep 17 00:00:00 2001 From: Fredric Silberberg Date: Fri, 5 Apr 2024 10:04:41 -0700 Subject: [PATCH 6/6] Test suggestsions --- .../Semantics/CollectionExpressionTests.cs | 13 ++++++--- .../ObjectAndCollectionInitializerTests.cs | 28 +++++++++++++++++++ 2 files changed, 37 insertions(+), 4 deletions(-) diff --git a/src/Compilers/CSharp/Test/Emit2/Semantics/CollectionExpressionTests.cs b/src/Compilers/CSharp/Test/Emit2/Semantics/CollectionExpressionTests.cs index 6b8aab64b5594..41338fb37430b 100644 --- a/src/Compilers/CSharp/Test/Emit2/Semantics/CollectionExpressionTests.cs +++ b/src/Compilers/CSharp/Test/Emit2/Semantics/CollectionExpressionTests.cs @@ -20452,7 +20452,7 @@ static void Main() CompileAndVerify(new[] { source, s_collectionExtensions }, verify: Verification.Skipped, expectedOutput: "[0, 1], "); } - [Fact(Skip = "https://github.com/dotnet/roslyn/issues/72762")] + [Fact] public void RefStruct_04() { var source = """ @@ -20470,9 +20470,14 @@ public void RefStruct_04() } """; - // https://github.com/dotnet/roslyn/issues/72762 - There should be diagnostics for S implementing an interface, and - // there should also be a diagnostic that `s` cannot be used a recevier for a dynamic call. - CreateCompilation(source).VerifyDiagnostics(); + CreateCompilation(source).VerifyDiagnostics( + // (5,7): error CS9230: Cannot perform a dynamic invocation on an expression with type 'S'. + // S s = [d]; + Diagnostic(ErrorCode.ERR_CannotDynamicInvokeOnExpression, "[d]").WithArguments("S").WithLocation(5, 7), + // (7,16): error CS8343: 'S': ref structs cannot implement interfaces + // ref struct S : IEnumerable + Diagnostic(ErrorCode.ERR_RefStructInterfaceImpl, "IEnumerable").WithArguments("S").WithLocation(7, 16) + ); } [CombinatorialData] diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/ObjectAndCollectionInitializerTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/ObjectAndCollectionInitializerTests.cs index d74969e7e8ad7..22c0f35991cb8 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/ObjectAndCollectionInitializerTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/ObjectAndCollectionInitializerTests.cs @@ -4158,5 +4158,33 @@ Element Values(2): Assert.Equal(SpecialType.System_Int32, typeInfo.Type.SpecialType); Assert.Equal("I", typeInfo.ConvertedType.ToDisplayString()); } + + [Fact] + public void DynamicInvocationOnRefStructs() + { + var source = """ + using System.Collections; + using System.Collections.Generic; + + dynamic d = null; + S s = new S() { d }; + + ref struct S : IEnumerable + { + public IEnumerator GetEnumerator() => throw null; + IEnumerator IEnumerable.GetEnumerator() => throw null; + public void Add(T t) => throw null; + } + """; + + CreateCompilation(source).VerifyDiagnostics( + // (5,15): error CS1922: Cannot initialize type 'S' with a collection initializer because it does not implement 'System.Collections.IEnumerable' + // S s = new S() { d }; + Diagnostic(ErrorCode.ERR_CollectionInitRequiresIEnumerable, "{ d }").WithArguments("S").WithLocation(5, 15), + // (7,16): error CS8343: 'S': ref structs cannot implement interfaces + // ref struct S : IEnumerable + Diagnostic(ErrorCode.ERR_RefStructInterfaceImpl, "IEnumerable").WithArguments("S").WithLocation(7, 16) + ); + } } }