From b06244c855f9e6f4026b3663e0967c6d50ad0e6b Mon Sep 17 00:00:00 2001 From: Rikki Gibson Date: Mon, 16 Oct 2023 15:32:24 -0700 Subject: [PATCH] Collection expressions - error when we can't emit good codegen for ImmutableArray (#70384) --- .../Portable/Binder/Binder_Conversions.cs | 9 ++ .../CSharp/Portable/CSharpResources.resx | 3 + .../CSharp/Portable/Errors/ErrorCode.cs | 1 + .../CSharp/Portable/Errors/ErrorFacts.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 ++ .../Semantics/CollectionExpressionTests.cs | 87 +++++++++++++++++++ 18 files changed, 166 insertions(+) diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Conversions.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Conversions.cs index be0854782dcbf..49f174dacb900 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Conversions.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Conversions.cs @@ -613,6 +613,15 @@ private BoundExpression ConvertCollectionExpression( ReportDiagnosticsIfUnmanagedCallersOnly(diagnostics, collectionBuilderMethod, syntax, isDelegateConversion: false); } break; + + case CollectionExpressionTypeKind.ImplementsIEnumerableT: + case CollectionExpressionTypeKind.ImplementsIEnumerable: + if (targetType.OriginalDefinition.Equals(Compilation.GetWellKnownType(WellKnownType.System_Collections_Immutable_ImmutableArray_T), TypeCompareKind.ConsiderEverything)) + { + diagnostics.Add(ErrorCode.ERR_CollectionExpressionImmutableArray, syntax, targetType.OriginalDefinition); + return BindCollectionExpressionForErrorRecovery(node, targetType, diagnostics); + } + break; } var elements = node.Elements; diff --git a/src/Compilers/CSharp/Portable/CSharpResources.resx b/src/Compilers/CSharp/Portable/CSharpResources.resx index 5e7696d728e6b..f0a3395ccefef 100644 --- a/src/Compilers/CSharp/Portable/CSharpResources.resx +++ b/src/Compilers/CSharp/Portable/CSharpResources.resx @@ -7809,4 +7809,7 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ Expected interpolated string + + This version of '{0}' cannot be used with collection expressions. + \ No newline at end of file diff --git a/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs b/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs index 963e72c823116..15b5f25c7dda8 100644 --- a/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs +++ b/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs @@ -2275,6 +2275,7 @@ internal enum ErrorCode ERR_InterceptorGlobalNamespace = 9206, ERR_InterceptableMethodMustBeOrdinary = 9207, + ERR_CollectionExpressionImmutableArray = 9210, #endregion // Note: you will need to do the following after adding warnings: diff --git a/src/Compilers/CSharp/Portable/Errors/ErrorFacts.cs b/src/Compilers/CSharp/Portable/Errors/ErrorFacts.cs index d143e438a1ca0..a116cf7c99eda 100644 --- a/src/Compilers/CSharp/Portable/Errors/ErrorFacts.cs +++ b/src/Compilers/CSharp/Portable/Errors/ErrorFacts.cs @@ -2404,6 +2404,7 @@ internal static bool IsBuildOnlyDiagnostic(ErrorCode code) case ErrorCode.WRN_Experimental: case ErrorCode.ERR_ExpectedInterpolatedString: case ErrorCode.ERR_InterceptorGlobalNamespace: + case ErrorCode.ERR_CollectionExpressionImmutableArray: 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 b16f0acc4d349..cb9c88c924bfd 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.cs.xlf @@ -427,6 +427,11 @@ A collection expression of type '{0}' cannot be used in this context because it may be exposed outside of the current scope. + + This version of '{0}' cannot be used with collection expressions. + This version of '{0}' cannot be used with collection expressions. + + There is no target type for the collection expression. There is no target type for the collection expression. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf index 38698bcceb89f..628ddde88a8d3 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf @@ -427,6 +427,11 @@ A collection expression of type '{0}' cannot be used in this context because it may be exposed outside of the current scope. + + This version of '{0}' cannot be used with collection expressions. + This version of '{0}' cannot be used with collection expressions. + + There is no target type for the collection expression. There is no target type for the collection expression. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf index df2cad5becf67..c327557b2f1c7 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf @@ -427,6 +427,11 @@ A collection expression of type '{0}' cannot be used in this context because it may be exposed outside of the current scope. + + This version of '{0}' cannot be used with collection expressions. + This version of '{0}' cannot be used with collection expressions. + + There is no target type for the collection expression. There is no target type for the collection expression. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf index d40c18829c85d..05a48b46c5087 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf @@ -427,6 +427,11 @@ A collection expression of type '{0}' cannot be used in this context because it may be exposed outside of the current scope. + + This version of '{0}' cannot be used with collection expressions. + This version of '{0}' cannot be used with collection expressions. + + There is no target type for the collection expression. There is no target type for the collection expression. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf index 756acb7438b95..57e846c169a93 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf @@ -427,6 +427,11 @@ A collection expression of type '{0}' cannot be used in this context because it may be exposed outside of the current scope. + + This version of '{0}' cannot be used with collection expressions. + This version of '{0}' cannot be used with collection expressions. + + There is no target type for the collection expression. There is no target type for the collection expression. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf index 0eaa95c425d02..1b15b14ecd4fb 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf @@ -427,6 +427,11 @@ A collection expression of type '{0}' cannot be used in this context because it may be exposed outside of the current scope. + + This version of '{0}' cannot be used with collection expressions. + This version of '{0}' cannot be used with collection expressions. + + There is no target type for the collection expression. There is no target type for the collection expression. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf index 37ad10ac20dde..f226147e9df12 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf @@ -427,6 +427,11 @@ A collection expression of type '{0}' cannot be used in this context because it may be exposed outside of the current scope. + + This version of '{0}' cannot be used with collection expressions. + This version of '{0}' cannot be used with collection expressions. + + There is no target type for the collection expression. There is no target type for the collection expression. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf index d073a43e3529a..20fad1e8b8c77 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf @@ -427,6 +427,11 @@ A collection expression of type '{0}' cannot be used in this context because it may be exposed outside of the current scope. + + This version of '{0}' cannot be used with collection expressions. + This version of '{0}' cannot be used with collection expressions. + + There is no target type for the collection expression. There is no target type for the collection expression. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf index ffe0777fe7f63..b73a1d17e428e 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf @@ -427,6 +427,11 @@ A collection expression of type '{0}' cannot be used in this context because it may be exposed outside of the current scope. + + This version of '{0}' cannot be used with collection expressions. + This version of '{0}' cannot be used with collection expressions. + + There is no target type for the collection expression. There is no target type for the collection expression. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf index 11de96785821d..152d345c2f518 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf @@ -427,6 +427,11 @@ A collection expression of type '{0}' cannot be used in this context because it may be exposed outside of the current scope. + + This version of '{0}' cannot be used with collection expressions. + This version of '{0}' cannot be used with collection expressions. + + There is no target type for the collection expression. Отсутствует целевой тип для выражения коллекции. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf index cfc61bb561085..c38c46feae0f9 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf @@ -427,6 +427,11 @@ A collection expression of type '{0}' cannot be used in this context because it may be exposed outside of the current scope. + + This version of '{0}' cannot be used with collection expressions. + This version of '{0}' cannot be used with collection expressions. + + There is no target type for the collection expression. There is no target type for the collection expression. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf index 2ba0f97fb11a5..ba3d1490d75ee 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf @@ -427,6 +427,11 @@ A collection expression of type '{0}' cannot be used in this context because it may be exposed outside of the current scope. + + This version of '{0}' cannot be used with collection expressions. + This version of '{0}' cannot be used with collection expressions. + + There is no target type for the collection expression. There is no target type for the collection expression. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf index b82f08a4fac92..392a913ae50d9 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf @@ -427,6 +427,11 @@ A collection expression of type '{0}' cannot be used in this context because it may be exposed outside of the current scope. + + This version of '{0}' cannot be used with collection expressions. + This version of '{0}' cannot be used with collection expressions. + + There is no target type for the collection expression. There is no target type for the collection expression. diff --git a/src/Compilers/CSharp/Test/Emit2/Semantics/CollectionExpressionTests.cs b/src/Compilers/CSharp/Test/Emit2/Semantics/CollectionExpressionTests.cs index 43ca663e35c12..2c0228c410766 100644 --- a/src/Compilers/CSharp/Test/Emit2/Semantics/CollectionExpressionTests.cs +++ b/src/Compilers/CSharp/Test/Emit2/Semantics/CollectionExpressionTests.cs @@ -21744,6 +21744,93 @@ .locals init (System.Collections.Generic.List V_0, """); } + [Fact] + public void ImmutableArray_06() + { + string sourceA = """ + using System.Collections.Immutable; + + class Program + { + static void Main() + { + ImmutableArray arr = [1, 2, 3]; + arr.Report(); + } + } + """; + + var comp = CreateCompilation(new[] { sourceA, s_collectionExtensions }, targetFramework: TargetFramework.Net60); + comp.VerifyEmitDiagnostics( + // 0.cs(7,35): error CS9210: This version of 'ImmutableArray' cannot be used with collection expressions. + // ImmutableArray arr = [1, 2, 3]; + Diagnostic(ErrorCode.ERR_CollectionExpressionImmutableArray, "[1, 2, 3]").WithArguments("System.Collections.Immutable.ImmutableArray").WithLocation(7, 35)); + + // Can work around this error in downlevel scenarios by defining ImmutableCollectionsMarshal.AsImmutableArray + string sourceB = """ + using System.Collections.Immutable; + + namespace System.Runtime.InteropServices + { + public static class ImmutableCollectionsMarshal + { + // nb: the real implementation of this would use an unsafe cast + public static ImmutableArray AsImmutableArray(T[] array) => ImmutableArray.Create(array); + } + } + """; + + var verifier = CompileAndVerify(new[] { sourceA, sourceB, s_collectionExtensions }, targetFramework: TargetFramework.Net60, verify: Verification.Skipped, expectedOutput: IncludeExpectedOutput("[1, 2, 3],")); + verifier.VerifyDiagnostics(); + verifier.VerifyIL("Program.Main", """ + { + // Code size 34 (0x22) + .maxstack 3 + IL_0000: ldc.i4.3 + IL_0001: newarr "int" + IL_0006: dup + IL_0007: ldtoken ".__StaticArrayInitTypeSize=12 .4636993D3E1DA4E9D6B8F87B79E8F7C6D018580D52661950EABC3845C5897A4D" + IL_000c: call "void System.Runtime.CompilerServices.RuntimeHelpers.InitializeArray(System.Array, System.RuntimeFieldHandle)" + IL_0011: call "System.Collections.Immutable.ImmutableArray System.Runtime.InteropServices.ImmutableCollectionsMarshal.AsImmutableArray(int[])" + IL_0016: box "System.Collections.Immutable.ImmutableArray" + IL_001b: ldc.i4.0 + IL_001c: call "void CollectionExtensions.Report(object, bool)" + IL_0021: ret + } + """); + } + + [Fact] + public void ImmutableArray_07() + { + // Test an ImmutableArray which implements only non-generic IEnumerable. + string sourceA = """ + using System.Collections.Immutable; + + class Program + { + static void Main() + { + ImmutableArray arr = [1, 2, 3]; + } + } + + namespace System.Collections.Immutable + { + struct ImmutableArray : IEnumerable + { + public void Add(T t) { } + IEnumerator IEnumerable.GetEnumerator() => null; + } + } + """; + + var comp = CreateCompilation(sourceA, targetFramework: TargetFramework.Mscorlib40); + comp.VerifyEmitDiagnostics( + // 0.cs(7,35): error CS9210: This version of 'ImmutableArray' cannot be used with collection expressions. + // ImmutableArray arr = [1, 2, 3]; + Diagnostic(ErrorCode.ERR_CollectionExpressionImmutableArray, "[1, 2, 3]").WithArguments("System.Collections.Immutable.ImmutableArray").WithLocation(7, 35)); + } [Fact] public void ElementNullability_ArrayCollection() {