/
githubmirror
/
roslyn
Обзор
Документация
Войти
/
githubmirror
/
roslyn
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
src/Compilers/CSharp/Test/Emit3/Semantics/CollectionExpressionTests.cs
47 754 строки
2 MB
AlekseyTs
Adjust IL used for a null check in the compiler-synthesized inline-array helpers (#84523)
20 июл 2026, 18:31
Не верифицирован
20 июл 2026, 18:31
4d33ccf
Код
Авторство
О чём код?
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. #nullable disable using System; using System.Collections.Generic; using System.Collections.Immutable; using System.Linq; using Microsoft.CodeAnalysis.CSharp.Symbols; using Microsoft.CodeAnalysis.CSharp.Symbols.Retargeting; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.CSharp.Test.Utilities; using Microsoft.CodeAnalysis.CSharp.UnitTests.Semantics; using Microsoft.CodeAnalysis.Operations; using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Test.Utilities; using Roslyn.Test.Utilities; using Xunit; namespace Microsoft.CodeAnalysis.CSharp.UnitTests { [CompilerTrait(CompilerFeature.CollectionExpressions)] public class CollectionExpressionTests : CSharpTestBase { private static readonly IEnumerable<KeyValuePair<string, ReportDiagnostic>> WithSpanAllocWarning = new[] { KeyValuePair.Create(GetIdForErrorCode(ErrorCode.WRN_CollectionExpressionRefStructMayAllocate), ReportDiagnostic.Warn), KeyValuePair.Create(GetIdForErrorCode(ErrorCode.WRN_CollectionExpressionRefStructSpreadMayAllocate), ReportDiagnostic.Warn) }; private static string IncludeExpectedOutput(string expectedOutput) => ExecutionConditionUtil.IsMonoOrCoreClr ? expectedOutput : null; internal const string s_collectionExtensions = """ using System; using System.Collections; using System.Linq; using System.Text; static partial class CollectionExtensions { private static void Append(StringBuilder builder, bool isFirst, object value) { if (!isFirst) builder.Append(", "); if (value is IEnumerable e && value is not string) { AppendCollection(builder, e); } else { builder.Append(value is null ? "null" : value.ToString()); } } private static void AppendCollection(StringBuilder builder, IEnumerable e) { builder.Append("["); bool isFirst = true; foreach (var i in e) { Append(builder, isFirst, i); isFirst = false; } builder.Append("]"); } internal static void Report(this object o, bool includeType = false) { var builder = new StringBuilder(); Append(builder, isFirst: true, o); if (includeType) Console.Write("({0}) ", GetTypeName(o.GetType())); Console.Write(builder.ToString()); Console.Write(", "); } internal static string GetTypeName(this Type type) { if (type.IsArray) { return GetTypeName(type.GetElementType()) + "[]"; } string typeName = type.Name; int index = typeName.LastIndexOf('`'); if (index >= 0) { typeName = typeName.Substring(0, index); } if (!type.IsGenericParameter) { if (type.DeclaringType is { } declaringType) { typeName = Concat(GetTypeName(declaringType), typeName); } else { typeName = Concat(type.Namespace, typeName); } } if (!type.IsGenericType) { return typeName; } var typeArgs = type.GetGenericArguments(); return $"{typeName}<{string.Join(", ", typeArgs.Select(GetTypeName))}>"; } private static string Concat(string container, string name) { return string.IsNullOrEmpty(container) ? name : container + "." + name; } } """; internal const string s_collectionExtensionsWithSpan = s_collectionExtensions + """ static partial class CollectionExtensions { internal static void ReportSpan<T>(this in Span<T> s) { Report((ReadOnlySpan<T>)s); } internal static void Report<T>(this in ReadOnlySpan<T> s) { var builder = new StringBuilder(); builder.Append("["); bool isFirst = true; foreach (var i in s) { Append(builder, isFirst, i); isFirst = false; } builder.Append("]"); Console.Write(builder.ToString()); Console.Write(", "); } } """; [Theory] [InlineData(LanguageVersion.CSharp11)] [InlineData(LanguageVersion.Preview)] public void LanguageVersionDiagnostics(LanguageVersion languageVersion) { string source = """ using System.Collections.Generic; class Program { static void Main() { object[] x = []; List<object> y = [1, 2, 3]; List<object[]> z = [[]]; } } """; var comp = CreateCompilation(source, parseOptions: TestOptions.Regular.WithLanguageVersion(languageVersion)); if (languageVersion == LanguageVersion.CSharp11) { comp.VerifyEmitDiagnostics( // (6,22): error CS9058: Feature 'collection expressions' is not available in C# 11.0. Please use language version 12.0 or greater. // object[] x = []; Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion11, "[").WithArguments("collection expressions", "12.0").WithLocation(6, 22), // (7,26): error CS9058: Feature 'collection expressions' is not available in C# 11.0. Please use language version 12.0 or greater. // List<object> y = [1, 2, 3]; Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion11, "[").WithArguments("collection expressions", "12.0").WithLocation(7, 26), // (8,28): error CS9058: Feature 'collection expressions' is not available in C# 11.0. Please use language version 12.0 or greater. // List<object[]> z = [[]]; Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion11, "[").WithArguments("collection expressions", "12.0").WithLocation(8, 28), // (8,29): error CS9058: Feature 'collection expressions' is not available in C# 11.0. Please use language version 12.0 or greater. // List<object[]> z = [[]]; Diagnostic(ErrorCode.ERR_FeatureNotAvailableInVersion11, "[").WithArguments("collection expressions", "12.0").WithLocation(8, 29)); } else { comp.VerifyEmitDiagnostics(); } } [Fact] public void NaturalType_01() { string source = """ class Program { static void Main() { object x = []; dynamic y = []; var z = []; } } """; var comp = CreateCompilation(source); comp.VerifyEmitDiagnostics( // (5,20): error CS9174: Cannot initialize type 'object' with a collection expression because the type is not constructible. // object x = []; Diagnostic(ErrorCode.ERR_CollectionExpressionTargetTypeNotConstructible, "[]").WithArguments("object").WithLocation(5, 20), // (6,21): error CS9174: Cannot initialize type 'dynamic' with a collection expression because the type is not constructible. // dynamic y = []; Diagnostic(ErrorCode.ERR_CollectionExpressionTargetTypeNotConstructible, "[]").WithArguments("dynamic").WithLocation(6, 21), // (7,17): error CS9176: There is no target type for the collection expression. // var z = []; Diagnostic(ErrorCode.ERR_CollectionExpressionNoTargetType, "[]").WithLocation(7, 17)); var tree = comp.SyntaxTrees[0]; var model = comp.GetSemanticModel(tree); var collections = tree.GetRoot().DescendantNodes().OfType<CollectionExpressionSyntax>().ToArray(); Assert.Equal(3, collections.Length); VerifyTypes(model, collections[0], expectedType: null, expectedConvertedType: "System.Object", ConversionKind.NoConversion); VerifyTypes(model, collections[1], expectedType: null, expectedConvertedType: "dynamic", ConversionKind.NoConversion); VerifyTypes(model, collections[2], expectedType: null, expectedConvertedType: null, ConversionKind.Identity); } [Fact] public void NaturalType_02() { string source = """ class Program { static void Main() { object x = [1]; dynamic y = [2]; var z = [3]; } } """; var comp = CreateCompilation(source); comp.VerifyEmitDiagnostics( // (5,20): error CS9174: Cannot initialize type 'object' with a collection expression because the type is not constructible. // object x = [1]; Diagnostic(ErrorCode.ERR_CollectionExpressionTargetTypeNotConstructible, "[1]").WithArguments("object").WithLocation(5, 20), // (6,21): error CS9174: Cannot initialize type 'dynamic' with a collection expression because the type is not constructible. // dynamic y = [2]; Diagnostic(ErrorCode.ERR_CollectionExpressionTargetTypeNotConstructible, "[2]").WithArguments("dynamic").WithLocation(6, 21), // (7,17): error CS9176: There is no target type for the collection expression. // var z = [3]; Diagnostic(ErrorCode.ERR_CollectionExpressionNoTargetType, "[3]").WithLocation(7, 17)); var tree = comp.SyntaxTrees[0]; var model = comp.GetSemanticModel(tree); var collections = tree.GetRoot().DescendantNodes().OfType<CollectionExpressionSyntax>().ToArray(); Assert.Equal(3, collections.Length); VerifyTypes(model, collections[0], expectedType: null, expectedConvertedType: "System.Object", ConversionKind.NoConversion); VerifyTypes(model, collections[1], expectedType: null, expectedConvertedType: "dynamic", ConversionKind.NoConversion); VerifyTypes(model, collections[2], expectedType: null, expectedConvertedType: null, ConversionKind.Identity); } [Fact] public void NaturalType_03() { string source = """ class Program { static void Main() { object x = [1, ""]; dynamic y = [2, ""]; var z = [3, ""]; } } """; var comp = CreateCompilation(source); comp.VerifyEmitDiagnostics( // (5,20): error CS9174: Cannot initialize type 'object' with a collection expression because the type is not constructible. // object x = [1, ""]; Diagnostic(ErrorCode.ERR_CollectionExpressionTargetTypeNotConstructible, @"[1, """"]").WithArguments("object").WithLocation(5, 20), // (6,21): error CS9174: Cannot initialize type 'dynamic' with a collection expression because the type is not constructible. // dynamic y = [2, ""]; Diagnostic(ErrorCode.ERR_CollectionExpressionTargetTypeNotConstructible, @"[2, """"]").WithArguments("dynamic").WithLocation(6, 21), // (7,17): error CS9176: There is no target type for the collection expression. // var z = [3, ""]; Diagnostic(ErrorCode.ERR_CollectionExpressionNoTargetType, @"[3, """"]").WithLocation(7, 17)); } [Fact] public void NaturalType_04() { string source = """ class Program { static void Main() { object x = [null]; dynamic y = [null]; var z = [null]; int?[] w = [null]; } } """; var comp = CreateCompilation(source); comp.VerifyEmitDiagnostics( // (5,20): error CS9174: Cannot initialize type 'object' with a collection expression because the type is not constructible. // object x = [null]; Diagnostic(ErrorCode.ERR_CollectionExpressionTargetTypeNotConstructible, "[null]").WithArguments("object").WithLocation(5, 20), // (6,21): error CS9174: Cannot initialize type 'dynamic' with a collection expression because the type is not constructible. // dynamic y = [null]; Diagnostic(ErrorCode.ERR_CollectionExpressionTargetTypeNotConstructible, "[null]").WithArguments("dynamic").WithLocation(6, 21), // (7,17): error CS9176: There is no target type for the collection expression. // var z = [null]; Diagnostic(ErrorCode.ERR_CollectionExpressionNoTargetType, "[null]").WithLocation(7, 17)); } [Fact] public void NaturalType_05() { string source = """ class Program { static void Main() { var x = [1, 2, null]; object y = [1, 2, null]; dynamic z = [1, 2, null]; int?[] w = [1, 2, null]; } } """; var comp = CreateCompilation(source); comp.VerifyEmitDiagnostics( // (5,17): error CS9176: There is no target type for the collection expression. // var x = [1, 2, null]; Diagnostic(ErrorCode.ERR_CollectionExpressionNoTargetType, "[1, 2, null]").WithLocation(5, 17), // (6,20): error CS9174: Cannot initialize type 'object' with a collection expression because the type is not constructible. // object y = [1, 2, null]; Diagnostic(ErrorCode.ERR_CollectionExpressionTargetTypeNotConstructible, "[1, 2, null]").WithArguments("object").WithLocation(6, 20), // (7,21): error CS9174: Cannot initialize type 'dynamic' with a collection expression because the type is not constructible. // dynamic z = [1, 2, null]; Diagnostic(ErrorCode.ERR_CollectionExpressionTargetTypeNotConstructible, "[1, 2, null]").WithArguments("dynamic").WithLocation(7, 21)); } [Fact] public void NaturalType_06() { string source = """ class Program { static void Main() { object[] x = [[]]; } } """; var comp = CreateCompilation(source); comp.VerifyEmitDiagnostics( // (5,23): error CS9174: Cannot initialize type 'object' with a collection expression because the type is not constructible. // object[] x = [[]]; Diagnostic(ErrorCode.ERR_CollectionExpressionTargetTypeNotConstructible, "[]").WithArguments("object").WithLocation(5, 23)); } [Fact] public void NaturalType_07() { string source = """ class Program { static void Main() { object[] y = [[2]]; } } """; var comp = CreateCompilation(source); comp.VerifyEmitDiagnostics( // (5,23): error CS9174: Cannot initialize type 'object' with a collection expression because the type is not constructible. // object[] y = [[2]]; Diagnostic(ErrorCode.ERR_CollectionExpressionTargetTypeNotConstructible, "[2]").WithArguments("object").WithLocation(5, 23)); } [Fact] public void NaturalType_08() { string source = """ class Program { static void Main() { var z = [[3]]; } } """; var comp = CreateCompilation(source); comp.VerifyEmitDiagnostics( // (5,17): error CS9176: There is no target type for the collection expression. // var z = [[3]]; Diagnostic(ErrorCode.ERR_CollectionExpressionNoTargetType, "[[3]]").WithLocation(5, 17)); } [Fact] public void NaturalType_09() { string source = """ class Program { static void Main() { F([]); F([[]]); } static T F<T>(T t) => t; } """; var comp = CreateCompilation(source); comp.VerifyEmitDiagnostics( // (5,9): error CS0411: The type arguments for method 'Program.F<T>(T)' cannot be inferred from the usage. Try specifying the type arguments explicitly. // F([]); Diagnostic(ErrorCode.ERR_CantInferMethTypeArgs, "F").WithArguments("Program.F<T>(T)").WithLocation(5, 9), // (6,9): error CS0411: The type arguments for method 'Program.F<T>(T)' cannot be inferred from the usage. Try specifying the type arguments explicitly. // F([[]]); Diagnostic(ErrorCode.ERR_CantInferMethTypeArgs, "F").WithArguments("Program.F<T>(T)").WithLocation(6, 9)); } [Fact] public void NaturalType_10() { string source = """ class Program { static void Main() { F([1, 2]).Report(); F([[3, 4]]).Report(); } static T F<T>(T t) => t; } """; var comp = CreateCompilation([source, s_collectionExtensions]); comp.VerifyEmitDiagnostics( // 0.cs(5,9): error CS0411: The type arguments for method 'Program.F<T>(T)' cannot be inferred from the usage. Try specifying the type arguments explicitly. // F([1, 2]).Report(); Diagnostic(ErrorCode.ERR_CantInferMethTypeArgs, "F").WithArguments("Program.F<T>(T)").WithLocation(5, 9), // 0.cs(6,9): error CS0411: The type arguments for method 'Program.F<T>(T)' cannot be inferred from the usage. Try specifying the type arguments explicitly. // F([[3, 4]]).Report(); Diagnostic(ErrorCode.ERR_CantInferMethTypeArgs, "F").WithArguments("Program.F<T>(T)").WithLocation(6, 9)); } [Fact] public void NaturalType_11() { string source = """ using System; class Program { static void Main() { var d1 = () => []; Func<int[]> d2 = () => []; } } """; var comp = CreateCompilation(source); comp.VerifyEmitDiagnostics( // (6,18): error CS8917: The delegate type could not be inferred. // var d1 = () => []; Diagnostic(ErrorCode.ERR_CannotInferDelegateType, "() => []").WithLocation(6, 18)); } [Fact] public void InterfaceType_01() { string source = """ using System.Collections; class Program { static void Main() { IEnumerable a = [1]; ICollection b = [2]; IList c = [3]; a.Report(includeType: true); b.Report(includeType: true); c.Report(includeType: true); } } """; var comp = CreateCompilation([source, s_collectionExtensions]); comp.VerifyEmitDiagnostics( // 0.cs(6,25): error CS9174: Cannot initialize type 'IEnumerable' with a collection expression because the type is not constructible. // IEnumerable a = [1]; Diagnostic(ErrorCode.ERR_CollectionExpressionTargetTypeNotConstructible, "[1]").WithArguments("System.Collections.IEnumerable").WithLocation(6, 25), // 0.cs(7,25): error CS9174: Cannot initialize type 'ICollection' with a collection expression because the type is not constructible. // ICollection b = [2]; Diagnostic(ErrorCode.ERR_CollectionExpressionTargetTypeNotConstructible, "[2]").WithArguments("System.Collections.ICollection").WithLocation(7, 25), // 0.cs(8,19): error CS9174: Cannot initialize type 'IList' with a collection expression because the type is not constructible. // IList c = [3]; Diagnostic(ErrorCode.ERR_CollectionExpressionTargetTypeNotConstructible, "[3]").WithArguments("System.Collections.IList").WithLocation(8, 19)); } [Fact] public void InterfaceType_02() { string source = """ using System.Collections.Generic; class Program { static void Main() { IEnumerable<int> a = []; ICollection<int> b = []; IList<int> c = []; IReadOnlyCollection<int> d = []; IReadOnlyList<int> e = []; a.Report(includeType: true); b.Report(includeType: true); c.Report(includeType: true); d.Report(includeType: true); e.Report(includeType: true); } } """; CompileAndVerify( [source, s_collectionExtensions], expectedOutput: "(System.Int32[]) [], (System.Collections.Generic.List<System.Int32>) [], (System.Collections.Generic.List<System.Int32>) [], (System.Int32[]) [], (System.Int32[]) [], "); } [Fact] public void InterfaceType_03() { string source = """ using System.Collections.Generic; class Program { static void Main() { IEnumerable<int> a = [1]; ICollection<int> b = [2]; IList<int> c = [3]; IReadOnlyCollection<int> d = [4]; IReadOnlyList<int> e = [5]; a.Report(includeType: true); b.Report(includeType: true); c.Report(includeType: true); d.Report(includeType: true); e.Report(includeType: true); } } """; CompileAndVerify( [source, s_collectionExtensions], expectedOutput: "(<>z__ReadOnlySingleElementList<System.Int32>) [1], (System.Collections.Generic.List<System.Int32>) [2], (System.Collections.Generic.List<System.Int32>) [3], (<>z__ReadOnlySingleElementList<System.Int32>) [4], (<>z__ReadOnlySingleElementList<System.Int32>) [5], "); } [Fact] public void NaturalType_23() { string source = """ class Program { static void Main() { var x = [null, 1]; object y = [null, 2]; int?[] z = [null, 3]; } } """; var comp = CreateCompilation(source); comp.VerifyEmitDiagnostics( // (5,17): error CS9176: There is no target type for the collection expression. // var x = [null, 1]; Diagnostic(ErrorCode.ERR_CollectionExpressionNoTargetType, "[null, 1]").WithLocation(5, 17), // (6,20): error CS9174: Cannot initialize type 'object' with a collection expression because the type is not constructible. // object y = [null, 2]; Diagnostic(ErrorCode.ERR_CollectionExpressionTargetTypeNotConstructible, "[null, 2]").WithArguments("object").WithLocation(6, 20)); } [Fact] public void NaturalType_24() { string source = """ class Program { static void F1(int i) { (string, int)[] x1 = [(null, default)]; string[] y1 = [i switch { _ => default }]; string[] z1 = [i == 0 ? null : default]; } static void F2(int i) /*<bind>*/ { var x2 = [(null, default)]; var y2 = [i switch { _ => default }]; var z2 = [i == 0 ? null : default]; } /*</bind>*/ } """; var comp = CreateCompilation(source); comp.VerifyEmitDiagnostics( // (12,18): error CS9176: There is no target type for the collection expression. // var x2 = [(null, default)]; Diagnostic(ErrorCode.ERR_CollectionExpressionNoTargetType, "[(null, default)]").WithLocation(12, 18), // (13,18): error CS9176: There is no target type for the collection expression. // var y2 = [i switch { _ => default }]; Diagnostic(ErrorCode.ERR_CollectionExpressionNoTargetType, "[i switch { _ => default }]").WithLocation(13, 18), // (13,21): error CS8506: No best type was found for the switch expression. // var y2 = [i switch { _ => default }]; Diagnostic(ErrorCode.ERR_SwitchExpressionNoBestType, "switch").WithLocation(13, 21), // (14,18): error CS9176: There is no target type for the collection expression. // var z2 = [i == 0 ? null : default]; Diagnostic(ErrorCode.ERR_CollectionExpressionNoTargetType, "[i == 0 ? null : default]").WithLocation(14, 18), // (14,19): error CS0173: Type of conditional expression cannot be determined because there is no implicit conversion between '<null>' and 'default' // var z2 = [i == 0 ? null : default]; Diagnostic(ErrorCode.ERR_InvalidQM, "i == 0 ? null : default").WithArguments("<null>", "default").WithLocation(14, 19)); VerifyOperationTreeForTest<BlockSyntax>(comp, """ IBlockOperation (3 statements, 3 locals) (OperationKind.Block, Type: null, IsInvalid) (Syntax: '{ ... }') Locals: Local_1: ? x2 Local_2: ? y2 Local_3: ? z2 IVariableDeclarationGroupOperation (1 declarations) (OperationKind.VariableDeclarationGroup, Type: null, IsInvalid) (Syntax: 'var x2 = [( ... default)];') IVariableDeclarationOperation (1 declarators) (OperationKind.VariableDeclaration, Type: null, IsInvalid) (Syntax: 'var x2 = [( ... , default)]') Declarators: IVariableDeclaratorOperation (Symbol: ? x2) (OperationKind.VariableDeclarator, Type: null, IsInvalid) (Syntax: 'x2 = [(null, default)]') Initializer: IVariableInitializerOperation (OperationKind.VariableInitializer, Type: null, IsInvalid) (Syntax: '= [(null, default)]') ICollectionExpressionOperation (1 elements, ConstructMethod: null) (OperationKind.CollectionExpression, Type: ?, IsInvalid) (Syntax: '[(null, default)]') Elements(1): ITupleOperation (OperationKind.Tuple, Type: null, IsInvalid) (Syntax: '(null, default)') NaturalType: null Elements(2): ILiteralOperation (OperationKind.Literal, Type: null, Constant: null, IsInvalid) (Syntax: 'null') IDefaultValueOperation (OperationKind.DefaultValue, Type: ?, IsInvalid) (Syntax: 'default') Initializer: null IVariableDeclarationGroupOperation (1 declarations) (OperationKind.VariableDeclarationGroup, Type: null, IsInvalid) (Syntax: 'var y2 = [i ... default }];') IVariableDeclarationOperation (1 declarators) (OperationKind.VariableDeclaration, Type: null, IsInvalid) (Syntax: 'var y2 = [i ... default }]') Declarators: IVariableDeclaratorOperation (Symbol: ? y2) (OperationKind.VariableDeclarator, Type: null, IsInvalid) (Syntax: 'y2 = [i swi ... default }]') Initializer: IVariableInitializerOperation (OperationKind.VariableInitializer, Type: null, IsInvalid) (Syntax: '= [i switch ... default }]') ICollectionExpressionOperation (1 elements, ConstructMethod: null) (OperationKind.CollectionExpression, Type: ?, IsInvalid) (Syntax: '[i switch { ... default }]') Elements(1): ISwitchExpressionOperation (1 arms, IsExhaustive: True) (OperationKind.SwitchExpression, Type: ?, IsInvalid) (Syntax: 'i switch { ... > default }') Value: IParameterReferenceOperation: i (OperationKind.ParameterReference, Type: System.Int32, IsInvalid) (Syntax: 'i') Arms(1): ISwitchExpressionArmOperation (0 locals) (OperationKind.SwitchExpressionArm, Type: null, IsInvalid) (Syntax: '_ => default') Pattern: IDiscardPatternOperation (OperationKind.DiscardPattern, Type: null, IsInvalid) (Syntax: '_') (InputType: System.Int32, NarrowedType: System.Int32) Value: IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: ?, IsInvalid, IsImplicit) (Syntax: 'default') Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) Operand: IDefaultValueOperation (OperationKind.DefaultValue, Type: ?, IsInvalid) (Syntax: 'default') Initializer: null IVariableDeclarationGroupOperation (1 declarations) (OperationKind.VariableDeclarationGroup, Type: null, IsInvalid) (Syntax: 'var z2 = [i ... : default];') IVariableDeclarationOperation (1 declarators) (OperationKind.VariableDeclaration, Type: null, IsInvalid) (Syntax: 'var z2 = [i ... : default]') Declarators: IVariableDeclaratorOperation (Symbol: ? z2) (OperationKind.VariableDeclarator, Type: null, IsInvalid) (Syntax: 'z2 = [i == ... : default]') Initializer: IVariableInitializerOperation (OperationKind.VariableInitializer, Type: null, IsInvalid) (Syntax: '= [i == 0 ? ... : default]') ICollectionExpressionOperation (1 elements, ConstructMethod: null) (OperationKind.CollectionExpression, Type: ?, IsInvalid) (Syntax: '[i == 0 ? n ... : default]') Elements(1): IConditionalOperation (OperationKind.Conditional, Type: ?, IsInvalid) (Syntax: 'i == 0 ? null : default') Condition: IBinaryOperation (BinaryOperatorKind.Equals) (OperationKind.Binary, Type: System.Boolean, IsInvalid) (Syntax: 'i == 0') Left: IParameterReferenceOperation: i (OperationKind.ParameterReference, Type: System.Int32, IsInvalid) (Syntax: 'i') Right: ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 0, IsInvalid) (Syntax: '0') WhenTrue: IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: ?, Constant: null, IsInvalid, IsImplicit) (Syntax: 'null') Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: True, IsUserDefined: False) (MethodSymbol: null) Operand: ILiteralOperation (OperationKind.Literal, Type: null, Constant: null, IsInvalid) (Syntax: 'null') WhenFalse: IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: ?, IsInvalid, IsImplicit) (Syntax: 'default') Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) Operand: IDefaultValueOperation (OperationKind.DefaultValue, Type: ?, IsInvalid) (Syntax: 'default') Initializer: null """); } [Fact] public void TargetType_01() { string source = """ class Program { static void F(bool b, object o) { int[] a1 = b ? [1] : []; int[] a2 = b? [] : [2]; object[] a3 = b ? [3] : [o]; object[] a4 = b ? [o] : [4]; int?[] a5 = b ? [5] : [null]; int?[] a6 = b ? [null] : [6]; } } """; var comp = CreateCompilation(source); comp.VerifyEmitDiagnostics(); } [Fact] public void TargetType_02() { string source = """ using System; class Program { static void F(bool b, object o) { Func<int[]> f1 = () => { if (b) return [1]; return []; }; Func<int[]> f2 = () => { if (b) return []; return [2]; }; Func<object[]> f3 = () => { if (b) return [3]; return [o]; }; Func<object[]> f4 = () => { if (b) return [o]; return [4]; }; Func<int?[]> f5 = () => { if (b) return [5]; return [null]; }; Func<int?[]> f6 = () => { if (b) return [null]; return [6]; }; } } """; var comp = CreateCompilation(source); comp.VerifyEmitDiagnostics(); } // Overload resolution should choose array over interface. [Fact] public void OverloadResolution_01() { string source = """ using System.Collections.Generic; class Program { static IEnumerable<int> F1(IEnumerable<int> arg) => arg; static int[] F1(int[] arg) => arg; static int[] F2(int[] arg) => arg; static IEnumerable<int> F2(IEnumerable<int> arg) => arg; static void Main() { var x = F1([]); var y = F2([1, 2]); x.Report(includeType: true); y.Report(includeType: true); } } """; CompileAndVerify(new[] { source, s_collectionExtensions }, expectedOutput: "(System.Int32[]) [], (System.Int32[]) [1, 2], "); } // Overload resolution should choose collection initializer type over interface. [Fact] public void OverloadResolution_02() { string source = """ using System.Collections.Generic; class Program { static IEnumerable<int> F1(IEnumerable<int> arg) => arg; static List<int> F1(List<int> arg) => arg; static List<int> F2(List<int> arg) => arg; static IEnumerable<int> F2(IEnumerable<int> arg) => arg; static void Main() { var x = F1([]); var y = F2([1, 2]); x.Report(includeType: true); y.Report(includeType: true); } } """; CompileAndVerify(new[] { source, s_collectionExtensions }, expectedOutput: "(System.Collections.Generic.List<System.Int32>) [], (System.Collections.Generic.List<System.Int32>) [1, 2], "); } [Fact] public void OverloadResolution_03() { string source = """ using System.Collections.Generic; class Program { static List<int> F(List<int> arg) => arg; static int[] F(int[] arg) => arg; static void Main() { var x = F([]); } } """; var comp = CreateCompilation(source); comp.VerifyEmitDiagnostics( // (8,17): error CS0121: The call is ambiguous between the following methods or properties: 'Program.F(List<int>)' and 'Program.F(int[])' // var x = F([]); Diagnostic(ErrorCode.ERR_AmbigCall, "F").WithArguments("Program.F(System.Collections.Generic.List<int>)", "Program.F(int[])").WithLocation(8, 17)); } [Fact] public void OverloadResolution_04() { string source = """ using System.Collections.Generic; class Program { static List<int> F1(List<int> arg) => arg; static int[] F1(int[] arg) => arg; static int[] F2(int[] arg) => arg; static List<int> F2(List<int> arg) => arg; static void Main() { var x = F1([1]); var y = F2([2]); } } """; var comp = CreateCompilation(source); comp.VerifyEmitDiagnostics( // (10,17): error CS0121: The call is ambiguous between the following methods or properties: 'Program.F1(List<int>)' and 'Program.F1(int[])' // var x = F1([1]); Diagnostic(ErrorCode.ERR_AmbigCall, "F1").WithArguments("Program.F1(System.Collections.Generic.List<int>)", "Program.F1(int[])").WithLocation(10, 17), // (11,17): error CS0121: The call is ambiguous between the following methods or properties: 'Program.F2(int[])' and 'Program.F2(List<int>)' // var y = F2([2]); Diagnostic(ErrorCode.ERR_AmbigCall, "F2").WithArguments("Program.F2(int[])", "Program.F2(System.Collections.Generic.List<int>)").WithLocation(11, 17)); } [Fact] public void OverloadResolution_05() { string source = """ using System.Collections.Generic; class Program { static List<int> F1(List<int> arg) => arg; static List<long?> F1(List<long?> arg) => arg; static List<long?> F2(List<long?> arg) => arg; static List<int> F2(List<int> arg) => arg; static void Main() { var x = F1([1]); var y = F2([2]); x.Report(includeType: true); y.Report(includeType: true); } } """; var expectedOutput = "(System.Collections.Generic.List<System.Int32>) [1], (System.Collections.Generic.List<System.Int32>) [2], "; CompileAndVerify([source, s_collectionExtensions], parseOptions: TestOptions.Regular13, expectedOutput: expectedOutput); CompileAndVerify([source, s_collectionExtensions], parseOptions: TestOptions.RegularPreview, expectedOutput: expectedOutput); CreateCompilation(source, parseOptions: TestOptions.Regular12).VerifyEmitDiagnostics( // (10,17): error CS0121: The call is ambiguous between the following methods or properties: 'Program.F1(List<int>)' and 'Program.F1(List<long?>)' // var x = F1([1]); Diagnostic(ErrorCode.ERR_AmbigCall, "F1").WithArguments("Program.F1(System.Collections.Generic.List<int>)", "Program.F1(System.Collections.Generic.List<long?>)").WithLocation(10, 17), // (11,17): error CS0121: The call is ambiguous between the following methods or properties: 'Program.F2(List<long?>)' and 'Program.F2(List<int>)' // var y = F2([2]); Diagnostic(ErrorCode.ERR_AmbigCall, "F2").WithArguments("Program.F2(System.Collections.Generic.List<long?>)", "Program.F2(System.Collections.Generic.List<int>)").WithLocation(11, 17)); } [Fact] public void OverloadResolution_06() { string source = """ using System.Collections.Generic; class Program { static List<int?> F1(List<int?> arg) => arg; static List<long> F1(List<long> arg) => arg; static List<long> F2(List<long> arg) => arg; static List<int?> F2(List<int?> arg) => arg; static void Main() { var x = F1([1]); var y = F2([2]); } } """; var comp = CreateCompilation(source); comp.VerifyEmitDiagnostics( // 0.cs(10,17): error CS0121: The call is ambiguous between the following methods or properties: 'Program.F1(List<int?>)' and 'Program.F1(List<long>)' // var x = F1([1]); Diagnostic(ErrorCode.ERR_AmbigCall, "F1").WithArguments("Program.F1(System.Collections.Generic.List<int?>)", "Program.F1(System.Collections.Generic.List<long>)").WithLocation(10, 17), // 0.cs(11,17): error CS0121: The call is ambiguous between the following methods or properties: 'Program.F2(List<long>)' and 'Program.F2(List<int?>)' // var y = F2([2]); Diagnostic(ErrorCode.ERR_AmbigCall, "F2").WithArguments("Program.F2(System.Collections.Generic.List<long>)", "Program.F2(System.Collections.Generic.List<int?>)").WithLocation(11, 17)); } [Fact] public void OverloadResolution_07() { string source = """ using System.Collections; using System.Collections.Generic; struct S : IEnumerable<int> { IEnumerator<int> IEnumerable<int>.GetEnumerator() => null; IEnumerator IEnumerable.GetEnumerator() => null; public void Add(int i) { } } class Program { static S F1(S arg) => arg; static List<int> F1(List<int> arg) => arg; static List<int> F2(List<int> arg) => arg; static S F2(S arg) => arg; static void Main() { var x = F1([1]); var y = F2([2]); } } """; var comp = CreateCompilation(source); comp.VerifyEmitDiagnostics( // (17,17): error CS0121: The call is ambiguous between the following methods or properties: 'Program.F1(S)' and 'Program.F1(List<int>)' // var x = F1([1]); Diagnostic(ErrorCode.ERR_AmbigCall, "F1").WithArguments("Program.F1(S)", "Program.F1(System.Collections.Generic.List<int>)").WithLocation(17, 17), // (18,17): error CS0121: The call is ambiguous between the following methods or properties: 'Program.F2(List<int>)' and 'Program.F2(S)' // var y = F2([2]); Diagnostic(ErrorCode.ERR_AmbigCall, "F2").WithArguments("Program.F2(System.Collections.Generic.List<int>)", "Program.F2(S)").WithLocation(18, 17)); } [Fact] public void OverloadResolution_08() { string source = """ using System.Collections.Generic; class Program { static IEnumerable<T> F1<T>(IEnumerable<T> arg) => arg; static T[] F1<T>(T[] arg) => arg; static T[] F2<T>(T[] arg) => arg; static IEnumerable<T> F2<T>(IEnumerable<T> arg) => arg; static void Main() { var x = F1([1]); var y = F2([2]); x.Report(includeType: true); y.Report(includeType: true); } } """; CompileAndVerify(new[] { source, s_collectionExtensions }, expectedOutput: "(System.Int32[]) [1], (System.Int32[]) [2], "); } [Fact] public void OverloadResolution_09() { string source = """ using System.Collections.Generic; class Program { static int[] F1(int[] arg) => arg; static string[] F1(string[] arg) => arg; static List<int> F2(List<int> arg) => arg; static List<string> F2(List<string> arg) => arg; static string[] F3(string[] arg) => arg; static List<int?> F3(List<int?> arg) => arg; static void Main() { F1([]); F2([]); F3([null]); } } """; var comp = CreateCompilation(source); comp.VerifyEmitDiagnostics( // (12,9): error CS0121: The call is ambiguous between the following methods or properties: 'Program.F1(int[])' and 'Program.F1(string[])' // F1([]); Diagnostic(ErrorCode.ERR_AmbigCall, "F1").WithArguments("Program.F1(int[])", "Program.F1(string[])").WithLocation(12, 9), // (13,9): error CS0121: The call is ambiguous between the following methods or properties: 'Program.F2(List<int>)' and 'Program.F2(List<string>)' // F2([]); Diagnostic(ErrorCode.ERR_AmbigCall, "F2").WithArguments("Program.F2(System.Collections.Generic.List<int>)", "Program.F2(System.Collections.Generic.List<string>)").WithLocation(13, 9), // (14,9): error CS0121: The call is ambiguous between the following methods or properties: 'Program.F3(string[])' and 'Program.F3(List<int?>)' // F3([null]); Diagnostic(ErrorCode.ERR_AmbigCall, "F3").WithArguments("Program.F3(string[])", "Program.F3(System.Collections.Generic.List<int?>)").WithLocation(14, 9)); } [Fact] public void OverloadResolution_ElementConversions_01() { string source = """ class Program { static string[] F(string[] arg) => arg; static int?[] F(int?[] arg) => arg; static void Main() { var x = F([null, 2, 3]); x.Report(includeType: true); } } """; CompileAndVerify( new[] { source, s_collectionExtensions }, expectedOutput: "(System.Nullable<System.Int32>[]) [null, 2, 3], "); } [Fact] public void OverloadResolution_ElementConversions_02() { string source = """ class Program { static string[] F(string[] arg) => arg; static int?[] F(int?[] arg) => arg; static void Main() { int?[] x = [null, 2, 3]; var y = F([..x]); y.Report(includeType: true); } } """; CompileAndVerify( new[] { source, s_collectionExtensions }, expectedOutput: "(System.Nullable<System.Int32>[]) [null, 2, 3], "); } [Fact] public void OverloadResolution_ElementConversions_03() { string source = """ using System.Collections; using System.Collections.Generic; class MyCollection : IEnumerable { private List<object> _items = new(); IEnumerator IEnumerable.GetEnumerator() => _items.GetEnumerator(); public void Add(object o) { _items.Add(o); } } class Program { static MyCollection F(MyCollection arg) => arg; static int?[] F(int?[] arg) => arg; static void Main() { var x = F([1, null]); x.Report(includeType: true); int?[] y = [null, 2]; var z = F([..y]); z.Report(includeType: true); F([3, ..y]).Report(includeType: true); F([..y, 4]).Report(includeType: true); int[] w = [5, 6, 7]; F([..y, ..w]).Report(includeType: true); F([..w, ..y]).Report(includeType: true); } } """; var expectedOutput = "(System.Nullable<System.Int32>[]) [1, null], (System.Nullable<System.Int32>[]) [null, 2], (System.Nullable<System.Int32>[]) [3, null, 2], " + "(System.Nullable<System.Int32>[]) [null, 2, 4], (System.Nullable<System.Int32>[]) [null, 2, 5, 6, 7], (System.Nullable<System.Int32>[]) [5, 6, 7, null, 2], "; CompileAndVerify([source, s_collectionExtensions], parseOptions: TestOptions.Regular13, expectedOutput: expectedOutput); CompileAndVerify([source, s_collectionExtensions], parseOptions: TestOptions.RegularPreview, expectedOutput: expectedOutput); CreateCompilation(source, parseOptions: TestOptions.Regular12).VerifyEmitDiagnostics( // (15,17): error CS0121: The call is ambiguous between the following methods or properties: 'Program.F(MyCollection)' and 'Program.F(int?[])' // var x = F([1, null]); Diagnostic(ErrorCode.ERR_AmbigCall, "F").WithArguments("Program.F(MyCollection)", "Program.F(int?[])").WithLocation(15, 17), // (18,17): error CS0121: The call is ambiguous between the following methods or properties: 'Program.F(MyCollection)' and 'Program.F(int?[])' // var z = F([..y]); Diagnostic(ErrorCode.ERR_AmbigCall, "F").WithArguments("Program.F(MyCollection)", "Program.F(int?[])").WithLocation(18, 17), // (20,9): error CS0121: The call is ambiguous between the following methods or properties: 'Program.F(MyCollection)' and 'Program.F(int?[])' // F([3, ..y]).Report(includeType: true); Diagnostic(ErrorCode.ERR_AmbigCall, "F").WithArguments("Program.F(MyCollection)", "Program.F(int?[])").WithLocation(20, 9), // (21,9): error CS0121: The call is ambiguous between the following methods or properties: 'Program.F(MyCollection)' and 'Program.F(int?[])' // F([..y, 4]).Report(includeType: true); Diagnostic(ErrorCode.ERR_AmbigCall, "F").WithArguments("Program.F(MyCollection)", "Program.F(int?[])").WithLocation(21, 9), // (23,9): error CS0121: The call is ambiguous between the following methods or properties: 'Program.F(MyCollection)' and 'Program.F(int?[])' // F([..y, ..w]).Report(includeType: true); Diagnostic(ErrorCode.ERR_AmbigCall, "F").WithArguments("Program.F(MyCollection)", "Program.F(int?[])").WithLocation(23, 9), // (24,9): error CS0121: The call is ambiguous between the following methods or properties: 'Program.F(MyCollection)' and 'Program.F(int?[])' // F([..w, ..y]).Report(includeType: true); Diagnostic(ErrorCode.ERR_AmbigCall, "F").WithArguments("Program.F(MyCollection)", "Program.F(int?[])").WithLocation(24, 9) ); } [Fact] public void OverloadResolution_ElementConversions_04() { string source = """ using System.Collections; using System.Collections.Generic; class MyCollection : IEnumerable<int?> { private List<int?> _items = new(); IEnumerator<int?> IEnumerable<int?>.GetEnumerator() => _items.GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => _items.GetEnumerator(); public void Add(int? i) { _items.Add(i); } } class Program { static MyCollection F(MyCollection arg) => arg; static int[] F(int[] arg) => arg; static void Main() { var x = F([1, null]); x.Report(includeType: true); int?[] y = [null, 2]; var z = F([..y]); z.Report(includeType: true); } } """; CompileAndVerify( new[] { source, s_collectionExtensions }, expectedOutput: "(MyCollection) [1, null], (MyCollection) [null, 2], "); } [Fact] public void OverloadResolution_ElementConversions_05() { string source = """ using System.Collections; using System.Collections.Generic; class MyCollection1 : IEnumerable { private List<object> _items = new(); IEnumerator IEnumerable.GetEnumerator() => _items.GetEnumerator(); public void Add(object o) { _items.Add(o); } public void Add(int? i) { _items.Add(i); } } class MyCollection2 : IEnumerable { private List<object> _items = new(); IEnumerator IEnumerable.GetEnumerator() => _items.GetEnumerator(); public void Add(object o) { _items.Add(o); } public void Add(int i) { _items.Add(i); } public void Add(string s) { _items.Add(s); } } class Program { static MyCollection1 F(MyCollection1 arg) => arg; static MyCollection2 F(MyCollection2 arg) => arg; static void Main() { var x = F([1, (string)null]); int?[] y = [null, 2]; var z = F([..y]); } } """; CreateCompilation(source).VerifyEmitDiagnostics( // (24,17): error CS0121: The call is ambiguous between the following methods or properties: 'Program.F(MyCollection1)' and 'Program.F(MyCollection2)' // var x = F([1, (string)null]); Diagnostic(ErrorCode.ERR_AmbigCall, "F").WithArguments("Program.F(MyCollection1)", "Program.F(MyCollection2)").WithLocation(24, 17), // (26,17): error CS0121: The call is ambiguous between the following methods or properties: 'Program.F(MyCollection1)' and 'Program.F(MyCollection2)' // var z = F([..y]); Diagnostic(ErrorCode.ERR_AmbigCall, "F").WithArguments("Program.F(MyCollection1)", "Program.F(MyCollection2)").WithLocation(26, 17) ); } [Fact] public void OverloadResolution_ElementConversions_06() { string sourceA = """ using System; using System.Collections; using System.Collections.Generic; using System.Runtime.CompilerServices; [CollectionBuilder(typeof(MyCollectionBuilder), nameof(MyCollectionBuilder.Create1))] class MyCollection1 : IEnumerable<int?> { private List<int?> _list; public MyCollection1(List<int?> list) { _list = list; } IEnumerator<int?> IEnumerable<int?>.GetEnumerator() => _list.GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => _list.GetEnumerator(); } [CollectionBuilder(typeof(MyCollectionBuilder), nameof(MyCollectionBuilder.Create2))] class MyCollection2 : IEnumerable<string> { private List<string> _list; public MyCollection2(List<string> list) { _list = list; } IEnumerator<string> IEnumerable<string>.GetEnumerator() => _list.GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => _list.GetEnumerator(); } class MyCollectionBuilder { public static MyCollection1 Create1(ReadOnlySpan<int?> items) => new MyCollection1(new(items.ToArray())); public static MyCollection2 Create2(ReadOnlySpan<string> items) => new MyCollection2(new(items.ToArray())); } """; string sourceB = """ class Program { static MyCollection1 F(MyCollection1 arg) => arg; static MyCollection2 F(MyCollection2 arg) => arg; static void Main() { var x = F([null, 2, 3]); x.Report(includeType: true); string[] y = [null]; var z = F([..y]); z.Report(includeType: true); } } """; CompileAndVerify( new[] { sourceA, sourceB, s_collectionExtensions, CollectionBuilderAttributeDefinition }, targetFramework: TargetFramework.Net80, verify: Verification.Fails, expectedOutput: IncludeExpectedOutput("(MyCollection1) [null, 2, 3], (MyCollection2) [null], ")); } [Fact] public void OverloadResolution_ElementConversions_07() { string source = """ using System.Collections.Generic; class Program { static ICollection<string> F(ICollection<string> arg) => arg; static ICollection<int?> F(ICollection<int?> arg) => arg; static void Main() { var x = F([null, 2, 3]); x.Report(includeType: true); string[] y = [null]; var z = F([..y]); z.Report(includeType: true); } } """; CompileAndVerify( new[] { source, s_collectionExtensions }, expectedOutput: "(System.Collections.Generic.List<System.Nullable<System.Int32>>) [null, 2, 3], (System.Collections.Generic.List<System.String>) [null], "); } [Fact] public void OverloadResolution_ArgumentErrors() { string source = """ using System.Linq; class Program { static void Main() { [Unknown2].Zip([Unknown1]); } } """; var comp = CreateCompilation(source); comp.VerifyEmitDiagnostics( // (6,10): error CS0103: The name 'Unknown2' does not exist in the current context // [Unknown2].Zip([Unknown1]); Diagnostic(ErrorCode.ERR_NameNotInContext, "Unknown2").WithArguments("Unknown2").WithLocation(6, 10), // (6,25): error CS0103: The name 'Unknown1' does not exist in the current context // [Unknown2].Zip([Unknown1]); Diagnostic(ErrorCode.ERR_NameNotInContext, "Unknown1").WithArguments("Unknown1").WithLocation(6, 25)); } internal const string example_RefStructCollection = """ using System; using System.Collections.Generic; using System.Runtime.CompilerServices; [CollectionBuilder(typeof(RefStructCollectionBuilder), nameof(RefStructCollectionBuilder.Create))] ref struct RefStructCollection<T> { public IEnumerator<T> GetEnumerator() => null; } static class RefStructCollectionBuilder { public static RefStructCollection<T> Create<T>(scoped ReadOnlySpan<T> items) => default; } """; internal const string example_GenericClassCollection = """ using System; using System.Collections.Generic; using System.Runtime.CompilerServices; [CollectionBuilder(typeof(GenericClassCollectionBuilder), nameof(GenericClassCollectionBuilder.Create))] class GenericClassCollection<T> { public IEnumerator<T> GetEnumerator() => null; } static class GenericClassCollectionBuilder { public static GenericClassCollection<T> Create<T>(ReadOnlySpan<T> items) => default; } """; internal const string example_NonGenericClassCollection = """ using System; using System.Collections.Generic; using System.Runtime.CompilerServices; [CollectionBuilder(typeof(NonGenericClassCollectionBuilder), nameof(NonGenericClassCollectionBuilder.Create))] class NonGenericClassCollection { public IEnumerator<object> GetEnumerator() => null; } static class NonGenericClassCollectionBuilder { public static NonGenericClassCollection Create(ReadOnlySpan<object> items) => default; } """; internal const string example_GenericClassesWithConversion = """ using System; using System.Collections; using System.Collections.Generic; public sealed class MyCollectionA<T> : IEnumerable<T> { public void Add(T t) { } IEnumerator<T> IEnumerable<T>.GetEnumerator() => null; IEnumerator IEnumerable.GetEnumerator() => null; } public sealed class MyCollectionB<T> : IEnumerable<T> { public void Add(T t) { } IEnumerator<T> IEnumerable<T>.GetEnumerator() => null; IEnumerator IEnumerable.GetEnumerator() => null; public static implicit operator MyCollectionA<T>(MyCollectionB<T> b) => default; } """; // Ref struct collection, with an implicit conversion from array. internal const string example_RefStructConvertibleFromArray = """ using System; using System.Collections.Generic; using System.Runtime.CompilerServices; [CollectionBuilder(typeof(RefStructConvertibleFromArrayBuilder), nameof(RefStructConvertibleFromArrayBuilder.Create))] public ref struct RefStructConvertibleFromArray<T> { private readonly T[] _array; public RefStructConvertibleFromArray(T[] array) { _array = array; } public IEnumerator<T> GetEnumerator() => new List<T>(_array).GetEnumerator(); public static implicit operator RefStructConvertibleFromArray<T>(T[] array) => new(array); } public static class RefStructConvertibleFromArrayBuilder { public static RefStructConvertibleFromArray<T> Create<T>(scoped ReadOnlySpan<T> items) { return new RefStructConvertibleFromArray<T>(items.ToArray()); } } """; [Theory] [InlineData("System.Span<T>", "T[]", "System.Span<System.Int32>", "System.Span<System.Int32>")] [InlineData("System.Span<T>", "int[]", "System.Span<System.Int32>", "System.Span<System.Int32>")] [InlineData("System.Span<T>", "System.Collections.Generic.IEnumerable<T>", "System.Span<System.Int32>", "System.Span<System.Int32>")] [InlineData("System.Span<T>", "System.Collections.Generic.IReadOnlyCollection<T>", "System.Span<System.Int32>", "System.Span<System.Int32>")] [InlineData("System.Span<T>", "System.Collections.Generic.IReadOnlyList<T>", "System.Span<System.Int32>", "System.Span<System.Int32>")] [InlineData("System.Span<T>", "System.Collections.Generic.ICollection<T>", "System.Span<System.Int32>", "System.Span<System.Int32>")] [InlineData("System.Span<T>", "System.Collections.Generic.IList<T>", "System.Span<System.Int32>", "System.Span<System.Int32>")] [InlineData("System.ReadOnlySpan<T>", "T[]", "System.ReadOnlySpan<System.Int32>", "System.ReadOnlySpan<System.Int32>")] [InlineData("System.ReadOnlySpan<T>", "System.Collections.Generic.IEnumerable<T>", "System.ReadOnlySpan<System.Int32>", "System.ReadOnlySpan<System.Int32>")] [InlineData("System.ReadOnlySpan<T>", "System.Collections.Generic.IReadOnlyCollection<T>", "System.ReadOnlySpan<System.Int32>", "System.ReadOnlySpan<System.Int32>")] [InlineData("System.ReadOnlySpan<T>", "System.Collections.Generic.IReadOnlyList<T>", "System.ReadOnlySpan<System.Int32>", "System.ReadOnlySpan<System.Int32>")] [InlineData("System.ReadOnlySpan<T>", "System.Collections.Generic.ICollection<T>", "System.ReadOnlySpan<System.Int32>", "System.ReadOnlySpan<System.Int32>")] [InlineData("System.ReadOnlySpan<T>", "System.Collections.Generic.IList<T>", "System.ReadOnlySpan<System.Int32>", "System.ReadOnlySpan<System.Int32>")] [InlineData("System.Span<T>", "System.Collections.Generic.HashSet<T>", null, null)] // rule requires array or array interface [InlineData("System.Span<T>", "System.ReadOnlySpan<object>", null, "System.Span<System.Int32>")] [InlineData("RefStructCollection<T>", "T[]", null, null, new[] { example_RefStructCollection })] [InlineData("RefStructCollection<T>", "RefStructCollection<object>", null, "RefStructCollection<System.Int32>", new[] { example_RefStructCollection })] [InlineData("RefStructCollection<int>", "GenericClassCollection<object>", null, "RefStructCollection<System.Int32>", new[] { example_RefStructCollection, example_GenericClassCollection })] [InlineData("RefStructCollection<object>", "GenericClassCollection<int>", null, "GenericClassCollection<System.Int32>", new[] { example_RefStructCollection, example_GenericClassCollection })] [InlineData("RefStructCollection<int>", "NonGenericClassCollection", null, "RefStructCollection<System.Int32>", new[] { example_RefStructCollection, example_NonGenericClassCollection })] [InlineData("GenericClassCollection<T>", "T[]", null, null, new[] { example_GenericClassCollection })] // rule requires span [InlineData("NonGenericClassCollection", "object[]", null, null, new[] { example_NonGenericClassCollection })] // rule requires span [InlineData("System.ReadOnlySpan<T>", "object[]", "System.ReadOnlySpan<System.Int32>", "System.ReadOnlySpan<System.Int32>")] [InlineData("System.ReadOnlySpan<T>", "long[]", "System.ReadOnlySpan<System.Int32>", "System.ReadOnlySpan<System.Int32>")] [InlineData("System.ReadOnlySpan<T>", "short[]", null, "System.ReadOnlySpan<System.Int32>")] [InlineData("System.ReadOnlySpan<T>", "int[]", "System.ReadOnlySpan<System.Int32>", "System.ReadOnlySpan<System.Int32>")] [InlineData("System.ReadOnlySpan<long>", "T[]", null, "System.Int32[]")] [InlineData("System.ReadOnlySpan<object>", "long[]", null, "System.Int64[]")] [InlineData("System.ReadOnlySpan<long>", "object[]", "System.ReadOnlySpan<System.Int64>", "System.ReadOnlySpan<System.Int64>")] [InlineData("System.ReadOnlySpan<long>", "string[]", "System.ReadOnlySpan<System.Int64>", "System.ReadOnlySpan<System.Int64>")] [InlineData("System.ReadOnlySpan<int>", "System.ReadOnlySpan<string>", "System.ReadOnlySpan<System.Int32>", "System.ReadOnlySpan<System.Int32>")] [InlineData("System.ReadOnlySpan<T>", "System.Span<T>", "System.ReadOnlySpan<System.Int32>", "System.ReadOnlySpan<System.Int32>")] [InlineData("System.ReadOnlySpan<T>", "System.Span<int>", "System.ReadOnlySpan<System.Int32>", "System.ReadOnlySpan<System.Int32>")] [InlineData("System.ReadOnlySpan<T>", "System.Span<object>", "System.ReadOnlySpan<System.Int32>", "System.ReadOnlySpan<System.Int32>")] [InlineData("System.ReadOnlySpan<T>", "System.Span<short>", null, "System.ReadOnlySpan<System.Int32>")] [InlineData("System.ReadOnlySpan<T>", "System.ReadOnlySpan<int>", "System.ReadOnlySpan<System.Int32>", "System.ReadOnlySpan<System.Int32>")] [InlineData("System.ReadOnlySpan<T>", "System.ReadOnlySpan<object>", null, "System.ReadOnlySpan<System.Int32>")] [InlineData("System.ReadOnlySpan<T>", "System.ReadOnlySpan<long>", null, "System.ReadOnlySpan<System.Int32>")] [InlineData("System.Span<T>", "System.Span<int>", "System.Span<System.Int32>", "System.Span<System.Int32>")] [InlineData("System.Span<T>", "System.Span<object>", null, "System.Span<System.Int32>")] [InlineData("System.Span<T>", "System.Span<short>", null, "System.Span<System.Int32>")] [InlineData("System.Span<T>", "System.Span<string>", "System.Span<System.Int32>", "System.Span<System.Int32>")] [InlineData("T[]", "int[]", "System.Int32[]", "System.Int32[]")] [InlineData("T[]", "object[]", null, "System.Int32[]")] [InlineData("T[]", "int?[]", null, "System.Int32[]")] [InlineData("System.Collections.Generic.ICollection<T>", "System.Collections.Generic.ICollection<int>", "System.Collections.Generic.ICollection<System.Int32>", "System.Collections.Generic.ICollection<System.Int32>")] [InlineData("System.Collections.Generic.ICollection<T>", "System.Collections.Generic.ICollection<object>", null, "System.Collections.Generic.ICollection<System.Int32>")] [InlineData("System.Collections.Generic.ICollection<T>", "System.Collections.Generic.ICollection<short>", null, "System.Collections.Generic.ICollection<System.Int32>")] [InlineData("System.Collections.Generic.ICollection<T>", "System.Collections.Generic.IReadOnlyCollection<T>", null, null)] [InlineData("MyCollectionA<T>", "MyCollectionB<T>", "MyCollectionB<System.Int32>", "MyCollectionB<System.Int32>", new[] { example_GenericClassesWithConversion })] [InlineData("MyCollectionA<int>", "MyCollectionB<T>", "MyCollectionB<System.Int32>", "MyCollectionB<System.Int32>", new[] { example_GenericClassesWithConversion })] [InlineData("MyCollectionA<T>", "MyCollectionB<long>", null, "MyCollectionA<System.Int32>", new[] { example_GenericClassesWithConversion })] [InlineData("MyCollectionA<T>", "MyCollectionB<object>", null, "MyCollectionA<System.Int32>", new[] { example_GenericClassesWithConversion })] [InlineData("MyCollectionB<T>", "MyCollectionB<long>", null, "MyCollectionB<System.Int32>", new[] { example_GenericClassesWithConversion })] [InlineData("RefStructConvertibleFromArray<T>", "T[]", "System.Int32[]", "System.Int32[]", new[] { example_RefStructConvertibleFromArray })] [InlineData("RefStructConvertibleFromArray<T>", "int[]", "System.Int32[]", "System.Int32[]", new[] { example_RefStructConvertibleFromArray })] [InlineData("RefStructConvertibleFromArray<object>", "T[]", null, "System.Int32[]", new[] { example_RefStructConvertibleFromArray })] [InlineData("RefStructConvertibleFromArray<T>", "object[]", null, "RefStructConvertibleFromArray<System.Int32>", new[] { example_RefStructConvertibleFromArray })] public void BetterConversionFromExpression_01A(string type1, string type2, string expectedType12, string expectedType13, string[] additionalSources = null) { string source = $$""" using System; class Program { {{generateMethod("F1", type1)}} {{generateMethod("F1", type2)}} {{generateMethod("F2", type2)}} {{generateMethod("F2", type1)}} static void Main() { var x = F1([1, 2, 3]); Console.WriteLine(x.GetTypeName()); var y = F2([4, 5]); Console.WriteLine(y.GetTypeName()); } } """; verify(TestOptions.Regular12, expectedType12); verify(TestOptions.Regular13, expectedType13); verify(TestOptions.RegularPreview, expectedType13); static string getTypeParameters(string type) => type.Contains("T[]") || type.Contains("<T>") ? "<T>" : ""; static string generateMethod(string methodName, string parameterType) => $"static Type {methodName}{getTypeParameters(parameterType)}({parameterType} value) => typeof({parameterType});"; static string generateMethodSignature(string methodName, string parameterType) => $"Program.{methodName}{getTypeParameters(parameterType)}({parameterType})"; static string[] getSources(string source, string[] additionalSources) { var builder = ArrayBuilder<string>.GetInstance(); builder.Add(source); builder.Add(s_collectionExtensions); if (additionalSources is { }) builder.AddRange(additionalSources); return builder.ToArrayAndFree(); } void verify(CSharpParseOptions parseOptions, string expectedType) { var comp = CreateCompilation( getSources(source, additionalSources), targetFramework: TargetFramework.Net80, parseOptions: parseOptions, options: TestOptions.ReleaseExe); if (expectedType is { }) { CompileAndVerify(comp, verify: Verification.Skipped, expectedOutput: IncludeExpectedOutput($""" {expectedType} {expectedType} """)); } else { comp.VerifyEmitDiagnostics( // 0.cs(10,17): error CS0121: The call is ambiguous between the following methods or properties: 'Program.F1(ReadOnlySpan<long>)' and 'Program.F1(ReadOnlySpan<object>)' // var x = F1([1, 2, 3]); Diagnostic(ErrorCode.ERR_AmbigCall, "F1").WithArguments(generateMethodSignature("F1", type1), generateMethodSignature("F1", type2)).WithLocation(10, 17), // 0.cs(12,17): error CS0121: The call is ambiguous between the following methods or properties: 'Program.F2(ReadOnlySpan<object>)' and 'Program.F2(ReadOnlySpan<long>)' // var y = F2([4, 5]); Diagnostic(ErrorCode.ERR_AmbigCall, "F2").WithArguments(generateMethodSignature("F2", type2), generateMethodSignature("F2", type1)).WithLocation(12, 17)); } } } [Theory] [InlineData("System.ReadOnlySpan<int>", "System.Span<int>", "System.ReadOnlySpan<System.Int32>")] [InlineData("System.ReadOnlySpan<int>", "System.Span<object>", null)] [InlineData("System.ReadOnlySpan<int>", "System.Span<int?>", null)] [InlineData("System.ReadOnlySpan<byte>", "System.Span<int>", null)] [InlineData("System.ReadOnlySpan<object>", "System.Span<int>", null)] // cannot convert object to int [InlineData("System.ReadOnlySpan<int?>", "System.Span<int>", null)] // cannot convert int? to int [InlineData("System.ReadOnlySpan<int>", "System.ReadOnlySpan<object>", null)] [InlineData("System.ReadOnlySpan<int>", "System.ReadOnlySpan<int?>", null)] [InlineData("System.ReadOnlySpan<object>", "System.ReadOnlySpan<int?>", null)] [InlineData("System.Span<int>", "System.Span<object>", null)] [InlineData("System.Span<int>", "System.Span<int?>", null)] [InlineData("System.Span<object>", "System.Span<int?>", null)] [InlineData("System.ReadOnlySpan<object>", "System.ReadOnlySpan<long>", null)] [InlineData("System.Span<int>", "int?[]", null)] [InlineData("System.Span<int>", "System.Collections.Generic.IEnumerable<int?>", null)] [InlineData("System.Span<int>", "System.Collections.Generic.IReadOnlyCollection<int?>", null)] [InlineData("System.Span<int>", "System.Collections.Generic.IReadOnlyList<int?>", null)] [InlineData("System.Span<int>", "System.Collections.Generic.ICollection<int?>", null)] [InlineData("System.Span<int>", "System.Collections.Generic.IList<int?>", null)] [InlineData("System.Span<int?>", "int[]", null)] // cannot convert int? to int [InlineData("System.Span<int?>", "System.Collections.Generic.IEnumerable<int>", null)] // cannot convert int? to int [InlineData("System.Span<int?>", "System.Collections.Generic.IReadOnlyCollection<int>", null)] // cannot convert int? to int [InlineData("System.Span<int?>", "System.Collections.Generic.IReadOnlyList<int>", null)] // cannot convert int? to int [InlineData("System.Span<int?>", "System.Collections.Generic.ICollection<int>", null)] // cannot convert int? to int [InlineData("System.Span<int?>", "System.Collections.Generic.IList<int>", null)] // cannot convert int? to int [InlineData("System.ReadOnlySpan<int>", "object[]", null)] [InlineData("System.ReadOnlySpan<int>", "System.Collections.Generic.IEnumerable<object>", null)] [InlineData("System.ReadOnlySpan<int>", "System.Collections.Generic.IReadOnlyCollection<object>", null)] [InlineData("System.ReadOnlySpan<int>", "System.Collections.Generic.IReadOnlyList<object>", null)] [InlineData("System.ReadOnlySpan<int>", "System.Collections.Generic.ICollection<object>", null)] [InlineData("System.ReadOnlySpan<int>", "System.Collections.Generic.IList<object>", null)] [InlineData("System.ReadOnlySpan<object>", "int[]", null)] // cannot convert object to int [InlineData("System.ReadOnlySpan<object>", "System.Collections.Generic.IEnumerable<int>", null)] // cannot convert object to int [InlineData("System.ReadOnlySpan<object>", "System.Collections.Generic.IReadOnlyCollection<int>", null)] // cannot convert object to int [InlineData("System.ReadOnlySpan<object>", "System.Collections.Generic.IReadOnlyList<int>", null)] // cannot convert object to int [InlineData("System.ReadOnlySpan<object>", "System.Collections.Generic.ICollection<int>", null)] // cannot convert object to int [InlineData("System.ReadOnlySpan<object>", "System.Collections.Generic.IList<int>", null)] // cannot convert object to int [InlineData("System.Collections.Generic.List<int>", "System.Collections.Generic.IEnumerable<int>", "System.Collections.Generic.List<System.Int32>")] [InlineData("int[]", "object[]", null)] // rule requires span [InlineData("int[]", "System.Collections.Generic.IReadOnlyList<object>", null)] // rule requires span [InlineData("System.Collections.Generic.List<int>", "System.Collections.Generic.List<byte>", null)] [InlineData("System.Collections.Generic.List<int?>", "System.Collections.Generic.List<long>", null)] [InlineData("System.Collections.Generic.List<int?>", "System.Collections.Generic.List<ulong>", null)] [InlineData("System.Collections.Generic.List<short>", "System.Collections.Generic.List<long>", null)] [InlineData("System.Collections.Generic.IEnumerable<int>", "System.Collections.Generic.List<byte>", null)] [InlineData("int[]", "System.Collections.Generic.List<byte>", null)] [InlineData("System.Collections.Generic.HashSet<short>", "System.Span<long>", null)] [InlineData("System.Collections.Generic.HashSet<short>", "System.ReadOnlySpan<long>", null)] [InlineData("System.Collections.Generic.HashSet<long>", "System.Span<short>", null)] [InlineData("System.Collections.Generic.HashSet<long>", "System.ReadOnlySpan<short>", null)] public void BetterConversionFromExpression_01B_Empty(string type1, string type2, string expectedType) { string source = $$""" using System; class Program { {{generateMethod("F1", type1)}} {{generateMethod("F1", type2)}} {{generateMethod("F2", type2)}} {{generateMethod("F2", type1)}} static void Main() { var a = F1([]); Console.WriteLine(a.GetTypeName()); var b = F2([]); Console.WriteLine(b.GetTypeName()); } } """; var comp = CreateCompilation( new[] { source, s_collectionExtensions }, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseExe); if (expectedType is { }) { CompileAndVerify(comp, verify: Verification.Skipped, expectedOutput: IncludeExpectedOutput($""" {expectedType} {expectedType} """)); } else { comp.VerifyEmitDiagnostics( // 0.cs(10,17): error CS0121: The call is ambiguous between the following methods or properties: 'Program.F1(int[])' and 'Program.F1(object[])' // var a = F1(); Diagnostic(ErrorCode.ERR_AmbigCall, "F1").WithArguments(generateMethodSignature("F1", type1), generateMethodSignature("F1", type2)).WithLocation(10, 17), // 0.cs(12,17): error CS0121: The call is ambiguous between the following methods or properties: 'Program.F2(object[])' and 'Program.F2(int[])' // var b = F2(); Diagnostic(ErrorCode.ERR_AmbigCall, "F2").WithArguments(generateMethodSignature("F2", type2), generateMethodSignature("F2", type1)).WithLocation(12, 17)); } static string generateMethod(string methodName, string parameterType) => $"static Type {methodName}({parameterType} value) => typeof({parameterType});"; static string generateMethodSignature(string methodName, string parameterType) => $"Program.{methodName}({parameterType})"; } [Theory] [InlineData("System.ReadOnlySpan<int>", "System.Span<int>", "System.ReadOnlySpan<System.Int32>")] [InlineData("System.ReadOnlySpan<int>", "System.Span<object>", "System.ReadOnlySpan<System.Int32>")] [InlineData("System.ReadOnlySpan<int>", "System.Span<int?>", "System.ReadOnlySpan<System.Int32>")] [InlineData("System.ReadOnlySpan<byte>", "System.Span<int>", "System.Span<System.Int32>")] [InlineData("System.ReadOnlySpan<object>", "System.Span<int>", "System.Span<System.Int32>")] [InlineData("System.ReadOnlySpan<int?>", "System.Span<int>", "System.Span<System.Int32>")] [InlineData("System.ReadOnlySpan<int>", "System.ReadOnlySpan<object>", "System.ReadOnlySpan<System.Int32>")] [InlineData("System.ReadOnlySpan<int>", "System.ReadOnlySpan<int?>", "System.ReadOnlySpan<System.Int32>")] [InlineData("System.ReadOnlySpan<object>", "System.ReadOnlySpan<int?>", "System.ReadOnlySpan<System.Nullable<System.Int32>>")] [InlineData("System.Span<int>", "System.Span<object>", "System.Span<System.Int32>")] [InlineData("System.Span<int>", "System.Span<int?>", "System.Span<System.Int32>")] [InlineData("System.Span<object>", "System.Span<int?>", "System.Span<System.Nullable<System.Int32>>")] [InlineData("System.ReadOnlySpan<object>", "System.ReadOnlySpan<long>", "System.ReadOnlySpan<System.Int64>")] [InlineData("System.Span<int>", "int?[]", "System.Span<System.Int32>")] [InlineData("System.Span<int>", "System.Collections.Generic.IEnumerable<int?>", "System.Span<System.Int32>")] [InlineData("System.Span<int>", "System.Collections.Generic.IReadOnlyCollection<int?>", "System.Span<System.Int32>")] [InlineData("System.Span<int>", "System.Collections.Generic.IReadOnlyList<int?>", "System.Span<System.Int32>")] [InlineData("System.Span<int>", "System.Collections.Generic.ICollection<int?>", "System.Span<System.Int32>")] [InlineData("System.Span<int>", "System.Collections.Generic.IList<int?>", "System.Span<System.Int32>")] [InlineData("System.Span<int?>", "int[]", "System.Int32[]")] [InlineData("System.Span<int?>", "System.Collections.Generic.IEnumerable<int>", "System.Collections.Generic.IEnumerable<System.Int32>")] [InlineData("System.Span<int?>", "System.Collections.Generic.IReadOnlyCollection<int>", "System.Collections.Generic.IReadOnlyCollection<System.Int32>")] [InlineData("System.Span<int?>", "System.Collections.Generic.IReadOnlyList<int>", "System.Collections.Generic.IReadOnlyList<System.Int32>")] [InlineData("System.Span<int?>", "System.Collections.Generic.ICollection<int>", "System.Collections.Generic.ICollection<System.Int32>")] [InlineData("System.Span<int?>", "System.Collections.Generic.IList<int>", "System.Collections.Generic.IList<System.Int32>")] [InlineData("System.ReadOnlySpan<int>", "object[]", "System.ReadOnlySpan<System.Int32>")] [InlineData("System.ReadOnlySpan<int>", "System.Collections.Generic.IEnumerable<object>", "System.ReadOnlySpan<System.Int32>")] [InlineData("System.ReadOnlySpan<int>", "System.Collections.Generic.IReadOnlyCollection<object>", "System.ReadOnlySpan<System.Int32>")] [InlineData("System.ReadOnlySpan<int>", "System.Collections.Generic.IReadOnlyList<object>", "System.ReadOnlySpan<System.Int32>")] [InlineData("System.ReadOnlySpan<int>", "System.Collections.Generic.ICollection<object>", "System.ReadOnlySpan<System.Int32>")] [InlineData("System.ReadOnlySpan<int>", "System.Collections.Generic.IList<object>", "System.ReadOnlySpan<System.Int32>")] [InlineData("System.ReadOnlySpan<object>", "int[]", "System.Int32[]")] [InlineData("System.ReadOnlySpan<object>", "System.Collections.Generic.IEnumerable<int>", "System.Collections.Generic.IEnumerable<System.Int32>")] [InlineData("System.ReadOnlySpan<object>", "System.Collections.Generic.IReadOnlyCollection<int>", "System.Collections.Generic.IReadOnlyCollection<System.Int32>")] [InlineData("System.ReadOnlySpan<object>", "System.Collections.Generic.IReadOnlyList<int>", "System.Collections.Generic.IReadOnlyList<System.Int32>")] [InlineData("System.ReadOnlySpan<object>", "System.Collections.Generic.ICollection<int>", "System.Collections.Generic.ICollection<System.Int32>")] [InlineData("System.ReadOnlySpan<object>", "System.Collections.Generic.IList<int>", "System.Collections.Generic.IList<System.Int32>")] [InlineData("System.Collections.Generic.List<int>", "System.Collections.Generic.IEnumerable<int>", "System.Collections.Generic.List<System.Int32>")] [InlineData("int[]", "object[]", "System.Int32[]")] [InlineData("int[]", "System.Collections.Generic.IReadOnlyList<object>", "System.Int32[]")] [InlineData("System.Collections.Generic.List<int>", "System.Collections.Generic.List<byte>", "System.Collections.Generic.List<System.Int32>")] [InlineData("System.Collections.Generic.List<int?>", "System.Collections.Generic.List<long>", null)] [InlineData("System.Collections.Generic.List<int?>", "System.Collections.Generic.List<ulong>", "System.Collections.Generic.List<System.Nullable<System.Int32>>")] [InlineData("System.Collections.Generic.List<short>", "System.Collections.Generic.List<long>", "System.Collections.Generic.List<System.Int16>")] [InlineData("System.Collections.Generic.IEnumerable<int>", "System.Collections.Generic.List<byte>", "System.Collections.Generic.IEnumerable<System.Int32>")] [InlineData("int[]", "System.Collections.Generic.List<byte>", "System.Int32[]")] [InlineData("System.Collections.Generic.HashSet<short>", "System.Span<long>", "System.Collections.Generic.HashSet<System.Int16>")] [InlineData("System.Collections.Generic.HashSet<short>", "System.ReadOnlySpan<long>", "System.Collections.Generic.HashSet<System.Int16>")] [InlineData("System.Collections.Generic.HashSet<long>", "System.Span<short>", "System.Span<System.Int16>")] [InlineData("System.Collections.Generic.HashSet<long>", "System.ReadOnlySpan<short>", "System.ReadOnlySpan<System.Int16>")] public void BetterConversionFromExpression_01B_NotEmpty(string type1, string type2, string expectedType) { string source = $$""" using System; class Program { {{generateMethod("F1", type1)}} {{generateMethod("F1", type2)}} {{generateMethod("F2", type2)}} {{generateMethod("F2", type1)}} static void Main() { var c = F1([1, 2, 3]); Console.WriteLine(c.GetTypeName()); var d = F2([4, 5]); Console.WriteLine(d.GetTypeName()); } } """; var comp = CreateCompilation( new[] { source, s_collectionExtensions }, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseExe); if (expectedType is { }) { CompileAndVerify(comp, verify: Verification.Skipped, expectedOutput: IncludeExpectedOutput($""" {expectedType} {expectedType} """)); } else { comp.VerifyEmitDiagnostics( // 0.cs(10,17): error CS0121: The call is ambiguous between the following methods or properties: 'Program.F1(int[])' and 'Program.F1(object[])' // var c = F1(1, 2, 3); Diagnostic(ErrorCode.ERR_AmbigCall, "F1").WithArguments(generateMethodSignature("F1", type1), generateMethodSignature("F1", type2)).WithLocation(10, 17), // 0.cs(12,17): error CS0121: The call is ambiguous between the following methods or properties: 'Program.F2(object[])' and 'Program.F2(int[])' // var d = F2(4, 5); Diagnostic(ErrorCode.ERR_AmbigCall, "F2").WithArguments(generateMethodSignature("F2", type2), generateMethodSignature("F2", type1)).WithLocation(12, 17)); } static string generateMethod(string methodName, string parameterType) => $"static Type {methodName}({parameterType} value) => typeof({parameterType});"; static string generateMethodSignature(string methodName, string parameterType) => $"Program.{methodName}({parameterType})"; } [Theory] [InlineData("System.ReadOnlySpan<int>", "System.Span<int>", "System.ReadOnlySpan<System.Int32>")] [InlineData("System.ReadOnlySpan<int>", "System.Span<object>", "System.ReadOnlySpan<System.Int32>")] [InlineData("System.ReadOnlySpan<int>", "System.Span<int?>", "System.ReadOnlySpan<System.Int32>")] [InlineData("System.ReadOnlySpan<byte>", "System.Span<int>", "System.ReadOnlySpan<System.Byte>")] [InlineData("System.ReadOnlySpan<object>", "System.Span<int>", null)] // cannot convert object to int [InlineData("System.ReadOnlySpan<int?>", "System.Span<int>", null)] // cannot convert int? to int [InlineData("System.ReadOnlySpan<int>", "System.ReadOnlySpan<object>", null)] [InlineData("System.ReadOnlySpan<int>", "System.ReadOnlySpan<int?>", null)] [InlineData("System.ReadOnlySpan<object>", "System.ReadOnlySpan<int?>", null)] [InlineData("System.Span<int>", "System.Span<object>", null)] [InlineData("System.Span<int>", "System.Span<int?>", null)] [InlineData("System.Span<object>", "System.Span<int?>", null)] [InlineData("System.ReadOnlySpan<object>", "System.ReadOnlySpan<long>", null)] [InlineData("System.Span<int>", "int?[]", "System.Span<System.Int32>")] [InlineData("System.Span<int>", "System.Collections.Generic.IEnumerable<int?>", "System.Span<System.Int32>")] [InlineData("System.Span<int>", "System.Collections.Generic.IReadOnlyCollection<int?>", "System.Span<System.Int32>")] [InlineData("System.Span<int>", "System.Collections.Generic.IReadOnlyList<int?>", "System.Span<System.Int32>")] [InlineData("System.Span<int>", "System.Collections.Generic.ICollection<int?>", "System.Span<System.Int32>")] [InlineData("System.Span<int>", "System.Collections.Generic.IList<int?>", "System.Span<System.Int32>")] [InlineData("System.Span<int?>", "int[]", null)] // cannot convert int? to int [InlineData("System.Span<int?>", "System.Collections.Generic.IEnumerable<int>", null)] // cannot convert int? to int [InlineData("System.Span<int?>", "System.Collections.Generic.IReadOnlyCollection<int>", null)] // cannot convert int? to int [InlineData("System.Span<int?>", "System.Collections.Generic.IReadOnlyList<int>", null)] // cannot convert int? to int [InlineData("System.Span<int?>", "System.Collections.Generic.ICollection<int>", null)] // cannot convert int? to int [InlineData("System.Span<int?>", "System.Collections.Generic.IList<int>", null)] // cannot convert int? to int [InlineData("System.ReadOnlySpan<int>", "object[]", "System.ReadOnlySpan<System.Int32>")] [InlineData("System.ReadOnlySpan<int>", "System.Collections.Generic.IEnumerable<object>", "System.ReadOnlySpan<System.Int32>")] [InlineData("System.ReadOnlySpan<int>", "System.Collections.Generic.IReadOnlyCollection<object>", "System.ReadOnlySpan<System.Int32>")] [InlineData("System.ReadOnlySpan<int>", "System.Collections.Generic.IReadOnlyList<object>", "System.ReadOnlySpan<System.Int32>")] [InlineData("System.ReadOnlySpan<int>", "System.Collections.Generic.ICollection<object>", "System.ReadOnlySpan<System.Int32>")] [InlineData("System.ReadOnlySpan<int>", "System.Collections.Generic.IList<object>", "System.ReadOnlySpan<System.Int32>")] [InlineData("System.ReadOnlySpan<object>", "int[]", null)] // cannot convert object to int [InlineData("System.ReadOnlySpan<object>", "System.Collections.Generic.IEnumerable<int>", null)] // cannot convert object to int [InlineData("System.ReadOnlySpan<object>", "System.Collections.Generic.IReadOnlyCollection<int>", null)] // cannot convert object to int [InlineData("System.ReadOnlySpan<object>", "System.Collections.Generic.IReadOnlyList<int>", null)] // cannot convert object to int [InlineData("System.ReadOnlySpan<object>", "System.Collections.Generic.ICollection<int>", null)] // cannot convert object to int [InlineData("System.ReadOnlySpan<object>", "System.Collections.Generic.IList<int>", null)] // cannot convert object to int [InlineData("System.Collections.Generic.List<int>", "System.Collections.Generic.IEnumerable<int>", "System.Collections.Generic.List<System.Int32>")] [InlineData("int[]", "object[]", null)] // rule requires span [InlineData("int[]", "System.Collections.Generic.IReadOnlyList<object>", null)] // rule requires span [InlineData("System.Collections.Generic.List<int>", "System.Collections.Generic.List<byte>", null)] [InlineData("System.Collections.Generic.List<int?>", "System.Collections.Generic.List<long>", null)] [InlineData("System.Collections.Generic.List<int?>", "System.Collections.Generic.List<ulong>", null)] [InlineData("System.Collections.Generic.List<short>", "System.Collections.Generic.List<long>", null)] [InlineData("System.Collections.Generic.IEnumerable<int>", "System.Collections.Generic.List<byte>", null)] [InlineData("int[]", "System.Collections.Generic.List<byte>", null)] [InlineData("System.Collections.Generic.HashSet<short>", "System.Span<long>", null)] [InlineData("System.Collections.Generic.HashSet<short>", "System.ReadOnlySpan<long>", null)] [InlineData("System.Collections.Generic.HashSet<long>", "System.Span<short>", null)] [InlineData("System.Collections.Generic.HashSet<long>", "System.ReadOnlySpan<short>", null)] public void BetterConversionFromExpression_01B_CSharp12(string type1, string type2, string expectedType) { string source = $$""" using System; class Program { {{generateMethod("F1", type1)}} {{generateMethod("F1", type2)}} {{generateMethod("F2", type2)}} {{generateMethod("F2", type1)}} static void Main() { var a = F1([]); Console.WriteLine(a.GetTypeName()); var b = F2([]); Console.WriteLine(b.GetTypeName()); var c = F1([1, 2, 3]); Console.WriteLine(c.GetTypeName()); var d = F2([4, 5]); Console.WriteLine(d.GetTypeName()); } } """; var comp = CreateCompilation( new[] { source, s_collectionExtensions }, targetFramework: TargetFramework.Net80, parseOptions: TestOptions.Regular12, options: TestOptions.ReleaseExe); if (expectedType is { }) { CompileAndVerify(comp, verify: Verification.Skipped, expectedOutput: IncludeExpectedOutput($""" {expectedType} {expectedType} {expectedType} {expectedType} """)); } else { comp.VerifyEmitDiagnostics( // 0.cs(10,17): error CS0121: The call is ambiguous between the following methods or properties: 'Program.F1(int[])' and 'Program.F1(object[])' // var a = F1([]); Diagnostic(ErrorCode.ERR_AmbigCall, "F1").WithArguments(generateMethodSignature("F1", type1), generateMethodSignature("F1", type2)).WithLocation(10, 17), // 0.cs(12,17): error CS0121: The call is ambiguous between the following methods or properties: 'Program.F2(object[])' and 'Program.F2(int[])' // var b = F2([]); Diagnostic(ErrorCode.ERR_AmbigCall, "F2").WithArguments(generateMethodSignature("F2", type2), generateMethodSignature("F2", type1)).WithLocation(12, 17), // 0.cs(14,17): error CS0121: The call is ambiguous between the following methods or properties: 'Program.F1(int[])' and 'Program.F1(object[])' // var c = F1([1, 2, 3]); Diagnostic(ErrorCode.ERR_AmbigCall, "F1").WithArguments(generateMethodSignature("F1", type1), generateMethodSignature("F1", type2)).WithLocation(14, 17), // 0.cs(16,17): error CS0121: The call is ambiguous between the following methods or properties: 'Program.F2(object[])' and 'Program.F2(int[])' // var d = F2([4, 5]); Diagnostic(ErrorCode.ERR_AmbigCall, "F2").WithArguments(generateMethodSignature("F2", type2), generateMethodSignature("F2", type1)).WithLocation(16, 17)); } static string generateMethod(string methodName, string parameterType) => $"static Type {methodName}({parameterType} value) => typeof({parameterType});"; static string generateMethodSignature(string methodName, string parameterType) => $"Program.{methodName}({parameterType})"; } [Theory, CombinatorialData] public void BetterConversionFromExpression_01C( [CombinatorialValues( "System.ReadOnlySpan<string>", "System.Span<string>", "string[]", "System.Collections.Generic.IEnumerable<string>", "System.Collections.Generic.IReadOnlyList<string>", "System.Collections.Generic.IReadOnlyCollection<string>", "System.Collections.Generic.IList<string>", "System.Collections.Generic.ICollection<string>" )] string stringType, [CombinatorialValues( "System.ReadOnlySpan<CustomHandler>", "System.Span<CustomHandler>", "CustomHandler[]", "System.Collections.Generic.IEnumerable<CustomHandler>", "System.Collections.Generic.IReadOnlyList<CustomHandler>", "System.Collections.Generic.IReadOnlyCollection<CustomHandler>", "System.Collections.Generic.IList<CustomHandler>", "System.Collections.Generic.ICollection<CustomHandler>")] string interpolatedType) { var testMethods = $$""" partial class Program { {{generateMethod("F1", stringType)}} {{generateMethod("F1", interpolatedType)}} {{generateMethod("F2", interpolatedType)}} {{generateMethod("F2", stringType)}} } """; var customHandler = GetInterpolatedStringCustomHandlerType("CustomHandler", "class", useBoolReturns: false, includeOneTimeHelpers: false); string source1 = $$""" var a = F1([]); var b = F2([]); var c = F1([$"{1}", $"2", $"{3}"]); var d = F2([$"{1}", $"2", $"{3}"]); """; var comp = CreateCompilation( [source1, testMethods, customHandler, s_collectionExtensions], targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseExe); string f1InterpolatedSig = generateMethodSignature("F1", interpolatedType); string f1StringSig = generateMethodSignature("F1", stringType); string f2InterpolatedSig = generateMethodSignature("F2", interpolatedType); string f2StringSig = generateMethodSignature("F2", stringType); comp.VerifyDiagnostics( // (1,9): error CS0121: The call is ambiguous between the following methods or properties: 'Program.F1(IReadOnlyList<string>)' and 'Program.F1(IReadOnlyCollection<CustomHandler>)' // var a = F1([]); Diagnostic(ErrorCode.ERR_AmbigCall, "F1").WithArguments(f1StringSig, f1InterpolatedSig).WithLocation(1, 9), // (2,9): error CS0121: The call is ambiguous between the following methods or properties: 'Program.F2(IReadOnlyCollection<CustomHandler>)' and 'Program.F2(IReadOnlyList<string>)' // var b = F2([]); Diagnostic(ErrorCode.ERR_AmbigCall, "F2").WithArguments(f2InterpolatedSig, f2StringSig).WithLocation(2, 9), // (3,9): error CS0121: The call is ambiguous between the following methods or properties: 'Program.F1(string[])' and 'Program.F1(CustomHandler[])' // var c = F1([$"{1}", $"2", $"{3}"]); Diagnostic(ErrorCode.ERR_AmbigCall, "F1").WithArguments(f1StringSig, f1InterpolatedSig).WithLocation(3, 9), // (4,9): error CS0121: The call is ambiguous between the following methods or properties: 'Program.F2(CustomHandler[])' and 'Program.F2(string[])' // var d = F2([$"{1}", $"2", $"{3}"]); Diagnostic(ErrorCode.ERR_AmbigCall, "F2").WithArguments(f2InterpolatedSig, f2StringSig).WithLocation(4, 9) ); var source2 = $$""" using System; var c = F1([$"{1}", $"{2}", $"{3}"]); Console.WriteLine(c.GetTypeName()); var d = F2([$"{1}", $"{2}", $"{3}"]); Console.WriteLine(d.GetTypeName()); var e = F1([$"1", $"2", $"3"]); Console.WriteLine(e.GetTypeName()); var f = F2([$"1", $"2", $"3"]); Console.WriteLine(f.GetTypeName()); """; comp = CreateCompilation( [source2, testMethods, customHandler, s_collectionExtensions], targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseExe); string outputStringType = stringType.Replace("string", "System.String"); CompileAndVerify(comp, verify: Verification.Skipped, expectedOutput: IncludeExpectedOutput($""" {interpolatedType} {interpolatedType} {outputStringType} {outputStringType} """)); static string generateMethod(string methodName, string parameterType) => $"static System.Type {methodName}({parameterType} value) => typeof({parameterType});"; static string generateMethodSignature(string methodName, string parameterType) => $"Program.{methodName}({parameterType})"; } [Fact] public void BetterConversionFromExpression_02() { string sourceA = """ using System; using static System.Console; partial class Program { static void Generic<T>(Span<T> value) { WriteLine("Span<T>"); } static void Generic<T>(T[] value) { WriteLine("T[]"); } static void Identical(Span<string> value) { WriteLine("Span<string>"); } static void Identical(string[] value) { WriteLine("string[]"); } static void SpanDerived(Span<string> value) { WriteLine("Span<string>"); } static void SpanDerived(object[] value) { WriteLine("object[]"); } static void ArrayDerived(Span<object> value) { WriteLine("Span<object>"); } static void ArrayDerived(string[] value) { WriteLine("string[]"); } } """; string sourceB1 = """ partial class Program { static void Main() { Generic(new[] { string.Empty }); // string[] Identical(new[] { string.Empty }); // string[] ArrayDerived(new[] { string.Empty }); // string[] Generic([string.Empty]); // Span<string> Identical([string.Empty]); // Span<string> SpanDerived([string.Empty]); // Span<string> ArrayDerived([string.Empty]); // string[] } } """; string expectedOutput = IncludeExpectedOutput(""" T[] string[] string[] Span<T> Span<string> Span<string> string[] """); var comp = CreateCompilation( new[] { sourceA, sourceB1 }, targetFramework: TargetFramework.Net80, parseOptions: TestOptions.Regular13, options: TestOptions.ReleaseExe); CompileAndVerify(comp, verify: Verification.Skipped, expectedOutput: expectedOutput); comp = CreateCompilation( new[] { sourceA, sourceB1 }, targetFramework: TargetFramework.Net80, parseOptions: TestOptions.RegularPreview, options: TestOptions.ReleaseExe); CompileAndVerify(comp, verify: Verification.Skipped, expectedOutput: expectedOutput); comp = CreateCompilation( new[] { sourceA, sourceB1 }, targetFramework: TargetFramework.Net80, parseOptions: TestOptions.Regular12, options: TestOptions.ReleaseExe); comp.VerifyDiagnostics( // 1.cs(12,9): error CS0121: The call is ambiguous between the following methods or properties: 'Program.ArrayDerived(Span<object>)' and 'Program.ArrayDerived(string[])' // ArrayDerived([string.Empty]); // string[] Diagnostic(ErrorCode.ERR_AmbigCall, "ArrayDerived").WithArguments("Program.ArrayDerived(System.Span<object>)", "Program.ArrayDerived(string[])").WithLocation(12, 9) ); string sourceB2 = """ partial class Program { static void Main() { SpanDerived(new[] { string.Empty }); // ambiguous } } """; // 1.cs(5,9): error CS0121: The call is ambiguous between the following methods or properties: 'Program.SpanDerived(Span<string>)' and 'Program.SpanDerived(object[])' // SpanDerived(new[] { string.Empty }); // ambiguous var expectedDiagnostic = Diagnostic(ErrorCode.ERR_AmbigCall, "SpanDerived").WithArguments("Program.SpanDerived(System.Span<string>)", "Program.SpanDerived(object[])").WithLocation(5, 9); comp = CreateCompilation( new[] { sourceA, sourceB2 }, parseOptions: TestOptions.Regular13, targetFramework: TargetFramework.Net80); comp.VerifyEmitDiagnostics(expectedDiagnostic); comp = CreateCompilation( new[] { sourceA, sourceB2 }, parseOptions: TestOptions.RegularPreview, options: TestOptions.ReleaseExe, targetFramework: TargetFramework.Net80); CompileAndVerify(comp, verify: Verification.Skipped, expectedOutput: IncludeExpectedOutput("Span<string>")).VerifyDiagnostics(); comp = CreateCompilation( new[] { sourceA, sourceB2 }, parseOptions: TestOptions.Regular12, targetFramework: TargetFramework.Net80); comp.VerifyEmitDiagnostics(expectedDiagnostic); } [WorkItem("https://github.com/dotnet/roslyn/issues/69634")] [Fact] public void BetterConversionFromExpression_03() { string sourceA = """ using System; using static System.Console; partial class Program { static void Unrelated(Span<int> value) { WriteLine("Span<int>"); } static void Unrelated(string[] value) { WriteLine("string[]"); } } """; string sourceB1 = """ partial class Program { static void Main() { Unrelated(new[] { 1 }); // Span<int> Unrelated(new[] { string.Empty }); // string[] Unrelated([2]); // Span<int> Unrelated([string.Empty]); // string[] } } """; var comp = CreateCompilation( new[] { sourceA, sourceB1 }, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseExe); CompileAndVerify(comp, verify: Verification.Skipped, expectedOutput: IncludeExpectedOutput(""" Span<int> string[] Span<int> string[] """)); string sourceB2 = """ partial class Program { static void Main() { Unrelated(new[] { default }); // error Unrelated([default]); // ambiguous } } """; comp = CreateCompilation( new[] { sourceA, sourceB2 }, targetFramework: TargetFramework.Net80); comp.VerifyEmitDiagnostics( // 1.cs(5,19): error CS0826: No best type found for implicitly-typed array // Unrelated(new[] { default }); // error Diagnostic(ErrorCode.ERR_ImplicitlyTypedArrayNoBestType, "new[] { default }").WithLocation(5, 19), // 1.cs(5,19): error CS1503: Argument 1: cannot convert from '?[]' to 'System.Span<int>' // Unrelated(new[] { default }); // error Diagnostic(ErrorCode.ERR_BadArgType, "new[] { default }").WithArguments("1", "?[]", "System.Span<int>").WithLocation(5, 19), // 1.cs(6,9): error CS0121: The call is ambiguous between the following methods or properties: 'Program.Unrelated(Span<int>)' and 'Program.Unrelated(string[])' // Unrelated([default]); // ambiguous Diagnostic(ErrorCode.ERR_AmbigCall, "Unrelated").WithArguments("Program.Unrelated(System.Span<int>)", "Program.Unrelated(string[])").WithLocation(6, 9)); } [Fact] public void BetterConversionFromExpression_04() { string source = """ using System; class Program { static void F1(int[] x, int[] y) { throw null; } static void F1(Span<object> x, ReadOnlySpan<int> y) { x.ReportSpan(); y.Report(); } static void F2(object x, string[] y) { throw null; } static void F2(string x, Span<object> y) { y.ReportSpan(); } static void Main() { F1([1], [2]); F2("3", ["4"]); } } """; var expectedDiagnostics = new[] { // 'params' works in this case. // For 'Inline collection expression' case it fails because: // - For the first argument, 'int[]' and 'Span<object>' -> `int[]` is better vs. `Span<object>` for 'params' // - For the second argument, 'int' and 'int' -> 'ReadOnlySpan<int>' is better vs. neither is better for 'params' // The first parameter is better for the first overload, and the second is better for the second overload, ambiguous. // 0.cs(10,9): error CS0121: The call is ambiguous between the following methods or properties: 'Program.F1(int[], int[])' and 'Program.F1(Span<object>, ReadOnlySpan<int>)' // F1([1], [2]); Diagnostic(ErrorCode.ERR_AmbigCall, "F1").WithArguments("Program.F1(int[], int[])", "Program.F1(System.Span<object>, System.ReadOnlySpan<int>)").WithLocation(10, 9), // 0.cs(11,9): error CS0121: The call is ambiguous between the following methods or properties: 'Program.F2(object, string[])' and 'Program.F2(string, Span<object>)' // F2("3", ["4"]); Diagnostic(ErrorCode.ERR_AmbigCall, "F2").WithArguments("Program.F2(object, string[])", "Program.F2(string, System.Span<object>)").WithLocation(11, 9) }; var comp = CreateCompilation(new[] { source, s_collectionExtensionsWithSpan }, parseOptions: TestOptions.Regular13, targetFramework: TargetFramework.Net80); comp.VerifyDiagnostics(expectedDiagnostics); comp = CreateCompilation(new[] { source, s_collectionExtensionsWithSpan }, parseOptions: TestOptions.RegularPreview, targetFramework: TargetFramework.Net80); comp.VerifyDiagnostics(expectedDiagnostics); CompileAndVerify( new[] { source, s_collectionExtensionsWithSpan }, targetFramework: TargetFramework.Net80, parseOptions: TestOptions.Regular12, verify: Verification.Skipped, expectedOutput: IncludeExpectedOutput("[1], [2], [4], ")); } [Fact] public void BetterConversionFromExpression_05() { string source = """ using System; class Program { static void F1(Span<int> x, int[] y) { throw null; } static void F1(int[] x, ReadOnlySpan<int> y) { } static void F2(string x, string[] y) { throw null; } static void F2(object x, Span<string> y) { } static void Main() { F1([1], [2]); F2("3", ["4"]); } } """; var comp = CreateCompilation(source, targetFramework: TargetFramework.Net80); comp.VerifyEmitDiagnostics( // (10,9): error CS0121: The call is ambiguous between the following methods or properties: 'Program.F1(Span<int>, int[])' and 'Program.F1(int[], ReadOnlySpan<int>)' // F1([1], [2]); Diagnostic(ErrorCode.ERR_AmbigCall, "F1").WithArguments("Program.F1(System.Span<int>, int[])", "Program.F1(int[], System.ReadOnlySpan<int>)").WithLocation(10, 9), // (11,9): error CS0121: The call is ambiguous between the following methods or properties: 'Program.F2(string, string[])' and 'Program.F2(object, Span<string>)' // F2("3", ["4"]); Diagnostic(ErrorCode.ERR_AmbigCall, "F2").WithArguments("Program.F2(string, string[])", "Program.F2(object, System.Span<string>)").WithLocation(11, 9)); } // Two ref struct collection types, with an implicit conversion from one to the other. [Fact] public void BetterConversionFromExpression_06() { string source = """ using System; using System.Collections.Generic; using System.Runtime.CompilerServices; [CollectionBuilder(typeof(MyCollectionBuilder), nameof(MyCollectionBuilder.Create1))] ref struct MyCollection1<T> { private readonly List<T> _list; public MyCollection1(List<T> list) { _list = list; } public IEnumerator<T> GetEnumerator() => _list.GetEnumerator(); public static implicit operator MyCollection2<T>(MyCollection1<T> c) => new(c._list); } [CollectionBuilder(typeof(MyCollectionBuilder), nameof(MyCollectionBuilder.Create2))] ref struct MyCollection2<T> { private readonly List<T> _list; public MyCollection2(List<T> list) { _list = list; } public IEnumerator<T> GetEnumerator() => _list.GetEnumerator(); } static class MyCollectionBuilder { public static MyCollection1<T> Create1<T>(scoped ReadOnlySpan<T> items) { return new MyCollection1<T>(new List<T>(items.ToArray())); } public static MyCollection2<T> Create2<T>(scoped ReadOnlySpan<T> items) { return new MyCollection2<T>(new List<T>(items.ToArray())); } } class Program { static void F1<T>(MyCollection1<T> c) { Console.WriteLine("MyCollection1<T>"); } static void F1<T>(MyCollection2<T> c) { Console.WriteLine("MyCollection2<T>"); } static void F2(MyCollection2<object> c) { Console.WriteLine("MyCollection2<object>"); } static void F2(MyCollection1<object> c) { Console.WriteLine("MyCollection1<object>"); } static void Main() { F1([1, 2, 3]); F2([4, null]); F1((MyCollection1<object>)[6]); F1((MyCollection2<int>)[7]); F2((MyCollection2<object>)[8]); } } """; CompileAndVerify( source, targetFramework: TargetFramework.Net80, verify: Verification.Skipped, expectedOutput: IncludeExpectedOutput(""" MyCollection1<T> MyCollection1<object> MyCollection1<T> MyCollection2<T> MyCollection2<object> """)); } [Fact] public void BetterConversionFromExpression_07() { string source = """ using System; class Program { static void F1(ReadOnlySpan<int> value) => Console.WriteLine("ReadOnlySpan<int>"); static void F1(ReadOnlySpan<object> value) => Console.WriteLine("ReadOnlySpan<object>"); static void F2(Span<string> value) => Console.WriteLine("Span<string>"); static void F2(Span<object> value) => Console.WriteLine("Span<object>"); static void Main() { F1([1, 2, 3]); F2(["a", "b"]); } } """; var expectedOutput = IncludeExpectedOutput(""" ReadOnlySpan<int> Span<string> """); CompileAndVerify( source, targetFramework: TargetFramework.Net80, parseOptions: TestOptions.Regular13, verify: Verification.Skipped, expectedOutput: expectedOutput); CompileAndVerify( source, targetFramework: TargetFramework.Net80, parseOptions: TestOptions.RegularPreview, verify: Verification.Skipped, expectedOutput: expectedOutput); CreateCompilation(source, parseOptions: TestOptions.Regular12, targetFramework: TargetFramework.Net80).VerifyEmitDiagnostics( // (10,9): error CS0121: The call is ambiguous between the following methods or properties: 'Program.F1(ReadOnlySpan<int>)' and 'Program.F1(ReadOnlySpan<object>)' // F1([1, 2, 3]); Diagnostic(ErrorCode.ERR_AmbigCall, "F1").WithArguments("Program.F1(System.ReadOnlySpan<int>)", "Program.F1(System.ReadOnlySpan<object>)").WithLocation(10, 9), // (11,9): error CS0121: The call is ambiguous between the following methods or properties: 'Program.F2(Span<string>)' and 'Program.F2(Span<object>)' // F2(["a", "b"]); Diagnostic(ErrorCode.ERR_AmbigCall, "F2").WithArguments("Program.F2(System.Span<string>)", "Program.F2(System.Span<object>)").WithLocation(11, 9)); } [Fact] public void BetterConversionFromExpression_08A() { string source = """ using System; class Program { static void F1(int[] value) => Console.WriteLine("int[]"); static void F1(object[] value) => Console.WriteLine("object[]"); static void Main() { F1([1, 2, 3]); } } """; var expectedOutput = "int[]"; CompileAndVerify(source, parseOptions: TestOptions.Regular13, expectedOutput: expectedOutput).VerifyDiagnostics(); CompileAndVerify(source, parseOptions: TestOptions.RegularPreview, expectedOutput: expectedOutput).VerifyDiagnostics(); CreateCompilation(source, parseOptions: TestOptions.Regular12).VerifyEmitDiagnostics( // (8,9): error CS0121: The call is ambiguous between the following methods or properties: 'Program.F1(int[])' and 'Program.F1(object[])' // F1([1, 2, 3]); Diagnostic(ErrorCode.ERR_AmbigCall, "F1").WithArguments("Program.F1(int[])", "Program.F1(object[])").WithLocation(8, 9)); } [Fact] public void BetterConversionFromExpression_08B() { string source = """ using System; class Program { static void F2(string[] value) { Console.WriteLine("string[]"); } static void F2(object[] value) { Console.WriteLine("object[]"); } static void Main() { F2(["a", "b"]); } } """; CompileAndVerify(source, expectedOutput: "string[]"); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/73857")] public void BetterConversionFromExpression_08C() { string source = """ using System; class Program { static void F2(ReadOnlySpan<string> value) { Console.WriteLine("ReadOnlySpan<string>"); } static void F2(ReadOnlySpan<object> value) { Console.WriteLine("ReadOnlySpan<object>"); } static void Main() { F2(["a", "b"]); } } """; CompileAndVerify(source, targetFramework: TargetFramework.Net80, verify: Verification.Skipped, expectedOutput: IncludeExpectedOutput("ReadOnlySpan<string>")); } [Theory] [InlineData(LanguageVersion.Preview)] [InlineData(LanguageVersion.CSharp13)] [InlineData(LanguageVersion.CSharp12)] public void BetterConversionFromExpression_09(LanguageVersion version) { string source = """ using System; using System.Collections.Generic; class Program { static void F2(List<int> value) { Console.WriteLine("List<int>"); } static void F2(List<byte> value) { Console.WriteLine("List<byte>"); } static void Main() { F2([1, (byte)2]); } } """; CreateCompilation(source, parseOptions: TestOptions.Regular.WithLanguageVersion(version)).VerifyDiagnostics( // (9,9): error CS0121: The call is ambiguous between the following methods or properties: 'Program.F2(List<int>)' and 'Program.F2(List<byte>)' // F2([1, (byte)2]); Diagnostic(ErrorCode.ERR_AmbigCall, "F2").WithArguments("Program.F2(System.Collections.Generic.List<int>)", "Program.F2(System.Collections.Generic.List<byte>)").WithLocation(9, 9)); } [Fact] public void BetterConversionFromExpression_10() { string source = """ using System; using System.Collections.Generic; class Program { static void F2(List<int> value) { Console.WriteLine("List<int>"); } static void F2(List<byte> value) { Console.WriteLine("List<byte>"); } static void Main() { F2([(byte)1, (byte)2]); } } """; var expectedOutput = "List<byte>"; CompileAndVerify(source, parseOptions: TestOptions.Regular13, expectedOutput: expectedOutput); CompileAndVerify(source, parseOptions: TestOptions.RegularPreview, expectedOutput: expectedOutput); CreateCompilation(source, parseOptions: TestOptions.Regular12).VerifyDiagnostics( // (9,9): error CS0121: The call is ambiguous between the following methods or properties: 'Program.F2(List<int>)' and 'Program.F2(List<byte>)' // F2([1, (byte)2]); Diagnostic(ErrorCode.ERR_AmbigCall, "F2").WithArguments("Program.F2(System.Collections.Generic.List<int>)", "Program.F2(System.Collections.Generic.List<byte>)").WithLocation(9, 9)); } [Theory] [InlineData("System.FormattableString")] [InlineData("System.IFormattable")] public void BetterConversionFromExpression_11(string formatType) { string source = $$""" using System; class Program { static void F2(ReadOnlySpan<string> value) { Console.WriteLine("ReadOnlySpan<string>"); } static void F2(ReadOnlySpan<{{formatType}}> value) { Console.WriteLine("ReadOnlySpan<{{formatType}}>"); } static void Main() { F2([$"{1}"]); } } """; string expectedOutput = IncludeExpectedOutput("ReadOnlySpan<string>"); CompileAndVerify(source, parseOptions: TestOptions.Regular13, targetFramework: TargetFramework.Net80, verify: Verification.Skipped, expectedOutput: expectedOutput); CompileAndVerify(source, parseOptions: TestOptions.RegularPreview, targetFramework: TargetFramework.Net80, verify: Verification.Skipped, expectedOutput: expectedOutput); CreateCompilation(source, parseOptions: TestOptions.Regular12, targetFramework: TargetFramework.Net80).VerifyDiagnostics( // (8,9): error CS0121: The call is ambiguous between the following methods or properties: 'Program.F2(ReadOnlySpan<string>)' and 'Program.F2(ReadOnlySpan<IFormattable>)' // F2([$"{1}"]); Diagnostic(ErrorCode.ERR_AmbigCall, "F2").WithArguments("Program.F2(System.ReadOnlySpan<string>)", $"Program.F2(System.ReadOnlySpan<{formatType}>)").WithLocation(8, 9) ); } [Fact] public void BetterConversionFromExpression_12() { string source = """ using System; M1([$"{1}"]); partial class Program { static void M1(System.ReadOnlySpan<string> x) => Console.WriteLine("ReadOnlySpan<string>"); static void M1(System.Span<CustomHandler> x) => Console.WriteLine("Span<CustomHandler>"); } partial class CustomHandler { public static implicit operator CustomHandler(string s) => new CustomHandler(0, 0); } """; var handler = GetInterpolatedStringCustomHandlerType("CustomHandler", "partial class", useBoolReturns: false, includeOneTimeHelpers: false); string expectedOutput13 = IncludeExpectedOutput("Span<CustomHandler>"); CompileAndVerify( new[] { source, handler }, targetFramework: TargetFramework.Net80, parseOptions: TestOptions.Regular13, verify: Verification.Skipped, expectedOutput: expectedOutput13); CompileAndVerify( new[] { source, handler }, targetFramework: TargetFramework.Net80, parseOptions: TestOptions.RegularPreview, verify: Verification.Skipped, expectedOutput: expectedOutput13); CompileAndVerify( new[] { source, handler }, targetFramework: TargetFramework.Net80, parseOptions: TestOptions.Regular12, verify: Verification.Skipped, expectedOutput: IncludeExpectedOutput("ReadOnlySpan<string>")); } [Theory] [CombinatorialData] public void BetterConversionFromExpression_13(bool dynamicReadOnlySpan) { var spanType = dynamicReadOnlySpan ? "Span<object>" : "Span<dynamic>"; var readOnlySpanType = dynamicReadOnlySpan ? "ReadOnlySpan<dynamic>" : "ReadOnlySpan<object>"; var source = $$""" using System; using System.Collections.Generic; object o = null; dynamic d = null; M1([o, o]); M1([d, d]); M1([o, d]); M1([d, o]); M2([o, o]); M2([d, d]); M2([o, d]); M2([d, o]); IEnumerable<object> x = []; IEnumerable<dynamic> y = []; M1([..x]); M1([..y]); M1([o, ..x]); M1([d, ..x]); M1([..y, o]); M1([..y, d]); M1([..x, ..y]); M1([..y, ..x]); partial class Program { static void M1({{spanType}} s) => Console.WriteLine("{{spanType}}"); static void M1({{readOnlySpanType}} s) => Console.WriteLine("{{readOnlySpanType}}"); static void M2({{readOnlySpanType}} s) => Console.WriteLine("{{readOnlySpanType}}"); static void M2({{spanType}} s) => Console.WriteLine("{{spanType}}"); } """; string expectedOutput = IncludeExpectedOutput($""" {readOnlySpanType} {readOnlySpanType} {readOnlySpanType} {readOnlySpanType} {readOnlySpanType} {readOnlySpanType} {readOnlySpanType} {readOnlySpanType} {readOnlySpanType} {readOnlySpanType} {readOnlySpanType} {readOnlySpanType} {readOnlySpanType} {readOnlySpanType} {readOnlySpanType} {readOnlySpanType} """); CompileAndVerify(source, verify: Verification.Skipped, targetFramework: TargetFramework.Net80, parseOptions: TestOptions.Regular13, expectedOutput: expectedOutput); CompileAndVerify(source, verify: Verification.Skipped, targetFramework: TargetFramework.Net80, parseOptions: TestOptions.RegularPreview, expectedOutput: expectedOutput); CompileAndVerify(source, verify: Verification.Skipped, targetFramework: TargetFramework.Net80, parseOptions: TestOptions.Regular12, expectedOutput: expectedOutput); } [Fact] public void BetterConversionFromExpression_14() { var source = $$""" using System.Collections.Generic; object o = null; dynamic d = null; o.M1([o, o]); o.M1([d, d]); o.M1([o, d]); o.M1([d, o]); IEnumerable<object> x = []; IEnumerable<dynamic> y = []; o.M1([..x]); o.M1([..y]); o.M1([o, ..x]); o.M1([d, ..x]); o.M1([..y, o]); o.M1([..y, d]); o.M1([..x, ..y]); o.M1([..y, ..x]); static class Ext1 { public static void M1(this object o, List<dynamic> d) { } } static class Ext2 { public static void M1(this object o, List<object> d) { } } """; var expectedDiagnostics = new[] { // (6,3): error CS0121: The call is ambiguous between the following methods or properties: 'Ext1.M1(object, List<dynamic>)' and 'Ext2.M1(object, List<object>)' // o.M1([o, o]); Diagnostic(ErrorCode.ERR_AmbigCall, "M1").WithArguments("Ext1.M1(object, System.Collections.Generic.List<dynamic>)", "Ext2.M1(object, System.Collections.Generic.List<object>)").WithLocation(6, 3), // (7,3): error CS0121: The call is ambiguous between the following methods or properties: 'Ext1.M1(object, List<dynamic>)' and 'Ext2.M1(object, List<object>)' // o.M1([d, d]); Diagnostic(ErrorCode.ERR_AmbigCall, "M1").WithArguments("Ext1.M1(object, System.Collections.Generic.List<dynamic>)", "Ext2.M1(object, System.Collections.Generic.List<object>)").WithLocation(7, 3), // (8,3): error CS0121: The call is ambiguous between the following methods or properties: 'Ext1.M1(object, List<dynamic>)' and 'Ext2.M1(object, List<object>)' // o.M1([o, d]); Diagnostic(ErrorCode.ERR_AmbigCall, "M1").WithArguments("Ext1.M1(object, System.Collections.Generic.List<dynamic>)", "Ext2.M1(object, System.Collections.Generic.List<object>)").WithLocation(8, 3), // (9,3): error CS0121: The call is ambiguous between the following methods or properties: 'Ext1.M1(object, List<dynamic>)' and 'Ext2.M1(object, List<object>)' // o.M1([d, o]); Diagnostic(ErrorCode.ERR_AmbigCall, "M1").WithArguments("Ext1.M1(object, System.Collections.Generic.List<dynamic>)", "Ext2.M1(object, System.Collections.Generic.List<object>)").WithLocation(9, 3), // (13,3): error CS0121: The call is ambiguous between the following methods or properties: 'Ext1.M1(object, List<dynamic>)' and 'Ext2.M1(object, List<object>)' // o.M1([..x]); Diagnostic(ErrorCode.ERR_AmbigCall, "M1").WithArguments("Ext1.M1(object, System.Collections.Generic.List<dynamic>)", "Ext2.M1(object, System.Collections.Generic.List<object>)").WithLocation(13, 3), // (14,3): error CS0121: The call is ambiguous between the following methods or properties: 'Ext1.M1(object, List<dynamic>)' and 'Ext2.M1(object, List<object>)' // o.M1([..y]); Diagnostic(ErrorCode.ERR_AmbigCall, "M1").WithArguments("Ext1.M1(object, System.Collections.Generic.List<dynamic>)", "Ext2.M1(object, System.Collections.Generic.List<object>)").WithLocation(14, 3), // (15,3): error CS0121: The call is ambiguous between the following methods or properties: 'Ext1.M1(object, List<dynamic>)' and 'Ext2.M1(object, List<object>)' // o.M1([o, ..x]); Diagnostic(ErrorCode.ERR_AmbigCall, "M1").WithArguments("Ext1.M1(object, System.Collections.Generic.List<dynamic>)", "Ext2.M1(object, System.Collections.Generic.List<object>)").WithLocation(15, 3), // (16,3): error CS0121: The call is ambiguous between the following methods or properties: 'Ext1.M1(object, List<dynamic>)' and 'Ext2.M1(object, List<object>)' // o.M1([d, ..x]); Diagnostic(ErrorCode.ERR_AmbigCall, "M1").WithArguments("Ext1.M1(object, System.Collections.Generic.List<dynamic>)", "Ext2.M1(object, System.Collections.Generic.List<object>)").WithLocation(16, 3), // (17,3): error CS0121: The call is ambiguous between the following methods or properties: 'Ext1.M1(object, List<dynamic>)' and 'Ext2.M1(object, List<object>)' // o.M1([..y, o]); Diagnostic(ErrorCode.ERR_AmbigCall, "M1").WithArguments("Ext1.M1(object, System.Collections.Generic.List<dynamic>)", "Ext2.M1(object, System.Collections.Generic.List<object>)").WithLocation(17, 3), // (18,3): error CS0121: The call is ambiguous between the following methods or properties: 'Ext1.M1(object, List<dynamic>)' and 'Ext2.M1(object, List<object>)' // o.M1([..y, d]); Diagnostic(ErrorCode.ERR_AmbigCall, "M1").WithArguments("Ext1.M1(object, System.Collections.Generic.List<dynamic>)", "Ext2.M1(object, System.Collections.Generic.List<object>)").WithLocation(18, 3), // (19,3): error CS0121: The call is ambiguous between the following methods or properties: 'Ext1.M1(object, List<dynamic>)' and 'Ext2.M1(object, List<object>)' // o.M1([..x, ..y]); Diagnostic(ErrorCode.ERR_AmbigCall, "M1").WithArguments("Ext1.M1(object, System.Collections.Generic.List<dynamic>)", "Ext2.M1(object, System.Collections.Generic.List<object>)").WithLocation(19, 3), // (20,3): error CS0121: The call is ambiguous between the following methods or properties: 'Ext1.M1(object, List<dynamic>)' and 'Ext2.M1(object, List<object>)' // o.M1([..y, ..x]); Diagnostic(ErrorCode.ERR_AmbigCall, "M1").WithArguments("Ext1.M1(object, System.Collections.Generic.List<dynamic>)", "Ext2.M1(object, System.Collections.Generic.List<object>)").WithLocation(20, 3) }; CreateCompilation(source, targetFramework: TargetFramework.Net80, parseOptions: TestOptions.Regular13).VerifyDiagnostics(expectedDiagnostics); CreateCompilation(source, targetFramework: TargetFramework.Net80, parseOptions: TestOptions.RegularPreview).VerifyDiagnostics(expectedDiagnostics); CreateCompilation(source, targetFramework: TargetFramework.Net80, parseOptions: TestOptions.Regular12).VerifyDiagnostics(expectedDiagnostics); } [Theory] [CombinatorialData] public void BetterConversionFromExpression_15(bool dynamicList) { var ienumerableType = dynamicList ? "IEnumerable<object>" : "IEnumerable<dynamic>"; var listType = dynamicList ? "List<dynamic>" : "List<object>"; var source = $$""" using System; using System.Collections.Generic; object o = null; dynamic d = null; M1([o, o]); M1([d, d]); M1([o, d]); M1([d, o]); M2([o, o]); M2([d, d]); M2([o, d]); M2([d, o]); IEnumerable<object> x = []; IEnumerable<dynamic> y = []; M1([..x]); M1([..y]); M1([o, ..x]); M1([d, ..x]); M1([..y, o]); M1([..y, d]); M1([..x, ..y]); M1([..y, ..x]); partial class Program { static void M1({{ienumerableType}} s) => Console.WriteLine("{{ienumerableType}}"); static void M1({{listType}} s) => Console.WriteLine("{{listType}}"); static void M2({{listType}} s) => Console.WriteLine("{{listType}}"); static void M2({{ienumerableType}} s) => Console.WriteLine("{{ienumerableType}}"); } """; string expectedOutput = IncludeExpectedOutput($""" {listType} {listType} {listType} {listType} {listType} {listType} {listType} {listType} {listType} {listType} {listType} {listType} {listType} {listType} {listType} {listType} """); CompileAndVerify(source, verify: Verification.Skipped, targetFramework: TargetFramework.Net80, parseOptions: TestOptions.Regular13, expectedOutput: expectedOutput); CompileAndVerify(source, verify: Verification.Skipped, targetFramework: TargetFramework.Net80, parseOptions: TestOptions.RegularPreview, expectedOutput: expectedOutput); CompileAndVerify(source, verify: Verification.Skipped, targetFramework: TargetFramework.Net80, parseOptions: TestOptions.Regular12, expectedOutput: expectedOutput); } [Theory] [CombinatorialData] public void BetterConversionFromExpression_16(bool dynamicList) { var hashSetType = dynamicList ? "HashSet<object>" : "HashSet<dynamic>"; var listType = dynamicList ? "List<dynamic>" : "List<object>"; var source = $$""" using System.Collections.Generic; object o = null; dynamic d = null; M1([o, o]); M1([d, d]); M1([o, d]); M1([d, o]); M2([o, o]); M2([d, d]); M2([o, d]); M2([d, o]); IEnumerable<object> x = []; IEnumerable<dynamic> y = []; M1([..x]); M1([..y]); M1([o, ..x]); M1([d, ..x]); M1([..y, o]); M1([..y, d]); M1([..x, ..y]); M1([..y, ..x]); partial class Program { static void M1({{hashSetType}} s) { } static void M1({{listType}} s) { } static void M2({{listType}} s) { } static void M2({{hashSetType}} s) { } } """; var expectedDiagnostics = new[] { // (6,1): error CS0121: The call is ambiguous between the following methods or properties: 'Program.M1(HashSet<dynamic>)' and 'Program.M1(List<object>)' // M1([o, o]); Diagnostic(ErrorCode.ERR_AmbigCall, "M1").WithArguments(generateMethodSignature("M1", hashSetType), generateMethodSignature("M1", listType)).WithLocation(6, 1), // (7,1): error CS0121: The call is ambiguous between the following methods or properties: 'Program.M1(HashSet<dynamic>)' and 'Program.M1(List<object>)' // M1([d, d]); Diagnostic(ErrorCode.ERR_AmbigCall, "M1").WithArguments(generateMethodSignature("M1", hashSetType), generateMethodSignature("M1", listType)).WithLocation(7, 1), // (8,1): error CS0121: The call is ambiguous between the following methods or properties: 'Program.M1(HashSet<dynamic>)' and 'Program.M1(List<object>)' // M1([o, d]); Diagnostic(ErrorCode.ERR_AmbigCall, "M1").WithArguments(generateMethodSignature("M1", hashSetType), generateMethodSignature("M1", listType)).WithLocation(8, 1), // (9,1): error CS0121: The call is ambiguous between the following methods or properties: 'Program.M1(HashSet<dynamic>)' and 'Program.M1(List<object>)' // M1([d, o]); Diagnostic(ErrorCode.ERR_AmbigCall, "M1").WithArguments(generateMethodSignature("M1", hashSetType), generateMethodSignature("M1", listType)).WithLocation(9, 1), // (10,1): error CS0121: The call is ambiguous between the following methods or properties: 'Program.M2(List<object>)' and 'Program.M2(HashSet<dynamic>)' // M2([o, o]); Diagnostic(ErrorCode.ERR_AmbigCall, "M2").WithArguments(generateMethodSignature("M2", listType),generateMethodSignature("M2", hashSetType)).WithLocation(10, 1), // (11,1): error CS0121: The call is ambiguous between the following methods or properties: 'Program.M2(List<object>)' and 'Program.M2(HashSet<dynamic>)' // M2([d, d]); Diagnostic(ErrorCode.ERR_AmbigCall, "M2").WithArguments(generateMethodSignature("M2", listType),generateMethodSignature("M2", hashSetType)).WithLocation(11, 1), // (12,1): error CS0121: The call is ambiguous between the following methods or properties: 'Program.M2(List<object>)' and 'Program.M2(HashSet<dynamic>)' // M2([o, d]); Diagnostic(ErrorCode.ERR_AmbigCall, "M2").WithArguments(generateMethodSignature("M2", listType),generateMethodSignature("M2", hashSetType)).WithLocation(12, 1), // (13,1): error CS0121: The call is ambiguous between the following methods or properties: 'Program.M2(List<object>)' and 'Program.M2(HashSet<dynamic>)' // M2([d, o]); Diagnostic(ErrorCode.ERR_AmbigCall, "M2").WithArguments(generateMethodSignature("M2", listType),generateMethodSignature("M2", hashSetType)).WithLocation(13, 1), // (17,1): error CS0121: The call is ambiguous between the following methods or properties: 'Program.M1(HashSet<dynamic>)' and 'Program.M1(List<object>)' // M1([..x]); Diagnostic(ErrorCode.ERR_AmbigCall, "M1").WithArguments(generateMethodSignature("M1", hashSetType), generateMethodSignature("M1", listType)).WithLocation(17, 1), // (18,1): error CS0121: The call is ambiguous between the following methods or properties: 'Program.M1(HashSet<dynamic>)' and 'Program.M1(List<object>)' // M1([..y]); Diagnostic(ErrorCode.ERR_AmbigCall, "M1").WithArguments(generateMethodSignature("M1", hashSetType), generateMethodSignature("M1", listType)).WithLocation(18, 1), // (19,1): error CS0121: The call is ambiguous between the following methods or properties: 'Program.M1(HashSet<dynamic>)' and 'Program.M1(List<object>)' // M1([o, ..x]); Diagnostic(ErrorCode.ERR_AmbigCall, "M1").WithArguments(generateMethodSignature("M1", hashSetType), generateMethodSignature("M1", listType)).WithLocation(19, 1), // (20,1): error CS0121: The call is ambiguous between the following methods or properties: 'Program.M1(HashSet<dynamic>)' and 'Program.M1(List<object>)' // M1([d, ..x]); Diagnostic(ErrorCode.ERR_AmbigCall, "M1").WithArguments(generateMethodSignature("M1", hashSetType), generateMethodSignature("M1", listType)).WithLocation(20, 1), // (21,1): error CS0121: The call is ambiguous between the following methods or properties: 'Program.M1(HashSet<dynamic>)' and 'Program.M1(List<object>)' // M1([..y, o]); Diagnostic(ErrorCode.ERR_AmbigCall, "M1").WithArguments(generateMethodSignature("M1", hashSetType), generateMethodSignature("M1", listType)).WithLocation(21, 1), // (22,1): error CS0121: The call is ambiguous between the following methods or properties: 'Program.M1(HashSet<dynamic>)' and 'Program.M1(List<object>)' // M1([..y, d]); Diagnostic(ErrorCode.ERR_AmbigCall, "M1").WithArguments(generateMethodSignature("M1", hashSetType), generateMethodSignature("M1", listType)).WithLocation(22, 1), // (23,1): error CS0121: The call is ambiguous between the following methods or properties: 'Program.M1(HashSet<dynamic>)' and 'Program.M1(List<object>)' // M1([..x, ..y]); Diagnostic(ErrorCode.ERR_AmbigCall, "M1").WithArguments(generateMethodSignature("M1", hashSetType), generateMethodSignature("M1", listType)).WithLocation(23, 1), // (24,1): error CS0121: The call is ambiguous between the following methods or properties: 'Program.M1(HashSet<dynamic>)' and 'Program.M1(List<object>)' // M1([..y, ..x]); Diagnostic(ErrorCode.ERR_AmbigCall, "M1").WithArguments(generateMethodSignature("M1", hashSetType), generateMethodSignature("M1", listType)).WithLocation(24, 1) }; CreateCompilation(source, targetFramework: TargetFramework.Net80, parseOptions: TestOptions.Regular13).VerifyDiagnostics(expectedDiagnostics); CreateCompilation(source, targetFramework: TargetFramework.Net80, parseOptions: TestOptions.RegularPreview).VerifyDiagnostics(expectedDiagnostics); CreateCompilation(source, targetFramework: TargetFramework.Net80, parseOptions: TestOptions.Regular12).VerifyDiagnostics(expectedDiagnostics); static string generateMethodSignature(string methodName, string parameterType) => $"Program.{methodName}(System.Collections.Generic.{parameterType})"; } [Fact] public void BetterConversionFromExpression_17() { var source = $$""" using System; using System.Collections.Generic; M1([(a: 1, b: 2)]); M1([(a: 1, d: 2)]); M1([(c: 1, b: 2)]); M1([(c: 1, d: 2)]); M2([(a: 1, b: 2)]); M2([(a: 1, d: 2)]); M2([(c: 1, b: 2)]); M2([(c: 1, d: 2)]); IEnumerable<(int a, int b)> x = []; IEnumerable<(int c, int d)> y = []; M1([..x]); M1([..y]); M1([(a: 1, b: 2), ..x]); M1([(c: 3, d: 4), ..x]); M1([..y, (a: 5, b: 6)]); M1([..y, (c: 7, d: 8)]); M1([..x, ..y]); M1([..y, ..x]); partial class Program { static void M1(Span<(int a, int b)> s) => Console.WriteLine("a, b"); static void M1(ReadOnlySpan<(int c, int d)> s) => Console.WriteLine("c, d"); static void M2(ReadOnlySpan<(int c, int d)> s) => Console.WriteLine("c, d"); static void M2(Span<(int a, int b)> s) => Console.WriteLine("a, b"); } """; string expectedOutput = IncludeExpectedOutput($""" c, d c, d c, d c, d c, d c, d c, d c, d c, d c, d c, d c, d c, d c, d c, d c, d """); CompileAndVerify(source, verify: Verification.Skipped, targetFramework: TargetFramework.Net80, parseOptions: TestOptions.Regular13, expectedOutput: expectedOutput); CompileAndVerify(source, verify: Verification.Skipped, targetFramework: TargetFramework.Net80, parseOptions: TestOptions.RegularPreview, expectedOutput: expectedOutput); CompileAndVerify(source, verify: Verification.Skipped, targetFramework: TargetFramework.Net80, parseOptions: TestOptions.Regular12, expectedOutput: expectedOutput); } [Fact] public void BetterConversionFromExpression_18() { var source = $$""" using System; using System.Collections.Generic; object o = null; o.M1([(a: 1, b: 2)]); o.M1([(a: 1, d: 2)]); o.M1([(c: 1, b: 2)]); o.M1([(c: 1, d: 2)]); IEnumerable<(int a, int b)> x = []; IEnumerable<(int c, int d)> y = []; o.M1([..x]); o.M1([..y]); o.M1([(a: 1, b: 2), ..x]); o.M1([(c: 3, d: 4), ..x]); o.M1([..y, (a: 5, b: 6)]); o.M1([..y, (c: 7, d: 8)]); o.M1([..x, ..y]); o.M1([..y, ..x]); static class Ex1 { public static void M1(this object o, List<(int a, int b)> s) => Console.WriteLine("a, b"); } static class Ex2 { public static void M1(this object o, List<(int c, int d)> s) => Console.WriteLine("c, d"); } """; var expectedDiagnostics = new[] { // (6,3): error CS0121: The call is ambiguous between the following methods or properties: 'Ex1.M1(object, List<(int a, int b)>)' and 'Ex2.M1(object, List<(int c, int d)>)' // o.M1([(a: 1, b: 2)]); Diagnostic(ErrorCode.ERR_AmbigCall, "M1").WithArguments("Ex1.M1(object, System.Collections.Generic.List<(int a, int b)>)", "Ex2.M1(object, System.Collections.Generic.List<(int c, int d)>)").WithLocation(6, 3), // (7,3): error CS0121: The call is ambiguous between the following methods or properties: 'Ex1.M1(object, List<(int a, int b)>)' and 'Ex2.M1(object, List<(int c, int d)>)' // o.M1([(a: 1, d: 2)]); Diagnostic(ErrorCode.ERR_AmbigCall, "M1").WithArguments("Ex1.M1(object, System.Collections.Generic.List<(int a, int b)>)", "Ex2.M1(object, System.Collections.Generic.List<(int c, int d)>)").WithLocation(7, 3), // (8,3): error CS0121: The call is ambiguous between the following methods or properties: 'Ex1.M1(object, List<(int a, int b)>)' and 'Ex2.M1(object, List<(int c, int d)>)' // o.M1([(c: 1, b: 2)]); Diagnostic(ErrorCode.ERR_AmbigCall, "M1").WithArguments("Ex1.M1(object, System.Collections.Generic.List<(int a, int b)>)", "Ex2.M1(object, System.Collections.Generic.List<(int c, int d)>)").WithLocation(8, 3), // (9,3): error CS0121: The call is ambiguous between the following methods or properties: 'Ex1.M1(object, List<(int a, int b)>)' and 'Ex2.M1(object, List<(int c, int d)>)' // o.M1([(c: 1, d: 2)]); Diagnostic(ErrorCode.ERR_AmbigCall, "M1").WithArguments("Ex1.M1(object, System.Collections.Generic.List<(int a, int b)>)", "Ex2.M1(object, System.Collections.Generic.List<(int c, int d)>)").WithLocation(9, 3), // (13,3): error CS0121: The call is ambiguous between the following methods or properties: 'Ex1.M1(object, List<(int a, int b)>)' and 'Ex2.M1(object, List<(int c, int d)>)' // o.M1([..x]); Diagnostic(ErrorCode.ERR_AmbigCall, "M1").WithArguments("Ex1.M1(object, System.Collections.Generic.List<(int a, int b)>)", "Ex2.M1(object, System.Collections.Generic.List<(int c, int d)>)").WithLocation(13, 3), // (14,3): error CS0121: The call is ambiguous between the following methods or properties: 'Ex1.M1(object, List<(int a, int b)>)' and 'Ex2.M1(object, List<(int c, int d)>)' // o.M1([..y]); Diagnostic(ErrorCode.ERR_AmbigCall, "M1").WithArguments("Ex1.M1(object, System.Collections.Generic.List<(int a, int b)>)", "Ex2.M1(object, System.Collections.Generic.List<(int c, int d)>)").WithLocation(14, 3), // (15,3): error CS0121: The call is ambiguous between the following methods or properties: 'Ex1.M1(object, List<(int a, int b)>)' and 'Ex2.M1(object, List<(int c, int d)>)' // o.M1([(a: 1, b: 2), ..x]); Diagnostic(ErrorCode.ERR_AmbigCall, "M1").WithArguments("Ex1.M1(object, System.Collections.Generic.List<(int a, int b)>)", "Ex2.M1(object, System.Collections.Generic.List<(int c, int d)>)").WithLocation(15, 3), // (16,3): error CS0121: The call is ambiguous between the following methods or properties: 'Ex1.M1(object, List<(int a, int b)>)' and 'Ex2.M1(object, List<(int c, int d)>)' // o.M1([(c: 3, d: 4), ..x]); Diagnostic(ErrorCode.ERR_AmbigCall, "M1").WithArguments("Ex1.M1(object, System.Collections.Generic.List<(int a, int b)>)", "Ex2.M1(object, System.Collections.Generic.List<(int c, int d)>)").WithLocation(16, 3), // (17,3): error CS0121: The call is ambiguous between the following methods or properties: 'Ex1.M1(object, List<(int a, int b)>)' and 'Ex2.M1(object, List<(int c, int d)>)' // o.M1([..y, (a: 5, b: 6)]); Diagnostic(ErrorCode.ERR_AmbigCall, "M1").WithArguments("Ex1.M1(object, System.Collections.Generic.List<(int a, int b)>)", "Ex2.M1(object, System.Collections.Generic.List<(int c, int d)>)").WithLocation(17, 3), // (18,3): error CS0121: The call is ambiguous between the following methods or properties: 'Ex1.M1(object, List<(int a, int b)>)' and 'Ex2.M1(object, List<(int c, int d)>)' // o.M1([..y, (c: 7, d: 8)]); Diagnostic(ErrorCode.ERR_AmbigCall, "M1").WithArguments("Ex1.M1(object, System.Collections.Generic.List<(int a, int b)>)", "Ex2.M1(object, System.Collections.Generic.List<(int c, int d)>)").WithLocation(18, 3), // (19,3): error CS0121: The call is ambiguous between the following methods or properties: 'Ex1.M1(object, List<(int a, int b)>)' and 'Ex2.M1(object, List<(int c, int d)>)' // o.M1([..x, ..y]); Diagnostic(ErrorCode.ERR_AmbigCall, "M1").WithArguments("Ex1.M1(object, System.Collections.Generic.List<(int a, int b)>)", "Ex2.M1(object, System.Collections.Generic.List<(int c, int d)>)").WithLocation(19, 3), // (20,3): error CS0121: The call is ambiguous between the following methods or properties: 'Ex1.M1(object, List<(int a, int b)>)' and 'Ex2.M1(object, List<(int c, int d)>)' // o.M1([..y, ..x]); Diagnostic(ErrorCode.ERR_AmbigCall, "M1").WithArguments("Ex1.M1(object, System.Collections.Generic.List<(int a, int b)>)", "Ex2.M1(object, System.Collections.Generic.List<(int c, int d)>)").WithLocation(20, 3) }; CreateCompilation(source, targetFramework: TargetFramework.Net80, parseOptions: TestOptions.Regular13).VerifyDiagnostics(expectedDiagnostics); CreateCompilation(source, targetFramework: TargetFramework.Net80, parseOptions: TestOptions.RegularPreview).VerifyDiagnostics(expectedDiagnostics); CreateCompilation(source, targetFramework: TargetFramework.Net80, parseOptions: TestOptions.Regular12).VerifyDiagnostics(expectedDiagnostics); } [Theory] [CombinatorialData] public void BetterConversionFromExpression_19( [CombinatorialValues( "List<int>", "List<int?>", "List<byte>", "List<object>", "List<(int, int)>" )] string type1, [CombinatorialValues( "List<int>", "List<int?>", "List<byte>", "List<object>", "List<(int, int)>" )] string type2) { if (type1 == type2) { return; } var source = $$""" using System; using System.Collections.Generic; M1([default]); M1([new()]); M1(default, default); M1(new(), new()); partial class Program { static void M1(params {{type1}} s) => Console.WriteLine("{{type1}}"); static void M1(params {{type2}} s) => Console.WriteLine("{{type2}}"); } """; var comp = CreateCompilation(source); string expectedType; switch (type1, type2) { // (int, int) vs everything but object is ambiguous case ("List<(int, int)>", not "List<object>"): case (not "List<object>", "List<(int, int)>"): comp.VerifyDiagnostics( // (4,1): error CS0121: The call is ambiguous between the following methods or properties: 'Program.M1(params List<int?>)' and 'Program.M1(params List<(int, int)>)' // M1([default]); Diagnostic(ErrorCode.ERR_AmbigCall, "M1").WithArguments($"Program.M1(params System.Collections.Generic.{type1})", $"Program.M1(params System.Collections.Generic.{type2})").WithLocation(4, 1), // (5,1): error CS0121: The call is ambiguous between the following methods or properties: 'Program.M1(params List<int?>)' and 'Program.M1(params List<(int, int)>)' // M1([new()]); Diagnostic(ErrorCode.ERR_AmbigCall, "M1").WithArguments($"Program.M1(params System.Collections.Generic.{type1})", $"Program.M1(params System.Collections.Generic.{type2})").WithLocation(5, 1), // (6,1): error CS0121: The call is ambiguous between the following methods or properties: 'Program.M1(params List<int?>)' and 'Program.M1(params List<(int, int)>)' // M1(default, default); Diagnostic(ErrorCode.ERR_AmbigCall, "M1").WithArguments($"Program.M1(params System.Collections.Generic.{type1})", $"Program.M1(params System.Collections.Generic.{type2})").WithLocation(6, 1), // (7,1): error CS0121: The call is ambiguous between the following methods or properties: 'Program.M1(params List<int?>)' and 'Program.M1(params List<(int, int)>)' // M1(new(), new()); Diagnostic(ErrorCode.ERR_AmbigCall, "M1").WithArguments($"Program.M1(params System.Collections.Generic.{type1})", $"Program.M1(params System.Collections.Generic.{type2})").WithLocation(7, 1) ); return; // Then byte is always preferred when present case ("List<byte>", _): case (_, "List<byte>"): expectedType = "List<byte>"; break; // Then int case ("List<int>", _): case (_, "List<int>"): expectedType = "List<int>"; break; // Then int? case ("List<int?>", _): case (_, "List<int?>"): expectedType = "List<int?>"; break; // Finally (int, int). It's only non-ambiguous when the other type is object case ("List<object>", "List<(int, int)>"): case ("List<(int, int)>", "List<object>"): expectedType = "List<(int, int)>"; break; // Unreachable default: throw ExceptionUtilities.Unreachable(); } CompileAndVerify(comp, expectedOutput: $""" {expectedType} {expectedType} {expectedType} {expectedType} """); } [Theory] [InlineData("System.ReadOnlySpan<char>")] [InlineData("System.Span<char>")] public void BetterConversionFromExpression_String_01(string spanType) { string source = $$""" using static System.Console; class Program { static void F1({{spanType}} value) { WriteLine("F1({{spanType}})"); } static void F1(string value) { WriteLine("F1(string)"); } static void F2(string value) { WriteLine("F2(string)"); } static void F2({{spanType}} value) { WriteLine("F2({{spanType}})"); } static void Main() { F1([]); F2([]); F1(['a', 'b', 'c']); F2(['1', '2', '3']); } } """; var comp = CreateCompilation( source, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseExe); CompileAndVerify(comp, verify: Verification.Skipped, expectedOutput: IncludeExpectedOutput($$""" F1({{spanType}}) F2({{spanType}}) F1({{spanType}}) F2({{spanType}}) """)); } [Theory] [InlineData("System.ReadOnlySpan<int>")] [InlineData("System.Span<int>")] [InlineData("System.ReadOnlySpan<object>")] [InlineData("System.Span<object>")] public void BetterConversionFromExpression_String_02(string spanType) { string source = $$""" using static System.Console; class Program { static void F1({{spanType}} value) { WriteLine("F1({{spanType}})"); } static void F1(string value) { WriteLine("F1(string)"); } static void F2(string value) { WriteLine("F2(string)"); } static void F2({{spanType}} value) { WriteLine("F2({{spanType}})"); } static void Main() { F1([]); F2([]); F1(['a', 'b', 'c']); F2(['1', '2', '3']); } } """; var comp = CreateCompilation( source, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseExe); CompileAndVerify(comp, verify: Verification.Skipped, expectedOutput: IncludeExpectedOutput($$""" F1({{spanType}}) F2({{spanType}}) F1({{spanType}}) F2({{spanType}}) """)); } [Theory] [InlineData("System.ReadOnlySpan<byte>")] [InlineData("System.Span<byte>")] public void BetterConversionFromExpression_String_03(string spanType) { string source = $$""" using static System.Console; class Program { static void F1({{spanType}} value) { WriteLine("F1({{spanType}})"); } static void F1(string value) { WriteLine("F1(string)"); } static void F2(string value) { WriteLine("F2(string)"); } static void F2({{spanType}} value) { WriteLine("F2({{spanType}})"); } static void Main() { F1([]); F2([]); } } """; var comp = CreateCompilation( source, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseExe); CompileAndVerify(comp, verify: Verification.Skipped, expectedOutput: IncludeExpectedOutput($$""" F1({{spanType}}) F2({{spanType}}) """)); } [Theory] [InlineData("System.ReadOnlySpan<MyChar>")] [InlineData("System.Span<MyChar>")] public void BetterConversionFromExpression_String_04(string spanType) { string source = $$""" using static System.Console; class MyChar { private readonly int _i; public MyChar(int i) { _i = i; } public static implicit operator MyChar(int i) => new MyChar(i); public static implicit operator char(MyChar c) => (char)c._i; } class Program { static void F1({{spanType}} value) { WriteLine("F1({{spanType}})"); } static void F1(string value) { WriteLine("F1(string)"); } static void F2(string value) { WriteLine("F2(string)"); } static void F2({{spanType}} value) { WriteLine("F2({{spanType}})"); } static void Main() { F1([]); F2([]); F1(['a', 'b', 'c']); F2(['1', '2', '3']); } } """; var comp = CreateCompilation( source, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseExe); CompileAndVerify(comp, verify: Verification.Skipped, expectedOutput: IncludeExpectedOutput($$""" F1({{spanType}}) F2({{spanType}}) F1({{spanType}}) F2({{spanType}}) """)); } [Fact] public void BetterConversionFromExpression_String_05() { string source = $$""" using System.Collections.Generic; using static System.Console; class Program { static void F(IEnumerable<char> value) { WriteLine("F(IEnumerable<char>)"); } static void F(string value) { WriteLine("F(string)"); } static void Main() { F([]); F(['a']); } } """; CompileAndVerify(source, expectedOutput: """ F(IEnumerable<char>) F(IEnumerable<char>) """); } [Fact] public void BestCommonType_01() { string source = """ class Program { static void Main() { var x = new[] { new int[0], [1, 2, 3] }; x.Report(includeType: true); var y = new[] { new[] { new int[0] }, [[1, 2, 3]] }; y.Report(includeType: true); } } """; CompileAndVerify(new[] { source, s_collectionExtensions }, expectedOutput: "(System.Int32[][]) [[], [1, 2, 3]], (System.Int32[][][]) [[[]], [[1, 2, 3]]], "); } [Fact] public void BestCommonType_02() { string source = """ class Program { static void Main() { var x = new[] { new byte[0], [1, 2, 3] }; x.Report(includeType: true); var y = new[] { new[] { new byte[0] }, [[1, 2, 3]] }; y.Report(includeType: true); } } """; CompileAndVerify(new[] { source, s_collectionExtensions }, expectedOutput: "(System.Byte[][]) [[], [1, 2, 3]], (System.Byte[][][]) [[[]], [[1, 2, 3]]], "); } [Fact] public void BestCommonType_03() { string source = """ class Program { static void Main(string[] args) { var x = new[] { [""], new object[0] }; var y = new[] { [[""]], [new object[0]] }; } } """; var comp = CreateCompilation(source); comp.VerifyEmitDiagnostics( // (6,17): error CS0826: No best type found for implicitly-typed array // var y = new[] { [[""]], [new object[0]] }; Diagnostic(ErrorCode.ERR_ImplicitlyTypedArrayNoBestType, @"new[] { [[""""]], [new object[0]] }").WithLocation(6, 17)); } [Fact] public void BestCommonType_04() { string source = """ class Program { static void Main(string[] args) { var x = args.Length > 0 ? new int[0] : [1, 2, 3]; x.Report(includeType: true); var y = args.Length == 0 ? [[4, 5]] : new[] { new byte[0] }; y.Report(includeType: true); } } """; CompileAndVerify(new[] { source, s_collectionExtensions }, expectedOutput: "(System.Int32[]) [1, 2, 3], (System.Byte[][]) [[4, 5]], "); } [Fact] public void BestCommonType_05() { string source = """ class Program { static void Main(string[] args) { bool b = args.Length > 0; var y = b ? [new int[0]] : [[1, 2, 3]]; } } """; var comp = CreateCompilation(source); comp.VerifyEmitDiagnostics( // (6,17): error CS0173: Type of conditional expression cannot be determined because there is no implicit conversion between 'collection expression' and 'collection expression' // var y = b ? [new int[0]] : [[1, 2, 3]]; Diagnostic(ErrorCode.ERR_InvalidQM, "b ? [new int[0]] : [[1, 2, 3]]").WithArguments("collection expression", "collection expression").WithLocation(6, 17)); } [Fact] public void TypeInference_01() { string source = """ static class Program { static T F<T>(T a, T b) { return b; } static void Main() { var x = F(["str"]); var y = F([[], [1, 2]]); } } """; var comp = CreateCompilation(source); comp.VerifyEmitDiagnostics( // (9,17): error CS7036: There is no argument given that corresponds to the required parameter 'b' of 'Program.F<T>(T, T)' // var x = F(["str"]); Diagnostic(ErrorCode.ERR_NoCorrespondingArgument, "F").WithArguments("b", "Program.F<T>(T, T)").WithLocation(9, 17), // (10,17): error CS7036: There is no argument given that corresponds to the required parameter 'b' of 'Program.F<T>(T, T)' // var y = F([[], [1, 2]]); Diagnostic(ErrorCode.ERR_NoCorrespondingArgument, "F").WithArguments("b", "Program.F<T>(T, T)").WithLocation(10, 17)); } [Fact] public void TypeInference_02() { string source = """ static class Program { static T F<T>(T a, T b) { return b; } static void Main() { _ = F([new int[0]], new[] { [1, 2, 3] }); } } """; var comp = CreateCompilation(source); comp.VerifyEmitDiagnostics( // (9,29): error CS0826: No best type found for implicitly-typed array // _ = F([new int[0]], new[] { [1, 2, 3] }); Diagnostic(ErrorCode.ERR_ImplicitlyTypedArrayNoBestType, "new[] { [1, 2, 3] }").WithLocation(9, 29)); } [Fact] public void TypeInference_03() { string source = """ class Program { static T[] AsArray1<T>(T[] args) => args; static T[] AsArray2<T>(params T[] args) => args; static void Main() { var a = AsArray1([1, 2, 3]); a.Report(); var b = AsArray2(["4", null]); b.Report(); } } """; CompileAndVerify(new[] { source, s_collectionExtensions }, expectedOutput: "[1, 2, 3], [4, null], "); } [Fact] public void TypeInference_04() { string source = """ class Program { static T[] AsArray<T>(T[] args) { return args; } static void Main() { AsArray([]); AsArray([1, null]); } } """; var comp = CreateCompilation(source); comp.VerifyEmitDiagnostics( // (9,9): error CS0411: The type arguments for method 'Program.AsArray<T>(T[])' cannot be inferred from the usage. Try specifying the type arguments explicitly. // AsArray([]); Diagnostic(ErrorCode.ERR_CantInferMethTypeArgs, "AsArray").WithArguments("Program.AsArray<T>(T[])").WithLocation(9, 9), // (10,21): error CS0037: Cannot convert null to 'int' because it is a non-nullable value type // AsArray([1, null]); Diagnostic(ErrorCode.ERR_ValueCantBeNull, "null").WithArguments("int").WithLocation(10, 21)); } [Fact] public void TypeInference_06() { string source = """ class Program { static T[] AsArray<T>(T[] args) { return args; } static void F(bool b, int x, int y) { var a = AsArray([.. b ? [x] : [y]]); a.Report(); } static void Main() { F(false, 1, 2); } } """; var comp = CreateCompilation(new[] { source, s_collectionExtensions }); comp.VerifyEmitDiagnostics( // 0.cs(9,29): error CS0173: Type of conditional expression cannot be determined because there is no implicit conversion between 'collection expression' and 'collection expression' // var a = AsArray([.. b ? [x] : [y]]); Diagnostic(ErrorCode.ERR_InvalidQM, "b ? [x] : [y]").WithArguments("collection expression", "collection expression").WithLocation(9, 29)); } [Fact] public void TypeInference_07() { string source = """ static class Program { static T[] AsArray<T>(this T[] args) { return args; } static void Main() { var a = [1, 2, 3].AsArray(); a.Report(); } } """; var comp = CreateCompilation(new[] { source, s_collectionExtensions }); comp.VerifyEmitDiagnostics( // 0.cs(9,17): error CS9176: There is no target type for the collection expression. // var a = [1, 2, 3].AsArray(); Diagnostic(ErrorCode.ERR_CollectionExpressionNoTargetType, "[1, 2, 3]").WithLocation(9, 17)); } [Fact] public void TypeInference_08() { string source = """ using System.Collections; using System.Collections.Generic; struct S<T> : IEnumerable<T> { private List<T> _list; public void Add(T t) { _list ??= new List<T>(); _list.Add(t); } public IEnumerator<T> GetEnumerator() { _list ??= new List<T>(); return _list.GetEnumerator(); } IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); } static class Program { static S<T> AsCollection<T>(this S<T> args) { return args; } static void Main() { var a = AsCollection([1, 2, 3]); var b = [4].AsCollection(); } } """; var comp = CreateCompilation(source); comp.VerifyEmitDiagnostics( // (27,17): error CS9176: There is no target type for the collection expression. // var b = [4].AsCollection(); Diagnostic(ErrorCode.ERR_CollectionExpressionNoTargetType, "[4]").WithLocation(27, 17)); } [Fact] public void TypeInference_09() { string source = """ using System.Collections; struct S<T> : IEnumerable { public void Add(T t) { } IEnumerator IEnumerable.GetEnumerator() => null; } static class Program { static S<T> AsCollection<T>(this S<T> args) { return args; } static void Main() { _ = AsCollection([1, 2, 3]); _ = [4].AsCollection(); } } """; var comp = CreateCompilation(source); comp.VerifyEmitDiagnostics( // (15,13): error CS0411: The type arguments for method 'Program.AsCollection<T>(S<T>)' cannot be inferred from the usage. Try specifying the type arguments explicitly. // _ = AsCollection([1, 2, 3]); Diagnostic(ErrorCode.ERR_CantInferMethTypeArgs, "AsCollection").WithArguments("Program.AsCollection<T>(S<T>)").WithLocation(15, 13), // (16,13): error CS9176: There is no target type for the collection expression. // _ = [4].AsCollection(); Diagnostic(ErrorCode.ERR_CollectionExpressionNoTargetType, "[4]").WithLocation(16, 13)); } [Fact] public void TypeInference_10() { string source = """ using System.Collections.Generic; class Program { static T[] F<T>(T[] arg) => arg; static List<T> F<T>(List<T> arg) => arg; static void Main() { _ = F([1, 2, 3]); } } """; var comp = CreateCompilation(source); comp.VerifyEmitDiagnostics( // (8,13): error CS0121: The call is ambiguous between the following methods or properties: 'Program.F<T>(T[])' and 'Program.F<T>(List<T>)' // _ = F([1, 2, 3]); Diagnostic(ErrorCode.ERR_AmbigCall, "F").WithArguments("Program.F<T>(T[])", "Program.F<T>(System.Collections.Generic.List<T>)").WithLocation(8, 13)); } [Fact] public void TypeInference_11() { string source = """ using System.Collections; struct S<T> : IEnumerable { public void Add(T t) { } IEnumerator IEnumerable.GetEnumerator() => throw null; } class Program { static T[] F<T>(T[] arg) => arg; static S<T> F<T>(S<T> arg) => arg; static void Main() { var x = F([1, 2, 3]); x.Report(true); } } """; CompileAndVerify(new[] { source, s_collectionExtensions }, expectedOutput: "(System.Int32[]) [1, 2, 3], "); } [Fact] public void TypeInference_12() { string source = """ class Program { static T[] F<T>(T[] x, T[] y) => x; static void Main() { var x = F(["1"], [(object)"2"]); x.Report(includeType: true); var y = F([(object)"3"], ["4"]); y.Report(includeType: true); } } """; CompileAndVerify(new[] { source, s_collectionExtensions }, expectedOutput: "(System.Object[]) [1], (System.Object[]) [3], "); } [Fact] public void TypeInference_13A() { string source = """ class Program { static T[] F<T>(T[] x, T[] y) => x; static void Main() { var x = F([1], [(long)2]); x.Report(includeType: true); var y = F([(long)3], [4]); y.Report(includeType: true); } } """; CompileAndVerify(new[] { source, s_collectionExtensions }, expectedOutput: "(System.Int64[]) [1], (System.Int64[]) [3], "); } [Fact] public void TypeInference_13B() { string source = """ using System.Collections.Generic; class Program { static HashSet<T> F<T>(HashSet<T> x, HashSet<T> y) => x; static void Main() { var x = F([1], [(long)2]); x.Report(includeType: true); var y = F([(long)3], [4]); y.Report(includeType: true); } } """; CompileAndVerify(new[] { source, s_collectionExtensions }, expectedOutput: "(System.Collections.Generic.HashSet<System.Int64>) [1], (System.Collections.Generic.HashSet<System.Int64>) [3], "); } [Fact] public void TypeInference_14() { string source = """ class Program { static T[] F<T>(T[][] x) => x[0]; static void Main() { var x = F([[1, 2, 3]]); x.Report(includeType: true); } } """; CompileAndVerify(new[] { source, s_collectionExtensions }, expectedOutput: "(System.Int32[]) [1, 2, 3], "); } [Fact] public void TypeInference_15() { string source = """ class Program { static T F0<T>(T[] x, T y) => y; static T[] F1<T>(T[] x, T[] y) => y; static T[] F2<T>(T[][] x, T[][] y) => y[0]; static void Main() { var x = F0(new byte[0], 1); var y = F1(new byte[0], [1, 2]); var z = F2(new[] { new byte[0] }, [[3, 4]]); } } """; var comp = CreateCompilation(source); comp.VerifyEmitDiagnostics( // (8,17): error CS0411: The type arguments for method 'Program.F0<T>(T[], T)' cannot be inferred from the usage. Try specifying the type arguments explicitly. // var x = F0(new byte[0], 1); Diagnostic(ErrorCode.ERR_CantInferMethTypeArgs, "F0").WithArguments("Program.F0<T>(T[], T)").WithLocation(8, 17), // (9,17): error CS0411: The type arguments for method 'Program.F1<T>(T[], T[])' cannot be inferred from the usage. Try specifying the type arguments explicitly. // var y = F1(new byte[0], [1, 2]); Diagnostic(ErrorCode.ERR_CantInferMethTypeArgs, "F1").WithArguments("Program.F1<T>(T[], T[])").WithLocation(9, 17), // (10,17): error CS0411: The type arguments for method 'Program.F2<T>(T[][], T[][])' cannot be inferred from the usage. Try specifying the type arguments explicitly. // var z = F2(new[] { new byte[0] }, [[3, 4]]); Diagnostic(ErrorCode.ERR_CantInferMethTypeArgs, "F2").WithArguments("Program.F2<T>(T[][], T[][])").WithLocation(10, 17)); } [Fact] public void TypeInference_16() { string source = """ class Program { static T[] F1<T>(T[] x, T[] y) => y; static T[] F2<T>(T[][] x, T[][] y) => y[0]; static void Main() { var x = F1([1], [(byte)2]); x.Report(true); var y = F2([[3]], [[(byte)4]]); y.Report(true); } } """; CompileAndVerify(new[] { source, s_collectionExtensions }, expectedOutput: "(System.Int32[]) [2], (System.Int32[]) [4], "); } [Fact] public void TypeInference_17() { string source = """ class Program { static T[] F1<T>(T[] x, T[] y) => y; static T[] F2<T>(T[][] x, T[][] y) => y[0]; static void Main() { var x = F1([(long)1], [(int?)2]); var y = F2([[(int?)3]], [[(long)4]]); } } """; var comp = CreateCompilation(source); comp.VerifyEmitDiagnostics( // (7,17): error CS0411: The type arguments for method 'Program.F1<T>(T[], T[])' cannot be inferred from the usage. Try specifying the type arguments explicitly. // var x = F1([(long)1], [(int?)2]); Diagnostic(ErrorCode.ERR_CantInferMethTypeArgs, "F1").WithArguments("Program.F1<T>(T[], T[])").WithLocation(7, 17), // (8,17): error CS0411: The type arguments for method 'Program.F2<T>(T[][], T[][])' cannot be inferred from the usage. Try specifying the type arguments explicitly. // var y = F2([[(int?)3]], [[(long)4]]); Diagnostic(ErrorCode.ERR_CantInferMethTypeArgs, "F2").WithArguments("Program.F2<T>(T[][], T[][])").WithLocation(8, 17)); } [Fact] public void TypeInference_18() { string source = """ using System.Collections.Generic; class Program { static List<T[]> AsListOfArray<T>(List<T[]> arg) => arg; static void Main() { var x = AsListOfArray([[4, 5], []]); x.Report(true); } } """; CompileAndVerify(new[] { source, s_collectionExtensions }, expectedOutput: "(System.Collections.Generic.List<System.Int32[]>) [[4, 5], []], "); } [Fact] public void TypeInference_19() { string source = """ class Program { static T[] F<T>(T[][] x) => x[1]; static void Main() { var y = F([new byte[0], [1, 2, 3]]); } } """; var comp = CreateCompilation(source); comp.VerifyEmitDiagnostics( // (6,17): error CS0411: The type arguments for method 'Program.F<T>(T[][])' cannot be inferred from the usage. Try specifying the type arguments explicitly. // var y = F([new byte[0], [1, 2, 3]]); Diagnostic(ErrorCode.ERR_CantInferMethTypeArgs, "F").WithArguments("Program.F<T>(T[][])").WithLocation(6, 17)); } [Fact] public void TypeInference_20() { string source = """ class Program { static T[] F<T>(in T[] x, T[] y) => x; static void Main() { var x = F([1], [2]); x.Report(true); var y = F([3], [(object)4]); y.Report(true); } } """; CompileAndVerify(new[] { source, s_collectionExtensions }, expectedOutput: "(System.Int32[]) [1], (System.Object[]) [3], "); } [Fact] public void TypeInference_21() { string source = """ class Program { static T[] F<T>(in T[] x, T[] y) => x; static void Main() { var y = F(in [3], [(object)4]); } } """; var comp = CreateCompilation(source); comp.VerifyEmitDiagnostics( // (6,22): error CS8156: An expression cannot be used in this context because it may not be passed or returned by reference // var y = F(in [3], [(object)4]); Diagnostic(ErrorCode.ERR_RefReturnLvalueExpected, "[3]").WithLocation(6, 22)); } [Fact] public void TypeInference_22() { string source = """ class Program { static T[] F1<T>(T[] x, T[] y) => y; static T[] F2<T>(T[][] x, T[][] y) => y[0]; static void Main() { var x = F1([], [default, 2]); x.Report(true); var y = F2([[null]], [[default, (int?)4]]); y.Report(true); } } """; CompileAndVerify(new[] { source, s_collectionExtensions }, expectedOutput: "(System.Int32[]) [0, 2], (System.Nullable<System.Int32>[]) [null, 4], "); } [Fact] public void TypeInference_23() { string source = """ class Program { static T[] F1<T>(T[] x, T[] y) => y; static T[] F2<T>(T[][] x, T[][] y) => y[0]; static void Main() { var x = F1([], [default]); var y = F2([[null]], [[default]]); } } """; var comp = CreateCompilation(source); comp.VerifyEmitDiagnostics( // (7,17): error CS0411: The type arguments for method 'Program.F1<T>(T[], T[])' cannot be inferred from the usage. Try specifying the type arguments explicitly. // var x = F1([], [default]); Diagnostic(ErrorCode.ERR_CantInferMethTypeArgs, "F1").WithArguments("Program.F1<T>(T[], T[])").WithLocation(7, 17), // (8,17): error CS0411: The type arguments for method 'Program.F2<T>(T[][], T[][])' cannot be inferred from the usage. Try specifying the type arguments explicitly. // var y = F2([[null]], [[default]]); Diagnostic(ErrorCode.ERR_CantInferMethTypeArgs, "F2").WithArguments("Program.F2<T>(T[][], T[][])").WithLocation(8, 17)); } [Fact] public void TypeInference_24() { string source = """ using System; using System.Collections.Generic; class Program { static ReadOnlySpan<T> F1<T>(Span<T> x, ReadOnlySpan<T> y) => y; static List<T> F2<T>(Span<T[]> x, ReadOnlySpan<List<T>> y) => y[0]; static void Main() { var x = F1([], [default, 2]); x.Report(); var y = F2([[null]], [[default, (int?)4]]); y.Report(); } } """; CompileAndVerify( new[] { source, s_collectionExtensionsWithSpan }, targetFramework: TargetFramework.Net70, verify: Verification.Skipped, expectedOutput: IncludeExpectedOutput("[0, 2], [null, 4], ")); } [Fact] public void TypeInference_25() { string source = """ using System; using System.Collections.Generic; class Program { static ReadOnlySpan<T> F1<T>(Span<T> x, ReadOnlySpan<T> y) => y; static List<T> F2<T>(Span<T[]> x, ReadOnlySpan<List<T>> y) => y[0]; static void Main() { var x = F1([], [default]); var y = F2([[null]], [[default]]); } } """; var comp = CreateCompilation(source, targetFramework: TargetFramework.Net70); comp.VerifyEmitDiagnostics( // (9,17): error CS0411: The type arguments for method 'Program.F1<T>(Span<T>, ReadOnlySpan<T>)' cannot be inferred from the usage. Try specifying the type arguments explicitly. // var x = F1([], [default]); Diagnostic(ErrorCode.ERR_CantInferMethTypeArgs, "F1").WithArguments("Program.F1<T>(System.Span<T>, System.ReadOnlySpan<T>)").WithLocation(9, 17), // (10,17): error CS0411: The type arguments for method 'Program.F2<T>(Span<T[]>, ReadOnlySpan<List<T>>)' cannot be inferred from the usage. Try specifying the type arguments explicitly. // var y = F2([[null]], [[default]]); Diagnostic(ErrorCode.ERR_CantInferMethTypeArgs, "F2").WithArguments("Program.F2<T>(System.Span<T[]>, System.ReadOnlySpan<System.Collections.Generic.List<T>>)").WithLocation(10, 17)); } [Fact] public void TypeInference_26() { string source = """ class Program { static void F<T>(T x) { } static void Main() { F([]); F([null, default, 0]); } } """; var comp = CreateCompilation(source); comp.VerifyEmitDiagnostics( // (6,9): error CS0411: The type arguments for method 'Program.F<T>(T)' cannot be inferred from the usage. Try specifying the type arguments explicitly. // F([]); Diagnostic(ErrorCode.ERR_CantInferMethTypeArgs, "F").WithArguments("Program.F<T>(T)").WithLocation(6, 9), // (7,9): error CS0411: The type arguments for method 'Program.F<T>(T)' cannot be inferred from the usage. Try specifying the type arguments explicitly. // F([null, default, 0]); Diagnostic(ErrorCode.ERR_CantInferMethTypeArgs, "F").WithArguments("Program.F<T>(T)").WithLocation(7, 9)); } [Fact] public void TypeInference_27() { string source = """ class Program { static void F<T>(T[,] x) { } static void Main() { F([]); F([null, default, 0]); } } """; var comp = CreateCompilation(source); comp.VerifyEmitDiagnostics( // (6,9): error CS0411: The type arguments for method 'Program.F<T>(T[*,*])' cannot be inferred from the usage. Try specifying the type arguments explicitly. // F([]); Diagnostic(ErrorCode.ERR_CantInferMethTypeArgs, "F").WithArguments("Program.F<T>(T[*,*])").WithLocation(6, 9), // (7,11): error CS9174: Cannot initialize type 'int[*,*]' with a collection expression because the type is not constructible. // F([null, default, 0]); Diagnostic(ErrorCode.ERR_CollectionExpressionTargetTypeNotConstructible, "[null, default, 0]").WithArguments("int[*,*]").WithLocation(7, 11)); } [Fact] public void TypeInference_28() { string source = """ class Program { static void F<T>(string x, T[] y) { } static void Main() { F([], ['B']); F([default], ['B']); F(['A'], ['B']); } } """; var comp = CreateCompilation(source); comp.VerifyEmitDiagnostics( // (6,11): error CS1729: 'string' does not contain a constructor that takes 0 arguments // F([], ['B']); Diagnostic(ErrorCode.ERR_BadCtorArgCount, "[]").WithArguments("string", "0").WithLocation(6, 11), // (7,11): error CS1729: 'string' does not contain a constructor that takes 0 arguments // F([default], ['B']); Diagnostic(ErrorCode.ERR_BadCtorArgCount, "[default]").WithArguments("string", "0").WithLocation(7, 11), // (7,11): error CS1061: 'string' does not contain a definition for 'Add' and no accessible extension method 'Add' accepting a first argument of type 'string' could be found (are you missing a using directive or an assembly reference?) // F([default], ['B']); Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "[default]").WithArguments("string", "Add").WithLocation(7, 11), // (8,11): error CS1729: 'string' does not contain a constructor that takes 0 arguments // F(['A'], ['B']); Diagnostic(ErrorCode.ERR_BadCtorArgCount, "['A']").WithArguments("string", "0").WithLocation(8, 11), // (8,11): error CS1061: 'string' does not contain a definition for 'Add' and no accessible extension method 'Add' accepting a first argument of type 'string' could be found (are you missing a using directive or an assembly reference?) // F(['A'], ['B']); Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "['A']").WithArguments("string", "Add").WithLocation(8, 11)); } [Fact] public void TypeInference_29() { string source = """ delegate void D(); enum E { } class Program { static void F1<T>(dynamic x, T[] y) { } static void F2<T>(D x, T[] y) { } static void F3<T>(E x, T[] y) { } static void Main() { F1([1], [2]); F2([3], [4]); F3([5], [6]); } } """; var comp = CreateCompilation(source); comp.VerifyEmitDiagnostics( // (10,12): error CS9174: Cannot initialize type 'dynamic' with a collection expression because the type is not constructible. // F1([1], [2]); Diagnostic(ErrorCode.ERR_CollectionExpressionTargetTypeNotConstructible, "[1]").WithArguments("dynamic").WithLocation(10, 12), // (11,12): error CS9174: Cannot initialize type 'D' with a collection expression because the type is not constructible. // F2([3], [4]); Diagnostic(ErrorCode.ERR_CollectionExpressionTargetTypeNotConstructible, "[3]").WithArguments("D").WithLocation(11, 12), // (12,12): error CS9174: Cannot initialize type 'E' with a collection expression because the type is not constructible. // F3([5], [6]); Diagnostic(ErrorCode.ERR_CollectionExpressionTargetTypeNotConstructible, "[5]").WithArguments("E").WithLocation(12, 12)); } [Fact] public void TypeInference_30() { string source = """ delegate void D(); enum E { } class Program { static void F1<T>(dynamic[] x, T[] y) { } static void F2<T>(D[] x, T[] y) { } static void F3<T>(E[] x, T[] y) { } static void Main() { F1([1], [2]); F2([null], [4]); F3([default], [6]); } } """; var comp = CreateCompilation(source); comp.VerifyEmitDiagnostics(); } [Fact] public void TypeInference_31() { string source = """ class Program { static void F<T>(T[] x) { } static void Main() { F([null]); F([Unknown]); F([Main()]); } } """; var comp = CreateCompilation(source); comp.VerifyEmitDiagnostics( // (6,9): error CS0411: The type arguments for method 'Program.F<T>(T[])' cannot be inferred from the usage. Try specifying the type arguments explicitly. // F([null]); Diagnostic(ErrorCode.ERR_CantInferMethTypeArgs, "F").WithArguments("Program.F<T>(T[])").WithLocation(6, 9), // (7,12): error CS0103: The name 'Unknown' does not exist in the current context // F([Unknown]); Diagnostic(ErrorCode.ERR_NameNotInContext, "Unknown").WithArguments("Unknown").WithLocation(7, 12), // (8,9): error CS0411: The type arguments for method 'Program.F<T>(T[])' cannot be inferred from the usage. Try specifying the type arguments explicitly. // F([Main()]); Diagnostic(ErrorCode.ERR_CantInferMethTypeArgs, "F").WithArguments("Program.F<T>(T[])").WithLocation(8, 9)); } [Fact] public void TypeInference_32() { string source = """ delegate void D(); class Program { static T[] F<T>(T[] x) => x; static void Main() { var x = F([null, Main]); x.Report(includeType: true); var y = F([Main, (D)Main]); y.Report(includeType: true); } } """; CompileAndVerify(new[] { source, s_collectionExtensions }, expectedOutput: "(System.Action[]) [null, System.Action], (D[]) [D, D], "); } [Fact] public void TypeInference_33() { string source = """ delegate byte D(); class Program { static T[] F<T>(T[] x) => x; static void Main() { var x = F([null, () => 1]); x.Report(includeType: true); var y = F([() => 2, (D)(() => 3)]); y.Report(includeType: true); } } """; CompileAndVerify(new[] { source, s_collectionExtensions }, expectedOutput: "(System.Func<System.Int32>[]) [null, System.Func`1[System.Int32]], (D[]) [D, D], "); } [Fact] public void TypeInference_OutputTypeInference() { string source = """ using System; using System.Collections.Generic; class Program { static List<Func<T>> F1<T>(List<Func<T>> x) => x; static string F2() => null; static void Main() { var x = F1([F2]); x.Report(); var y = F1([null, () => 1]); y.Report(); var z = F1([F2, () => default]); z.Report(); } } """; CompileAndVerify( new[] { source, s_collectionExtensions }, expectedOutput: "[System.Func`1[System.String]], [null, System.Func`1[System.Int32]], [System.Func`1[System.String], System.Func`1[System.String]], "); } [Fact] public void TypeInference_OutputTypeInference_Tuple() { string source = """ using System; class Program { static (Func<T>, int)[] F<T>((Func<T>, int)[] x) { return x; } static void Main() { var x = F([(null, 1), (() => "2", 2)]); x.Report(); } } """; CompileAndVerify( new[] { source, s_collectionExtensions }, expectedOutput: "[(, 1), (System.Func`1[System.String], 2)], "); } // An output type inference from an anonymous function requires the return value has a type. // Collection expressions do not have a natural type, so inference fails as expected. [WorkItem("https://github.com/dotnet/roslyn/issues/69488")] [Fact] public void TypeInference_OutputTypeInference_LambdaExpression() { string source = """ using System; using System.Collections.Generic; class Program { static void TupleResult<T>(Func<(T, T)> x) { Console.WriteLine(typeof(T).Name); } static void CollectionResult<T>(Func<T[]> x) { Console.WriteLine(typeof(T).Name); } static void Main() { TupleResult(() => (1, 2)); CollectionResult(() => new[] { 1, 2 }); CollectionResult(() => [1, 2]); } } """; var comp = CreateCompilation(source); comp.VerifyEmitDiagnostics( // (20,9): error CS0411: The type arguments for method 'Program.CollectionResult<T>(Func<T[]>)' cannot be inferred from the usage. Try specifying the type arguments explicitly. // CollectionResult(() => [1, 2]); Diagnostic(ErrorCode.ERR_CantInferMethTypeArgs, "CollectionResult").WithArguments("Program.CollectionResult<T>(System.Func<T[]>)").WithLocation(20, 9)); } [Fact] public void TypeInference_35() { string source = """ using System; using System.Collections.Generic; class Program { static List<Action<T>> F1<T>(List<Action<T>> x) => x; static void F2(string s) { } static void Main() { var x = F1([F2, (string s) => { }]); x.Report(); var y = F1([null, (int a) => { }]); y.Report(); } } """; CompileAndVerify( new[] { source, s_collectionExtensions }, expectedOutput: "[System.Action`1[System.String], System.Action`1[System.String]], [null, System.Action`1[System.Int32]], "); } [Fact] public void TypeInference_36() { string source = """ using System; using System.Collections.Generic; class Program { static List<Func<T>> F1<T>(List<Func<T>> x) => x; static string F2() => null; static void Main() { var x = F1([() => default]); var y = F1([() => 2, F2]); } } """; var comp = CreateCompilation(source); comp.VerifyEmitDiagnostics( // (9,17): error CS0411: The type arguments for method 'Program.F1<T>(List<Func<T>>)' cannot be inferred from the usage. Try specifying the type arguments explicitly. // var x = F1([() => default]); Diagnostic(ErrorCode.ERR_CantInferMethTypeArgs, "F1").WithArguments("Program.F1<T>(System.Collections.Generic.List<System.Func<T>>)").WithLocation(9, 17), // (10,17): error CS0411: The type arguments for method 'Program.F1<T>(List<Func<T>>)' cannot be inferred from the usage. Try specifying the type arguments explicitly. // var y = F1([null, () => 1]); Diagnostic(ErrorCode.ERR_CantInferMethTypeArgs, "F1").WithArguments("Program.F1<T>(System.Collections.Generic.List<System.Func<T>>)").WithLocation(10, 17)); } [Fact] public void TypeInference_37() { string source = """ class Program { static (T, U)[] F<T, U>((T, U)[] x) => x; static void Main() { var x = F([(1, "2")]); x.Report(includeType: true); var y = F([default, (3, (byte)4)]); y.Report(includeType: true); } } """; CompileAndVerify( new[] { source, s_collectionExtensions }, expectedOutput: "(System.ValueTuple<System.Int32, System.String>[]) [(1, 2)], (System.ValueTuple<System.Int32, System.Byte>[]) [(0, 0), (3, 4)], "); } [Fact] public void TypeInference_38() { string source = """ class Program { static T[] F1<T>(T[] x, T y) => x; static T[] F2<T>(T[] x, ref T y) => x; static T[] F3<T>(T[] x, in T y) => x; static T[] F4<T>(T[] x, out T y) { y = default; return x; } static void Main() { object y = null; var x1 = F1([1], y); var x2 = F2([2], ref y); var x3A = F3([3], y); var x3B = F3([3], in y); var x4 = F4([4], out y); x1.Report(true); x2.Report(true); x3A.Report(true); x3B.Report(true); x4.Report(true); } } """; CompileAndVerify(new[] { source, s_collectionExtensions }, expectedOutput: "(System.Object[]) [1], (System.Object[]) [2], (System.Object[]) [3], (System.Object[]) [3], (System.Object[]) [4], "); } [Fact] public void TypeInference_39A() { string source = """ class Program { static T[] F1<T>(T[] x, T y) => x; static T[] F3<T>(T[] x, in T y) => x; static void Main() { byte y = 0; var x1 = F1([1], y); var x3A = F3([3], y); x1.Report(true); x3A.Report(true); } } """; CompileAndVerify(new[] { source, s_collectionExtensions }, expectedOutput: "(System.Int32[]) [1], (System.Int32[]) [3], "); } [Fact] public void TypeInference_39B() { string source = """ class Program { static T[] F2<T>(T[] x, ref T y) => x; static T[] F3<T>(T[] x, in T y) => x; static T[] F4<T>(T[] x, out T y) { y = default; return x; } static void Main() { byte y = 0; var x2 = F2([2], ref y); var x3B = F3([3], in y); var x4 = F4([4], out y); } } """; var comp = CreateCompilation(source); comp.VerifyEmitDiagnostics( // (9,18): error CS0411: The type arguments for method 'Program.F2<T>(T[], ref T)' cannot be inferred from the usage. Try specifying the type arguments explicitly. // var x2 = F2([2], ref y); Diagnostic(ErrorCode.ERR_CantInferMethTypeArgs, "F2").WithArguments("Program.F2<T>(T[], ref T)").WithLocation(9, 18), // (10,19): error CS0411: The type arguments for method 'Program.F3<T>(T[], in T)' cannot be inferred from the usage. Try specifying the type arguments explicitly. // var x3B = F3([3], in y); Diagnostic(ErrorCode.ERR_CantInferMethTypeArgs, "F3").WithArguments("Program.F3<T>(T[], in T)").WithLocation(10, 19), // (11,18): error CS0411: The type arguments for method 'Program.F4<T>(T[], out T)' cannot be inferred from the usage. Try specifying the type arguments explicitly. // var x4 = F4([4], out y); Diagnostic(ErrorCode.ERR_CantInferMethTypeArgs, "F4").WithArguments("Program.F4<T>(T[], out T)").WithLocation(11, 18)); } [Fact] public void TypeInference_40() { string source = """ using System; class Program { static Func<T[]> F<T>(Func<T[]> arg) => arg; static void Main(string[] args) { var x = F(() => [1, 2, 3]); x.Report(includeType: true); var y = F(() => { if (args.Length == 0) return []; return [1, 2, 3]; }); y.Report(includeType: true); } } """; var comp = CreateCompilation(new[] { source, s_collectionExtensions }); comp.VerifyEmitDiagnostics( // 0.cs(7,17): error CS0411: The type arguments for method 'Program.F<T>(Func<T[]>)' cannot be inferred from the usage. Try specifying the type arguments explicitly. // var x = F(() => [1, 2, 3]); Diagnostic(ErrorCode.ERR_CantInferMethTypeArgs, "F").WithArguments("Program.F<T>(System.Func<T[]>)").WithLocation(7, 17), // 0.cs(9,17): error CS0411: The type arguments for method 'Program.F<T>(Func<T[]>)' cannot be inferred from the usage. Try specifying the type arguments explicitly. // var y = F(() => { if (args.Length == 0) return []; return [1, 2, 3]; }); Diagnostic(ErrorCode.ERR_CantInferMethTypeArgs, "F").WithArguments("Program.F<T>(System.Func<T[]>)").WithLocation(9, 17)); } [Fact] public void TypeInference_Spread_01() { string source = """ class Program { static T[] F<T>(T[] arg) => arg; static void Main() { int[] x = [1, 2]; object[] y = [3]; F([..x]).Report(includeType: true); F([..x, ..y]).Report(includeType: true); F([..y, ..x]).Report(includeType: true); } } """; CompileAndVerify(new[] { source, s_collectionExtensions }, expectedOutput: "(System.Int32[]) [1, 2], (System.Object[]) [1, 2, 3], (System.Object[]) [3, 1, 2], "); } [Fact] public void TypeInference_Spread_02() { string source = """ class Program { static T[] F<T>(T[] arg) => arg; static void Main() { object[] x = [1, 2]; var y = F([..x, 3]); y.Report(includeType: true); } } """; CompileAndVerify(new[] { source, s_collectionExtensions }, expectedOutput: "(System.Object[]) [1, 2, 3], "); } [Fact] public void TypeInference_Spread_03() { string source = """ class Program { static T[] F<T>(T[] arg) => arg; static void Main() { int[] x = [1, 2]; var y = F([..x, (object)3]); y.Report(includeType: true); } } """; CompileAndVerify(new[] { source, s_collectionExtensions }, expectedOutput: "(System.Object[]) [1, 2, 3], "); } // If we allow inference from a spread element _expression_ rather than simply // from the spread element _type_, and if we allow spread element expressions to // be collection expressions, then MethodTypeInferrer.MakeOutputTypeInferences // will probably need to make an output type inference from each element within // the nested spread element collection expression. [Fact] public void TypeInference_Spread_04() { string source = """ using System; class Program { static Func<T>[] F<T>(Func<T>[] arg) => arg; static void Main() { var x = F([null, .. [() => 1]]); x.Report(); } } """; var comp = CreateCompilation(new[] { source, s_collectionExtensions }); comp.VerifyEmitDiagnostics( // 0.cs(7,29): error CS9176: There is no target type for the collection expression. // var x = F([null, .. [() => 1]]); Diagnostic(ErrorCode.ERR_CollectionExpressionNoTargetType, "[() => 1]").WithLocation(7, 29)); } [Fact] public void TypeInference_Spread_05() { string source = """ class Program { static T[] F<T>(T[] arg) => arg; static void Main() { object x = new[] { 2, 3 }; var y = F([..x]); var z = F([1, ..x]); } } """; var comp = CreateCompilation(new[] { source, s_collectionExtensions }); comp.VerifyEmitDiagnostics( // 0.cs(7,22): error CS9212: Spread operator '..' cannot operate on variables of type 'object' because 'object' does not contain a public instance or extension definition for 'GetEnumerator' // var y = F([..x]); Diagnostic(ErrorCode.ERR_SpreadMissingMember, "x").WithArguments("object", "GetEnumerator").WithLocation(7, 22), // 0.cs(8,25): error CS9212: Spread operator '..' cannot operate on variables of type 'object' because 'object' does not contain a public instance or extension definition for 'GetEnumerator' // var z = F([1, ..x]); Diagnostic(ErrorCode.ERR_SpreadMissingMember, "x").WithArguments("object", "GetEnumerator").WithLocation(8, 25)); } [Fact] public void TypeInference_Spread_06() { string source = """ class Program { static T[] F<T>(T[] arg) => arg; static void Main() { dynamic x = new[] { 2, 3 }; var y = F([..x]); y.Report(includeType: true); var z = F([1, ..x]); z.Report(includeType: true); } } """; CompileAndVerify( new[] { source, s_collectionExtensions }, references: new[] { CSharpRef }, expectedOutput: "(System.Object[]) [2, 3], (System.Object[]) [1, 2, 3], "); } [Fact] public void TypeInference_Spread_07() { string source = """ #nullable enable class Program { static void Main() { string[] a = []; string?[] b = []; object[] aa = [..a, ..a]; object[] ab = [..a, ..b]; // 1 object[] bb = [..b, ..b]; // 2 } } """; var comp = CreateCompilation(source); comp.VerifyEmitDiagnostics( // (9,31): warning CS8601: Possible null reference assignment. // object[] ab = [..a, ..b]; // 1 Diagnostic(ErrorCode.WRN_NullReferenceAssignment, "b").WithLocation(9, 31), // (10,26): warning CS8601: Possible null reference assignment. // object[] bb = [..b, ..b]; // 2 Diagnostic(ErrorCode.WRN_NullReferenceAssignment, "b").WithLocation(10, 26), // (10,31): warning CS8601: Possible null reference assignment. // object[] bb = [..b, ..b]; // 2 Diagnostic(ErrorCode.WRN_NullReferenceAssignment, "b").WithLocation(10, 31)); } [Fact] public void TypeInference_Spread_08() { string source = """ #nullable enable using System.Collections.Generic; class Program { static void Main() { IEnumerable<string>[] a = []; IEnumerable<string?>[] b = []; IEnumerable<object>[] aa = [..a, ..a]; IEnumerable<object>[] ab = [..a, ..b]; // 1 IEnumerable<object>[] bb = [..b, ..b]; // 2 } } """; var comp = CreateCompilation(source); comp.VerifyEmitDiagnostics( // (10,44): warning CS8619: Nullability of reference types in value of type 'IEnumerable<string?>' doesn't match target type 'IEnumerable<object>'. // IEnumerable<object>[] ab = [..a, ..b]; // 1 Diagnostic(ErrorCode.WRN_NullabilityMismatchInAssignment, "b").WithArguments("System.Collections.Generic.IEnumerable<string?>", "System.Collections.Generic.IEnumerable<object>").WithLocation(10, 44), // (11,39): warning CS8619: Nullability of reference types in value of type 'IEnumerable<string?>' doesn't match target type 'IEnumerable<object>'. // IEnumerable<object>[] bb = [..b, ..b]; // 2 Diagnostic(ErrorCode.WRN_NullabilityMismatchInAssignment, "b").WithArguments("System.Collections.Generic.IEnumerable<string?>", "System.Collections.Generic.IEnumerable<object>").WithLocation(11, 39), // (11,44): warning CS8619: Nullability of reference types in value of type 'IEnumerable<string?>' doesn't match target type 'IEnumerable<object>'. // IEnumerable<object>[] bb = [..b, ..b]; // 2 Diagnostic(ErrorCode.WRN_NullabilityMismatchInAssignment, "b").WithArguments("System.Collections.Generic.IEnumerable<string?>", "System.Collections.Generic.IEnumerable<object>").WithLocation(11, 44)); } [Fact] public void TypeInference_Spread_09() { string source = """ using System; class Program { static T[] F<T>(T[] arg) => arg; static void Main() { dynamic[] x = new[] { "one", null }; string[] y = [..x]; y.Report(includeType: true); } } """; CompileAndVerify( new[] { source, s_collectionExtensions }, references: new[] { CSharpRef }, expectedOutput: "(System.String[]) [one, null], "); } [Fact] public void TypeInference_Spread_10() { string source = """ using System; class Program { static T[] F<T>(T[] arg) => arg; static void Main() { dynamic[] x = new[] { "one", null }; var y = F([..x]); var z = F([..x, "three"]); y.Report(includeType: true); Console.Write("{0}, ", y[0].Length); z.Report(includeType: true); Console.Write("{0}, ", z[2].Length); } } """; CompileAndVerify( new[] { source, s_collectionExtensions }, references: new[] { CSharpRef }, expectedOutput: "(System.Object[]) [one, null], 3, (System.Object[]) [one, null, three], 5, "); } [Fact] public void Spread_String() { string source = """ class Program { static char[] GetChars() => [.."abcd"]; static void Main() { GetChars().Report(); } } """; var verifier = CompileAndVerify( new[] { source, s_collectionExtensions }, expectedOutput: IncludeExpectedOutput("[a, b, c, d], ")); verifier.VerifyIL("Program.GetChars", """ { // Code size 11 (0xb) .maxstack 1 IL_0000: ldstr "abcd" IL_0005: call "char[] System.Linq.Enumerable.ToArray<char>(System.Collections.Generic.IEnumerable<char>)" IL_000a: ret } """); } [CombinatorialData] [Theory] public void Spread_RefEnumerable(bool useCompilationReference) { string sourceA = """ using System; using System.Collections; using System.Collections.Generic; using System.Runtime.CompilerServices; [CollectionBuilder(typeof(MyCollectionBuilder), nameof(MyCollectionBuilder.Create))] public class MyCollection<T> { private readonly T[] _items; public MyCollection(T[] items) { _items = items; } public MyEnumerator<T> GetEnumerator() => new(_items); } public class MyCollectionBuilder { public static MyCollection<T> Create<T>(ReadOnlySpan<T> items) => new MyCollection<T>(items.ToArray()); } public struct MyEnumerator<T> { private readonly T[] _items; private int _index; public MyEnumerator(T[] items) { _items = items; _index = -1; } public bool MoveNext() { if (_index < _items.Length) _index++; return _index < _items.Length; } public ref T Current => ref _items[_index]; } """; var comp = CreateCompilation(sourceA, targetFramework: TargetFramework.Net80); var refA = AsReference(comp, useCompilationReference); string sourceB1 = """ using System; class Program { static void Main() { MyCollection<int> x = [1, 2, 3]; MyCollection<object> y = [..x, 4]; foreach (int i in y) Console.Write("{0}, ", i); } } """; CompileAndVerify( sourceB1, references: new[] { refA }, targetFramework: TargetFramework.Net80, verify: Verification.FailsPEVerify, expectedOutput: IncludeExpectedOutput("1, 2, 3, 4, ")); string sourceB2 = """ using System; class Program { static MyCollection<T> F<T>(MyCollection<T> c) { return c; } static void Main() { MyCollection<int> x = F([1, 2, 3]); foreach (int i in x) Console.Write("{0}, ", i); MyCollection<int> y = F([..x]); foreach (int i in y) Console.Write("{0}, ", i); } } """; CompileAndVerify( sourceB2, references: new[] { refA }, targetFramework: TargetFramework.Net80, verify: Verification.FailsPEVerify, expectedOutput: IncludeExpectedOutput("1, 2, 3, 1, 2, 3, ")); } [Fact] public void TypeInference_NullableValueType() { string source = """ using System.Collections; using System.Collections.Generic; var a = Program.AsCollection([1, 2, 3]); a.Report(); struct S<T> : IEnumerable<T> { private List<T> _list; public void Add(T t) { _list ??= new List<T>(); _list.Add(t); } public IEnumerator<T> GetEnumerator() { _list ??= new List<T>(); return _list.GetEnumerator(); } IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); } static partial class Program { static S<T>? AsCollection<T>(S<T>? args) => args; } """; var comp = CreateCompilation(new[] { source, s_collectionExtensions }); comp.VerifyEmitDiagnostics(); CompileAndVerify(comp, expectedOutput: "[1, 2, 3],"); var tree = comp.SyntaxTrees.First(); var invocation = tree.GetRoot().DescendantNodes().OfType<InvocationExpressionSyntax>().First(); Assert.Equal("Program.AsCollection([1, 2, 3])", invocation.ToFullString()); var model = comp.GetSemanticModel(tree); Assert.Equal("S<System.Int32>? Program.AsCollection<System.Int32>(S<System.Int32>? args)", model.GetSymbolInfo(invocation).Symbol.ToTestDisplayString()); } [Fact] public void TypeInference_NullableValueType_ExtensionMethod() { string source = """ using System.Collections; using System.Collections.Generic; struct S<T> : IEnumerable<T> { private List<T> _list; public void Add(T t) { _list ??= new List<T>(); _list.Add(t); } public IEnumerator<T> GetEnumerator() { _list ??= new List<T>(); return _list.GetEnumerator(); } IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); } static class Program { static S<T>? AsCollection<T>(this S<T>? args) { return args; } static void Main() { var a = AsCollection([1, 2, 3]); var b = [4].AsCollection(); } } """; var comp = CreateCompilation(source); comp.VerifyEmitDiagnostics( // (27,17): error CS9176: There is no target type for the collection expression. // var b = [4].AsCollection(); Diagnostic(ErrorCode.ERR_CollectionExpressionNoTargetType, "[4]").WithLocation(27, 17) ); } [Fact] public void MemberAccess_01() { string source = """ class Program { static void Main() { [].GetHashCode(); []?.GetHashCode(); [][0].GetHashCode(); } } """; var comp = CreateCompilation(source); comp.VerifyEmitDiagnostics( // (5,9): error CS9176: There is no target type for the collection expression. // [].GetHashCode(); Diagnostic(ErrorCode.ERR_CollectionExpressionNoTargetType, "[]").WithLocation(5, 9), // (6,9): error CS9176: There is no target type for the collection expression. // []?.GetHashCode(); Diagnostic(ErrorCode.ERR_CollectionExpressionNoTargetType, "[]").WithLocation(6, 9), // (7,9): error CS9176: There is no target type for the collection expression. // [][0].GetHashCode(); Diagnostic(ErrorCode.ERR_CollectionExpressionNoTargetType, "[]").WithLocation(7, 9)); } [Fact] public void MemberAccess_02() { string source = """ class Program { static void Main() { [1].GetHashCode(); [2]?.GetHashCode(); [3][0].GetHashCode(); } } """; var comp = CreateCompilation(source); comp.VerifyEmitDiagnostics( // (5,9): error CS9176: There is no target type for the collection expression. // [1].GetHashCode(); Diagnostic(ErrorCode.ERR_CollectionExpressionNoTargetType, "[1]").WithLocation(5, 9), // (6,9): error CS9176: There is no target type for the collection expression. // [2]?.GetHashCode(); Diagnostic(ErrorCode.ERR_CollectionExpressionNoTargetType, "[2]").WithLocation(6, 9), // (7,9): error CS9176: There is no target type for the collection expression. // [3][0].GetHashCode(); Diagnostic(ErrorCode.ERR_CollectionExpressionNoTargetType, "[3]").WithLocation(7, 9)); } [Fact] public void MemberAccess_03() { string source = """ class Program { static void Main() { _ = [].GetHashCode(); _ = []?.GetHashCode(); _ = [][0].GetHashCode(); } } """; var comp = CreateCompilation(source); comp.VerifyEmitDiagnostics( // (5,13): error CS9176: There is no target type for the collection expression. // _ = [].GetHashCode(); Diagnostic(ErrorCode.ERR_CollectionExpressionNoTargetType, "[]").WithLocation(5, 13), // (6,13): error CS9176: There is no target type for the collection expression. // _ = []?.GetHashCode(); Diagnostic(ErrorCode.ERR_CollectionExpressionNoTargetType, "[]").WithLocation(6, 13), // (7,13): error CS9176: There is no target type for the collection expression. // _ = [][0].GetHashCode(); Diagnostic(ErrorCode.ERR_CollectionExpressionNoTargetType, "[]").WithLocation(7, 13)); } [Fact] public void MemberAccess_04() { string source = """ class Program { static void Main() { _ = [1].GetHashCode(); _ = [2]?.GetHashCode(); _ = [3][0].GetHashCode(); } } """; var comp = CreateCompilation(source); comp.VerifyEmitDiagnostics( // (5,13): error CS9176: There is no target type for the collection expression. // _ = [1].GetHashCode(); Diagnostic(ErrorCode.ERR_CollectionExpressionNoTargetType, "[1]").WithLocation(5, 13), // (6,13): error CS9176: There is no target type for the collection expression. // _ = [2]?.GetHashCode(); Diagnostic(ErrorCode.ERR_CollectionExpressionNoTargetType, "[2]").WithLocation(6, 13), // (7,13): error CS9176: There is no target type for the collection expression. // _ = [3][0].GetHashCode(); Diagnostic(ErrorCode.ERR_CollectionExpressionNoTargetType, "[3]").WithLocation(7, 13)); } [WorkItem("https://github.com/dotnet/roslyn/issues/69839")] [Fact] public void ListInterfaces_01() { string sourceA = """ namespace System { public class Object { } public abstract class ValueType { } public class String { } public class Type { } public struct Void { } public struct Boolean { } public struct Int32 { } } namespace System.Collections { public interface IEnumerable { } } namespace System.Collections.Generic { public interface IA { } public interface IB<T> { } public interface IC<T> { } public interface ID<T1, T2> { } public class List<T> : IEnumerable, IA, IB<T>, IC<object>, ID<T, object> { public void Add(T t) { } } } """; string sourceB = """ using System.Collections.Generic; class Program { static void Main() { List<int> l = [1]; IA a = [2]; IB<object> b = [3]; IC<object> c = [4]; ID<object, object> d = [5]; } } """; var comp = CreateEmptyCompilation(new[] { sourceA, sourceB }, parseOptions: TestOptions.RegularPreview.WithNoRefSafetyRulesAttribute()); comp.VerifyEmitDiagnostics( // warning CS8021: No value for RuntimeMetadataVersion found. No assembly containing System.Object was found nor was a value for RuntimeMetadataVersion specified through options. Diagnostic(ErrorCode.WRN_NoRuntimeMetadataVersion).WithLocation(1, 1), // 1.cs(7,16): error CS9174: Cannot initialize type 'IA' with a collection expression because the type is not constructible. // IA a = [2]; Diagnostic(ErrorCode.ERR_CollectionExpressionTargetTypeNotConstructible, "[2]").WithArguments("System.Collections.Generic.IA").WithLocation(7, 16), // 1.cs(8,24): error CS9174: Cannot initialize type 'IB<object>' with a collection expression because the type is not constructible. // IB<object> b = [3]; Diagnostic(ErrorCode.ERR_CollectionExpressionTargetTypeNotConstructible, "[3]").WithArguments("System.Collections.Generic.IB<object>").WithLocation(8, 24), // 1.cs(9,24): error CS9174: Cannot initialize type 'IC<object>' with a collection expression because the type is not constructible. // IC<object> c = [4]; Diagnostic(ErrorCode.ERR_CollectionExpressionTargetTypeNotConstructible, "[4]").WithArguments("System.Collections.Generic.IC<object>").WithLocation(9, 24), // 1.cs(10,32): error CS9174: Cannot initialize type 'ID<object, object>' with a collection expression because the type is not constructible. // ID<object, object> d = [5]; Diagnostic(ErrorCode.ERR_CollectionExpressionTargetTypeNotConstructible, "[5]").WithArguments("System.Collections.Generic.ID<object, object>").WithLocation(10, 32)); var collectionType = comp.GetWellKnownType(WellKnownType.System_Collections_Generic_List_T).Construct(comp.GetSpecialType(SpecialType.System_Int32)); Assert.Equal(CollectionExpressionTypeKind.ImplementsIEnumerable, ConversionsBase.GetCollectionExpressionTypeKind(comp, collectionType, out _)); } [Fact] public void ListInterfaces_02() { string sourceA = """ namespace System { public class Object { } public abstract class ValueType { } public class String { } public class Type { } public struct Void { } public struct Boolean { } public struct Int32 { } public interface IEquatable<T> { bool Equals(T other); } } namespace System.Collections { public interface IEnumerable { } } namespace System.Collections.Generic { public class List<T> : IEnumerable, IEquatable<List<T>> { public bool Equals(List<T> other) => false; public void Add(T t) { } } } """; string sourceB = """ using System; using System.Collections.Generic; class Program { static void Main() { List<int> l = [1]; IEquatable<int> e = [2]; } } """; var comp = CreateEmptyCompilation(new[] { sourceA, sourceB }, parseOptions: TestOptions.RegularPreview.WithNoRefSafetyRulesAttribute()); comp.VerifyEmitDiagnostics( // warning CS8021: No value for RuntimeMetadataVersion found. No assembly containing System.Object was found nor was a value for RuntimeMetadataVersion specified through options. Diagnostic(ErrorCode.WRN_NoRuntimeMetadataVersion).WithLocation(1, 1), // 1.cs(8,29): error CS9174: Cannot initialize type 'IEquatable<int>' with a collection expression because the type is not constructible. // IEquatable<int> e = [2]; Diagnostic(ErrorCode.ERR_CollectionExpressionTargetTypeNotConstructible, "[2]").WithArguments("System.IEquatable<int>").WithLocation(8, 29)); var collectionType = comp.GetWellKnownType(WellKnownType.System_Collections_Generic_List_T).Construct(comp.GetSpecialType(SpecialType.System_Int32)); Assert.Equal(CollectionExpressionTypeKind.ImplementsIEnumerable, ConversionsBase.GetCollectionExpressionTypeKind(comp, collectionType, out _)); } [Theory, CombinatorialData] public void ListInterface_ReadOnly_FromSingleSpread_Covariant_Spans( [CombinatorialValues("IEnumerable", "IReadOnlyCollection", "IReadOnlyList")] string listInterface, bool readOnlySpan) { var spanType = readOnlySpan ? "ReadOnlySpan" : "Span"; var source = $$""" using System; using System.Collections.Generic; class Program { static void Main() { {{spanType}}<Derived> d = [new(), new(), new()]; var b = M(d); b.Report(); } static {{listInterface}}<Base> M({{spanType}}<Derived> span) => [.. span]; } class Base { private static int _totalCount; private readonly int _id; public Base() { _id = _totalCount++; } public override string ToString() => $"{GetType().Name} {_id}"; } class Derived : Base; """; var verifier = CompileAndVerify( [source, s_collectionExtensions], targetFramework: TargetFramework.Net90, expectedOutput: IncludeExpectedOutput("[Derived 0, Derived 1, Derived 2], "), verify: Verification.Skipped); verifier.VerifyDiagnostics(); verifier.VerifyIL("Program.M", $$""" { // Code size 13 (0xd) .maxstack 1 IL_0000: ldarga.s V_0 IL_0002: call "Derived[] System.{{spanType}}<Derived>.ToArray()" IL_0007: newobj "<>z__ReadOnlyArray<Base>..ctor(Base[])" IL_000c: ret } """); } [Theory, CombinatorialData] public void ListInterface_Mutable_FromSingleSpread_Covariant_Spans( [CombinatorialValues("ICollection", "IList")] string listInterface, bool readOnlySpan) { var spanType = readOnlySpan ? "ReadOnlySpan" : "Span"; var source = $$""" using System; using System.Collections.Generic; class Program { static void Main() { {{spanType}}<Derived> d = [new(), new(), new()]; var b = M(d); b.Report(); b.Add(new Base()); b.Report(); } static {{listInterface}}<Base> M({{spanType}}<Derived> span) => [.. span]; } class Base { private static int _totalCount; private readonly int _id; public Base() { _id = _totalCount++; } public override string ToString() => $"{GetType().Name} {_id}"; } class Derived : Base; """; var verifier = CompileAndVerify( [source, s_collectionExtensions], targetFramework: TargetFramework.Net90, expectedOutput: IncludeExpectedOutput("[Derived 0, Derived 1, Derived 2], [Derived 0, Derived 1, Derived 2, Base 3], "), verify: Verification.Skipped); verifier.VerifyDiagnostics(); verifier.VerifyIL("Program.M", $$""" { // Code size 84 (0x54) .maxstack 2 .locals init (System.{{spanType}}<Derived> V_0, int V_1, System.Collections.Generic.List<Base> V_2, System.Span<Base> V_3, int V_4, System.{{spanType}}<Derived>.Enumerator V_5, Derived V_6) IL_0000: ldarg.0 IL_0001: stloc.0 IL_0002: ldloca.s V_0 IL_0004: call "int System.{{spanType}}<Derived>.Length.get" IL_0009: stloc.1 IL_000a: ldloc.1 IL_000b: newobj "System.Collections.Generic.List<Base>..ctor(int)" IL_0010: stloc.2 IL_0011: ldloc.2 IL_0012: ldloc.1 IL_0013: call "void System.Runtime.InteropServices.CollectionsMarshal.SetCount<Base>(System.Collections.Generic.List<Base>, int)" IL_0018: ldloc.2 IL_0019: call "System.Span<Base> System.Runtime.InteropServices.CollectionsMarshal.AsSpan<Base>(System.Collections.Generic.List<Base>)" IL_001e: stloc.3 IL_001f: ldc.i4.0 IL_0020: stloc.s V_4 IL_0022: ldloca.s V_0 IL_0024: call "System.{{spanType}}<Derived>.Enumerator System.{{spanType}}<Derived>.GetEnumerator()" IL_0029: stloc.s V_5 IL_002b: br.s IL_0049 IL_002d: ldloca.s V_5 IL_002f: call "ref {{(readOnlySpan ? "readonly " : "")}}Derived System.{{spanType}}<Derived>.Enumerator.Current.get" IL_0034: ldind.ref IL_0035: stloc.s V_6 IL_0037: ldloca.s V_3 IL_0039: ldloc.s V_4 IL_003b: call "ref Base System.Span<Base>.this[int].get" IL_0040: ldloc.s V_6 IL_0042: stind.ref IL_0043: ldloc.s V_4 IL_0045: ldc.i4.1 IL_0046: add IL_0047: stloc.s V_4 IL_0049: ldloca.s V_5 IL_004b: call "bool System.{{spanType}}<Derived>.Enumerator.MoveNext()" IL_0050: brtrue.s IL_002d IL_0052: ldloc.2 IL_0053: ret } """); } [Theory] [InlineData("IEnumerable")] [InlineData("IReadOnlyCollection")] [InlineData("IReadOnlyList")] public void ListInterface_ReadOnly_FromSingleSpread_Covariant_List(string listInterface) { var source = $$""" using System.Collections.Generic; class Program { static void Main() { List<Derived> d = [new(), new(), new()]; var b = M(d); b.Report(); } static {{listInterface}}<Base> M(List<Derived> list) => [.. list]; } class Base { private static int _totalCount; private readonly int _id; public Base() { _id = _totalCount++; } public override string ToString() => $"{GetType().Name} {_id}"; } class Derived : Base; """; var verifier = CompileAndVerify( [source, s_collectionExtensions], targetFramework: TargetFramework.Net90, expectedOutput: IncludeExpectedOutput("[Derived 0, Derived 1, Derived 2], "), verify: Verification.Skipped); verifier.VerifyDiagnostics(); verifier.VerifyIL("Program.M", """ { // Code size 12 (0xc) .maxstack 1 IL_0000: ldarg.0 IL_0001: callvirt "Derived[] System.Collections.Generic.List<Derived>.ToArray()" IL_0006: newobj "<>z__ReadOnlyArray<Base>..ctor(Base[])" IL_000b: ret } """); } [Theory] [InlineData("ICollection")] [InlineData("IList")] public void ListInterface_Mutable_FromSingleSpread_Covariant_List(string listInterface) { var source = $$""" using System.Collections.Generic; class Program { static void Main() { List<Derived> d = [new(), new(), new()]; var b = M(d); b.Report(); b.Add(new Base()); b.Report(); } static {{listInterface}}<Base> M(List<Derived> list) => [.. list]; } class Base { private static int _totalCount; private readonly int _id; public Base() { _id = _totalCount++; } public override string ToString() => $"{GetType().Name} {_id}"; } class Derived : Base; """; var verifier = CompileAndVerify( [source, s_collectionExtensions], targetFramework: TargetFramework.Net90, expectedOutput: IncludeExpectedOutput("[Derived 0, Derived 1, Derived 2], [Derived 0, Derived 1, Derived 2, Base 3], "), verify: Verification.Skipped); verifier.VerifyDiagnostics(); verifier.VerifyIL("Program.M", """ { // Code size 91 (0x5b) .maxstack 3 .locals init (int V_0, System.Collections.Generic.List<Base> V_1, System.Span<Base> V_2, int V_3, System.Collections.Generic.List<Derived>.Enumerator V_4, Derived V_5) IL_0000: ldarg.0 IL_0001: dup IL_0002: callvirt "int System.Collections.Generic.List<Derived>.Count.get" IL_0007: stloc.0 IL_0008: ldloc.0 IL_0009: newobj "System.Collections.Generic.List<Base>..ctor(int)" IL_000e: stloc.1 IL_000f: ldloc.1 IL_0010: ldloc.0 IL_0011: call "void System.Runtime.InteropServices.CollectionsMarshal.SetCount<Base>(System.Collections.Generic.List<Base>, int)" IL_0016: ldloc.1 IL_0017: call "System.Span<Base> System.Runtime.InteropServices.CollectionsMarshal.AsSpan<Base>(System.Collections.Generic.List<Base>)" IL_001c: stloc.2 IL_001d: ldc.i4.0 IL_001e: stloc.3 IL_001f: callvirt "System.Collections.Generic.List<Derived>.Enumerator System.Collections.Generic.List<Derived>.GetEnumerator()" IL_0024: stloc.s V_4 .try { IL_0026: br.s IL_0040 IL_0028: ldloca.s V_4 IL_002a: call "Derived System.Collections.Generic.List<Derived>.Enumerator.Current.get" IL_002f: stloc.s V_5 IL_0031: ldloca.s V_2 IL_0033: ldloc.3 IL_0034: call "ref Base System.Span<Base>.this[int].get" IL_0039: ldloc.s V_5 IL_003b: stind.ref IL_003c: ldloc.3 IL_003d: ldc.i4.1 IL_003e: add IL_003f: stloc.3 IL_0040: ldloca.s V_4 IL_0042: call "bool System.Collections.Generic.List<Derived>.Enumerator.MoveNext()" IL_0047: brtrue.s IL_0028 IL_0049: leave.s IL_0059 } finally { IL_004b: ldloca.s V_4 IL_004d: constrained. "System.Collections.Generic.List<Derived>.Enumerator" IL_0053: callvirt "void System.IDisposable.Dispose()" IL_0058: endfinally } IL_0059: ldloc.1 IL_005a: ret } """); } [Theory, CombinatorialData] public void ListInterface_ReadOnly_FromSingleSpread_SameRuntimeType_Spans( [CombinatorialValues("IEnumerable", "IReadOnlyCollection", "IReadOnlyList")] string listInterface, bool readOnlySpan) { var spanType = readOnlySpan ? "ReadOnlySpan" : "Span"; var source = $$""" using System; using System.Collections.Generic; class Program { static void Main() { {{spanType}}<object> d = [1, 2, 3]; var b = M(d); b.Report(); } static {{listInterface}}<dynamic> M({{spanType}}<object> span) => [.. span]; } class Base { private static int _totalCount; private readonly int _id; public Base() { _id = _totalCount++; } public override string ToString() => $"{GetType().Name} {_id}"; } class Derived : Base; """; var verifier = CompileAndVerify( [source, s_collectionExtensions], targetFramework: TargetFramework.Net90, expectedOutput: IncludeExpectedOutput("[1, 2, 3], "), verify: Verification.Skipped); verifier.VerifyDiagnostics(); verifier.VerifyIL("Program.M", $$""" { // Code size 13 (0xd) .maxstack 1 IL_0000: ldarga.s V_0 IL_0002: call "object[] System.{{spanType}}<object>.ToArray()" IL_0007: newobj "<>z__ReadOnlyArray<dynamic>..ctor(dynamic[])" IL_000c: ret } """); } [Theory, CombinatorialData] public void ListInterface_Mutable_FromSingleSpread_SameRuntimeType_Spans( [CombinatorialValues("ICollection", "IList")] string listInterface, bool readOnlySpan) { var spanType = readOnlySpan ? "ReadOnlySpan" : "Span"; var source = $$""" using System; using System.Collections.Generic; class Program { static void Main() { {{spanType}}<object> d = [1, 2, 3]; var b = M(d); b.Report(); b.Add("a"); b.Report(); } static {{listInterface}}<dynamic> M({{spanType}}<object> span) => [.. span]; } class Base { private static int _totalCount; private readonly int _id; public Base() { _id = _totalCount++; } public override string ToString() => $"{GetType().Name} {_id}"; } class Derived : Base; """; var verifier = CompileAndVerify( [source, s_collectionExtensions], targetFramework: TargetFramework.Net90, expectedOutput: IncludeExpectedOutput("[1, 2, 3], [1, 2, 3, a], "), verify: Verification.Skipped); verifier.VerifyDiagnostics(); verifier.VerifyIL("Program.M", $$""" { // Code size 65 (0x41) .maxstack 5 .locals init (System.{{spanType}}<object> V_0, int V_1, System.Span<dynamic> V_2, int V_3) IL_0000: ldarg.0 IL_0001: stloc.0 IL_0002: ldloca.s V_0 IL_0004: call "int System.{{spanType}}<object>.Length.get" IL_0009: stloc.1 IL_000a: ldloc.1 IL_000b: newobj "System.Collections.Generic.List<dynamic>..ctor(int)" IL_0010: dup IL_0011: ldloc.1 IL_0012: call "void System.Runtime.InteropServices.CollectionsMarshal.SetCount<dynamic>(System.Collections.Generic.List<dynamic>, int)" IL_0017: dup IL_0018: call "System.Span<dynamic> System.Runtime.InteropServices.CollectionsMarshal.AsSpan<dynamic>(System.Collections.Generic.List<dynamic>)" IL_001d: stloc.2 IL_001e: ldc.i4.0 IL_001f: stloc.3 IL_0020: ldloca.s V_0 IL_0022: ldloca.s V_2 IL_0024: ldloc.3 IL_0025: ldloca.s V_0 IL_0027: call "int System.{{spanType}}<object>.Length.get" IL_002c: call "System.Span<dynamic> System.Span<dynamic>.Slice(int, int)" IL_0031: call "void System.{{spanType}}<object>.CopyTo(System.Span<object>)" IL_0036: ldloc.3 IL_0037: ldloca.s V_0 IL_0039: call "int System.{{spanType}}<object>.Length.get" IL_003e: add IL_003f: stloc.3 IL_0040: ret } """); } [Theory] [InlineData("IEnumerable")] [InlineData("IReadOnlyCollection")] [InlineData("IReadOnlyList")] public void ListInterface_ReadOnly_FromSingleSpread_SameRuntimeType_List(string listInterface) { var source = $$""" using System.Collections.Generic; class Program { static void Main() { List<object> d = [1, 2, 3]; var b = M(d); b.Report(); } static {{listInterface}}<dynamic> M(List<object> list) => [.. list]; } class Base { private static int _totalCount; private readonly int _id; public Base() { _id = _totalCount++; } public override string ToString() => $"{GetType().Name} {_id}"; } class Derived : Base; """; var verifier = CompileAndVerify( [source, s_collectionExtensions], targetFramework: TargetFramework.Net90, expectedOutput: IncludeExpectedOutput("[1, 2, 3], "), verify: Verification.Skipped); verifier.VerifyDiagnostics(); verifier.VerifyIL("Program.M", """ { // Code size 12 (0xc) .maxstack 1 IL_0000: ldarg.0 IL_0001: callvirt "object[] System.Collections.Generic.List<object>.ToArray()" IL_0006: newobj "<>z__ReadOnlyArray<dynamic>..ctor(dynamic[])" IL_000b: ret } """); } [Theory] [InlineData("ICollection")] [InlineData("IList")] public void ListInterface_Mutable_FromSingleSpread_SameRuntimeType_List(string listInterface) { var source = $$""" using System.Collections.Generic; class Program { static void Main() { List<object> d = [1, 2, 3]; var b = M(d); b.Report(); b.Add("a"); b.Report(); } static {{listInterface}}<dynamic> M(List<object> list) => [.. list]; } class Base { private static int _totalCount; private readonly int _id; public Base() { _id = _totalCount++; } public override string ToString() => $"{GetType().Name} {_id}"; } class Derived : Base; """; var verifier = CompileAndVerify( [source, s_collectionExtensions], targetFramework: TargetFramework.Net90, expectedOutput: IncludeExpectedOutput("[1, 2, 3], [1, 2, 3, a], "), verify: Verification.Skipped); verifier.VerifyDiagnostics(); verifier.VerifyIL("Program.M", """ { // Code size 72 (0x48) .maxstack 4 .locals init (int V_0, System.Collections.Generic.List<dynamic> V_1, System.Span<dynamic> V_2, int V_3, System.Span<object> V_4) IL_0000: ldarg.0 IL_0001: dup IL_0002: callvirt "int System.Collections.Generic.List<object>.Count.get" IL_0007: stloc.0 IL_0008: ldloc.0 IL_0009: newobj "System.Collections.Generic.List<dynamic>..ctor(int)" IL_000e: stloc.1 IL_000f: ldloc.1 IL_0010: ldloc.0 IL_0011: call "void System.Runtime.InteropServices.CollectionsMarshal.SetCount<dynamic>(System.Collections.Generic.List<dynamic>, int)" IL_0016: ldloc.1 IL_0017: call "System.Span<dynamic> System.Runtime.InteropServices.CollectionsMarshal.AsSpan<dynamic>(System.Collections.Generic.List<dynamic>)" IL_001c: stloc.2 IL_001d: ldc.i4.0 IL_001e: stloc.3 IL_001f: call "System.Span<object> System.Runtime.InteropServices.CollectionsMarshal.AsSpan<object>(System.Collections.Generic.List<object>)" IL_0024: stloc.s V_4 IL_0026: ldloca.s V_4 IL_0028: ldloca.s V_2 IL_002a: ldloc.3 IL_002b: ldloca.s V_4 IL_002d: call "int System.Span<object>.Length.get" IL_0032: call "System.Span<dynamic> System.Span<dynamic>.Slice(int, int)" IL_0037: call "void System.Span<object>.CopyTo(System.Span<object>)" IL_003c: ldloc.3 IL_003d: ldloca.s V_4 IL_003f: call "int System.Span<object>.Length.get" IL_0044: add IL_0045: stloc.3 IL_0046: ldloc.1 IL_0047: ret } """); } [Theory, CombinatorialData] public void ListInterface_ReadOnly_FromSingleSpread_Boxing_Spans( [CombinatorialValues("IEnumerable", "IReadOnlyCollection", "IReadOnlyList")] string listInterface, bool readOnlySpan) { var spanType = readOnlySpan ? "ReadOnlySpan" : "Span"; var source = $$""" using System; using System.Collections.Generic; class Program { static void Main() { {{spanType}}<int> d = [1, 2, 3]; var b = M(d); b.Report(); } static {{listInterface}}<object> M({{spanType}}<int> span) => [.. span]; } class Base { private static int _totalCount; private readonly int _id; public Base() { _id = _totalCount++; } public override string ToString() => $"{GetType().Name} {_id}"; } class Derived : Base; """; var verifier = CompileAndVerify( [source, s_collectionExtensions], targetFramework: TargetFramework.Net90, expectedOutput: IncludeExpectedOutput("[1, 2, 3], "), verify: Verification.Skipped); verifier.VerifyDiagnostics(); verifier.VerifyIL("Program.M", $$""" { // Code size 67 (0x43) .maxstack 3 .locals init (System.{{spanType}}<int> V_0, int V_1, object[] V_2, System.{{spanType}}<int>.Enumerator V_3, int V_4) IL_0000: ldarg.0 IL_0001: stloc.0 IL_0002: ldc.i4.0 IL_0003: stloc.1 IL_0004: ldloca.s V_0 IL_0006: call "int System.{{spanType}}<int>.Length.get" IL_000b: newarr "object" IL_0010: stloc.2 IL_0011: ldloca.s V_0 IL_0013: call "System.{{spanType}}<int>.Enumerator System.{{spanType}}<int>.GetEnumerator()" IL_0018: stloc.3 IL_0019: br.s IL_0033 IL_001b: ldloca.s V_3 IL_001d: call "ref {{(readOnlySpan ? "readonly " : "")}}int System.{{spanType}}<int>.Enumerator.Current.get" IL_0022: ldind.i4 IL_0023: stloc.s V_4 IL_0025: ldloc.2 IL_0026: ldloc.1 IL_0027: ldloc.s V_4 IL_0029: box "int" IL_002e: stelem.ref IL_002f: ldloc.1 IL_0030: ldc.i4.1 IL_0031: add IL_0032: stloc.1 IL_0033: ldloca.s V_3 IL_0035: call "bool System.{{spanType}}<int>.Enumerator.MoveNext()" IL_003a: brtrue.s IL_001b IL_003c: ldloc.2 IL_003d: newobj "<>z__ReadOnlyArray<object>..ctor(object[])" IL_0042: ret } """); } [Theory, CombinatorialData] public void ListInterface_Mutable_FromSingleSpread_Boxing_Spans( [CombinatorialValues("ICollection", "IList")] string listInterface, bool readOnlySpan) { var spanType = readOnlySpan ? "ReadOnlySpan" : "Span"; var source = $$""" using System; using System.Collections.Generic; class Program { static void Main() { {{spanType}}<int> d = [1, 2, 3]; var b = M(d); b.Report(); b.Add("a"); b.Report(); } static {{listInterface}}<object> M({{spanType}}<int> span) => [.. span]; } class Base { private static int _totalCount; private readonly int _id; public Base() { _id = _totalCount++; } public override string ToString() => $"{GetType().Name} {_id}"; } class Derived : Base; """; var verifier = CompileAndVerify( [source, s_collectionExtensions], targetFramework: TargetFramework.Net90, expectedOutput: IncludeExpectedOutput("[1, 2, 3], [1, 2, 3, a], "), verify: Verification.Skipped); verifier.VerifyDiagnostics(); verifier.VerifyIL("Program.M", $$""" { // Code size 89 (0x59) .maxstack 2 .locals init (System.{{spanType}}<int> V_0, int V_1, System.Collections.Generic.List<object> V_2, System.Span<object> V_3, int V_4, System.{{spanType}}<int>.Enumerator V_5, int V_6) IL_0000: ldarg.0 IL_0001: stloc.0 IL_0002: ldloca.s V_0 IL_0004: call "int System.{{spanType}}<int>.Length.get" IL_0009: stloc.1 IL_000a: ldloc.1 IL_000b: newobj "System.Collections.Generic.List<object>..ctor(int)" IL_0010: stloc.2 IL_0011: ldloc.2 IL_0012: ldloc.1 IL_0013: call "void System.Runtime.InteropServices.CollectionsMarshal.SetCount<object>(System.Collections.Generic.List<object>, int)" IL_0018: ldloc.2 IL_0019: call "System.Span<object> System.Runtime.InteropServices.CollectionsMarshal.AsSpan<object>(System.Collections.Generic.List<object>)" IL_001e: stloc.3 IL_001f: ldc.i4.0 IL_0020: stloc.s V_4 IL_0022: ldloca.s V_0 IL_0024: call "System.{{spanType}}<int>.Enumerator System.{{spanType}}<int>.GetEnumerator()" IL_0029: stloc.s V_5 IL_002b: br.s IL_004e IL_002d: ldloca.s V_5 IL_002f: call "ref {{(readOnlySpan ? "readonly " : "")}}int System.{{spanType}}<int>.Enumerator.Current.get" IL_0034: ldind.i4 IL_0035: stloc.s V_6 IL_0037: ldloca.s V_3 IL_0039: ldloc.s V_4 IL_003b: call "ref object System.Span<object>.this[int].get" IL_0040: ldloc.s V_6 IL_0042: box "int" IL_0047: stind.ref IL_0048: ldloc.s V_4 IL_004a: ldc.i4.1 IL_004b: add IL_004c: stloc.s V_4 IL_004e: ldloca.s V_5 IL_0050: call "bool System.{{spanType}}<int>.Enumerator.MoveNext()" IL_0055: brtrue.s IL_002d IL_0057: ldloc.2 IL_0058: ret } """); } [Theory] [InlineData("IEnumerable")] [InlineData("IReadOnlyCollection")] [InlineData("IReadOnlyList")] public void ListInterface_ReadOnly_FromSingleSpread_Boxing_List(string listInterface) { var source = $$""" using System.Collections.Generic; class Program { static void Main() { List<int> d = [1, 2, 3]; var b = M(d); b.Report(); } static {{listInterface}}<object> M(List<int> list) => [.. list]; } class Base { private static int _totalCount; private readonly int _id; public Base() { _id = _totalCount++; } public override string ToString() => $"{GetType().Name} {_id}"; } class Derived : Base; """; var verifier = CompileAndVerify( [source, s_collectionExtensions], targetFramework: TargetFramework.Net90, expectedOutput: IncludeExpectedOutput("[1, 2, 3], "), verify: Verification.Skipped); verifier.VerifyDiagnostics(); verifier.VerifyIL("Program.M", """ { // Code size 76 (0x4c) .maxstack 3 .locals init (int V_0, object[] V_1, System.Collections.Generic.List<int>.Enumerator V_2, int V_3) IL_0000: ldarg.0 IL_0001: ldc.i4.0 IL_0002: stloc.0 IL_0003: dup IL_0004: callvirt "int System.Collections.Generic.List<int>.Count.get" IL_0009: newarr "object" IL_000e: stloc.1 IL_000f: callvirt "System.Collections.Generic.List<int>.Enumerator System.Collections.Generic.List<int>.GetEnumerator()" IL_0014: stloc.2 .try { IL_0015: br.s IL_002c IL_0017: ldloca.s V_2 IL_0019: call "int System.Collections.Generic.List<int>.Enumerator.Current.get" IL_001e: stloc.3 IL_001f: ldloc.1 IL_0020: ldloc.0 IL_0021: ldloc.3 IL_0022: box "int" IL_0027: stelem.ref IL_0028: ldloc.0 IL_0029: ldc.i4.1 IL_002a: add IL_002b: stloc.0 IL_002c: ldloca.s V_2 IL_002e: call "bool System.Collections.Generic.List<int>.Enumerator.MoveNext()" IL_0033: brtrue.s IL_0017 IL_0035: leave.s IL_0045 } finally { IL_0037: ldloca.s V_2 IL_0039: constrained. "System.Collections.Generic.List<int>.Enumerator" IL_003f: callvirt "void System.IDisposable.Dispose()" IL_0044: endfinally } IL_0045: ldloc.1 IL_0046: newobj "<>z__ReadOnlyArray<object>..ctor(object[])" IL_004b: ret } """); } [Theory] [InlineData("ICollection")] [InlineData("IList")] public void ListInterface_Mutable_FromSingleSpread_Boxing_List(string listInterface) { var source = $$""" using System.Collections.Generic; class Program { static void Main() { List<int> d = [1, 2, 3]; var b = M(d); b.Report(); b.Add("a"); b.Report(); } static {{listInterface}}<object> M(List<int> list) => [.. list]; } class Base { private static int _totalCount; private readonly int _id; public Base() { _id = _totalCount++; } public override string ToString() => $"{GetType().Name} {_id}"; } class Derived : Base; """; var verifier = CompileAndVerify( [source, s_collectionExtensions], targetFramework: TargetFramework.Net90, expectedOutput: IncludeExpectedOutput("[1, 2, 3], [1, 2, 3, a], "), verify: Verification.Skipped); verifier.VerifyDiagnostics(); verifier.VerifyIL("Program.M", """ { // Code size 96 (0x60) .maxstack 3 .locals init (int V_0, System.Collections.Generic.List<object> V_1, System.Span<object> V_2, int V_3, System.Collections.Generic.List<int>.Enumerator V_4, int V_5) IL_0000: ldarg.0 IL_0001: dup IL_0002: callvirt "int System.Collections.Generic.List<int>.Count.get" IL_0007: stloc.0 IL_0008: ldloc.0 IL_0009: newobj "System.Collections.Generic.List<object>..ctor(int)" IL_000e: stloc.1 IL_000f: ldloc.1 IL_0010: ldloc.0 IL_0011: call "void System.Runtime.InteropServices.CollectionsMarshal.SetCount<object>(System.Collections.Generic.List<object>, int)" IL_0016: ldloc.1 IL_0017: call "System.Span<object> System.Runtime.InteropServices.CollectionsMarshal.AsSpan<object>(System.Collections.Generic.List<object>)" IL_001c: stloc.2 IL_001d: ldc.i4.0 IL_001e: stloc.3 IL_001f: callvirt "System.Collections.Generic.List<int>.Enumerator System.Collections.Generic.List<int>.GetEnumerator()" IL_0024: stloc.s V_4 .try { IL_0026: br.s IL_0045 IL_0028: ldloca.s V_4 IL_002a: call "int System.Collections.Generic.List<int>.Enumerator.Current.get" IL_002f: stloc.s V_5 IL_0031: ldloca.s V_2 IL_0033: ldloc.3 IL_0034: call "ref object System.Span<object>.this[int].get" IL_0039: ldloc.s V_5 IL_003b: box "int" IL_0040: stind.ref IL_0041: ldloc.3 IL_0042: ldc.i4.1 IL_0043: add IL_0044: stloc.3 IL_0045: ldloca.s V_4 IL_0047: call "bool System.Collections.Generic.List<int>.Enumerator.MoveNext()" IL_004c: brtrue.s IL_0028 IL_004e: leave.s IL_005e } finally { IL_0050: ldloca.s V_4 IL_0052: constrained. "System.Collections.Generic.List<int>.Enumerator" IL_0058: callvirt "void System.IDisposable.Dispose()" IL_005d: endfinally } IL_005e: ldloc.1 IL_005f: ret } """); } [Fact] public void ListInterfaces_NoInterfaces() { string sourceA = """ namespace System { public class Object { } public abstract class ValueType { } public class String { } public class Type { } public struct Void { } public struct Boolean { } public struct Int32 { } } namespace System.Collections.Generic { public interface IEnumerable<T> { } public class List<T> { public List(int capacity) { } public void Add(T t) { } } } """; string sourceB = """ using System; using System.Collections.Generic; class Program { static void Main() { List<int> l = [1]; IEnumerable<int> e = [2]; } } """; var comp = CreateEmptyCompilation(new[] { sourceA, sourceB }, parseOptions: TestOptions.RegularPreview.WithNoRefSafetyRulesAttribute()); comp.VerifyEmitDiagnostics( // warning CS8021: No value for RuntimeMetadataVersion found. No assembly containing System.Object was found nor was a value for RuntimeMetadataVersion specified through options. Diagnostic(ErrorCode.WRN_NoRuntimeMetadataVersion).WithLocation(1, 1), // 1.cs(7,23): error CS9174: Cannot initialize type 'List<int>' with a collection expression because the type is not constructible. // List<int> l = [1]; Diagnostic(ErrorCode.ERR_CollectionExpressionTargetTypeNotConstructible, "[1]").WithArguments("System.Collections.Generic.List<int>").WithLocation(7, 23), // 1.cs(8,30): error CS0656: Missing compiler required member 'System.Collections.Generic.List`1..ctor' // IEnumerable<int> e = [2]; Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "[2]").WithArguments("System.Collections.Generic.List`1", ".ctor").WithLocation(8, 30), // 1.cs(8,30): error CS0656: Missing compiler required member 'System.Collections.Generic.List`1.ToArray' // IEnumerable<int> e = [2]; Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "[2]").WithArguments("System.Collections.Generic.List`1", "ToArray").WithLocation(8, 30)); var listType = comp.GetWellKnownType(WellKnownType.System_Collections_Generic_List_T).Construct(comp.GetSpecialType(SpecialType.System_Int32)); Assert.Equal(CollectionExpressionTypeKind.None, ConversionsBase.GetCollectionExpressionTypeKind(comp, listType, out var elementType)); Assert.False(elementType.HasType); } [Theory] [InlineData("IEnumerable<int>")] [InlineData("IReadOnlyCollection<object>")] [InlineData("IReadOnlyList<int>")] [InlineData("ICollection<object>")] [InlineData("IList<int>")] public void ListInterfaces_MissingList(string collectionType) { string source = $$""" using System.Collections.Generic; class Program { static void F(IEnumerable<int> e) { {{collectionType}} c; c = []; c = [..e]; } } """; var comp = CreateCompilation(source); comp.MakeTypeMissing(WellKnownType.System_Collections_Generic_List_T); comp.VerifyEmitDiagnostics( // (7,13): error CS0656: Missing compiler required member 'System.Collections.Generic.List`1..ctor' // c = []; Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "[]").WithArguments("System.Collections.Generic.List`1", ".ctor").WithLocation(7, 13), // (7,13): error CS0656: Missing compiler required member 'System.Collections.Generic.List`1..ctor' // c = []; Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "[]").WithArguments("System.Collections.Generic.List`1", ".ctor").WithLocation(7, 13), // (7,13): error CS0656: Missing compiler required member 'System.Collections.Generic.List`1.Add' // c = []; Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "[]").WithArguments("System.Collections.Generic.List`1", "Add").WithLocation(7, 13), // (7,13): error CS0656: Missing compiler required member 'System.Collections.Generic.List`1.ToArray' // c = []; Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "[]").WithArguments("System.Collections.Generic.List`1", "ToArray").WithLocation(7, 13), // (8,13): error CS0656: Missing compiler required member 'System.Collections.Generic.List`1..ctor' // c = [..e]; Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "[..e]").WithArguments("System.Collections.Generic.List`1", ".ctor").WithLocation(8, 13), // (8,13): error CS0656: Missing compiler required member 'System.Collections.Generic.List`1..ctor' // c = [..e]; Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "[..e]").WithArguments("System.Collections.Generic.List`1", ".ctor").WithLocation(8, 13), // (8,13): error CS0656: Missing compiler required member 'System.Collections.Generic.List`1.Add' // c = [..e]; Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "[..e]").WithArguments("System.Collections.Generic.List`1", "Add").WithLocation(8, 13), // (8,13): error CS0656: Missing compiler required member 'System.Collections.Generic.List`1.ToArray' // c = [..e]; Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "[..e]").WithArguments("System.Collections.Generic.List`1", "ToArray").WithLocation(8, 13)); } [Fact] public void Array_01() { string source = """ class Program { static int[] Create1() => []; static object[] Create2() => [1, 2]; static int[] Create3() => [3, 4, 5]; static long?[] Create4() => [null, 7]; static void Main() { Create1().Report(); Create2().Report(); Create3().Report(); Create4().Report(); } } """; var verifier = CompileAndVerify(new[] { source, s_collectionExtensions }, expectedOutput: "[], [1, 2], [3, 4, 5], [null, 7], "); verifier.VerifyIL("Program.Create1", """ { // Code size 6 (0x6) .maxstack 1 IL_0000: call "int[] System.Array.Empty<int>()" IL_0005: ret } """); verifier.VerifyIL("Program.Create2", """ { // Code size 25 (0x19) .maxstack 4 IL_0000: ldc.i4.2 IL_0001: newarr "object" IL_0006: dup IL_0007: ldc.i4.0 IL_0008: ldc.i4.1 IL_0009: box "int" IL_000e: stelem.ref IL_000f: dup IL_0010: ldc.i4.1 IL_0011: ldc.i4.2 IL_0012: box "int" IL_0017: stelem.ref IL_0018: ret } """); verifier.VerifyIL("Program.Create3", """ { // Code size 18 (0x12) .maxstack 3 IL_0000: ldc.i4.3 IL_0001: newarr "int" IL_0006: dup IL_0007: ldtoken "<PrivateImplementationDetails>.__StaticArrayInitTypeSize=12 <PrivateImplementationDetails>.CE99AE045C8B2A2A8A58FD1A2120956E74E90322EEF45F7DFE1CA73EEFE655D4" IL_000c: call "void System.Runtime.CompilerServices.RuntimeHelpers.InitializeArray(System.Array, System.RuntimeFieldHandle)" IL_0011: ret } """); verifier.VerifyIL("Program.Create4", """ { // Code size 21 (0x15) .maxstack 4 IL_0000: ldc.i4.2 IL_0001: newarr "long?" IL_0006: dup IL_0007: ldc.i4.1 IL_0008: ldc.i4.7 IL_0009: conv.i8 IL_000a: newobj "long?..ctor(long)" IL_000f: stelem "long?" IL_0014: ret } """); } [Fact] public void Array_02() { string source = """ using System; class Program { static int[][] Create1() => []; static object[][] Create2() => [[]]; static object[][] Create3() => [[1], [2, 3]]; static void Main() { Report(Create1()); Report(Create2()); Report(Create3()); } static void Report<T>(T[][] a) { Console.Write("Length={0}, ", a.Length); foreach (var x in a) { Console.Write("Length={0}, ", x.Length); foreach (var y in x) Console.Write("{0}, ", y); } Console.WriteLine(); } } """; var verifier = CompileAndVerify(source, expectedOutput: """ Length=0, Length=1, Length=0, Length=2, Length=1, 1, Length=2, 2, 3, """); verifier.VerifyIL("Program.Create1", """ { // Code size 6 (0x6) .maxstack 1 IL_0000: call "int[][] System.Array.Empty<int[]>()" IL_0005: ret } """); verifier.VerifyIL("Program.Create2", """ { // Code size 15 (0xf) .maxstack 4 IL_0000: ldc.i4.1 IL_0001: newarr "object[]" IL_0006: dup IL_0007: ldc.i4.0 IL_0008: call "object[] System.Array.Empty<object>()" IL_000d: stelem.ref IL_000e: ret } """); verifier.VerifyIL("Program.Create3", """ { // Code size 52 (0x34) .maxstack 7 IL_0000: ldc.i4.2 IL_0001: newarr "object[]" IL_0006: dup IL_0007: ldc.i4.0 IL_0008: ldc.i4.1 IL_0009: newarr "object" IL_000e: dup IL_000f: ldc.i4.0 IL_0010: ldc.i4.1 IL_0011: box "int" IL_0016: stelem.ref IL_0017: stelem.ref IL_0018: dup IL_0019: ldc.i4.1 IL_001a: ldc.i4.2 IL_001b: newarr "object" IL_0020: dup IL_0021: ldc.i4.0 IL_0022: ldc.i4.2 IL_0023: box "int" IL_0028: stelem.ref IL_0029: dup IL_002a: ldc.i4.1 IL_002b: ldc.i4.3 IL_002c: box "int" IL_0031: stelem.ref IL_0032: stelem.ref IL_0033: ret } """); } [Fact] public void Array_03() { string source = """ class Program { static void Main() { object o; o = (int[])[]; o.Report(); o = (long?[])[null, 2]; o.Report(); } } """; CompileAndVerify(new[] { source, s_collectionExtensions }, expectedOutput: "[], [null, 2], "); } [Fact] public void Array_04() { string source = """ class Program { static void Main() { object[,] x = []; int[,] y = [null, 2]; } } """; var comp = CreateCompilation(source); comp.VerifyEmitDiagnostics( // (5,23): error CS9174: Cannot initialize type 'object[*,*]' with a collection expression because the type is not constructible. // object[,] x = []; Diagnostic(ErrorCode.ERR_CollectionExpressionTargetTypeNotConstructible, "[]").WithArguments("object[*,*]").WithLocation(5, 23), // (6,20): error CS9174: Cannot initialize type 'int[*,*]' with a collection expression because the type is not constructible. // int[,] y = [null, 2]; Diagnostic(ErrorCode.ERR_CollectionExpressionTargetTypeNotConstructible, "[null, 2]").WithArguments("int[*,*]").WithLocation(6, 20)); } [Fact] public void Array_05() { string source = """ class Program { static void Main() { int[,] z = [[1, 2], [3, 4]]; } } """; var comp = CreateCompilation(source); comp.VerifyEmitDiagnostics( // (5,20): error CS9174: Cannot initialize type 'int[*,*]' with a collection expression because the type is not constructible. // int[,] z = [[1, 2], [3, 4]]; Diagnostic(ErrorCode.ERR_CollectionExpressionTargetTypeNotConstructible, "[[1, 2], [3, 4]]").WithArguments("int[*,*]").WithLocation(5, 20)); } [Fact] public void Array_06() { var source = """ using System; class Program { static void Main() { Derived[] d = { new(), new(), new() }; var bases = M(d); bases.Report(); bases[0] = new Base(); bases.Report(); } static Base[] M(Span<Derived> span) => [.. span]; } class Base { private static int _totalCount; private readonly int _id; public Base() { _id = _totalCount++; } public override string ToString() => $"{GetType().Name} {_id}"; } class Derived : Base; """; var verifier = CompileAndVerify( [source, s_collectionExtensions], targetFramework: TargetFramework.Net90, expectedOutput: IncludeExpectedOutput("[Derived 0, Derived 1, Derived 2], [Base 3, Derived 1, Derived 2], "), verify: Verification.Skipped); verifier.VerifyDiagnostics(); verifier.VerifyIL("Program.M", """ { // Code size 57 (0x39) .maxstack 3 .locals init (System.Span<Derived> V_0, int V_1, Base[] V_2, System.Span<Derived>.Enumerator V_3, Derived V_4) IL_0000: ldarg.0 IL_0001: stloc.0 IL_0002: ldc.i4.0 IL_0003: stloc.1 IL_0004: ldloca.s V_0 IL_0006: call "int System.Span<Derived>.Length.get" IL_000b: newarr "Base" IL_0010: stloc.2 IL_0011: ldloca.s V_0 IL_0013: call "System.Span<Derived>.Enumerator System.Span<Derived>.GetEnumerator()" IL_0018: stloc.3 IL_0019: br.s IL_002e IL_001b: ldloca.s V_3 IL_001d: call "ref Derived System.Span<Derived>.Enumerator.Current.get" IL_0022: ldind.ref IL_0023: stloc.s V_4 IL_0025: ldloc.2 IL_0026: ldloc.1 IL_0027: ldloc.s V_4 IL_0029: stelem.ref IL_002a: ldloc.1 IL_002b: ldc.i4.1 IL_002c: add IL_002d: stloc.1 IL_002e: ldloca.s V_3 IL_0030: call "bool System.Span<Derived>.Enumerator.MoveNext()" IL_0035: brtrue.s IL_001b IL_0037: ldloc.2 IL_0038: ret } """); } [Fact] public void Array_07() { var source = """ using System; class Program { static void Main() { Derived[] d = { new(), new(), new() }; var bases = M(d); bases.Report(); bases[0] = new Base(); bases.Report(); } static Base[] M(ReadOnlySpan<Derived> span) => [.. span]; } class Base { private static int _totalCount; private readonly int _id; public Base() { _id = _totalCount++; } public override string ToString() => $"{GetType().Name} {_id}"; } class Derived : Base; """; var verifier = CompileAndVerify( [source, s_collectionExtensions], targetFramework: TargetFramework.Net90, expectedOutput: IncludeExpectedOutput("[Derived 0, Derived 1, Derived 2], [Base 3, Derived 1, Derived 2], "), verify: Verification.Skipped); verifier.VerifyDiagnostics(); verifier.VerifyIL("Program.M", """ { // Code size 57 (0x39) .maxstack 3 .locals init (System.ReadOnlySpan<Derived> V_0, int V_1, Base[] V_2, System.ReadOnlySpan<Derived>.Enumerator V_3, Derived V_4) IL_0000: ldarg.0 IL_0001: stloc.0 IL_0002: ldc.i4.0 IL_0003: stloc.1 IL_0004: ldloca.s V_0 IL_0006: call "int System.ReadOnlySpan<Derived>.Length.get" IL_000b: newarr "Base" IL_0010: stloc.2 IL_0011: ldloca.s V_0 IL_0013: call "System.ReadOnlySpan<Derived>.Enumerator System.ReadOnlySpan<Derived>.GetEnumerator()" IL_0018: stloc.3 IL_0019: br.s IL_002e IL_001b: ldloca.s V_3 IL_001d: call "ref readonly Derived System.ReadOnlySpan<Derived>.Enumerator.Current.get" IL_0022: ldind.ref IL_0023: stloc.s V_4 IL_0025: ldloc.2 IL_0026: ldloc.1 IL_0027: ldloc.s V_4 IL_0029: stelem.ref IL_002a: ldloc.1 IL_002b: ldc.i4.1 IL_002c: add IL_002d: stloc.1 IL_002e: ldloca.s V_3 IL_0030: call "bool System.ReadOnlySpan<Derived>.Enumerator.MoveNext()" IL_0035: brtrue.s IL_001b IL_0037: ldloc.2 IL_0038: ret } """); } [Theory, CombinatorialData] public void Array_08(bool readOnlySpan) { var spanType = readOnlySpan ? "ReadOnlySpan" : "Span"; var source = $$""" using System; class Program { static void Main() { dynamic[] d = { 1, 2, 3 }; var objs = M(d); objs.Report(); objs[0] = "a"; objs.Report(); } static object[] M({{spanType}}<dynamic> span) => [.. span]; } """; var verifier = CompileAndVerify( [source, s_collectionExtensions], targetFramework: TargetFramework.Net90, expectedOutput: IncludeExpectedOutput("[1, 2, 3], [a, 2, 3], "), verify: Verification.Skipped); verifier.VerifyDiagnostics(); verifier.VerifyIL("Program.M", $$""" { // Code size 8 (0x8) .maxstack 1 IL_0000: ldarga.s V_0 IL_0002: call "dynamic[] System.{{spanType}}<dynamic>.ToArray()" IL_0007: ret } """); } [Theory, CombinatorialData] public void Array_09(bool readOnlySpan) { var spanType = readOnlySpan ? "ReadOnlySpan" : "Span"; var source = $$""" using System; class Program { static void Main() { int[] d = { 1, 2, 3 }; var objs = M(d); objs.Report(); objs[0] = "a"; objs.Report(); } static object[] M({{spanType}}<int> span) => [.. span]; } """; var verifier = CompileAndVerify( [source, s_collectionExtensions], targetFramework: TargetFramework.Net90, expectedOutput: IncludeExpectedOutput("[1, 2, 3], [a, 2, 3], "), verify: Verification.Skipped); verifier.VerifyDiagnostics(); verifier.VerifyIL("Program.M", $$""" { // Code size 62 (0x3e) .maxstack 3 .locals init (System.{{spanType}}<int> V_0, int V_1, object[] V_2, System.{{spanType}}<int>.Enumerator V_3, int V_4) IL_0000: ldarg.0 IL_0001: stloc.0 IL_0002: ldc.i4.0 IL_0003: stloc.1 IL_0004: ldloca.s V_0 IL_0006: call "int System.{{spanType}}<int>.Length.get" IL_000b: newarr "object" IL_0010: stloc.2 IL_0011: ldloca.s V_0 IL_0013: call "System.{{spanType}}<int>.Enumerator System.{{spanType}}<int>.GetEnumerator()" IL_0018: stloc.3 IL_0019: br.s IL_0033 IL_001b: ldloca.s V_3 IL_001d: call "ref {{(readOnlySpan ? "readonly " : "")}}int System.{{spanType}}<int>.Enumerator.Current.get" IL_0022: ldind.i4 IL_0023: stloc.s V_4 IL_0025: ldloc.2 IL_0026: ldloc.1 IL_0027: ldloc.s V_4 IL_0029: box "int" IL_002e: stelem.ref IL_002f: ldloc.1 IL_0030: ldc.i4.1 IL_0031: add IL_0032: stloc.1 IL_0033: ldloca.s V_3 IL_0035: call "bool System.{{spanType}}<int>.Enumerator.MoveNext()" IL_003a: brtrue.s IL_001b IL_003c: ldloc.2 IL_003d: ret } """); } [Theory] [CombinatorialData] public void Span_01(bool useReadOnlySpan) { string spanType = useReadOnlySpan ? "ReadOnlySpan" : "Span"; string source = $$""" using System; class Program { static void Create1() { {{spanType}}<int> s = []; s.Report(); } static void Create2() { {{spanType}}<object> s = [1, 2]; s.Report(); } static void Create3() { {{spanType}}<int> s = [3, 4, 5]; s.Report(); } static void Create4() { {{spanType}}<long?> s = [null, 7]; s.Report(); } static void Main() { Create1(); Create2(); Create3(); Create4(); } } """; var verifier = CompileAndVerify( new[] { source, s_collectionExtensionsWithSpan }, targetFramework: TargetFramework.Net70, verify: Verification.Skipped, expectedOutput: IncludeExpectedOutput("[], [1, 2], [3, 4, 5], [null, 7], ")); if (useReadOnlySpan) { verifier.VerifyIL("Program.Create1", """ { // Code size 16 (0x10) .maxstack 1 .locals init (System.ReadOnlySpan<int> V_0) //s IL_0000: ldloca.s V_0 IL_0002: initobj "System.ReadOnlySpan<int>" IL_0008: ldloca.s V_0 IL_000a: call "void CollectionExtensions.Report<int>(in System.ReadOnlySpan<int>)" IL_000f: ret } """); verifier.VerifyIL("Program.Create2", """ { // Code size 39 (0x27) .maxstack 5 .locals init (System.ReadOnlySpan<object> V_0) //s IL_0000: ldloca.s V_0 IL_0002: ldc.i4.2 IL_0003: newarr "object" IL_0008: dup IL_0009: ldc.i4.0 IL_000a: ldc.i4.1 IL_000b: box "int" IL_0010: stelem.ref IL_0011: dup IL_0012: ldc.i4.1 IL_0013: ldc.i4.2 IL_0014: box "int" IL_0019: stelem.ref IL_001a: call "System.ReadOnlySpan<object>..ctor(object[])" IL_001f: ldloca.s V_0 IL_0021: call "void CollectionExtensions.Report<object>(in System.ReadOnlySpan<object>)" IL_0026: ret } """); verifier.VerifyIL("Program.Create3", """ { // Code size 19 (0x13) .maxstack 1 .locals init (System.ReadOnlySpan<int> V_0) //s IL_0000: ldtoken "<PrivateImplementationDetails>.__StaticArrayInitTypeSize=12_Align=4 <PrivateImplementationDetails>.CE99AE045C8B2A2A8A58FD1A2120956E74E90322EEF45F7DFE1CA73EEFE655D44" IL_0005: call "System.ReadOnlySpan<int> System.Runtime.CompilerServices.RuntimeHelpers.CreateSpan<int>(System.RuntimeFieldHandle)" IL_000a: stloc.0 IL_000b: ldloca.s V_0 IL_000d: call "void CollectionExtensions.Report<int>(in System.ReadOnlySpan<int>)" IL_0012: ret } """); verifier.VerifyIL("Program.Create4", """ { // Code size 35 (0x23) .maxstack 5 .locals init (System.ReadOnlySpan<long?> V_0) //s IL_0000: ldloca.s V_0 IL_0002: ldc.i4.2 IL_0003: newarr "long?" IL_0008: dup IL_0009: ldc.i4.1 IL_000a: ldc.i4.7 IL_000b: conv.i8 IL_000c: newobj "long?..ctor(long)" IL_0011: stelem "long?" IL_0016: call "System.ReadOnlySpan<long?>..ctor(long?[])" IL_001b: ldloca.s V_0 IL_001d: call "void CollectionExtensions.Report<long?>(in System.ReadOnlySpan<long?>)" IL_0022: ret } """); } else { verifier.VerifyIL("Program.Create1", """ { // Code size 23 (0x17) .maxstack 1 .locals init (System.Span<int> V_0, System.ReadOnlySpan<int> V_1) IL_0000: ldloca.s V_0 IL_0002: initobj "System.Span<int>" IL_0008: ldloc.0 IL_0009: call "System.ReadOnlySpan<int> System.Span<int>.op_Implicit(System.Span<int>)" IL_000e: stloc.1 IL_000f: ldloca.s V_1 IL_0011: call "void CollectionExtensions.Report<int>(in System.ReadOnlySpan<int>)" IL_0016: ret } """); verifier.VerifyIL("Program.Create2", """ { // Code size 43 (0x2b) .maxstack 4 .locals init (System.ReadOnlySpan<object> V_0) IL_0000: ldc.i4.2 IL_0001: newarr "object" IL_0006: dup IL_0007: ldc.i4.0 IL_0008: ldc.i4.1 IL_0009: box "int" IL_000e: stelem.ref IL_000f: dup IL_0010: ldc.i4.1 IL_0011: ldc.i4.2 IL_0012: box "int" IL_0017: stelem.ref IL_0018: newobj "System.Span<object>..ctor(object[])" IL_001d: call "System.ReadOnlySpan<object> System.Span<object>.op_Implicit(System.Span<object>)" IL_0022: stloc.0 IL_0023: ldloca.s V_0 IL_0025: call "void CollectionExtensions.Report<object>(in System.ReadOnlySpan<object>)" IL_002a: ret } """); verifier.VerifyIL("Program.Create3", """ { // Code size 36 (0x24) .maxstack 3 .locals init (System.ReadOnlySpan<int> V_0) IL_0000: ldc.i4.3 IL_0001: newarr "int" IL_0006: dup IL_0007: ldtoken "<PrivateImplementationDetails>.__StaticArrayInitTypeSize=12 <PrivateImplementationDetails>.CE99AE045C8B2A2A8A58FD1A2120956E74E90322EEF45F7DFE1CA73EEFE655D4" IL_000c: call "void System.Runtime.CompilerServices.RuntimeHelpers.InitializeArray(System.Array, System.RuntimeFieldHandle)" IL_0011: newobj "System.Span<int>..ctor(int[])" IL_0016: call "System.ReadOnlySpan<int> System.Span<int>.op_Implicit(System.Span<int>)" IL_001b: stloc.0 IL_001c: ldloca.s V_0 IL_001e: call "void CollectionExtensions.Report<int>(in System.ReadOnlySpan<int>)" IL_0023: ret } """); verifier.VerifyIL("Program.Create4", """ { // Code size 39 (0x27) .maxstack 4 .locals init (System.ReadOnlySpan<long?> V_0) IL_0000: ldc.i4.2 IL_0001: newarr "long?" IL_0006: dup IL_0007: ldc.i4.1 IL_0008: ldc.i4.7 IL_0009: conv.i8 IL_000a: newobj "long?..ctor(long)" IL_000f: stelem "long?" IL_0014: newobj "System.Span<long?>..ctor(long?[])" IL_0019: call "System.ReadOnlySpan<long?> System.Span<long?>.op_Implicit(System.Span<long?>)" IL_001e: stloc.0 IL_001f: ldloca.s V_0 IL_0021: call "void CollectionExtensions.Report<long?>(in System.ReadOnlySpan<long?>)" IL_0026: ret } """); } } [Theory] [CombinatorialData] public void Span_02(bool useReadOnlySpan) { string spanType = useReadOnlySpan ? "ReadOnlySpan" : "Span"; string source = $$""" using System; class Program { static void Main() { {{spanType}}<string> x = []; {{spanType}}<int> y = [1, 2, 3]; x.Report(); y.Report(); } } """; CompileAndVerify( new[] { source, s_collectionExtensionsWithSpan }, targetFramework: TargetFramework.Net70, verify: Verification.Skipped, expectedOutput: IncludeExpectedOutput("[], [1, 2, 3], ")); } [Theory] [CombinatorialData] public void Span_03(bool useReadOnlySpan) { string spanType = useReadOnlySpan ? "ReadOnlySpan" : "Span"; string source = $$""" using System; class Program { static void Main() { var x = ({{spanType}}<string>)[]; var y = ({{spanType}}<int>)[1, 2, 3]; x.Report(); y.Report(); } } """; CompileAndVerify( new[] { source, s_collectionExtensionsWithSpan }, targetFramework: TargetFramework.Net70, verify: Verification.Skipped, expectedOutput: IncludeExpectedOutput("[], [1, 2, 3], ")); } [Theory] [CombinatorialData] public void Span_04(bool useReadOnlySpan) { string spanType = useReadOnlySpan ? "ReadOnlySpan" : "Span"; string source = $$""" using System; class Program { static ref readonly {{spanType}}<int> F1() { return ref F2<int>([]); } static ref readonly {{spanType}}<T> F2<T>(in {{spanType}}<T> s) { return ref s; } } """; var comp = CreateCompilation(source, targetFramework: TargetFramework.Net70); comp.VerifyEmitDiagnostics( // (6,20): error CS8347: Cannot use a result of 'Program.F2<int>(in Span<int>)' in this context because it may expose variables referenced by parameter 's' outside of their declaration scope // return ref F2<int>([]); Diagnostic(ErrorCode.ERR_EscapeCall, "F2<int>([])").WithArguments($"Program.F2<int>(in System.{spanType}<int>)", "s").WithLocation(6, 20), // (6,28): error CS8156: An expression cannot be used in this context because it may not be passed or returned by reference // return ref F2<int>([]); Diagnostic(ErrorCode.ERR_RefReturnLvalueExpected, "[]").WithLocation(6, 28)); } [Theory] [CombinatorialData] public void Span_05(bool useReadOnlySpan) { string spanType = useReadOnlySpan ? "ReadOnlySpan" : "Span"; string source = $$""" using System; class Program { static ref readonly {{spanType}}<int> F1() { return ref F2<int>([]); } static ref readonly {{spanType}}<T> F2<T>(scoped in {{spanType}}<T> s) { throw null; } } """; var comp = CreateCompilation(source, targetFramework: TargetFramework.Net70); comp.VerifyEmitDiagnostics(); } [Theory, CombinatorialData] public void Span_06(bool useReadOnlySpan) { var spanType = useReadOnlySpan ? "ReadOnlySpan" : "Span"; var source = $$""" using System; class Program { static void Main() { {{spanType}}<Derived> d = CreateArray(); Span<Base> b = [.. d]; b.Report(); b[0] = new Base(); b.Report(); } static Derived[] CreateArray() => [new(), new(), new()]; } class Base { private static int _totalCount; private readonly int _id; public Base() { _id = _totalCount++; } public override string ToString() => $"{GetType().Name} {_id}"; } class Derived : Base; """; var verifier = CompileAndVerify( [source, s_collectionExtensionsWithSpan], targetFramework: TargetFramework.Net90, expectedOutput: IncludeExpectedOutput("[Derived 0, Derived 1, Derived 2], [Base 3, Derived 1, Derived 2], "), verify: Verification.Skipped); verifier.VerifyDiagnostics(); verifier.VerifyIL("Program.Main", $$""" { // Code size 118 (0x76) .maxstack 3 .locals init (System.Span<Base> V_0, //b System.{{spanType}}<Derived> V_1, int V_2, Base[] V_3, System.{{spanType}}<Derived>.Enumerator V_4, Derived V_5, System.ReadOnlySpan<Base> V_6) IL_0000: call "Derived[] Program.CreateArray()" IL_0005: call "System.{{spanType}}<Derived> System.{{spanType}}<Derived>.op_Implicit(Derived[])" IL_000a: stloc.1 IL_000b: ldc.i4.0 IL_000c: stloc.2 IL_000d: ldloca.s V_1 IL_000f: call "int System.{{spanType}}<Derived>.Length.get" IL_0014: newarr "Base" IL_0019: stloc.3 IL_001a: ldloca.s V_1 IL_001c: call "System.{{spanType}}<Derived>.Enumerator System.{{spanType}}<Derived>.GetEnumerator()" IL_0021: stloc.s V_4 IL_0023: br.s IL_0038 IL_0025: ldloca.s V_4 IL_0027: call "ref {{(useReadOnlySpan ? "readonly " : "")}}Derived System.{{spanType}}<Derived>.Enumerator.Current.get" IL_002c: ldind.ref IL_002d: stloc.s V_5 IL_002f: ldloc.3 IL_0030: ldloc.2 IL_0031: ldloc.s V_5 IL_0033: stelem.ref IL_0034: ldloc.2 IL_0035: ldc.i4.1 IL_0036: add IL_0037: stloc.2 IL_0038: ldloca.s V_4 IL_003a: call "bool System.{{spanType}}<Derived>.Enumerator.MoveNext()" IL_003f: brtrue.s IL_0025 IL_0041: ldloca.s V_0 IL_0043: ldloc.3 IL_0044: call "System.Span<Base>..ctor(Base[])" IL_0049: ldloc.0 IL_004a: call "System.ReadOnlySpan<Base> System.Span<Base>.op_Implicit(System.Span<Base>)" IL_004f: stloc.s V_6 IL_0051: ldloca.s V_6 IL_0053: call "void CollectionExtensions.Report<Base>(in System.ReadOnlySpan<Base>)" IL_0058: ldloca.s V_0 IL_005a: ldc.i4.0 IL_005b: call "ref Base System.Span<Base>.this[int].get" IL_0060: newobj "Base..ctor()" IL_0065: stind.ref IL_0066: ldloc.0 IL_0067: call "System.ReadOnlySpan<Base> System.Span<Base>.op_Implicit(System.Span<Base>)" IL_006c: stloc.s V_6 IL_006e: ldloca.s V_6 IL_0070: call "void CollectionExtensions.Report<Base>(in System.ReadOnlySpan<Base>)" IL_0075: ret } """); } [Fact] public void Span_07() { var source = """ using System; using System.Collections.Generic; class Program { static void Main() { List<Derived> d = CreateList(); Span<Base> b = [.. d]; b.Report(); b[0] = new Base(); b.Report(); } static List<Derived> CreateList() => [new(), new(), new()]; } class Base { private static int _totalCount; private readonly int _id; public Base() { _id = _totalCount++; } public override string ToString() => $"{GetType().Name} {_id}"; } class Derived : Base; """; var verifier = CompileAndVerify( [source, s_collectionExtensionsWithSpan], targetFramework: TargetFramework.Net90, expectedOutput: IncludeExpectedOutput("[Derived 0, Derived 1, Derived 2], [Base 3, Derived 1, Derived 2], "), verify: Verification.Skipped); verifier.VerifyDiagnostics(); verifier.VerifyIL("Program.Main", """ { // Code size 123 (0x7b) .maxstack 3 .locals init (System.Span<Base> V_0, //b int V_1, Base[] V_2, System.Collections.Generic.List<Derived>.Enumerator V_3, Derived V_4, System.ReadOnlySpan<Base> V_5) IL_0000: call "System.Collections.Generic.List<Derived> Program.CreateList()" IL_0005: ldc.i4.0 IL_0006: stloc.1 IL_0007: dup IL_0008: callvirt "int System.Collections.Generic.List<Derived>.Count.get" IL_000d: newarr "Base" IL_0012: stloc.2 IL_0013: callvirt "System.Collections.Generic.List<Derived>.Enumerator System.Collections.Generic.List<Derived>.GetEnumerator()" IL_0018: stloc.3 .try { IL_0019: br.s IL_002d IL_001b: ldloca.s V_3 IL_001d: call "Derived System.Collections.Generic.List<Derived>.Enumerator.Current.get" IL_0022: stloc.s V_4 IL_0024: ldloc.2 IL_0025: ldloc.1 IL_0026: ldloc.s V_4 IL_0028: stelem.ref IL_0029: ldloc.1 IL_002a: ldc.i4.1 IL_002b: add IL_002c: stloc.1 IL_002d: ldloca.s V_3 IL_002f: call "bool System.Collections.Generic.List<Derived>.Enumerator.MoveNext()" IL_0034: brtrue.s IL_001b IL_0036: leave.s IL_0046 } finally { IL_0038: ldloca.s V_3 IL_003a: constrained. "System.Collections.Generic.List<Derived>.Enumerator" IL_0040: callvirt "void System.IDisposable.Dispose()" IL_0045: endfinally } IL_0046: ldloca.s V_0 IL_0048: ldloc.2 IL_0049: call "System.Span<Base>..ctor(Base[])" IL_004e: ldloc.0 IL_004f: call "System.ReadOnlySpan<Base> System.Span<Base>.op_Implicit(System.Span<Base>)" IL_0054: stloc.s V_5 IL_0056: ldloca.s V_5 IL_0058: call "void CollectionExtensions.Report<Base>(in System.ReadOnlySpan<Base>)" IL_005d: ldloca.s V_0 IL_005f: ldc.i4.0 IL_0060: call "ref Base System.Span<Base>.this[int].get" IL_0065: newobj "Base..ctor()" IL_006a: stind.ref IL_006b: ldloc.0 IL_006c: call "System.ReadOnlySpan<Base> System.Span<Base>.op_Implicit(System.Span<Base>)" IL_0071: stloc.s V_5 IL_0073: ldloca.s V_5 IL_0075: call "void CollectionExtensions.Report<Base>(in System.ReadOnlySpan<Base>)" IL_007a: ret } """); } [Theory, CombinatorialData] public void Span_08(bool useReadOnlySpan) { var spanType = useReadOnlySpan ? "ReadOnlySpan" : "Span"; var source = $$""" using System; class Program { static void Main() { {{spanType}}<object> o = CreateArray(); Span<dynamic> d = [.. o]; d.Report(); d[0] = "a"; d.Report(); } static object[] CreateArray() => [1, 2, 3]; } """; var verifier = CompileAndVerify( [source, s_collectionExtensionsWithSpan], targetFramework: TargetFramework.Net90, expectedOutput: IncludeExpectedOutput("[1, 2, 3], [a, 2, 3], "), verify: Verification.Skipped); verifier.VerifyDiagnostics(); verifier.VerifyIL("Program.Main", $$""" { // Code size 68 (0x44) .maxstack 2 .locals init (System.{{spanType}}<object> V_0, //o System.Span<dynamic> V_1, //d System.ReadOnlySpan<dynamic> V_2) IL_0000: call "object[] Program.CreateArray()" IL_0005: call "System.{{spanType}}<object> System.{{spanType}}<object>.op_Implicit(object[])" IL_000a: stloc.0 IL_000b: ldloca.s V_1 IL_000d: ldloca.s V_0 IL_000f: call "object[] System.{{spanType}}<object>.ToArray()" IL_0014: call "System.Span<dynamic>..ctor(dynamic[])" IL_0019: ldloc.1 IL_001a: call "System.ReadOnlySpan<dynamic> System.Span<dynamic>.op_Implicit(System.Span<dynamic>)" IL_001f: stloc.2 IL_0020: ldloca.s V_2 IL_0022: call "void CollectionExtensions.Report<dynamic>(in System.ReadOnlySpan<dynamic>)" IL_0027: ldloca.s V_1 IL_0029: ldc.i4.0 IL_002a: call "ref dynamic System.Span<dynamic>.this[int].get" IL_002f: ldstr "a" IL_0034: stind.ref IL_0035: ldloc.1 IL_0036: call "System.ReadOnlySpan<dynamic> System.Span<dynamic>.op_Implicit(System.Span<dynamic>)" IL_003b: stloc.2 IL_003c: ldloca.s V_2 IL_003e: call "void CollectionExtensions.Report<dynamic>(in System.ReadOnlySpan<dynamic>)" IL_0043: ret } """); } [Fact] public void Span_09() { var source = """ using System; using System.Collections.Generic; class Program { static void Main() { List<object> o = CreateList(); Span<dynamic> d = [.. o]; d.Report(); d[0] = "a"; d.Report(); } static List<object> CreateList() => [1, 2, 3]; } """; var verifier = CompileAndVerify( [source, s_collectionExtensionsWithSpan], targetFramework: TargetFramework.Net90, expectedOutput: IncludeExpectedOutput("[1, 2, 3], [a, 2, 3], "), verify: Verification.Skipped); verifier.VerifyDiagnostics(); verifier.VerifyIL("Program.Main", """ { // Code size 62 (0x3e) .maxstack 2 .locals init (System.Collections.Generic.List<object> V_0, //o System.Span<dynamic> V_1, //d System.ReadOnlySpan<dynamic> V_2) IL_0000: call "System.Collections.Generic.List<object> Program.CreateList()" IL_0005: stloc.0 IL_0006: ldloca.s V_1 IL_0008: ldloc.0 IL_0009: callvirt "object[] System.Collections.Generic.List<object>.ToArray()" IL_000e: call "System.Span<dynamic>..ctor(dynamic[])" IL_0013: ldloc.1 IL_0014: call "System.ReadOnlySpan<dynamic> System.Span<dynamic>.op_Implicit(System.Span<dynamic>)" IL_0019: stloc.2 IL_001a: ldloca.s V_2 IL_001c: call "void CollectionExtensions.Report<dynamic>(in System.ReadOnlySpan<dynamic>)" IL_0021: ldloca.s V_1 IL_0023: ldc.i4.0 IL_0024: call "ref dynamic System.Span<dynamic>.this[int].get" IL_0029: ldstr "a" IL_002e: stind.ref IL_002f: ldloc.1 IL_0030: call "System.ReadOnlySpan<dynamic> System.Span<dynamic>.op_Implicit(System.Span<dynamic>)" IL_0035: stloc.2 IL_0036: ldloca.s V_2 IL_0038: call "void CollectionExtensions.Report<dynamic>(in System.ReadOnlySpan<dynamic>)" IL_003d: ret } """); } [Theory, CombinatorialData] public void Span_10(bool useReadOnlySpan) { var spanType = useReadOnlySpan ? "ReadOnlySpan" : "Span"; var source = $$""" using System; class Program { static void Main() { {{spanType}}<int> i = CreateArray(); Span<object> o = [.. i]; o.Report(); o[0] = "a"; o.Report(); } static int[] CreateArray() => [1, 2, 3]; } """; var verifier = CompileAndVerify( [source, s_collectionExtensionsWithSpan], targetFramework: TargetFramework.Net90, expectedOutput: IncludeExpectedOutput("[1, 2, 3], [a, 2, 3], "), verify: Verification.Skipped); verifier.VerifyDiagnostics(); verifier.VerifyIL("Program.Main", $$""" { // Code size 123 (0x7b) .maxstack 3 .locals init (System.Span<object> V_0, //o System.{{spanType}}<int> V_1, int V_2, object[] V_3, System.{{spanType}}<int>.Enumerator V_4, int V_5, System.ReadOnlySpan<object> V_6) IL_0000: call "int[] Program.CreateArray()" IL_0005: call "System.{{spanType}}<int> System.{{spanType}}<int>.op_Implicit(int[])" IL_000a: stloc.1 IL_000b: ldc.i4.0 IL_000c: stloc.2 IL_000d: ldloca.s V_1 IL_000f: call "int System.{{spanType}}<int>.Length.get" IL_0014: newarr "object" IL_0019: stloc.3 IL_001a: ldloca.s V_1 IL_001c: call "System.{{spanType}}<int>.Enumerator System.{{spanType}}<int>.GetEnumerator()" IL_0021: stloc.s V_4 IL_0023: br.s IL_003d IL_0025: ldloca.s V_4 IL_0027: call "ref {{(useReadOnlySpan ? "readonly " : "")}}int System.{{spanType}}<int>.Enumerator.Current.get" IL_002c: ldind.i4 IL_002d: stloc.s V_5 IL_002f: ldloc.3 IL_0030: ldloc.2 IL_0031: ldloc.s V_5 IL_0033: box "int" IL_0038: stelem.ref IL_0039: ldloc.2 IL_003a: ldc.i4.1 IL_003b: add IL_003c: stloc.2 IL_003d: ldloca.s V_4 IL_003f: call "bool System.{{spanType}}<int>.Enumerator.MoveNext()" IL_0044: brtrue.s IL_0025 IL_0046: ldloca.s V_0 IL_0048: ldloc.3 IL_0049: call "System.Span<object>..ctor(object[])" IL_004e: ldloc.0 IL_004f: call "System.ReadOnlySpan<object> System.Span<object>.op_Implicit(System.Span<object>)" IL_0054: stloc.s V_6 IL_0056: ldloca.s V_6 IL_0058: call "void CollectionExtensions.Report<object>(in System.ReadOnlySpan<object>)" IL_005d: ldloca.s V_0 IL_005f: ldc.i4.0 IL_0060: call "ref object System.Span<object>.this[int].get" IL_0065: ldstr "a" IL_006a: stind.ref IL_006b: ldloc.0 IL_006c: call "System.ReadOnlySpan<object> System.Span<object>.op_Implicit(System.Span<object>)" IL_0071: stloc.s V_6 IL_0073: ldloca.s V_6 IL_0075: call "void CollectionExtensions.Report<object>(in System.ReadOnlySpan<object>)" IL_007a: ret } """); } [Fact] public void Span_11() { var source = $$""" using System; using System.Collections.Generic; class Program { static void Main() { List<int> i = CreateList(); Span<object> o = [.. i]; o.Report(); o[0] = "a"; o.Report(); } static List<int> CreateList() => [1, 2, 3]; } """; var verifier = CompileAndVerify( [source, s_collectionExtensionsWithSpan], targetFramework: TargetFramework.Net90, expectedOutput: IncludeExpectedOutput("[1, 2, 3], [a, 2, 3], "), verify: Verification.Skipped); verifier.VerifyDiagnostics(); verifier.VerifyIL("Program.Main", """ { // Code size 128 (0x80) .maxstack 3 .locals init (System.Span<object> V_0, //o int V_1, object[] V_2, System.Collections.Generic.List<int>.Enumerator V_3, int V_4, System.ReadOnlySpan<object> V_5) IL_0000: call "System.Collections.Generic.List<int> Program.CreateList()" IL_0005: ldc.i4.0 IL_0006: stloc.1 IL_0007: dup IL_0008: callvirt "int System.Collections.Generic.List<int>.Count.get" IL_000d: newarr "object" IL_0012: stloc.2 IL_0013: callvirt "System.Collections.Generic.List<int>.Enumerator System.Collections.Generic.List<int>.GetEnumerator()" IL_0018: stloc.3 .try { IL_0019: br.s IL_0032 IL_001b: ldloca.s V_3 IL_001d: call "int System.Collections.Generic.List<int>.Enumerator.Current.get" IL_0022: stloc.s V_4 IL_0024: ldloc.2 IL_0025: ldloc.1 IL_0026: ldloc.s V_4 IL_0028: box "int" IL_002d: stelem.ref IL_002e: ldloc.1 IL_002f: ldc.i4.1 IL_0030: add IL_0031: stloc.1 IL_0032: ldloca.s V_3 IL_0034: call "bool System.Collections.Generic.List<int>.Enumerator.MoveNext()" IL_0039: brtrue.s IL_001b IL_003b: leave.s IL_004b } finally { IL_003d: ldloca.s V_3 IL_003f: constrained. "System.Collections.Generic.List<int>.Enumerator" IL_0045: callvirt "void System.IDisposable.Dispose()" IL_004a: endfinally } IL_004b: ldloca.s V_0 IL_004d: ldloc.2 IL_004e: call "System.Span<object>..ctor(object[])" IL_0053: ldloc.0 IL_0054: call "System.ReadOnlySpan<object> System.Span<object>.op_Implicit(System.Span<object>)" IL_0059: stloc.s V_5 IL_005b: ldloca.s V_5 IL_005d: call "void CollectionExtensions.Report<object>(in System.ReadOnlySpan<object>)" IL_0062: ldloca.s V_0 IL_0064: ldc.i4.0 IL_0065: call "ref object System.Span<object>.this[int].get" IL_006a: ldstr "a" IL_006f: stind.ref IL_0070: ldloc.0 IL_0071: call "System.ReadOnlySpan<object> System.Span<object>.op_Implicit(System.Span<object>)" IL_0076: stloc.s V_5 IL_0078: ldloca.s V_5 IL_007a: call "void CollectionExtensions.Report<object>(in System.ReadOnlySpan<object>)" IL_007f: ret } """); } [Fact] public void Span_MissingConstructor() { string source = """ using System; class Program { static void Main() { Span<string> x = []; ReadOnlySpan<int> y = [1, 2, 3]; } } """; var comp = CreateCompilation(source, targetFramework: TargetFramework.Net70); comp.MakeMemberMissing(WellKnownMember.System_Span_T__ctor_Array); comp.VerifyEmitDiagnostics( // (6,26): error CS0656: Missing compiler required member 'System.Span`1..ctor' // Span<string> x = []; Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "[]").WithArguments("System.Span`1", ".ctor").WithLocation(6, 26)); comp = CreateCompilation(source, targetFramework: TargetFramework.Net70); comp.MakeMemberMissing(WellKnownMember.System_ReadOnlySpan_T__ctor_Array); comp.VerifyEmitDiagnostics( // (7,31): error CS0656: Missing compiler required member 'System.ReadOnlySpan`1..ctor' // ReadOnlySpan<int> y = [1, 2, 3]; Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "[1, 2, 3]").WithArguments("System.ReadOnlySpan`1", ".ctor").WithLocation(7, 31)); } [Fact] public void InterfaceType() { string source = """ using System; using System.Collections; using System.Runtime.InteropServices; [ComImport] [Guid("1FC6664D-C61E-4131-81CD-A3EE0DD6098F")] [CoClass(typeof(C))] interface I : IEnumerable { void Add(int i); } class C : I { IEnumerator IEnumerable.GetEnumerator() => null; void I.Add(int i) { } } class Program { static void Main() { I i; i = new() { }; i = new() { 1, 2 }; i = []; i = [3, 4]; } } """; var comp = CreateCompilation(source); comp.VerifyEmitDiagnostics( // (26,13): error CS9174: Cannot initialize type 'I' with a collection expression because the type is not constructible. // i = []; Diagnostic(ErrorCode.ERR_CollectionExpressionTargetTypeNotConstructible, "[]").WithArguments("I").WithLocation(26, 13), // (27,13): error CS9174: Cannot initialize type 'I' with a collection expression because the type is not constructible. // i = [3, 4]; Diagnostic(ErrorCode.ERR_CollectionExpressionTargetTypeNotConstructible, "[3, 4]").WithArguments("I").WithLocation(27, 13)); } [Fact] public void StringType_01() { string source = """ class Program { static void Main() { string s; s = []; s = [default]; s = [null]; s = ['a']; s = [1]; s = [..""]; } } """; var comp = CreateCompilation(source); comp.VerifyEmitDiagnostics( // (6,13): error CS1729: 'string' does not contain a constructor that takes 0 arguments // s = []; Diagnostic(ErrorCode.ERR_BadCtorArgCount, "[]").WithArguments("string", "0").WithLocation(6, 13), // (7,13): error CS1729: 'string' does not contain a constructor that takes 0 arguments // s = [default]; Diagnostic(ErrorCode.ERR_BadCtorArgCount, "[default]").WithArguments("string", "0").WithLocation(7, 13), // (7,13): error CS1061: 'string' does not contain a definition for 'Add' and no accessible extension method 'Add' accepting a first argument of type 'string' could be found (are you missing a using directive or an assembly reference?) // s = [default]; Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "[default]").WithArguments("string", "Add").WithLocation(7, 13), // (8,13): error CS1729: 'string' does not contain a constructor that takes 0 arguments // s = [null]; Diagnostic(ErrorCode.ERR_BadCtorArgCount, "[null]").WithArguments("string", "0").WithLocation(8, 13), // (8,13): error CS1061: 'string' does not contain a definition for 'Add' and no accessible extension method 'Add' accepting a first argument of type 'string' could be found (are you missing a using directive or an assembly reference?) // s = [null]; Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "[null]").WithArguments("string", "Add").WithLocation(8, 13), // (8,14): error CS0037: Cannot convert null to 'char' because it is a non-nullable value type // s = [null]; Diagnostic(ErrorCode.ERR_ValueCantBeNull, "null").WithArguments("char").WithLocation(8, 14), // (9,13): error CS1729: 'string' does not contain a constructor that takes 0 arguments // s = ['a']; Diagnostic(ErrorCode.ERR_BadCtorArgCount, "['a']").WithArguments("string", "0").WithLocation(9, 13), // (9,13): error CS1061: 'string' does not contain a definition for 'Add' and no accessible extension method 'Add' accepting a first argument of type 'string' could be found (are you missing a using directive or an assembly reference?) // s = ['a']; Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "['a']").WithArguments("string", "Add").WithLocation(9, 13), // (10,13): error CS1729: 'string' does not contain a constructor that takes 0 arguments // s = [1]; Diagnostic(ErrorCode.ERR_BadCtorArgCount, "[1]").WithArguments("string", "0").WithLocation(10, 13), // (10,13): error CS1061: 'string' does not contain a definition for 'Add' and no accessible extension method 'Add' accepting a first argument of type 'string' could be found (are you missing a using directive or an assembly reference?) // s = [1]; Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "[1]").WithArguments("string", "Add").WithLocation(10, 13), // (10,14): error CS0029: Cannot implicitly convert type 'int' to 'char' // s = [1]; Diagnostic(ErrorCode.ERR_NoImplicitConv, "1").WithArguments("int", "char").WithLocation(10, 14), // (11,13): error CS1729: 'string' does not contain a constructor that takes 0 arguments // s = [..""]; Diagnostic(ErrorCode.ERR_BadCtorArgCount, @"[..""""]").WithArguments("string", "0").WithLocation(11, 13), // (11,13): error CS1061: 'string' does not contain a definition for 'Add' and no accessible extension method 'Add' accepting a first argument of type 'string' could be found (are you missing a using directive or an assembly reference?) // s = [..""]; Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, @"[..""""]").WithArguments("string", "Add").WithLocation(11, 13)); } [WorkItem("https://github.com/dotnet/roslyn/pull/71492")] [Fact] public void StringType_02() { string source = """ class Program { static void Main() { _ = (string)[]; _ = (string)[default]; _ = (string)[null]; _ = (string)['a']; _ = (string)[1]; _ = (string)[..""]; } } """; var comp = CreateCompilation(source); comp.VerifyEmitDiagnostics( // (5,21): error CS1729: 'string' does not contain a constructor that takes 0 arguments // _ = (string)[]; Diagnostic(ErrorCode.ERR_BadCtorArgCount, "[]").WithArguments("string", "0").WithLocation(5, 21), // (6,21): error CS1729: 'string' does not contain a constructor that takes 0 arguments // _ = (string)[default]; Diagnostic(ErrorCode.ERR_BadCtorArgCount, "[default]").WithArguments("string", "0").WithLocation(6, 21), // (6,21): error CS1061: 'string' does not contain a definition for 'Add' and no accessible extension method 'Add' accepting a first argument of type 'string' could be found (are you missing a using directive or an assembly reference?) // _ = (string)[default]; Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "[default]").WithArguments("string", "Add").WithLocation(6, 21), // (6,22): error CS8716: There is no target type for the default literal. // _ = (string)[default]; Diagnostic(ErrorCode.ERR_DefaultLiteralNoTargetType, "default").WithLocation(6, 22), // (7,21): error CS1729: 'string' does not contain a constructor that takes 0 arguments // _ = (string)[null]; Diagnostic(ErrorCode.ERR_BadCtorArgCount, "[null]").WithArguments("string", "0").WithLocation(7, 21), // (7,21): error CS1061: 'string' does not contain a definition for 'Add' and no accessible extension method 'Add' accepting a first argument of type 'string' could be found (are you missing a using directive or an assembly reference?) // _ = (string)[null]; Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "[null]").WithArguments("string", "Add").WithLocation(7, 21), // (7,22): error CS0037: Cannot convert null to 'char' because it is a non-nullable value type // _ = (string)[null]; Diagnostic(ErrorCode.ERR_ValueCantBeNull, "null").WithArguments("char").WithLocation(7, 22), // (8,21): error CS1729: 'string' does not contain a constructor that takes 0 arguments // _ = (string)['a']; Diagnostic(ErrorCode.ERR_BadCtorArgCount, "['a']").WithArguments("string", "0").WithLocation(8, 21), // (8,21): error CS1061: 'string' does not contain a definition for 'Add' and no accessible extension method 'Add' accepting a first argument of type 'string' could be found (are you missing a using directive or an assembly reference?) // _ = (string)['a']; Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "['a']").WithArguments("string", "Add").WithLocation(8, 21), // (9,21): error CS1729: 'string' does not contain a constructor that takes 0 arguments // _ = (string)[1]; Diagnostic(ErrorCode.ERR_BadCtorArgCount, "[1]").WithArguments("string", "0").WithLocation(9, 21), // (9,21): error CS1061: 'string' does not contain a definition for 'Add' and no accessible extension method 'Add' accepting a first argument of type 'string' could be found (are you missing a using directive or an assembly reference?) // _ = (string)[1]; Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "[1]").WithArguments("string", "Add").WithLocation(9, 21), // (9,22): error CS0029: Cannot implicitly convert type 'int' to 'char' // _ = (string)[1]; Diagnostic(ErrorCode.ERR_NoImplicitConv, "1").WithArguments("int", "char").WithLocation(9, 22), // (10,21): error CS1729: 'string' does not contain a constructor that takes 0 arguments // _ = (string)[..""]; Diagnostic(ErrorCode.ERR_BadCtorArgCount, @"[..""""]").WithArguments("string", "0").WithLocation(10, 21), // (10,21): error CS1061: 'string' does not contain a definition for 'Add' and no accessible extension method 'Add' accepting a first argument of type 'string' could be found (are you missing a using directive or an assembly reference?) // _ = (string)[..""]; Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, @"[..""""]").WithArguments("string", "Add").WithLocation(10, 21)); } [Fact] public void EnumType_01() { string source = """ enum E { } class Program { static void Main() { E e; e = []; e = [1, 2]; } } """; var comp = CreateCompilation(source); comp.VerifyEmitDiagnostics( // (7,13): error CS9174: Cannot initialize type 'E' with a collection expression because the type is not constructible. // e = []; Diagnostic(ErrorCode.ERR_CollectionExpressionTargetTypeNotConstructible, "[]").WithArguments("E").WithLocation(7, 13), // (8,13): error CS9174: Cannot initialize type 'E' with a collection expression because the type is not constructible. // e = [1, 2]; Diagnostic(ErrorCode.ERR_CollectionExpressionTargetTypeNotConstructible, "[1, 2]").WithArguments("E").WithLocation(8, 13)); } [Fact] public void EnumType_02() { string sourceA = """ using System.Collections; namespace System { public class Object { } public abstract class ValueType { } public class String { } public class Type { } public struct Void { } public struct Boolean { } public struct Int32 { } public struct Enum : IEnumerable { } } namespace System.Collections { public interface IEnumerable { } } namespace System.Collections.Generic { public class List<T> : IEnumerable { } } """; string sourceB = """ enum E { } class Program { static void Main() { E e; e = []; e = [1, 2]; } } """; var comp = CreateEmptyCompilation(new[] { sourceA, sourceB }, parseOptions: TestOptions.RegularPreview.WithNoRefSafetyRulesAttribute()); // ConversionsBase.GetConstructibleCollectionType() ignores whether the enum // implements IEnumerable, so the type is not considered constructible. comp.VerifyEmitDiagnostics( // warning CS8021: No value for RuntimeMetadataVersion found. No assembly containing System.Object was found nor was a value for RuntimeMetadataVersion specified through options. Diagnostic(ErrorCode.WRN_NoRuntimeMetadataVersion).WithLocation(1, 1), // 1.cs(7,13): error CS9174: Cannot initialize type 'E' with a collection expression because the type is not constructible. // e = []; Diagnostic(ErrorCode.ERR_CollectionExpressionTargetTypeNotConstructible, "[]").WithArguments("E").WithLocation(7, 13), // 1.cs(8,13): error CS9174: Cannot initialize type 'E' with a collection expression because the type is not constructible. // e = [1, 2]; Diagnostic(ErrorCode.ERR_CollectionExpressionTargetTypeNotConstructible, "[1, 2]").WithArguments("E").WithLocation(8, 13)); } [Fact] public void DelegateType_01() { string source = """ delegate void D(); class Program { static void Main() { D d; d = []; d = [1, 2]; } } """; var comp = CreateCompilation(source); comp.VerifyEmitDiagnostics( // (7,13): error CS9174: Cannot initialize type 'D' with a collection expression because the type is not constructible. // d = []; Diagnostic(ErrorCode.ERR_CollectionExpressionTargetTypeNotConstructible, "[]").WithArguments("D").WithLocation(7, 13), // (8,13): error CS9174: Cannot initialize type 'D' with a collection expression because the type is not constructible. // d = [1, 2]; Diagnostic(ErrorCode.ERR_CollectionExpressionTargetTypeNotConstructible, "[1, 2]").WithArguments("D").WithLocation(8, 13)); } [Fact] public void DelegateType_02() { string sourceA = """ using System.Collections; namespace System { public class Object { } public abstract class ValueType { } public class String { } public class Type { } public struct Void { } public struct Boolean { } public struct Int32 { } public struct IntPtr { } public abstract class Delegate : IEnumerable { } public abstract class MulticastDelegate : Delegate { } } namespace System.Collections { public interface IEnumerable { } } namespace System.Collections.Generic { public class List<T> : IEnumerable { } } """; string sourceB = """ delegate void D(); class Program { static void Main() { D d; d = []; d = [1, 2]; } } """; var comp = CreateEmptyCompilation(new[] { sourceA, sourceB }, parseOptions: TestOptions.RegularPreview.WithNoRefSafetyRulesAttribute()); // ConversionsBase.GetConstructibleCollectionType() ignores whether the delegate // implements IEnumerable, so the type is not considered constructible. comp.VerifyEmitDiagnostics( // warning CS8021: No value for RuntimeMetadataVersion found. No assembly containing System.Object was found nor was a value for RuntimeMetadataVersion specified through options. Diagnostic(ErrorCode.WRN_NoRuntimeMetadataVersion).WithLocation(1, 1), // 1.cs(7,13): error CS9174: Cannot initialize type 'D' with a collection expression because the type is not constructible. // d = []; Diagnostic(ErrorCode.ERR_CollectionExpressionTargetTypeNotConstructible, "[]").WithArguments("D").WithLocation(7, 13), // 1.cs(8,13): error CS9174: Cannot initialize type 'D' with a collection expression because the type is not constructible. // d = [1, 2]; Diagnostic(ErrorCode.ERR_CollectionExpressionTargetTypeNotConstructible, "[1, 2]").WithArguments("D").WithLocation(8, 13)); } [Fact] public void PointerType_01() { string source = """ class Program { unsafe static void Main() { int* x = []; int* y = [1, 2]; var z = (int*)[3]; } } """; var comp = CreateCompilation(source, options: TestOptions.UnsafeReleaseExe); comp.VerifyEmitDiagnostics( // (5,18): error CS9174: Cannot initialize type 'int*' with a collection expression because the type is not constructible. // int* x = []; Diagnostic(ErrorCode.ERR_CollectionExpressionTargetTypeNotConstructible, "[]").WithArguments("int*").WithLocation(5, 18), // (6,18): error CS9174: Cannot initialize type 'int*' with a collection expression because the type is not constructible. // int* y = [1, 2]; Diagnostic(ErrorCode.ERR_CollectionExpressionTargetTypeNotConstructible, "[1, 2]").WithArguments("int*").WithLocation(6, 18), // (7,23): error CS9174: Cannot initialize type 'int*' with a collection expression because the type is not constructible. // var z = (int*)[3]; Diagnostic(ErrorCode.ERR_CollectionExpressionTargetTypeNotConstructible, "[3]").WithArguments("int*").WithLocation(7, 23)); } [Fact] public void PointerType_02() { string source = """ class Program { unsafe static void Main() { delegate*<void> x = []; delegate*<void> y = [1, 2]; var z = (delegate*<void>)[3]; } } """; var comp = CreateCompilation(source, options: TestOptions.UnsafeReleaseExe); comp.VerifyEmitDiagnostics( // (5,29): error CS9174: Cannot initialize type 'delegate*<void>' with a collection expression because the type is not constructible. // delegate*<void> x = []; Diagnostic(ErrorCode.ERR_CollectionExpressionTargetTypeNotConstructible, "[]").WithArguments("delegate*<void>").WithLocation(5, 29), // (6,29): error CS9174: Cannot initialize type 'delegate*<void>' with a collection expression because the type is not constructible. // delegate*<void> y = [1, 2]; Diagnostic(ErrorCode.ERR_CollectionExpressionTargetTypeNotConstructible, "[1, 2]").WithArguments("delegate*<void>").WithLocation(6, 29), // (7,34): error CS9174: Cannot initialize type 'delegate*<void>' with a collection expression because the type is not constructible. // var z = (delegate*<void>)[3]; Diagnostic(ErrorCode.ERR_CollectionExpressionTargetTypeNotConstructible, "[3]").WithArguments("delegate*<void>").WithLocation(7, 34)); } [Fact] public void PointerType_03() { string source = """ class Program { unsafe static void Main() { void* p = null; delegate*<void> d = null; var x = [p]; var y = [d]; } } """; var comp = CreateCompilation(source, options: TestOptions.UnsafeReleaseExe); comp.VerifyEmitDiagnostics( // (7,17): error CS9176: There is no target type for the collection expression. // var x = [p]; Diagnostic(ErrorCode.ERR_CollectionExpressionNoTargetType, "[p]").WithLocation(7, 17), // (8,17): error CS9176: There is no target type for the collection expression. // var y = [d]; Diagnostic(ErrorCode.ERR_CollectionExpressionNoTargetType, "[d]").WithLocation(8, 17)); } [Fact] public void PointerType_04() { string source = """ using System; class Program { unsafe static void Main() { void*[] a = [null, (void*)2]; foreach (void* p in a) Console.Write("{0}, ", (nint)p); } } """; CompileAndVerify(source, options: TestOptions.UnsafeReleaseExe, targetFramework: TargetFramework.Net80, verify: Verification.Skipped, expectedOutput: IncludeExpectedOutput("0, 2, ")); } [Fact] public void PointerType_05A() { string source = """ using System.Collections; using System.Collections.Generic; class C : IEnumerable { public void Add(object o) { } unsafe public void Add(void* p) { } IEnumerator IEnumerable.GetEnumerator() => null; } class Program { unsafe static void Main() { void* p = (void*)2; C c = [null, p]; c.Report(); } } """; var comp = CreateCompilation(new[] { source, s_collectionExtensions }, options: TestOptions.UnsafeReleaseExe, targetFramework: TargetFramework.Net80); comp.VerifyEmitDiagnostics( // 0.cs(14,22): error CS0029: Cannot implicitly convert type 'void*' to 'object' // C c = [null, p]; Diagnostic(ErrorCode.ERR_NoImplicitConv, "p").WithArguments("void*", "object").WithLocation(14, 22)); } [Fact] public void PointerType_05B() { string source = """ using System.Collections; using System.Collections.Generic; class C : IEnumerable { private List<nint> _list = new List<nint>(); unsafe public void Add(void* p) { _list.Add((nint)p); } public Enumerator GetEnumerator() => new Enumerator(_list); IEnumerator IEnumerable.GetEnumerator() => _list.GetEnumerator(); } class Enumerator { private readonly List<nint> _list; private int _index; public Enumerator(List<nint> list) { _list = list; _index = -1; } public bool MoveNext() { if (_index < _list.Count) _index++; return _index < _list.Count; } public unsafe void* Current => (void*)_list[_index]; } class Program { unsafe static void Main() { void* p = (void*)2; C c = [null, p]; c.Report(); } } """; CompileAndVerify(new[] { source, s_collectionExtensions }, options: TestOptions.UnsafeReleaseExe, targetFramework: TargetFramework.Net80, verify: Verification.Skipped, expectedOutput: IncludeExpectedOutput("[0, 2], ")); } [Fact] public void PointerType_06() { string source = """ using System; unsafe class Program { static void Main() { int*[] arr = [null]; Console.Write(arr.Length); Console.Write((nint)arr[0]); int*[] arr1 = [..arr]; Console.Write(arr1.Length); Console.Write((nint)arr1[0]); } } """; var verifier = CompileAndVerify(new[] { source, s_collectionExtensions }, options: TestOptions.UnsafeReleaseExe, targetFramework: TargetFramework.Net80, verify: Verification.Skipped, expectedOutput: IncludeExpectedOutput("1010")); verifier.VerifyIL("Program.Main", """ { // Code size 86 (0x56) .maxstack 4 .locals init (int V_0, int*[] V_1, int*[] V_2, int V_3, int* V_4) IL_0000: ldc.i4.1 IL_0001: newarr "int*" IL_0006: dup IL_0007: ldc.i4.0 IL_0008: ldc.i4.0 IL_0009: conv.u IL_000a: stelem.i IL_000b: dup IL_000c: ldlen IL_000d: conv.i4 IL_000e: call "void System.Console.Write(int)" IL_0013: dup IL_0014: ldc.i4.0 IL_0015: ldelem.i IL_0016: conv.i8 IL_0017: call "void System.Console.Write(long)" IL_001c: ldc.i4.0 IL_001d: stloc.0 IL_001e: dup IL_001f: ldlen IL_0020: conv.i4 IL_0021: newarr "int*" IL_0026: stloc.1 IL_0027: stloc.2 IL_0028: ldc.i4.0 IL_0029: stloc.3 IL_002a: br.s IL_003e IL_002c: ldloc.2 IL_002d: ldloc.3 IL_002e: ldelem.i IL_002f: stloc.s V_4 IL_0031: ldloc.1 IL_0032: ldloc.0 IL_0033: ldloc.s V_4 IL_0035: stelem.i IL_0036: ldloc.0 IL_0037: ldc.i4.1 IL_0038: add IL_0039: stloc.0 IL_003a: ldloc.3 IL_003b: ldc.i4.1 IL_003c: add IL_003d: stloc.3 IL_003e: ldloc.3 IL_003f: ldloc.2 IL_0040: ldlen IL_0041: conv.i4 IL_0042: blt.s IL_002c IL_0044: ldloc.1 IL_0045: dup IL_0046: ldlen IL_0047: conv.i4 IL_0048: call "void System.Console.Write(int)" IL_004d: ldc.i4.0 IL_004e: ldelem.i IL_004f: conv.i8 IL_0050: call "void System.Console.Write(long)" IL_0055: ret } """); } [Fact] public void PointerType_07() { string source = """ using System; unsafe class Program { static void Main() { int*[] arr = [null]; Console.Write(arr.Length); Console.Write((nint)arr[0]); int*[] arr1 = [..arr, ..arr]; Console.Write(arr1.Length); Console.Write((nint)arr1[0]); Console.Write((nint)arr1[1]); } } """; var verifier = CompileAndVerify(new[] { source, s_collectionExtensions }, options: TestOptions.UnsafeReleaseExe, targetFramework: TargetFramework.Net80, verify: Verification.Skipped, expectedOutput: IncludeExpectedOutput("10200")); verifier.VerifyIL("Program.Main", """ { // Code size 149 (0x95) .maxstack 4 .locals init (int*[] V_0, int*[] V_1, int V_2, int*[] V_3, int*[] V_4, int V_5, int* V_6) IL_0000: ldc.i4.1 IL_0001: newarr "int*" IL_0006: dup IL_0007: ldc.i4.0 IL_0008: ldc.i4.0 IL_0009: conv.u IL_000a: stelem.i IL_000b: dup IL_000c: ldlen IL_000d: conv.i4 IL_000e: call "void System.Console.Write(int)" IL_0013: dup IL_0014: ldc.i4.0 IL_0015: ldelem.i IL_0016: conv.i8 IL_0017: call "void System.Console.Write(long)" IL_001c: dup IL_001d: stloc.0 IL_001e: stloc.1 IL_001f: ldc.i4.0 IL_0020: stloc.2 IL_0021: ldloc.0 IL_0022: ldlen IL_0023: conv.i4 IL_0024: ldloc.1 IL_0025: ldlen IL_0026: conv.i4 IL_0027: add IL_0028: newarr "int*" IL_002d: stloc.3 IL_002e: ldloc.0 IL_002f: stloc.s V_4 IL_0031: ldc.i4.0 IL_0032: stloc.s V_5 IL_0034: br.s IL_004c IL_0036: ldloc.s V_4 IL_0038: ldloc.s V_5 IL_003a: ldelem.i IL_003b: stloc.s V_6 IL_003d: ldloc.3 IL_003e: ldloc.2 IL_003f: ldloc.s V_6 IL_0041: stelem.i IL_0042: ldloc.2 IL_0043: ldc.i4.1 IL_0044: add IL_0045: stloc.2 IL_0046: ldloc.s V_5 IL_0048: ldc.i4.1 IL_0049: add IL_004a: stloc.s V_5 IL_004c: ldloc.s V_5 IL_004e: ldloc.s V_4 IL_0050: ldlen IL_0051: conv.i4 IL_0052: blt.s IL_0036 IL_0054: ldloc.1 IL_0055: stloc.s V_4 IL_0057: ldc.i4.0 IL_0058: stloc.s V_5 IL_005a: br.s IL_0072 IL_005c: ldloc.s V_4 IL_005e: ldloc.s V_5 IL_0060: ldelem.i IL_0061: stloc.s V_6 IL_0063: ldloc.3 IL_0064: ldloc.2 IL_0065: ldloc.s V_6 IL_0067: stelem.i IL_0068: ldloc.2 IL_0069: ldc.i4.1 IL_006a: add IL_006b: stloc.2 IL_006c: ldloc.s V_5 IL_006e: ldc.i4.1 IL_006f: add IL_0070: stloc.s V_5 IL_0072: ldloc.s V_5 IL_0074: ldloc.s V_4 IL_0076: ldlen IL_0077: conv.i4 IL_0078: blt.s IL_005c IL_007a: ldloc.3 IL_007b: dup IL_007c: ldlen IL_007d: conv.i4 IL_007e: call "void System.Console.Write(int)" IL_0083: dup IL_0084: ldc.i4.0 IL_0085: ldelem.i IL_0086: conv.i8 IL_0087: call "void System.Console.Write(long)" IL_008c: ldc.i4.1 IL_008d: ldelem.i IL_008e: conv.i8 IL_008f: call "void System.Console.Write(long)" IL_0094: ret } """); } [Fact] public void CollectionInitializerType_01() { string source = """ using System.Collections.Generic; class Program { static List<int> Create1() => []; static List<object> Create2() => [1, 2]; static List<int> Create3() => [3, 4, 5]; static List<long?> Create4() => [null, 7]; static void Main() { Create1().Report(); Create2().Report(); Create3().Report(); Create4().Report(); } } """; var verifier = CompileAndVerify(new[] { source, s_collectionExtensions }, expectedOutput: "[], [1, 2], [3, 4, 5], [null, 7], "); verifier.VerifyIL("Program.Create1", """ { // Code size 6 (0x6) .maxstack 1 IL_0000: newobj "System.Collections.Generic.List<int>..ctor()" IL_0005: ret } """); verifier.VerifyIL("Program.Create2", """ { // Code size 31 (0x1f) .maxstack 3 IL_0000: ldc.i4.2 IL_0001: newobj "System.Collections.Generic.List<object>..ctor(int)" IL_0006: dup IL_0007: ldc.i4.1 IL_0008: box "int" IL_000d: callvirt "void System.Collections.Generic.List<object>.Add(object)" IL_0012: dup IL_0013: ldc.i4.2 IL_0014: box "int" IL_0019: callvirt "void System.Collections.Generic.List<object>.Add(object)" IL_001e: ret } """); verifier.VerifyIL("Program.Create3", """ { // Code size 28 (0x1c) .maxstack 3 IL_0000: ldc.i4.3 IL_0001: newobj "System.Collections.Generic.List<int>..ctor(int)" IL_0006: dup IL_0007: ldc.i4.3 IL_0008: callvirt "void System.Collections.Generic.List<int>.Add(int)" IL_000d: dup IL_000e: ldc.i4.4 IL_000f: callvirt "void System.Collections.Generic.List<int>.Add(int)" IL_0014: dup IL_0015: ldc.i4.5 IL_0016: callvirt "void System.Collections.Generic.List<int>.Add(int)" IL_001b: ret } """); verifier.VerifyIL("Program.Create4", """ { // Code size 35 (0x23) .maxstack 3 .locals init (long? V_0) IL_0000: ldc.i4.2 IL_0001: newobj "System.Collections.Generic.List<long?>..ctor(int)" IL_0006: dup IL_0007: ldloca.s V_0 IL_0009: initobj "long?" IL_000f: ldloc.0 IL_0010: callvirt "void System.Collections.Generic.List<long?>.Add(long?)" IL_0015: dup IL_0016: ldc.i4.7 IL_0017: conv.i8 IL_0018: newobj "long?..ctor(long)" IL_001d: callvirt "void System.Collections.Generic.List<long?>.Add(long?)" IL_0022: ret } """); } [Fact] public void CollectionInitializerType_02() { string source = """ S s; s = []; s = [1, 2]; s = [default]; s = [Unknown]; struct S { } """; var comp = CreateCompilation(source); comp.VerifyEmitDiagnostics( // (2,5): error CS9174: Cannot initialize type 'S' with a collection expression because the type is not constructible. // s = []; Diagnostic(ErrorCode.ERR_CollectionExpressionTargetTypeNotConstructible, "[]").WithArguments("S").WithLocation(2, 5), // (3,5): error CS9174: Cannot initialize type 'S' with a collection expression because the type is not constructible. // s = [1, 2]; Diagnostic(ErrorCode.ERR_CollectionExpressionTargetTypeNotConstructible, "[1, 2]").WithArguments("S").WithLocation(3, 5), // (4,5): error CS9174: Cannot initialize type 'S' with a collection expression because the type is not constructible. // s = [default]; Diagnostic(ErrorCode.ERR_CollectionExpressionTargetTypeNotConstructible, "[default]").WithArguments("S").WithLocation(4, 5), // (5,6): error CS0103: The name 'Unknown' does not exist in the current context // s = [Unknown]; Diagnostic(ErrorCode.ERR_NameNotInContext, "Unknown").WithArguments("Unknown").WithLocation(5, 6)); } [Fact] public void CollectionInitializerType_03() { string sourceA = """ using System.Collections; struct S : IEnumerable { IEnumerator IEnumerable.GetEnumerator() { yield break; } } """; string sourceB1 = """ S s; s = []; s.Report(); """; CompileAndVerify(new[] { sourceA, sourceB1, s_collectionExtensions }, expectedOutput: "[], "); string sourceB2 = """ S s; s = [1, 2]; s = [.. new object()]; """; var comp = CreateCompilation(new[] { sourceA, sourceB2 }); comp.VerifyEmitDiagnostics( // 1.cs(2,5): error CS1061: 'S' does not contain a definition for 'Add' and no accessible extension method 'Add' accepting a first argument of type 'S' could be found (are you missing a using directive or an assembly reference?) // s = [1, 2]; Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "[1, 2]").WithArguments("S", "Add").WithLocation(2, 5), // 1.cs(3,9): error CS9212: Spread operator '..' cannot operate on variables of type 'object' because 'object' does not contain a public instance or extension definition for 'GetEnumerator' // s = [.. new object()]; Diagnostic(ErrorCode.ERR_SpreadMissingMember, "new object()").WithArguments("object", "GetEnumerator").WithLocation(3, 9)); } [Fact] public void CollectionInitializerType_04() { string source = """ using System.Collections; C c; c = []; c = [1, 2]; class C : IEnumerable { C(object o) { } IEnumerator IEnumerable.GetEnumerator() => throw null; public void Add(int i) { } } """; var comp = CreateCompilation(source); comp.VerifyEmitDiagnostics( // (3,5): error CS1729: 'C' does not contain a constructor that takes 0 arguments // c = []; Diagnostic(ErrorCode.ERR_BadCtorArgCount, "[]").WithArguments("C", "0").WithLocation(3, 5), // (4,5): error CS1729: 'C' does not contain a constructor that takes 0 arguments // c = [1, 2]; Diagnostic(ErrorCode.ERR_BadCtorArgCount, "[1, 2]").WithArguments("C", "0").WithLocation(4, 5)); } [WorkItem("https://github.com/dotnet/roslyn/pull/71492")] [Fact] public void CollectionInitializerType_05() { string source = """ using System.Collections; using System.Collections.Generic; class A : IEnumerable<int> { A() { } public void Add(int i) { } IEnumerator<int> IEnumerable<int>.GetEnumerator() => null; IEnumerator IEnumerable.GetEnumerator() => null; static A Create1() => []; } class B { static A Create2() => [1, 2]; static object Create3() => (A)[]; } """; var comp = CreateCompilation(source); comp.VerifyEmitDiagnostics( // (13,27): error CS0122: 'A.A()' is inaccessible due to its protection level // static A Create2() => [1, 2]; Diagnostic(ErrorCode.ERR_BadAccess, "[1, 2]").WithArguments("A.A()").WithLocation(13, 27), // (14,35): error CS0122: 'A.A()' is inaccessible due to its protection level // static object Create3() => (A)[]; Diagnostic(ErrorCode.ERR_BadAccess, "[]").WithArguments("A.A()").WithLocation(14, 35)); } [Fact] public void CollectionInitializerType_06() { string sourceA = """ using System.Collections; using System.Collections.Generic; class C<T> : IEnumerable { private List<T> _list = new List<T>(); public void Add(T t) { _list.Add(t); } IEnumerator IEnumerable.GetEnumerator() => _list.GetEnumerator(); } """; string sourceB1 = """ class Program { static void Main() { C<int> c; object o; c = []; o = (C<object>)[]; c.Report(); o.Report(); } } """; CompileAndVerify(new[] { sourceA, sourceB1, s_collectionExtensions }, expectedOutput: "[], [], "); string sourceB2 = """ class Program { static void Main() { C<int> c; object o; c = [1, 2]; o = (C<object>)[3, 4]; c.Report(); o.Report(); } } """; var comp = CreateCompilation(new[] { sourceA, sourceB2 }); CompileAndVerify(new[] { sourceA, sourceB2, s_collectionExtensions }, expectedOutput: "[1, 2], [3, 4], "); } [Fact] public void CollectionInitializerType_ConstructorOptionalParameters() { string source = """ using System.Collections; using System.Collections.Generic; class C : IEnumerable<int> { private List<int> _list = new List<int>(); internal C(int x = 1, int y = 2) { } public void Add(int i) { _list.Add(i); } IEnumerator<int> IEnumerable<int>.GetEnumerator() => _list.GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => _list.GetEnumerator(); } class Program { static void Main() { C c; object o; c = []; o = (C)([]); c.Report(); o.Report(); c = [1, 2]; o = (C)([3, 4]); c.Report(); o.Report(); } } """; CompileAndVerify(new[] { source, s_collectionExtensions }, expectedOutput: "[], [], [1, 2], [3, 4], "); } [Fact] public void CollectionInitializerType_ConstructorParamsArray() { string source = """ using System.Collections; using System.Collections.Generic; class C : IEnumerable<int> { private List<int> _list = new List<int>(); internal C(params int[] args) { } public void Add(int i) { _list.Add(i); } IEnumerator<int> IEnumerable<int>.GetEnumerator() => _list.GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => _list.GetEnumerator(); } class Program { static void Main() { C c; object o; c = []; o = (C)([]); c.Report(); o.Report(); c = [1, 2]; o = (C)([3, 4]); c.Report(); o.Report(); } } """; CompileAndVerify(new[] { source, s_collectionExtensions }, expectedOutput: "[], [], [1, 2], [3, 4], "); } [Fact] public void CollectionInitializerType_07() { string source = """ using System.Collections; using System.Collections.Generic; abstract class A : IEnumerable<int> { public void Add(int i) { } IEnumerator<int> IEnumerable<int>.GetEnumerator() => null; IEnumerator IEnumerable.GetEnumerator() => null; } class B : A { } class Program { static void Main() { A a = []; B b = []; } } """; var comp = CreateCompilation(source); comp.VerifyEmitDiagnostics( // (14,15): error CS0144: Cannot create an instance of the abstract type or interface 'A' // A a = []; Diagnostic(ErrorCode.ERR_NoNewAbstract, "[]").WithArguments("A").WithLocation(14, 15)); } [WorkItem("https://github.com/dotnet/roslyn/pull/71492")] [Fact] public void CollectionInitializerType_08A() { string source = """ using System; using System.Collections; using System.Collections.Generic; struct S0<T> : IEnumerable { private List<T> _list; public void Add(T t) { GetList().Add(t); } IEnumerator IEnumerable.GetEnumerator() => GetList().GetEnumerator(); private List<T> GetList() => _list ??= new(); } class Program { static void Main() { object o = (S0<int>)[]; o.Report(); o = (S0<int>)[1, 2]; o.Report(); S0<int> s = []; s.Report(); s = [1, 2]; s.Report(); } } """; CompileAndVerify(new[] { source, s_collectionExtensions }, expectedOutput: "[], [1, 2], [], [1, 2], "); } [Fact] public void CollectionInitializerType_08B() { string source = """ using System; using System.Collections; using System.Collections.Generic; struct S1<T> : IEnumerable<T> { public void Add(T t) { } IEnumerator<T> IEnumerable<T>.GetEnumerator() => throw null; IEnumerator IEnumerable.GetEnumerator() => throw null; } struct S2<T> : IEnumerable<T> { public S2() { } public void Add(T t) { } IEnumerator<T> IEnumerable<T>.GetEnumerator() => throw null; IEnumerator IEnumerable.GetEnumerator() => throw null; } class Program { static void M1() { object o = (S1<int>)[]; S1<int> s = [1, 2]; } static void M2() { S2<int> s = []; object o = (S2<int>)[1, 2]; } } """; var verifier = CompileAndVerify(source); verifier.VerifyIL("Program.M1", """ { // Code size 35 (0x23) .maxstack 2 .locals init (S1<int> V_0) IL_0000: ldloca.s V_0 IL_0002: initobj "S1<int>" IL_0008: ldloc.0 IL_0009: pop IL_000a: ldloca.s V_0 IL_000c: initobj "S1<int>" IL_0012: ldloca.s V_0 IL_0014: ldc.i4.1 IL_0015: call "void S1<int>.Add(int)" IL_001a: ldloca.s V_0 IL_001c: ldc.i4.2 IL_001d: call "void S1<int>.Add(int)" IL_0022: ret } """); verifier.VerifyIL("Program.M2", """ { // Code size 30 (0x1e) .maxstack 2 .locals init (S2<int> V_0) IL_0000: newobj "S2<int>..ctor()" IL_0005: pop IL_0006: ldloca.s V_0 IL_0008: call "S2<int>..ctor()" IL_000d: ldloca.s V_0 IL_000f: ldc.i4.1 IL_0010: call "void S2<int>.Add(int)" IL_0015: ldloca.s V_0 IL_0017: ldc.i4.2 IL_0018: call "void S2<int>.Add(int)" IL_001d: ret } """); } [Fact] public void CollectionInitializerType_09() { string source = """ using System.Collections; using System.Collections.Generic; class Program { static void Main() { UnknownType u; u = []; u = [null, B]; } } """; var comp = CreateCompilation(source); comp.VerifyEmitDiagnostics( // (7,9): error CS0246: The type or namespace name 'UnknownType' could not be found (are you missing a using directive or an assembly reference?) // UnknownType u; Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "UnknownType").WithArguments("UnknownType").WithLocation(7, 9), // (9,20): error CS0103: The name 'B' does not exist in the current context // u = [null, B]; Diagnostic(ErrorCode.ERR_NameNotInContext, "B").WithArguments("B").WithLocation(9, 20)); } [Fact] public void CollectionInitializerType_10() { string source = """ using System.Collections; using System.Collections.Generic; struct S<T> : IEnumerable<string> { public void Add(string i) { } IEnumerator<string> IEnumerable<string>.GetEnumerator() => null; IEnumerator IEnumerable.GetEnumerator() => null; } class Program { static void Main() { S<UnknownType> s; s = []; s = [null, B]; } } """; var comp = CreateCompilation(source); comp.VerifyEmitDiagnostics( // (13,11): error CS0246: The type or namespace name 'UnknownType' could not be found (are you missing a using directive or an assembly reference?) // S<UnknownType> s; Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "UnknownType").WithArguments("UnknownType").WithLocation(13, 11), // (15,20): error CS0103: The name 'B' does not exist in the current context // s = [null, B]; Diagnostic(ErrorCode.ERR_NameNotInContext, "B").WithArguments("B").WithLocation(15, 20)); } [Fact] public void CollectionInitializerType_11() { string source = """ using System.Collections.Generic; class Program { static void Main() { List<List<int>> l; l = [[], [2, 3]]; l = [[], {2, 3}]; } } """; var comp = CreateCompilation(source); comp.VerifyEmitDiagnostics( // (8,18): error CS1003: Syntax error, ']' expected // l = [[], {2, 3}]; Diagnostic(ErrorCode.ERR_SyntaxError, "{").WithArguments("]").WithLocation(8, 18), // (8,18): error CS1002: ; expected // l = [[], {2, 3}]; Diagnostic(ErrorCode.ERR_SemicolonExpected, "{").WithLocation(8, 18), // (8,20): error CS1002: ; expected // l = [[], {2, 3}]; Diagnostic(ErrorCode.ERR_SemicolonExpected, ",").WithLocation(8, 20), // (8,20): error CS1513: } expected // l = [[], {2, 3}]; Diagnostic(ErrorCode.ERR_RbraceExpected, ",").WithLocation(8, 20), // (8,23): error CS1002: ; expected // l = [[], {2, 3}]; Diagnostic(ErrorCode.ERR_SemicolonExpected, "}").WithLocation(8, 23), // (8,24): error CS1513: } expected // l = [[], {2, 3}]; Diagnostic(ErrorCode.ERR_RbraceExpected, "]").WithLocation(8, 24)); } [Fact] public void CollectionInitializerType_12() { string source = """ using System.Collections; using System.Collections.Generic; class C : IEnumerable { List<string> _list = new List<string>(); public void Add(int i) { _list.Add($"i={i}"); } public void Add(object o) { _list.Add($"o={o}"); } IEnumerator IEnumerable.GetEnumerator() => _list.GetEnumerator(); } class Program { static void Main() { C x = []; C y = [1, (object)2]; x.Report(); y.Report(); } } """; CompileAndVerify(new[] { source, s_collectionExtensions }, expectedOutput: "[], [i=1, o=2], "); } [Fact] public void CollectionInitializerType_13A() { string source = """ using System.Collections; interface IA { } interface IB { } class AB : IA, IB { } class C : IEnumerable { public void Add(IA a) { } public void Add(IB b) { } IEnumerator IEnumerable.GetEnumerator() => null; } class Program { static void Main() { C c = [(IA)null, (IB)null, new AB()]; } } """; var comp = CreateCompilation(source); comp.VerifyEmitDiagnostics( // (15,36): error CS0121: The call is ambiguous between the following methods or properties: 'C.Add(IA)' and 'C.Add(IB)' // C c = [(IA)null, (IB)null, new AB()]; Diagnostic(ErrorCode.ERR_AmbigCall, "new AB()").WithArguments("C.Add(IA)", "C.Add(IB)").WithLocation(15, 36)); } [Fact] public void CollectionInitializerType_13B() { string source = """ using System.Collections; interface IA { } interface IB { } class AB : IA, IB { } class C : IEnumerable { IEnumerator IEnumerable.GetEnumerator() => null; } static class Extensions { public static void Add(this C c, IA a) { } public static void Add(this C c, IB b) { } } class Program { static void Main() { C c = [(IA)null, (IB)null, new AB()]; } } """; var comp = CreateCompilation(source); comp.VerifyEmitDiagnostics( // (18,36): error CS0121: The call is ambiguous between the following methods or properties: 'Extensions.Add(C, IA)' and 'Extensions.Add(C, IB)' // C c = [(IA)null, (IB)null, new AB()]; Diagnostic(ErrorCode.ERR_AmbigCall, "new AB()").WithArguments("Extensions.Add(C, IA)", "Extensions.Add(C, IB)").WithLocation(18, 36)); } [Fact] public void CollectionInitializerType_14A() { string source = """ using System.Collections; struct S<T> : IEnumerable { public void Add(T x, T y) { } IEnumerator IEnumerable.GetEnumerator() => throw null; } class Program { static void Main() { S<int> s; s = []; s = [1, ..s]; } } """; var comp = CreateCompilation(source); comp.VerifyEmitDiagnostics( // (13,13): error CS9215: Collection expression type 'S<int>' must have an instance or extension method 'Add' that can be called with a single argument. // s = [1, ..s]; Diagnostic(ErrorCode.ERR_CollectionExpressionMissingAdd, "[1, ..s]").WithArguments("S<int>").WithLocation(13, 13)); } [Fact] public void CollectionInitializerType_14B() { string source = """ using System.Collections; using System.Collections.Generic; struct S<T> : IEnumerable<T> { public void Add(T x, T y) { } IEnumerator<T> IEnumerable<T>.GetEnumerator() => throw null; IEnumerator IEnumerable.GetEnumerator() => throw null; } class Program { static void Main() { S<int> s; s = []; s = [1, ..s]; } } """; var comp = CreateCompilation(source); comp.VerifyEmitDiagnostics( // (15,13): error CS9215: Collection expression type 'S<int>' must have an instance or extension method 'Add' that can be called with a single argument. // s = [1, ..s]; Diagnostic(ErrorCode.ERR_CollectionExpressionMissingAdd, "[1, ..s]").WithArguments("S<int>").WithLocation(15, 13)); } [Fact] public void CollectionInitializerType_14C() { string source = """ using System.Collections; using System.Collections.Generic; struct S<T> : IEnumerable<T> { IEnumerator<T> IEnumerable<T>.GetEnumerator() => throw null; IEnumerator IEnumerable.GetEnumerator() => throw null; } static class Extensions { public static void Add<T>(this S<T> s, T x, T y) { } } class Program { static void Main() { S<int> s; s = []; s = [1, ..s]; } } """; var comp = CreateCompilation(source); comp.VerifyEmitDiagnostics( // (18,13): error CS9215: Collection expression type 'S<int>' must have an instance or extension method 'Add' that can be called with a single argument. // s = [1, ..s]; Diagnostic(ErrorCode.ERR_CollectionExpressionMissingAdd, "[1, ..s]").WithArguments("S<int>").WithLocation(18, 13)); } [Fact] public void CollectionInitializerType_15A() { string source = """ using System.Collections; using System.Collections.Generic; class C<T> : IEnumerable { List<T> _list = new List<T>(); public void Add(T t, int index = -1) { _list.Add(t); } IEnumerator IEnumerable.GetEnumerator() => _list.GetEnumerator(); } class Program { static void Main() { C<int> c = []; c.Report(); c = [1, 2]; c.Report(); } } """; CompileAndVerify(new[] { source, s_collectionExtensions }, expectedOutput: "[], [1, 2], "); } [Fact] public void CollectionInitializerType_15B() { string source = """ using System.Collections; using System.Collections.Generic; class C<T> : IEnumerable<T> { List<T> _list = new List<T>(); public void Add(T t, int index = -1) { _list.Add(t); } IEnumerator<T> IEnumerable<T>.GetEnumerator() => _list.GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => _list.GetEnumerator(); } class Program { static void Main() { C<int> c = []; c.Report(); c = [1, 2]; c.Report(); } } """; CompileAndVerify(new[] { source, s_collectionExtensions }, expectedOutput: "[], [1, 2], "); } [Fact] public void CollectionInitializerType_16A() { string source = """ using System.Collections; using System.Collections.Generic; class C<T> : IEnumerable { List<T> _list = new List<T>(); public void Add(T t, params T[] args) { _list.Add(t); } IEnumerator IEnumerable.GetEnumerator() => _list.GetEnumerator(); } class Program { static void Main() { C<int> c = []; c.Report(); c = [1, 2]; c.Report(); } } """; CompileAndVerify(new[] { source, s_collectionExtensions }, expectedOutput: "[], [1, 2], "); } [Fact] public void CollectionInitializerType_16B() { string source = """ using System.Collections; using System.Collections.Generic; class C<T> : IEnumerable<T> { List<T> _list = new List<T>(); public void Add(T t, params T[] args) { _list.Add(t); } IEnumerator<T> IEnumerable<T>.GetEnumerator() => _list.GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => _list.GetEnumerator(); } class Program { static void Main() { C<int> c = []; c.Report(); c = [1, 2]; c.Report(); } } """; CompileAndVerify(new[] { source, s_collectionExtensions }, expectedOutput: "[], [1, 2], "); } [Fact] public void CollectionInitializerType_17() { string sourceA = """ using System.Collections; using System.Collections.Generic; class C<T> : IEnumerable<T> { List<T> _list = new List<T>(); public void Add(params T[] args) { _list.AddRange(args); } IEnumerator<T> IEnumerable<T>.GetEnumerator() => _list.GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => _list.GetEnumerator(); } """; string sourceB1 = """ class Program { static void Main() { C<int> c = [[], [1, 2]]; c.Report(); } } """; var comp = CreateCompilation(new[] { sourceA, sourceB1, s_collectionExtensions }); comp.VerifyEmitDiagnostics( // 1.cs(5,21): error CS9174: Cannot initialize type 'int' with a collection expression because the type is not constructible. // C<int> c = [[], [1, 2]]; Diagnostic(ErrorCode.ERR_CollectionExpressionTargetTypeNotConstructible, "[]").WithArguments("int").WithLocation(5, 21), // 1.cs(5,25): error CS9174: Cannot initialize type 'int' with a collection expression because the type is not constructible. // C<int> c = [[], [1, 2]]; Diagnostic(ErrorCode.ERR_CollectionExpressionTargetTypeNotConstructible, "[1, 2]").WithArguments("int").WithLocation(5, 25)); string sourceB2 = """ class Program { static void Main() { C<int> c = [3]; c.Report(); } } """; var verifier = CompileAndVerify(new[] { sourceA, sourceB2, s_collectionExtensions }, expectedOutput: "[3], "); verifier.VerifyIL("Program.Main", """ { // Code size 30 (0x1e) .maxstack 5 .locals init (C<int> V_0) IL_0000: newobj "C<int>..ctor()" IL_0005: stloc.0 IL_0006: ldloc.0 IL_0007: ldc.i4.1 IL_0008: newarr "int" IL_000d: dup IL_000e: ldc.i4.0 IL_000f: ldc.i4.3 IL_0010: stelem.i4 IL_0011: callvirt "void C<int>.Add(params int[])" IL_0016: ldloc.0 IL_0017: ldc.i4.0 IL_0018: call "void CollectionExtensions.Report(object, bool)" IL_001d: ret } """); } [Fact] public void CollectionInitializerType_18A() { string source = """ using System.Collections; class S<T, U> : IEnumerable { internal void Add(T t) { } private void Add(U u) { } IEnumerator IEnumerable.GetEnumerator() => throw null; static S<T, U> Create(T t, U u) => [t, u]; } class Program { static S<T, U> Create<T, U>(T x, U y) => [x, y]; } """; var comp = CreateCompilation(source); comp.VerifyEmitDiagnostics( // (11,50): error CS1950: The best overloaded Add method 'S<T, U>.Add(T)' for the collection initializer has some invalid arguments // static S<T, U> Create<T, U>(T x, U y) => [x, y]; Diagnostic(ErrorCode.ERR_BadArgTypesForCollectionAdd, "y").WithArguments("S<T, U>.Add(T)").WithLocation(11, 50), // (11,50): error CS1503: Argument 1: cannot convert from 'U' to 'T' // static S<T, U> Create<T, U>(T x, U y) => [x, y]; Diagnostic(ErrorCode.ERR_BadArgType, "y").WithArguments("1", "U", "T").WithLocation(11, 50)); } [Fact] public void CollectionInitializerType_18B() { string source = """ using System.Collections; using System.Collections.Generic; class S<T, U> : IEnumerable<T> { internal void Add(T t) { } private void Add(U u) { } IEnumerator<T> IEnumerable<T>.GetEnumerator() => throw null; IEnumerator IEnumerable.GetEnumerator() => throw null; static S<T, U> Create(T t, U u) => [t, u]; } class Program { static S<T, U> Create<T, U>(T x, U y) => [x, y]; } """; var comp = CreateCompilation(source); comp.VerifyEmitDiagnostics( // (9,44): error CS0029: Cannot implicitly convert type 'U' to 'T' // static S<T, U> Create(T t, U u) => [t, u]; Diagnostic(ErrorCode.ERR_NoImplicitConv, "u").WithArguments("U", "T").WithLocation(9, 44), // (13,50): error CS0029: Cannot implicitly convert type 'U' to 'T' // static S<T, U> Create<T, U>(T x, U y) => [x, y]; Diagnostic(ErrorCode.ERR_NoImplicitConv, "y").WithArguments("U", "T").WithLocation(13, 50)); } [Fact] public void CollectionInitializerType_18C() { string source = """ using System.Collections; using System.Collections.Generic; class S<T, U> : IEnumerable<U> { internal void Add(T t) { } private void Add(U u) { } IEnumerator<U> IEnumerable<U>.GetEnumerator() => throw null; IEnumerator IEnumerable.GetEnumerator() => throw null; static S<T, U> Create(T t, U u) => [t, u]; } class Program { static S<T, U> Create<T, U>(T x, U y) => [x, y]; } """; var comp = CreateCompilation(source); comp.VerifyEmitDiagnostics( // (9,41): error CS0029: Cannot implicitly convert type 'T' to 'U' // static S<T, U> Create(T t, U u) => [t, u]; Diagnostic(ErrorCode.ERR_NoImplicitConv, "t").WithArguments("T", "U").WithLocation(9, 41), // (13,47): error CS0029: Cannot implicitly convert type 'T' to 'U' // static S<T, U> Create<T, U>(T x, U y) => [x, y]; Diagnostic(ErrorCode.ERR_NoImplicitConv, "x").WithArguments("T", "U").WithLocation(13, 47)); } [Fact] public void CollectionInitializerType_18D() { string source = """ using System.Collections; using System.Collections.Generic; abstract class A<T> : IEnumerable<T> { IEnumerator<T> IEnumerable<T>.GetEnumerator() => throw null; IEnumerator IEnumerable.GetEnumerator() => throw null; } class B<T, U> : A<T>, IEnumerable<U> { internal void Add(T t) { } private void Add(U u) { } IEnumerator<U> IEnumerable<U>.GetEnumerator() => throw null; static B<T, U> Create(T t, U u) => [t, u]; } class Program { static B<T, U> Create<T, U>(T x, U y) => [x, y]; } """; var comp = CreateCompilation(source); comp.VerifyEmitDiagnostics( // (13,40): error CS9213: Collection expression target 'B<T, U>' has no element type. // static B<T, U> Create(T t, U u) => [t, u]; Diagnostic(ErrorCode.ERR_CollectionExpressionTargetNoElementType, "[t, u]").WithArguments("B<T, U>").WithLocation(13, 40), // (17,46): error CS9213: Collection expression target 'B<T, U>' has no element type. // static B<T, U> Create<T, U>(T x, U y) => [x, y]; Diagnostic(ErrorCode.ERR_CollectionExpressionTargetNoElementType, "[x, y]").WithArguments("B<T, U>").WithLocation(17, 46)); } [Fact] public void CollectionInitializerType_19A() { string source = """ class Program { static void Main() { string s; s = []; s = ['a']; } } """; var comp = CreateCompilation(source); comp.VerifyEmitDiagnostics( // (6,13): error CS1729: 'string' does not contain a constructor that takes 0 arguments // s = []; Diagnostic(ErrorCode.ERR_BadCtorArgCount, "[]").WithArguments("string", "0").WithLocation(6, 13), // (7,13): error CS1729: 'string' does not contain a constructor that takes 0 arguments // s = ['a']; Diagnostic(ErrorCode.ERR_BadCtorArgCount, "['a']").WithArguments("string", "0").WithLocation(7, 13), // (7,13): error CS1061: 'string' does not contain a definition for 'Add' and no accessible extension method 'Add' accepting a first argument of type 'string' could be found (are you missing a using directive or an assembly reference?) // s = ['a']; Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "['a']").WithArguments("string", "Add").WithLocation(7, 13)); } [Fact] public void CollectionInitializerType_19B() { string source = """ class Program { static void Main() { string s; s = []; s = ['a']; } } static class E { internal static void Add(this string s, char c) { } } """; var comp = CreateCompilation(source); comp.VerifyEmitDiagnostics( // (6,13): error CS1729: 'string' does not contain a constructor that takes 0 arguments // s = []; Diagnostic(ErrorCode.ERR_BadCtorArgCount, "[]").WithArguments("string", "0").WithLocation(6, 13), // (7,13): error CS1729: 'string' does not contain a constructor that takes 0 arguments // s = ['a']; Diagnostic(ErrorCode.ERR_BadCtorArgCount, "['a']").WithArguments("string", "0").WithLocation(7, 13)); } [Fact] public void CollectionInitializerType_20() { string source = """ using System.Collections; using System.Collections.Generic; class MyCollection : IEnumerable<object> { private List<object> _list = new List<object>(); public void Add(int? i) { _list.Add(i); } public void Add(object o) { _list.Add(o); } IEnumerator<object> IEnumerable<object>.GetEnumerator() => _list.GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => _list.GetEnumerator(); } class Program { static void Main() { Create("1", [2], [3.0]).Report(); } static MyCollection Create(string x, int[] y, double[] z) { return /*<bind>*/[x, ..y, ..z]/*</bind>*/; } } """; var comp = CreateCompilation(new[] { source, s_collectionExtensions }, options: TestOptions.ReleaseExe); CompileAndVerify(comp, expectedOutput: "[1, 2, 3], "); VerifyOperationTreeForTest<CollectionExpressionSyntax>(comp, """ ICollectionExpressionOperation (3 elements, ConstructMethod: MyCollection..ctor()) (OperationKind.CollectionExpression, Type: MyCollection) (Syntax: '[x, ..y, ..z]') Elements(3): IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Object, IsImplicit) (Syntax: 'x') Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: True, IsUserDefined: False) (MethodSymbol: null) Operand: IParameterReferenceOperation: x (OperationKind.ParameterReference, Type: System.String) (Syntax: 'x') ISpreadOperation (ElementType: System.Int32) (OperationKind.Spread, Type: null) (Syntax: '..y') Operand: IParameterReferenceOperation: y (OperationKind.ParameterReference, Type: System.Int32[]) (Syntax: 'y') ElementConversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) (ImplicitNullable) ISpreadOperation (ElementType: System.Double) (OperationKind.Spread, Type: null) (Syntax: '..z') Operand: IParameterReferenceOperation: z (OperationKind.ParameterReference, Type: System.Double[]) (Syntax: 'z') ElementConversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) (Boxing) """); var tree = comp.SyntaxTrees[0]; var method = tree.GetRoot().DescendantNodes().OfType<MethodDeclarationSyntax>().Single(m => m.Identifier.Text == "Create"); VerifyFlowGraph(comp, method, """ Block[B0] - Entry Statements (0) Next (Regular) Block[B1] Block[B1] - Block Predecessors: [B0] Statements (0) Next (Return) Block[B2] IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: MyCollection, IsImplicit) (Syntax: '[x, ..y, ..z]') Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) (CollectionExpression) Operand: ICollectionExpressionOperation (3 elements, ConstructMethod: MyCollection..ctor()) (OperationKind.CollectionExpression, Type: MyCollection) (Syntax: '[x, ..y, ..z]') Elements(3): IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Object, IsImplicit) (Syntax: 'x') Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: True, IsUserDefined: False) (MethodSymbol: null) (ImplicitReference) Operand: IParameterReferenceOperation: x (OperationKind.ParameterReference, Type: System.String) (Syntax: 'x') ISpreadOperation (ElementType: System.Int32) (OperationKind.Spread, Type: null) (Syntax: '..y') Operand: IParameterReferenceOperation: y (OperationKind.ParameterReference, Type: System.Int32[]) (Syntax: 'y') ElementConversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) (ImplicitNullable) ISpreadOperation (ElementType: System.Double) (OperationKind.Spread, Type: null) (Syntax: '..z') Operand: IParameterReferenceOperation: z (OperationKind.ParameterReference, Type: System.Double[]) (Syntax: 'z') ElementConversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) (Boxing) Block[B2] - Exit Predecessors: [B1] Statements (0) """); } [Fact] public void CollectionInitializerType_InaccessibleConstructor() { string source = """ using System.Collections; using System.Collections.Generic; class MyCollection<T> : IEnumerable<T> { private MyCollection() { } public void Add(T t) { } IEnumerator<T> IEnumerable<T>.GetEnumerator() => throw null; IEnumerator IEnumerable.GetEnumerator() => throw null; static MyCollection<T> _x = []; static MyCollection<T> _y = [default, .._x]; } class Program { static void Main() { MyCollection<int> x = []; MyCollection<int> y = [1, ..x]; } } """; var comp = CreateCompilation(source); comp.VerifyEmitDiagnostics( // (16,31): error CS0122: 'MyCollection<int>.MyCollection()' is inaccessible due to its protection level // MyCollection<int> x = []; Diagnostic(ErrorCode.ERR_BadAccess, "[]").WithArguments("MyCollection<int>.MyCollection()").WithLocation(16, 31), // (17,31): error CS0122: 'MyCollection<int>.MyCollection()' is inaccessible due to its protection level // MyCollection<int> y = [1, ..x]; Diagnostic(ErrorCode.ERR_BadAccess, "[1, ..x]").WithArguments("MyCollection<int>.MyCollection()").WithLocation(17, 31)); } [Fact] public void CollectionInitializerType_InaccessibleAdd() { string source = """ using System.Collections; using System.Collections.Generic; class MyCollection<T> : IEnumerable<T> { private void Add(T t) { } IEnumerator<T> IEnumerable<T>.GetEnumerator() => throw null; IEnumerator IEnumerable.GetEnumerator() => throw null; static MyCollection<T> _x = []; static MyCollection<T> _y = [default, .._x]; } class Program { static void Main() { MyCollection<int> x = []; MyCollection<int> y = [1, ..x]; } } """; var comp = CreateCompilation(source); comp.VerifyEmitDiagnostics( // (16,31): error CS0122: 'MyCollection<int>.Add(int)' is inaccessible due to its protection level // MyCollection<int> y = [1, ..x]; Diagnostic(ErrorCode.ERR_BadAccess, "[1, ..x]").WithArguments("MyCollection<int>.Add(int)").WithLocation(16, 31)); } [Fact] public void CollectionInitializerType_InaccessibleExtensionAdd() { string sourceA = """ using System.Collections; using System.Collections.Generic; public class MyCollection<T> : IEnumerable<T> { IEnumerator<T> IEnumerable<T>.GetEnumerator() => throw null; IEnumerator IEnumerable.GetEnumerator() => throw null; static MyCollection<T> _x = []; static MyCollection<T> _y = [default, .._x]; } public static class Extensions { internal static void Add<T>(this MyCollection<T> c, T t) { } } """; var comp = CreateCompilation(sourceA); var refA = comp.ToMetadataReference(); string sourceB = """ class Program { static void Main() { MyCollection<int> x = []; MyCollection<int> y = [1, ..x]; } } """; comp = CreateCompilation(sourceB, references: new[] { refA }); comp.VerifyEmitDiagnostics( // (6,31): error CS1061: 'MyCollection<int>' does not contain a definition for 'Add' and no accessible extension method 'Add' accepting a first argument of type 'MyCollection<int>' could be found (are you missing a using directive or an assembly reference?) // MyCollection<int> y = [1, ..x]; Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "[1, ..x]").WithArguments("MyCollection<int>", "Add").WithLocation(6, 31)); } [Fact] public void CollectionInitializerType_AddByValue() { string source = """ using System.Collections; using System.Collections.Generic; struct MyCollection<T> : IEnumerable { private List<T> _list = new(); public MyCollection() { } public void Add(T t) { _list.Add(t); } public IEnumerator<T> GetEnumerator() => _list.GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => _list.GetEnumerator(); } class Program { static void Main() { MyCollection<object> x = new() { 1 }; MyCollection<int> y = []; MyCollection<object> z = [..x, ..y, 3]; foreach (var i in z) System.Console.Write("{0}, ", i); } } """; CompileAndVerify(source, expectedOutput: "1, 3, "); } [Fact] public void CollectionInitializerType_AddByRef_Out() { string source = """ using System.Collections; using System.Collections.Generic; class MyCollection<T> : IEnumerable { public void Add(out T t) => throw null; public IEnumerator<T> GetEnumerator() => null; IEnumerator IEnumerable.GetEnumerator() => null; } class Program { static void Main() { MyCollection<object> x = new() { 1 }; MyCollection<int> y = []; MyCollection<object> z = [..x, ..y, 3]; } } """; var comp = CreateCompilation(source); comp.VerifyEmitDiagnostics( // (13,42): error CS1954: The best overloaded method match 'MyCollection<object>.Add(out object)' for the collection initializer element cannot be used. Collection initializer 'Add' methods cannot have ref or out parameters. // MyCollection<object> x = new() { 1 }; Diagnostic(ErrorCode.ERR_InitializerAddHasParamModifiers, "1").WithArguments("MyCollection<object>.Add(out object)").WithLocation(13, 42), // (15,34): error CS1954: The best overloaded method match 'MyCollection<object>.Add(out object)' for the collection initializer element cannot be used. Collection initializer 'Add' methods cannot have ref or out parameters. // MyCollection<object> z = [..x, ..y, 3]; Diagnostic(ErrorCode.ERR_InitializerAddHasParamModifiers, "[..x, ..y, 3]").WithArguments("MyCollection<object>.Add(out object)").WithLocation(15, 34)); } [Fact] public void CollectionInitializerType_AddByRef_Ref() { string source = """ using System.Collections; using System.Collections.Generic; class MyCollection<T> : IEnumerable { public void Add(ref T t) { } public IEnumerator<T> GetEnumerator() => null; IEnumerator IEnumerable.GetEnumerator() => null; } class Program { static void Main() { MyCollection<object> x = new() { 1 }; MyCollection<int> y = []; MyCollection<object> z = [..x, ..y, 3]; } } """; var comp = CreateCompilation(source); comp.VerifyEmitDiagnostics( // (13,42): error CS1954: The best overloaded method match 'MyCollection<object>.Add(ref object)' for the collection initializer element cannot be used. Collection initializer 'Add' methods cannot have ref or out parameters. // MyCollection<object> x = new() { 1 }; Diagnostic(ErrorCode.ERR_InitializerAddHasParamModifiers, "1").WithArguments("MyCollection<object>.Add(ref object)").WithLocation(13, 42), // (15,34): error CS1954: The best overloaded method match 'MyCollection<object>.Add(ref object)' for the collection initializer element cannot be used. Collection initializer 'Add' methods cannot have ref or out parameters. // MyCollection<object> z = [..x, ..y, 3]; Diagnostic(ErrorCode.ERR_InitializerAddHasParamModifiers, "[..x, ..y, 3]").WithArguments("MyCollection<object>.Add(ref object)").WithLocation(15, 34)); } [Fact] public void CollectionInitializerType_AddByRef_In() { string source = """ using System.Collections; using System.Collections.Generic; class MyCollection<T> : IEnumerable { private List<T> _list = new(); public void Add(in T t) { _list.Add(t); } public IEnumerator<T> GetEnumerator() => _list.GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => _list.GetEnumerator(); } class Program { static void Main() { MyCollection<object> x = new() { 1 }; MyCollection<int> y = []; MyCollection<object> z = [..x, ..y, 3]; foreach (var i in z) System.Console.Write("{0}, ", i); } } """; CompileAndVerify(source, expectedOutput: "1, 3, "); } [Fact] public void CollectionInitializerType_AddByRef_RefReadonly() { string source = """ using System.Collections; using System.Collections.Generic; class MyCollection<T> : IEnumerable { private List<T> _list = new(); public void Add(ref readonly T t) { _list.Add(t); } public IEnumerator<T> GetEnumerator() => _list.GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => _list.GetEnumerator(); } class Program { static void Main() { MyCollection<object> x = new() { 1 }; MyCollection<int> y = []; int v = 4; MyCollection<object> z = [..x, ..y, 3, v]; foreach (var i in z) System.Console.Write("{0}, ", i); } } """; var comp = CreateCompilation(source, options: TestOptions.ReleaseExe); comp.VerifyEmitDiagnostics( // (14,42): warning CS9193: Argument 1 should be a variable because it is passed to a 'ref readonly' parameter // MyCollection<object> x = new() { 1 }; Diagnostic(ErrorCode.WRN_RefReadonlyNotVariable, "1").WithArguments("1").WithLocation(14, 42), // (17,35): warning CS9193: Argument 1 should be a variable because it is passed to a 'ref readonly' parameter // MyCollection<object> z = [..x, ..y, 3, v]; Diagnostic(ErrorCode.WRN_RefReadonlyNotVariable, "..x").WithArguments("1").WithLocation(17, 35), // (17,40): warning CS9193: Argument 1 should be a variable because it is passed to a 'ref readonly' parameter // MyCollection<object> z = [..x, ..y, 3, v]; Diagnostic(ErrorCode.WRN_RefReadonlyNotVariable, "..y").WithArguments("1").WithLocation(17, 40), // (17,45): warning CS9193: Argument 1 should be a variable because it is passed to a 'ref readonly' parameter // MyCollection<object> z = [..x, ..y, 3, v]; Diagnostic(ErrorCode.WRN_RefReadonlyNotVariable, "3").WithArguments("1").WithLocation(17, 45)); CompileAndVerify(comp, expectedOutput: "1, 3, 4, "); } [Fact] public void CollectionInitializerType_AddByValue_Extension() { string source = """ using System.Collections; using System.Collections.Generic; struct MyCollection<T> : IEnumerable { private List<T> _list = new(); public MyCollection() { } internal void AddValue(T t) { _list.Add(t); } public IEnumerator<T> GetEnumerator() => _list.GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => _list.GetEnumerator(); } static class Extensions { public static void Add<T>(this ref MyCollection<T> c, T t) { c.AddValue(t); } } class Program { static void Main() { MyCollection<object> x = new() { 1 }; MyCollection<int> y = []; MyCollection<object> z = [..x, ..y, 3]; foreach (var i in z) System.Console.Write("{0}, ", i); } } """; CompileAndVerify(source, expectedOutput: "1, 3, "); } [Fact] public void CollectionInitializerType_AddByRef_Out_Extension() { string source = """ using System.Collections; using System.Collections.Generic; struct MyCollection<T> : IEnumerable { public IEnumerator<T> GetEnumerator() => null; IEnumerator IEnumerable.GetEnumerator() => null; } static class Extensions { public static void Add<T>(this ref MyCollection<T> c, out T t) => throw null; } class Program { static void Main() { MyCollection<object> x = new() { 1 }; MyCollection<int> y = []; MyCollection<object> z = [..x, ..y, 3]; } } """; var comp = CreateCompilation(source); comp.VerifyEmitDiagnostics( // (16,42): error CS0411: The type arguments for method 'Extensions.Add<T>(ref MyCollection<T>, out T)' cannot be inferred from the usage. Try specifying the type arguments explicitly. // MyCollection<object> x = new() { 1 }; Diagnostic(ErrorCode.ERR_CantInferMethTypeArgs, "1").WithArguments("Extensions.Add<T>(ref MyCollection<T>, out T)").WithLocation(16, 42), // (18,34): error CS1954: The best overloaded method match 'Extensions.Add<T>(ref MyCollection<T>, out T)' for the collection initializer element cannot be used. Collection initializer 'Add' methods cannot have ref or out parameters. // MyCollection<object> z = [..x, ..y, 3]; Diagnostic(ErrorCode.ERR_InitializerAddHasParamModifiers, "[..x, ..y, 3]").WithArguments("Extensions.Add<T>(ref MyCollection<T>, out T)").WithLocation(18, 34)); } [Fact] public void CollectionInitializerType_AddByRef_Ref_Extension() { string source = """ using System.Collections; using System.Collections.Generic; struct MyCollection<T> : IEnumerable { public IEnumerator<T> GetEnumerator() => null; IEnumerator IEnumerable.GetEnumerator() => null; } static class Extensions { public static void Add<T>(this ref MyCollection<T> c, ref T t) => throw null; } class Program { static void Main() { MyCollection<object> x = new() { 1 }; MyCollection<int> y = []; MyCollection<object> z = [..x, ..y, 3]; } } """; var comp = CreateCompilation(source); comp.VerifyEmitDiagnostics( // (16,42): error CS0411: The type arguments for method 'Extensions.Add<T>(ref MyCollection<T>, ref T)' cannot be inferred from the usage. Try specifying the type arguments explicitly. // MyCollection<object> x = new() { 1 }; Diagnostic(ErrorCode.ERR_CantInferMethTypeArgs, "1").WithArguments("Extensions.Add<T>(ref MyCollection<T>, ref T)").WithLocation(16, 42), // (18,34): error CS1954: The best overloaded method match 'Extensions.Add<T>(ref MyCollection<T>, ref T)' for the collection initializer element cannot be used. Collection initializer 'Add' methods cannot have ref or out parameters. // MyCollection<object> z = [..x, ..y, 3]; Diagnostic(ErrorCode.ERR_InitializerAddHasParamModifiers, "[..x, ..y, 3]").WithArguments("Extensions.Add<T>(ref MyCollection<T>, ref T)").WithLocation(18, 34)); } [Fact] public void CollectionInitializerType_AddByRef_In_Extension() { string source = """ using System.Collections; using System.Collections.Generic; struct MyCollection<T> : IEnumerable { private List<T> _list = new(); public MyCollection() { } internal void AddValue(T t) { _list.Add(t); } public IEnumerator<T> GetEnumerator() => _list.GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => _list.GetEnumerator(); } static class Extensions { public static void Add<T>(this ref MyCollection<T> c, in T t) { c.AddValue(t); } } class Program { static void Main() { MyCollection<object> x = new() { 1 }; MyCollection<int> y = []; MyCollection<object> z = [..x, ..y, 3]; foreach (var i in z) System.Console.Write("{0}, ", i); } } """; CompileAndVerify(source, expectedOutput: "1, 3, "); } [Fact] public void CollectionInitializerType_AddByRef_RefReadonly_Extension() { string source = """ using System.Collections; using System.Collections.Generic; struct MyCollection<T> : IEnumerable { private List<T> _list = new(); public MyCollection() { } internal void AddValue(T t) { _list.Add(t); } public IEnumerator<T> GetEnumerator() => _list.GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => _list.GetEnumerator(); } static class Extensions { public static void Add<T>(this ref MyCollection<T> c, ref readonly T t) { c.AddValue(t); } } class Program { static void Main() { MyCollection<object> x = new() { 1 }; MyCollection<int> y = []; MyCollection<object> z = [..x, ..y, 3]; foreach (var i in z) System.Console.Write("{0}, ", i); } } """; CompileAndVerify(source, expectedOutput: "1, 3, "); } [Fact] public void CollectionInitializerType_InvalidParameterByValue_Extension() { string source = """ using System.Collections; using System.Collections.Generic; struct MyCollection<T> : IEnumerable { public IEnumerator<T> GetEnumerator() => throw null; IEnumerator IEnumerable.GetEnumerator() => throw null; } static class Extensions { public static void Add<T>(this ref MyCollection<T> c, string s) { } } class Program { static void Main() { MyCollection<string> x; x = new() { "1" }; x = ["2"]; MyCollection<int> y; y = new() { 3 }; y = [4]; } } """; var comp = CreateCompilation(source); comp.VerifyEmitDiagnostics( // (20,21): error CS1950: The best overloaded Add method 'Extensions.Add<int>(ref MyCollection<int>, string)' for the collection initializer has some invalid arguments // y = new() { 3 }; Diagnostic(ErrorCode.ERR_BadArgTypesForCollectionAdd, "3").WithArguments("Extensions.Add<int>(ref MyCollection<int>, string)").WithLocation(20, 21), // (20,21): error CS1503: Argument 2: cannot convert from 'int' to 'string' // y = new() { 3 }; Diagnostic(ErrorCode.ERR_BadArgType, "3").WithArguments("2", "int", "string").WithLocation(20, 21), // (21,14): error CS1950: The best overloaded Add method 'Extensions.Add<int>(ref MyCollection<int>, string)' for the collection initializer has some invalid arguments // y = [4]; Diagnostic(ErrorCode.ERR_BadArgTypesForCollectionAdd, "4").WithArguments("Extensions.Add<int>(ref MyCollection<int>, string)").WithLocation(21, 14), // (21,14): error CS1503: Argument 2: cannot convert from 'int' to 'string' // y = [4]; Diagnostic(ErrorCode.ERR_BadArgType, "4").WithArguments("2", "int", "string").WithLocation(21, 14)); } [Fact] public void CollectionInitializerType_InvalidParameterByRef_Extension() { string source = """ using System.Collections; using System.Collections.Generic; struct MyCollection<T> : IEnumerable { public IEnumerator<T> GetEnumerator() => throw null; IEnumerator IEnumerable.GetEnumerator() => throw null; } static class Extensions { public static void Add<T>(this ref MyCollection<T> c, ref string s) { } } class Program { static void Main() { MyCollection<string> x; x = new() { "1" }; x = ["2"]; MyCollection<int> y; y = new() { 3 }; y = [4]; } } """; var comp = CreateCompilation(source); comp.VerifyEmitDiagnostics( // (17,21): error CS1954: The best overloaded method match 'Extensions.Add<string>(ref MyCollection<string>, ref string)' for the collection initializer element cannot be used. Collection initializer 'Add' methods cannot have ref or out parameters. // x = new() { "1" }; Diagnostic(ErrorCode.ERR_InitializerAddHasParamModifiers, @"""1""").WithArguments("Extensions.Add<string>(ref MyCollection<string>, ref string)").WithLocation(17, 21), // (18,13): error CS1954: The best overloaded method match 'Extensions.Add<T>(ref MyCollection<T>, ref string)' for the collection initializer element cannot be used. Collection initializer 'Add' methods cannot have ref or out parameters. // x = ["2"]; Diagnostic(ErrorCode.ERR_InitializerAddHasParamModifiers, @"[""2""]").WithArguments("Extensions.Add<T>(ref MyCollection<T>, ref string)").WithLocation(18, 13), // (20,21): error CS1954: The best overloaded method match 'Extensions.Add<int>(ref MyCollection<int>, ref string)' for the collection initializer element cannot be used. Collection initializer 'Add' methods cannot have ref or out parameters. // y = new() { 3 }; Diagnostic(ErrorCode.ERR_InitializerAddHasParamModifiers, "3").WithArguments("Extensions.Add<int>(ref MyCollection<int>, ref string)").WithLocation(20, 21), // (21,13): error CS1954: The best overloaded method match 'Extensions.Add<T>(ref MyCollection<T>, ref string)' for the collection initializer element cannot be used. Collection initializer 'Add' methods cannot have ref or out parameters. // y = [4]; Diagnostic(ErrorCode.ERR_InitializerAddHasParamModifiers, "[4]").WithArguments("Extensions.Add<T>(ref MyCollection<T>, ref string)").WithLocation(21, 13)); } [Fact] public void CollectionInitializerType_WrongSignature() { string source = """ using System.Collections; using System.Collections.Generic; struct MyCollection<T> : IEnumerable { public static void Add(T t) { } public IEnumerator<T> GetEnumerator() => throw null; IEnumerator IEnumerable.GetEnumerator() => throw null; } class Program { static void Main() { MyCollection<int> x = []; MyCollection<int> y = [1, ..x]; } } """; var comp = CreateCompilation(source); comp.VerifyEmitDiagnostics( // (14,31): error CS1921: The best overloaded method match for 'MyCollection<int>.Add(int)' has wrong signature for the initializer element. The initializable Add must be an accessible instance method. // MyCollection<int> y = [1, ..x]; Diagnostic(ErrorCode.ERR_InitializerAddHasWrongSignature, "[1, ..x]").WithArguments("MyCollection<int>.Add(int)").WithLocation(14, 31)); } [Fact] public void CollectionInitializerType_ObsoleteAdd_01() { string source = """ using System; using System.Collections; using System.Collections.Generic; struct MyCollection<T> : IEnumerable { [Obsolete] public void Add(T t) { } public IEnumerator<T> GetEnumerator() => throw null; IEnumerator IEnumerable.GetEnumerator() => throw null; } class Program { static void Main() { MyCollection<int> x = []; MyCollection<int> y = [1, ..x]; } } """; var comp = CreateCompilation(source); comp.VerifyEmitDiagnostics( // (15,32): warning CS1064: The best overloaded Add method 'MyCollection<int>.Add(int)' for the collection initializer element is obsolete. // MyCollection<int> y = [1, ..x]; Diagnostic(ErrorCode.WRN_DeprecatedCollectionInitAdd, "1").WithArguments("MyCollection<int>.Add(int)").WithLocation(15, 32), // (15,37): warning CS1064: The best overloaded Add method 'MyCollection<int>.Add(int)' for the collection initializer element is obsolete. // MyCollection<int> y = [1, ..x]; Diagnostic(ErrorCode.WRN_DeprecatedCollectionInitAdd, "x").WithArguments("MyCollection<int>.Add(int)").WithLocation(15, 37)); } [Fact] public void CollectionInitializerType_ObsoleteAdd_02() { string source = """ using System; using System.Collections; using System.Collections.Generic; struct MyCollection<T> : IEnumerable { [Obsolete("do not use")] public void Add(T t) { } public IEnumerator<T> GetEnumerator() => throw null; IEnumerator IEnumerable.GetEnumerator() => throw null; } class Program { static void Main() { MyCollection<int> x = []; MyCollection<int> y = [1, ..x]; } } """; var comp = CreateCompilation(source); comp.VerifyEmitDiagnostics( // (15,32): warning CS1062: The best overloaded Add method 'MyCollection<int>.Add(int)' for the collection initializer element is obsolete. do not use // MyCollection<int> y = [1, ..x]; Diagnostic(ErrorCode.WRN_DeprecatedCollectionInitAddStr, "1").WithArguments("MyCollection<int>.Add(int)", "do not use").WithLocation(15, 32), // (15,37): warning CS1062: The best overloaded Add method 'MyCollection<int>.Add(int)' for the collection initializer element is obsolete. do not use // MyCollection<int> y = [1, ..x]; Diagnostic(ErrorCode.WRN_DeprecatedCollectionInitAddStr, "x").WithArguments("MyCollection<int>.Add(int)", "do not use").WithLocation(15, 37)); } [Fact] public void CollectionInitializerType_ObsoleteAdd_03() { string source = """ using System; using System.Collections; using System.Collections.Generic; struct MyCollection<T> : IEnumerable { [Obsolete("do not use", error: true)] public void Add(T t) { } public IEnumerator<T> GetEnumerator() => throw null; IEnumerator IEnumerable.GetEnumerator() => throw null; } class Program { static void Main() { MyCollection<int> x = []; MyCollection<int> y = [1, ..x]; } } """; var comp = CreateCompilation(source); comp.VerifyEmitDiagnostics( // (15,32): error CS1063: The best overloaded Add method 'MyCollection<int>.Add(int)' for the collection initializer element is obsolete. do not use // MyCollection<int> y = [1, ..x]; Diagnostic(ErrorCode.ERR_DeprecatedCollectionInitAddStr, "1").WithArguments("MyCollection<int>.Add(int)", "do not use").WithLocation(15, 32), // (15,37): error CS1063: The best overloaded Add method 'MyCollection<int>.Add(int)' for the collection initializer element is obsolete. do not use // MyCollection<int> y = [1, ..x]; Diagnostic(ErrorCode.ERR_DeprecatedCollectionInitAddStr, "x").WithArguments("MyCollection<int>.Add(int)", "do not use").WithLocation(15, 37)); } [Fact] public void CollectionInitializerType_Dynamic_01() { string source = """ using System.Collections; using System.Collections.Generic; class MyCollection : IEnumerable<int> { private List<int> _list = new List<int>(); public void Add(int i) { _list.Add(i); } public void Add(string s) { _list.Add(s.Length); } IEnumerator<int> IEnumerable<int>.GetEnumerator() => _list.GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => _list.GetEnumerator(); } class Program { static void Main() { Create(1, "two").Report(); } static MyCollection Create(dynamic x, dynamic y) { return /*<bind>*/[x, y]/*</bind>*/; } } """; var comp = CreateCompilation(new[] { source, s_collectionExtensions }, references: new[] { CSharpRef }, options: TestOptions.ReleaseExe); CompileAndVerify(comp, expectedOutput: "[1, 3], "); VerifyOperationTreeForTest<CollectionExpressionSyntax>(comp, """ ICollectionExpressionOperation (2 elements, ConstructMethod: MyCollection..ctor()) (OperationKind.CollectionExpression, Type: MyCollection) (Syntax: '[x, y]') Elements(2): IParameterReferenceOperation: x (OperationKind.ParameterReference, Type: dynamic) (Syntax: 'x') IParameterReferenceOperation: y (OperationKind.ParameterReference, Type: dynamic) (Syntax: 'y') """); var tree = comp.SyntaxTrees[0]; var method = tree.GetRoot().DescendantNodes().OfType<MethodDeclarationSyntax>().Single(m => m.Identifier.Text == "Create"); VerifyFlowGraph(comp, method, """ Block[B0] - Entry Statements (0) Next (Regular) Block[B1] Block[B1] - Block Predecessors: [B0] Statements (0) Next (Return) Block[B2] IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: MyCollection, IsImplicit) (Syntax: '[x, y]') Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) (CollectionExpression) Operand: ICollectionExpressionOperation (2 elements, ConstructMethod: MyCollection..ctor()) (OperationKind.CollectionExpression, Type: MyCollection) (Syntax: '[x, y]') Elements(2): IParameterReferenceOperation: x (OperationKind.ParameterReference, Type: dynamic) (Syntax: 'x') IParameterReferenceOperation: y (OperationKind.ParameterReference, Type: dynamic) (Syntax: 'y') Block[B2] - Exit Predecessors: [B1] Statements (0) """); } [Fact] public void CollectionInitializerType_Dynamic_02() { string source = """ using System.Collections; using System.Collections.Generic; class MyCollection : IEnumerable<int> { private List<int> _list = new List<int>(); public void Add(int i) { _list.Add(i); } public void Add(string s) { _list.Add(s.Length); } IEnumerator<int> IEnumerable<int>.GetEnumerator() => _list.GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => _list.GetEnumerator(); } class Program { static void Main() { Create([1], [2, "three"]).Report(); } static MyCollection Create(int[] x, dynamic[] y) { return /*<bind>*/[..x, ..y]/*</bind>*/; } } """; var comp = CreateCompilation(new[] { source, s_collectionExtensions }, references: new[] { CSharpRef }, options: TestOptions.ReleaseExe); CompileAndVerify(comp, expectedOutput: "[1, 2, 5], "); VerifyOperationTreeForTest<CollectionExpressionSyntax>(comp, """ ICollectionExpressionOperation (2 elements, ConstructMethod: MyCollection..ctor()) (OperationKind.CollectionExpression, Type: MyCollection) (Syntax: '[..x, ..y]') Elements(2): ISpreadOperation (ElementType: System.Int32) (OperationKind.Spread, Type: null) (Syntax: '..x') Operand: IParameterReferenceOperation: x (OperationKind.ParameterReference, Type: System.Int32[]) (Syntax: 'x') ElementConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) (Identity) ISpreadOperation (ElementType: dynamic) (OperationKind.Spread, Type: null) (Syntax: '..y') Operand: IParameterReferenceOperation: y (OperationKind.ParameterReference, Type: dynamic[]) (Syntax: 'y') ElementConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) (Identity) """); var tree = comp.SyntaxTrees[0]; var method = tree.GetRoot().DescendantNodes().OfType<MethodDeclarationSyntax>().Single(m => m.Identifier.Text == "Create"); VerifyFlowGraph(comp, method, """ Block[B0] - Entry Statements (0) Next (Regular) Block[B1] Block[B1] - Block Predecessors: [B0] Statements (0) Next (Return) Block[B2] IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: MyCollection, IsImplicit) (Syntax: '[..x, ..y]') Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) (CollectionExpression) Operand: ICollectionExpressionOperation (2 elements, ConstructMethod: MyCollection..ctor()) (OperationKind.CollectionExpression, Type: MyCollection) (Syntax: '[..x, ..y]') Elements(2): ISpreadOperation (ElementType: System.Int32) (OperationKind.Spread, Type: null) (Syntax: '..x') Operand: IParameterReferenceOperation: x (OperationKind.ParameterReference, Type: System.Int32[]) (Syntax: 'x') ElementConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) (Identity) ISpreadOperation (ElementType: dynamic) (OperationKind.Spread, Type: null) (Syntax: '..y') Operand: IParameterReferenceOperation: y (OperationKind.ParameterReference, Type: dynamic[]) (Syntax: 'y') ElementConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) (Identity) Block[B2] - Exit Predecessors: [B1] Statements (0) """); } [Fact] public void CollectionInitializerType_ExtensionAdd() { string source = """ using System.Collections; using System.Collections.Generic; class MyCollection<T> : IEnumerable<T> { private List<T> _list = new List<T>(); public void __AddInternal(T t) { _list.Add(t); } IEnumerator<T> IEnumerable<T>.GetEnumerator() => _list.GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => _list.GetEnumerator(); } static class Extensions { public static void Add<T>(this MyCollection<T> c, T t) { c.__AddInternal(t); } } class Program { static void Main() { int x = 1; int[] y = [2, 3]; MyCollection<object> z = Create(x, y); z.Report(); } static MyCollection<object> Create(int x, int[] y) { return /*<bind>*/[x, ..y]/*</bind>*/; } } """; var comp = CreateCompilation(new[] { source, s_collectionExtensions }, options: TestOptions.ReleaseExe); CompileAndVerify(comp, expectedOutput: "[1, 2, 3], "); VerifyOperationTreeForTest<CollectionExpressionSyntax>(comp, """ ICollectionExpressionOperation (2 elements, ConstructMethod: MyCollection<System.Object>..ctor()) (OperationKind.CollectionExpression, Type: MyCollection<System.Object>) (Syntax: '[x, ..y]') Elements(2): IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Object, IsImplicit) (Syntax: 'x') Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) Operand: IParameterReferenceOperation: x (OperationKind.ParameterReference, Type: System.Int32) (Syntax: 'x') ISpreadOperation (ElementType: System.Int32) (OperationKind.Spread, Type: null) (Syntax: '..y') Operand: IParameterReferenceOperation: y (OperationKind.ParameterReference, Type: System.Int32[]) (Syntax: 'y') ElementConversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) (Boxing) """); var tree = comp.SyntaxTrees[0]; var method = tree.GetRoot().DescendantNodes().OfType<MethodDeclarationSyntax>().Single(m => m.Identifier.Text == "Create"); VerifyFlowGraph(comp, method, """ Block[B0] - Entry Statements (0) Next (Regular) Block[B1] Block[B1] - Block Predecessors: [B0] Statements (0) Next (Return) Block[B2] IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: MyCollection<System.Object>, IsImplicit) (Syntax: '[x, ..y]') Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) (CollectionExpression) Operand: ICollectionExpressionOperation (2 elements, ConstructMethod: MyCollection<System.Object>..ctor()) (OperationKind.CollectionExpression, Type: MyCollection<System.Object>) (Syntax: '[x, ..y]') Elements(2): IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Object, IsImplicit) (Syntax: 'x') Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) (Boxing) Operand: IParameterReferenceOperation: x (OperationKind.ParameterReference, Type: System.Int32) (Syntax: 'x') ISpreadOperation (ElementType: System.Int32) (OperationKind.Spread, Type: null) (Syntax: '..y') Operand: IParameterReferenceOperation: y (OperationKind.ParameterReference, Type: System.Int32[]) (Syntax: 'y') ElementConversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) (Boxing) Block[B2] - Exit Predecessors: [B1] Statements (0) """); } [Fact] public void CollectionInitializerType_ExtensionAdd_Ref() { string source = """ using System.Collections; using System.Collections.Generic; struct MyCollection<T> : IEnumerable<T> { private List<T> _list; public void __AddInternal(T t) { GetList().Add(t); } IEnumerator<T> IEnumerable<T>.GetEnumerator() => GetList().GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => GetList().GetEnumerator(); List<T> GetList() => _list ??= new(); } static class Extensions { public static void Add<T>(this ref MyCollection<T> c, T t) { c.__AddInternal(t); } } class Program { static void Main() { int x = 1; int[] y = [2, 3]; MyCollection<object> z = Create(x, y); z.Report(); } static MyCollection<object> Create(int x, int[] y) { return /*<bind>*/[x, ..y]/*</bind>*/; } } """; var comp = CreateCompilation(new[] { source, s_collectionExtensions }, options: TestOptions.ReleaseExe); CompileAndVerify(comp, expectedOutput: "[1, 2, 3], "); VerifyOperationTreeForTest<CollectionExpressionSyntax>(comp, """ ICollectionExpressionOperation (2 elements, ConstructMethod: MyCollection<System.Object>..ctor()) (OperationKind.CollectionExpression, Type: MyCollection<System.Object>) (Syntax: '[x, ..y]') Elements(2): IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Object, IsImplicit) (Syntax: 'x') Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) Operand: IParameterReferenceOperation: x (OperationKind.ParameterReference, Type: System.Int32) (Syntax: 'x') ISpreadOperation (ElementType: System.Int32) (OperationKind.Spread, Type: null) (Syntax: '..y') Operand: IParameterReferenceOperation: y (OperationKind.ParameterReference, Type: System.Int32[]) (Syntax: 'y') ElementConversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) (Boxing) """); var tree = comp.SyntaxTrees[0]; var method = tree.GetRoot().DescendantNodes().OfType<MethodDeclarationSyntax>().Single(m => m.Identifier.Text == "Create"); VerifyFlowGraph(comp, method, """ Block[B0] - Entry Statements (0) Next (Regular) Block[B1] Block[B1] - Block Predecessors: [B0] Statements (0) Next (Return) Block[B2] IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: MyCollection<System.Object>, IsImplicit) (Syntax: '[x, ..y]') Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) (CollectionExpression) Operand: ICollectionExpressionOperation (2 elements, ConstructMethod: MyCollection<System.Object>..ctor()) (OperationKind.CollectionExpression, Type: MyCollection<System.Object>) (Syntax: '[x, ..y]') Elements(2): IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Object, IsImplicit) (Syntax: 'x') Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) (Boxing) Operand: IParameterReferenceOperation: x (OperationKind.ParameterReference, Type: System.Int32) (Syntax: 'x') ISpreadOperation (ElementType: System.Int32) (OperationKind.Spread, Type: null) (Syntax: '..y') Operand: IParameterReferenceOperation: y (OperationKind.ParameterReference, Type: System.Int32[]) (Syntax: 'y') ElementConversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) (Boxing) Block[B2] - Exit Predecessors: [B1] Statements (0) """); } [Fact] public void CollectionInitializerType_GetEnumeratorPattern_Generic() { string sourceA = """ using System.Collections; using System.Collections.Generic; class MyCollection : IEnumerable<int> { private List<string> _list = new(); public void Add(string s) { _list.Add(s); } public void Add(int i) { throw null; } public IEnumerator<string> GetEnumerator() => _list.GetEnumerator(); IEnumerator<int> IEnumerable<int>.GetEnumerator() => throw null; IEnumerator IEnumerable.GetEnumerator() => throw null; } """; string sourceB1 = """ using System; class Program { static void Main() { MyCollection x = ["1", null]; MyCollection y = [..x, "3"]; foreach (var i in y) Console.Write("{0}, ", i ?? "null"); } } """; CompileAndVerify(new[] { sourceA, sourceB1 }, expectedOutput: "1, null, 3, "); string sourceB2 = """ class Program { static void Main() { MyCollection x = [1, default]; MyCollection y = [..x, 3]; } } """; var comp = CreateCompilation(new[] { sourceA, sourceB2 }); comp.VerifyEmitDiagnostics( // 1.cs(5,27): error CS0029: Cannot implicitly convert type 'int' to 'string' // MyCollection x = [1, default]; Diagnostic(ErrorCode.ERR_NoImplicitConv, "1").WithArguments("int", "string").WithLocation(5, 27), // 1.cs(6,32): error CS0029: Cannot implicitly convert type 'int' to 'string' // MyCollection y = [..x, 3]; Diagnostic(ErrorCode.ERR_NoImplicitConv, "3").WithArguments("int", "string").WithLocation(6, 32)); } [Fact] public void CollectionInitializerType_GetEnumeratorPattern_NonGeneric() { string sourceA = """ using System.Collections; using System.Collections.Generic; class MyCollection : IEnumerable { private List<string> _list = new(); public void Add(string s) { _list.Add(s); } public void Add(int i) { throw null; } public IEnumerator<string> GetEnumerator() => _list.GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => throw null; } """; string sourceB1 = """ using System; class Program { static void Main() { MyCollection x = ["1", null]; MyCollection y = [..x, "3"]; foreach (var i in y) Console.Write("{0}, ", i ?? "null"); } } """; CompileAndVerify(new[] { sourceA, sourceB1 }, expectedOutput: "1, null, 3, "); string sourceB2 = """ class Program { static void Main() { MyCollection x = [1, default]; MyCollection y = [..x, 3]; } } """; var comp = CreateCompilation(new[] { sourceA, sourceB2 }); comp.VerifyEmitDiagnostics( // 1.cs(5,27): error CS0029: Cannot implicitly convert type 'int' to 'string' // MyCollection x = [1, default]; Diagnostic(ErrorCode.ERR_NoImplicitConv, "1").WithArguments("int", "string").WithLocation(5, 27), // 1.cs(6,32): error CS0029: Cannot implicitly convert type 'int' to 'string' // MyCollection y = [..x, 3]; Diagnostic(ErrorCode.ERR_NoImplicitConv, "3").WithArguments("int", "string").WithLocation(6, 32)); } [Fact] public void CollectionInitializerType_GetEnumeratorPattern_NoImplementations() { string sourceA = """ using System.Collections; using System.Collections.Generic; class MyCollection { private List<string> _list = new(); public void Add(string s) { _list.Add(s); } public IEnumerator<string> GetEnumerator() => _list.GetEnumerator(); } """; string sourceB1 = """ using System; class Program { static void Main() { MyCollection c = new(); c.Add("1"); c.Add(default); foreach (var i in c) Console.Write("{0}, ", i ?? "null"); } } """; CompileAndVerify(new[] { sourceA, sourceB1 }, expectedOutput: "1, null, "); string sourceB2 = """ class Program { static void Main() { MyCollection x = ["1", default]; MyCollection y = [..x]; } } """; var comp = CreateCompilation(new[] { sourceA, sourceB2 }); comp.VerifyEmitDiagnostics( // 1.cs(5,26): error CS9174: Cannot initialize type 'MyCollection' with a collection expression because the type is not constructible. // MyCollection x = ["1", default]; Diagnostic(ErrorCode.ERR_CollectionExpressionTargetTypeNotConstructible, @"[""1"", default]").WithArguments("MyCollection").WithLocation(5, 26), // 1.cs(6,26): error CS9174: Cannot initialize type 'MyCollection' with a collection expression because the type is not constructible. // MyCollection y = [..x]; Diagnostic(ErrorCode.ERR_CollectionExpressionTargetTypeNotConstructible, "[..x]").WithArguments("MyCollection").WithLocation(6, 26)); } [Fact] public void CollectionInitializerType_GetEnumeratorPattern_RefStruct_Generic() { string sourceA = """ using System.Collections; using System.Collections.Generic; ref struct R { public R(object value) { Value = value; } public readonly object Value; } class MyCollection : IEnumerable<object> { private List<object> _list = new(); public void Add(R r) { _list.Add(r.Value); } public MyEnumerator GetEnumerator() => new MyEnumerator(_list); IEnumerator<object> IEnumerable<object>.GetEnumerator() => throw null; IEnumerator IEnumerable.GetEnumerator() => throw null; } class MyEnumerator { private List<object> _list; private int _index = -1; public MyEnumerator(List<object> list) { _list = list; } public bool MoveNext() { if (_index < _list.Count) _index++; return _index < _list.Count; } public R Current => new R(_list[_index]); } """; string sourceB = """ using System; class Program { static void Main() { MyCollection x = [new R(1)]; MyCollection y = [..x, new R(2)]; foreach (var i in y) Console.Write("{0}, ", i.Value); } } """; CompileAndVerify(new[] { sourceA, sourceB }, verify: Verification.FailsILVerify, expectedOutput: "1, 2, "); } [Fact] public void CollectionInitializerType_GetEnumeratorPattern_RefStruct_NonGeneric() { string sourceA = """ using System.Collections; using System.Collections.Generic; ref struct R { public R(object value) { Value = value; } public readonly object Value; } class MyCollection : IEnumerable { private List<object> _list = new(); public void Add(R r) { _list.Add(r.Value); } public MyEnumerator GetEnumerator() => new MyEnumerator(_list); IEnumerator IEnumerable.GetEnumerator() => throw null; } class MyEnumerator { private List<object> _list; private int _index = -1; public MyEnumerator(List<object> list) { _list = list; } public bool MoveNext() { if (_index < _list.Count) _index++; return _index < _list.Count; } public R Current => new R(_list[_index]); } """; string sourceB = """ using System; class Program { static void Main() { MyCollection x = [new R(1)]; MyCollection y = [..x, new R(2)]; foreach (var i in y) Console.Write("{0}, ", i.Value); } } """; CompileAndVerify(new[] { sourceA, sourceB }, verify: Verification.FailsILVerify, expectedOutput: "1, 2, "); } [WorkItem("https://github.com/dotnet/roslyn/issues/71387")] [Fact] public void CollectionInitializerType_MultipleIEnumerableTImplementations_01() { string sourceA = """ using System.Collections; using System.Collections.Generic; class MyCollection : IEnumerable<object>, IEnumerable<string> { private List<string> _list = new(); public void Add(string s) { _list.Add(s); } IEnumerator<string> IEnumerable<string>.GetEnumerator() => _list.GetEnumerator(); IEnumerator<object> IEnumerable<object>.GetEnumerator() => _list.GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => _list.GetEnumerator(); } """; string sourceB1 = """ class Program { static void Main() { foreach (var i in new MyCollection()) { } } } """; var comp = CreateCompilation(new[] { sourceA, sourceB1 }); comp.VerifyEmitDiagnostics( // 1.cs(5,27): error CS1640: foreach statement cannot operate on variables of type 'MyCollection' because it implements multiple instantiations of 'IEnumerable<T>'; try casting to a specific interface instantiation // foreach (var i in new MyCollection()) { } Diagnostic(ErrorCode.ERR_MultipleIEnumOfT, "new MyCollection()").WithArguments("MyCollection", "System.Collections.Generic.IEnumerable<T>").WithLocation(5, 27)); string sourceB2 = """ class Program { static void Main() { MyCollection c; c = []; c = ["1"]; c = [2]; } } """; comp = CreateCompilation(new[] { sourceA, sourceB2 }); comp.VerifyEmitDiagnostics( // 1.cs(6,13): error CS9213: Collection expression target 'MyCollection' has no element type. // c = []; Diagnostic(ErrorCode.ERR_CollectionExpressionTargetNoElementType, "[]").WithArguments("MyCollection").WithLocation(6, 13), // 1.cs(7,13): error CS9213: Collection expression target 'MyCollection' has no element type. // c = ["1"]; Diagnostic(ErrorCode.ERR_CollectionExpressionTargetNoElementType, @"[""1""]").WithArguments("MyCollection").WithLocation(7, 13), // 1.cs(8,13): error CS9213: Collection expression target 'MyCollection' has no element type. // c = [2]; Diagnostic(ErrorCode.ERR_CollectionExpressionTargetNoElementType, "[2]").WithArguments("MyCollection").WithLocation(8, 13)); } [WorkItem("https://github.com/dotnet/roslyn/issues/71387")] [Fact] public void CollectionInitializerType_MultipleIEnumerableTImplementations_02() { string sourceA = """ using System.Collections; using System.Collections.Generic; abstract class MyCollectionBase<T> : IEnumerable<T> { public List<object> _list = new(); public void Add(T t) { _list.Add(t); } IEnumerator<T> IEnumerable<T>.GetEnumerator() { foreach (var i in _list) yield return (T)i; } IEnumerator IEnumerable.GetEnumerator() => _list.GetEnumerator(); } class MyCollection<T, U> : MyCollectionBase<T>, IEnumerable<U> { public void Add(U u) { _list.Add(u); } IEnumerator<U> IEnumerable<U>.GetEnumerator() { foreach (var i in _list) yield return (U)i; } } """; string sourceB1 = """ class Program { static void Main() { foreach (var i in new MyCollection<object, string>()) { } foreach (var i in new MyCollection<int, string>()) { } } static void F<T, U>() { foreach (var i in new MyCollection<T, U>()) { } } } """; var comp = CreateCompilation(new[] { sourceA, sourceB1 }); comp.VerifyEmitDiagnostics( // 1.cs(5,27): error CS1640: foreach statement cannot operate on variables of type 'MyCollection<object, string>' because it implements multiple instantiations of 'IEnumerable<T>'; try casting to a specific interface instantiation // foreach (var i in new MyCollection<object, string>()) { } Diagnostic(ErrorCode.ERR_MultipleIEnumOfT, "new MyCollection<object, string>()").WithArguments("MyCollection<object, string>", "System.Collections.Generic.IEnumerable<T>").WithLocation(5, 27), // 1.cs(6,27): error CS1640: foreach statement cannot operate on variables of type 'MyCollection<int, string>' because it implements multiple instantiations of 'IEnumerable<T>'; try casting to a specific interface instantiation // foreach (var i in new MyCollection<int, string>()) { } Diagnostic(ErrorCode.ERR_MultipleIEnumOfT, "new MyCollection<int, string>()").WithArguments("MyCollection<int, string>", "System.Collections.Generic.IEnumerable<T>").WithLocation(6, 27), // 1.cs(10,27): error CS1640: foreach statement cannot operate on variables of type 'MyCollection<T, U>' because it implements multiple instantiations of 'IEnumerable<T>'; try casting to a specific interface instantiation // foreach (var i in new MyCollection<T, U>()) { } Diagnostic(ErrorCode.ERR_MultipleIEnumOfT, "new MyCollection<T, U>()").WithArguments("MyCollection<T, U>", "System.Collections.Generic.IEnumerable<T>").WithLocation(10, 27)); string sourceB2 = """ class Program { static void Main() { Create<object, string>([]); Create<int, string>([2]); } static void F<T, U>(T t) { Create<T, U>([t]); } static void Create<T, U>(MyCollection<T, U> c) { } } """; comp = CreateCompilation(new[] { sourceA, sourceB2 }); comp.VerifyEmitDiagnostics( // 1.cs(5,32): error CS9213: Collection expression target 'MyCollection<object, string>' has no element type. // Create<object, string>([]); Diagnostic(ErrorCode.ERR_CollectionExpressionTargetNoElementType, "[]").WithArguments("MyCollection<object, string>").WithLocation(5, 32), // 1.cs(6,29): error CS9213: Collection expression target 'MyCollection<int, string>' has no element type. // Create<int, string>([2]); Diagnostic(ErrorCode.ERR_CollectionExpressionTargetNoElementType, "[2]").WithArguments("MyCollection<int, string>").WithLocation(6, 29), // 1.cs(10,22): error CS9213: Collection expression target 'MyCollection<T, U>' has no element type. // Create<T, U>([t]); Diagnostic(ErrorCode.ERR_CollectionExpressionTargetNoElementType, "[t]").WithArguments("MyCollection<T, U>").WithLocation(10, 22)); string sourceB3 = """ class Program { static void Main() { Create<int, int>([3]); F(4); } static void F<T>(T t) { Create<T, T>([t]); } static void Create<T, U>(MyCollection<T, U> c) { c.Report(); } } """; CompileAndVerify(new[] { sourceA, sourceB3, s_collectionExtensions }, expectedOutput: "[3], [4], "); } [Theory] [InlineData("class")] [InlineData("struct")] public void TypeParameter_01(string type) { string source = $$""" using System; using System.Collections; using System.Collections.Generic; interface I<T> : IEnumerable<T> { void Add(T t); } {{type}} C<T> : I<T> { private List<T> _list; public void Add(T t) { GetList().Add(t); } IEnumerator<T> IEnumerable<T>.GetEnumerator() { return GetList().GetEnumerator(); } IEnumerator IEnumerable.GetEnumerator() { return GetList().GetEnumerator(); } private List<T> GetList() => _list ??= new List<T>(); } class Program { static void Main() { CreateEmpty<C<object>, object>().Report(); Create<C<long?>, long?>(null, 2).Report(); } static T CreateEmpty<T, U>() where T : I<U>, new() { return []; } static T Create<T, U>(U a, U b) where T : I<U>, new() { return [a, b]; } } """; var verifier = CompileAndVerify(new[] { source, s_collectionExtensions }, expectedOutput: "[], [null, 2], "); verifier.VerifyIL("Program.CreateEmpty<T, U>", """ { // Code size 6 (0x6) .maxstack 1 IL_0000: call "T System.Activator.CreateInstance<T>()" IL_0005: ret } """); verifier.VerifyIL("Program.Create<T, U>", """ { // Code size 36 (0x24) .maxstack 2 .locals init (T V_0) IL_0000: call "T System.Activator.CreateInstance<T>()" IL_0005: stloc.0 IL_0006: ldloca.s V_0 IL_0008: ldarg.0 IL_0009: constrained. "T" IL_000f: callvirt "void I<U>.Add(U)" IL_0014: ldloca.s V_0 IL_0016: ldarg.1 IL_0017: constrained. "T" IL_001d: callvirt "void I<U>.Add(U)" IL_0022: ldloc.0 IL_0023: ret } """); } [Fact] public void TypeParameter_02() { string source = """ using System.Collections; using System.Collections.Generic; interface I<T> : IEnumerable<T> { void Add(T t); } struct S<T> : I<T> { List<T> _list; public void Add(T t) { GetList().Add(t); } IEnumerator<T> IEnumerable<T>.GetEnumerator() => GetList().GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => GetList().GetEnumerator(); private List<T> GetList() => _list ??= new List<T>(); } class Program { public static void Main() { Program.Create1<S<int>, int>().Report(); Program.Create2<S<int>, int>().Report(); Program.Create3<S<int>, int>(42, 43).Report(); Program.Create4<S<int>, int>(44, 45).Report(); } static T Create1<T, U>() where T : struct, I<U> => []; static T? Create2<T, U>() where T : struct, I<U> => []; static T Create3<T, U>(U x, U y) where T : struct, I<U> => [x, y]; static T? Create4<T, U>(U x, U y) where T : struct, I<U> => [x, y]; } """; var comp = CreateCompilation(new[] { source, s_collectionExtensions }, options: TestOptions.ReleaseExe); comp.VerifyEmitDiagnostics(); var verifier = CompileAndVerify(comp, expectedOutput: "[], [], [42, 43], [44, 45],"); verifier.VerifyIL("Program.Create1<T, U>", """ { // Code size 6 (0x6) .maxstack 1 IL_0000: call "T System.Activator.CreateInstance<T>()" IL_0005: ret } """); verifier.VerifyIL("Program.Create2<T, U>", """ { // Code size 11 (0xb) .maxstack 1 IL_0000: call "T System.Activator.CreateInstance<T>()" IL_0005: newobj "T?..ctor(T)" IL_000a: ret } """); verifier.VerifyIL("Program.Create3<T, U>", """ { // Code size 36 (0x24) .maxstack 2 .locals init (T V_0) IL_0000: call "T System.Activator.CreateInstance<T>()" IL_0005: stloc.0 IL_0006: ldloca.s V_0 IL_0008: ldarg.0 IL_0009: constrained. "T" IL_000f: callvirt "void I<U>.Add(U)" IL_0014: ldloca.s V_0 IL_0016: ldarg.1 IL_0017: constrained. "T" IL_001d: callvirt "void I<U>.Add(U)" IL_0022: ldloc.0 IL_0023: ret } """); verifier.VerifyIL("Program.Create4<T, U>", """ { // Code size 41 (0x29) .maxstack 2 .locals init (T V_0) IL_0000: call "T System.Activator.CreateInstance<T>()" IL_0005: stloc.0 IL_0006: ldloca.s V_0 IL_0008: ldarg.0 IL_0009: constrained. "T" IL_000f: callvirt "void I<U>.Add(U)" IL_0014: ldloca.s V_0 IL_0016: ldarg.1 IL_0017: constrained. "T" IL_001d: callvirt "void I<U>.Add(U)" IL_0022: ldloc.0 IL_0023: newobj "T?..ctor(T)" IL_0028: ret } """); } [Fact] public void TypeParameter_03() { string source = """ using System.Collections; class Program { static T Create1<T, U>() where T : IEnumerable => []; // 1 static T Create2<T, U>() where T : class, IEnumerable => []; // 2 static T Create3<T, U>() where T : struct, IEnumerable => []; static T Create4<T, U>() where T : IEnumerable, new() => []; } """; var comp = CreateCompilation(source); comp.VerifyEmitDiagnostics( // (4,55): error CS0304: Cannot create an instance of the variable type 'T' because it does not have the new() constraint // static T Create1<T, U>() where T : IEnumerable => []; // 1 Diagnostic(ErrorCode.ERR_NoNewTyvar, "[]").WithArguments("T").WithLocation(4, 55), // (5,62): error CS0304: Cannot create an instance of the variable type 'T' because it does not have the new() constraint // static T Create2<T, U>() where T : class, IEnumerable => []; // 2 Diagnostic(ErrorCode.ERR_NoNewTyvar, "[]").WithArguments("T").WithLocation(5, 62)); } [Fact] public void TypeParameter_04() { string source = """ using System.Collections; interface IAdd : IEnumerable { void Add(object o); } class Program { static T Create1<T>() where T : IAdd => [1]; // 1 static T Create2<T>() where T : class, IAdd => [2]; // 2 static T Create3<T>() where T : struct, IAdd => [3]; static T Create4<T>() where T : IAdd, new() => [4]; } """; var comp = CreateCompilation(source); comp.VerifyEmitDiagnostics( // (8,45): error CS0304: Cannot create an instance of the variable type 'T' because it does not have the new() constraint // static T Create1<T>() where T : IAdd => [1]; // 1 Diagnostic(ErrorCode.ERR_NoNewTyvar, "[1]").WithArguments("T").WithLocation(8, 45), // (9,52): error CS0304: Cannot create an instance of the variable type 'T' because it does not have the new() constraint // static T Create2<T>() where T : class, IAdd => [2]; // 2 Diagnostic(ErrorCode.ERR_NoNewTyvar, "[2]").WithArguments("T").WithLocation(9, 52)); } [Fact] public void CollectionInitializerType_MissingIEnumerable() { string source = """ struct S { } class Program { static void Main() { S s = []; object o = (S)([1, 2]); } } """; var comp = CreateCompilation(source); comp.MakeTypeMissing(SpecialType.System_Collections_IEnumerable); comp.VerifyEmitDiagnostics( // (8,15): error CS9174: Cannot initialize type 'S' with a collection expression because the type is not constructible. // S s = []; Diagnostic(ErrorCode.ERR_CollectionExpressionTargetTypeNotConstructible, "[]").WithArguments("S").WithLocation(8, 15), // (9,24): error CS9174: Cannot initialize type 'S' with a collection expression because the type is not constructible. // object o = (S)([1, 2]); Diagnostic(ErrorCode.ERR_CollectionExpressionTargetTypeNotConstructible, "[1, 2]").WithArguments("S").WithLocation(9, 24)); } [Fact] public void CollectionInitializerType_UseSiteErrors() { string assemblyA = GetUniqueName(); string sourceA = """ public class A1 { } public class A2 { } """; var comp = CreateCompilation(sourceA, assemblyName: assemblyA); var refA = comp.EmitToImageReference(); string sourceB = """ using System.Collections; using System.Collections.Generic; public class B1 : IEnumerable<int> { List<int> _list = new List<int>(); public B1(A1 a = null) { } public void Add(int i) { _list.Add(i); } IEnumerator<int> IEnumerable<int>.GetEnumerator() => _list.GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => _list.GetEnumerator(); } public class B2 : IEnumerable<int> { List<int> _list = new List<int>(); public void Add(int x, A2 y = null) { _list.Add(x); } IEnumerator<int> IEnumerable<int>.GetEnumerator() => _list.GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => _list.GetEnumerator(); } """; comp = CreateCompilation(sourceB, references: new[] { refA }); var refB = comp.EmitToImageReference(); string sourceC = """ class C { static void Main() { B1 x; x = []; x.Report(); x = [1, 2]; x.Report(); B2 y; y = []; y.Report(); y = [3, 4]; y.Report(); } } """; CompileAndVerify(new[] { sourceC, s_collectionExtensions }, references: new[] { refA, refB }, expectedOutput: "[], [1, 2], [], [3, 4], "); comp = CreateCompilation(new[] { sourceC, s_collectionExtensions }, references: new[] { refB }); comp.VerifyEmitDiagnostics( // 0.cs(6,13): error CS0012: The type 'A1' is defined in an assembly that is not referenced. You must add a reference to assembly '6f8345f1-4f51-4a7a-a9f6-0597f76af3b9, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'. // x = []; Diagnostic(ErrorCode.ERR_NoTypeDef, "[]").WithArguments("A1", $"{assemblyA}, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null").WithLocation(6, 13), // 0.cs(8,13): error CS0012: The type 'A1' is defined in an assembly that is not referenced. You must add a reference to assembly '6f8345f1-4f51-4a7a-a9f6-0597f76af3b9, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'. // x = [1, 2]; Diagnostic(ErrorCode.ERR_NoTypeDef, "[1, 2]").WithArguments("A1", $"{assemblyA}, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null").WithLocation(8, 13), // 0.cs(13,14): error CS0012: The type 'A2' is defined in an assembly that is not referenced. You must add a reference to assembly '6f8345f1-4f51-4a7a-a9f6-0597f76af3b9, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'. // y = [3, 4]; Diagnostic(ErrorCode.ERR_NoTypeDef, "[3, 4]").WithArguments("A2", $"{assemblyA}, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null").WithLocation(13, 13)); } [Fact] public void ConditionalAdd_01() { string source = """ using System.Collections; using System.Collections.Generic; using System.Diagnostics; class C<T, U> : IEnumerable<object> { List<object> _list = new List<object>(); [Conditional("DEBUG")] internal void Add(object o) { _list.Add(o); } internal void Add(U u) { _list.Add(u); } IEnumerator<object> IEnumerable<object>.GetEnumerator() => _list.GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => _list.GetEnumerator(); } class Program { static void Main() { C<int, string> c = [1, "2", 3]; c.Report(); } } """; var parseOptions = TestOptions.RegularPreview; CompileAndVerify(new[] { source, s_collectionExtensions }, parseOptions: parseOptions.WithPreprocessorSymbols("DEBUG"), expectedOutput: "[1, 2, 3], "); CompileAndVerify(new[] { source, s_collectionExtensions }, parseOptions: parseOptions, expectedOutput: "[2], "); } [Fact] public void ConditionalAdd_02() { string source = """ using System.Collections; using System.Collections.Generic; using System.Diagnostics; class C<T, U> : IEnumerable<object> { List<object> _list = new List<object>(); [Conditional("DEBUG")] internal void Add(object o) { _list.Add(o); } internal void Add(U u) { _list.Add(u); } IEnumerator<object> IEnumerable<object>.GetEnumerator() => _list.GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => _list.GetEnumerator(); } class Program { static void Main() { int[] x = [1, 2]; string[] y = ["3"]; C<int, string> c = [..x, ..y]; c.Report(); } } """; var parseOptions = TestOptions.RegularPreview; CompileAndVerify(new[] { source, s_collectionExtensions }, parseOptions: parseOptions.WithPreprocessorSymbols("DEBUG"), expectedOutput: "[1, 2, 3], "); CompileAndVerify(new[] { source, s_collectionExtensions }, parseOptions: parseOptions, expectedOutput: "[3], "); } [Fact] public void DictionaryElement_01() { string source = """ using System.Collections.Generic; class Program { static void Main() { Dictionary<int, int> d; d = []; d.Report(); } } """; CompileAndVerify(new[] { source, s_collectionExtensions }, expectedOutput: "[], "); } [Fact] public void DictionaryElement_02() { string source = """ using System.Collections.Generic; class Program { static void Main() { Dictionary<int, int> d; d = [default]; d = [new KeyValuePair<int, int>(1, 2)]; d = [3:4]; } } """; var comp = CreateCompilation(source); comp.VerifyEmitDiagnostics( // (7,13): error CS9215: Collection expression type 'Dictionary<int, int>' must have an instance or extension method 'Add' that can be called with a single argument. // d = [default]; Diagnostic(ErrorCode.ERR_CollectionExpressionMissingAdd, "[default]").WithArguments("System.Collections.Generic.Dictionary<int, int>").WithLocation(7, 13), // (8,13): error CS9215: Collection expression type 'Dictionary<int, int>' must have an instance or extension method 'Add' that can be called with a single argument. // d = [new KeyValuePair<int, int>(1, 2)]; Diagnostic(ErrorCode.ERR_CollectionExpressionMissingAdd, "[new KeyValuePair<int, int>(1, 2)]").WithArguments("System.Collections.Generic.Dictionary<int, int>").WithLocation(8, 13), // (9,15): error CS1003: Syntax error, ',' expected // d = [3:4]; Diagnostic(ErrorCode.ERR_SyntaxError, ":").WithArguments(",").WithLocation(9, 15), // (9,16): error CS1003: Syntax error, ',' expected // d = [3:4]; Diagnostic(ErrorCode.ERR_SyntaxError, "4").WithArguments(",").WithLocation(9, 16)); } [Theory] [CombinatorialData] public void SpreadElement_01( [CombinatorialValues("IEnumerable<int>", "int[]", "List<int>", "Span<int>", "ReadOnlySpan<int>")] string spreadType, [CombinatorialValues("IEnumerable<int>", "int[]", "List<int>", "Span<int>", "ReadOnlySpan<int>")] string collectionType) { string source = $$""" using System; using System.Collections; using System.Collections.Generic; class Program { static void Main() { F([1, 2, 3]); } static void F({{spreadType}} x) { {{collectionType}} y = [..x]; y.Report(); } } """; var verifier = CompileAndVerify( new[] { source, s_collectionExtensionsWithSpan }, options: TestOptions.ReleaseExe, targetFramework: TargetFramework.Net70, verify: Verification.Skipped, expectedOutput: IncludeExpectedOutput("[1, 2, 3], ")); // Verify some of the cases. string expectedIL = (spreadType, collectionType) switch { ("IEnumerable<int>", "IEnumerable<int>") => """ { // Code size 24 (0x18) .maxstack 3 IL_0000: newobj "System.Collections.Generic.List<int>..ctor()" IL_0005: dup IL_0006: ldarg.0 IL_0007: callvirt "void System.Collections.Generic.List<int>.AddRange(System.Collections.Generic.IEnumerable<int>)" IL_000c: newobj "<>z__ReadOnlyList<int>..ctor(System.Collections.Generic.List<int>)" IL_0011: ldc.i4.0 IL_0012: call "void CollectionExtensions.Report(object, bool)" IL_0017: ret } """, ("IEnumerable<int>", "int[]") => """ { // Code size 20 (0x14) .maxstack 1 .locals init (System.ReadOnlySpan<int> V_0) IL_0000: ldarg.0 IL_0001: call "int[] System.Linq.Enumerable.ToArray<int>(System.Collections.Generic.IEnumerable<int>)" IL_0006: call "System.ReadOnlySpan<int> System.ReadOnlySpan<int>.op_Implicit(int[])" IL_000b: stloc.0 IL_000c: ldloca.s V_0 IL_000e: call "void CollectionExtensions.Report<int>(in System.ReadOnlySpan<int>)" IL_0013: ret } """, ("IEnumerable<int>", "Span<int>") => """ { // Code size 25 (0x19) .maxstack 1 .locals init (System.ReadOnlySpan<int> V_0) IL_0000: ldarg.0 IL_0001: call "int[] System.Linq.Enumerable.ToArray<int>(System.Collections.Generic.IEnumerable<int>)" IL_0006: newobj "System.Span<int>..ctor(int[])" IL_000b: call "System.ReadOnlySpan<int> System.Span<int>.op_Implicit(System.Span<int>)" IL_0010: stloc.0 IL_0011: ldloca.s V_0 IL_0013: call "void CollectionExtensions.Report<int>(in System.ReadOnlySpan<int>)" IL_0018: ret } """, ("int[]", "int[]") => """ { // Code size 20 (0x14) .maxstack 1 .locals init (System.ReadOnlySpan<int> V_0) IL_0000: ldarg.0 IL_0001: call "int[] System.Linq.Enumerable.ToArray<int>(System.Collections.Generic.IEnumerable<int>)" IL_0006: call "System.ReadOnlySpan<int> System.ReadOnlySpan<int>.op_Implicit(int[])" IL_000b: stloc.0 IL_000c: ldloca.s V_0 IL_000e: call "void CollectionExtensions.Report<int>(in System.ReadOnlySpan<int>)" IL_0013: ret } """, ("ReadOnlySpan<int>", "ReadOnlySpan<int>") => """ { // Code size 22 (0x16) .maxstack 2 .locals init (System.ReadOnlySpan<int> V_0) //y IL_0000: ldloca.s V_0 IL_0002: ldarga.s V_0 IL_0004: call "int[] System.ReadOnlySpan<int>.ToArray()" IL_0009: call "System.ReadOnlySpan<int>..ctor(int[])" IL_000e: ldloca.s V_0 IL_0010: call "void CollectionExtensions.Report<int>(in System.ReadOnlySpan<int>)" IL_0015: ret } """, _ => null }; if (expectedIL is { }) { verifier.VerifyIL("Program.F", expectedIL); } } [Theory] [InlineData("int[]")] [InlineData("System.Collections.Generic.List<int>")] [InlineData("System.Span<int>")] [InlineData("System.ReadOnlySpan<int>")] public void SpreadElement_02(string collectionType) { string source = $$""" class Program { static void Main() { {{collectionType}} c = []; Append(c); } static void Append({{collectionType}} x) { {{collectionType}} y = [1, 2]; {{collectionType}} z = [..x, ..y]; z.Report(); } } """; var verifier = CompileAndVerify( new[] { source, s_collectionExtensionsWithSpan }, options: TestOptions.ReleaseExe, targetFramework: TargetFramework.Net70, verify: Verification.Skipped, expectedOutput: IncludeExpectedOutput("[1, 2], ")); if (collectionType == "System.ReadOnlySpan<int>") { verifier.VerifyIL("Program.Append", """ { // Code size 145 (0x91) .maxstack 5 .locals init (System.ReadOnlySpan<int> V_0, //y System.ReadOnlySpan<int> V_1, //z System.ReadOnlySpan<int> V_2, System.ReadOnlySpan<int> V_3, int V_4, int[] V_5, System.Span<int> V_6, System.Span<int> V_7) IL_0000: ldtoken "<PrivateImplementationDetails>.__StaticArrayInitTypeSize=8_Align=4 <PrivateImplementationDetails>.34FB5C825DE7CA4AEA6E712F19D439C1DA0C92C37B423936C5F618545CA4FA1F4" IL_0005: call "System.ReadOnlySpan<int> System.Runtime.CompilerServices.RuntimeHelpers.CreateSpan<int>(System.RuntimeFieldHandle)" IL_000a: stloc.0 IL_000b: ldloca.s V_1 IL_000d: ldarg.0 IL_000e: stloc.2 IL_000f: ldloc.0 IL_0010: stloc.3 IL_0011: ldc.i4.0 IL_0012: stloc.s V_4 IL_0014: ldloca.s V_2 IL_0016: call "int System.ReadOnlySpan<int>.Length.get" IL_001b: ldloca.s V_3 IL_001d: call "int System.ReadOnlySpan<int>.Length.get" IL_0022: add IL_0023: newarr "int" IL_0028: stloc.s V_5 IL_002a: ldloca.s V_2 IL_002c: ldloc.s V_5 IL_002e: newobj "System.Span<int>..ctor(int[])" IL_0033: stloc.s V_6 IL_0035: ldloca.s V_6 IL_0037: ldloc.s V_4 IL_0039: ldloca.s V_2 IL_003b: call "int System.ReadOnlySpan<int>.Length.get" IL_0040: call "System.Span<int> System.Span<int>.Slice(int, int)" IL_0045: call "void System.ReadOnlySpan<int>.CopyTo(System.Span<int>)" IL_004a: ldloc.s V_4 IL_004c: ldloca.s V_2 IL_004e: call "int System.ReadOnlySpan<int>.Length.get" IL_0053: add IL_0054: stloc.s V_4 IL_0056: ldloca.s V_3 IL_0058: ldloc.s V_5 IL_005a: newobj "System.Span<int>..ctor(int[])" IL_005f: stloc.s V_7 IL_0061: ldloca.s V_7 IL_0063: ldloc.s V_4 IL_0065: ldloca.s V_3 IL_0067: call "int System.ReadOnlySpan<int>.Length.get" IL_006c: call "System.Span<int> System.Span<int>.Slice(int, int)" IL_0071: call "void System.ReadOnlySpan<int>.CopyTo(System.Span<int>)" IL_0076: ldloc.s V_4 IL_0078: ldloca.s V_3 IL_007a: call "int System.ReadOnlySpan<int>.Length.get" IL_007f: add IL_0080: stloc.s V_4 IL_0082: ldloc.s V_5 IL_0084: call "System.ReadOnlySpan<int>..ctor(int[])" IL_0089: ldloca.s V_1 IL_008b: call "void CollectionExtensions.Report<int>(in System.ReadOnlySpan<int>)" IL_0090: ret } """); } } [Fact] public void SpreadElement_03() { string source = """ using System.Collections; using System.Collections.Generic; struct S<T> : IEnumerable<T> { private List<T> _list; public void Add(T t) { _list ??= new List<T>(); _list.Add(t); } public IEnumerator<T> GetEnumerator() { _list ??= new List<T>(); return _list.GetEnumerator(); } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } } class Program { static void Main() { S<int> s; s = []; s = Append(s); s.Report(); } static S<int> Append(S<int> x) { S<int> y = [1, 2]; return [..x, ..y]; } } """; var verifier = CompileAndVerify( new[] { source, s_collectionExtensions }, options: TestOptions.ReleaseExe, expectedOutput: "[1, 2], "); verifier.VerifyIL("Program.Append", """ { // Code size 126 (0x7e) .maxstack 2 .locals init (S<int> V_0, //y S<int> V_1, System.Collections.Generic.IEnumerator<int> V_2, int V_3) IL_0000: ldloca.s V_1 IL_0002: initobj "S<int>" IL_0008: ldloca.s V_1 IL_000a: ldc.i4.1 IL_000b: call "void S<int>.Add(int)" IL_0010: ldloca.s V_1 IL_0012: ldc.i4.2 IL_0013: call "void S<int>.Add(int)" IL_0018: ldloc.1 IL_0019: stloc.0 IL_001a: ldloca.s V_1 IL_001c: initobj "S<int>" IL_0022: ldarga.s V_0 IL_0024: call "System.Collections.Generic.IEnumerator<int> S<int>.GetEnumerator()" IL_0029: stloc.2 .try { IL_002a: br.s IL_003b IL_002c: ldloc.2 IL_002d: callvirt "int System.Collections.Generic.IEnumerator<int>.Current.get" IL_0032: stloc.3 IL_0033: ldloca.s V_1 IL_0035: ldloc.3 IL_0036: call "void S<int>.Add(int)" IL_003b: ldloc.2 IL_003c: callvirt "bool System.Collections.IEnumerator.MoveNext()" IL_0041: brtrue.s IL_002c IL_0043: leave.s IL_004f } finally { IL_0045: ldloc.2 IL_0046: brfalse.s IL_004e IL_0048: ldloc.2 IL_0049: callvirt "void System.IDisposable.Dispose()" IL_004e: endfinally } IL_004f: ldloca.s V_0 IL_0051: call "System.Collections.Generic.IEnumerator<int> S<int>.GetEnumerator()" IL_0056: stloc.2 .try { IL_0057: br.s IL_0068 IL_0059: ldloc.2 IL_005a: callvirt "int System.Collections.Generic.IEnumerator<int>.Current.get" IL_005f: stloc.3 IL_0060: ldloca.s V_1 IL_0062: ldloc.3 IL_0063: call "void S<int>.Add(int)" IL_0068: ldloc.2 IL_0069: callvirt "bool System.Collections.IEnumerator.MoveNext()" IL_006e: brtrue.s IL_0059 IL_0070: leave.s IL_007c } finally { IL_0072: ldloc.2 IL_0073: brfalse.s IL_007b IL_0075: ldloc.2 IL_0076: callvirt "void System.IDisposable.Dispose()" IL_007b: endfinally } IL_007c: ldloc.1 IL_007d: ret } """); } [Fact] public void SpreadElement_04() { string source = """ class Program { static void Main() { var a = [1, 2, ..[]]; } } """; var comp = CreateCompilation(source); comp.VerifyEmitDiagnostics( // (5,26): error CS9176: There is no target type for the collection expression. // var a = [1, 2, ..[]]; Diagnostic(ErrorCode.ERR_CollectionExpressionNoTargetType, "[]").WithLocation(5, 26)); } [Fact] public void SpreadElement_05() { string source = """ class Program { static void Main() { int[] a = [1, 2]; a = [..a, ..[]]; a = [..[default]]; } } """; var comp = CreateCompilation(source); comp.VerifyEmitDiagnostics( // (6,21): error CS9176: There is no target type for the collection expression. // a = [..a, ..[]]; Diagnostic(ErrorCode.ERR_CollectionExpressionNoTargetType, "[]").WithLocation(6, 21), // (7,16): error CS9176: There is no target type for the collection expression. // a = [..[default]]; Diagnostic(ErrorCode.ERR_CollectionExpressionNoTargetType, "[default]").WithLocation(7, 16)); } [Fact] public void SpreadElement_06() { string source = """ class Program { static string[] Append(string a, string b, bool c) { return [a, b, .. c ? [null] : []]; } } """; var comp = CreateCompilation(source); comp.VerifyEmitDiagnostics( // (5,26): error CS0173: Type of conditional expression cannot be determined because there is no implicit conversion between 'collection expression' and 'collection expression' // return [a, b, .. c ? [null] : []]; Diagnostic(ErrorCode.ERR_InvalidQM, "c ? [null] : []").WithArguments("collection expression", "collection expression").WithLocation(5, 26)); } [Fact] public void SpreadElement_07() { string source = """ using System.Collections.Generic; class Program { static void Main() { int[,] a = new[,] { { 1, 2 }, { 3, 4 } }; int[] b = F(a); b.Report(); } static int[] F(int[,] a) => [..a]; } """; var verifier = CompileAndVerify(new[] { source, s_collectionExtensions }, expectedOutput: "[1, 2, 3, 4], "); verifier.VerifyIL("Program.F", """ { // Code size 101 (0x65) .maxstack 3 .locals init (int V_0, int[] V_1, int[,] V_2, int V_3, int V_4, int V_5, int V_6, int V_7) IL_0000: ldarg.0 IL_0001: ldc.i4.0 IL_0002: stloc.0 IL_0003: dup IL_0004: callvirt "int System.Array.Length.get" IL_0009: newarr "int" IL_000e: stloc.1 IL_000f: stloc.2 IL_0010: ldloc.2 IL_0011: ldc.i4.0 IL_0012: callvirt "int System.Array.GetUpperBound(int)" IL_0017: stloc.3 IL_0018: ldloc.2 IL_0019: ldc.i4.1 IL_001a: callvirt "int System.Array.GetUpperBound(int)" IL_001f: stloc.s V_4 IL_0021: ldloc.2 IL_0022: ldc.i4.0 IL_0023: callvirt "int System.Array.GetLowerBound(int)" IL_0028: stloc.s V_5 IL_002a: br.s IL_005e IL_002c: ldloc.2 IL_002d: ldc.i4.1 IL_002e: callvirt "int System.Array.GetLowerBound(int)" IL_0033: stloc.s V_6 IL_0035: br.s IL_0052 IL_0037: ldloc.2 IL_0038: ldloc.s V_5 IL_003a: ldloc.s V_6 IL_003c: call "int[*,*].Get" IL_0041: stloc.s V_7 IL_0043: ldloc.1 IL_0044: ldloc.0 IL_0045: ldloc.s V_7 IL_0047: stelem.i4 IL_0048: ldloc.0 IL_0049: ldc.i4.1 IL_004a: add IL_004b: stloc.0 IL_004c: ldloc.s V_6 IL_004e: ldc.i4.1 IL_004f: add IL_0050: stloc.s V_6 IL_0052: ldloc.s V_6 IL_0054: ldloc.s V_4 IL_0056: ble.s IL_0037 IL_0058: ldloc.s V_5 IL_005a: ldc.i4.1 IL_005b: add IL_005c: stloc.s V_5 IL_005e: ldloc.s V_5 IL_0060: ldloc.3 IL_0061: ble.s IL_002c IL_0063: ldloc.1 IL_0064: ret } """); } [Fact] public void SpreadElement_08() { string source = """ class Program { static void Main() { int[] a = [1, 2, 3]; object[] b = F1(a); b.Report(); long?[] c = F2(a); c.Report(); object[] d = F3<int, object>(a); d.Report(); } static object[] F1(int[] a) => [..a]; static long?[] F2(int[] a) => [..a]; static U[] F3<T, U>(T[] a) where T : U => [..a]; } """; var verifier = CompileAndVerify(new[] { source, s_collectionExtensions }, expectedOutput: "[1, 2, 3], [1, 2, 3], [1, 2, 3], "); verifier.VerifyIL("Program.F1", """ { // Code size 48 (0x30) .maxstack 3 .locals init (int V_0, object[] V_1, int[] V_2, int V_3, int V_4) IL_0000: ldarg.0 IL_0001: ldc.i4.0 IL_0002: stloc.0 IL_0003: dup IL_0004: ldlen IL_0005: conv.i4 IL_0006: newarr "object" IL_000b: stloc.1 IL_000c: stloc.2 IL_000d: ldc.i4.0 IL_000e: stloc.3 IL_000f: br.s IL_0028 IL_0011: ldloc.2 IL_0012: ldloc.3 IL_0013: ldelem.i4 IL_0014: stloc.s V_4 IL_0016: ldloc.1 IL_0017: ldloc.0 IL_0018: ldloc.s V_4 IL_001a: box "int" IL_001f: stelem.ref IL_0020: ldloc.0 IL_0021: ldc.i4.1 IL_0022: add IL_0023: stloc.0 IL_0024: ldloc.3 IL_0025: ldc.i4.1 IL_0026: add IL_0027: stloc.3 IL_0028: ldloc.3 IL_0029: ldloc.2 IL_002a: ldlen IL_002b: conv.i4 IL_002c: blt.s IL_0011 IL_002e: ldloc.1 IL_002f: ret } """); verifier.VerifyIL("Program.F2", """ { // Code size 53 (0x35) .maxstack 3 .locals init (int V_0, long?[] V_1, int[] V_2, int V_3, int V_4) IL_0000: ldarg.0 IL_0001: ldc.i4.0 IL_0002: stloc.0 IL_0003: dup IL_0004: ldlen IL_0005: conv.i4 IL_0006: newarr "long?" IL_000b: stloc.1 IL_000c: stloc.2 IL_000d: ldc.i4.0 IL_000e: stloc.3 IL_000f: br.s IL_002d IL_0011: ldloc.2 IL_0012: ldloc.3 IL_0013: ldelem.i4 IL_0014: stloc.s V_4 IL_0016: ldloc.1 IL_0017: ldloc.0 IL_0018: ldloc.s V_4 IL_001a: conv.i8 IL_001b: newobj "long?..ctor(long)" IL_0020: stelem "long?" IL_0025: ldloc.0 IL_0026: ldc.i4.1 IL_0027: add IL_0028: stloc.0 IL_0029: ldloc.3 IL_002a: ldc.i4.1 IL_002b: add IL_002c: stloc.3 IL_002d: ldloc.3 IL_002e: ldloc.2 IL_002f: ldlen IL_0030: conv.i4 IL_0031: blt.s IL_0011 IL_0033: ldloc.1 IL_0034: ret } """); verifier.VerifyIL("Program.F3<T, U>", """ { // Code size 61 (0x3d) .maxstack 3 .locals init (int V_0, U[] V_1, T[] V_2, int V_3, T V_4) IL_0000: ldarg.0 IL_0001: ldc.i4.0 IL_0002: stloc.0 IL_0003: dup IL_0004: ldlen IL_0005: conv.i4 IL_0006: newarr "U" IL_000b: stloc.1 IL_000c: stloc.2 IL_000d: ldc.i4.0 IL_000e: stloc.3 IL_000f: br.s IL_0035 IL_0011: ldloc.2 IL_0012: ldloc.3 IL_0013: ldelem "T" IL_0018: stloc.s V_4 IL_001a: ldloc.1 IL_001b: ldloc.0 IL_001c: ldloc.s V_4 IL_001e: box "T" IL_0023: unbox.any "U" IL_0028: stelem "U" IL_002d: ldloc.0 IL_002e: ldc.i4.1 IL_002f: add IL_0030: stloc.0 IL_0031: ldloc.3 IL_0032: ldc.i4.1 IL_0033: add IL_0034: stloc.3 IL_0035: ldloc.3 IL_0036: ldloc.2 IL_0037: ldlen IL_0038: conv.i4 IL_0039: blt.s IL_0011 IL_003b: ldloc.1 IL_003c: ret } """); } [Theory] [InlineData("List")] [InlineData("Span")] [InlineData("ReadOnlySpan")] public void SpreadElement_09(string collectionType) { string source = $$""" using System; using System.Collections.Generic; class Program { static void Main() { {{collectionType}}<int> a = [1, 2, 3]; F1(a); F2<int, object>(a); } static void F1({{collectionType}}<int> a) { {{collectionType}}<object> b = [..a]; b.Report(); } static void F2<T, U>({{collectionType}}<T> a) where T : U { {{collectionType}}<U> b = [..a]; b.Report(); } } """; CompileAndVerify( new[] { source, s_collectionExtensionsWithSpan }, targetFramework: TargetFramework.Net70, verify: Verification.Skipped, expectedOutput: IncludeExpectedOutput("[1, 2, 3], [1, 2, 3], ")); } [Fact] public void SpreadElement_10() { string source = """ using System.Collections; class Program { static void Main() { IEnumerable a = new[] { 1, 2, 3 }; object[] b = [..a, 4]; b.Report(); } } """; CompileAndVerify(new[] { source, s_collectionExtensions }, expectedOutput: "[1, 2, 3, 4], "); } [Fact] public void SpreadElement_11() { string source = """ using System.Collections; class Program { static void Main() { F([1, 2, 3]); } static int[] F(IEnumerable s) => [..s]; } """; var comp = CreateCompilation(source); comp.VerifyEmitDiagnostics( // (6,11): error CS9174: Cannot initialize type 'IEnumerable' with a collection expression because the type is not constructible. // F([1, 2, 3]); Diagnostic(ErrorCode.ERR_CollectionExpressionTargetTypeNotConstructible, "[1, 2, 3]").WithArguments("System.Collections.IEnumerable").WithLocation(6, 11), // (8,41): error CS0029: Cannot implicitly convert type 'object' to 'int' // static int[] F(IEnumerable s) => [..s]; Diagnostic(ErrorCode.ERR_NoImplicitConv, "s").WithArguments("object", "int").WithLocation(8, 41)); } [Theory] [InlineData("object[]")] [InlineData("MyList<object>")] [InlineData("int[]")] [InlineData("MyList<int>")] public void SpreadElement_Dynamic_01_DynamicBinding(string resultType) { string source = $$""" using System.Collections.Generic; class Program { static {{resultType}} F(List<dynamic> e) { return [..e]; } static void Main() { var a = F([1, 2, 3]); a.Report(); } } namespace System.Collections.Generic { class MyList<T> : List<T> { public new void Add(T x) => base.Add(x); public void Add(string x) => throw null; } } """; var verifier = CompileAndVerify(new[] { source, s_collectionExtensions }, references: new[] { CSharpRef }, options: TestOptions.ReleaseExe, expectedOutput: "[1, 2, 3], "); if (resultType == "int[]") { verifier.VerifyIL("Program.F", """ { // Code size 129 (0x81) .maxstack 5 .locals init (int V_0, int[] V_1, System.Collections.Generic.List<dynamic>.Enumerator V_2, object V_3) IL_0000: ldarg.0 IL_0001: ldc.i4.0 IL_0002: stloc.0 IL_0003: dup IL_0004: callvirt "int System.Collections.Generic.List<dynamic>.Count.get" IL_0009: newarr "int" IL_000e: stloc.1 IL_000f: callvirt "System.Collections.Generic.List<dynamic>.Enumerator System.Collections.Generic.List<dynamic>.GetEnumerator()" IL_0014: stloc.2 .try { IL_0015: br.s IL_0066 IL_0017: ldloca.s V_2 IL_0019: call "dynamic System.Collections.Generic.List<dynamic>.Enumerator.Current.get" IL_001e: stloc.3 IL_001f: ldloc.1 IL_0020: ldloc.0 IL_0021: ldsfld "System.Runtime.CompilerServices.CallSite<System.Func<System.Runtime.CompilerServices.CallSite, dynamic, int>> Program.<>o__0.<>p__0" IL_0026: brtrue.s IL_004c IL_0028: ldc.i4.0 IL_0029: ldtoken "int" IL_002e: call "System.Type System.Type.GetTypeFromHandle(System.RuntimeTypeHandle)" IL_0033: ldtoken "Program" IL_0038: call "System.Type System.Type.GetTypeFromHandle(System.RuntimeTypeHandle)" IL_003d: call "System.Runtime.CompilerServices.CallSiteBinder Microsoft.CSharp.RuntimeBinder.Binder.Convert(Microsoft.CSharp.RuntimeBinder.CSharpBinderFlags, System.Type, System.Type)" IL_0042: call "System.Runtime.CompilerServices.CallSite<System.Func<System.Runtime.CompilerServices.CallSite, dynamic, int>> System.Runtime.CompilerServices.CallSite<System.Func<System.Runtime.CompilerServices.CallSite, dynamic, int>>.Create(System.Runtime.CompilerServices.CallSiteBinder)" IL_0047: stsfld "System.Runtime.CompilerServices.CallSite<System.Func<System.Runtime.CompilerServices.CallSite, dynamic, int>> Program.<>o__0.<>p__0" IL_004c: ldsfld "System.Runtime.CompilerServices.CallSite<System.Func<System.Runtime.CompilerServices.CallSite, dynamic, int>> Program.<>o__0.<>p__0" IL_0051: ldfld "System.Func<System.Runtime.CompilerServices.CallSite, dynamic, int> System.Runtime.CompilerServices.CallSite<System.Func<System.Runtime.CompilerServices.CallSite, dynamic, int>>.Target" IL_0056: ldsfld "System.Runtime.CompilerServices.CallSite<System.Func<System.Runtime.CompilerServices.CallSite, dynamic, int>> Program.<>o__0.<>p__0" IL_005b: ldloc.3 IL_005c: callvirt "int System.Func<System.Runtime.CompilerServices.CallSite, dynamic, int>.Invoke(System.Runtime.CompilerServices.CallSite, dynamic)" IL_0061: stelem.i4 IL_0062: ldloc.0 IL_0063: ldc.i4.1 IL_0064: add IL_0065: stloc.0 IL_0066: ldloca.s V_2 IL_0068: call "bool System.Collections.Generic.List<dynamic>.Enumerator.MoveNext()" IL_006d: brtrue.s IL_0017 IL_006f: leave.s IL_007f } finally { IL_0071: ldloca.s V_2 IL_0073: constrained. "System.Collections.Generic.List<dynamic>.Enumerator" IL_0079: callvirt "void System.IDisposable.Dispose()" IL_007e: endfinally } IL_007f: ldloc.1 IL_0080: ret } """); } else if (resultType == "MyList<object>") { verifier.VerifyIL("Program.F", """ { // Code size 141 (0x8d) .maxstack 9 .locals init (System.Collections.Generic.MyList<object> V_0, System.Collections.Generic.List<dynamic>.Enumerator V_1, object V_2) IL_0000: newobj "System.Collections.Generic.MyList<object>..ctor()" IL_0005: stloc.0 IL_0006: ldarg.0 IL_0007: callvirt "System.Collections.Generic.List<dynamic>.Enumerator System.Collections.Generic.List<dynamic>.GetEnumerator()" IL_000c: stloc.1 .try { IL_000d: br.s IL_0072 IL_000f: ldloca.s V_1 IL_0011: call "dynamic System.Collections.Generic.List<dynamic>.Enumerator.Current.get" IL_0016: stloc.2 IL_0017: ldsfld "System.Runtime.CompilerServices.CallSite<System.Action<System.Runtime.CompilerServices.CallSite, System.Collections.Generic.MyList<object>, dynamic>> Program.<>o__0.<>p__0" IL_001c: brtrue.s IL_005c IL_001e: ldc.i4 0x100 IL_0023: ldstr "Add" IL_0028: ldnull IL_0029: ldtoken "Program" IL_002e: call "System.Type System.Type.GetTypeFromHandle(System.RuntimeTypeHandle)" IL_0033: ldc.i4.2 IL_0034: newarr "Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo" IL_0039: dup IL_003a: ldc.i4.0 IL_003b: ldc.i4.1 IL_003c: ldnull IL_003d: call "Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo.Create(Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfoFlags, string)" IL_0042: stelem.ref IL_0043: dup IL_0044: ldc.i4.1 IL_0045: ldc.i4.0 IL_0046: ldnull IL_0047: call "Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo.Create(Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfoFlags, string)" IL_004c: stelem.ref IL_004d: call "System.Runtime.CompilerServices.CallSiteBinder Microsoft.CSharp.RuntimeBinder.Binder.InvokeMember(Microsoft.CSharp.RuntimeBinder.CSharpBinderFlags, string, System.Collections.Generic.IEnumerable<System.Type>, System.Type, System.Collections.Generic.IEnumerable<Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo>)" IL_0052: call "System.Runtime.CompilerServices.CallSite<System.Action<System.Runtime.CompilerServices.CallSite, System.Collections.Generic.MyList<object>, dynamic>> System.Runtime.CompilerServices.CallSite<System.Action<System.Runtime.CompilerServices.CallSite, System.Collections.Generic.MyList<object>, dynamic>>.Create(System.Runtime.CompilerServices.CallSiteBinder)" IL_0057: stsfld "System.Runtime.CompilerServices.CallSite<System.Action<System.Runtime.CompilerServices.CallSite, System.Collections.Generic.MyList<object>, dynamic>> Program.<>o__0.<>p__0" IL_005c: ldsfld "System.Runtime.CompilerServices.CallSite<System.Action<System.Runtime.CompilerServices.CallSite, System.Collections.Generic.MyList<object>, dynamic>> Program.<>o__0.<>p__0" IL_0061: ldfld "System.Action<System.Runtime.CompilerServices.CallSite, System.Collections.Generic.MyList<object>, dynamic> System.Runtime.CompilerServices.CallSite<System.Action<System.Runtime.CompilerServices.CallSite, System.Collections.Generic.MyList<object>, dynamic>>.Target" IL_0066: ldsfld "System.Runtime.CompilerServices.CallSite<System.Action<System.Runtime.CompilerServices.CallSite, System.Collections.Generic.MyList<object>, dynamic>> Program.<>o__0.<>p__0" IL_006b: ldloc.0 IL_006c: ldloc.2 IL_006d: callvirt "void System.Action<System.Runtime.CompilerServices.CallSite, System.Collections.Generic.MyList<object>, dynamic>.Invoke(System.Runtime.CompilerServices.CallSite, System.Collections.Generic.MyList<object>, dynamic)" IL_0072: ldloca.s V_1 IL_0074: call "bool System.Collections.Generic.List<dynamic>.Enumerator.MoveNext()" IL_0079: brtrue.s IL_000f IL_007b: leave.s IL_008b } finally { IL_007d: ldloca.s V_1 IL_007f: constrained. "System.Collections.Generic.List<dynamic>.Enumerator" IL_0085: callvirt "void System.IDisposable.Dispose()" IL_008a: endfinally } IL_008b: ldloc.0 IL_008c: ret } """); } else if (resultType == "MyList<int>") { verifier.VerifyIL("Program.F", """ { // Code size 141 (0x8d) .maxstack 9 .locals init (System.Collections.Generic.MyList<int> V_0, System.Collections.Generic.List<dynamic>.Enumerator V_1, object V_2) IL_0000: newobj "System.Collections.Generic.MyList<int>..ctor()" IL_0005: stloc.0 IL_0006: ldarg.0 IL_0007: callvirt "System.Collections.Generic.List<dynamic>.Enumerator System.Collections.Generic.List<dynamic>.GetEnumerator()" IL_000c: stloc.1 .try { IL_000d: br.s IL_0072 IL_000f: ldloca.s V_1 IL_0011: call "dynamic System.Collections.Generic.List<dynamic>.Enumerator.Current.get" IL_0016: stloc.2 IL_0017: ldsfld "System.Runtime.CompilerServices.CallSite<System.Action<System.Runtime.CompilerServices.CallSite, System.Collections.Generic.MyList<int>, dynamic>> Program.<>o__0.<>p__0" IL_001c: brtrue.s IL_005c IL_001e: ldc.i4 0x100 IL_0023: ldstr "Add" IL_0028: ldnull IL_0029: ldtoken "Program" IL_002e: call "System.Type System.Type.GetTypeFromHandle(System.RuntimeTypeHandle)" IL_0033: ldc.i4.2 IL_0034: newarr "Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo" IL_0039: dup IL_003a: ldc.i4.0 IL_003b: ldc.i4.1 IL_003c: ldnull IL_003d: call "Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo.Create(Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfoFlags, string)" IL_0042: stelem.ref IL_0043: dup IL_0044: ldc.i4.1 IL_0045: ldc.i4.0 IL_0046: ldnull IL_0047: call "Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo.Create(Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfoFlags, string)" IL_004c: stelem.ref IL_004d: call "System.Runtime.CompilerServices.CallSiteBinder Microsoft.CSharp.RuntimeBinder.Binder.InvokeMember(Microsoft.CSharp.RuntimeBinder.CSharpBinderFlags, string, System.Collections.Generic.IEnumerable<System.Type>, System.Type, System.Collections.Generic.IEnumerable<Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo>)" IL_0052: call "System.Runtime.CompilerServices.CallSite<System.Action<System.Runtime.CompilerServices.CallSite, System.Collections.Generic.MyList<int>, dynamic>> System.Runtime.CompilerServices.CallSite<System.Action<System.Runtime.CompilerServices.CallSite, System.Collections.Generic.MyList<int>, dynamic>>.Create(System.Runtime.CompilerServices.CallSiteBinder)" IL_0057: stsfld "System.Runtime.CompilerServices.CallSite<System.Action<System.Runtime.CompilerServices.CallSite, System.Collections.Generic.MyList<int>, dynamic>> Program.<>o__0.<>p__0" IL_005c: ldsfld "System.Runtime.CompilerServices.CallSite<System.Action<System.Runtime.CompilerServices.CallSite, System.Collections.Generic.MyList<int>, dynamic>> Program.<>o__0.<>p__0" IL_0061: ldfld "System.Action<System.Runtime.CompilerServices.CallSite, System.Collections.Generic.MyList<int>, dynamic> System.Runtime.CompilerServices.CallSite<System.Action<System.Runtime.CompilerServices.CallSite, System.Collections.Generic.MyList<int>, dynamic>>.Target" IL_0066: ldsfld "System.Runtime.CompilerServices.CallSite<System.Action<System.Runtime.CompilerServices.CallSite, System.Collections.Generic.MyList<int>, dynamic>> Program.<>o__0.<>p__0" IL_006b: ldloc.0 IL_006c: ldloc.2 IL_006d: callvirt "void System.Action<System.Runtime.CompilerServices.CallSite, System.Collections.Generic.MyList<int>, dynamic>.Invoke(System.Runtime.CompilerServices.CallSite, System.Collections.Generic.MyList<int>, dynamic)" IL_0072: ldloca.s V_1 IL_0074: call "bool System.Collections.Generic.List<dynamic>.Enumerator.MoveNext()" IL_0079: brtrue.s IL_000f IL_007b: leave.s IL_008b } finally { IL_007d: ldloca.s V_1 IL_007f: constrained. "System.Collections.Generic.List<dynamic>.Enumerator" IL_0085: callvirt "void System.IDisposable.Dispose()" IL_008a: endfinally } IL_008b: ldloc.0 IL_008c: ret } """); } else { Assert.Equal("object[]", resultType); } } [Theory] [InlineData("List<object>")] [InlineData("List<int>")] public void SpreadElement_Dynamic_01_StaticBinding(string resultType) { string source = $$""" using System.Collections.Generic; class Program { static {{resultType}} F(List<dynamic> e) { return [..e]; } static void Main() { var a = F([1, 2, 3]); a.Report(); } } """; var verifier = CompileAndVerify(new[] { source, s_collectionExtensions }, references: new[] { CSharpRef }, options: TestOptions.ReleaseExe, expectedOutput: "[1, 2, 3], "); if (resultType == "List<object>") { verifier.VerifyIL("Program.F", """ { // Code size 7 (0x7) .maxstack 1 IL_0000: ldarg.0 IL_0001: call "System.Collections.Generic.List<object> System.Linq.Enumerable.ToList<object>(System.Collections.Generic.IEnumerable<object>)" IL_0006: ret } """); } else { Assert.Equal("List<int>", resultType); verifier.VerifyIL("Program.F", """ { // Code size 141 (0x8d) .maxstack 9 .locals init (System.Collections.Generic.List<int> V_0, System.Collections.Generic.List<dynamic>.Enumerator V_1, object V_2) IL_0000: newobj "System.Collections.Generic.List<int>..ctor()" IL_0005: stloc.0 IL_0006: ldarg.0 IL_0007: callvirt "System.Collections.Generic.List<dynamic>.Enumerator System.Collections.Generic.List<dynamic>.GetEnumerator()" IL_000c: stloc.1 .try { IL_000d: br.s IL_0072 IL_000f: ldloca.s V_1 IL_0011: call "dynamic System.Collections.Generic.List<dynamic>.Enumerator.Current.get" IL_0016: stloc.2 IL_0017: ldsfld "System.Runtime.CompilerServices.CallSite<System.Action<System.Runtime.CompilerServices.CallSite, System.Collections.Generic.List<int>, dynamic>> Program.<>o__0.<>p__0" IL_001c: brtrue.s IL_005c IL_001e: ldc.i4 0x100 IL_0023: ldstr "Add" IL_0028: ldnull IL_0029: ldtoken "Program" IL_002e: call "System.Type System.Type.GetTypeFromHandle(System.RuntimeTypeHandle)" IL_0033: ldc.i4.2 IL_0034: newarr "Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo" IL_0039: dup IL_003a: ldc.i4.0 IL_003b: ldc.i4.1 IL_003c: ldnull IL_003d: call "Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo.Create(Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfoFlags, string)" IL_0042: stelem.ref IL_0043: dup IL_0044: ldc.i4.1 IL_0045: ldc.i4.0 IL_0046: ldnull IL_0047: call "Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo.Create(Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfoFlags, string)" IL_004c: stelem.ref IL_004d: call "System.Runtime.CompilerServices.CallSiteBinder Microsoft.CSharp.RuntimeBinder.Binder.InvokeMember(Microsoft.CSharp.RuntimeBinder.CSharpBinderFlags, string, System.Collections.Generic.IEnumerable<System.Type>, System.Type, System.Collections.Generic.IEnumerable<Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo>)" IL_0052: call "System.Runtime.CompilerServices.CallSite<System.Action<System.Runtime.CompilerServices.CallSite, System.Collections.Generic.List<int>, dynamic>> System.Runtime.CompilerServices.CallSite<System.Action<System.Runtime.CompilerServices.CallSite, System.Collections.Generic.List<int>, dynamic>>.Create(System.Runtime.CompilerServices.CallSiteBinder)" IL_0057: stsfld "System.Runtime.CompilerServices.CallSite<System.Action<System.Runtime.CompilerServices.CallSite, System.Collections.Generic.List<int>, dynamic>> Program.<>o__0.<>p__0" IL_005c: ldsfld "System.Runtime.CompilerServices.CallSite<System.Action<System.Runtime.CompilerServices.CallSite, System.Collections.Generic.List<int>, dynamic>> Program.<>o__0.<>p__0" IL_0061: ldfld "System.Action<System.Runtime.CompilerServices.CallSite, System.Collections.Generic.List<int>, dynamic> System.Runtime.CompilerServices.CallSite<System.Action<System.Runtime.CompilerServices.CallSite, System.Collections.Generic.List<int>, dynamic>>.Target" IL_0066: ldsfld "System.Runtime.CompilerServices.CallSite<System.Action<System.Runtime.CompilerServices.CallSite, System.Collections.Generic.List<int>, dynamic>> Program.<>o__0.<>p__0" IL_006b: ldloc.0 IL_006c: ldloc.2 IL_006d: callvirt "void System.Action<System.Runtime.CompilerServices.CallSite, System.Collections.Generic.List<int>, dynamic>.Invoke(System.Runtime.CompilerServices.CallSite, System.Collections.Generic.List<int>, dynamic)" IL_0072: ldloca.s V_1 IL_0074: call "bool System.Collections.Generic.List<dynamic>.Enumerator.MoveNext()" IL_0079: brtrue.s IL_000f IL_007b: leave.s IL_008b } finally { IL_007d: ldloca.s V_1 IL_007f: constrained. "System.Collections.Generic.List<dynamic>.Enumerator" IL_0085: callvirt "void System.IDisposable.Dispose()" IL_008a: endfinally } IL_008b: ldloc.0 IL_008c: ret } """); } } [Fact] public void SpreadElement_12() { string source = """ class Program { static void Main() { object x = new[] { 2, 3 }; int[] y = [1, ..x]; } } """; var comp = CreateCompilation(source); comp.VerifyEmitDiagnostics( // (6,25): error CS9212: Spread operator '..' cannot operate on variables of type 'object' because 'object' does not contain a public instance or extension definition for 'GetEnumerator' // int[] y = [1, ..x]; Diagnostic(ErrorCode.ERR_SpreadMissingMember, "x").WithArguments("object", "GetEnumerator").WithLocation(6, 25)); } [Fact] public void SpreadElement_13() { string source = """ class Program { static void Main() { int[] x = [..null]; } } """; var comp = CreateCompilation(source); comp.VerifyEmitDiagnostics( // (5,22): error CS0186: Use of null is not valid in this context // int[] x = [..null]; Diagnostic(ErrorCode.ERR_NullNotValid, "null").WithLocation(5, 22)); } [Fact] public void SpreadElement_14() { string source = """ class Program { static void Main() { int[] x = [..Main]; } } """; var comp = CreateCompilation(source); comp.VerifyEmitDiagnostics( // (5,22): error CS0446: Foreach cannot operate on a 'method group'. Did you intend to invoke the 'method group'? // int[] x = [..Main]; Diagnostic(ErrorCode.ERR_AnonMethGrpInForEach, "Main").WithArguments("method group").WithLocation(5, 22)); } [Fact] public void SpreadElement_15() { // Optimization: pass ReadOnlySpan directly to collection builder method without copying string source = """ using System; using System.Collections; using System.Collections.Generic; using System.Runtime.CompilerServices; [CollectionBuilder(typeof(MyCollectionBuilder), nameof(MyCollectionBuilder.Create))] class MyCollection<T> : IEnumerable<T> { private readonly T[] _arr; public IEnumerator<T> GetEnumerator() => ((IEnumerable<T>)_arr).GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); public MyCollection(T[] arr) { _arr = arr; } } class MyCollectionBuilder { public static MyCollection<T> Create<T>(ReadOnlySpan<T> span) => new(span.ToArray()); } class Program { static void Main() { M([1, 2, 3]).Report(); } static MyCollection<int> M(ReadOnlySpan<int> span) => [.. span]; } """; var verifier = CompileAndVerify([source, s_collectionExtensions], targetFramework: TargetFramework.Net80, expectedOutput: IncludeExpectedOutput("[1, 2, 3], "), verify: Verification.Skipped); verifier.VerifyDiagnostics(); verifier.VerifyIL("Program.M", """ { // Code size 7 (0x7) .maxstack 1 IL_0000: ldarg.0 IL_0001: call "MyCollection<int> MyCollectionBuilder.Create<int>(System.ReadOnlySpan<int>)" IL_0006: ret } """); } [Fact] public void SpreadElement_16() { // Spread operand element type differs from result collection element type by CLR signature string source = """ using System; using System.Collections; using System.Collections.Generic; using System.Runtime.CompilerServices; [CollectionBuilder(typeof(MyCollectionBuilder), nameof(MyCollectionBuilder.Create))] class MyCollection<T> : IEnumerable<T> { private readonly T[] _arr; public IEnumerator<T> GetEnumerator() => ((IEnumerable<T>)_arr).GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); public MyCollection(T[] arr) { _arr = arr; } } class MyCollectionBuilder { public static MyCollection<T> Create<T>(ReadOnlySpan<T> span) => new(span.ToArray()); } class Program { static void Main() { M([1, 2, 3]).Report(); } static MyCollection<object> M(ReadOnlySpan<int> span) => [.. span]; } """; var verifier = CompileAndVerify([source, s_collectionExtensions], targetFramework: TargetFramework.Net80, expectedOutput: IncludeExpectedOutput("[1, 2, 3], "), verify: Verification.Skipped); verifier.VerifyDiagnostics(); verifier.VerifyIL("Program.M", """ { // Code size 72 (0x48) .maxstack 3 .locals init (System.ReadOnlySpan<int> V_0, int V_1, object[] V_2, System.ReadOnlySpan<int>.Enumerator V_3, int V_4) IL_0000: ldarg.0 IL_0001: stloc.0 IL_0002: ldc.i4.0 IL_0003: stloc.1 IL_0004: ldloca.s V_0 IL_0006: call "int System.ReadOnlySpan<int>.Length.get" IL_000b: newarr "object" IL_0010: stloc.2 IL_0011: ldloca.s V_0 IL_0013: call "System.ReadOnlySpan<int>.Enumerator System.ReadOnlySpan<int>.GetEnumerator()" IL_0018: stloc.3 IL_0019: br.s IL_0033 IL_001b: ldloca.s V_3 IL_001d: call "ref readonly int System.ReadOnlySpan<int>.Enumerator.Current.get" IL_0022: ldind.i4 IL_0023: stloc.s V_4 IL_0025: ldloc.2 IL_0026: ldloc.1 IL_0027: ldloc.s V_4 IL_0029: box "int" IL_002e: stelem.ref IL_002f: ldloc.1 IL_0030: ldc.i4.1 IL_0031: add IL_0032: stloc.1 IL_0033: ldloca.s V_3 IL_0035: call "bool System.ReadOnlySpan<int>.Enumerator.MoveNext()" IL_003a: brtrue.s IL_001b IL_003c: ldloc.2 IL_003d: newobj "System.ReadOnlySpan<object>..ctor(object[])" IL_0042: call "MyCollection<object> MyCollectionBuilder.Create<object>(System.ReadOnlySpan<object>)" IL_0047: ret } """); } [Fact] public void SpreadElement_17() { // 'dynamic' and 'object' are the same things during runtime, so apply 'no-copy' optimization string source = """ using System; using System.Collections; using System.Collections.Generic; using System.Runtime.CompilerServices; [CollectionBuilder(typeof(MyCollectionBuilder), nameof(MyCollectionBuilder.Create))] class MyCollection<T> : IEnumerable<T> { private readonly T[] _arr; public IEnumerator<T> GetEnumerator() => ((IEnumerable<T>)_arr).GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); public MyCollection(T[] arr) { _arr = arr; } } class MyCollectionBuilder { public static MyCollection<T> Create<T>(ReadOnlySpan<T> span) => new(span.ToArray()); } class Program { static void Main() { M([1, 2, 3]).Report(); } static MyCollection<object> M(ReadOnlySpan<dynamic> span) => [.. span]; } """; var verifier = CompileAndVerify([source, s_collectionExtensions], targetFramework: TargetFramework.Net80, expectedOutput: IncludeExpectedOutput("[1, 2, 3], "), verify: Verification.Skipped); verifier.VerifyDiagnostics(); verifier.VerifyIL("Program.M", """ { // Code size 7 (0x7) .maxstack 1 IL_0000: ldarg.0 IL_0001: call "MyCollection<object> MyCollectionBuilder.Create<object>(System.ReadOnlySpan<object>)" IL_0006: ret } """); } [Fact] public void SpreadElement_18() { // Tuple element names only have effect in the code, // thus '(int A, int B)' and just '(int, int)' are the same thing, // so apply 'no-copy' optimization here as well string source = """ using System; using System.Collections; using System.Collections.Generic; using System.Runtime.CompilerServices; [CollectionBuilder(typeof(MyCollectionBuilder), nameof(MyCollectionBuilder.Create))] class MyCollection<T> : IEnumerable<T> { private readonly T[] _arr; public IEnumerator<T> GetEnumerator() => ((IEnumerable<T>)_arr).GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); public MyCollection(T[] arr) { _arr = arr; } } class MyCollectionBuilder { public static MyCollection<T> Create<T>(ReadOnlySpan<T> span) => new(span.ToArray()); } class Program { static void Main() { M([(1, 1), (2, 2), (3, 3)]).Report(); } static MyCollection<(int A, int B)> M(ReadOnlySpan<(int, int)> span) => [.. span]; } """; var verifier = CompileAndVerify([source, s_collectionExtensions], targetFramework: TargetFramework.Net80, expectedOutput: IncludeExpectedOutput("[(1, 1), (2, 2), (3, 3)], "), verify: Verification.Skipped); verifier.VerifyDiagnostics(); verifier.VerifyIL("Program.M", """ { // Code size 7 (0x7) .maxstack 1 IL_0000: ldarg.0 IL_0001: call "MyCollection<System.ValueTuple<int, int>> MyCollectionBuilder.Create<System.ValueTuple<int, int>>(System.ReadOnlySpan<System.ValueTuple<int, int>>)" IL_0006: ret } """); } [Fact] public void SpreadElement_19() { // Spread operand element type differs from result collection element type by CLR signature string source = """ using System; using System.Collections; using System.Collections.Generic; using System.Runtime.CompilerServices; [CollectionBuilder(typeof(MyCollectionBuilder), nameof(MyCollectionBuilder.Create))] class MyCollection<T> : IEnumerable<T> { private readonly T[] _arr; public IEnumerator<T> GetEnumerator() => ((IEnumerable<T>)_arr).GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); public MyCollection(T[] arr) { _arr = arr; } } class MyCollectionBuilder { public static MyCollection<T> Create<T>(ReadOnlySpan<T> span) => new(span.ToArray()); } class Program { static void Main() { M([(1, 1), (2, 2), (3, 3)]).Report(); } static MyCollection<(object, object)> M(ReadOnlySpan<(int, int)> span) => [.. span]; } """; var verifier = CompileAndVerify([source, s_collectionExtensions], targetFramework: TargetFramework.Net80, expectedOutput: IncludeExpectedOutput("[(1, 1), (2, 2), (3, 3)], "), verify: Verification.Skipped); verifier.VerifyDiagnostics(); verifier.VerifyIL("Program.M", """ { // Code size 106 (0x6a) .maxstack 4 .locals init (System.ReadOnlySpan<System.ValueTuple<int, int>> V_0, int V_1, System.ValueTuple<object, object>[] V_2, System.ReadOnlySpan<System.ValueTuple<int, int>>.Enumerator V_3, System.ValueTuple<int, int> V_4, System.ValueTuple<int, int> V_5) IL_0000: ldarg.0 IL_0001: stloc.0 IL_0002: ldc.i4.0 IL_0003: stloc.1 IL_0004: ldloca.s V_0 IL_0006: call "int System.ReadOnlySpan<System.ValueTuple<int, int>>.Length.get" IL_000b: newarr "System.ValueTuple<object, object>" IL_0010: stloc.2 IL_0011: ldloca.s V_0 IL_0013: call "System.ReadOnlySpan<System.ValueTuple<int, int>>.Enumerator System.ReadOnlySpan<System.ValueTuple<int, int>>.GetEnumerator()" IL_0018: stloc.3 IL_0019: br.s IL_0055 IL_001b: ldloca.s V_3 IL_001d: call "ref readonly System.ValueTuple<int, int> System.ReadOnlySpan<System.ValueTuple<int, int>>.Enumerator.Current.get" IL_0022: ldobj "System.ValueTuple<int, int>" IL_0027: stloc.s V_4 IL_0029: ldloc.2 IL_002a: ldloc.1 IL_002b: ldloc.s V_4 IL_002d: stloc.s V_5 IL_002f: ldloc.s V_5 IL_0031: ldfld "int System.ValueTuple<int, int>.Item1" IL_0036: box "int" IL_003b: ldloc.s V_5 IL_003d: ldfld "int System.ValueTuple<int, int>.Item2" IL_0042: box "int" IL_0047: newobj "System.ValueTuple<object, object>..ctor(object, object)" IL_004c: stelem "System.ValueTuple<object, object>" IL_0051: ldloc.1 IL_0052: ldc.i4.1 IL_0053: add IL_0054: stloc.1 IL_0055: ldloca.s V_3 IL_0057: call "bool System.ReadOnlySpan<System.ValueTuple<int, int>>.Enumerator.MoveNext()" IL_005c: brtrue.s IL_001b IL_005e: ldloc.2 IL_005f: newobj "System.ReadOnlySpan<System.ValueTuple<object, object>>..ctor(System.ValueTuple<object, object>[])" IL_0064: call "MyCollection<System.ValueTuple<object, object>> MyCollectionBuilder.Create<System.ValueTuple<object, object>>(System.ReadOnlySpan<System.ValueTuple<object, object>>)" IL_0069: ret } """); } [Fact] public void SpreadElement_20() { // Nullability annotations only affect diagnostics and have no effect on the runtime, // so 'T?[]' and 'T[]' are the same times, therefore apply 'no-copy' optimization here string source = """ #nullable enable using System; using System.Collections; using System.Collections.Generic; using System.Runtime.CompilerServices; [CollectionBuilder(typeof(MyCollectionBuilder), nameof(MyCollectionBuilder.Create))] class MyCollection<T> : IEnumerable<T> { private readonly T[] _arr; public IEnumerator<T> GetEnumerator() => ((IEnumerable<T>)_arr).GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); public MyCollection(T[] arr) { _arr = arr; } } class MyCollectionBuilder { public static MyCollection<T> Create<T>(ReadOnlySpan<T> span) => new(span.ToArray()); } class Program { static void Main() { M([[1], [2], [3]]).Report(); } static MyCollection<T?[]> M<T>(ReadOnlySpan<T[]> span) => [.. span]; } """; var verifier = CompileAndVerify([source, s_collectionExtensions], targetFramework: TargetFramework.Net80, expectedOutput: IncludeExpectedOutput("[[1], [2], [3]], "), verify: Verification.Skipped); verifier.VerifyDiagnostics(); verifier.VerifyIL("Program.M<T>", """ { // Code size 7 (0x7) .maxstack 1 IL_0000: ldarg.0 IL_0001: call "MyCollection<T[]> MyCollectionBuilder.Create<T[]>(System.ReadOnlySpan<T[]>)" IL_0006: ret } """); } [Fact] public void SpreadElement_Dynamic_02() { string source = """ class Program { static void Main() { dynamic x = new[] { 2, 3 }; object[] y = [1, ..x]; y.Report(); } } """; CompileAndVerify(new[] { source, s_collectionExtensions }, references: new[] { CSharpRef }, expectedOutput: "[1, 2, 3], "); } [WorkItem("https://github.com/dotnet/roslyn/issues/69704")] [Theory] [InlineData("int")] [InlineData("object")] public void SpreadElement_Untyped_ArrayTarget(string targetElementType) { string source = $$""" using System.Collections; class Program { static void Main() { dynamic d1 = new object[] { 1 }; dynamic d2 = new int[] { 2 }; IEnumerable e1 = new object[] { 3 }; IEnumerable e2 = new int[] { 4 }; {{targetElementType}}[] a = [..d1, ..d2, ..e1, ..e2]; a.Report(); } } """; var comp = CreateCompilation(new[] { source, s_collectionExtensions }, references: new[] { CSharpRef }, options: TestOptions.ReleaseExe); if (targetElementType == "int") { comp.VerifyEmitDiagnostics( // 0.cs(10,22): error CS0029: Cannot implicitly convert type 'object' to 'int' // int[] a = [..d1, ..d2, ..e1, ..e2]; Diagnostic(ErrorCode.ERR_NoImplicitConv, "d1").WithArguments("object", "int").WithLocation(10, 22), // 0.cs(10,28): error CS0029: Cannot implicitly convert type 'object' to 'int' // int[] a = [..d1, ..d2, ..e1, ..e2]; Diagnostic(ErrorCode.ERR_NoImplicitConv, "d2").WithArguments("object", "int").WithLocation(10, 28), // 0.cs(10,34): error CS0029: Cannot implicitly convert type 'object' to 'int' // int[] a = [..d1, ..d2, ..e1, ..e2]; Diagnostic(ErrorCode.ERR_NoImplicitConv, "e1").WithArguments("object", "int").WithLocation(10, 34), // 0.cs(10,40): error CS0029: Cannot implicitly convert type 'object' to 'int' // int[] a = [..d1, ..d2, ..e1, ..e2]; Diagnostic(ErrorCode.ERR_NoImplicitConv, "e2").WithArguments("object", "int").WithLocation(10, 40)); } else { CompileAndVerify(comp, expectedOutput: "[1, 2, 3, 4], "); } } [WorkItem("https://github.com/dotnet/roslyn/issues/69704")] [Theory] [CombinatorialData] public void SpreadElement_Untyped_ArrayInterfaceTarget( [CombinatorialValues("IEnumerable", "IReadOnlyList", "IList")] string targetInterfaceType, [CombinatorialValues("int", "object")] string targetElementType) { string source = $$""" using System.Collections; using System.Collections.Generic; class Program { static void Main() { dynamic d1 = new object[] { 1 }; dynamic d2 = new int[] { 2 }; IEnumerable e1 = new object[] { 3 }; IEnumerable e2 = new int[] { 4 }; {{targetInterfaceType}}<{{targetElementType}}> c = [..d1, ..d2, ..e1, ..e2]; c.Report(); } } """; var comp = CreateCompilation(new[] { source, s_collectionExtensions }, references: new[] { CSharpRef }, options: TestOptions.ReleaseExe); if (targetElementType == "int") { comp.VerifyEmitDiagnostics( // 0.cs(12,16): error CS0029: Cannot implicitly convert type 'object' to 'int' // [..d1, ..d2, ..e1, ..e2]; Diagnostic(ErrorCode.ERR_NoImplicitConv, "d1").WithArguments("object", "int").WithLocation(12, 16), // 0.cs(12,22): error CS0029: Cannot implicitly convert type 'object' to 'int' // [..d1, ..d2, ..e1, ..e2]; Diagnostic(ErrorCode.ERR_NoImplicitConv, "d2").WithArguments("object", "int").WithLocation(12, 22), // 0.cs(12,28): error CS0029: Cannot implicitly convert type 'object' to 'int' // [..d1, ..d2, ..e1, ..e2]; Diagnostic(ErrorCode.ERR_NoImplicitConv, "e1").WithArguments("object", "int").WithLocation(12, 28), // 0.cs(12,34): error CS0029: Cannot implicitly convert type 'object' to 'int' // [..d1, ..d2, ..e1, ..e2]; Diagnostic(ErrorCode.ERR_NoImplicitConv, "e2").WithArguments("object", "int").WithLocation(12, 34)); } else { CompileAndVerify(comp, expectedOutput: "[1, 2, 3, 4], "); } } [WorkItem("https://github.com/dotnet/roslyn/issues/69704")] [Theory] [InlineData("int")] [InlineData("object")] public void SpreadElement_Untyped_NonGenericCollectionInitializerTarget(string targetElementType) { string sourceA = $$""" using System.Collections; using System.Collections.Generic; class MyCollection : IEnumerable { private List<object> _list = new(); public void Add({{targetElementType}} i) { _list.Add(i); } IEnumerator IEnumerable.GetEnumerator() => _list.GetEnumerator(); } """; string sourceB = """ using System.Collections; class Program { static void Main() { dynamic d1 = new object[] { 1 }; dynamic d2 = new int[] { 2 }; IEnumerable e1 = new object[] { 3 }; IEnumerable e2 = new int[] { 4 }; MyCollection c = [..d1, ..d2, ..e1, ..e2]; c.Report(); int[] x = [5]; int[] y = [6]; c = [..(dynamic)x, ..(IEnumerable)y]; c.Report(); } } """; var comp = CreateCompilation(new[] { sourceA, sourceB, s_collectionExtensions }, references: new[] { CSharpRef }, options: TestOptions.ReleaseExe); if (targetElementType == "int") { comp.VerifyEmitDiagnostics( // 1.cs(10,27): error CS1503: Argument 1: cannot convert from 'object' to 'int' // MyCollection c = [..d1, ..d2, ..e1, ..e2]; Diagnostic(ErrorCode.ERR_BadArgType, "..d1").WithArguments("1", "object", "int").WithLocation(10, 27), // 1.cs(10,29): error CS1950: The best overloaded Add method 'MyCollection.Add(int)' for the collection initializer has some invalid arguments // MyCollection c = [..d1, ..d2, ..e1, ..e2]; Diagnostic(ErrorCode.ERR_BadArgTypesForCollectionAdd, "d1").WithArguments("MyCollection.Add(int)").WithLocation(10, 29), // 1.cs(10,33): error CS1503: Argument 1: cannot convert from 'object' to 'int' // MyCollection c = [..d1, ..d2, ..e1, ..e2]; Diagnostic(ErrorCode.ERR_BadArgType, "..d2").WithArguments("1", "object", "int").WithLocation(10, 33), // 1.cs(10,35): error CS1950: The best overloaded Add method 'MyCollection.Add(int)' for the collection initializer has some invalid arguments // MyCollection c = [..d1, ..d2, ..e1, ..e2]; Diagnostic(ErrorCode.ERR_BadArgTypesForCollectionAdd, "d2").WithArguments("MyCollection.Add(int)").WithLocation(10, 35), // 1.cs(10,39): error CS1503: Argument 1: cannot convert from 'object' to 'int' // MyCollection c = [..d1, ..d2, ..e1, ..e2]; Diagnostic(ErrorCode.ERR_BadArgType, "..e1").WithArguments("1", "object", "int").WithLocation(10, 39), // 1.cs(10,41): error CS1950: The best overloaded Add method 'MyCollection.Add(int)' for the collection initializer has some invalid arguments // MyCollection c = [..d1, ..d2, ..e1, ..e2]; Diagnostic(ErrorCode.ERR_BadArgTypesForCollectionAdd, "e1").WithArguments("MyCollection.Add(int)").WithLocation(10, 41), // 1.cs(10,45): error CS1503: Argument 1: cannot convert from 'object' to 'int' // MyCollection c = [..d1, ..d2, ..e1, ..e2]; Diagnostic(ErrorCode.ERR_BadArgType, "..e2").WithArguments("1", "object", "int").WithLocation(10, 45), // 1.cs(10,47): error CS1950: The best overloaded Add method 'MyCollection.Add(int)' for the collection initializer has some invalid arguments // MyCollection c = [..d1, ..d2, ..e1, ..e2]; Diagnostic(ErrorCode.ERR_BadArgTypesForCollectionAdd, "e2").WithArguments("MyCollection.Add(int)").WithLocation(10, 47), // 1.cs(14,14): error CS1503: Argument 1: cannot convert from 'object' to 'int' // c = [..(dynamic)x, ..(IEnumerable)y]; Diagnostic(ErrorCode.ERR_BadArgType, "..(dynamic)x").WithArguments("1", "object", "int").WithLocation(14, 14), // 1.cs(14,16): error CS1950: The best overloaded Add method 'MyCollection.Add(int)' for the collection initializer has some invalid arguments // c = [..(dynamic)x, ..(IEnumerable)y]; Diagnostic(ErrorCode.ERR_BadArgTypesForCollectionAdd, "(dynamic)x").WithArguments("MyCollection.Add(int)").WithLocation(14, 16), // 1.cs(14,28): error CS1503: Argument 1: cannot convert from 'object' to 'int' // c = [..(dynamic)x, ..(IEnumerable)y]; Diagnostic(ErrorCode.ERR_BadArgType, "..(IEnumerable)y").WithArguments("1", "object", "int").WithLocation(14, 28), // 1.cs(14,30): error CS1950: The best overloaded Add method 'MyCollection.Add(int)' for the collection initializer has some invalid arguments // c = [..(dynamic)x, ..(IEnumerable)y]; Diagnostic(ErrorCode.ERR_BadArgTypesForCollectionAdd, "(IEnumerable)y").WithArguments("MyCollection.Add(int)").WithLocation(14, 30)); } else { CompileAndVerify(comp, expectedOutput: "[1, 2, 3, 4], [5, 6], "); } } [WorkItem("https://github.com/dotnet/roslyn/issues/69704")] [Theory] [InlineData("int")] [InlineData("object")] public void SpreadElement_Untyped_GenericCollectionInitializerTarget(string targetElementType) { string sourceA = """ using System.Collections; using System.Collections.Generic; class MyCollection<T> : IEnumerable<T> { private List<T> _list = new(); public void Add(T t) { _list.Add(t); } IEnumerator<T> IEnumerable<T>.GetEnumerator() => _list.GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => _list.GetEnumerator(); } """; string sourceB = $$""" using System.Collections; class Program { static void Main() { dynamic d1 = new object[] { 1 }; dynamic d2 = new int[] { 2 }; IEnumerable e1 = new object[] { 3 }; IEnumerable e2 = new int[] { 4 }; MyCollection<{{targetElementType}}> c = [..d1, ..d2, ..e1, ..e2]; c.Report(); int[] x = [5]; int[] y = [6]; c = [..(dynamic)x, ..(IEnumerable)y]; c.Report(); } } """; var comp = CreateCompilation(new[] { sourceA, sourceB, s_collectionExtensions }, references: new[] { CSharpRef }, options: TestOptions.ReleaseExe); if (targetElementType == "int") { comp.VerifyEmitDiagnostics( // 1.cs(10,34): error CS0029: Cannot implicitly convert type 'object' to 'int' // MyCollection<int> c = [..d1, ..d2, ..e1, ..e2]; Diagnostic(ErrorCode.ERR_NoImplicitConv, "d1").WithArguments("object", "int").WithLocation(10, 34), // 1.cs(10,40): error CS0029: Cannot implicitly convert type 'object' to 'int' // MyCollection<int> c = [..d1, ..d2, ..e1, ..e2]; Diagnostic(ErrorCode.ERR_NoImplicitConv, "d2").WithArguments("object", "int").WithLocation(10, 40), // 1.cs(10,46): error CS0029: Cannot implicitly convert type 'object' to 'int' // MyCollection<int> c = [..d1, ..d2, ..e1, ..e2]; Diagnostic(ErrorCode.ERR_NoImplicitConv, "e1").WithArguments("object", "int").WithLocation(10, 46), // 1.cs(10,52): error CS0029: Cannot implicitly convert type 'object' to 'int' // MyCollection<int> c = [..d1, ..d2, ..e1, ..e2]; Diagnostic(ErrorCode.ERR_NoImplicitConv, "e2").WithArguments("object", "int").WithLocation(10, 52), // 1.cs(14,16): error CS0029: Cannot implicitly convert type 'object' to 'int' // c = [..(dynamic)x, ..(IEnumerable)y]; Diagnostic(ErrorCode.ERR_NoImplicitConv, "(dynamic)x").WithArguments("object", "int").WithLocation(14, 16), // 1.cs(14,30): error CS0029: Cannot implicitly convert type 'object' to 'int' // c = [..(dynamic)x, ..(IEnumerable)y]; Diagnostic(ErrorCode.ERR_NoImplicitConv, "(IEnumerable)y").WithArguments("object", "int").WithLocation(14, 30)); } else { CompileAndVerify(comp, expectedOutput: "[1, 2, 3, 4], [5, 6], "); } } [WorkItem("https://github.com/dotnet/roslyn/issues/69704")] [Theory] [InlineData("int")] [InlineData("object")] public void SpreadElement_Untyped_CollectionBuilderTarget(string targetElementType) { string sourceA = """ using System; using System.Collections; using System.Collections.Generic; using System.Runtime.CompilerServices; [CollectionBuilder(typeof(MyCollectionBuilder), "Create")] class MyCollection<T> : IEnumerable<T> { private List<T> _list; public MyCollection(ReadOnlySpan<T> items) { _list = new(items.ToArray()); } public IEnumerator<T> GetEnumerator() => _list.GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => _list.GetEnumerator(); } class MyCollectionBuilder { public static MyCollection<T> Create<T>(ReadOnlySpan<T> items) => new(items); } """; string sourceB = $$""" using System.Collections; class Program { static void Main() { dynamic d1 = new object[] { 1 }; dynamic d2 = new int[] { 2 }; IEnumerable e1 = new object[] { 3 }; IEnumerable e2 = new int[] { 4 }; MyCollection<{{targetElementType}}> c = [..d1, ..d2, ..e1, ..e2]; c.Report(); int[] x = [5]; int[] y = [6]; c = [..(dynamic)x, ..(IEnumerable)y]; c.Report(); } } """; var comp = CreateCompilation(new[] { sourceA, sourceB, s_collectionExtensions }, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseExe); if (targetElementType == "int") { comp.VerifyEmitDiagnostics( // 1.cs(10,34): error CS0029: Cannot implicitly convert type 'object' to 'int' // MyCollection<int> c = [..d1, ..d2, ..e1, ..e2]; Diagnostic(ErrorCode.ERR_NoImplicitConv, "d1").WithArguments("object", "int").WithLocation(10, 34), // 1.cs(10,40): error CS0029: Cannot implicitly convert type 'object' to 'int' // MyCollection<int> c = [..d1, ..d2, ..e1, ..e2]; Diagnostic(ErrorCode.ERR_NoImplicitConv, "d2").WithArguments("object", "int").WithLocation(10, 40), // 1.cs(10,46): error CS0029: Cannot implicitly convert type 'object' to 'int' // MyCollection<int> c = [..d1, ..d2, ..e1, ..e2]; Diagnostic(ErrorCode.ERR_NoImplicitConv, "e1").WithArguments("object", "int").WithLocation(10, 46), // 1.cs(10,52): error CS0029: Cannot implicitly convert type 'object' to 'int' // MyCollection<int> c = [..d1, ..d2, ..e1, ..e2]; Diagnostic(ErrorCode.ERR_NoImplicitConv, "e2").WithArguments("object", "int").WithLocation(10, 52), // 1.cs(14,16): error CS0029: Cannot implicitly convert type 'object' to 'int' // c = [..(dynamic)x, ..(IEnumerable)y]; Diagnostic(ErrorCode.ERR_NoImplicitConv, "(dynamic)x").WithArguments("object", "int").WithLocation(14, 16), // 1.cs(14,30): error CS0029: Cannot implicitly convert type 'object' to 'int' // c = [..(dynamic)x, ..(IEnumerable)y]; Diagnostic(ErrorCode.ERR_NoImplicitConv, "(IEnumerable)y").WithArguments("object", "int").WithLocation(14, 30)); } else { CompileAndVerify( comp, verify: Verification.FailsPEVerify, expectedOutput: IncludeExpectedOutput("[1, 2, 3, 4], [5, 6], ")); } } [Fact] public void SpreadElement_Dynamic_04() { var source = """ using System.Collections.Generic; class Program { static void Main() { object[] objs = [1,2,3]; objs.Report(); List<dynamic> dyns = [..objs]; dyns.Report(); } } """; var verifier = CompileAndVerify(new[] { source, s_collectionExtensions }, expectedOutput: "[1, 2, 3], [1, 2, 3],", verify: Verification.Skipped); verifier.VerifyDiagnostics(); verifier.VerifyIL("Program.Main", """ { // Code size 52 (0x34) .maxstack 4 IL_0000: ldc.i4.3 IL_0001: newarr "object" IL_0006: dup IL_0007: ldc.i4.0 IL_0008: ldc.i4.1 IL_0009: box "int" IL_000e: stelem.ref IL_000f: dup IL_0010: ldc.i4.1 IL_0011: ldc.i4.2 IL_0012: box "int" IL_0017: stelem.ref IL_0018: dup IL_0019: ldc.i4.2 IL_001a: ldc.i4.3 IL_001b: box "int" IL_0020: stelem.ref IL_0021: dup IL_0022: ldc.i4.0 IL_0023: call "void CollectionExtensions.Report(object, bool)" IL_0028: call "System.Collections.Generic.List<dynamic> System.Linq.Enumerable.ToList<dynamic>(System.Collections.Generic.IEnumerable<dynamic>)" IL_002d: ldc.i4.0 IL_002e: call "void CollectionExtensions.Report(object, bool)" IL_0033: ret } """); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/71183")] public void SpreadElement_Dynamic_05() { var source = """ using System.Collections.Generic; class Program { static void Main() { List<dynamic> dyns = [1,2,3]; dyns.Report(); object[] objs = [..dyns]; objs.Report(); } } """; var verifier = CompileAndVerify(new[] { source, s_collectionExtensions }, expectedOutput: IncludeExpectedOutput("[1, 2, 3], [1, 2, 3],"), targetFramework: TargetFramework.Net80, verify: Verification.Skipped); verifier.VerifyDiagnostics(); verifier.VerifyIL("Program.Main", """ { // Code size 86 (0x56) .maxstack 3 .locals init (int V_0, System.Span<dynamic> V_1) IL_0000: ldc.i4.3 IL_0001: stloc.0 IL_0002: ldloc.0 IL_0003: newobj "System.Collections.Generic.List<dynamic>..ctor(int)" IL_0008: dup IL_0009: ldloc.0 IL_000a: call "void System.Runtime.InteropServices.CollectionsMarshal.SetCount<dynamic>(System.Collections.Generic.List<dynamic>, int)" IL_000f: dup IL_0010: call "System.Span<dynamic> System.Runtime.InteropServices.CollectionsMarshal.AsSpan<dynamic>(System.Collections.Generic.List<dynamic>)" IL_0015: stloc.1 IL_0016: ldloca.s V_1 IL_0018: ldc.i4.0 IL_0019: call "ref dynamic System.Span<dynamic>.this[int].get" IL_001e: ldc.i4.1 IL_001f: box "int" IL_0024: stind.ref IL_0025: ldloca.s V_1 IL_0027: ldc.i4.1 IL_0028: call "ref dynamic System.Span<dynamic>.this[int].get" IL_002d: ldc.i4.2 IL_002e: box "int" IL_0033: stind.ref IL_0034: ldloca.s V_1 IL_0036: ldc.i4.2 IL_0037: call "ref dynamic System.Span<dynamic>.this[int].get" IL_003c: ldc.i4.3 IL_003d: box "int" IL_0042: stind.ref IL_0043: dup IL_0044: ldc.i4.0 IL_0045: call "void CollectionExtensions.Report(object, bool)" IL_004a: callvirt "dynamic[] System.Collections.Generic.List<dynamic>.ToArray()" IL_004f: ldc.i4.0 IL_0050: call "void CollectionExtensions.Report(object, bool)" IL_0055: ret } """); } [Fact] public void SpreadElement_NullabilityDifference() { var source = """ #nullable enable using System.Collections.Generic; class Program { static void Main() { object[] objs = [1,2,3]; objs.Report(); List<object?> list = [..objs]; list.Report(); } } """; var verifier = CompileAndVerify(new[] { source, s_collectionExtensions }, expectedOutput: "[1, 2, 3], [1, 2, 3],", verify: Verification.Skipped); verifier.VerifyDiagnostics(); verifier.VerifyIL("Program.Main", """ { // Code size 52 (0x34) .maxstack 4 IL_0000: ldc.i4.3 IL_0001: newarr "object" IL_0006: dup IL_0007: ldc.i4.0 IL_0008: ldc.i4.1 IL_0009: box "int" IL_000e: stelem.ref IL_000f: dup IL_0010: ldc.i4.1 IL_0011: ldc.i4.2 IL_0012: box "int" IL_0017: stelem.ref IL_0018: dup IL_0019: ldc.i4.2 IL_001a: ldc.i4.3 IL_001b: box "int" IL_0020: stelem.ref IL_0021: dup IL_0022: ldc.i4.0 IL_0023: call "void CollectionExtensions.Report(object, bool)" IL_0028: call "System.Collections.Generic.List<object> System.Linq.Enumerable.ToList<object>(System.Collections.Generic.IEnumerable<object>)" IL_002d: ldc.i4.0 IL_002e: call "void CollectionExtensions.Report(object, bool)" IL_0033: ret } """); } [Fact] public void SpreadElement_MissingList() { string source = """ using System.Collections.Generic; class Program { static void Main() { int[] a = [1, 2]; IEnumerable<int> e = a; int[] b; b = [..a]; b = [..e]; } } """; var comp = CreateCompilation(source); comp.MakeTypeMissing(WellKnownType.System_Collections_Generic_List_T); comp.VerifyEmitDiagnostics( // (9,13): error CS0656: Missing compiler required member 'System.Collections.Generic.List`1..ctor' // b = [..a]; Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "[..a]").WithArguments("System.Collections.Generic.List`1", ".ctor").WithLocation(9, 13), // (9,13): error CS0656: Missing compiler required member 'System.Collections.Generic.List`1..ctor' // b = [..a]; Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "[..a]").WithArguments("System.Collections.Generic.List`1", ".ctor").WithLocation(9, 13), // (9,13): error CS0656: Missing compiler required member 'System.Collections.Generic.List`1.Add' // b = [..a]; Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "[..a]").WithArguments("System.Collections.Generic.List`1", "Add").WithLocation(9, 13), // (9,13): error CS0656: Missing compiler required member 'System.Collections.Generic.List`1.ToArray' // b = [..a]; Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "[..a]").WithArguments("System.Collections.Generic.List`1", "ToArray").WithLocation(9, 13), // (10,13): error CS0656: Missing compiler required member 'System.Collections.Generic.List`1..ctor' // b = [..e]; Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "[..e]").WithArguments("System.Collections.Generic.List`1", ".ctor").WithLocation(10, 13), // (10,13): error CS0656: Missing compiler required member 'System.Collections.Generic.List`1..ctor' // b = [..e]; Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "[..e]").WithArguments("System.Collections.Generic.List`1", ".ctor").WithLocation(10, 13), // (10,13): error CS0656: Missing compiler required member 'System.Collections.Generic.List`1.Add' // b = [..e]; Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "[..e]").WithArguments("System.Collections.Generic.List`1", "Add").WithLocation(10, 13), // (10,13): error CS0656: Missing compiler required member 'System.Collections.Generic.List`1.ToArray' // b = [..e]; Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "[..e]").WithArguments("System.Collections.Generic.List`1", "ToArray").WithLocation(10, 13)); comp = CreateCompilation(source); comp.MakeMemberMissing(WellKnownMember.System_Collections_Generic_List_T__ctor); comp.VerifyEmitDiagnostics( // (9,13): error CS0656: Missing compiler required member 'System.Collections.Generic.List`1..ctor' // b = [..a]; Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "[..a]").WithArguments("System.Collections.Generic.List`1", ".ctor").WithLocation(9, 13), // (10,13): error CS0656: Missing compiler required member 'System.Collections.Generic.List`1..ctor' // b = [..e]; Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "[..e]").WithArguments("System.Collections.Generic.List`1", ".ctor").WithLocation(10, 13)); comp = CreateCompilation(source); comp.MakeMemberMissing(WellKnownMember.System_Collections_Generic_List_T__ctorInt32); comp.VerifyEmitDiagnostics( // (9,13): error CS0656: Missing compiler required member 'System.Collections.Generic.List`1..ctor' // b = [..a]; Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "[..a]").WithArguments("System.Collections.Generic.List`1", ".ctor").WithLocation(9, 13), // (10,13): error CS0656: Missing compiler required member 'System.Collections.Generic.List`1..ctor' // b = [..e]; Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "[..e]").WithArguments("System.Collections.Generic.List`1", ".ctor").WithLocation(10, 13)); comp = CreateCompilation(source); comp.MakeMemberMissing(WellKnownMember.System_Collections_Generic_List_T__Add); comp.VerifyEmitDiagnostics( // (9,13): error CS0656: Missing compiler required member 'System.Collections.Generic.List`1.Add' // b = [..a]; Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "[..a]").WithArguments("System.Collections.Generic.List`1", "Add").WithLocation(9, 13), // (10,13): error CS0656: Missing compiler required member 'System.Collections.Generic.List`1.Add' // b = [..e]; Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "[..e]").WithArguments("System.Collections.Generic.List`1", "Add").WithLocation(10, 13)); comp = CreateCompilation(source); comp.MakeMemberMissing(WellKnownMember.System_Collections_Generic_List_T__ToArray); comp.VerifyEmitDiagnostics( // (9,13): error CS0656: Missing compiler required member 'System.Collections.Generic.List`1.ToArray' // b = [..a]; Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "[..a]").WithArguments("System.Collections.Generic.List`1", "ToArray").WithLocation(9, 13), // (10,13): error CS0656: Missing compiler required member 'System.Collections.Generic.List`1.ToArray' // b = [..e]; Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "[..e]").WithArguments("System.Collections.Generic.List`1", "ToArray").WithLocation(10, 13)); } [Fact] public void SpreadElement_KnownLength() { string source = """ using System.Collections.Generic; class Program { static void Main() { int[] x = Convert([], [1, 2, 3]); x.Report(); } static T[] Convert<T>(List<T> x, List<T> y) => [..x, ..y]; } """; var verifier = CompileAndVerify( new[] { source, s_collectionExtensions }, targetFramework: TargetFramework.Net80, verify: Verification.FailsPEVerify, expectedOutput: IncludeExpectedOutput("[1, 2, 3], ")); verifier.VerifyIL("Program.Convert<T>", """ { // Code size 120 (0x78) .maxstack 4 .locals init (System.Collections.Generic.List<T> V_0, int V_1, T[] V_2, System.Span<T> V_3, System.Span<T> V_4, System.Span<T> V_5, System.Span<T> V_6) IL_0000: ldarg.0 IL_0001: ldarg.1 IL_0002: stloc.0 IL_0003: ldc.i4.0 IL_0004: stloc.1 IL_0005: dup IL_0006: callvirt "int System.Collections.Generic.List<T>.Count.get" IL_000b: ldloc.0 IL_000c: callvirt "int System.Collections.Generic.List<T>.Count.get" IL_0011: add IL_0012: newarr "T" IL_0017: stloc.2 IL_0018: call "System.Span<T> System.Runtime.InteropServices.CollectionsMarshal.AsSpan<T>(System.Collections.Generic.List<T>)" IL_001d: stloc.3 IL_001e: ldloca.s V_3 IL_0020: ldloc.2 IL_0021: newobj "System.Span<T>..ctor(T[])" IL_0026: stloc.s V_5 IL_0028: ldloca.s V_5 IL_002a: ldloc.1 IL_002b: ldloca.s V_3 IL_002d: call "int System.Span<T>.Length.get" IL_0032: call "System.Span<T> System.Span<T>.Slice(int, int)" IL_0037: call "void System.Span<T>.CopyTo(System.Span<T>)" IL_003c: ldloc.1 IL_003d: ldloca.s V_3 IL_003f: call "int System.Span<T>.Length.get" IL_0044: add IL_0045: stloc.1 IL_0046: ldloc.0 IL_0047: call "System.Span<T> System.Runtime.InteropServices.CollectionsMarshal.AsSpan<T>(System.Collections.Generic.List<T>)" IL_004c: stloc.s V_4 IL_004e: ldloca.s V_4 IL_0050: ldloc.2 IL_0051: newobj "System.Span<T>..ctor(T[])" IL_0056: stloc.s V_6 IL_0058: ldloca.s V_6 IL_005a: ldloc.1 IL_005b: ldloca.s V_4 IL_005d: call "int System.Span<T>.Length.get" IL_0062: call "System.Span<T> System.Span<T>.Slice(int, int)" IL_0067: call "void System.Span<T>.CopyTo(System.Span<T>)" IL_006c: ldloc.1 IL_006d: ldloca.s V_4 IL_006f: call "int System.Span<T>.Length.get" IL_0074: add IL_0075: stloc.1 IL_0076: ldloc.2 IL_0077: ret } """); } [Fact] public void SpreadElement_KnownLength_EvaluationOrder_01() { string source = """ using System; using System.Collections.Generic; class Program { static T Identity<T>(string id, T value) { Console.WriteLine(id); return value; } static void Main() { F().Report(); } static int[] F() { return [..Identity("A", new[] { 1, 2 }), ..Identity("B", new [,] { { 3, 4 }, { 5, 6 } }), ..Identity("C", new List<int> { 7, 8 })]; } } """; var verifier = CompileAndVerify( new[] { source, s_collectionExtensions }, targetFramework: TargetFramework.Net80, verify: Verification.FailsPEVerify, expectedOutput: IncludeExpectedOutput(""" A B C [1, 2, 3, 4, 5, 6, 7, 8], """)); verifier.VerifyIL("Program.F", """ { // Code size 305 (0x131) .maxstack 5 .locals init (int[] V_0, int[,] V_1, System.Collections.Generic.List<int> V_2, int V_3, int[] V_4, System.ReadOnlySpan<int> V_5, System.Span<int> V_6, System.Span<int> V_7, int[,] V_8, int V_9, int V_10, int V_11, int V_12, int V_13, System.Span<int> V_14) IL_0000: ldstr "A" IL_0005: ldc.i4.2 IL_0006: newarr "int" IL_000b: dup IL_000c: ldc.i4.0 IL_000d: ldc.i4.1 IL_000e: stelem.i4 IL_000f: dup IL_0010: ldc.i4.1 IL_0011: ldc.i4.2 IL_0012: stelem.i4 IL_0013: call "int[] Program.Identity<int[]>(string, int[])" IL_0018: stloc.0 IL_0019: ldstr "B" IL_001e: ldc.i4.2 IL_001f: ldc.i4.2 IL_0020: newobj "int[*,*]..ctor" IL_0025: dup IL_0026: ldtoken "<PrivateImplementationDetails>.__StaticArrayInitTypeSize=16 <PrivateImplementationDetails>.BA7C5EE6E0192FDFE80274584650A2FB8DAE9213BD63AE7B31FE4D088074CB83" IL_002b: call "void System.Runtime.CompilerServices.RuntimeHelpers.InitializeArray(System.Array, System.RuntimeFieldHandle)" IL_0030: call "int[,] Program.Identity<int[,]>(string, int[,])" IL_0035: stloc.1 IL_0036: ldstr "C" IL_003b: newobj "System.Collections.Generic.List<int>..ctor()" IL_0040: dup IL_0041: ldc.i4.7 IL_0042: callvirt "void System.Collections.Generic.List<int>.Add(int)" IL_0047: dup IL_0048: ldc.i4.8 IL_0049: callvirt "void System.Collections.Generic.List<int>.Add(int)" IL_004e: call "System.Collections.Generic.List<int> Program.Identity<System.Collections.Generic.List<int>>(string, System.Collections.Generic.List<int>)" IL_0053: stloc.2 IL_0054: ldc.i4.0 IL_0055: stloc.3 IL_0056: ldloc.0 IL_0057: ldlen IL_0058: conv.i4 IL_0059: ldloc.1 IL_005a: callvirt "int System.Array.Length.get" IL_005f: add IL_0060: ldloc.2 IL_0061: callvirt "int System.Collections.Generic.List<int>.Count.get" IL_0066: add IL_0067: newarr "int" IL_006c: stloc.s V_4 IL_006e: ldloca.s V_5 IL_0070: ldloc.0 IL_0071: call "System.ReadOnlySpan<int>..ctor(int[])" IL_0076: ldloca.s V_5 IL_0078: ldloc.s V_4 IL_007a: newobj "System.Span<int>..ctor(int[])" IL_007f: stloc.s V_7 IL_0081: ldloca.s V_7 IL_0083: ldloc.3 IL_0084: ldloca.s V_5 IL_0086: call "int System.ReadOnlySpan<int>.Length.get" IL_008b: call "System.Span<int> System.Span<int>.Slice(int, int)" IL_0090: call "void System.ReadOnlySpan<int>.CopyTo(System.Span<int>)" IL_0095: ldloc.3 IL_0096: ldloca.s V_5 IL_0098: call "int System.ReadOnlySpan<int>.Length.get" IL_009d: add IL_009e: stloc.3 IL_009f: ldloc.1 IL_00a0: stloc.s V_8 IL_00a2: ldloc.s V_8 IL_00a4: ldc.i4.0 IL_00a5: callvirt "int System.Array.GetUpperBound(int)" IL_00aa: stloc.s V_9 IL_00ac: ldloc.s V_8 IL_00ae: ldc.i4.1 IL_00af: callvirt "int System.Array.GetUpperBound(int)" IL_00b4: stloc.s V_10 IL_00b6: ldloc.s V_8 IL_00b8: ldc.i4.0 IL_00b9: callvirt "int System.Array.GetLowerBound(int)" IL_00be: stloc.s V_11 IL_00c0: br.s IL_00f7 IL_00c2: ldloc.s V_8 IL_00c4: ldc.i4.1 IL_00c5: callvirt "int System.Array.GetLowerBound(int)" IL_00ca: stloc.s V_12 IL_00cc: br.s IL_00eb IL_00ce: ldloc.s V_8 IL_00d0: ldloc.s V_11 IL_00d2: ldloc.s V_12 IL_00d4: call "int[*,*].Get" IL_00d9: stloc.s V_13 IL_00db: ldloc.s V_4 IL_00dd: ldloc.3 IL_00de: ldloc.s V_13 IL_00e0: stelem.i4 IL_00e1: ldloc.3 IL_00e2: ldc.i4.1 IL_00e3: add IL_00e4: stloc.3 IL_00e5: ldloc.s V_12 IL_00e7: ldc.i4.1 IL_00e8: add IL_00e9: stloc.s V_12 IL_00eb: ldloc.s V_12 IL_00ed: ldloc.s V_10 IL_00ef: ble.s IL_00ce IL_00f1: ldloc.s V_11 IL_00f3: ldc.i4.1 IL_00f4: add IL_00f5: stloc.s V_11 IL_00f7: ldloc.s V_11 IL_00f9: ldloc.s V_9 IL_00fb: ble.s IL_00c2 IL_00fd: ldloc.2 IL_00fe: call "System.Span<int> System.Runtime.InteropServices.CollectionsMarshal.AsSpan<int>(System.Collections.Generic.List<int>)" IL_0103: stloc.s V_6 IL_0105: ldloca.s V_6 IL_0107: ldloc.s V_4 IL_0109: newobj "System.Span<int>..ctor(int[])" IL_010e: stloc.s V_14 IL_0110: ldloca.s V_14 IL_0112: ldloc.3 IL_0113: ldloca.s V_6 IL_0115: call "int System.Span<int>.Length.get" IL_011a: call "System.Span<int> System.Span<int>.Slice(int, int)" IL_011f: call "void System.Span<int>.CopyTo(System.Span<int>)" IL_0124: ldloc.3 IL_0125: ldloca.s V_6 IL_0127: call "int System.Span<int>.Length.get" IL_012c: add IL_012d: stloc.3 IL_012e: ldloc.s V_4 IL_0130: ret } """); } [Theory] [InlineData("int[]", false)] [InlineData("int[]", true)] [InlineData("System.Collections.Generic.IEnumerable<int>", false)] [InlineData("System.Collections.Generic.IEnumerable<int>", true)] public void SpreadElement_KnownLength_EvaluationOrder_02(string collectionType, bool includeLength) { string sourceA = """ using System; using System.Collections; using System.Collections.Generic; partial class MyCollection<T> : IEnumerable { private readonly string _id; private readonly List<T> _list; public MyCollection(string id, T[] items) { _id = id; _list = new(); _list.AddRange(items); } public MyEnumerator<T> GetEnumerator() { Console.WriteLine("{0}: GetEnumerator", _id); return new(_id, _list); } IEnumerator IEnumerable.GetEnumerator() => throw null; } class MyEnumerator<T> : IDisposable { private readonly string _id; private readonly List<T> _list; private int _index; public MyEnumerator(string id, List<T> list) { _id = id; _list = list; _index = -1; } public bool MoveNext() { if (_index < _list.Count) _index++; return _index < _list.Count; } public T Current { get { Console.WriteLine("{0}: [{1}]", _id, _index); return _list[_index]; } } void IDisposable.Dispose() { Console.WriteLine("{0}: Dispose", _id); } } """; if (includeLength) { sourceA += """ partial class MyCollection<T> { public int Length { get { Console.WriteLine("{0}: Length", _id); return _list.Count; } } } """; } string sourceB = $$""" using System; partial class Program { static T One<T>(string id, T value) { Console.WriteLine("{0}: One", id); return value; } static MyCollection<T> Many<T>(string id, params T[] items) { Console.WriteLine("{0}: Many", id); return new(id, items); } static void Report({{collectionType}} c) { c.Report(); } } """; string sourceC; string expectedOutput; // Maximum number of temporaries. sourceC = """ Report([..Many("A", 1), One("B", 2), ..Many("C", 3, 4), One("D", 5)]); """; expectedOutput = includeLength ? """ A: Many B: One C: Many A: Length C: Length A: GetEnumerator A: [0] A: Dispose C: GetEnumerator C: [0] C: [1] C: Dispose D: One [1, 2, 3, 4, 5], """ : """ A: Many A: GetEnumerator A: [0] A: Dispose B: One C: Many C: GetEnumerator C: [0] C: [1] C: Dispose D: One [1, 2, 3, 4, 5], """; CompileAndVerify( new[] { sourceA, sourceB, sourceC, s_collectionExtensions }, targetFramework: TargetFramework.Net80, verify: Verification.FailsPEVerify, expectedOutput: IncludeExpectedOutput(expectedOutput)); // Exceeded maximum number of temporaries. sourceC = """ Report([..Many("A", 1), One("B", 2), One("C", 3), ..Many("D", 4, 5)]); """; expectedOutput = """ A: Many A: GetEnumerator A: [0] A: Dispose B: One C: One D: Many D: GetEnumerator D: [0] D: [1] D: Dispose [1, 2, 3, 4, 5], """; CompileAndVerify( new[] { sourceA, sourceB, sourceC, s_collectionExtensions }, targetFramework: TargetFramework.Net80, verify: Verification.FailsPEVerify, expectedOutput: IncludeExpectedOutput(expectedOutput)); } [Fact] public void KnownLength_List() { string source = """ using System.Collections.Generic; class Program { static void Main() { var x = F0(); var y = F1(); var z = F2(y); z.Report(); } static List<int> F0() => []; static List<int> F1() => [1, 2, 3]; static List<object> F2(List<int> c) => [4, ..c]; } """; var verifier = CompileAndVerify( new[] { source, s_collectionExtensions }, expectedOutput: "[4, 1, 2, 3], "); verifier.VerifyIL("Program.F0", """ { // Code size 6 (0x6) .maxstack 1 IL_0000: newobj "System.Collections.Generic.List<int>..ctor()" IL_0005: ret } """); verifier.VerifyIL("Program.F1", """ { // Code size 28 (0x1c) .maxstack 3 IL_0000: ldc.i4.3 IL_0001: newobj "System.Collections.Generic.List<int>..ctor(int)" IL_0006: dup IL_0007: ldc.i4.1 IL_0008: callvirt "void System.Collections.Generic.List<int>.Add(int)" IL_000d: dup IL_000e: ldc.i4.2 IL_000f: callvirt "void System.Collections.Generic.List<int>.Add(int)" IL_0014: dup IL_0015: ldc.i4.3 IL_0016: callvirt "void System.Collections.Generic.List<int>.Add(int)" IL_001b: ret } """); verifier.VerifyIL("Program.F2", """ { // Code size 88 (0x58) .maxstack 2 .locals init (object V_0, System.Collections.Generic.List<int> V_1, System.Collections.Generic.List<object> V_2, System.Collections.Generic.List<int>.Enumerator V_3, int V_4) IL_0000: ldc.i4.4 IL_0001: box "int" IL_0006: stloc.0 IL_0007: ldarg.0 IL_0008: stloc.1 IL_0009: ldc.i4.1 IL_000a: ldloc.1 IL_000b: callvirt "int System.Collections.Generic.List<int>.Count.get" IL_0010: add IL_0011: newobj "System.Collections.Generic.List<object>..ctor(int)" IL_0016: stloc.2 IL_0017: ldloc.2 IL_0018: ldloc.0 IL_0019: callvirt "void System.Collections.Generic.List<object>.Add(object)" IL_001e: ldloc.1 IL_001f: callvirt "System.Collections.Generic.List<int>.Enumerator System.Collections.Generic.List<int>.GetEnumerator()" IL_0024: stloc.3 .try { IL_0025: br.s IL_003d IL_0027: ldloca.s V_3 IL_0029: call "int System.Collections.Generic.List<int>.Enumerator.Current.get" IL_002e: stloc.s V_4 IL_0030: ldloc.2 IL_0031: ldloc.s V_4 IL_0033: box "int" IL_0038: callvirt "void System.Collections.Generic.List<object>.Add(object)" IL_003d: ldloca.s V_3 IL_003f: call "bool System.Collections.Generic.List<int>.Enumerator.MoveNext()" IL_0044: brtrue.s IL_0027 IL_0046: leave.s IL_0056 } finally { IL_0048: ldloca.s V_3 IL_004a: constrained. "System.Collections.Generic.List<int>.Enumerator" IL_0050: callvirt "void System.IDisposable.Dispose()" IL_0055: endfinally } IL_0056: ldloc.2 IL_0057: ret } """); } [Fact] public void KnownLength_List_MissingConstructor_01() { string source = """ using System.Collections.Generic; class Program { static void Main() { List<int> x = []; List<int> y = [1, 2, 3]; } } """; var comp = CreateCompilation(source); comp.MakeMemberMissing(WellKnownMember.System_Collections_Generic_List_T__ctorInt32); comp.VerifyEmitDiagnostics( // (7,23): error CS0656: Missing compiler required member 'System.Collections.Generic.List`1..ctor' // List<int> y = [1, 2, 3]; Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "[1, 2, 3]").WithArguments("System.Collections.Generic.List`1", ".ctor").WithLocation(7, 23)); } [Fact] public void KnownLength_List_MissingConstructor_02() { string source = """ using System.Collections.Generic; class Program { static void Main() { List<int> y = new() { 1, 2, 3 }; List<object> z = [4, ..y]; } } """; var comp = CreateCompilation(source); comp.MakeMemberMissing(WellKnownMember.System_Collections_Generic_List_T__ctorInt32); comp.VerifyEmitDiagnostics( // (7,26): error CS0656: Missing compiler required member 'System.Collections.Generic.List`1..ctor' // List<object> z = [4, ..y]; Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "[4, ..y]").WithArguments("System.Collections.Generic.List`1", ".ctor").WithLocation(7, 26)); } [Fact] public void SpreadElement_LengthSideEffects() { string source = """ using System; using System.Collections; using System.Collections.Generic; struct S : IEnumerable<object> { internal static int TotalUse; internal int InstanceUse; private object[] _items; public S(object[] items) { _items = items; } public int Length => GetLength(); private int GetLength() { Console.WriteLine("Length"); InstanceUse++; TotalUse++; return _items.Length; } public IEnumerator<object> GetEnumerator() { foreach (var item in _items) yield return item; } IEnumerator IEnumerable.GetEnumerator() => _items.GetEnumerator(); } class Program { static void Main() { var s = new S(new object[] { 1, 2, 3 }); Console.WriteLine("Before: {0}, {1}", s.InstanceUse, S.TotalUse); object[] a = [..s]; Console.WriteLine("After: {0}, {1}", s.InstanceUse, S.TotalUse); a.Report(); } } """; CompileAndVerify( new[] { source, s_collectionExtensions }, targetFramework: TargetFramework.Net80, verify: Verification.FailsPEVerify, expectedOutput: IncludeExpectedOutput(""" Before: 0, 0 Length After: 0, 1 [1, 2, 3], """)); } [Fact] public void SpreadElement_LengthObsolete() { string source = """ using System; using System.Collections; using System.Collections.Generic; class MyCollection : IEnumerable { private object[] _items; public MyCollection(object[] items) { _items = items; } [Obsolete(null, error: true)] public int Count => _items.Length; IEnumerator IEnumerable.GetEnumerator() => _items.GetEnumerator(); } class Program { static void Main() { IEnumerable<int> x = []; MyCollection y = new([1, 2, 3]); object[] z = [..x, ..y]; } } """; var comp = CreateCompilation(source); comp.VerifyEmitDiagnostics( // (17,30): warning CS0612: 'MyCollection.Count' is obsolete // object[] z = [..x, ..y]; Diagnostic(ErrorCode.WRN_DeprecatedSymbol, "y").WithArguments("MyCollection.Count").WithLocation(17, 30)); } [Fact] public void SpreadElement_LengthUseSiteError() { string assemblyA = GetUniqueName(); string sourceA = """ public class A { } """; var comp = CreateCompilation(sourceA, assemblyName: assemblyA); var refA = comp.EmitToImageReference(); string sourceB = """ using System.Collections; public class B : A { private object[] _items; public B(object[] items) { _items = items; } public IEnumerator GetEnumerator() => _items.GetEnumerator(); } """; comp = CreateCompilation(sourceB, references: new[] { refA }); var refB = comp.EmitToImageReference(); string sourceC = """ class C { static object[] F(B b) => [..b]; } """; comp = CreateCompilation(sourceC, references: new[] { refA, refB }); comp.VerifyEmitDiagnostics(); comp = CreateCompilation(sourceC, references: new[] { refB }); comp.VerifyEmitDiagnostics( // (3,34): error CS0012: The type 'A' is defined in an assembly that is not referenced. You must add a reference to assembly '05867d3a-d497-48ef-b11e-9567f6786f5a, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'. // static object[] F(B b) => [..b]; Diagnostic(ErrorCode.ERR_NoTypeDef, "b").WithArguments("A", $"{assemblyA}, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null").WithLocation(3, 34), // (3,34): error CS0012: The type 'A' is defined in an assembly that is not referenced. You must add a reference to assembly '05867d3a-d497-48ef-b11e-9567f6786f5a, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'. // static object[] F(B b) => [..b]; Diagnostic(ErrorCode.ERR_NoTypeDef, "b").WithArguments("A", $"{assemblyA}, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null").WithLocation(3, 34)); } [CombinatorialData] [Theory] public void ArrayEmpty_01(bool includeEmptyArray) { string source = """ using System.Collections.Generic; class Program { static void Main() { EmptyArray<object>().Report(); EmptyIEnumerable<object>().Report(); EmptyICollection<object>().Report(); EmptyIList<object>().Report(); EmptyIReadOnlyCollection<object>().Report(); EmptyIReadOnlyList<object>().Report(); } static T[] EmptyArray<T>() => []; static IEnumerable<T> EmptyIEnumerable<T>() => []; static ICollection<T> EmptyICollection<T>() => []; static IList<T> EmptyIList<T>() => []; static IReadOnlyCollection<T> EmptyIReadOnlyCollection<T>() => []; static IReadOnlyList<T> EmptyIReadOnlyList<T>() => []; } """; var comp = CreateCompilation(new[] { source, s_collectionExtensions }, options: TestOptions.ReleaseExe); if (!includeEmptyArray) { comp.MakeMemberMissing(SpecialMember.System_Array__Empty); } var verifier = CompileAndVerify( comp, expectedOutput: "[], [], [], [], [], [], "); string expectedIL = !includeEmptyArray ? """ { // Code size 7 (0x7) .maxstack 1 IL_0000: ldc.i4.0 IL_0001: newarr "T" IL_0006: ret } """ : """ { // Code size 6 (0x6) .maxstack 1 IL_0000: call "T[] System.Array.Empty<T>()" IL_0005: ret } """; verifier.VerifyIL("Program.EmptyArray<T>", expectedIL); verifier.VerifyIL("Program.EmptyIEnumerable<T>", expectedIL); verifier.VerifyIL("Program.EmptyIReadOnlyCollection<T>", expectedIL); verifier.VerifyIL("Program.EmptyIReadOnlyList<T>", expectedIL); expectedIL = """ { // Code size 6 (0x6) .maxstack 1 IL_0000: newobj "System.Collections.Generic.List<T>..ctor()" IL_0005: ret } """; verifier.VerifyIL("Program.EmptyICollection<T>", expectedIL); verifier.VerifyIL("Program.EmptyIList<T>", expectedIL); } [CombinatorialData] [Theory] public void ArrayEmpty_02(bool includeEmptyArray) { string source = """ using System.Collections.Generic; class Program { static void Main() { EmptyArray().Report(); EmptyIEnumerable().Report(); EmptyICollection().Report(); EmptyIList().Report(); EmptyIReadOnlyCollection().Report(); EmptyIReadOnlyList().Report(); } static string[] EmptyArray() => []; static IEnumerable<string> EmptyIEnumerable() => []; static ICollection<string> EmptyICollection() => []; static IList<string> EmptyIList() => []; static IReadOnlyCollection<string> EmptyIReadOnlyCollection() => []; static IReadOnlyList<string> EmptyIReadOnlyList() => []; } """; var comp = CreateCompilation(new[] { source, s_collectionExtensions }, options: TestOptions.ReleaseExe); if (!includeEmptyArray) { comp.MakeMemberMissing(SpecialMember.System_Array__Empty); } var verifier = CompileAndVerify( comp, expectedOutput: "[], [], [], [], [], [], "); string expectedIL = !includeEmptyArray ? """ { // Code size 7 (0x7) .maxstack 1 IL_0000: ldc.i4.0 IL_0001: newarr "string" IL_0006: ret } """ : """ { // Code size 6 (0x6) .maxstack 1 IL_0000: call "string[] System.Array.Empty<string>()" IL_0005: ret } """; verifier.VerifyIL("Program.EmptyArray", expectedIL); verifier.VerifyIL("Program.EmptyIEnumerable", expectedIL); verifier.VerifyIL("Program.EmptyIReadOnlyCollection", expectedIL); verifier.VerifyIL("Program.EmptyIReadOnlyList", expectedIL); expectedIL = """ { // Code size 6 (0x6) .maxstack 1 IL_0000: newobj "System.Collections.Generic.List<string>..ctor()" IL_0005: ret } """; verifier.VerifyIL("Program.EmptyICollection", expectedIL); verifier.VerifyIL("Program.EmptyIList", expectedIL); } [Fact] public void ArrayEmpty_PointerElementType() { string source = """ unsafe class Program { static void Main() { EmptyArray().Report(); EmptyNestedArray().Report(); } static void*[] EmptyArray() => []; static void*[][] EmptyNestedArray() => []; } """; var verifier = CompileAndVerify( new[] { source, s_collectionExtensions }, options: TestOptions.UnsafeReleaseExe, verify: Verification.FailsPEVerify, expectedOutput: "[], [], "); verifier.VerifyIL("Program.EmptyArray", """ { // Code size 7 (0x7) .maxstack 1 IL_0000: ldc.i4.0 IL_0001: newarr "void*" IL_0006: ret } """); verifier.VerifyIL("Program.EmptyNestedArray", """ { // Code size 6 (0x6) .maxstack 1 IL_0000: call "void*[][] System.Array.Empty<void*[]>()" IL_0005: ret } """); } [Fact] public void ArrayEmpty_MissingMethod() { string source = """ using System.Collections.Generic; class Program { static void Main() { int[] x = []; IEnumerable<int> y = []; x.Report(); y.Report(); } } """; var comp = CreateCompilation(new[] { source, s_collectionExtensions }, options: TestOptions.ReleaseExe); var verifier = CompileAndVerify(comp, expectedOutput: "[], [], "); verifier.VerifyIL("Program.Main", """ { // Code size 25 (0x19) .maxstack 2 .locals init (System.Collections.Generic.IEnumerable<int> V_0) //y IL_0000: call "int[] System.Array.Empty<int>()" IL_0005: call "int[] System.Array.Empty<int>()" IL_000a: stloc.0 IL_000b: ldc.i4.0 IL_000c: call "void CollectionExtensions.Report(object, bool)" IL_0011: ldloc.0 IL_0012: ldc.i4.0 IL_0013: call "void CollectionExtensions.Report(object, bool)" IL_0018: ret } """); comp = CreateCompilation(new[] { source, s_collectionExtensions }, options: TestOptions.ReleaseExe); comp.MakeMemberMissing(SpecialMember.System_Array__Empty); verifier = CompileAndVerify(comp, expectedOutput: "[], [], "); verifier.VerifyIL("Program.Main", """ { // Code size 27 (0x1b) .maxstack 3 .locals init (int[] V_0) //x IL_0000: ldc.i4.0 IL_0001: newarr "int" IL_0006: stloc.0 IL_0007: ldc.i4.0 IL_0008: newarr "int" IL_000d: ldloc.0 IL_000e: ldc.i4.0 IL_000f: call "void CollectionExtensions.Report(object, bool)" IL_0014: ldc.i4.0 IL_0015: call "void CollectionExtensions.Report(object, bool)" IL_001a: ret } """); } [CombinatorialData] [Theory] public void SynthesizedReadOnlyArray([CombinatorialValues("IEnumerable<T>", "IReadOnlyCollection<T>", "IReadOnlyList<T>")] string targetType) { string source = $$""" using System; using System.Collections; using System.Collections.Generic; using static System.Console; class Program { static void Main() { Report<int>([], 2); Report<object>([1, 2, null], (object)2); } static void Report<T>({{targetType}} x, T value) { int length = ((IReadOnlyCollection<T>)x).Count; T[] a; Write("IEnumerable.GetEnumerator(): "); ((IEnumerable)x).Report(includeType: true); WriteLine(); WriteLine("ICollection.Count: {0}", ((ICollection)x).Count); WriteLine("ICollection.IsSynchronized: {0}", ((ICollection)x).IsSynchronized); WriteLine("ICollection.SyncRoot == (object)x: {0}", ((ICollection)x).SyncRoot == (object)x); Write("ICollection.CopyTo(..., 0): "); a = new T[length]; ((ICollection)x).CopyTo(a, 0); a.Report(includeType: true); WriteLine(); WriteLine("IList.IsFixedSize: {0}", ((IList)x).IsFixedSize); WriteLine("IList.IsReadOnly: {0}", ((IList)x).IsReadOnly); if (length > 1) WriteLine("IList.this[1].get: {0}", ((IList)x)[1]); if (length > 1) WriteLine("IList.this[1].set: {0}", Invoke(() => ((IList)x)[1] = value)); WriteLine("IList.Add(value): {0}", Invoke(() => ((IList)x).Add(value))); WriteLine("IList.Clear(): {0}", Invoke(() => ((IList)x).Clear())); WriteLine("IList.Contains(value): {0}", ((IList)x).Contains(value)); WriteLine("IList.IndexOf(value): {0}", ((IList)x).IndexOf(value)); WriteLine("IList.Insert(0, value): {0}", Invoke(() => ((IList)x).Insert(0, value))); WriteLine("IList.Remove(value): {0}", Invoke(() => ((IList)x).Remove(value))); if (length > 1) WriteLine("IList.RemoveAt(1): {0}", Invoke(() => ((IList)x).RemoveAt(1))); Write("IEnumerable<T>.GetEnumerator(): "); ((IEnumerable<T>)x).Report(includeType: true); WriteLine(); WriteLine("IReadOnlyCollection<T>.Count: {0}", ((IReadOnlyCollection<T>)x).Count); if (length > 1) WriteLine("IReadOnlyList<T>.this[1]: {0}", ((IReadOnlyList<T>)x)[1]); WriteLine("ICollection<T>.Count: {0}", ((ICollection<T>)x).Count); WriteLine("ICollection<T>.IsReadOnly: {0}", ((ICollection<T>)x).IsReadOnly); WriteLine("ICollection<T>.Add(value): {0}", Invoke(() => ((ICollection<T>)x).Add(value))); WriteLine("ICollection<T>.Clear(): {0}", Invoke(() => ((ICollection<T>)x).Clear())); WriteLine("ICollection<T>.Contains(value): {0}", ((ICollection<T>)x).Contains(value)); Write("ICollection<T>.CopyTo(..., 0): "); a = new T[length]; ((ICollection<T>)x).CopyTo(a, 0); a.Report(includeType: true); WriteLine(); WriteLine("ICollection<T>.Remove(value): {0}", Invoke(() => ((ICollection<T>)x).Remove(value))); if (length > 1) WriteLine("IList<T>.this[1].get: {0}", ((IList<T>)x)[1]); if (length > 1) WriteLine("IList<T>.this[1].set: {0}", Invoke(() => ((IList<T>)x)[1] = value)); WriteLine("IList<T>.IndexOf(value): {0}", ((IList<T>)x).IndexOf(value)); WriteLine("IList<T>.Insert(0, value): {0}", Invoke(() => ((IList<T>)x).Insert(0, value))); if (length > 1) WriteLine("IList<T>.RemoveAt(1): {0}", Invoke(() => ((IList<T>)x).RemoveAt(1))); } static string Invoke(Action a) { try { a(); return "completed"; } catch (Exception e) { return e.GetType().FullName; } } } """; var verifier = CompileAndVerify( new[] { source, s_collectionExtensions }, symbolValidator: module => { var synthesizedType = module.GlobalNamespace.GetTypeMember("<>z__ReadOnlyArray"); Assert.Equal("<>z__ReadOnlyArray<T>", synthesizedType.ToTestDisplayString()); Assert.Equal("<>z__ReadOnlyArray`1", synthesizedType.MetadataName); }, expectedOutput: """ IEnumerable.GetEnumerator(): (System.Int32[]) [], ICollection.Count: 0 ICollection.IsSynchronized: False ICollection.SyncRoot == (object)x: True ICollection.CopyTo(..., 0): (System.Int32[]) [], IList.IsFixedSize: True IList.IsReadOnly: False IList.Add(value): System.NotSupportedException IList.Clear(): completed IList.Contains(value): False IList.IndexOf(value): -1 IList.Insert(0, value): System.NotSupportedException IList.Remove(value): System.NotSupportedException IEnumerable<T>.GetEnumerator(): (System.Int32[]) [], IReadOnlyCollection<T>.Count: 0 ICollection<T>.Count: 0 ICollection<T>.IsReadOnly: True ICollection<T>.Add(value): System.NotSupportedException ICollection<T>.Clear(): System.NotSupportedException ICollection<T>.Contains(value): False ICollection<T>.CopyTo(..., 0): (System.Int32[]) [], ICollection<T>.Remove(value): System.NotSupportedException IList<T>.IndexOf(value): -1 IList<T>.Insert(0, value): System.NotSupportedException IEnumerable.GetEnumerator(): (<>z__ReadOnlyArray<System.Object>) [1, 2, null], ICollection.Count: 3 ICollection.IsSynchronized: False ICollection.SyncRoot == (object)x: True ICollection.CopyTo(..., 0): (System.Object[]) [1, 2, null], IList.IsFixedSize: True IList.IsReadOnly: True IList.this[1].get: 2 IList.this[1].set: System.NotSupportedException IList.Add(value): System.NotSupportedException IList.Clear(): System.NotSupportedException IList.Contains(value): True IList.IndexOf(value): 1 IList.Insert(0, value): System.NotSupportedException IList.Remove(value): System.NotSupportedException IList.RemoveAt(1): System.NotSupportedException IEnumerable<T>.GetEnumerator(): (<>z__ReadOnlyArray<System.Object>) [1, 2, null], IReadOnlyCollection<T>.Count: 3 IReadOnlyList<T>.this[1]: 2 ICollection<T>.Count: 3 ICollection<T>.IsReadOnly: True ICollection<T>.Add(value): System.NotSupportedException ICollection<T>.Clear(): System.NotSupportedException ICollection<T>.Contains(value): True ICollection<T>.CopyTo(..., 0): (System.Object[]) [1, 2, null], ICollection<T>.Remove(value): System.NotSupportedException IList<T>.this[1].get: 2 IList<T>.this[1].set: System.NotSupportedException IList<T>.IndexOf(value): 1 IList<T>.Insert(0, value): System.NotSupportedException IList<T>.RemoveAt(1): System.NotSupportedException """); string expectedNotSupportedIL = """ { // Code size 6 (0x6) .maxstack 1 IL_0000: newobj "System.NotSupportedException..ctor()" IL_0005: throw } """; verifier.VerifyIL("<>z__ReadOnlyArray<T>..ctor(T[])", """ { // Code size 14 (0xe) .maxstack 2 IL_0000: ldarg.0 IL_0001: call "object..ctor()" IL_0006: ldarg.0 IL_0007: ldarg.1 IL_0008: stfld "T[] <>z__ReadOnlyArray<T>._items" IL_000d: ret } """); verifier.VerifyIL("<>z__ReadOnlyArray<T>.System.Collections.IEnumerable.GetEnumerator()", """ { // Code size 12 (0xc) .maxstack 1 IL_0000: ldarg.0 IL_0001: ldfld "T[] <>z__ReadOnlyArray<T>._items" IL_0006: callvirt "System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()" IL_000b: ret } """); verifier.VerifyIL("<>z__ReadOnlyArray<T>.System.Collections.Generic.IEnumerable<T>.GetEnumerator()", """ { // Code size 12 (0xc) .maxstack 1 IL_0000: ldarg.0 IL_0001: ldfld "T[] <>z__ReadOnlyArray<T>._items" IL_0006: callvirt "System.Collections.Generic.IEnumerator<T> System.Collections.Generic.IEnumerable<T>.GetEnumerator()" IL_000b: ret } """); verifier.VerifyIL("<>z__ReadOnlyArray<T>.System.Collections.ICollection.get_Count()", """ { // Code size 9 (0x9) .maxstack 1 IL_0000: ldarg.0 IL_0001: ldfld "T[] <>z__ReadOnlyArray<T>._items" IL_0006: ldlen IL_0007: conv.i4 IL_0008: ret } """); verifier.VerifyIL("<>z__ReadOnlyArray<T>.System.Collections.ICollection.get_IsSynchronized()", """ { // Code size 2 (0x2) .maxstack 1 IL_0000: ldc.i4.0 IL_0001: ret } """); verifier.VerifyIL("<>z__ReadOnlyArray<T>.System.Collections.ICollection.get_SyncRoot()", """ { // Code size 2 (0x2) .maxstack 1 IL_0000: ldarg.0 IL_0001: ret } """); verifier.VerifyIL("<>z__ReadOnlyArray<T>.System.Collections.ICollection.CopyTo(System.Array, int)", """ { // Code size 14 (0xe) .maxstack 3 IL_0000: ldarg.0 IL_0001: ldfld "T[] <>z__ReadOnlyArray<T>._items" IL_0006: ldarg.1 IL_0007: ldarg.2 IL_0008: callvirt "void System.Collections.ICollection.CopyTo(System.Array, int)" IL_000d: ret } """); verifier.VerifyIL("<>z__ReadOnlyArray<T>.System.Collections.IList.get_Item(int)", """ { // Code size 18 (0x12) .maxstack 2 IL_0000: ldarg.0 IL_0001: ldfld "T[] <>z__ReadOnlyArray<T>._items" IL_0006: ldarg.1 IL_0007: ldelem "T" IL_000c: box "T" IL_0011: ret } """); verifier.VerifyIL("<>z__ReadOnlyArray<T>.System.Collections.IList.set_Item(int, object)", expectedNotSupportedIL); verifier.VerifyIL("<>z__ReadOnlyArray<T>.System.Collections.IList.get_IsFixedSize()", """ { // Code size 2 (0x2) .maxstack 1 IL_0000: ldc.i4.1 IL_0001: ret } """); verifier.VerifyIL("<>z__ReadOnlyArray<T>.System.Collections.IList.get_IsReadOnly()", """ { // Code size 2 (0x2) .maxstack 1 IL_0000: ldc.i4.1 IL_0001: ret } """); verifier.VerifyIL("<>z__ReadOnlyArray<T>.System.Collections.IList.Add(object)", expectedNotSupportedIL); verifier.VerifyIL("<>z__ReadOnlyArray<T>.System.Collections.IList.Clear()", expectedNotSupportedIL); verifier.VerifyIL("<>z__ReadOnlyArray<T>.System.Collections.IList.Contains(object)", """ { // Code size 13 (0xd) .maxstack 2 IL_0000: ldarg.0 IL_0001: ldfld "T[] <>z__ReadOnlyArray<T>._items" IL_0006: ldarg.1 IL_0007: callvirt "bool System.Collections.IList.Contains(object)" IL_000c: ret } """); verifier.VerifyIL("<>z__ReadOnlyArray<T>.System.Collections.IList.IndexOf(object)", """ { // Code size 13 (0xd) .maxstack 2 IL_0000: ldarg.0 IL_0001: ldfld "T[] <>z__ReadOnlyArray<T>._items" IL_0006: ldarg.1 IL_0007: callvirt "int System.Collections.IList.IndexOf(object)" IL_000c: ret } """); verifier.VerifyIL("<>z__ReadOnlyArray<T>.System.Collections.IList.Insert(int, object)", expectedNotSupportedIL); verifier.VerifyIL("<>z__ReadOnlyArray<T>.System.Collections.IList.Remove(object)", expectedNotSupportedIL); verifier.VerifyIL("<>z__ReadOnlyArray<T>.System.Collections.IList.RemoveAt(int)", expectedNotSupportedIL); verifier.VerifyIL("<>z__ReadOnlyArray<T>.System.Collections.Generic.IReadOnlyCollection<T>.get_Count()", """ { // Code size 9 (0x9) .maxstack 1 IL_0000: ldarg.0 IL_0001: ldfld "T[] <>z__ReadOnlyArray<T>._items" IL_0006: ldlen IL_0007: conv.i4 IL_0008: ret } """); verifier.VerifyIL("<>z__ReadOnlyArray<T>.System.Collections.Generic.IReadOnlyList<T>.get_Item(int)", """ { // Code size 13 (0xd) .maxstack 2 IL_0000: ldarg.0 IL_0001: ldfld "T[] <>z__ReadOnlyArray<T>._items" IL_0006: ldarg.1 IL_0007: ldelem "T" IL_000c: ret } """); verifier.VerifyIL("<>z__ReadOnlyArray<T>.System.Collections.Generic.ICollection<T>.get_IsReadOnly()", """ { // Code size 2 (0x2) .maxstack 1 IL_0000: ldc.i4.1 IL_0001: ret } """); verifier.VerifyIL("<>z__ReadOnlyArray<T>.System.Collections.Generic.ICollection<T>.get_Count()", """ { // Code size 9 (0x9) .maxstack 1 IL_0000: ldarg.0 IL_0001: ldfld "T[] <>z__ReadOnlyArray<T>._items" IL_0006: ldlen IL_0007: conv.i4 IL_0008: ret } """); verifier.VerifyIL("<>z__ReadOnlyArray<T>.System.Collections.Generic.ICollection<T>.Add(T)", expectedNotSupportedIL); verifier.VerifyIL("<>z__ReadOnlyArray<T>.System.Collections.Generic.ICollection<T>.Clear()", expectedNotSupportedIL); verifier.VerifyIL("<>z__ReadOnlyArray<T>.System.Collections.Generic.ICollection<T>.Contains(T)", """ { // Code size 13 (0xd) .maxstack 2 IL_0000: ldarg.0 IL_0001: ldfld "T[] <>z__ReadOnlyArray<T>._items" IL_0006: ldarg.1 IL_0007: callvirt "bool System.Collections.Generic.ICollection<T>.Contains(T)" IL_000c: ret } """); verifier.VerifyIL("<>z__ReadOnlyArray<T>.System.Collections.Generic.ICollection<T>.CopyTo(T[], int)", """ { // Code size 14 (0xe) .maxstack 3 IL_0000: ldarg.0 IL_0001: ldfld "T[] <>z__ReadOnlyArray<T>._items" IL_0006: ldarg.1 IL_0007: ldarg.2 IL_0008: callvirt "void System.Collections.Generic.ICollection<T>.CopyTo(T[], int)" IL_000d: ret } """); verifier.VerifyIL("<>z__ReadOnlyArray<T>.System.Collections.Generic.ICollection<T>.Remove(T)", expectedNotSupportedIL); verifier.VerifyIL("<>z__ReadOnlyArray<T>.System.Collections.Generic.IList<T>.get_Item(int)", """ { // Code size 13 (0xd) .maxstack 2 IL_0000: ldarg.0 IL_0001: ldfld "T[] <>z__ReadOnlyArray<T>._items" IL_0006: ldarg.1 IL_0007: ldelem "T" IL_000c: ret } """); verifier.VerifyIL("<>z__ReadOnlyArray<T>.System.Collections.Generic.IList<T>.set_Item(int, T)", expectedNotSupportedIL); verifier.VerifyIL("<>z__ReadOnlyArray<T>.System.Collections.Generic.IList<T>.IndexOf(T)", """ { // Code size 13 (0xd) .maxstack 2 IL_0000: ldarg.0 IL_0001: ldfld "T[] <>z__ReadOnlyArray<T>._items" IL_0006: ldarg.1 IL_0007: callvirt "int System.Collections.Generic.IList<T>.IndexOf(T)" IL_000c: ret } """); verifier.VerifyIL("<>z__ReadOnlyArray<T>.System.Collections.Generic.IList<T>.Insert(int, T)", expectedNotSupportedIL); verifier.VerifyIL("<>z__ReadOnlyArray<T>.System.Collections.Generic.IList<T>.RemoveAt(int)", expectedNotSupportedIL); } [CombinatorialData] [Theory] public void SynthesizedReadOnlyList_01([CombinatorialValues("IEnumerable<T>", "IReadOnlyCollection<T>", "IReadOnlyList<T>")] string targetType) { string source = $$""" using System; using System.Collections; using System.Collections.Generic; using static System.Console; class Program { static void Main() { IEnumerable<int> x = EmptyEnumerable<int>(); object[] y = [1, 2, null]; Report<int>([..x]); Report<object>([..x, ..y]); } static IEnumerable<T> EmptyEnumerable<T>() { yield break; } static void Report<T>({{targetType}} x) { int length = ((IReadOnlyCollection<T>)x).Count; T[] a; Write("IEnumerable.GetEnumerator(): "); ((IEnumerable)x).Report(includeType: true); WriteLine(); WriteLine("ICollection.Count: {0}", ((ICollection)x).Count); WriteLine("ICollection.IsSynchronized: {0}", ((ICollection)x).IsSynchronized); WriteLine("ICollection.SyncRoot == (object)x: {0}", ((ICollection)x).SyncRoot == (object)x); Write("ICollection.CopyTo(..., 0): "); a = new T[length]; ((ICollection)x).CopyTo(a, 0); a.Report(includeType: true); WriteLine(); WriteLine("IList.IsFixedSize: {0}", ((IList)x).IsFixedSize); WriteLine("IList.IsReadOnly: {0}", ((IList)x).IsReadOnly); if (length > 1) WriteLine("IList.this[1].get: {0}", ((IList)x)[1]); if (length > 1) WriteLine("IList.this[1].set: {0}", Invoke(() => ((IList)x)[1] = default)); WriteLine("IList.Add(default): {0}", Invoke(() => ((IList)x).Add(default))); WriteLine("IList.Clear(): {0}", Invoke(() => ((IList)x).Clear())); WriteLine("IList.Contains(default): {0}", ((IList)x).Contains(default)); WriteLine("IList.IndexOf(default): {0}", ((IList)x).IndexOf(default)); WriteLine("IList.Insert(0, default): {0}", Invoke(() => ((IList)x).Insert(0, default))); WriteLine("IList.Remove(default): {0}", Invoke(() => ((IList)x).Remove(default))); if (length > 1) WriteLine("IList.RemoveAt(1): {0}", Invoke(() => ((IList)x).RemoveAt(1))); Write("IEnumerable<T>.GetEnumerator(): "); ((IEnumerable<T>)x).Report(includeType: true); WriteLine(); WriteLine("IReadOnlyCollection<T>.Count: {0}", ((IReadOnlyCollection<T>)x).Count); if (length > 1) WriteLine("IReadOnlyList<T>.this[1]: {0}", ((IReadOnlyList<T>)x)[1]); WriteLine("ICollection<T>.Count: {0}", ((ICollection<T>)x).Count); WriteLine("ICollection<T>.IsReadOnly: {0}", ((ICollection<T>)x).IsReadOnly); WriteLine("ICollection<T>.Add(default): {0}", Invoke(() => ((ICollection<T>)x).Add(default))); WriteLine("ICollection<T>.Clear(): {0}", Invoke(() => ((ICollection<T>)x).Clear())); WriteLine("ICollection<T>.Contains(default): {0}", ((ICollection<T>)x).Contains(default)); Write("ICollection<T>.CopyTo(..., 0): "); a = new T[length]; ((ICollection<T>)x).CopyTo(a, 0); a.Report(includeType: true); WriteLine(); WriteLine("ICollection<T>.Remove(default): {0}", Invoke(() => ((ICollection<T>)x).Remove(default))); if (length > 1) WriteLine("IList<T>.this[1].get: {0}", ((IList<T>)x)[1]); if (length > 1) WriteLine("IList<T>.this[1].set: {0}", Invoke(() => ((IList<T>)x)[1] = default)); WriteLine("IList<T>.IndexOf(default): {0}", ((IList<T>)x).IndexOf(default)); WriteLine("IList<T>.Insert(0, default): {0}", Invoke(() => ((IList<T>)x).Insert(0, default))); if (length > 1) WriteLine("IList<T>.RemoveAt(1): {0}", Invoke(() => ((IList<T>)x).RemoveAt(1))); } static string Invoke(Action a) { try { a(); return "completed"; } catch (Exception e) { return e.GetType().FullName; } } } """; var verifier = CompileAndVerify( new[] { source, s_collectionExtensions }, expectedOutput: """ IEnumerable.GetEnumerator(): (<>z__ReadOnlyList<System.Int32>) [], ICollection.Count: 0 ICollection.IsSynchronized: False ICollection.SyncRoot == (object)x: True ICollection.CopyTo(..., 0): (System.Int32[]) [], IList.IsFixedSize: True IList.IsReadOnly: True IList.Add(default): System.NotSupportedException IList.Clear(): System.NotSupportedException IList.Contains(default): False IList.IndexOf(default): -1 IList.Insert(0, default): System.NotSupportedException IList.Remove(default): System.NotSupportedException IEnumerable<T>.GetEnumerator(): (<>z__ReadOnlyList<System.Int32>) [], IReadOnlyCollection<T>.Count: 0 ICollection<T>.Count: 0 ICollection<T>.IsReadOnly: True ICollection<T>.Add(default): System.NotSupportedException ICollection<T>.Clear(): System.NotSupportedException ICollection<T>.Contains(default): False ICollection<T>.CopyTo(..., 0): (System.Int32[]) [], ICollection<T>.Remove(default): System.NotSupportedException IList<T>.IndexOf(default): -1 IList<T>.Insert(0, default): System.NotSupportedException IEnumerable.GetEnumerator(): (<>z__ReadOnlyList<System.Object>) [1, 2, null], ICollection.Count: 3 ICollection.IsSynchronized: False ICollection.SyncRoot == (object)x: True ICollection.CopyTo(..., 0): (System.Object[]) [1, 2, null], IList.IsFixedSize: True IList.IsReadOnly: True IList.this[1].get: 2 IList.this[1].set: System.NotSupportedException IList.Add(default): System.NotSupportedException IList.Clear(): System.NotSupportedException IList.Contains(default): True IList.IndexOf(default): 2 IList.Insert(0, default): System.NotSupportedException IList.Remove(default): System.NotSupportedException IList.RemoveAt(1): System.NotSupportedException IEnumerable<T>.GetEnumerator(): (<>z__ReadOnlyList<System.Object>) [1, 2, null], IReadOnlyCollection<T>.Count: 3 IReadOnlyList<T>.this[1]: 2 ICollection<T>.Count: 3 ICollection<T>.IsReadOnly: True ICollection<T>.Add(default): System.NotSupportedException ICollection<T>.Clear(): System.NotSupportedException ICollection<T>.Contains(default): True ICollection<T>.CopyTo(..., 0): (System.Object[]) [1, 2, null], ICollection<T>.Remove(default): System.NotSupportedException IList<T>.this[1].get: 2 IList<T>.this[1].set: System.NotSupportedException IList<T>.IndexOf(default): 2 IList<T>.Insert(0, default): System.NotSupportedException IList<T>.RemoveAt(1): System.NotSupportedException """); string expectedNotSupportedIL = """ { // Code size 6 (0x6) .maxstack 1 IL_0000: newobj "System.NotSupportedException..ctor()" IL_0005: throw } """; verifier.VerifyIL("<>z__ReadOnlyList<T>..ctor(System.Collections.Generic.List<T>)", """ { // Code size 14 (0xe) .maxstack 2 IL_0000: ldarg.0 IL_0001: call "object..ctor()" IL_0006: ldarg.0 IL_0007: ldarg.1 IL_0008: stfld "System.Collections.Generic.List<T> <>z__ReadOnlyList<T>._items" IL_000d: ret } """); verifier.VerifyIL("<>z__ReadOnlyList<T>.System.Collections.IEnumerable.GetEnumerator()", """ { // Code size 12 (0xc) .maxstack 1 IL_0000: ldarg.0 IL_0001: ldfld "System.Collections.Generic.List<T> <>z__ReadOnlyList<T>._items" IL_0006: callvirt "System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()" IL_000b: ret } """); verifier.VerifyIL("<>z__ReadOnlyList<T>.System.Collections.Generic.IEnumerable<T>.GetEnumerator()", """ { // Code size 12 (0xc) .maxstack 1 IL_0000: ldarg.0 IL_0001: ldfld "System.Collections.Generic.List<T> <>z__ReadOnlyList<T>._items" IL_0006: callvirt "System.Collections.Generic.IEnumerator<T> System.Collections.Generic.IEnumerable<T>.GetEnumerator()" IL_000b: ret } """); verifier.VerifyIL("<>z__ReadOnlyList<T>.System.Collections.ICollection.get_Count()", """ { // Code size 12 (0xc) .maxstack 1 IL_0000: ldarg.0 IL_0001: ldfld "System.Collections.Generic.List<T> <>z__ReadOnlyList<T>._items" IL_0006: callvirt "int System.Collections.Generic.List<T>.Count.get" IL_000b: ret } """); verifier.VerifyIL("<>z__ReadOnlyList<T>.System.Collections.ICollection.get_IsSynchronized()", """ { // Code size 2 (0x2) .maxstack 1 IL_0000: ldc.i4.0 IL_0001: ret } """); verifier.VerifyIL("<>z__ReadOnlyList<T>.System.Collections.ICollection.get_SyncRoot()", """ { // Code size 2 (0x2) .maxstack 1 IL_0000: ldarg.0 IL_0001: ret } """); verifier.VerifyIL("<>z__ReadOnlyList<T>.System.Collections.ICollection.CopyTo(System.Array, int)", """ { // Code size 14 (0xe) .maxstack 3 IL_0000: ldarg.0 IL_0001: ldfld "System.Collections.Generic.List<T> <>z__ReadOnlyList<T>._items" IL_0006: ldarg.1 IL_0007: ldarg.2 IL_0008: callvirt "void System.Collections.ICollection.CopyTo(System.Array, int)" IL_000d: ret } """); verifier.VerifyIL("<>z__ReadOnlyList<T>.System.Collections.IList.get_Item(int)", """ { // Code size 18 (0x12) .maxstack 2 IL_0000: ldarg.0 IL_0001: ldfld "System.Collections.Generic.List<T> <>z__ReadOnlyList<T>._items" IL_0006: ldarg.1 IL_0007: callvirt "T System.Collections.Generic.List<T>.this[int].get" IL_000c: box "T" IL_0011: ret } """); verifier.VerifyIL("<>z__ReadOnlyList<T>.System.Collections.IList.set_Item(int, object)", expectedNotSupportedIL); verifier.VerifyIL("<>z__ReadOnlyList<T>.System.Collections.IList.get_IsFixedSize()", """ { // Code size 2 (0x2) .maxstack 1 IL_0000: ldc.i4.1 IL_0001: ret } """); verifier.VerifyIL("<>z__ReadOnlyList<T>.System.Collections.IList.get_IsReadOnly()", """ { // Code size 2 (0x2) .maxstack 1 IL_0000: ldc.i4.1 IL_0001: ret } """); verifier.VerifyIL("<>z__ReadOnlyList<T>.System.Collections.IList.Add(object)", expectedNotSupportedIL); verifier.VerifyIL("<>z__ReadOnlyList<T>.System.Collections.IList.Clear()", expectedNotSupportedIL); verifier.VerifyIL("<>z__ReadOnlyList<T>.System.Collections.IList.Contains(object)", """ { // Code size 13 (0xd) .maxstack 2 IL_0000: ldarg.0 IL_0001: ldfld "System.Collections.Generic.List<T> <>z__ReadOnlyList<T>._items" IL_0006: ldarg.1 IL_0007: callvirt "bool System.Collections.IList.Contains(object)" IL_000c: ret } """); verifier.VerifyIL("<>z__ReadOnlyList<T>.System.Collections.IList.IndexOf(object)", """ { // Code size 13 (0xd) .maxstack 2 IL_0000: ldarg.0 IL_0001: ldfld "System.Collections.Generic.List<T> <>z__ReadOnlyList<T>._items" IL_0006: ldarg.1 IL_0007: callvirt "int System.Collections.IList.IndexOf(object)" IL_000c: ret } """); verifier.VerifyIL("<>z__ReadOnlyList<T>.System.Collections.IList.Insert(int, object)", expectedNotSupportedIL); verifier.VerifyIL("<>z__ReadOnlyList<T>.System.Collections.IList.Remove(object)", expectedNotSupportedIL); verifier.VerifyIL("<>z__ReadOnlyList<T>.System.Collections.IList.RemoveAt(int)", expectedNotSupportedIL); verifier.VerifyIL("<>z__ReadOnlyList<T>.System.Collections.Generic.IReadOnlyCollection<T>.get_Count()", """ { // Code size 12 (0xc) .maxstack 1 IL_0000: ldarg.0 IL_0001: ldfld "System.Collections.Generic.List<T> <>z__ReadOnlyList<T>._items" IL_0006: callvirt "int System.Collections.Generic.List<T>.Count.get" IL_000b: ret } """); verifier.VerifyIL("<>z__ReadOnlyList<T>.System.Collections.Generic.IReadOnlyList<T>.get_Item(int)", """ { // Code size 13 (0xd) .maxstack 2 IL_0000: ldarg.0 IL_0001: ldfld "System.Collections.Generic.List<T> <>z__ReadOnlyList<T>._items" IL_0006: ldarg.1 IL_0007: callvirt "T System.Collections.Generic.List<T>.this[int].get" IL_000c: ret } """); verifier.VerifyIL("<>z__ReadOnlyList<T>.System.Collections.Generic.ICollection<T>.get_IsReadOnly()", """ { // Code size 2 (0x2) .maxstack 1 IL_0000: ldc.i4.1 IL_0001: ret } """); verifier.VerifyIL("<>z__ReadOnlyList<T>.System.Collections.Generic.ICollection<T>.get_Count()", """ { // Code size 12 (0xc) .maxstack 1 IL_0000: ldarg.0 IL_0001: ldfld "System.Collections.Generic.List<T> <>z__ReadOnlyList<T>._items" IL_0006: callvirt "int System.Collections.Generic.List<T>.Count.get" IL_000b: ret } """); verifier.VerifyIL("<>z__ReadOnlyList<T>.System.Collections.Generic.ICollection<T>.Add(T)", expectedNotSupportedIL); verifier.VerifyIL("<>z__ReadOnlyList<T>.System.Collections.Generic.ICollection<T>.Clear()", expectedNotSupportedIL); verifier.VerifyIL("<>z__ReadOnlyList<T>.System.Collections.Generic.ICollection<T>.Contains(T)", """ { // Code size 13 (0xd) .maxstack 2 IL_0000: ldarg.0 IL_0001: ldfld "System.Collections.Generic.List<T> <>z__ReadOnlyList<T>._items" IL_0006: ldarg.1 IL_0007: callvirt "bool System.Collections.Generic.List<T>.Contains(T)" IL_000c: ret } """); verifier.VerifyIL("<>z__ReadOnlyList<T>.System.Collections.Generic.ICollection<T>.CopyTo(T[], int)", """ { // Code size 14 (0xe) .maxstack 3 IL_0000: ldarg.0 IL_0001: ldfld "System.Collections.Generic.List<T> <>z__ReadOnlyList<T>._items" IL_0006: ldarg.1 IL_0007: ldarg.2 IL_0008: callvirt "void System.Collections.Generic.List<T>.CopyTo(T[], int)" IL_000d: ret } """); verifier.VerifyIL("<>z__ReadOnlyList<T>.System.Collections.Generic.ICollection<T>.Remove(T)", expectedNotSupportedIL); verifier.VerifyIL("<>z__ReadOnlyList<T>.System.Collections.Generic.IList<T>.get_Item(int)", """ { // Code size 13 (0xd) .maxstack 2 IL_0000: ldarg.0 IL_0001: ldfld "System.Collections.Generic.List<T> <>z__ReadOnlyList<T>._items" IL_0006: ldarg.1 IL_0007: callvirt "T System.Collections.Generic.List<T>.this[int].get" IL_000c: ret } """); verifier.VerifyIL("<>z__ReadOnlyList<T>.System.Collections.Generic.IList<T>.set_Item(int, T)", expectedNotSupportedIL); verifier.VerifyIL("<>z__ReadOnlyList<T>.System.Collections.Generic.IList<T>.IndexOf(T)", """ { // Code size 13 (0xd) .maxstack 2 IL_0000: ldarg.0 IL_0001: ldfld "System.Collections.Generic.List<T> <>z__ReadOnlyList<T>._items" IL_0006: ldarg.1 IL_0007: callvirt "int System.Collections.Generic.List<T>.IndexOf(T)" IL_000c: ret } """); verifier.VerifyIL("<>z__ReadOnlyList<T>.System.Collections.Generic.IList<T>.Insert(int, T)", expectedNotSupportedIL); verifier.VerifyIL("<>z__ReadOnlyList<T>.System.Collections.Generic.IList<T>.RemoveAt(int)", expectedNotSupportedIL); } [Fact] public void SynthesizedReadOnlyList_02() { string source = """ using System; using System.Collections.Generic; class Program { static void Main() { foreach (var i in F(1, 2)) { Console.Write("{0}, ", i); } } static IEnumerable<int> F(int x, int y) => [x, y]; } """; CompileAndVerify( source, targetFramework: TargetFramework.Net80, verify: Verification.FailsPEVerify, expectedOutput: IncludeExpectedOutput("1, 2, ")); } [Fact] public void SynthesizedReadOnlyList_03() { string source = """ using System; using System.Collections.Generic; class Program { static void Main() { foreach (var i in F(1, 2, new[] { 3 })) { Console.Write("{0}, ", i); } } static IEnumerable<int> F(int x, int y, IEnumerable<int> e) => [x, y, ..e]; } """; CompileAndVerify( source, targetFramework: TargetFramework.Net80, verify: Verification.FailsPEVerify, expectedOutput: IncludeExpectedOutput("1, 2, 3, ")); } // Compare members of synthesized types to a similar type from source. [Fact] public void SynthesizedReadOnlyList_Members() { string sourceA = """ using System; using System.Collections; using System.Collections.Generic; internal sealed class ReadOnlyArray<T> : IEnumerable, IEnumerable<T>, ICollection, IList, IReadOnlyCollection<T>, IReadOnlyList<T>, ICollection<T>, IList<T> { private readonly T[] _items; public ReadOnlyArray(T[] items) { _items = items; } IEnumerator IEnumerable.GetEnumerator() => _items.GetEnumerator(); IEnumerator<T> IEnumerable<T>.GetEnumerator() => throw new NotSupportedException(); int ICollection.Count => _items.Length; bool ICollection.IsSynchronized => false; object ICollection.SyncRoot => this; void ICollection.CopyTo(Array array, int arrayIndex) { } object IList.this[int index] { get => _items[index]; set => throw new NotSupportedException(); } bool IList.IsFixedSize => true; bool IList.IsReadOnly => true; int IList.Add(object item) => throw new NotSupportedException(); void IList.Clear() => throw new NotSupportedException(); bool IList.Contains(object item) => throw new NotSupportedException(); int IList.IndexOf(object o) => Array.IndexOf(_items, o); void IList.Insert(int index, object item) => throw new NotSupportedException(); void IList.Remove(object item) => throw new NotSupportedException(); void IList.RemoveAt(int index) => throw new NotSupportedException(); int IReadOnlyCollection<T>.Count => _items.Length; T IReadOnlyList<T>.this[int index] => _items[index]; int ICollection<T>.Count => _items.Length; bool ICollection<T>.IsReadOnly => true; void ICollection<T>.Add(T item) => throw new NotSupportedException(); void ICollection<T>.Clear() => throw new NotSupportedException(); bool ICollection<T>.Contains(T item) => throw new NotSupportedException(); void ICollection<T>.CopyTo(T[] array, int arrayIndex) { } T IList<T>.this[int index] { get => _items[index]; set => throw new NotSupportedException(); } int IList<T>.IndexOf(T t) => Array.IndexOf<T>(_items, t); void IList<T>.Insert(int index, T item) => throw new NotSupportedException(); bool ICollection<T>.Remove(T item) => throw new NotSupportedException(); void IList<T>.RemoveAt(int index) => throw new NotSupportedException(); } """; // Collection expressions below ensure the types are synthesized. string sourceB = """ using System.Collections.Generic; class Program { static void Main() { IEnumerable<int> x = [0, 1]; IEnumerable<int> y = [..x]; } } """; var verifier = CompileAndVerify( new[] { sourceA, sourceB }, targetFramework: TargetFramework.Net80, verify: Verification.FailsPEVerify); var sourceType = ((CSharpCompilation)verifier.Compilation).GetMember<NamedTypeSymbol>("ReadOnlyArray"); verifier.TestData.TryGetMethodData("<>z__ReadOnlyArray<T>..ctor(T[])", out var arrayMemberData); verifier.TestData.TryGetMethodData("<>z__ReadOnlyList<T>..ctor(System.Collections.Generic.List<T>)", out var listMemberData); compareTypes(sourceType, ((MethodSymbol)arrayMemberData.Method).ContainingType); compareTypes(sourceType, ((MethodSymbol)listMemberData.Method).ContainingType); static void compareTypes(NamedTypeSymbol sourceType, NamedTypeSymbol synthesizedType) { compareMembers(sourceType, synthesizedType, "System.Collections.ICollection.Count"); compareMembers(sourceType, synthesizedType, "System.Collections.IList.this[]"); compareMembers(sourceType, synthesizedType, "System.Collections.IList.get_Item"); compareMembers(sourceType, synthesizedType, "System.Collections.IList.set_Item"); compareMembers(sourceType, synthesizedType, "System.Collections.IList.Contains"); compareMembers(sourceType, synthesizedType, "System.Collections.Generic.ICollection<T>.IsReadOnly"); compareMembers(sourceType, synthesizedType, "System.Collections.Generic.ICollection<T>.get_IsReadOnly"); compareMembers(sourceType, synthesizedType, "System.Collections.Generic.ICollection<T>.Contains"); compareMembers(sourceType, synthesizedType, "System.Collections.Generic.IList<T>.this[]"); compareMembers(sourceType, synthesizedType, "System.Collections.Generic.IList<T>.get_Item"); compareMembers(sourceType, synthesizedType, "System.Collections.Generic.IList<T>.set_Item"); } static void compareMembers(NamedTypeSymbol sourceType, NamedTypeSymbol synthesizedType, string memberName) { var sourceMember = sourceType.GetMembers(memberName).Single(); var synthesizedMember = synthesizedType.GetMembers(memberName).Single(); Assert.Equal(sourceMember.IsAbstract, synthesizedMember.IsAbstract); Assert.Equal(sourceMember.IsVirtual, synthesizedMember.IsVirtual); Assert.Equal(sourceMember.IsOverride, synthesizedMember.IsOverride); } } [Theory] [InlineData(SpecialType.System_Collections_IEnumerable, "System.Collections.IEnumerable")] [InlineData(SpecialType.System_Collections_Generic_IEnumerable_T, "System.Collections.Generic.IEnumerable`1")] [InlineData(SpecialType.System_Collections_Generic_IReadOnlyCollection_T, "System.Collections.Generic.IReadOnlyCollection`1", true)] [InlineData(SpecialType.System_Collections_Generic_IReadOnlyList_T, "System.Collections.Generic.IReadOnlyList`1", true)] [InlineData(SpecialType.System_Collections_Generic_ICollection_T, "System.Collections.Generic.ICollection`1")] [InlineData(SpecialType.System_Collections_Generic_IList_T, "System.Collections.Generic.IList`1")] public void SynthesizedReadOnlyList_MissingSpecialTypes(SpecialType missingType, string missingTypeName, bool isOptional = false) { string source = """ using System.Collections.Generic; class Program { static void Main() { IEnumerable<int> x = [0]; IEnumerable<int> y = [..x]; } } """; var comp = CreateCompilation(source); comp.MakeTypeMissing(missingType); comp.VerifyEmitDiagnostics(isOptional ? [] : [ // (6,30): error CS0518: Predefined type 'System.Collections.IEnumerable' is not defined or imported // IEnumerable<int> x = [0]; Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, "[0]").WithArguments(missingTypeName).WithLocation(6, 30), // (7,30): error CS0518: Predefined type 'System.Collections.IEnumerable' is not defined or imported // IEnumerable<int> y = [..x]; Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, "[..x]").WithArguments(missingTypeName).WithLocation(7, 30), ]); } [Theory] [InlineData(new SpecialType[0])] [InlineData(new[] { SpecialType.System_Collections_Generic_IReadOnlyCollection_T })] [InlineData(new[] { SpecialType.System_Collections_Generic_IReadOnlyList_T })] [InlineData(new[] { SpecialType.System_Collections_Generic_IReadOnlyCollection_T, SpecialType.System_Collections_Generic_IReadOnlyList_T })] public void SynthesizedReadOnlyList_MissingOptionalSpecialTypes_01(SpecialType[] missingTypes) { string source = """ using System.Collections.Generic; class Program { static void Main() { IEnumerable<int> x = [0]; IEnumerable<int> y = [0, 1]; IEnumerable<int> z = [..x]; } } """; var comp = CreateCompilation(source); foreach (var missingType in missingTypes) { comp.MakeTypeMissing(missingType); } var verifier = CompileAndVerify( comp, symbolValidator: module => { verifyInterfaces(module, "<>z__ReadOnlyArray"); verifyInterfaces(module, "<>z__ReadOnlyList"); verifyInterfaces(module, "<>z__ReadOnlySingleElementList"); }); verifier.VerifyDiagnostics(); void verifyInterfaces(ModuleSymbol module, string typeName) { var synthesizedType = module.GlobalNamespace.GetTypeMember(typeName); var interfaces = synthesizedType.InterfacesNoUseSiteDiagnostics(); AssertEx.Equal( missingTypes is [] ? new[] { "System.Collections.IEnumerable", "System.Collections.ICollection", "System.Collections.IList", "System.Collections.Generic.IEnumerable<T>", "System.Collections.Generic.IReadOnlyCollection<T>", "System.Collections.Generic.IReadOnlyList<T>", "System.Collections.Generic.ICollection<T>", "System.Collections.Generic.IList<T>", } : new[] { "System.Collections.IEnumerable", "System.Collections.ICollection", "System.Collections.IList", "System.Collections.Generic.IEnumerable<T>", "System.Collections.Generic.ICollection<T>", "System.Collections.Generic.IList<T>", }, interfaces.ToTestDisplayStrings()); } } [ConditionalFact(typeof(CoreClrOnly))] public void SynthesizedReadOnlyList_MissingOptionalSpecialTypes_02() { string runtime = @" namespace System { using System.Collections; public class NotSupportedException : Exception {} public class Array : ICollection, IList { public static T[] Empty<T>() => throw null; IEnumerator IEnumerable.GetEnumerator() => throw null; void ICollection.CopyTo(Array array, int index) => throw null; int ICollection.Count => throw null; object ICollection.SyncRoot => throw null; bool ICollection.IsSynchronized => throw null; object IList.this[int index] { get => throw null; set => throw null; } int IList.Add(object value) => throw null; bool IList.Contains(object value) => throw null; void IList.Clear() => throw null; bool IList.IsReadOnly => throw null; bool IList.IsFixedSize => throw null; int IList.IndexOf(object value) => throw null; void IList.Insert(int index, object value)=> throw null; void IList.Remove(object value) => throw null; void IList.RemoveAt(int index)=> throw null; } public class Attribute { } [Flags] public enum AttributeTargets { Assembly = 0x1, Module = 0x2, Class = 0x4, Struct = 0x8, Enum = 0x10, Constructor = 0x20, Method = 0x40, Property = 0x80, Field = 0x100, Event = 0x200, Interface = 0x400, Parameter = 0x800, Delegate = 0x1000, ReturnValue = 0x2000, GenericParameter = 0x4000, All = 0x7FFF } [AttributeUsage(AttributeTargets.Class, Inherited = true)] public sealed class AttributeUsageAttribute : Attribute { public AttributeUsageAttribute(AttributeTargets validOn) { } public bool AllowMultiple { get => throw null; set { } } public bool Inherited { get => throw null; set { } } public AttributeTargets ValidOn => throw null; } public struct Boolean { } public struct Byte { } public class Delegate { public static Delegate CreateDelegate(Type type, object firstArgument, Reflection.MethodInfo method) => null; } public abstract class Enum : IComparable { } public class Exception { } public class FlagsAttribute : Attribute { } public delegate T Func<out T>(); public delegate U Func<in T, out U>(T arg); public interface IComparable { } public interface IDisposable { void Dispose(); } public struct Int16 { } public struct Int32 { } public struct Int64 { } public struct IntPtr { } public class MulticastDelegate : Delegate { } public struct Nullable<T> { } public class Object { } public sealed class ParamArrayAttribute : Attribute { } public struct RuntimeMethodHandle { } public struct RuntimeTypeHandle { } public class String : IComparable { public static String Empty = null; } public class Type { public static Type GetTypeFromHandle(RuntimeTypeHandle handle) => null; } public class ValueType { } public struct Void { } namespace Collections { public interface IEnumerable { IEnumerator GetEnumerator(); } public interface IEnumerator { object Current { get; } bool MoveNext(); void Reset(); } public interface ICollection : IEnumerable { void CopyTo(Array array, int index); int Count { get; } object SyncRoot { get; } bool IsSynchronized { get; } } public interface IList : ICollection { object this[int index] { get; set; } int Add(object value); bool Contains(object value); void Clear(); bool IsReadOnly { get; } bool IsFixedSize { get; } int IndexOf(object value); void Insert(int index, object value); void Remove(object value); void RemoveAt(int index); } } namespace Collections.Generic { public interface IEnumerable<out T> : IEnumerable { new IEnumerator<T> GetEnumerator(); } public interface IEnumerator<out T> : IEnumerator, IDisposable { new T Current { get; } } public interface ICollection<T> : IEnumerable<T> { int Count { get; } bool IsReadOnly { get; } void Add(T item); void Clear(); bool Contains(T item); void CopyTo(T[] array, int arrayIndex); bool Remove(T item); } public interface IList<T> : ICollection<T> { T this[int index] { get; set; } int IndexOf(T item); void Insert(int index, T item); void RemoveAt(int index); } public class List<T> : System.Collections.Generic.ICollection<T>, System.Collections.Generic.IEnumerable<T>, System.Collections.Generic.IList<T>, System.Collections.ICollection, System.Collections.IEnumerable, System.Collections.IList { public List() { } public List(System.Collections.Generic.IEnumerable<T> collection) { } public List(int capacity) { } public int Capacity { get { throw null; } set { } } public int Count { get { throw null; } } public T this[int index] { get { throw null; } set { } } bool System.Collections.Generic.ICollection<T>.IsReadOnly { get { throw null; } } bool System.Collections.ICollection.IsSynchronized { get { throw null; } } object System.Collections.ICollection.SyncRoot { get { throw null; } } bool System.Collections.IList.IsFixedSize { get { throw null; } } bool System.Collections.IList.IsReadOnly { get { throw null; } } object System.Collections.IList.this[int index] { get { throw null; } set { } } public void Add(T item) { } public void Clear() { } public bool Contains(T item) { throw null; } public void CopyTo(int index, T[] array, int arrayIndex, int count) { } public void CopyTo(T[] array) { } public void CopyTo(T[] array, int arrayIndex) { } public int EnsureCapacity(int capacity) { throw null; } public int IndexOf(T item) { throw null; } public int IndexOf(T item, int index) { throw null; } public int IndexOf(T item, int index, int count) { throw null; } public void Insert(int index, T item) { } public bool Remove(T item) { throw null; } public void RemoveAt(int index) { } System.Collections.Generic.IEnumerator<T> System.Collections.Generic.IEnumerable<T>.GetEnumerator() { throw null; } void System.Collections.ICollection.CopyTo(System.Array array, int arrayIndex) { } System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { throw null; } int System.Collections.IList.Add(object item) { throw null; } bool System.Collections.IList.Contains(object item) { throw null; } int System.Collections.IList.IndexOf(object item) { throw null; } void System.Collections.IList.Insert(int index, object item) { } void System.Collections.IList.Remove(object item) { } public T[] ToArray() { throw null; } } } namespace Reflection { public class AssemblyVersionAttribute : Attribute { public AssemblyVersionAttribute(string version) { } } public class DefaultMemberAttribute : Attribute { public DefaultMemberAttribute(string name) { } } public abstract class MemberInfo { } public abstract class MethodBase : MemberInfo { public static MethodBase GetMethodFromHandle(RuntimeMethodHandle handle) => throw null; } public abstract class MethodInfo : MethodBase { public virtual Delegate CreateDelegate(Type delegateType, object target) => throw null; } } } "; string source = """ using System.Collections.Generic; class Program { static void Main() { IEnumerable<int> x = [0, 1]; IEnumerable<int> z = [..x]; } } """; var reference = CreateEmptyCompilation(runtime, assemblyName: "System.Runtime").VerifyDiagnostics().EmitToImageReference(); var comp = CreateEmptyCompilation(source, references: [reference]); var verifier = CompileAndVerify( comp, symbolValidator: module => { verifyInterfaces(module, "<>z__ReadOnlyArray"); verifyInterfaces(module, "<>z__ReadOnlyList"); }); verifier.VerifyDiagnostics(); void verifyInterfaces(ModuleSymbol module, string typeName) { var synthesizedType = module.GlobalNamespace.GetTypeMember(typeName); var interfaces = synthesizedType.InterfacesNoUseSiteDiagnostics(); AssertEx.Equal( new[] { "System.Collections.IEnumerable", "System.Collections.ICollection", "System.Collections.IList", "System.Collections.Generic.IEnumerable<T>", "System.Collections.Generic.ICollection<T>", "System.Collections.Generic.IList<T>", }, interfaces.ToTestDisplayStrings()); } } [Theory] [InlineData((int)SpecialMember.System_Collections_IEnumerable__GetEnumerator, "System.Collections.IEnumerable", "GetEnumerator")] [InlineData((int)SpecialMember.System_Collections_Generic_IEnumerable_T__GetEnumerator, "System.Collections.Generic.IEnumerable`1", "GetEnumerator")] [InlineData((int)SpecialMember.System_Collections_Generic_IReadOnlyCollection_T__Count, "System.Collections.Generic.IReadOnlyCollection`1", "Count")] [InlineData((int)SpecialMember.System_Collections_Generic_IReadOnlyList_T__get_Item, "System.Collections.Generic.IReadOnlyList`1", "get_Item")] [InlineData((int)SpecialMember.System_Collections_Generic_ICollection_T__Count, "System.Collections.Generic.ICollection`1", "Count")] [InlineData((int)SpecialMember.System_Collections_Generic_ICollection_T__IsReadOnly, "System.Collections.Generic.ICollection`1", "IsReadOnly")] [InlineData((int)SpecialMember.System_Collections_Generic_ICollection_T__Add, "System.Collections.Generic.ICollection`1", "Add")] [InlineData((int)SpecialMember.System_Collections_Generic_ICollection_T__Clear, "System.Collections.Generic.ICollection`1", "Clear")] [InlineData((int)SpecialMember.System_Collections_Generic_ICollection_T__Contains, "System.Collections.Generic.ICollection`1", "Contains")] [InlineData((int)SpecialMember.System_Collections_Generic_ICollection_T__CopyTo, "System.Collections.Generic.ICollection`1", "CopyTo")] [InlineData((int)SpecialMember.System_Collections_Generic_ICollection_T__Remove, "System.Collections.Generic.ICollection`1", "Remove")] [InlineData((int)SpecialMember.System_Collections_Generic_IList_T__get_Item, "System.Collections.Generic.IList`1", "get_Item")] [InlineData((int)SpecialMember.System_Collections_Generic_IList_T__IndexOf, "System.Collections.Generic.IList`1", "IndexOf")] [InlineData((int)SpecialMember.System_Collections_Generic_IList_T__Insert, "System.Collections.Generic.IList`1", "Insert")] [InlineData((int)SpecialMember.System_Collections_Generic_IList_T__RemoveAt, "System.Collections.Generic.IList`1", "RemoveAt")] public void SynthesizedReadOnlyList_MissingSpecialMembers(int missingMember, string missingMemberTypeName, string missingMemberName) { string source = """ using System.Collections.Generic; class Program { static void Main() { IEnumerable<int> x = [0]; IEnumerable<int> y = [..x]; } } """; var comp = CreateCompilation(source); comp.MakeMemberMissing((SpecialMember)missingMember); comp.VerifyEmitDiagnostics( // (6,30): error CS0656: Missing compiler required member 'System.Collections.IEnumerable.GetEnumerator' // IEnumerable<int> x = [0]; Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "[0]").WithArguments(missingMemberTypeName, missingMemberName).WithLocation(6, 30), // (7,30): error CS0656: Missing compiler required member 'System.Collections.IEnumerable.GetEnumerator' // IEnumerable<int> y = [..x]; Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "[..x]").WithArguments(missingMemberTypeName, missingMemberName).WithLocation(7, 30)); } [Theory] [InlineData((int)WellKnownType.System_Collections_ICollection, "System.Collections.ICollection")] [InlineData((int)WellKnownType.System_Collections_IList, "System.Collections.IList")] public void SynthesizedReadOnlyList_MissingWellKnownTypes(int missingType, string missingTypeName) { string source = """ using System.Collections.Generic; class Program { static void Main() { IEnumerable<int> x = [0]; IEnumerable<int> y = [..x]; } } """; var comp = CreateCompilation(source); comp.MakeTypeMissing((WellKnownType)missingType); comp.VerifyEmitDiagnostics( // (6,30): error CS0518: Predefined type 'System.Collections.IEnumerable' is not defined or imported // IEnumerable<int> x = [0]; Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, "[0]").WithArguments(missingTypeName).WithLocation(6, 30), // (7,30): error CS0518: Predefined type 'System.Collections.IEnumerable' is not defined or imported // IEnumerable<int> y = [..x]; Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, "[..x]").WithArguments(missingTypeName).WithLocation(7, 30)); } [Fact] public void SynthesizedReadOnlyList_MissingWellKnownTypes_List() { string source = """ using System.Collections.Generic; class Program { static void Main() { IEnumerable<int> x = [0]; IEnumerable<int> y = [..x]; } } """; var comp = CreateCompilation(source); comp.MakeTypeMissing(WellKnownType.System_Collections_Generic_List_T); comp.VerifyEmitDiagnostics( // (6,30): error CS0656: Missing compiler required member 'System.Collections.Generic.List`1..ctor' // IEnumerable<int> x = [0]; Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "[0]").WithArguments("System.Collections.Generic.List`1", ".ctor").WithLocation(6, 30), // (6,30): error CS0656: Missing compiler required member 'System.Collections.Generic.List`1..ctor' // IEnumerable<int> x = [0]; Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "[0]").WithArguments("System.Collections.Generic.List`1", ".ctor").WithLocation(6, 30), // (6,30): error CS0656: Missing compiler required member 'System.Collections.Generic.List`1.Add' // IEnumerable<int> x = [0]; Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "[0]").WithArguments("System.Collections.Generic.List`1", "Add").WithLocation(6, 30), // (6,30): error CS0656: Missing compiler required member 'System.Collections.Generic.List`1.ToArray' // IEnumerable<int> x = [0]; Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "[0]").WithArguments("System.Collections.Generic.List`1", "ToArray").WithLocation(6, 30), // (7,30): error CS0656: Missing compiler required member 'System.Collections.Generic.List`1..ctor' // IEnumerable<int> y = [..x]; Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "[..x]").WithArguments("System.Collections.Generic.List`1", ".ctor").WithLocation(7, 30), // (7,30): error CS0656: Missing compiler required member 'System.Collections.Generic.List`1..ctor' // IEnumerable<int> y = [..x]; Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "[..x]").WithArguments("System.Collections.Generic.List`1", ".ctor").WithLocation(7, 30), // (7,30): error CS0656: Missing compiler required member 'System.Collections.Generic.List`1.Add' // IEnumerable<int> y = [..x]; Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "[..x]").WithArguments("System.Collections.Generic.List`1", "Add").WithLocation(7, 30), // (7,30): error CS0656: Missing compiler required member 'System.Collections.Generic.List`1.ToArray' // IEnumerable<int> y = [..x]; Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "[..x]").WithArguments("System.Collections.Generic.List`1", "ToArray").WithLocation(7, 30)); } [Theory] [InlineData((int)WellKnownMember.System_Collections_ICollection__Count, "System.Collections.ICollection", "Count")] [InlineData((int)WellKnownMember.System_Collections_ICollection__IsSynchronized, "System.Collections.ICollection", "IsSynchronized")] [InlineData((int)WellKnownMember.System_Collections_ICollection__SyncRoot, "System.Collections.ICollection", "SyncRoot")] [InlineData((int)WellKnownMember.System_Collections_ICollection__CopyTo, "System.Collections.ICollection", "CopyTo")] [InlineData((int)WellKnownMember.System_Collections_IList__get_Item, "System.Collections.IList", "get_Item")] [InlineData((int)WellKnownMember.System_Collections_IList__IsFixedSize, "System.Collections.IList", "IsFixedSize")] [InlineData((int)WellKnownMember.System_Collections_IList__IsReadOnly, "System.Collections.IList", "IsReadOnly")] [InlineData((int)WellKnownMember.System_Collections_IList__Add, "System.Collections.IList", "Add")] [InlineData((int)WellKnownMember.System_Collections_IList__Clear, "System.Collections.IList", "Clear")] [InlineData((int)WellKnownMember.System_Collections_IList__Contains, "System.Collections.IList", "Contains")] [InlineData((int)WellKnownMember.System_Collections_IList__IndexOf, "System.Collections.IList", "IndexOf")] [InlineData((int)WellKnownMember.System_Collections_IList__Insert, "System.Collections.IList", "Insert")] [InlineData((int)WellKnownMember.System_Collections_IList__Remove, "System.Collections.IList", "Remove")] [InlineData((int)WellKnownMember.System_Collections_IList__RemoveAt, "System.Collections.IList", "RemoveAt")] [InlineData((int)WellKnownMember.System_NotSupportedException__ctor, "System.NotSupportedException", ".ctor")] public void SynthesizedReadOnlyList_MissingWellKnownMembers(int missingMember, string missingMemberTypeName, string missingMemberName) { string source = """ using System.Collections.Generic; class Program { static void Main() { IEnumerable<int> x = [0]; IEnumerable<int> y = [..x]; } } """; var comp = CreateCompilation(source); comp.MakeMemberMissing((WellKnownMember)missingMember); comp.VerifyEmitDiagnostics( // (6,30): error CS0656: Missing compiler required member 'System.Collections.Generic.IReadOnlyCollection`1.Count' // IEnumerable<int> x = [0]; Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "[0]").WithArguments(missingMemberTypeName, missingMemberName).WithLocation(6, 30), // (7,30): error CS0656: Missing compiler required member 'System.Collections.Generic.IReadOnlyCollection`1.Count' // IEnumerable<int> y = [..x]; Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "[..x]").WithArguments(missingMemberTypeName, missingMemberName).WithLocation(7, 30)); } [Theory] [InlineData((int)WellKnownMember.System_Collections_Generic_List_T__Contains, "System.Collections.Generic.List`1", "Contains")] [InlineData((int)WellKnownMember.System_Collections_Generic_List_T__CopyTo, "System.Collections.Generic.List`1", "CopyTo")] [InlineData((int)WellKnownMember.System_Collections_Generic_List_T__get_Item, "System.Collections.Generic.List`1", "get_Item")] [InlineData((int)WellKnownMember.System_Collections_Generic_List_T__IndexOf, "System.Collections.Generic.List`1", "IndexOf")] public void SynthesizedReadOnlyList_MissingWellKnownMembers_UnknownLength(int missingMember, string missingMemberTypeName, string missingMemberName) { string source = """ using System.Collections.Generic; class Program { static void Main() { IEnumerable<int> x = [0]; IEnumerable<int> y = [..x]; } } """; var comp = CreateCompilation(source); comp.MakeMemberMissing((WellKnownMember)missingMember); comp.VerifyEmitDiagnostics( // (7,30): error CS0656: Missing compiler required member 'System.Collections.Generic.IReadOnlyCollection`1.Count' // IEnumerable<int> y = [..x]; Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "[..x]").WithArguments(missingMemberTypeName, missingMemberName).WithLocation(7, 30)); } [Fact] public void SynthesizedReadOnlyList_Dynamic() { string source = """ using System.Collections.Generic; class Program { static void Main() { dynamic d = 2; IEnumerable<int> x = [1, d, default]; IEnumerable<dynamic> y = [1, d, default]; x.Report(includeType: true); y.Report(includeType: true); } } """; CompileAndVerify( new[] { source, s_collectionExtensions }, references: new[] { CSharpRef }, expectedOutput: "(<>z__ReadOnlyArray<System.Int32>) [1, 2, 0], (<>z__ReadOnlyArray<System.Object>) [1, 2, null], "); } [Fact] [WorkItem("https://github.com/dotnet/roslyn/issues/72539")] [WorkItem("https://github.com/dotnet/roslyn/issues/74676")] public void SynthesizedCollections_EnsureAttributes() { string source = """ using System; using System.Collections.Generic; class Program { static void Main() { IEnumerable<int> x = [1]; IEnumerable<int> y = [2, 3]; IEnumerable<int> z = [.. x]; Report(x); Report(y); Report(z); } static void Report<T>(IEnumerable<T> e) { var type = e.GetType(); Console.Write("{0}: ", type.Name); foreach (var a in type.GetCustomAttributes(inherit: false)) Console.Write("{0}, ", a); Console.WriteLine(); } } """; CompileAndVerify( source, symbolValidator: module => { var globalNamespace = module.GlobalNamespace; verifyCompilerGeneratedType(globalNamespace.GetTypeMember("<>z__ReadOnlySingleElementList")); verifyCompilerGeneratedType(globalNamespace.GetTypeMember("<>z__ReadOnlyArray")); verifyCompilerGeneratedType(globalNamespace.GetTypeMember("<>z__ReadOnlyList")); }, expectedOutput: """ <>z__ReadOnlySingleElementList`1: System.Diagnostics.DebuggerDisplayAttribute, System.Runtime.CompilerServices.CompilerGeneratedAttribute, <>z__ReadOnlyArray`1: System.Diagnostics.DebuggerDisplayAttribute, System.Runtime.CompilerServices.CompilerGeneratedAttribute, <>z__ReadOnlyList`1: System.Diagnostics.DebuggerDisplayAttribute, System.Runtime.CompilerServices.CompilerGeneratedAttribute, """); static void verifyCompilerGeneratedType(NamedTypeSymbol type) { Assert.Collection(type.GetAttributes(), a => { Assert.Equal("System.Diagnostics.DebuggerDisplayAttribute", a.AttributeClass?.ToTestDisplayString()); Assert.Equal("Count = {System.Collections.ICollection.Count}", a.CommonConstructorArguments.Single().Value); }, a => Assert.Equal("System.Runtime.CompilerServices.CompilerGeneratedAttribute", a.AttributeClass?.ToTestDisplayString())); Assert.DoesNotContain(type.GetMembers(), m => m.GetAttributes().Any(a => a.AttributeClass?.ToTestDisplayString() == "System.Runtime.CompilerServices.CompilerGeneratedAttribute")); } } [Fact] public void Nullable_01() { string source = """ #nullable enable class Program { static void Main() { object?[] x = [1]; x[0].ToString(); // 1 object[] y = [null]; // 2 y[0].ToString(); y = [2, null]; // 3 y[1].ToString(); object[]? z = []; z.ToString(); z = [3]; z.ToString(); } } """; var comp = CreateCompilation(source); comp.VerifyEmitDiagnostics( // (7,9): warning CS8602: Dereference of a possibly null reference. // x[0].ToString(); // 1 Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "x[0]").WithLocation(7, 9), // (8,23): warning CS8625: Cannot convert null literal to non-nullable reference type. // object[] y = [null]; // 2 Diagnostic(ErrorCode.WRN_NullAsNonNullable, "null").WithLocation(8, 23), // (10,17): warning CS8625: Cannot convert null literal to non-nullable reference type. // y = [2, null]; // 3 Diagnostic(ErrorCode.WRN_NullAsNonNullable, "null").WithLocation(10, 17)); } [Fact] public void Nullable_02() { string source = """ #nullable enable using System.Collections.Generic; class Program { static void Main() { List<object?> x = [1]; x[0].ToString(); // 1 List<object> y = [null]; // 2 y[0].ToString(); y = [2, null]; // 3 y[1].ToString(); List<object>? z = []; z.ToString(); z = [3]; z.ToString(); } } """; var comp = CreateCompilation(source); comp.VerifyEmitDiagnostics( // (8,9): warning CS8602: Dereference of a possibly null reference. // x[0].ToString(); // 1 Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "x[0]").WithLocation(8, 9), // (9,27): warning CS8625: Cannot convert null literal to non-nullable reference type. // List<object> y = [null]; // 2 Diagnostic(ErrorCode.WRN_NullAsNonNullable, "null").WithLocation(9, 27), // (11,17): warning CS8625: Cannot convert null literal to non-nullable reference type. // y = [2, null]; // 3 Diagnostic(ErrorCode.WRN_NullAsNonNullable, "null").WithLocation(11, 17)); } [Fact] public void Nullable_03() { string source = """ #nullable enable using System.Collections; struct S<T> : IEnumerable { public void Add(T t) { } public T this[int index] => default!; IEnumerator IEnumerable.GetEnumerator() => default!; } class Program { static void Main() { S<object?> x = [1]; x[0].ToString(); // 1 S<object> y = [null]; // 2 y[0].ToString(); y = [2, null]; // 3 y[1].ToString(); } } """; var comp = CreateCompilation(source); comp.VerifyEmitDiagnostics( // (14,9): warning CS8602: Dereference of a possibly null reference. // x[0].ToString(); // 1 Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "x[0]").WithLocation(14, 9), // (15,24): warning CS8625: Cannot convert null literal to non-nullable reference type. // S<object> y = [null]; // 2 Diagnostic(ErrorCode.WRN_NullAsNonNullable, "null").WithLocation(15, 24), // (17,17): warning CS8625: Cannot convert null literal to non-nullable reference type. // y = [2, null]; // 3 Diagnostic(ErrorCode.WRN_NullAsNonNullable, "null").WithLocation(17, 17)); } [Fact] public void Nullable_04() { string source = """ #nullable enable using System.Collections; S<object>? x = []; x.Report(); x = []; S<object>? y = [1]; y = [2]; struct S<T> : IEnumerable { public void Add(T t) { } public T this[int index] => default!; IEnumerator IEnumerable.GetEnumerator() { yield break; } } """; var comp = CreateCompilation(new[] { source, s_collectionExtensions }); comp.VerifyEmitDiagnostics(); CompileAndVerify(comp, expectedOutput: "[],"); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75560")] public void Nullable_Spread_01() { var source = """ #nullable enable using System.Collections.Generic; { IEnumerable<object?> a = [null]; object[] b = [..a]; } { IEnumerable<object?> a = [null]; object[] b = [..a!]; } """; CreateCompilation(source).VerifyDiagnostics( // (5,21): warning CS8601: Possible null reference assignment. // object[] b = [..a]; Diagnostic(ErrorCode.WRN_NullReferenceAssignment, "a").WithLocation(5, 21)); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75560")] public void Nullable_Spread_02() { var source = """ #nullable enable using System.Collections.Generic; { IEnumerable<object>? a = null; object[] b = [..a]; } { IEnumerable<object>? a = null; object[] b = [..a!]; } """; CreateCompilation(source).VerifyDiagnostics( // (5,21): warning CS8602: Dereference of a possibly null reference. // object[] b = [..a]; Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "a").WithLocation(5, 21)); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/75560")] public void Nullable_Spread_03() { var source = """ #nullable enable using System.Collections.Generic; { IEnumerable<object?> a1 = [null]; IEnumerable<object?> a2 = [null]; object[] b = [..(m() ? a1 : a2)]; } { IEnumerable<object?> a1 = [null]; IEnumerable<object?> a2 = [null]; object[] b = [..(m() ? a1 : a2)!]; } bool m() => throw null!; """; CreateCompilation(source).VerifyDiagnostics( // (6,22): warning CS8601: Possible null reference assignment. // object[] b = [..(m() ? a1 : a2)]; Diagnostic(ErrorCode.WRN_NullReferenceAssignment, "m() ? a1 : a2").WithLocation(6, 22)); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/69447")] public void NullableValueType_ImplicitConversion() { string src = """ using System; using System.Collections; using System.Collections.Generic; using System.Runtime.CompilerServices; Program.M().Report(); [CollectionBuilder(typeof(MyCollectionBuilder), nameof(MyCollectionBuilder.Create))] public struct MyCollection<T> : IEnumerable<T> { private readonly List<T> _list; public MyCollection(List<T> list) { _list = list; } public IEnumerator<T> GetEnumerator() => _list.GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); } public class MyCollectionBuilder { public static MyCollection<T> Create<T>(ReadOnlySpan<T> items) { return new MyCollection<T>(new List<T>(items.ToArray())); } } partial class Program { static MyCollection<int>? M() { return [1, 2, 3]; } } """; var comp = CreateCompilation(new[] { src, s_collectionExtensions }, targetFramework: TargetFramework.Net80); comp.VerifyDiagnostics(); var verifier = CompileAndVerify(comp, expectedOutput: IncludeExpectedOutput("[1, 2, 3],"), verify: Verification.FailsPEVerify); verifier.VerifyIL("Program.M", """ { // Code size 21 (0x15) .maxstack 1 IL_0000: ldtoken "<PrivateImplementationDetails>.__StaticArrayInitTypeSize=12_Align=4 <PrivateImplementationDetails>.4636993D3E1DA4E9D6B8F87B79E8F7C6D018580D52661950EABC3845C5897A4D4" IL_0005: call "System.ReadOnlySpan<int> System.Runtime.CompilerServices.RuntimeHelpers.CreateSpan<int>(System.RuntimeFieldHandle)" IL_000a: call "MyCollection<int> MyCollectionBuilder.Create<int>(System.ReadOnlySpan<int>)" IL_000f: newobj "MyCollection<int>?..ctor(MyCollection<int>)" IL_0014: ret } """); var tree = comp.SyntaxTrees.First(); var model = comp.GetSemanticModel(tree); var returnValue = tree.GetRoot().DescendantNodes().OfType<ReturnStatementSyntax>().Last().Expression; var conversion = model.GetConversion(returnValue); Assert.True(conversion.IsValid); Assert.True(conversion.IsNullable); Assert.False(conversion.IsCollectionExpression); Assert.Equal(1, conversion.UnderlyingConversions.Length); var underlyingConversion = conversion.UnderlyingConversions[0]; Assert.True(underlyingConversion.IsValid); Assert.False(underlyingConversion.IsNullable); Assert.True(underlyingConversion.IsCollectionExpression); var typeInfo = model.GetTypeInfo(returnValue); Assert.Null(typeInfo.Type); Assert.Equal("MyCollection<System.Int32>?", typeInfo.ConvertedType.ToTestDisplayString()); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/69447")] public void NullableValueType_ImplicitConversion_Byte() { string src = """ using System; using System.Collections; using System.Collections.Generic; using System.Runtime.CompilerServices; Program.M().Report(); [CollectionBuilder(typeof(MyCollectionBuilder), nameof(MyCollectionBuilder.Create))] public struct MyCollection<T> : IEnumerable<T> { private readonly List<T> _list; public MyCollection(List<T> list) { _list = list; } public IEnumerator<T> GetEnumerator() => _list.GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); } public class MyCollectionBuilder { public static MyCollection<T> Create<T>(ReadOnlySpan<T> items) { return new MyCollection<T>(new List<T>(items.ToArray())); } } partial class Program { static MyCollection<byte>? M() { return [1, 2, 3]; } } """; var comp = CreateCompilation(new[] { src, s_collectionExtensions }, targetFramework: TargetFramework.Net80); comp.VerifyDiagnostics(); var verifier = CompileAndVerify(comp, expectedOutput: IncludeExpectedOutput("[1, 2, 3],"), verify: Verification.Fails with { ILVerifyMessage = """ [M]: Cannot change initonly field outside its .ctor. { Offset = 0x0 } [M]: Unexpected type on the stack. { Offset = 0x6, Found = address of '<PrivateImplementationDetails>+__StaticArrayInitTypeSize=3', Expected = Native Int } """ }); verifier.VerifyIL("Program.M", """ { // Code size 22 (0x16) .maxstack 2 IL_0000: ldsflda "<PrivateImplementationDetails>.__StaticArrayInitTypeSize=3 <PrivateImplementationDetails>.039058C6F2C0CB492C533B0A4D14EF77CC0F78ABCCCED5287D84A1A2011CFB81" IL_0005: ldc.i4.3 IL_0006: newobj "System.ReadOnlySpan<byte>..ctor(void*, int)" IL_000b: call "MyCollection<byte> MyCollectionBuilder.Create<byte>(System.ReadOnlySpan<byte>)" IL_0010: newobj "MyCollection<byte>?..ctor(MyCollection<byte>)" IL_0015: ret } """); var tree = comp.SyntaxTrees.First(); var model = comp.GetSemanticModel(tree); var returnValue = tree.GetRoot().DescendantNodes().OfType<ReturnStatementSyntax>().Last().Expression; var conversion = model.GetConversion(returnValue); Assert.True(conversion.IsValid); Assert.True(conversion.IsNullable); Assert.False(conversion.IsCollectionExpression); Assert.Equal(1, conversion.UnderlyingConversions.Length); var underlyingConversion = conversion.UnderlyingConversions[0]; Assert.True(underlyingConversion.IsValid); Assert.False(underlyingConversion.IsNullable); Assert.True(underlyingConversion.IsCollectionExpression); var typeInfo = model.GetTypeInfo(returnValue); Assert.Null(typeInfo.Type); Assert.Equal("MyCollection<System.Byte>?", typeInfo.ConvertedType.ToTestDisplayString()); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/69447")] public void NullableValueType_ImplicitConversion_Nullability() { string src = """ #nullable enable using System; using System.Collections; using System.Collections.Generic; using System.Runtime.CompilerServices; MyCollection<int>? x = [1, 2, 3]; x.Value.ToString(); x = null; x.Value.ToString(); // 1 [CollectionBuilder(typeof(MyCollectionBuilder), nameof(MyCollectionBuilder.Create))] public struct MyCollection<T> : IEnumerable<T> { private readonly List<T> _list; public MyCollection(List<T> list) { _list = list; } public IEnumerator<T> GetEnumerator() => _list.GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); } public class MyCollectionBuilder { public static MyCollection<T> Create<T>(ReadOnlySpan<T> items) { return new MyCollection<T>(new List<T>(items.ToArray())); } } """; var comp = CreateCompilation(new[] { src, s_collectionExtensions }, targetFramework: TargetFramework.Net80); comp.VerifyDiagnostics( // 0.cs(11,1): warning CS8629: Nullable value type may be null. // x.Value.ToString(); // 1 Diagnostic(ErrorCode.WRN_NullableValueTypeMayBeNull, "x").WithLocation(11, 1) ); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/69447")] public void NullableValueType_BadConversion() { string src = """ int? x = [1, 2, 3]; """; var comp = CreateCompilation(new[] { src, s_collectionExtensions }, targetFramework: TargetFramework.Net80); comp.VerifyDiagnostics( // 0.cs(1,10): error CS9174: Cannot initialize type 'int?' with a collection expression because the type is not constructible. // int? x = [1, 2, 3]; Diagnostic(ErrorCode.ERR_CollectionExpressionTargetTypeNotConstructible, "[1, 2, 3]").WithArguments("int?").WithLocation(1, 10) ); var tree = comp.SyntaxTrees.First(); var model = comp.GetSemanticModel(tree); var collection = tree.GetRoot().DescendantNodes().OfType<CollectionExpressionSyntax>().Single(); var conversion = model.GetConversion(collection); Assert.False(conversion.IsValid); var typeInfo = model.GetTypeInfo(collection); Assert.Null(typeInfo.Type); Assert.Equal("System.Int32?", typeInfo.ConvertedType.ToTestDisplayString()); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/69447")] public void NullableValueType_ExplicitCast() { string src = """ using System; using System.Collections; using System.Collections.Generic; using System.Runtime.CompilerServices; Program.M().Report(); [CollectionBuilder(typeof(MyCollectionBuilder), nameof(MyCollectionBuilder.Create))] public struct MyCollection<T> : IEnumerable<T> { private readonly List<T> _list; public MyCollection(List<T> list) { _list = list; } public IEnumerator<T> GetEnumerator() => _list.GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); } public class MyCollectionBuilder { public static MyCollection<T> Create<T>(ReadOnlySpan<T> items) { return new MyCollection<T>(new List<T>(items.ToArray())); } } partial class Program { static MyCollection<int>? M() { return (MyCollection<int>?)[1, 2, 3]; } } """; var comp = CreateCompilation(new[] { src, s_collectionExtensions }, targetFramework: TargetFramework.Net80); comp.VerifyDiagnostics(); var verifier = CompileAndVerify(comp, expectedOutput: IncludeExpectedOutput("[1, 2, 3],"), verify: Verification.FailsPEVerify); verifier.VerifyIL("Program.M", """ { // Code size 21 (0x15) .maxstack 1 IL_0000: ldtoken "<PrivateImplementationDetails>.__StaticArrayInitTypeSize=12_Align=4 <PrivateImplementationDetails>.4636993D3E1DA4E9D6B8F87B79E8F7C6D018580D52661950EABC3845C5897A4D4" IL_0005: call "System.ReadOnlySpan<int> System.Runtime.CompilerServices.RuntimeHelpers.CreateSpan<int>(System.RuntimeFieldHandle)" IL_000a: call "MyCollection<int> MyCollectionBuilder.Create<int>(System.ReadOnlySpan<int>)" IL_000f: newobj "MyCollection<int>?..ctor(MyCollection<int>)" IL_0014: ret } """); var tree = comp.SyntaxTrees.First(); var model = comp.GetSemanticModel(tree); var cast = tree.GetRoot().DescendantNodes().OfType<ReturnStatementSyntax>().Last().Expression; Assert.Equal("(MyCollection<int>?)[1, 2, 3]", cast.ToFullString()); var castConversion = model.GetConversion(cast); Assert.True(castConversion.IsIdentity); var value = tree.GetRoot().DescendantNodes().OfType<CastExpressionSyntax>().Last().Expression; Assert.Equal("[1, 2, 3]", value.ToFullString()); var conversion = model.GetConversion(value); Assert.True(conversion.IsIdentity); var typeInfo = model.GetTypeInfo(value); Assert.Null(typeInfo.Type); Assert.Null(typeInfo.ConvertedType); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/69447")] public void NullableValueType_MissingSystemNullableCtor() { string src = """ using System; using System.Collections; using System.Collections.Generic; using System.Runtime.CompilerServices; Program.M().Report(); [CollectionBuilder(typeof(MyCollectionBuilder), nameof(MyCollectionBuilder.Create))] public struct MyCollection<T> : IEnumerable<T> { private readonly List<T> _list; public MyCollection(List<T> list) { _list = list; } public IEnumerator<T> GetEnumerator() => _list.GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); } public class MyCollectionBuilder { public static MyCollection<T> Create<T>(ReadOnlySpan<T> items) { return new MyCollection<T>(new List<T>(items.ToArray())); } } partial class Program { static MyCollection<int>? M() { return (MyCollection<int>?)[1, 2, 3]; } } """; var comp = CreateCompilation(new[] { src, s_collectionExtensions }, targetFramework: TargetFramework.Net80); comp.MakeMemberMissing(SpecialMember.System_Nullable_T__ctor); comp.VerifyDiagnostics( // 0.cs(29,36): error CS0656: Missing compiler required member 'System.Nullable`1..ctor' // return (MyCollection<int>?)[1, 2, 3]; Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "[1, 2, 3]").WithArguments("System.Nullable`1", ".ctor").WithLocation(29, 36) ); } [Fact] public void ExplicitCast_SemanticModel() { string src = """ using System; using System.Collections; using System.Collections.Generic; using System.Runtime.CompilerServices; Program.M().Report(); [CollectionBuilder(typeof(MyCollectionBuilder), nameof(MyCollectionBuilder.Create))] public struct MyCollection<T> : IEnumerable<T> { private readonly List<T> _list; public MyCollection(List<T> list) { _list = list; } public IEnumerator<T> GetEnumerator() => _list.GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); } public class MyCollectionBuilder { public static MyCollection<T> Create<T>(ReadOnlySpan<T> items) { return new MyCollection<T>(new List<T>(items.ToArray())); } } partial class Program { static MyCollection<int> M() { return (MyCollection<int>)/*<bind>*/[1, 2, 3]/*</bind>*/; } } """; var comp = CreateCompilation(new[] { src, s_collectionExtensions }, targetFramework: TargetFramework.Net80); comp.VerifyDiagnostics(); var verifier = CompileAndVerify(comp, expectedOutput: IncludeExpectedOutput("[1, 2, 3],"), verify: Verification.FailsPEVerify); verifier.VerifyIL("Program.M", """ { // Code size 16 (0x10) .maxstack 1 IL_0000: ldtoken "<PrivateImplementationDetails>.__StaticArrayInitTypeSize=12_Align=4 <PrivateImplementationDetails>.4636993D3E1DA4E9D6B8F87B79E8F7C6D018580D52661950EABC3845C5897A4D4" IL_0005: call "System.ReadOnlySpan<int> System.Runtime.CompilerServices.RuntimeHelpers.CreateSpan<int>(System.RuntimeFieldHandle)" IL_000a: call "MyCollection<int> MyCollectionBuilder.Create<int>(System.ReadOnlySpan<int>)" IL_000f: ret } """); VerifyOperationTreeForTest<CollectionExpressionSyntax>(comp, """ ICollectionExpressionOperation (3 elements, ConstructMethod: MyCollection<System.Int32> MyCollectionBuilder.Create<System.Int32>(System.ReadOnlySpan<System.Int32> items)) (OperationKind.CollectionExpression, Type: MyCollection<System.Int32>) (Syntax: '[1, 2, 3]') ConstructArguments(1): IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: items) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: '[1, 2, 3]') ICollectionExpressionElementsPlaceholderOperation (OperationKind.CollectionExpressionElementsPlaceholder, Type: System.ReadOnlySpan<System.Int32>, IsImplicit) (Syntax: '[1, 2, 3]') InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) Elements(3): ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1) (Syntax: '1') ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 2) (Syntax: '2') ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 3) (Syntax: '3') """); var tree = comp.SyntaxTrees.First(); var model = comp.GetSemanticModel(tree); var cast = tree.GetRoot().DescendantNodes().OfType<ReturnStatementSyntax>().Last().Expression; Assert.Equal("(MyCollection<int>)/*<bind>*/[1, 2, 3]/*</bind>*/", cast.ToFullString()); var castConversion = model.GetConversion(cast); Assert.True(castConversion.IsIdentity); var value = tree.GetRoot().DescendantNodes().OfType<CastExpressionSyntax>().Last().Expression; Assert.Equal("[1, 2, 3]/*</bind>*/", value.ToFullString()); var conversion = model.GetConversion(value); Assert.True(conversion.IsValid); Assert.True(conversion.IsIdentity); var typeInfo = model.GetTypeInfo(value); Assert.Null(typeInfo.Type); Assert.Null(typeInfo.ConvertedType); } [Fact] public void NestedCollection_SemanticModel() { string src = """ Program.M().Report(); partial class Program { static int[][] M() { return /*<bind>*/[[1], [2]]/*</bind>*/; } } """; var comp = CreateCompilation(new[] { src, s_collectionExtensions }, targetFramework: TargetFramework.Net80); comp.VerifyDiagnostics(); var verifier = CompileAndVerify(comp, expectedOutput: IncludeExpectedOutput("[[1], [2]],"), verify: Verification.FailsPEVerify); verifier.VerifyIL("Program.M", """ { // Code size 33 (0x21) .maxstack 7 IL_0000: ldc.i4.2 IL_0001: newarr "int[]" IL_0006: dup IL_0007: ldc.i4.0 IL_0008: ldc.i4.1 IL_0009: newarr "int" IL_000e: dup IL_000f: ldc.i4.0 IL_0010: ldc.i4.1 IL_0011: stelem.i4 IL_0012: stelem.ref IL_0013: dup IL_0014: ldc.i4.1 IL_0015: ldc.i4.1 IL_0016: newarr "int" IL_001b: dup IL_001c: ldc.i4.0 IL_001d: ldc.i4.2 IL_001e: stelem.i4 IL_001f: stelem.ref IL_0020: ret } """); VerifyOperationTreeForTest<CollectionExpressionSyntax>(comp, """ ICollectionExpressionOperation (2 elements, ConstructMethod: null) (OperationKind.CollectionExpression, Type: System.Int32[][]) (Syntax: '[[1], [2]]') Elements(2): IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Int32[], IsImplicit) (Syntax: '[1]') Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) Operand: ICollectionExpressionOperation (1 elements, ConstructMethod: null) (OperationKind.CollectionExpression, Type: System.Int32[]) (Syntax: '[1]') Elements(1): ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1) (Syntax: '1') IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Int32[], IsImplicit) (Syntax: '[2]') Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) Operand: ICollectionExpressionOperation (1 elements, ConstructMethod: null) (OperationKind.CollectionExpression, Type: System.Int32[]) (Syntax: '[2]') Elements(1): ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 2) (Syntax: '2') """); var tree = comp.SyntaxTrees.First(); var model = comp.GetSemanticModel(tree); var nestedCollection = tree.GetRoot().DescendantNodes().OfType<CollectionExpressionSyntax>().Last(); Assert.Equal("[2]", nestedCollection.ToFullString()); var conversion = model.GetConversion(nestedCollection); Assert.True(conversion.IsValid); Assert.False(conversion.IsIdentity); Assert.True(conversion.IsCollectionExpression); var typeInfo = model.GetTypeInfo(nestedCollection); Assert.Null(typeInfo.Type); Assert.Equal("System.Int32[]", typeInfo.ConvertedType.ToTestDisplayString()); } [Fact] public void NestedCollection_NullableValueType_SemanticModel() { string src = """ using System; using System.Collections; using System.Collections.Generic; using System.Runtime.CompilerServices; Program.M().Report(); [CollectionBuilder(typeof(MyCollectionBuilder), nameof(MyCollectionBuilder.Create))] public struct MyCollection<T> : IEnumerable<T> { private readonly List<T> _list; public MyCollection(List<T> list) { _list = list; } public IEnumerator<T> GetEnumerator() => _list.GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); } public class MyCollectionBuilder { public static MyCollection<T> Create<T>(ReadOnlySpan<T> items) { return new MyCollection<T>(new List<T>(items.ToArray())); } } partial class Program { static MyCollection<MyCollection<int>?> M() { return [[1], [2]]; } } """; var comp = CreateCompilation(new[] { src, s_collectionExtensions }, targetFramework: TargetFramework.Net80); comp.VerifyDiagnostics(); // ILVerify failure: //[InlineArrayAsReadOnlySpan]: Return type is ByRef, TypedReference, ArgHandle, or ArgIterator. { Offset = 0x18 } var verifier = CompileAndVerify(comp, expectedOutput: IncludeExpectedOutput("[[1], [2]],"), verify: Verification.Fails); verifier.VerifyIL("Program.M", """ { // Code size 88 (0x58) .maxstack 2 .locals init (<>y__InlineArray2<MyCollection<int>?> V_0) IL_0000: ldloca.s V_0 IL_0002: initobj "<>y__InlineArray2<MyCollection<int>?>" IL_0008: ldloca.s V_0 IL_000a: ldc.i4.0 IL_000b: call "ref MyCollection<int>? <PrivateImplementationDetails>.InlineArrayElementRef<<>y__InlineArray2<MyCollection<int>?>, MyCollection<int>?>(ref <>y__InlineArray2<MyCollection<int>?>, int)" IL_0010: ldtoken "<PrivateImplementationDetails>.__StaticArrayInitTypeSize=4_Align=4 <PrivateImplementationDetails>.67ABDD721024F0FF4E0B3F4C2FC13BC5BAD42D0B7851D456D88D203D15AAA4504" IL_0015: call "System.ReadOnlySpan<int> System.Runtime.CompilerServices.RuntimeHelpers.CreateSpan<int>(System.RuntimeFieldHandle)" IL_001a: call "MyCollection<int> MyCollectionBuilder.Create<int>(System.ReadOnlySpan<int>)" IL_001f: newobj "MyCollection<int>?..ctor(MyCollection<int>)" IL_0024: stobj "MyCollection<int>?" IL_0029: ldloca.s V_0 IL_002b: ldc.i4.1 IL_002c: call "ref MyCollection<int>? <PrivateImplementationDetails>.InlineArrayElementRef<<>y__InlineArray2<MyCollection<int>?>, MyCollection<int>?>(ref <>y__InlineArray2<MyCollection<int>?>, int)" IL_0031: ldtoken "<PrivateImplementationDetails>.__StaticArrayInitTypeSize=4_Align=4 <PrivateImplementationDetails>.26B25D457597A7B0463F9620F666DD10AA2C4373A505967C7C8D70922A2D6ECE4" IL_0036: call "System.ReadOnlySpan<int> System.Runtime.CompilerServices.RuntimeHelpers.CreateSpan<int>(System.RuntimeFieldHandle)" IL_003b: call "MyCollection<int> MyCollectionBuilder.Create<int>(System.ReadOnlySpan<int>)" IL_0040: newobj "MyCollection<int>?..ctor(MyCollection<int>)" IL_0045: stobj "MyCollection<int>?" IL_004a: ldloca.s V_0 IL_004c: ldc.i4.2 IL_004d: call "System.ReadOnlySpan<MyCollection<int>?> <PrivateImplementationDetails>.InlineArrayAsReadOnlySpan<<>y__InlineArray2<MyCollection<int>?>, MyCollection<int>?>(in <>y__InlineArray2<MyCollection<int>?>, int)" IL_0052: call "MyCollection<MyCollection<int>?> MyCollectionBuilder.Create<MyCollection<int>?>(System.ReadOnlySpan<MyCollection<int>?>)" IL_0057: ret } """); var tree = comp.SyntaxTrees.First(); var model = comp.GetSemanticModel(tree); var nestedCollection = tree.GetRoot().DescendantNodes().OfType<CollectionExpressionSyntax>().Last(); Assert.Equal("[2]", nestedCollection.ToFullString()); var conversion = model.GetConversion(nestedCollection); Assert.True(conversion.IsValid); Assert.False(conversion.IsIdentity); Assert.True(conversion.IsNullable); Assert.Equal(1, conversion.UnderlyingConversions.Length); var underlyingConversion = conversion.UnderlyingConversions[0]; Assert.True(underlyingConversion.IsValid); Assert.False(underlyingConversion.IsNullable); Assert.True(underlyingConversion.IsCollectionExpression); var typeInfo = model.GetTypeInfo(nestedCollection); Assert.Null(typeInfo.Type); Assert.Equal("MyCollection<System.Int32>?", typeInfo.ConvertedType.ToTestDisplayString()); } [Fact] public void OrderOfEvaluation() { string source = """ using System; using System.Collections; using System.Collections.Generic; class C<T> : IEnumerable<T> { private List<T> _list = new List<T>(); public void Add(T t) { Console.WriteLine("Add {0}", t); _list.Add(t); } IEnumerator<T> IEnumerable<T>.GetEnumerator() => _list.GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => throw null; } class Program { static void Main() { C<int> x = [Get(1), Get(2)]; C<C<int>> y = [[Get(3)], [Get(4), Get(5)]]; } static int Get(int value) { Console.WriteLine("Get {0}", value); return value; } } """; CompileAndVerify(source, expectedOutput: """ Get 1 Add 1 Get 2 Add 2 Get 3 Add 3 Add C`1[System.Int32] Get 4 Add 4 Get 5 Add 5 Add C`1[System.Int32] """); } // Ensure collection expression conversions are not standard implicit conversions // and, as a result, are ignored when determining user-defined conversions. [Fact] public void UserDefinedConversions_01() { string source = """ struct S { public static implicit operator S(int[] a) => default; } class Program { static void Main() { S s = []; s = [1, 2]; s = (S)([3, 4]); } } """; var comp = CreateCompilation(source); comp.VerifyEmitDiagnostics( // (9,15): error CS9174: Cannot initialize type 'S' with a collection expression because the type is not constructible. // S s = []; Diagnostic(ErrorCode.ERR_CollectionExpressionTargetTypeNotConstructible, "[]").WithArguments("S").WithLocation(9, 15), // (10,13): error CS9174: Cannot initialize type 'S' with a collection expression because the type is not constructible. // s = [1, 2]; Diagnostic(ErrorCode.ERR_CollectionExpressionTargetTypeNotConstructible, "[1, 2]").WithArguments("S").WithLocation(10, 13), // (11,17): error CS9174: Cannot initialize type 'S' with a collection expression because the type is not constructible. // s = (S)([3, 4]); Diagnostic(ErrorCode.ERR_CollectionExpressionTargetTypeNotConstructible, "[3, 4]").WithArguments("S").WithLocation(11, 17)); } [Fact] public void UserDefinedConversions_02() { string source = """ struct S { public static explicit operator S(int[] a) => default; } class Program { static void Main() { S s = []; s = [1, 2]; s = (S)([3, 4]); } } """; var comp = CreateCompilation(source); comp.VerifyEmitDiagnostics( // (9,15): error CS9174: Cannot initialize type 'S' with a collection expression because the type is not constructible. // S s = []; Diagnostic(ErrorCode.ERR_CollectionExpressionTargetTypeNotConstructible, "[]").WithArguments("S").WithLocation(9, 15), // (10,13): error CS9174: Cannot initialize type 'S' with a collection expression because the type is not constructible. // s = [1, 2]; Diagnostic(ErrorCode.ERR_CollectionExpressionTargetTypeNotConstructible, "[1, 2]").WithArguments("S").WithLocation(10, 13), // (11,17): error CS9174: Cannot initialize type 'S' with a collection expression because the type is not constructible. // s = (S)([3, 4]); Diagnostic(ErrorCode.ERR_CollectionExpressionTargetTypeNotConstructible, "[3, 4]").WithArguments("S").WithLocation(11, 17)); } [Fact] public void PrimaryConstructorParameters_01() { string source = """ struct S(int x, int y, int z) { int[] F = [x, y]; int[] M() => [y]; static void Main() { var s = new S(1, 2, 3); s.F.Report(); s.M().Report(); } } """; var comp = CreateCompilation(new[] { source, s_collectionExtensions }, options: TestOptions.ReleaseExe); comp.VerifyEmitDiagnostics( // 0.cs(1,28): warning CS9113: Parameter 'z' is unread. // struct S(int x, int y, int z) Diagnostic(ErrorCode.WRN_UnreadPrimaryConstructorParameter, "z").WithArguments("z").WithLocation(1, 28)); var verifier = CompileAndVerify(comp, expectedOutput: "[1, 2], [2], "); verifier.VerifyIL("S..ctor(int, int, int)", """ { // Code size 33 (0x21) .maxstack 5 IL_0000: ldarg.0 IL_0001: ldarg.2 IL_0002: stfld "int S.<y>P" IL_0007: ldarg.0 IL_0008: ldc.i4.2 IL_0009: newarr "int" IL_000e: dup IL_000f: ldc.i4.0 IL_0010: ldarg.1 IL_0011: stelem.i4 IL_0012: dup IL_0013: ldc.i4.1 IL_0014: ldarg.0 IL_0015: ldfld "int S.<y>P" IL_001a: stelem.i4 IL_001b: stfld "int[] S.F" IL_0020: ret } """); } [Fact] public void PrimaryConstructorParameters_02() { string source = """ using System; class C(int x, int y, int z) { Func<int[]> F = () => [x, y]; Func<int[]> M() => () => [y]; static void Main() { var c = new C(1, 2, 3); c.F().Report(); c.M()().Report(); } } """; var comp = CreateCompilation(new[] { source, s_collectionExtensions }, options: TestOptions.ReleaseExe); comp.VerifyEmitDiagnostics( // 0.cs(2,27): warning CS9113: Parameter 'z' is unread. // class C(int x, int y, int z) Diagnostic(ErrorCode.WRN_UnreadPrimaryConstructorParameter, "z").WithArguments("z").WithLocation(2, 27)); var verifier = CompileAndVerify(comp, verify: Verification.Fails, expectedOutput: "[1, 2], [2], "); verifier.VerifyIL("C..ctor(int, int, int)", """ { // Code size 52 (0x34) .maxstack 3 .locals init (C.<>c__DisplayClass0_0 V_0) //CS$<>8__locals0 IL_0000: ldarg.0 IL_0001: ldarg.2 IL_0002: stfld "int C.<y>P" IL_0007: newobj "C.<>c__DisplayClass0_0..ctor()" IL_000c: stloc.0 IL_000d: ldloc.0 IL_000e: ldarg.1 IL_000f: stfld "int C.<>c__DisplayClass0_0.x" IL_0014: ldloc.0 IL_0015: ldarg.0 IL_0016: stfld "C C.<>c__DisplayClass0_0.<>4__this" IL_001b: ldarg.0 IL_001c: ldloc.0 IL_001d: ldftn "int[] C.<>c__DisplayClass0_0.<.ctor>b__0()" IL_0023: newobj "System.Func<int[]>..ctor(object, System.IntPtr)" IL_0028: stfld "System.Func<int[]> C.F" IL_002d: ldarg.0 IL_002e: call "object..ctor()" IL_0033: ret } """); } [Fact] public void PrimaryConstructorParameters_03() { string source = """ using System.Collections.Generic; class A(int[] x, List<int> y) { public int[] X = x; public List<int> Y = y; } class B(int x, int y, int z) : A([y, z], [z]) { } class Program { static void Main() { var b = new B(1, 2, 3); b.X.Report(); b.Y.Report(); } } """; var comp = CreateCompilation(new[] { source, s_collectionExtensions }, options: TestOptions.ReleaseExe); comp.VerifyEmitDiagnostics( // 0.cs(7,13): warning CS9113: Parameter 'x' is unread. // class B(int x, int y, int z) : A([y, z], [z]) Diagnostic(ErrorCode.WRN_UnreadPrimaryConstructorParameter, "x").WithArguments("x").WithLocation(7, 13)); var verifier = CompileAndVerify(comp, expectedOutput: "[2, 3], [3], "); verifier.VerifyIL("B..ctor(int, int, int)", """ { // Code size 34 (0x22) .maxstack 5 IL_0000: ldarg.0 IL_0001: ldc.i4.2 IL_0002: newarr "int" IL_0007: dup IL_0008: ldc.i4.0 IL_0009: ldarg.2 IL_000a: stelem.i4 IL_000b: dup IL_000c: ldc.i4.1 IL_000d: ldarg.3 IL_000e: stelem.i4 IL_000f: ldc.i4.1 IL_0010: newobj "System.Collections.Generic.List<int>..ctor(int)" IL_0015: dup IL_0016: ldarg.3 IL_0017: callvirt "void System.Collections.Generic.List<int>.Add(int)" IL_001c: call "A..ctor(int[], System.Collections.Generic.List<int>)" IL_0021: ret } """); } [Fact] public void SemanticModel() { string source = """ using System; using System.Collections; using System.Collections.Generic; struct S1 : IEnumerable { public void Add(object o) { } IEnumerator IEnumerable.GetEnumerator() => throw null; } struct S2 { } class Program { static void Main() { int[] v1 = []; List<object> v2 = []; Span<int> v3 = []; ReadOnlySpan<object> v4 = []; S1 v5 = []; S2 v6 = []; var v7 = (int[])[]; var v8 = (List<object>)[]; var v9 = (Span<int>)[]; var v10 = (ReadOnlySpan<object>)[]; var v11 = (S1)([]); var v12 = (S2)([]); } } """; var comp = CreateCompilation(source, targetFramework: TargetFramework.Net70); comp.VerifyEmitDiagnostics( // (21,17): error CS9174: Cannot initialize type 'S2' with a collection expression because the type is not constructible. // S2 v6 = []; Diagnostic(ErrorCode.ERR_CollectionExpressionTargetTypeNotConstructible, "[]").WithArguments("S2").WithLocation(21, 17), // (27,24): error CS9174: Cannot initialize type 'S2' with a collection expression because the type is not constructible. // var v12 = (S2)([]); Diagnostic(ErrorCode.ERR_CollectionExpressionTargetTypeNotConstructible, "[]").WithArguments("S2").WithLocation(27, 24)); var tree = comp.SyntaxTrees[0]; var model = comp.GetSemanticModel(tree); var collections = tree.GetRoot().DescendantNodes().OfType<CollectionExpressionSyntax>().ToArray(); Assert.Equal(12, collections.Length); VerifyTypes(model, collections[0], expectedType: null, expectedConvertedType: "System.Int32[]", ConversionKind.CollectionExpression); VerifyTypes(model, collections[1], expectedType: null, expectedConvertedType: "System.Collections.Generic.List<System.Object>", ConversionKind.CollectionExpression); VerifyTypes(model, collections[2], expectedType: null, expectedConvertedType: "System.Span<System.Int32>", ConversionKind.CollectionExpression); VerifyTypes(model, collections[3], expectedType: null, expectedConvertedType: "System.ReadOnlySpan<System.Object>", ConversionKind.CollectionExpression); VerifyTypes(model, collections[4], expectedType: null, expectedConvertedType: "S1", ConversionKind.CollectionExpression); VerifyTypes(model, collections[5], expectedType: null, expectedConvertedType: "S2", ConversionKind.NoConversion); VerifyTypes(model, collections[6], expectedType: null, expectedConvertedType: null, ConversionKind.Identity); VerifyTypes(model, collections[7], expectedType: null, expectedConvertedType: null, ConversionKind.Identity); VerifyTypes(model, collections[8], expectedType: null, expectedConvertedType: null, ConversionKind.Identity); VerifyTypes(model, collections[9], expectedType: null, expectedConvertedType: null, ConversionKind.Identity); VerifyTypes(model, collections[10], expectedType: null, expectedConvertedType: null, ConversionKind.Identity); VerifyTypes(model, collections[11], expectedType: null, expectedConvertedType: null, ConversionKind.Identity); } private static void VerifyTypes(SemanticModel model, ExpressionSyntax expr, string expectedType, string expectedConvertedType, ConversionKind expectedConversionKind) { var typeInfo = model.GetTypeInfo(expr); var conversion = model.GetConversion(expr); Assert.Equal(expectedType, typeInfo.Type?.ToTestDisplayString()); Assert.Equal(expectedConvertedType, typeInfo.ConvertedType?.ToTestDisplayString()); Assert.Equal(expectedConversionKind, conversion.Kind); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/72541")] public void NamedArgumentConversion() { var source = """ #nullable enable using System.Collections.Generic; static class C { static void Main() { C.M(y: [new D { }]); } static void M(string x, IReadOnlyList<D> y) { } } class D { } """; var comp = CreateCompilation(source); comp.VerifyDiagnostics( // (8,11): error CS7036: There is no argument given that corresponds to the required parameter 'x' of 'C.M(string, IReadOnlyList<D>)' // C.M(y: [new D { }]); Diagnostic(ErrorCode.ERR_NoCorrespondingArgument, "M").WithArguments("x", "C.M(string, System.Collections.Generic.IReadOnlyList<D>)").WithLocation(8, 11)); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/72541")] public void NamedArgumentConversion_CollectionInitializer() { var source = """ #nullable enable using System.Collections.Generic; static class C { static void Main() { C.M(y: new() { new D() { } }); } static void M(string x, List<D> y) { } } class D { } """; var comp = CreateCompilation(source); comp.VerifyDiagnostics( // (8,11): error CS7036: There is no argument given that corresponds to the required parameter 'x' of 'C.M(string, List<D>)' // C.M(y: new() { new D() { } }); Diagnostic(ErrorCode.ERR_NoCorrespondingArgument, "M").WithArguments("x", "C.M(string, System.Collections.Generic.List<D>)").WithLocation(8, 11)); } [CombinatorialData] [Theory] public void CollectionBuilder_01(bool useCompilationReference) { string sourceA = """ using System; using System.Collections; using System.Collections.Generic; using System.Runtime.CompilerServices; [CollectionBuilder(typeof(MyCollectionBuilder), nameof(MyCollectionBuilder.Create))] public struct MyCollection<T> : IEnumerable<T> { private readonly List<T> _list; public MyCollection(List<T> list) { _list = list; } public IEnumerator<T> GetEnumerator() => _list.GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); } public class MyCollectionBuilder { public static MyCollection<T> Create<T>(ReadOnlySpan<T> items) { return new MyCollection<T>(new List<T>(items.ToArray())); } } """; var comp = CreateCompilation(sourceA, targetFramework: TargetFramework.Net80); var refA = AsReference(comp, useCompilationReference); string sourceB1 = """ class Program { static void Main() { MyCollection<string> x = F0(); x.Report(); MyCollection<int> y = F1(); y.Report(); MyCollection<object> z = F2(3, 4); z.Report(); } static MyCollection<string> F0() { return []; } static MyCollection<int> F1() { return [0, 1, 2]; } static MyCollection<object> F2(int x, object y) { return [x, y, null]; } } """; var verifier = CompileAndVerify( new[] { sourceB1, s_collectionExtensions }, references: new[] { refA }, targetFramework: TargetFramework.Net80, verify: Verification.Fails, expectedOutput: IncludeExpectedOutput("[], [0, 1, 2], [3, 4, null], ")); verifier.VerifyIL("Program.F0", """ { // Code size 15 (0xf) .maxstack 1 .locals init (System.ReadOnlySpan<string> V_0) IL_0000: ldloca.s V_0 IL_0002: initobj "System.ReadOnlySpan<string>" IL_0008: ldloc.0 IL_0009: call "MyCollection<string> MyCollectionBuilder.Create<string>(System.ReadOnlySpan<string>)" IL_000e: ret } """); verifier.VerifyIL("Program.F1", """ { // Code size 16 (0x10) .maxstack 1 IL_0000: ldtoken "<PrivateImplementationDetails>.__StaticArrayInitTypeSize=12_Align=4 <PrivateImplementationDetails>.AD5DC1478DE06A4C2728EA528BD9361A4B945E92A414BF4D180CEDAAEAA5F4CC4" IL_0005: call "System.ReadOnlySpan<int> System.Runtime.CompilerServices.RuntimeHelpers.CreateSpan<int>(System.RuntimeFieldHandle)" IL_000a: call "MyCollection<int> MyCollectionBuilder.Create<int>(System.ReadOnlySpan<int>)" IL_000f: ret } """); verifier.VerifyIL("Program.F2", """ { // Code size 57 (0x39) .maxstack 2 .locals init (<>y__InlineArray3<object> V_0) IL_0000: ldloca.s V_0 IL_0002: initobj "<>y__InlineArray3<object>" IL_0008: ldloca.s V_0 IL_000a: ldc.i4.0 IL_000b: call "ref object <PrivateImplementationDetails>.InlineArrayElementRef<<>y__InlineArray3<object>, object>(ref <>y__InlineArray3<object>, int)" IL_0010: ldarg.0 IL_0011: box "int" IL_0016: stind.ref IL_0017: ldloca.s V_0 IL_0019: ldc.i4.1 IL_001a: call "ref object <PrivateImplementationDetails>.InlineArrayElementRef<<>y__InlineArray3<object>, object>(ref <>y__InlineArray3<object>, int)" IL_001f: ldarg.1 IL_0020: stind.ref IL_0021: ldloca.s V_0 IL_0023: ldc.i4.2 IL_0024: call "ref object <PrivateImplementationDetails>.InlineArrayElementRef<<>y__InlineArray3<object>, object>(ref <>y__InlineArray3<object>, int)" IL_0029: ldnull IL_002a: stind.ref IL_002b: ldloca.s V_0 IL_002d: ldc.i4.3 IL_002e: call "System.ReadOnlySpan<object> <PrivateImplementationDetails>.InlineArrayAsReadOnlySpan<<>y__InlineArray3<object>, object>(in <>y__InlineArray3<object>, int)" IL_0033: call "MyCollection<object> MyCollectionBuilder.Create<object>(System.ReadOnlySpan<object>)" IL_0038: ret } """); string sourceB2 = """ class Program { static void Main() { MyCollection<object> c = F2([1, 2]); c.Report(); } static MyCollection<object> F2(MyCollection<object> c) { return [..c, 3]; } } """; verifier = CompileAndVerify( new[] { sourceB2, s_collectionExtensions }, references: new[] { refA }, targetFramework: TargetFramework.Net80, verify: Verification.Fails, expectedOutput: IncludeExpectedOutput("[1, 2, 3], ")); verifier.VerifyIL("Program.F2", """ { // Code size 79 (0x4f) .maxstack 2 .locals init (System.Collections.Generic.List<object> V_0, System.Collections.Generic.IEnumerator<object> V_1, object V_2) IL_0000: newobj "System.Collections.Generic.List<object>..ctor()" IL_0005: stloc.0 IL_0006: ldarga.s V_0 IL_0008: call "System.Collections.Generic.IEnumerator<object> MyCollection<object>.GetEnumerator()" IL_000d: stloc.1 .try { IL_000e: br.s IL_001e IL_0010: ldloc.1 IL_0011: callvirt "object System.Collections.Generic.IEnumerator<object>.Current.get" IL_0016: stloc.2 IL_0017: ldloc.0 IL_0018: ldloc.2 IL_0019: callvirt "void System.Collections.Generic.List<object>.Add(object)" IL_001e: ldloc.1 IL_001f: callvirt "bool System.Collections.IEnumerator.MoveNext()" IL_0024: brtrue.s IL_0010 IL_0026: leave.s IL_0032 } finally { IL_0028: ldloc.1 IL_0029: brfalse.s IL_0031 IL_002b: ldloc.1 IL_002c: callvirt "void System.IDisposable.Dispose()" IL_0031: endfinally } IL_0032: ldloc.0 IL_0033: ldc.i4.3 IL_0034: box "int" IL_0039: callvirt "void System.Collections.Generic.List<object>.Add(object)" IL_003e: ldloc.0 IL_003f: callvirt "object[] System.Collections.Generic.List<object>.ToArray()" IL_0044: newobj "System.ReadOnlySpan<object>..ctor(object[])" IL_0049: call "MyCollection<object> MyCollectionBuilder.Create<object>(System.ReadOnlySpan<object>)" IL_004e: ret } """); } [CombinatorialData] [Theory] public void CollectionBuilder_02A( [CombinatorialValues(TargetFramework.Net70, TargetFramework.Net80)] TargetFramework targetFramework, bool useCompilationReference) { string sourceA = """ using System; using System.Collections; using System.Collections.Generic; using System.Runtime.CompilerServices; [CollectionBuilder(typeof(MyCollectionBuilder), nameof(MyCollectionBuilder.Create))] public struct MyCollection<T> : IEnumerable<T> { private readonly List<T> _list; public MyCollection(List<T> list) { _list = list; } public IEnumerator<T> GetEnumerator() => _list.GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); } public class MyCollectionBuilder { public static MyCollection<T> Create<T>(ReadOnlySpan<T> items) { return new MyCollection<T>(new List<T>(items.ToArray())); } } """; var sources = targetFramework == TargetFramework.Net70 ? new[] { sourceA, CollectionBuilderAttributeDefinition } : new[] { sourceA }; var comp = CreateCompilation(sources, targetFramework: targetFramework); var refA = AsReference(comp, useCompilationReference); string sourceB = """ class Program { static void Main() { var x = F(); x.Report(); } static MyCollection<int?> F() { return [1, 2, null]; } } """; comp = CreateCompilation(new[] { sourceB, s_collectionExtensions }, references: new[] { refA }, targetFramework: targetFramework, options: TestOptions.ReleaseExe); comp.VerifyEmitDiagnostics(); var verifier = CompileAndVerify( comp, symbolValidator: module => { var type = module.GlobalNamespace.GetTypeMembers("<>y__InlineArray3").SingleOrDefault(); if (targetFramework == TargetFramework.Net80) { Assert.NotNull(type); } else { Assert.Null(type); } }, verify: targetFramework == TargetFramework.Net80 ? Verification.Fails : Verification.FailsPEVerify, expectedOutput: IncludeExpectedOutput("[1, 2, null], ")); if (targetFramework == TargetFramework.Net80) { verifier.VerifyIL("Program.F", """ { // Code size 74 (0x4a) .maxstack 2 .locals init (<>y__InlineArray3<int?> V_0) IL_0000: ldloca.s V_0 IL_0002: initobj "<>y__InlineArray3<int?>" IL_0008: ldloca.s V_0 IL_000a: ldc.i4.0 IL_000b: call "ref int? <PrivateImplementationDetails>.InlineArrayElementRef<<>y__InlineArray3<int?>, int?>(ref <>y__InlineArray3<int?>, int)" IL_0010: ldc.i4.1 IL_0011: newobj "int?..ctor(int)" IL_0016: stobj "int?" IL_001b: ldloca.s V_0 IL_001d: ldc.i4.1 IL_001e: call "ref int? <PrivateImplementationDetails>.InlineArrayElementRef<<>y__InlineArray3<int?>, int?>(ref <>y__InlineArray3<int?>, int)" IL_0023: ldc.i4.2 IL_0024: newobj "int?..ctor(int)" IL_0029: stobj "int?" IL_002e: ldloca.s V_0 IL_0030: ldc.i4.2 IL_0031: call "ref int? <PrivateImplementationDetails>.InlineArrayElementRef<<>y__InlineArray3<int?>, int?>(ref <>y__InlineArray3<int?>, int)" IL_0036: initobj "int?" IL_003c: ldloca.s V_0 IL_003e: ldc.i4.3 IL_003f: call "System.ReadOnlySpan<int?> <PrivateImplementationDetails>.InlineArrayAsReadOnlySpan<<>y__InlineArray3<int?>, int?>(in <>y__InlineArray3<int?>, int)" IL_0044: call "MyCollection<int?> MyCollectionBuilder.Create<int?>(System.ReadOnlySpan<int?>)" IL_0049: ret } """); } else { verifier.VerifyIL("Program.F", """ { // Code size 43 (0x2b) .maxstack 4 IL_0000: ldc.i4.3 IL_0001: newarr "int?" IL_0006: dup IL_0007: ldc.i4.0 IL_0008: ldc.i4.1 IL_0009: newobj "int?..ctor(int)" IL_000e: stelem "int?" IL_0013: dup IL_0014: ldc.i4.1 IL_0015: ldc.i4.2 IL_0016: newobj "int?..ctor(int)" IL_001b: stelem "int?" IL_0020: newobj "System.ReadOnlySpan<int?>..ctor(int?[])" IL_0025: call "MyCollection<int?> MyCollectionBuilder.Create<int?>(System.ReadOnlySpan<int?>)" IL_002a: ret } """); } } // As above, but with TargetFramework.NetFramework. [ConditionalFact(typeof(DesktopOnly))] public void CollectionBuilder_02B() { string sourceA = """ using System; using System.Collections; using System.Collections.Generic; using System.Runtime.CompilerServices; [CollectionBuilder(typeof(MyCollectionBuilder), nameof(MyCollectionBuilder.Create))] public struct MyCollection<T> : IEnumerable<T> { private readonly List<T> _list; public MyCollection(List<T> list) { _list = list; } public IEnumerator<T> GetEnumerator() => _list.GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); } public class MyCollectionBuilder { public static MyCollection<T> Create<T>(ReadOnlySpan<T> items) { var list = new List<T>(); foreach (var i in items) list.Add(i); return new MyCollection<T>(list); } } """; string sourceB = """ class Program { static void Main() { var x = F(); x.Report(); } static MyCollection<int?> F() { return [1, 2, null]; } } """; var comp = CreateCompilationWithSpanAndMemoryExtensions( new[] { sourceA, sourceB, s_collectionExtensions, CollectionBuilderAttributeDefinition }, targetFramework: TargetFramework.NetFramework, options: TestOptions.ReleaseExe); comp.VerifyEmitDiagnostics(); var verifier = CompileAndVerify( comp, symbolValidator: module => { var type = module.GlobalNamespace.GetTypeMembers("<>y__InlineArray3").SingleOrDefault(); Assert.Null(type); }, expectedOutput: "[1, 2, null], "); verifier.VerifyIL("Program.F", """ { // Code size 43 (0x2b) .maxstack 4 IL_0000: ldc.i4.3 IL_0001: newarr "int?" IL_0006: dup IL_0007: ldc.i4.0 IL_0008: ldc.i4.1 IL_0009: newobj "int?..ctor(int)" IL_000e: stelem "int?" IL_0013: dup IL_0014: ldc.i4.1 IL_0015: ldc.i4.2 IL_0016: newobj "int?..ctor(int)" IL_001b: stelem "int?" IL_0020: newobj "System.ReadOnlySpan<int?>..ctor(int?[])" IL_0025: call "MyCollection<int?> MyCollectionBuilder.Create<int?>(System.ReadOnlySpan<int?>)" IL_002a: ret } """); } [Fact] public void CollectionBuilder_InlineArrayTypes() { string sourceA = """ using System; using System.Collections; using System.Collections.Generic; using System.Runtime.CompilerServices; [CollectionBuilder(typeof(MyCollectionBuilder), nameof(MyCollectionBuilder.Create))] public struct MyCollection<T> : IEnumerable<T> { private readonly List<T> _list; public MyCollection(List<T> list) { _list = list; } public IEnumerator<T> GetEnumerator() => _list.GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); } public class MyCollectionBuilder { public static MyCollection<T> Create<T>(ReadOnlySpan<T> items) { return new MyCollection<T>(new List<T>(items.ToArray())); } } class A { static void M() { MyCollection<object> x; x = []; x = [null, null]; x = [1, 2, 3]; } } """; var comp = CreateCompilation(sourceA, targetFramework: TargetFramework.Net80); CompileAndVerify( comp, symbolValidator: module => { AssertEx.Equal(new[] { "<>y__InlineArray2", "<>y__InlineArray3" }, getInlineArrayTypeNames(module)); }, verify: Verification.Skipped); var refA = comp.EmitToImageReference(); string sourceB = """ class B { static void M<T>(MyCollection<T> c) { } static void M1() { M<int?>([1]); } static void M2() { M([(object)4, 5, 6]); M(["a"]); M(["b"]); } } """; comp = CreateCompilation(sourceB, references: new[] { refA }, targetFramework: TargetFramework.Net80); CompileAndVerify( comp, symbolValidator: module => { AssertEx.Equal(new[] { "<>y__InlineArray3" }, getInlineArrayTypeNames(module)); }, verify: Verification.Skipped); const int n = 1025; var builder = new System.Text.StringBuilder(); for (int i = 0; i < n; i++) { if (i > 0) builder.Append(", "); builder.Append(i.ToString(System.Globalization.CultureInfo.InvariantCulture)); } string sourceC = $$""" using System; using System.Linq; class Program { static void Main() { MyCollection<object> c = [{{builder.ToString()}}]; Console.WriteLine(c.Count()); } } """; comp = CreateCompilation(sourceC, references: new[] { refA }, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseExe); CompileAndVerify( comp, symbolValidator: module => { AssertEx.Equal(new[] { $"<>y__InlineArray{n}" }, getInlineArrayTypeNames(module)); }, verify: Verification.Skipped, expectedOutput: IncludeExpectedOutput($"{n}")); static ImmutableArray<string> getInlineArrayTypeNames(ModuleSymbol module) { return module.GlobalNamespace.GetTypeMembers().WhereAsArray(t => t.Name.StartsWith("<>y__InlineArray")).SelectAsArray(t => t.Name); } } [CombinatorialData] [Theory] public void CollectionBuilder_RefStructCollection(bool useCompilationReference, bool useScoped) { string qualifier = useScoped ? "scoped " : ""; string sourceA = $$""" using System; using System.Collections; using System.Collections.Generic; using System.Runtime.CompilerServices; [CollectionBuilder(typeof(MyCollectionBuilder), nameof(MyCollectionBuilder.Create))] public ref struct MyCollection<T> { private readonly List<T> _list; public MyCollection(List<T> list) { _list = list; } public IEnumerator<T> GetEnumerator() => _list.GetEnumerator(); public T[] ToArray() => _list.ToArray(); } public static class MyCollectionBuilder { public static MyCollection<T> Create<T>({{qualifier}}ReadOnlySpan<T> items) { return new MyCollection<T>(new List<T>(items.ToArray())); } } """; var comp = CreateCompilation(sourceA, targetFramework: TargetFramework.Net80); var refA = AsReference(comp, useCompilationReference); string sourceB = """ using System; #pragma warning disable 219 class Program { static void Main() { F().Report(); } static object[] F() { MyCollection<object> c = [1, 2, 3]; return c.ToArray(); } } """; var verifier = CompileAndVerify( new[] { sourceB, s_collectionExtensions }, references: new[] { refA }, targetFramework: TargetFramework.Net80, verify: Verification.Skipped, expectedOutput: IncludeExpectedOutput("[1, 2, 3], ")); verifier.VerifyIL("Program.F", $$""" { // Code size 75 (0x4b) .maxstack 2 .locals init (MyCollection<object> V_0, //c <>y__InlineArray3<object> V_1) IL_0000: ldloca.s V_1 IL_0002: initobj "<>y__InlineArray3<object>" IL_0008: ldloca.s V_1 IL_000a: ldc.i4.0 IL_000b: call "ref object <PrivateImplementationDetails>.InlineArrayElementRef<<>y__InlineArray3<object>, object>(ref <>y__InlineArray3<object>, int)" IL_0010: ldc.i4.1 IL_0011: box "int" IL_0016: stind.ref IL_0017: ldloca.s V_1 IL_0019: ldc.i4.1 IL_001a: call "ref object <PrivateImplementationDetails>.InlineArrayElementRef<<>y__InlineArray3<object>, object>(ref <>y__InlineArray3<object>, int)" IL_001f: ldc.i4.2 IL_0020: box "int" IL_0025: stind.ref IL_0026: ldloca.s V_1 IL_0028: ldc.i4.2 IL_0029: call "ref object <PrivateImplementationDetails>.InlineArrayElementRef<<>y__InlineArray3<object>, object>(ref <>y__InlineArray3<object>, int)" IL_002e: ldc.i4.3 IL_002f: box "int" IL_0034: stind.ref IL_0035: ldloca.s V_1 IL_0037: ldc.i4.3 IL_0038: call "System.ReadOnlySpan<object> <PrivateImplementationDetails>.InlineArrayAsReadOnlySpan<<>y__InlineArray3<object>, object>(in <>y__InlineArray3<object>, int)" IL_003d: call "MyCollection<object> MyCollectionBuilder.Create<object>({{qualifier}}System.ReadOnlySpan<object>)" IL_0042: stloc.0 IL_0043: ldloca.s V_0 IL_0045: call "object[] MyCollection<object>.ToArray()" IL_004a: ret } """); } [CombinatorialData] [Theory] public void CollectionBuilder_NonGenericCollection(bool useCompilationReference) { string sourceA = """ using System; using System.Collections; using System.Collections.Generic; using System.Runtime.CompilerServices; [CollectionBuilder(typeof(MyCollectionBuilder), "Create")] public sealed class MyCollection : IEnumerable<object> { private readonly List<object> _list; public MyCollection(List<object> list) { _list = list; } public IEnumerator<object> GetEnumerator() => _list.GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); } public sealed class MyCollectionBuilder { public static MyCollection Create(ReadOnlySpan<object> items) => new MyCollection(new List<object>(items.ToArray())); } """; var comp = CreateCompilation(sourceA, targetFramework: TargetFramework.Net80); var refA = AsReference(comp, useCompilationReference); string sourceB = """ class Program { static void Main() { MyCollection x = []; x.Report(); MyCollection y = [1, 2, 3]; y.Report(); } } """; CompileAndVerify( new[] { sourceB, s_collectionExtensions }, references: new[] { refA }, targetFramework: TargetFramework.Net80, verify: Verification.Fails, expectedOutput: IncludeExpectedOutput("[], [1, 2, 3], ")); } [CombinatorialData] [Theory] public void CollectionBuilder_InterfaceCollection_ReturnInterface(bool useCompilationReference) { string sourceA = """ using System; using System.Collections; using System.Collections.Generic; using System.Runtime.CompilerServices; [CollectionBuilder(typeof(MyCollectionBuilder), "Create")] public interface IMyCollection<T> : IEnumerable<T> { } public sealed class MyCollectionBuilder { public static IMyCollection<T> Create<T>(ReadOnlySpan<T> items) => new MyCollection<T>(new List<T>(items.ToArray())); public sealed class MyCollection<T> : IMyCollection<T> { private readonly List<T> _list; public MyCollection(List<T> list) { _list = list; } public IEnumerator<T> GetEnumerator() => _list.GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); } } """; var comp = CreateCompilation(sourceA, targetFramework: TargetFramework.Net80); var refA = AsReference(comp, useCompilationReference); string sourceB = """ class Program { static void Main() { IMyCollection<string> x = []; x.Report(includeType: true); IMyCollection<int> y = [1, 2, 3]; y.Report(includeType: true); } } """; CompileAndVerify( new[] { sourceB, s_collectionExtensions }, references: new[] { refA }, targetFramework: TargetFramework.Net80, verify: Verification.FailsPEVerify, expectedOutput: IncludeExpectedOutput("(MyCollectionBuilder.MyCollection<System.String>) [], (MyCollectionBuilder.MyCollection<System.Int32>) [1, 2, 3], ")); } [CombinatorialData] [Theory] public void CollectionBuilder_InterfaceCollection_ReturnImplementation(bool useCompilationReference) { string sourceA = """ using System; using System.Collections; using System.Collections.Generic; using System.Runtime.CompilerServices; [CollectionBuilder(typeof(MyCollectionBuilder), "Create")] public interface IMyCollection<T> : IEnumerable<T> { } public sealed class MyCollectionBuilder { public static MyCollection<T> Create<T>(ReadOnlySpan<T> items) => new MyCollection<T>(new List<T>(items.ToArray())); public sealed class MyCollection<T> : IMyCollection<T> { private readonly List<T> _list; public MyCollection(List<T> list) { _list = list; } public IEnumerator<T> GetEnumerator() => _list.GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); } } """; var comp = CreateCompilation(sourceA, targetFramework: TargetFramework.Net80); var refA = AsReference(comp, useCompilationReference); string sourceB = """ class Program { static void Main() { IMyCollection<string> x = []; x.Report(includeType: true); IMyCollection<int> y = [1, 2, 3]; y.Report(includeType: true); } } """; CompileAndVerify( new[] { sourceB, s_collectionExtensions }, references: new[] { refA }, targetFramework: TargetFramework.Net80, verify: Verification.FailsPEVerify, expectedOutput: IncludeExpectedOutput("(MyCollectionBuilder.MyCollection<System.String>) [], (MyCollectionBuilder.MyCollection<System.Int32>) [1, 2, 3], ")); } [CombinatorialData] [Theory] public void CollectionBuilder_NestedCollectionAndBuilder(bool useCompilationReference) { string sourceA = """ using System; using System.Collections; using System.Collections.Generic; using System.Runtime.CompilerServices; public class Container { [CollectionBuilder(typeof(MyCollectionBuilder), "Create")] public sealed class MyCollection<T> : IEnumerable<T> { private readonly List<T> _list; public MyCollection(List<T> list) { _list = list; } public IEnumerator<T> GetEnumerator() => _list.GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); } public sealed class MyCollectionBuilder { public static MyCollection<T> Create<T>(ReadOnlySpan<T> items) => new MyCollection<T>(new List<T>(items.ToArray())); } } """; var comp = CreateCompilation(sourceA, targetFramework: TargetFramework.Net80); var refA = AsReference(comp, useCompilationReference); string sourceB = """ class Program { static void Main() { Container.MyCollection<string> x = []; x.Report(includeType: true); Container.MyCollection<object> y = [1, 2, 3]; y.Report(includeType: true); } } """; CompileAndVerify( new[] { sourceB, s_collectionExtensions }, references: new[] { refA }, targetFramework: TargetFramework.Net80, verify: Verification.Fails, expectedOutput: IncludeExpectedOutput("(Container.MyCollection<System.String>) [], (Container.MyCollection<System.Object>) [1, 2, 3], ")); } [CombinatorialData] [Theory] public void CollectionBuilder_NoElementType(bool useCompilationReference) { string sourceA = """ using System; using System.Runtime.CompilerServices; [CollectionBuilder(typeof(MyCollectionBuilder), "Create")] public struct MyCollection<T> { public MyCollection(T[] array) { } } public class MyCollectionBuilder { public static MyCollection<T> Create<T>(ReadOnlySpan<T> items) => default; } """; var comp = CreateCompilation(sourceA, targetFramework: TargetFramework.Net80); var refA = AsReference(comp, useCompilationReference); string sourceB = """ #pragma warning disable 219 class Program { static void Main() { MyCollection<object> x = []; MyCollection<int> y = [1, 2, 3]; MyCollection<string> z = new(); } } """; comp = CreateCompilation(sourceB, references: new[] { refA }, targetFramework: TargetFramework.Net80); comp.VerifyEmitDiagnostics( // (6,34): error CS9188: 'MyCollection<object>' has a CollectionBuilderAttribute but no element type. // MyCollection<object> x = []; Diagnostic(ErrorCode.ERR_CollectionBuilderNoElementType, "[]").WithArguments("MyCollection<object>").WithLocation(6, 34), // (7,31): error CS9188: 'MyCollection<int>' has a CollectionBuilderAttribute but no element type. // MyCollection<int> y = [1, 2, 3]; Diagnostic(ErrorCode.ERR_CollectionBuilderNoElementType, "[1, 2, 3]").WithArguments("MyCollection<int>").WithLocation(7, 31)); } [CombinatorialData] [Theory] public void CollectionBuilder_ElementTypeFromPattern_01(bool useCompilationReference) { string sourceA = """ using System; using System.Runtime.CompilerServices; [CollectionBuilder(typeof(MyCollectionBuilder), "Create")] public struct MyCollection<T> { private readonly T[] _array; public MyCollection(T[] array) { _array = array; } public MyEnumerator<T> GetEnumerator() => new MyEnumerator<T>(_array); } public struct MyEnumerator<T> { private readonly T[] _array; private int _index; public MyEnumerator(T[] array) { _array = array; _index = -1; } public bool MoveNext() { if (_index < _array.Length) _index++; return _index < _array.Length; } public T Current => _array[_index]; } public struct MyCollectionBuilder { public static MyCollection<T> Create<T>(ReadOnlySpan<T> items) => new MyCollection<T>(items.ToArray()); } """; var comp = CreateCompilation(sourceA, targetFramework: TargetFramework.Net80); var refA = AsReference(comp, useCompilationReference); string sourceB = """ using System.Collections.Generic; class Program { static void Main() { MyCollection<string> x = []; GetElements(x).Report(); MyCollection<int> y = [1, 2, 3]; GetElements(y).Report(); } static IEnumerable<T> GetElements<T>(MyCollection<T> c) { foreach (var e in c) yield return e; } } """; CompileAndVerify( new[] { sourceB, s_collectionExtensions }, references: new[] { refA }, targetFramework: TargetFramework.Net80, verify: Verification.FailsPEVerify, expectedOutput: IncludeExpectedOutput("[], [1, 2, 3], ")); } [CombinatorialData] [Theory] public void CollectionBuilder_ElementTypeFromPattern_02(bool useCompilationReference) { string sourceA = """ using System; using System.Runtime.CompilerServices; [CollectionBuilder(typeof(MyCollectionBuilder), "Create")] public struct MyCollection { private readonly object[] _array; public MyCollection(object[] array) { _array = array; } public MyEnumerator GetEnumerator() => new MyEnumerator(_array); } public struct MyEnumerator { private readonly object[] _array; private int _index; public MyEnumerator(object[] array) { _array = array; _index = -1; } public bool MoveNext() { if (_index < _array.Length) _index++; return _index < _array.Length; } public object Current => _array[_index]; } public struct MyCollectionBuilder { public static MyCollection Create(ReadOnlySpan<object> items) => new MyCollection(items.ToArray()); } """; var comp = CreateCompilation(sourceA, targetFramework: TargetFramework.Net80); var refA = AsReference(comp, useCompilationReference); string sourceB = """ using System.Collections; class Program { static void Main() { MyCollection x = []; GetElements(x).Report(); MyCollection y = [1, 2, 3]; GetElements(y).Report(); } static IEnumerable GetElements(MyCollection c) { foreach (var e in c) yield return e; } } """; CompileAndVerify( new[] { sourceB, s_collectionExtensions }, references: new[] { refA }, targetFramework: TargetFramework.Net80, verify: Verification.Fails, expectedOutput: IncludeExpectedOutput("[], [1, 2, 3], ")); } [CombinatorialData] [Theory] public void CollectionBuilder_ObjectElementType_01(bool useCompilationReference) { string sourceA = """ using System; using System.Collections; using System.Runtime.CompilerServices; [CollectionBuilder(typeof(MyCollectionBuilder), "Create")] public struct MyCollection : IEnumerable { private readonly object[] _array; public MyCollection(object[] array) { _array = array; } IEnumerator IEnumerable.GetEnumerator() => _array.GetEnumerator(); } public struct MyCollectionBuilder { public static MyCollection Create(ReadOnlySpan<object> items) => new MyCollection(items.ToArray()); } """; var comp = CreateCompilation(sourceA, targetFramework: TargetFramework.Net80); var refA = AsReference(comp, useCompilationReference); string sourceB = """ class Program { static void Main() { MyCollection x = []; x.Report(); MyCollection y = [1, 2, 3]; y.Report(); } } """; CompileAndVerify( new[] { sourceB, s_collectionExtensions }, references: new[] { refA }, targetFramework: TargetFramework.Net80, verify: Verification.Fails, expectedOutput: IncludeExpectedOutput("[], [1, 2, 3], ")); } [CombinatorialData] [Theory] public void CollectionBuilder_ObjectElementType_02(bool useCompilationReference) { string sourceA = """ using System; using System.Collections; using System.Runtime.CompilerServices; [CollectionBuilder(typeof(MyCollectionBuilder), "Create")] public struct MyCollection<T> : IEnumerable { public MyCollection(T[] array) { } public IEnumerator GetEnumerator() => default; } public class MyCollectionBuilder { public static MyCollection<T> Create<T>(ReadOnlySpan<T> items) => default; } """; var comp = CreateCompilation(sourceA, targetFramework: TargetFramework.Net80); var refA = AsReference(comp, useCompilationReference); string sourceB = """ #pragma warning disable 219 class Program { static void Main() { MyCollection<object> x = []; MyCollection<int> y = [1, 2, 3]; MyCollection<string> z = new(); } } """; comp = CreateCompilation(sourceB, references: new[] { refA }, targetFramework: TargetFramework.Net80); comp.VerifyEmitDiagnostics( // (6,34): error CS9187: Could not find an accessible 'Create' method with the expected signature: a static method whose last parameter is of type 'ReadOnlySpan<object>' and return type 'MyCollection<T>'. // MyCollection<object> x = []; Diagnostic(ErrorCode.ERR_CollectionBuilderAttributeMethodNotFound, "[]").WithArguments("Create", "object", "MyCollection<T>").WithLocation(6, 34), // (7,31): error CS9187: Could not find an accessible 'Create' method with the expected signature: a static method whose last parameter is of type 'ReadOnlySpan<object>' and return type 'MyCollection<T>'. // MyCollection<int> y = [1, 2, 3]; Diagnostic(ErrorCode.ERR_CollectionBuilderAttributeMethodNotFound, "[1, 2, 3]").WithArguments("Create", "object", "MyCollection<T>").WithLocation(7, 31)); } [CombinatorialData] [Theory] public void CollectionBuilder_ConstructedElementType(bool useCompilationReference) { string sourceA = """ using System; using System.Collections; using System.Collections.Generic; using System.Runtime.CompilerServices; public sealed class E<T> { private readonly T _t; public E(T t) { _t = t; } public override string ToString() => $"E({_t})"; } [CollectionBuilder(typeof(Builder), "Create")] public sealed class C<T> : IEnumerable<E<T>> { private readonly List<E<T>> _list; public C(List<E<T>> list) { _list = list; } public IEnumerator<E<T>> GetEnumerator() => _list.GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); } public sealed class Builder { public static C<T> Create<T>(ReadOnlySpan<E<T>> items) => new C<T>(new List<E<T>>(items.ToArray())); } """; var comp = CreateCompilation(sourceA, targetFramework: TargetFramework.Net80); var refA = AsReference(comp, useCompilationReference); string sourceB = """ class Program { static void Main() { C<string> x = [null]; x.Report(includeType: true); C<int> y = [new E<int>(1), default]; y.Report(includeType: true); } } """; CompileAndVerify( new[] { sourceB, s_collectionExtensions }, references: new[] { refA }, targetFramework: TargetFramework.Net80, verify: Verification.Fails, expectedOutput: IncludeExpectedOutput("(C<System.String>) [null], (C<System.Int32>) [E(1), null], ")); } [Fact] public void CollectionBuilder_ElementTypeMismatch_01() { string source = """ using System; using System.Collections; using System.Collections.Generic; using System.Runtime.CompilerServices; [CollectionBuilder(typeof(MyCollectionBuilder), nameof(MyCollectionBuilder.Create))] class MyCollection : IEnumerable { private List<string> _items; public MyCollection(List<string> items) { _items = items; } IEnumerator IEnumerable.GetEnumerator() => _items.GetEnumerator(); } class MyCollectionBuilder { public static MyCollection Create(ReadOnlySpan<string> items) => new MyCollection(new(items.ToArray())); } class Program { static void Main() { MyCollection c = [null]; } } """; var comp = CreateCompilation(source, targetFramework: TargetFramework.Net80); comp.VerifyEmitDiagnostics( // (20,26): error CS9187: Could not find an accessible 'Create' method with the expected signature: a static method whose last parameter is of type 'ReadOnlySpan<object>' and return type 'MyCollection'. // MyCollection c = [null]; Diagnostic(ErrorCode.ERR_CollectionBuilderAttributeMethodNotFound, "[null]").WithArguments("Create", "object", "MyCollection").WithLocation(20, 26)); } [Fact] public void CollectionBuilder_ElementTypeMismatch_02() { string source = """ using System; using System.Collections; using System.Collections.Generic; using System.Runtime.CompilerServices; [CollectionBuilder(typeof(MyCollectionBuilder), nameof(MyCollectionBuilder.Create))] class MyCollection : IEnumerable<object> { private List<string> _items; public MyCollection(List<string> items) { _items = items; } IEnumerator<object> IEnumerable<object>.GetEnumerator() => _items.GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => _items.GetEnumerator(); } class MyCollectionBuilder { public static MyCollection Create(ReadOnlySpan<string> items) => new MyCollection(new(items.ToArray())); } class Program { static void Main() { MyCollection c = [null]; } } """; var comp = CreateCompilation(source, targetFramework: TargetFramework.Net80); comp.VerifyEmitDiagnostics( // (21,26): error CS9187: Could not find an accessible 'Create' method with the expected signature: a static method whose last parameter is of type 'ReadOnlySpan<object>' and return type 'MyCollection'. // MyCollection c = [null]; Diagnostic(ErrorCode.ERR_CollectionBuilderAttributeMethodNotFound, "[null]").WithArguments("Create", "object", "MyCollection").WithLocation(21, 26)); } [CombinatorialData] [Theory] public void CollectionBuilder_Dictionary(bool useCompilationReference) { string sourceA = """ using System; using System.Collections; using System.Collections.Generic; using System.Runtime.CompilerServices; [CollectionBuilder(typeof(MyDictionaryBuilder), "Create")] public class MyImmutableDictionary<K, V> : IEnumerable<KeyValuePair<K, V>> { private readonly Dictionary<K, V> _d; public MyImmutableDictionary(ReadOnlySpan<KeyValuePair<K, V>> items) { _d = new(); foreach (var (k, v) in items) _d.Add(k, v); } public IEnumerator<KeyValuePair<K, V>> GetEnumerator() => _d.GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); } public class MyDictionaryBuilder { public static MyImmutableDictionary<K, V> Create<K, V>(ReadOnlySpan<KeyValuePair<K, V>> items) => new MyImmutableDictionary<K, V>(items); } """; var comp = CreateCompilation(sourceA, targetFramework: TargetFramework.Net80); var refA = AsReference(comp, useCompilationReference); string sourceB = """ using System.Collections.Generic; class Program { static void Main() { MyImmutableDictionary<string, object> x = []; x.Report(); MyImmutableDictionary<string, int> y = [KeyValuePair.Create("one", 1), KeyValuePair.Create("two", 2)]; y.Report(); } } """; CompileAndVerify( new[] { sourceB, s_collectionExtensions }, references: new[] { refA }, targetFramework: TargetFramework.Net80, verify: Verification.Fails, expectedOutput: IncludeExpectedOutput("[], [[one, 1], [two, 2]], ")); } [Fact] public void CollectionBuilder_MissingBuilderType() { string sourceA = """ public class MyCollectionBuilder { } """; var comp = CreateCompilation(sourceA, targetFramework: TargetFramework.Net80); var refA = comp.EmitToImageReference(); string sourceB = """ using System.Collections; using System.Collections.Generic; using System.Runtime.CompilerServices; [CollectionBuilder(typeof(MyCollectionBuilder), "Create")] public struct MyCollection<T> : IEnumerable<T> { IEnumerator<T> IEnumerable<T>.GetEnumerator() => default; IEnumerator IEnumerable.GetEnumerator() => default; } """; comp = CreateCompilation(sourceB, references: new[] { refA }, targetFramework: TargetFramework.Net80); var refB = comp.EmitToImageReference(); string sourceC = """ #pragma warning disable 219 class Program { static void Main() { MyCollection<int> x = []; MyCollection<string> y = [null]; MyCollection<object> z = new(); } } """; comp = CreateCompilation(sourceC, references: new[] { refB }, targetFramework: TargetFramework.Net80); comp.VerifyEmitDiagnostics( // (6,31): error CS9187: Could not find an accessible 'Create' method with the expected signature: a static method whose last parameter is of type 'ReadOnlySpan<T>' and return type 'MyCollection<T>'. // MyCollection<int> x = []; Diagnostic(ErrorCode.ERR_CollectionBuilderAttributeMethodNotFound, "[]").WithArguments("Create", "T", "MyCollection<T>").WithLocation(6, 31), // (7,34): error CS9187: Could not find an accessible 'Create' method with the expected signature: a static method whose last parameter is of type 'ReadOnlySpan<T>' and return type 'MyCollection<T>'. // MyCollection<string> y = [null]; Diagnostic(ErrorCode.ERR_CollectionBuilderAttributeMethodNotFound, "[null]").WithArguments("Create", "T", "MyCollection<T>").WithLocation(7, 34)); } [CombinatorialData] [Theory] public void CollectionBuilder_MissingBuilderMethod(bool useCompilationReference) { string sourceA = """ using System.Collections; using System.Collections.Generic; using System.Runtime.CompilerServices; [CollectionBuilder(typeof(MyCollectionBuilder), "Create")] public struct MyCollection<T> : IEnumerable<T> { IEnumerator<T> IEnumerable<T>.GetEnumerator() => default; IEnumerator IEnumerable.GetEnumerator() => default; } public class MyCollectionBuilder { } """; var comp = CreateCompilation(sourceA, targetFramework: TargetFramework.Net80); var refA = AsReference(comp, useCompilationReference); string sourceB = """ #pragma warning disable 219 class Program { static void Main() { MyCollection<int> x = []; MyCollection<string> y = [null]; MyCollection<object> z = new(); } } """; comp = CreateCompilation(sourceB, references: new[] { refA }, targetFramework: TargetFramework.Net80); comp.VerifyEmitDiagnostics( // (6,31): error CS9187: Could not find an accessible 'Create' method with the expected signature: a static method whose last parameter is of type 'ReadOnlySpan<T>' and return type 'MyCollection<T>'. // MyCollection<int> x = []; Diagnostic(ErrorCode.ERR_CollectionBuilderAttributeMethodNotFound, "[]").WithArguments("Create", "T", "MyCollection<T>").WithLocation(6, 31), // (7,34): error CS9187: Could not find an accessible 'Create' method with the expected signature: a static method whose last parameter is of type 'ReadOnlySpan<T>' and return type 'MyCollection<T>'. // MyCollection<string> y = [null]; Diagnostic(ErrorCode.ERR_CollectionBuilderAttributeMethodNotFound, "[null]").WithArguments("Create", "T", "MyCollection<T>").WithLocation(7, 34)); } [Fact] public void CollectionBuilder_MissingBuilderMethod_FromMetadata() { // [CollectionBuilder(typeof(MyCollectionBuilder), "Create")] string sourceA = """ .class public sealed System.ReadOnlySpan`1<T> extends [mscorlib]System.ValueType { } .class public sealed System.Runtime.CompilerServices.CollectionBuilderAttribute extends [mscorlib]System.Attribute { .method public hidebysig specialname rtspecialname instance void .ctor(class [mscorlib]System.Type builderType, string methodName) cil managed { ret } } .class public sealed MyCollection`1<T> { .custom instance void System.Runtime.CompilerServices.CollectionBuilderAttribute::.ctor(class [mscorlib]System.Type, string) = { type(MyCollectionBuilder) string('Create') } .method public hidebysig specialname rtspecialname instance void .ctor() cil managed { ret } .method public instance class [mscorlib]System.Collections.Generic.IEnumerator`1<!T> GetEnumerator() { ldnull ret } } .class public sealed MyCollectionBuilder { .method public hidebysig specialname rtspecialname instance void .ctor() cil managed { ret } // Missing Create<T>() method } """; var refA = CompileIL(sourceA); string sourceB = """ #pragma warning disable 219 class Program { static void Main() { MyCollection<int> x = []; MyCollection<string> y = [null]; MyCollection<object> z = new(); } } """; var comp = CreateCompilation(sourceB, references: new[] { refA }); comp.VerifyEmitDiagnostics( // (6,31): error CS9187: Could not find an accessible 'Create' method with the expected signature: a static method whose last parameter is of type 'ReadOnlySpan<T>' and return type 'MyCollection<T>'. // MyCollection<int> x = []; Diagnostic(ErrorCode.ERR_CollectionBuilderAttributeMethodNotFound, "[]").WithArguments("Create", "T", "MyCollection<T>").WithLocation(6, 31), // (7,34): error CS9187: Could not find an accessible 'Create' method with the expected signature: a static method whose last parameter is of type 'ReadOnlySpan<T>' and return type 'MyCollection<T>'. // MyCollection<string> y = [null]; Diagnostic(ErrorCode.ERR_CollectionBuilderAttributeMethodNotFound, "[null]").WithArguments("Create", "T", "MyCollection<T>").WithLocation(7, 34)); } [Fact] public void CollectionBuilder_NullBuilderType() { string sourceA = """ using System.Collections; using System.Collections.Generic; using System.Runtime.CompilerServices; [CollectionBuilder(null, "Create")] public struct MyCollection<T> : IEnumerable<T> { IEnumerator<T> IEnumerable<T>.GetEnumerator() => default; IEnumerator IEnumerable.GetEnumerator() => default; } """; string sourceB = """ #pragma warning disable 219 class Program { static void Main() { MyCollection<int> x = []; MyCollection<string> y = [null]; MyCollection<object> z = new(); } } """; var comp = CreateCompilation(new[] { sourceA, sourceB }, targetFramework: TargetFramework.Net80); comp.VerifyEmitDiagnostics( // 0.cs(4,2): error CS9185: The CollectionBuilderAttribute builder type must be a non-generic class or struct. // [CollectionBuilder(null, "Create")] Diagnostic(ErrorCode.ERR_CollectionBuilderAttributeInvalidType, "CollectionBuilder").WithLocation(4, 2), // 1.cs(6,31): error CS9187: Could not find an accessible 'Create' method with the expected signature: a static method whose last parameter is of type 'ReadOnlySpan<T>' and return type 'MyCollection<T>'. // MyCollection<int> x = []; Diagnostic(ErrorCode.ERR_CollectionBuilderAttributeMethodNotFound, "[]").WithArguments("Create", "T", "MyCollection<T>").WithLocation(6, 31), // 1.cs(7,34): error CS9187: Could not find an accessible 'Create' method with the expected signature: a static method whose last parameter is of type 'ReadOnlySpan<T>' and return type 'MyCollection<T>'. // MyCollection<string> y = [null]; Diagnostic(ErrorCode.ERR_CollectionBuilderAttributeMethodNotFound, "[null]").WithArguments("Create", "T", "MyCollection<T>").WithLocation(7, 34)); } [Fact] public void CollectionBuilder_NullBuilderType_FromMetadata() { // [CollectionBuilder(null, "Create")] string sourceA = """ .class public sealed System.Runtime.CompilerServices.CollectionBuilderAttribute extends [mscorlib]System.Attribute { .method public hidebysig specialname rtspecialname instance void .ctor(class [mscorlib]System.Type builderType, string methodName) cil managed { ret } } .class public sealed MyCollection`1<T> { .custom instance void System.Runtime.CompilerServices.CollectionBuilderAttribute::.ctor(class [mscorlib]System.Type, string) = { type(nullref) string('Create') } .method public hidebysig specialname rtspecialname instance void .ctor() cil managed { ret } .method public instance class [mscorlib]System.Collections.Generic.IEnumerator`1<!T> GetEnumerator() { ldnull ret } } """; var refA = CompileIL(sourceA); string sourceB = """ #pragma warning disable 219 class Program { static void Main() { MyCollection<int> x = []; MyCollection<string> y = [null]; MyCollection<object> z = new(); } } """; var comp = CreateCompilation(sourceB, references: new[] { refA }); comp.VerifyEmitDiagnostics( // (6,31): error CS9187: Could not find an accessible 'Create' method with the expected signature: a static method whose last parameter is of type 'ReadOnlySpan<T>' and return type 'MyCollection<T>'. // MyCollection<int> x = []; Diagnostic(ErrorCode.ERR_CollectionBuilderAttributeMethodNotFound, "[]").WithArguments("Create", "T", "MyCollection<T>").WithLocation(6, 31), // (7,34): error CS9187: Could not find an accessible 'Create' method with the expected signature: a static method whose last parameter is of type 'ReadOnlySpan<T>' and return type 'MyCollection<T>'. // MyCollection<string> y = [null]; Diagnostic(ErrorCode.ERR_CollectionBuilderAttributeMethodNotFound, "[null]").WithArguments("Create", "T", "MyCollection<T>").WithLocation(7, 34)); } [Fact] public void CollectionBuilder_InvalidBuilderType_Interface() { string sourceA = """ using System; using System.Collections; using System.Collections.Generic; using System.Runtime.CompilerServices; [CollectionBuilder(typeof(MyCollectionBuilder), "Create")] public struct MyCollection<T> : IEnumerable<T> { IEnumerator<T> IEnumerable<T>.GetEnumerator() => default; IEnumerator IEnumerable.GetEnumerator() => default; } public interface MyCollectionBuilder { MyCollection<T> Create<T>(ReadOnlySpan<T> items); } """; string sourceB = """ #pragma warning disable 219 class Program { static void Main() { MyCollection<int> x = []; MyCollection<string> y = [null]; MyCollection<object> z = new(); } } """; var comp = CreateCompilation(new[] { sourceA, sourceB }, targetFramework: TargetFramework.Net80); comp.VerifyEmitDiagnostics( // 0.cs(5,2): error CS9185: The CollectionBuilderAttribute builder type must be a non-generic class or struct. // [CollectionBuilder(typeof(MyCollectionBuilder), "Create")] Diagnostic(ErrorCode.ERR_CollectionBuilderAttributeInvalidType, "CollectionBuilder").WithLocation(5, 2), // 1.cs(6,31): error CS9187: Could not find an accessible 'Create' method with the expected signature: a static method whose last parameter is of type 'ReadOnlySpan<T>' and return type 'MyCollection<T>'. // MyCollection<int> x = []; Diagnostic(ErrorCode.ERR_CollectionBuilderAttributeMethodNotFound, "[]").WithArguments("Create", "T", "MyCollection<T>").WithLocation(6, 31), // 1.cs(7,34): error CS9187: Could not find an accessible 'Create' method with the expected signature: a static method whose last parameter is of type 'ReadOnlySpan<T>' and return type 'MyCollection<T>'. // MyCollection<string> y = [null]; Diagnostic(ErrorCode.ERR_CollectionBuilderAttributeMethodNotFound, "[null]").WithArguments("Create", "T", "MyCollection<T>").WithLocation(7, 34)); } [Fact] public void CollectionBuilder_InvalidBuilderType_Interface_FromMetadata() { // [CollectionBuilder(typeof(MyCollectionBuilder), "Create")] // class MyCollection<T> { ... } // interface MyCollectionBuilder { ... } string sourceA = """ .class public sealed System.ReadOnlySpan`1<T> extends [mscorlib]System.ValueType { } .class public sealed System.Runtime.CompilerServices.CollectionBuilderAttribute extends [mscorlib]System.Attribute { .method public hidebysig specialname rtspecialname instance void .ctor(class [mscorlib]System.Type builderType, string methodName) cil managed { ret } } .class public sealed MyCollection`1<T> { .custom instance void System.Runtime.CompilerServices.CollectionBuilderAttribute::.ctor(class [mscorlib]System.Type, string) = { type(MyCollectionBuilder) string('Create') } .method public hidebysig specialname rtspecialname instance void .ctor() cil managed { ret } .method public instance class [mscorlib]System.Collections.Generic.IEnumerator`1<!T> GetEnumerator() { ldnull ret } } .class interface public abstract MyCollectionBuilder { .method public abstract virtual instance class MyCollection`1<!!T> Create<T>(valuetype System.ReadOnlySpan`1<!!T> items) { } } """; var refA = CompileIL(sourceA); string sourceB = """ #pragma warning disable 219 class Program { static void Main() { MyCollection<int> x = []; MyCollection<string> y = [null]; MyCollection<object> z = new(); } } """; var comp = CreateCompilation(sourceB, references: new[] { refA }); comp.VerifyEmitDiagnostics( // (6,31): error CS9187: Could not find an accessible 'Create' method with the expected signature: a static method whose last parameter is of type 'ReadOnlySpan<T>' and return type 'MyCollection<T>'. // MyCollection<int> x = []; Diagnostic(ErrorCode.ERR_CollectionBuilderAttributeMethodNotFound, "[]").WithArguments("Create", "T", "MyCollection<T>").WithLocation(6, 31), // (7,34): error CS9187: Could not find an accessible 'Create' method with the expected signature: a static method whose last parameter is of type 'ReadOnlySpan<T>' and return type 'MyCollection<T>'. // MyCollection<string> y = [null]; Diagnostic(ErrorCode.ERR_CollectionBuilderAttributeMethodNotFound, "[null]").WithArguments("Create", "T", "MyCollection<T>").WithLocation(7, 34)); } [CombinatorialData] [Theory] public void CollectionBuilder_InvalidBuilderType_03( [CombinatorialValues("public delegate void MyCollectionBuilder();", "public enum MyCollectionBuilder { }")] string builderTypeDefinition) { string sourceA = $$""" using System; using System.Collections; using System.Collections.Generic; using System.Runtime.CompilerServices; [CollectionBuilder(typeof(MyCollectionBuilder), "ToString")] public struct MyCollection<T> : IEnumerable<T> { IEnumerator<T> IEnumerable<T>.GetEnumerator() => default; IEnumerator IEnumerable.GetEnumerator() => default; } {{builderTypeDefinition}} """; string sourceB = """ #pragma warning disable 219 class Program { static void Main() { MyCollection<int> x = []; MyCollection<string> y = [null]; MyCollection<object> z = new(); } } """; var comp = CreateCompilation(new[] { sourceA, sourceB }, targetFramework: TargetFramework.Net80); comp.VerifyEmitDiagnostics( // 0.cs(5,2): error CS9185: The CollectionBuilderAttribute builder type must be a non-generic class or struct. // [CollectionBuilder(typeof(MyCollectionBuilder), "ToString")] Diagnostic(ErrorCode.ERR_CollectionBuilderAttributeInvalidType, "CollectionBuilder").WithLocation(5, 2), // 1.cs(6,31): error CS9187: Could not find an accessible 'ToString' method with the expected signature: a static method whose last parameter is of type 'ReadOnlySpan<T>' and return type 'MyCollection<T>'. // MyCollection<int> x = []; Diagnostic(ErrorCode.ERR_CollectionBuilderAttributeMethodNotFound, "[]").WithArguments("ToString", "T", "MyCollection<T>").WithLocation(6, 31), // 1.cs(7,34): error CS9187: Could not find an accessible 'ToString' method with the expected signature: a static method whose last parameter is of type 'ReadOnlySpan<T>' and return type 'MyCollection<T>'. // MyCollection<string> y = [null]; Diagnostic(ErrorCode.ERR_CollectionBuilderAttributeMethodNotFound, "[null]").WithArguments("ToString", "T", "MyCollection<T>").WithLocation(7, 34)); } [CombinatorialData] [Theory] public void CollectionBuilder_InvalidBuilderType_04( [CombinatorialValues("int[]", "int*", "(object, object)")] string builderTypeName) { string sourceA = $$""" using System.Collections; using System.Collections.Generic; using System.Runtime.CompilerServices; [CollectionBuilder(typeof({{builderTypeName}}), "ToString")] public struct MyCollection<T> : IEnumerable<T> { IEnumerator<T> IEnumerable<T>.GetEnumerator() => default; IEnumerator IEnumerable.GetEnumerator() => default; } """; string sourceB = """ #pragma warning disable 219 class Program { static void Main() { MyCollection<int> x = []; MyCollection<string> y = [null]; MyCollection<object> z = new(); } } """; var comp = CreateCompilation(new[] { sourceA, sourceB }, targetFramework: TargetFramework.Net80); comp.VerifyEmitDiagnostics( // 0.cs(4,2): error CS9185: The CollectionBuilderAttribute builder type must be a non-generic class or struct. // [CollectionBuilder(typeof(int*), "ToString")] Diagnostic(ErrorCode.ERR_CollectionBuilderAttributeInvalidType, "CollectionBuilder").WithLocation(4, 2), // 1.cs(6,31): error CS9187: Could not find an accessible 'ToString' method with the expected signature: a static method whose last parameter is of type 'ReadOnlySpan<T>' and return type 'MyCollection<T>'. // MyCollection<int> x = []; Diagnostic(ErrorCode.ERR_CollectionBuilderAttributeMethodNotFound, "[]").WithArguments("ToString", "T", "MyCollection<T>").WithLocation(6, 31), // 1.cs(7,34): error CS9187: Could not find an accessible 'ToString' method with the expected signature: a static method whose last parameter is of type 'ReadOnlySpan<T>' and return type 'MyCollection<T>'. // MyCollection<string> y = [null]; Diagnostic(ErrorCode.ERR_CollectionBuilderAttributeMethodNotFound, "[null]").WithArguments("ToString", "T", "MyCollection<T>").WithLocation(7, 34)); } [Fact] public void CollectionBuilder_InvalidBuilderType_TypeParameter() { string source = """ using System.Collections; using System.Collections.Generic; using System.Runtime.CompilerServices; struct Container<T> { [CollectionBuilder(typeof(T), "ToString")] public struct MyCollection : IEnumerable<int> { IEnumerator<int> IEnumerable<int>.GetEnumerator() => default; IEnumerator IEnumerable.GetEnumerator() => default; } } #pragma warning disable 219 class Program { static void Main() { Container<int>.MyCollection x = []; Container<string>.MyCollection y = [null]; Container<object>.MyCollection z = new(); } } """; var comp = CreateCompilation(source, targetFramework: TargetFramework.Net80); comp.VerifyEmitDiagnostics( // (6,24): error CS0416: 'T': an attribute argument cannot use type parameters // [CollectionBuilder(typeof(T), "ToString")] Diagnostic(ErrorCode.ERR_AttrArgWithTypeVars, "typeof(T)").WithArguments("T").WithLocation(6, 24), // (19,44): error CS1061: 'Container<string>.MyCollection' does not contain a definition for 'Add' and no accessible extension method 'Add' accepting a first argument of type 'Container<string>.MyCollection' could be found (are you missing a using directive or an assembly reference?) // Container<string>.MyCollection y = [null]; Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "[null]").WithArguments("Container<string>.MyCollection", "Add").WithLocation(19, 44), // (19,45): error CS0037: Cannot convert null to 'int' because it is a non-nullable value type // Container<string>.MyCollection y = [null]; Diagnostic(ErrorCode.ERR_ValueCantBeNull, "null").WithArguments("int").WithLocation(19, 45)); } [CombinatorialData] [Theory] public void CollectionBuilder_NullOrEmptyMethodName([CombinatorialValues("null", "\"\"")] string methodName) { string sourceA = $$""" using System.Collections; using System.Collections.Generic; using System.Runtime.CompilerServices; [CollectionBuilder(typeof(MyCollectionBuilder), {{methodName}})] public struct MyCollection<T> : IEnumerable<T> { IEnumerator<T> IEnumerable<T>.GetEnumerator() => default; IEnumerator IEnumerable.GetEnumerator() => default; } public class MyCollectionBuilder { } """; string sourceB = """ #pragma warning disable 219 class Program { static void Main() { MyCollection<int> x = []; MyCollection<string> y = [null]; MyCollection<object> z = new(); } } """; var comp = CreateCompilation(new[] { sourceA, sourceB }, targetFramework: TargetFramework.Net80); comp.VerifyEmitDiagnostics( // 0.cs(4,2): error CS9186: The CollectionBuilderAttribute method name is invalid. // [CollectionBuilder(typeof(MyCollectionBuilder), "")] Diagnostic(ErrorCode.ERR_CollectionBuilderAttributeInvalidMethodName, "CollectionBuilder").WithLocation(4, 2), // 1.cs(6,31): error CS9187: Could not find an accessible '' method with the expected signature: a static method whose last parameter is of type 'ReadOnlySpan<T>' and return type 'MyCollection<T>'. // MyCollection<int> x = []; Diagnostic(ErrorCode.ERR_CollectionBuilderAttributeMethodNotFound, "[]").WithArguments("", "T", "MyCollection<T>").WithLocation(6, 31), // 1.cs(7,34): error CS9187: Could not find an accessible '' method with the expected signature: a static method whose last parameter is of type 'ReadOnlySpan<T>' and return type 'MyCollection<T>'. // MyCollection<string> y = [null]; Diagnostic(ErrorCode.ERR_CollectionBuilderAttributeMethodNotFound, "[null]").WithArguments("", "T", "MyCollection<T>").WithLocation(7, 34)); } [CombinatorialData] [Theory] public void CollectionBuilder_NullOrEmptyMethodName_FromMetadata([CombinatorialValues("nullref", "''")] string methodName) { // [CollectionBuilder(typeof(MyCollectionBuilder), "")] string sourceA = $$""" .class public sealed System.ReadOnlySpan`1<T> extends [mscorlib]System.ValueType { } .class public sealed System.Runtime.CompilerServices.CollectionBuilderAttribute extends [mscorlib]System.Attribute { .method public hidebysig specialname rtspecialname instance void .ctor(class [mscorlib]System.Type builderType, string methodName) cil managed { ret } } .class public sealed MyCollection`1<T> { .custom instance void System.Runtime.CompilerServices.CollectionBuilderAttribute::.ctor(class [mscorlib]System.Type, string) = { type(MyCollectionBuilder) string({{methodName}}) } .method public hidebysig specialname rtspecialname instance void .ctor() cil managed { ret } .method public instance class [mscorlib]System.Collections.Generic.IEnumerator`1<!T> GetEnumerator() { ldnull ret } } .class public sealed MyCollectionBuilder { .method public hidebysig specialname rtspecialname instance void .ctor() cil managed { ret } .method public static class MyCollection`1<!!T> Create<T>(valuetype System.ReadOnlySpan`1<!!T> items) { ldnull ret } } """; var refA = CompileIL(sourceA); string sourceB = """ #pragma warning disable 219 class Program { static void Main() { MyCollection<int> x = []; MyCollection<string> y = [null]; MyCollection<object> z = new(); } } """; var comp = CreateCompilation(sourceB, references: new[] { refA }); comp.VerifyEmitDiagnostics( // (6,31): error CS9187: Could not find an accessible '' method with the expected signature: a static method whose last parameter is of type 'ReadOnlySpan<T>' and return type 'MyCollection<T>'. // MyCollection<int> x = []; Diagnostic(ErrorCode.ERR_CollectionBuilderAttributeMethodNotFound, "[]").WithArguments("", "T", "MyCollection<T>").WithLocation(6, 31), // (7,34): error CS9187: Could not find an accessible '' method with the expected signature: a static method whose last parameter is of type 'ReadOnlySpan<T>' and return type 'MyCollection<T>'. // MyCollection<string> y = [null]; Diagnostic(ErrorCode.ERR_CollectionBuilderAttributeMethodNotFound, "[null]").WithArguments("", "T", "MyCollection<T>").WithLocation(7, 34)); } [CombinatorialData] [Theory] public void CollectionBuilder_InstanceMethod(bool useCompilationReference) { string sourceA = """ using System; using System.Collections; using System.Collections.Generic; using System.Runtime.CompilerServices; [CollectionBuilder(typeof(MyCollectionBuilder), "Create")] public struct MyCollection<T> : IEnumerable<T> { IEnumerator<T> IEnumerable<T>.GetEnumerator() => default; IEnumerator IEnumerable.GetEnumerator() => default; } public class MyCollectionBuilder { public MyCollection<T> Create<T>(ReadOnlySpan<T> items) => default; } """; var comp = CreateCompilation(sourceA, targetFramework: TargetFramework.Net80); var refA = AsReference(comp, useCompilationReference); string sourceB = """ #pragma warning disable 219 class Program { static void Main() { MyCollection<int> x = []; MyCollection<string> y = [null]; MyCollection<object> z = new(); } } """; comp = CreateCompilation(sourceB, references: new[] { refA }, targetFramework: TargetFramework.Net80); comp.VerifyEmitDiagnostics( // (6,31): error CS9187: Could not find an accessible 'Create' method with the expected signature: a static method whose last parameter is of type 'ReadOnlySpan<T>' and return type 'MyCollection<T>'. // MyCollection<int> x = []; Diagnostic(ErrorCode.ERR_CollectionBuilderAttributeMethodNotFound, "[]").WithArguments("Create", "T", "MyCollection<T>").WithLocation(6, 31), // (7,34): error CS9187: Could not find an accessible 'Create' method with the expected signature: a static method whose last parameter is of type 'ReadOnlySpan<T>' and return type 'MyCollection<T>'. // MyCollection<string> y = [null]; Diagnostic(ErrorCode.ERR_CollectionBuilderAttributeMethodNotFound, "[null]").WithArguments("Create", "T", "MyCollection<T>").WithLocation(7, 34)); } [CombinatorialData] [Theory] public void CollectionBuilder_OtherMember_01( [CombinatorialValues( "public MyCollection Create = null;", "public MyCollection Create => null;", "public class Create { }")] string createMember, bool useCompilationReference) { string sourceA = $$""" using System; using System.Collections; using System.Runtime.CompilerServices; [CollectionBuilder(typeof(MyCollectionBuilder), "Create")] public class MyCollection : IEnumerable { IEnumerator IEnumerable.GetEnumerator() => default; } public class MyCollectionBuilder { {{createMember}} } """; var comp = CreateCompilation(sourceA, targetFramework: TargetFramework.Net80); var refA = AsReference(comp, useCompilationReference); string sourceB = """ #pragma warning disable 219 class Program { static void Main() { MyCollection x = []; MyCollection y = [null]; MyCollection z = new(); } } """; comp = CreateCompilation(sourceB, references: new[] { refA }, targetFramework: TargetFramework.Net80); comp.VerifyEmitDiagnostics( // (6,26): error CS9187: Could not find an accessible 'Create' method with the expected signature: a static method whose last parameter is of type 'ReadOnlySpan<object>' and return type 'MyCollection'. // MyCollection x = []; Diagnostic(ErrorCode.ERR_CollectionBuilderAttributeMethodNotFound, "[]").WithArguments("Create", "object", "MyCollection").WithLocation(6, 26), // (7,26): error CS9187: Could not find an accessible 'Create' method with the expected signature: a static method whose last parameter is of type 'ReadOnlySpan<object>' and return type 'MyCollection'. // MyCollection y = [null]; Diagnostic(ErrorCode.ERR_CollectionBuilderAttributeMethodNotFound, "[null]").WithArguments("Create", "object", "MyCollection").WithLocation(7, 26)); } [CombinatorialData] [Theory] public void CollectionBuilder_TypeDifferences_Dynamic_01(bool useCompilationReference) { CollectionBuilder_TypeDifferences("object", "dynamic", "1, 2, 3", "[1, 2, 3]", useCompilationReference); } [CombinatorialData] [Theory] public void CollectionBuilder_TypeDifferences_Dynamic_02(bool useCompilationReference) { string sourceA = $$""" using System; using System.Collections; using System.Collections.Generic; using System.Runtime.CompilerServices; [CollectionBuilder(typeof(MyCollectionBuilder), "Create")] public struct MyCollection { private readonly List<dynamic> _list; public MyCollection(List<dynamic> list) { _list = list; } public IEnumerator<dynamic> GetEnumerator() => _list.GetEnumerator(); } public sealed class MyCollectionBuilder { public static MyCollection Create(ReadOnlySpan<object> items) => new MyCollection(new List<dynamic>(items.ToArray())); } """; var comp = CreateCompilation(sourceA, targetFramework: TargetFramework.Net80); var refA = AsReference(comp, useCompilationReference); string sourceB = $$""" using System.Collections.Generic; #pragma warning disable 219 class Program { static void Main() { MyCollection x = []; GetElements(x).Report(); MyCollection y = [1, 2, 3]; GetElements(y).Report(); } static IEnumerable<object> GetElements(MyCollection c) { foreach (var e in c) yield return e; } } """; CompileAndVerify( new[] { sourceB, s_collectionExtensions }, references: new[] { refA }, targetFramework: TargetFramework.Net80, verify: Verification.Fails, expectedOutput: IncludeExpectedOutput($"[], [1, 2, 3], ")); } [CombinatorialData] [Theory] public void CollectionBuilder_TypeDifferences_TupleElementNames(bool useCompilationReference) { CollectionBuilder_TypeDifferences("(int, int)", "(int A, int B)", "(1, 2), default", "[(1, 2), (0, 0)]", useCompilationReference); CollectionBuilder_TypeDifferences("(int A, int B)", "(int, int)", "(1, 2), default", "[(1, 2), (0, 0)]", useCompilationReference); } [CombinatorialData] [Theory] public void CollectionBuilder_TypeDifferences_Nullability(bool useCompilationReference) { CollectionBuilder_TypeDifferences("object", "object?", "1, 2, 3", "[1, 2, 3]", useCompilationReference); CollectionBuilder_TypeDifferences("object?", "object", "1, null, 3", "[1, null, 3]", useCompilationReference); } private void CollectionBuilder_TypeDifferences(string collectionElementType, string builderElementType, string values, string expectedOutput, bool useCompilationReference) { string sourceA = $$""" #nullable enable using System; using System.Collections; using System.Collections.Generic; using System.Runtime.CompilerServices; [CollectionBuilder(typeof(MyCollectionBuilder), "Create")] public struct MyCollection : IEnumerable<{{collectionElementType}}> { private readonly List<{{collectionElementType}}> _list; public MyCollection(List<{{collectionElementType}}> list) { _list = list; } public IEnumerator<{{collectionElementType}}> GetEnumerator() => _list.GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); } public sealed class MyCollectionBuilder { public static MyCollection Create(ReadOnlySpan<{{builderElementType}}> items) => new MyCollection(new List<{{collectionElementType}}>(items.ToArray())); } """; var comp = CreateCompilation(sourceA, targetFramework: TargetFramework.Net80); var refA = AsReference(comp, useCompilationReference); string sourceB = $$""" #nullable enable #pragma warning disable 219 class Program { static void Main() { MyCollection x = []; x.Report(); MyCollection y = [{{values}}]; y.Report(); } } """; CompileAndVerify( new[] { sourceB, s_collectionExtensions }, references: new[] { refA }, targetFramework: TargetFramework.Net80, verify: Verification.Fails, expectedOutput: IncludeExpectedOutput($"[], {expectedOutput}, ")); } // If there are multiple attributes, the first is used. [CombinatorialData] [Theory] public void CollectionBuilder_MultipleAttributes(bool useCompilationReference) { string sourceAttribute = """ namespace System.Runtime.CompilerServices { [AttributeUsage(AttributeTargets.All, AllowMultiple = true)] public sealed class CollectionBuilderAttribute : Attribute { public CollectionBuilderAttribute(Type builderType, string methodName) { } } } """; string sourceA = """ using System; using System.Collections; using System.Collections.Generic; using System.Runtime.CompilerServices; [CollectionBuilder(typeof(MyCollectionBuilder1), "Create1")] [CollectionBuilder(typeof(MyCollectionBuilder2), "Create2")] public sealed class MyCollection<T> : IEnumerable<T> { private readonly List<T> _list; public MyCollection(List<T> list) { _list = list; } public IEnumerator<T> GetEnumerator() => _list.GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); } public struct MyCollectionBuilder1 { public static MyCollection<T> Create1<T>(ReadOnlySpan<T> items) => new MyCollection<T>(new List<T>(items.ToArray())); } public struct MyCollectionBuilder2 { public static MyCollection<T> Create2<T>(ReadOnlySpan<T> items) => throw null; } """; var comp = CreateCompilation(new[] { sourceAttribute, sourceA }, targetFramework: TargetFramework.Net80); var refA = AsReference(comp, useCompilationReference); string sourceB = """ class Program { static MyCollection<int> F() => [1, 2, 3]; static void Main() { F().Report(); } } """; var verifier = CompileAndVerify( new[] { sourceB, s_collectionExtensions }, references: new[] { refA }, targetFramework: TargetFramework.Net80, verify: Verification.FailsPEVerify, expectedOutput: IncludeExpectedOutput("[1, 2, 3], ")); comp = (CSharpCompilation)verifier.Compilation; var collectionType = (NamedTypeSymbol)comp.GetMember<MethodSymbol>("Program.F").ReturnType; Assert.Equal("MyCollection<System.Int32>", collectionType.ToTestDisplayString()); TypeSymbol builderType; string methodName; Assert.True(collectionType.HasCollectionBuilderAttribute(out builderType, out methodName)); Assert.Equal("MyCollectionBuilder1", builderType.ToTestDisplayString()); Assert.Equal("Create1", methodName); } [Fact] public void CollectionBuilder_GenericBuilderType_01() { string sourceA = """ using System; using System.Collections; using System.Collections.Generic; using System.Runtime.CompilerServices; [CollectionBuilder(typeof(MyCollectionBuilder<>), "Create")] public struct MyCollection<T> : IEnumerable<T> { IEnumerator<T> IEnumerable<T>.GetEnumerator() => default; IEnumerator IEnumerable.GetEnumerator() => default; } public sealed class MyCollectionBuilder<T> { public static MyCollection<T> Create(ReadOnlySpan<T> items) => default; } """; string sourceB = """ #pragma warning disable 219 class Program { static void Main() { MyCollection<int> x = []; MyCollection<string> y = [null]; MyCollection<object> z = new(); } } """; var comp = CreateCompilation(new[] { sourceA, sourceB }, targetFramework: TargetFramework.Net80); comp.VerifyEmitDiagnostics( // 0.cs(5,2): error CS9185: The CollectionBuilderAttribute builder type must be a non-generic class or struct. // [CollectionBuilder(typeof(MyCollectionBuilder<>), "Create")] Diagnostic(ErrorCode.ERR_CollectionBuilderAttributeInvalidType, "CollectionBuilder").WithLocation(5, 2), // 1.cs(6,31): error CS9187: Could not find an accessible 'Create' method with the expected signature: a static method whose last parameter is of type 'ReadOnlySpan<T>' and return type 'MyCollection<T>'. // MyCollection<int> x = []; Diagnostic(ErrorCode.ERR_CollectionBuilderAttributeMethodNotFound, "[]").WithArguments("Create", "T", "MyCollection<T>").WithLocation(6, 31), // 1.cs(7,34): error CS9187: Could not find an accessible 'Create' method with the expected signature: a static method whose last parameter is of type 'ReadOnlySpan<T>' and return type 'MyCollection<T>'. // MyCollection<string> y = [null]; Diagnostic(ErrorCode.ERR_CollectionBuilderAttributeMethodNotFound, "[null]").WithArguments("Create", "T", "MyCollection<T>").WithLocation(7, 34)); } [Fact] public void CollectionBuilder_GenericBuilderType_02() { string sourceA = """ using System; using System.Collections; using System.Collections.Generic; using System.Runtime.CompilerServices; [CollectionBuilder(typeof(MyCollectionBuilder<int>), "Create")] public struct MyCollection<T> : IEnumerable<T> { IEnumerator<T> IEnumerable<T>.GetEnumerator() => default; IEnumerator IEnumerable.GetEnumerator() => default; } public sealed class MyCollectionBuilder<T> { public static MyCollection<U> Create<U>(ReadOnlySpan<U> items) => default; } """; string sourceB = """ #pragma warning disable 219 class Program { static void Main() { MyCollection<int> x = []; MyCollection<string> y = [null]; MyCollection<object> z = new(); } } """; var comp = CreateCompilation(new[] { sourceA, sourceB }, targetFramework: TargetFramework.Net80); comp.VerifyEmitDiagnostics( // 0.cs(5,2): error CS9185: The CollectionBuilderAttribute builder type must be a non-generic class or struct. // [CollectionBuilder(typeof(MyCollectionBuilder<int>), "Create")] Diagnostic(ErrorCode.ERR_CollectionBuilderAttributeInvalidType, "CollectionBuilder").WithLocation(5, 2), // 1.cs(6,31): error CS9187: Could not find an accessible 'Create' method with the expected signature: a static method whose last parameter is of type 'ReadOnlySpan<T>' and return type 'MyCollection<T>'. // MyCollection<int> x = []; Diagnostic(ErrorCode.ERR_CollectionBuilderAttributeMethodNotFound, "[]").WithArguments("Create", "T", "MyCollection<T>").WithLocation(6, 31), // 1.cs(7,34): error CS9187: Could not find an accessible 'Create' method with the expected signature: a static method whose last parameter is of type 'ReadOnlySpan<T>' and return type 'MyCollection<T>'. // MyCollection<string> y = [null]; Diagnostic(ErrorCode.ERR_CollectionBuilderAttributeMethodNotFound, "[null]").WithArguments("Create", "T", "MyCollection<T>").WithLocation(7, 34)); } [Fact] public void CollectionBuilder_GenericBuilderType_03() { string source = """ using System; using System.Collections; using System.Collections.Generic; using System.Runtime.CompilerServices; public class Container<T> { [CollectionBuilder(typeof(MyCollectionBuilder), "Create")] public struct MyCollection : IEnumerable<T> { private readonly List<T> _list; public MyCollection(List<T> list) { _list = list; } public IEnumerator<T> GetEnumerator() => _list.GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); } public sealed class MyCollectionBuilder { public static MyCollection Create(ReadOnlySpan<T> items) => new MyCollection(new List<T>(items.ToArray())); } } #pragma warning disable 219 class Program { static void Main() { Container<string>.MyCollection x = []; Container<int>.MyCollection y = [default]; Container<object>.MyCollection z = new(); } } """; var comp = CreateCompilation(source, targetFramework: TargetFramework.Net80); comp.VerifyEmitDiagnostics( // (7,24): error CS0416: 'Container<T>.MyCollectionBuilder': an attribute argument cannot use type parameters // [CollectionBuilder(typeof(MyCollectionBuilder), "Create")] Diagnostic(ErrorCode.ERR_AttrArgWithTypeVars, "typeof(MyCollectionBuilder)").WithArguments("Container<T>.MyCollectionBuilder").WithLocation(7, 24), // (27,41): error CS1061: 'Container<int>.MyCollection' does not contain a definition for 'Add' and no accessible extension method 'Add' accepting a first argument of type 'Container<int>.MyCollection' could be found (are you missing a using directive or an assembly reference?) // Container<int>.MyCollection y = [default]; Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "[default]").WithArguments("Container<int>.MyCollection", "Add").WithLocation(27, 41)); } [CombinatorialData] [Theory] public void CollectionBuilder_GenericCollectionContainerType_01(bool useCompilationReference) { string sourceA = """ using System; using System.Collections; using System.Collections.Generic; using System.Runtime.CompilerServices; public class Container<T> { [CollectionBuilder(typeof(MyCollectionBuilder), "Create")] public struct MyCollection : IEnumerable<T> { private readonly List<T> _list; public MyCollection(List<T> list) { _list = list; } public IEnumerator<T> GetEnumerator() => _list.GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); } } public sealed class MyCollectionBuilder { public static Container<T>.MyCollection Create<T>(ReadOnlySpan<T> items) => new Container<T>.MyCollection(new List<T>(items.ToArray())); } """; var comp = CreateCompilation(sourceA, targetFramework: TargetFramework.Net80); var refA = AsReference(comp, useCompilationReference); string sourceB = """ class Program { static void Main() { Container<string>.MyCollection x = []; x.Report(includeType: true); Container<int>.MyCollection y = [1, 2, 3]; y.Report(includeType: true); } } """; CompileAndVerify( new[] { sourceB, s_collectionExtensions }, references: new[] { refA }, targetFramework: TargetFramework.Net80, verify: Verification.FailsPEVerify, expectedOutput: IncludeExpectedOutput("(Container<T>.MyCollection<System.String>) [], (Container<T>.MyCollection<System.Int32>) [1, 2, 3], ")); } [CombinatorialData] [Theory] public void CollectionBuilder_GenericCollectionContainerType_02(bool useCompilationReference) { string sourceA = """ using System; using System.Collections; using System.Collections.Generic; using System.Runtime.CompilerServices; public class Container<T> { [CollectionBuilder(typeof(MyCollectionBuilder), "Create")] public struct MyCollection : IEnumerable<int> { private readonly List<int> _list; public MyCollection(List<int> list) { _list = list; } public IEnumerator<int> GetEnumerator() => _list.GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); } } public sealed class MyCollectionBuilder { public static Container<T>.MyCollection Create<T>(ReadOnlySpan<int> items) => new Container<T>.MyCollection(new List<int>(items.ToArray())); } """; var comp = CreateCompilation(sourceA, targetFramework: TargetFramework.Net80); var refA = AsReference(comp, useCompilationReference); string sourceB = """ class Program { static void Main() { Container<int>.MyCollection x = []; x.Report(includeType: true); Container<string>.MyCollection y = [1, 2, 3]; y.Report(includeType: true); } } """; CompileAndVerify( new[] { sourceB, s_collectionExtensions }, references: new[] { refA }, targetFramework: TargetFramework.Net80, verify: Verification.FailsPEVerify, expectedOutput: IncludeExpectedOutput("(Container<T>.MyCollection<System.Int32>) [], (Container<T>.MyCollection<System.String>) [1, 2, 3], ")); } [CombinatorialData] [Theory] public void CollectionBuilder_GenericCollectionContainerType_03(bool useCompilationReference) { string sourceA = """ using System; using System.Collections; using System.Collections.Generic; using System.Runtime.CompilerServices; public class Container<T> { [CollectionBuilder(typeof(MyCollectionBuilder), "Create")] public struct MyCollection<U> : IEnumerable<U> { private readonly List<U> _list; public MyCollection(List<U> list) { _list = list; } public IEnumerator<U> GetEnumerator() => _list.GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); } } public sealed class MyCollectionBuilder { public static Container<T>.MyCollection<U> Create<T, U>(ReadOnlySpan<U> items) => new Container<T>.MyCollection<U>(new List<U>(items.ToArray())); } """; var comp = CreateCompilation(sourceA, targetFramework: TargetFramework.Net80); var refA = AsReference(comp, useCompilationReference); string sourceB = """ class Program { static void Main() { Container<int>.MyCollection<string> x = []; x.Report(includeType: true); Container<string>.MyCollection<int> y = [1, 2, 3]; y.Report(includeType: true); } } """; CompileAndVerify( new[] { sourceB, s_collectionExtensions }, references: new[] { refA }, targetFramework: TargetFramework.Net80, verify: Verification.FailsPEVerify, expectedOutput: IncludeExpectedOutput("(Container<T>.MyCollection<System.Int32, System.String>) [], (Container<T>.MyCollection<System.String, System.Int32>) [1, 2, 3], ")); } [CombinatorialData] [Theory] public void CollectionBuilder_GenericType_ElementTypeFirstOfTwo(bool useCompilationReference) { string sourceA = """ using System; using System.Collections; using System.Collections.Generic; using System.Runtime.CompilerServices; [CollectionBuilder(typeof(MyCollectionBuilder), "Create")] public struct MyCollection<T, U> : IEnumerable<T> { private readonly List<T> _list; public MyCollection(List<T> list) { _list = list; } public IEnumerator<T> GetEnumerator() => _list.GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); } public sealed class MyCollectionBuilder { public static MyCollection<T, U> Create<T, U>(ReadOnlySpan<T> items) => new MyCollection<T, U>(new List<T>(items.ToArray())); } """; var comp = CreateCompilation(sourceA, targetFramework: TargetFramework.Net80); var refA = AsReference(comp, useCompilationReference); string sourceB = """ class Program { static void Main() { MyCollection<string, int> x = []; x.Report(includeType: true); MyCollection<int, string> y = [1, 2, 3]; y.Report(includeType: true); } } """; CompileAndVerify( new[] { sourceB, s_collectionExtensions }, references: new[] { refA }, targetFramework: TargetFramework.Net80, verify: Verification.FailsPEVerify, expectedOutput: IncludeExpectedOutput("(MyCollection<System.String, System.Int32>) [], (MyCollection<System.Int32, System.String>) [1, 2, 3], ")); } [CombinatorialData] [Theory] public void CollectionBuilder_GenericType_ElementTypeSecondOfTwo(bool useCompilationReference) { string sourceA = """ using System; using System.Collections; using System.Collections.Generic; using System.Runtime.CompilerServices; [CollectionBuilder(typeof(MyCollectionBuilder), "Create")] public struct MyCollection<T, U> : IEnumerable<U> { private readonly List<U> _list; public MyCollection(List<U> list) { _list = list; } public IEnumerator<U> GetEnumerator() => _list.GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); } public sealed class MyCollectionBuilder { public static MyCollection<T, U> Create<T, U>(ReadOnlySpan<U> items) => new MyCollection<T, U>(new List<U>(items.ToArray())); } """; var comp = CreateCompilation(sourceA, targetFramework: TargetFramework.Net80); var refA = AsReference(comp, useCompilationReference); string sourceB = """ class Program { static void Main() { MyCollection<int, string> x = []; x.Report(includeType: true); MyCollection<string, int> y = [1, 2, 3]; y.Report(includeType: true); } } """; CompileAndVerify( new[] { sourceB, s_collectionExtensions }, references: new[] { refA }, targetFramework: TargetFramework.Net80, verify: Verification.FailsPEVerify, expectedOutput: IncludeExpectedOutput("(MyCollection<System.Int32, System.String>) [], (MyCollection<System.String, System.Int32>) [1, 2, 3], ")); } [CombinatorialData] [Theory] public void CollectionBuilder_InaccessibleBuilderType_01(bool useCompilationReference) { string sourceA = """ using System; using System.Collections; using System.Collections.Generic; using System.Runtime.CompilerServices; [CollectionBuilder(typeof(MyCollectionBuilder), "Create")] public struct MyCollection<T> : IEnumerable<T> { IEnumerator<T> IEnumerable<T>.GetEnumerator() => default; IEnumerator IEnumerable.GetEnumerator() => default; } internal class MyCollectionBuilder { public static MyCollection<T> Create<T>(ReadOnlySpan<T> items) => default; } """; var comp = CreateCompilation(sourceA, targetFramework: TargetFramework.Net80); var refA = AsReference(comp, useCompilationReference); string sourceB = """ #pragma warning disable 219 class Program { static void Main() { MyCollection<int> x = []; MyCollection<string> y = [null]; MyCollection<object> z = new(); } } """; comp = CreateCompilation(sourceB, references: new[] { refA }, targetFramework: TargetFramework.Net80); comp.VerifyEmitDiagnostics( // (6,31): error CS9187: Could not find an accessible 'Create' method with the expected signature: a static method whose last parameter is of type 'ReadOnlySpan<T>' and return type 'MyCollection<T>'. // MyCollection<int> x = []; Diagnostic(ErrorCode.ERR_CollectionBuilderAttributeMethodNotFound, "[]").WithArguments("Create", "T", "MyCollection<T>").WithLocation(6, 31), // (7,34): error CS9187: Could not find an accessible 'Create' method with the expected signature: a static method whose last parameter is of type 'ReadOnlySpan<T>' and return type 'MyCollection<T>'. // MyCollection<string> y = [null]; Diagnostic(ErrorCode.ERR_CollectionBuilderAttributeMethodNotFound, "[null]").WithArguments("Create", "T", "MyCollection<T>").WithLocation(7, 34)); } [CombinatorialData] [Theory] public void CollectionBuilder_NestedBuilderType(bool useCompilationReference) { string sourceA = """ using System; using System.Collections; using System.Collections.Generic; using System.Runtime.CompilerServices; [CollectionBuilder(typeof(MyCollectionBuilder), "Create")] public class MyCollection : IEnumerable<int> { private readonly List<int> _list; public MyCollection(List<int> list) { _list = list; } public IEnumerator<int> GetEnumerator() => _list.GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); public struct MyCollectionBuilder { public static MyCollection Create(ReadOnlySpan<int> items) => new MyCollection(new List<int>(items.ToArray())); } } """; var comp = CreateCompilation(sourceA, targetFramework: TargetFramework.Net80); var refA = AsReference(comp, useCompilationReference); string sourceB = """ #pragma warning disable 219 class Program { static void Main() { MyCollection x = []; x.Report(); MyCollection y = [1, 2, 3]; y.Report(); } } """; CompileAndVerify( new[] { sourceB, s_collectionExtensions }, references: new[] { refA }, targetFramework: TargetFramework.Net80, verify: Verification.FailsPEVerify, expectedOutput: IncludeExpectedOutput("[], [1, 2, 3], ")); } [CombinatorialData] [Theory] public void CollectionBuilder_InaccessibleBuilderType_02(bool useCompilationReference) { string sourceA = """ using System; using System.Collections; using System.Collections.Generic; using System.Runtime.CompilerServices; [CollectionBuilder(typeof(MyCollectionBuilder), "Create")] public class MyCollection : IEnumerable<int> { public IEnumerator<int> GetEnumerator() => default; IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); protected class MyCollectionBuilder { public static MyCollection Create(ReadOnlySpan<int> items) => default; } static readonly MyCollection _instance = [1, 2, 3]; } """; var comp = CreateCompilation(sourceA, targetFramework: TargetFramework.Net80); var refA = AsReference(comp, useCompilationReference); string sourceB = """ #pragma warning disable 219 class Program { static void Main() { MyCollection x = []; MyCollection y = [1, 2, 3]; MyCollection z = new(); } } """; comp = CreateCompilation(sourceB, references: new[] { refA }, targetFramework: TargetFramework.Net80); comp.VerifyEmitDiagnostics( // (6,26): error CS9187: Could not find an accessible 'Create' method with the expected signature: a static method whose last parameter is of type 'ReadOnlySpan<int>' and return type 'MyCollection'. // MyCollection x = []; Diagnostic(ErrorCode.ERR_CollectionBuilderAttributeMethodNotFound, "[]").WithArguments("Create", "int", "MyCollection").WithLocation(6, 26), // (7,26): error CS9187: Could not find an accessible 'Create' method with the expected signature: a static method whose last parameter is of type 'ReadOnlySpan<int>' and return type 'MyCollection'. // MyCollection y = [1, 2, 3]; Diagnostic(ErrorCode.ERR_CollectionBuilderAttributeMethodNotFound, "[1, 2, 3]").WithArguments("Create", "int", "MyCollection").WithLocation(7, 26)); } [CombinatorialData] [Theory] public void CollectionBuilder_InaccessibleMethod_01(bool useCompilationReference) { string sourceA = """ using System; using System.Collections; using System.Collections.Generic; using System.Runtime.CompilerServices; [CollectionBuilder(typeof(MyCollectionBuilder), "Create")] public struct MyCollection<T> : IEnumerable<T> { static readonly MyCollection<int> _instance = [1, 2, 3]; IEnumerator<T> IEnumerable<T>.GetEnumerator() => default; IEnumerator IEnumerable.GetEnumerator() => default; } public class MyCollectionBuilder { internal static MyCollection<T> Create<T>(ReadOnlySpan<T> items) => default; } """; var comp = CreateCompilation(sourceA, targetFramework: TargetFramework.Net80); var refA = AsReference(comp, useCompilationReference); string sourceB = """ #pragma warning disable 219 class Program { static void Main() { MyCollection<int> x = []; MyCollection<string> y = [null]; MyCollection<object> z = new(); } } """; comp = CreateCompilation(sourceB, references: new[] { refA }, targetFramework: TargetFramework.Net80); comp.VerifyEmitDiagnostics( // (6,31): error CS9187: Could not find an accessible 'Create' method with the expected signature: a static method whose last parameter is of type 'ReadOnlySpan<T>' and return type 'MyCollection<T>'. // MyCollection<int> x = []; Diagnostic(ErrorCode.ERR_CollectionBuilderAttributeMethodNotFound, "[]").WithArguments("Create", "T", "MyCollection<T>").WithLocation(6, 31), // (7,34): error CS9187: Could not find an accessible 'Create' method with the expected signature: a static method whose last parameter is of type 'ReadOnlySpan<T>' and return type 'MyCollection<T>'. // MyCollection<string> y = [null]; Diagnostic(ErrorCode.ERR_CollectionBuilderAttributeMethodNotFound, "[null]").WithArguments("Create", "T", "MyCollection<T>").WithLocation(7, 34)); } [Fact] public void CollectionBuilder_InaccessibleMethod_02() { string sourceA = """ using System; using System.Collections; using System.Collections.Generic; using System.Runtime.CompilerServices; [CollectionBuilder(typeof(MyCollectionBuilder), "Create")] struct MyCollection<T> : IEnumerable<T> { IEnumerator<T> IEnumerable<T>.GetEnumerator() => default; IEnumerator IEnumerable.GetEnumerator() => default; } class MyCollectionBuilder { private static MyCollection<T> Create<T>(ReadOnlySpan<T> items) => default; } """; string sourceB = """ #pragma warning disable 219 class Program { static void Main() { MyCollection<int> x = []; MyCollection<string> y = [null]; MyCollection<object> z = new(); } } """; var comp = CreateCompilation(new[] { sourceA, sourceB }, targetFramework: TargetFramework.Net80); comp.VerifyEmitDiagnostics( // (6,31): error CS9187: Could not find an accessible 'Create' method with the expected signature: a static method whose last parameter is of type 'ReadOnlySpan<T>' and return type 'MyCollection<T>'. // MyCollection<int> x = []; Diagnostic(ErrorCode.ERR_CollectionBuilderAttributeMethodNotFound, "[]").WithArguments("Create", "T", "MyCollection<T>").WithLocation(6, 31), // (7,34): error CS9187: Could not find an accessible 'Create' method with the expected signature: a static method whose last parameter is of type 'ReadOnlySpan<T>' and return type 'MyCollection<T>'. // MyCollection<string> y = [null]; Diagnostic(ErrorCode.ERR_CollectionBuilderAttributeMethodNotFound, "[null]").WithArguments("Create", "T", "MyCollection<T>").WithLocation(7, 34)); } [CombinatorialData] [Theory] public void CollectionBuilder_Overloads_01(bool useCompilationReference) { string sourceA = """ using System; using System.Collections; using System.Collections.Generic; using System.Runtime.CompilerServices; [CollectionBuilder(typeof(MyCollectionBuilder), "Create")] public struct MyCollection<T> : IEnumerable<T> { private readonly List<T> _list; public MyCollection(List<T> list) { _list = list; } public IEnumerator<T> GetEnumerator() => _list.GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); } public class MyCollectionBuilder { public static MyCollection<T> Create<T>() { throw null; } public static MyCollection<T> Create<T>(Span<T> items) { throw null; } public static MyCollection<T> Create<T>(ReadOnlySpan<T> items) { return new MyCollection<T>(new List<T>(items.ToArray())); } public static MyCollection<T> Create<T>(ReadOnlySpan<T> items, int index = 0) { throw null; } } """; var comp = CreateCompilation(sourceA, targetFramework: TargetFramework.Net80); var refA = AsReference(comp, useCompilationReference); string sourceB = """ class Program { static void Main() { MyCollection<string> x = []; x.Report(); MyCollection<int> y = [1, 2, 3]; y.Report(); } } """; CompileAndVerify( new[] { sourceB, s_collectionExtensions }, references: new[] { refA }, targetFramework: TargetFramework.Net80, verify: Verification.FailsPEVerify, expectedOutput: IncludeExpectedOutput("[], [1, 2, 3], ")); } [CombinatorialData] [Theory] public void CollectionBuilder_Overloads_02(bool useCompilationReference) { string sourceA = """ using System; using System.Collections; using System.Collections.Generic; using System.Runtime.CompilerServices; [CollectionBuilder(typeof(MyCollectionBuilder), "Create")] public struct MyCollection<T> : IEnumerable<T> { private readonly List<T> _list; public MyCollection(List<T> list) { _list = list; } public IEnumerator<T> GetEnumerator() => _list.GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); } public class MyCollectionBuilder { public static MyCollection<T> Create<T>(ReadOnlySpan<T> items) { return new MyCollection<T>(new List<T>(items.ToArray())); } public static MyCollection<int> Create(ReadOnlySpan<int> items) { throw null; } } """; var comp = CreateCompilation(sourceA, targetFramework: TargetFramework.Net80); var refA = AsReference(comp, useCompilationReference); string sourceB = """ class Program { static void Main() { MyCollection<string> x = []; x.Report(); MyCollection<int> y = [1, 2, 3]; y.Report(); } } """; CompileAndVerify( new[] { sourceB, s_collectionExtensions }, references: new[] { refA }, targetFramework: TargetFramework.Net80, verify: Verification.FailsPEVerify, expectedOutput: IncludeExpectedOutput("[], [1, 2, 3], ")); } [CombinatorialData] [Theory] public void CollectionBuilder_UnexpectedSignature_01( [CombinatorialValues( "public static MyCollection<int> Create(ReadOnlySpan<int> items) => default;", // constructed parameter and return types "public static MyCollection<T> Create<T>(ReadOnlySpan<int> items) => default;", // constructed parameter type "public static MyCollection<int> Create<T>(ReadOnlySpan<T> items) => default;", // constructed return type "public static MyCollection<T> Create<T>(ReadOnlySpan<T> items, int index = 0) => default;", // optional parameter "public static MyCollection<T> Create<T>() => default;", // no parameters "public static void Create<T>(ReadOnlySpan<T> items) { }", // no return type "public static MyCollection<T> Create<T>(ReadOnlySpan<T> items, int index = 0) => default;", // optional parameter "public static MyCollection<T> Create<T>(ReadOnlySpan<T> items, params object[] args) => default;", // params "public static MyCollection<T> Create<T, U>(ReadOnlySpan<T> items) => default;", // extra type parameter "public static MyCollection<T> Create<T>(Span<T> items) => default;", // Span<T> "public static MyCollection<T> Create<T>(T[] items) => default;", // T[] "public static MyCollection<T> Create<T>(in ReadOnlySpan<T> items) => default;", // in parameter "public static MyCollection<T> Create<T>(ref ReadOnlySpan<T> items) => default;", // ref parameter "public static MyCollection<T> Create<T>(out ReadOnlySpan<T> items) { items = default; return default; }")] // out parameter string methodDeclaration, bool useCompilationReference) { string sourceA = $$""" using System; using System.Collections; using System.Collections.Generic; using System.Runtime.CompilerServices; [CollectionBuilder(typeof(MyCollectionBuilder), "Create")] public struct MyCollection<T> : IEnumerable<T> { IEnumerator<T> IEnumerable<T>.GetEnumerator() => default; IEnumerator IEnumerable.GetEnumerator() => default; } public class MyCollectionBuilder { {{methodDeclaration}} } """; var comp = CreateCompilation(sourceA, targetFramework: TargetFramework.Net80); var refA = AsReference(comp, useCompilationReference); string sourceB = """ #pragma warning disable 219 class Program { static void Main() { MyCollection<string> x = []; MyCollection<int> y = [1, 2, 3]; MyCollection<object> z = new(); } } """; comp = CreateCompilation(sourceB, references: new[] { refA }, targetFramework: TargetFramework.Net80); comp.VerifyEmitDiagnostics( // (6,34): error CS9187: Could not find an accessible 'Create' method with the expected signature: a static method whose last parameter is of type 'ReadOnlySpan<T>' and return type 'MyCollection<T>'. // MyCollection<string> x = []; Diagnostic(ErrorCode.ERR_CollectionBuilderAttributeMethodNotFound, "[]").WithArguments("Create", "T", "MyCollection<T>").WithLocation(6, 34), // (7,31): error CS9187: Could not find an accessible 'Create' method with the expected signature: a static method whose last parameter is of type 'ReadOnlySpan<T>' and return type 'MyCollection<T>'. // MyCollection<int> y = [1, 2, 3]; Diagnostic(ErrorCode.ERR_CollectionBuilderAttributeMethodNotFound, "[1, 2, 3]").WithArguments("Create", "T", "MyCollection<T>").WithLocation(7, 31)); } [CombinatorialData] [Theory] public void CollectionBuilder_UnexpectedSignature_MoreTypeParameters(bool useCompilationReference) { string sourceA = """ using System; using System.Collections; using System.Collections.Generic; using System.Runtime.CompilerServices; [CollectionBuilder(typeof(MyCollectionBuilder), "Create")] public struct MyCollection : IEnumerable<object> { IEnumerator<object> IEnumerable<object>.GetEnumerator() => default; IEnumerator IEnumerable.GetEnumerator() => default; } public class MyCollectionBuilder { public static MyCollection Create<T>(ReadOnlySpan<T> items) => default; } """; var comp = CreateCompilation(sourceA, targetFramework: TargetFramework.Net80); var refA = AsReference(comp, useCompilationReference); string sourceB = """ #pragma warning disable 219 class Program { static void Main() { MyCollection x = []; MyCollection y = [1, 2, 3]; MyCollection z = new(); } } """; comp = CreateCompilation(sourceB, references: new[] { refA }, targetFramework: TargetFramework.Net80); comp.VerifyEmitDiagnostics( // (6,26): error CS9187: Could not find an accessible 'Create' method with the expected signature: a static method whose last parameter is of type 'ReadOnlySpan<object>' and return type 'MyCollection'. // MyCollection x = []; Diagnostic(ErrorCode.ERR_CollectionBuilderAttributeMethodNotFound, "[]").WithArguments("Create", "object", "MyCollection").WithLocation(6, 26), // (7,26): error CS9187: Could not find an accessible 'Create' method with the expected signature: a static method whose last parameter is of type 'ReadOnlySpan<object>' and return type 'MyCollection'. // MyCollection y = [1, 2, 3]; Diagnostic(ErrorCode.ERR_CollectionBuilderAttributeMethodNotFound, "[1, 2, 3]").WithArguments("Create", "object", "MyCollection").WithLocation(7, 26)); } [CombinatorialData] [Theory] public void CollectionBuilder_UnexpectedSignature_FewerTypeParameters(bool useCompilationReference) { string sourceA = """ using System; using System.Collections; using System.Collections.Generic; using System.Runtime.CompilerServices; [CollectionBuilder(typeof(MyCollectionBuilder), "Create")] public struct MyCollection<T, U> : IEnumerable<T> { public IEnumerator<T> GetEnumerator() => default; IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); } public sealed class MyCollectionBuilder { public static MyCollection<T, int> Create<T>(ReadOnlySpan<T> items) => default; } """; var comp = CreateCompilation(sourceA, targetFramework: TargetFramework.Net80); var refA = AsReference(comp, useCompilationReference); string sourceB = """ #pragma warning disable 219 class Program { static void Main() { MyCollection<string, int> x = []; MyCollection<int, string> y = [1, 2, 3]; MyCollection<int, object> z = new(); } } """; comp = CreateCompilation(sourceB, references: new[] { refA }, targetFramework: TargetFramework.Net80); comp.VerifyEmitDiagnostics( // (6,39): error CS9187: Could not find an accessible 'Create' method with the expected signature: a static method whose last parameter is of type 'ReadOnlySpan<T>' and return type 'MyCollection<T, U>'. // MyCollection<string, int> x = []; Diagnostic(ErrorCode.ERR_CollectionBuilderAttributeMethodNotFound, "[]").WithArguments("Create", "T", "MyCollection<T, U>").WithLocation(6, 39), // (7,39): error CS9187: Could not find an accessible 'Create' method with the expected signature: a static method whose last parameter is of type 'ReadOnlySpan<T>' and return type 'MyCollection<T, U>'. // MyCollection<int, string> y = [1, 2, 3]; Diagnostic(ErrorCode.ERR_CollectionBuilderAttributeMethodNotFound, "[1, 2, 3]").WithArguments("Create", "T", "MyCollection<T, U>").WithLocation(7, 39)); } [CombinatorialData] [Theory] public void CollectionBuilder_InheritedAttributeOnBaseCollection(bool useCompilationReference) { string sourceAttribute = """ namespace System.Runtime.CompilerServices { [AttributeUsage(AttributeTargets.All, Inherited = true, AllowMultiple = false)] public sealed class CollectionBuilderAttribute : Attribute { public CollectionBuilderAttribute(Type builderType, string methodName) { } } } """; string sourceA = """ using System; using System.Collections; using System.Collections.Generic; using System.Runtime.CompilerServices; [CollectionBuilder(typeof(MyCollectionBuilder), "Create")] public abstract class MyCollectionBase : IEnumerable<int> { public IEnumerator<int> GetEnumerator() => null; IEnumerator IEnumerable.GetEnumerator() => null; } public sealed class MyCollection : MyCollectionBase { } public sealed class MyCollectionBuilder { public static MyCollectionBase Create(ReadOnlySpan<int> items) => new MyCollection(); } """; var comp = CreateCompilation(new[] { sourceA, sourceAttribute }, targetFramework: TargetFramework.Net80); var refA = AsReference(comp, useCompilationReference); string sourceB = """ class Program { static void Main() { MyCollection x = []; MyCollection y = [2]; MyCollection z = new(); } } """; comp = CreateCompilation(sourceB, references: new[] { refA }, targetFramework: TargetFramework.Net80); comp.VerifyEmitDiagnostics( // (6,26): error CS1061: 'MyCollection' does not contain a definition for 'Add' and no accessible extension method 'Add' accepting a first argument of type 'MyCollection' could be found (are you missing a using directive or an assembly reference?) // MyCollection y = [2]; Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "[2]").WithArguments("MyCollection", "Add").WithLocation(6, 26)); } [CombinatorialData] [Theory] public void CollectionBuilder_CreateMethodOnBase(bool useCompilationReference) { string sourceA = """ using System; using System.Collections; using System.Collections.Generic; using System.Runtime.CompilerServices; [CollectionBuilder(typeof(MyCollectionBuilder), nameof(MyCollectionBuilder.Create))] public sealed class MyCollection : IEnumerable<int> { public IEnumerator<int> GetEnumerator() => null; IEnumerator IEnumerable.GetEnumerator() => null; } public abstract class MyCollectionBuilderBase { public static MyCollection Create(ReadOnlySpan<int> items) => new MyCollection(); } public sealed class MyCollectionBuilder : MyCollectionBuilderBase { } """; var comp = CreateCompilation(sourceA, targetFramework: TargetFramework.Net80); var refA = AsReference(comp, useCompilationReference); string sourceB = """ class Program { static void Main() { MyCollection x = []; MyCollection y = [1, 2, 3]; MyCollection z = new(); } } """; comp = CreateCompilation(sourceB, references: new[] { refA }, targetFramework: TargetFramework.Net80); comp.VerifyEmitDiagnostics( // (5,26): error CS9187: Could not find an accessible 'Create' method with the expected signature: a static method whose last parameter is of type 'ReadOnlySpan<int>' and return type 'MyCollection'. // MyCollection x = []; Diagnostic(ErrorCode.ERR_CollectionBuilderAttributeMethodNotFound, "[]").WithArguments("Create", "int", "MyCollection").WithLocation(5, 26), // (6,26): error CS9187: Could not find an accessible 'Create' method with the expected signature: a static method whose last parameter is of type 'ReadOnlySpan<int>' and return type 'MyCollection'. // MyCollection y = [1, 2, 3]; Diagnostic(ErrorCode.ERR_CollectionBuilderAttributeMethodNotFound, "[1, 2, 3]").WithArguments("Create", "int", "MyCollection").WithLocation(6, 26)); } [Fact] public void CollectionBuilder_Conversion_ImplicitReference_NonGeneric() { string sourceA = """ using System; using System.Collections; using System.Collections.Generic; using System.Runtime.CompilerServices; [CollectionBuilder(typeof(MyCollectionBuilder), nameof(MyCollectionBuilder.Create))] abstract class MyCollectionBase : IEnumerable<int> { private readonly List<int> _list; protected MyCollectionBase(List<int> list) { _list = list; } public IEnumerator<int> GetEnumerator() => _list.GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); } sealed class MyCollection : MyCollectionBase { public MyCollection(List<int> list) : base(list) { } } class MyCollectionBuilder { public static MyCollection Create(ReadOnlySpan<int> items) { return new MyCollection(new List<int>(items.ToArray())); } } """; string sourceB1 = """ class Program { static void Main() { MyCollectionBase x = []; x.Report(); MyCollectionBase y = [1, 2, 3]; y.Report(); } } """; CompileAndVerify( new[] { sourceA, sourceB1, s_collectionExtensions }, targetFramework: TargetFramework.Net80, verify: Verification.FailsPEVerify, expectedOutput: IncludeExpectedOutput("[], [1, 2, 3], ")); string sourceB2 = """ class Program { static void Main() { MyCollection x = []; MyCollection y = [1, 2, 3]; } } """; var comp = CreateCompilation( new[] { sourceA, sourceB2 }, targetFramework: TargetFramework.Net80); comp.VerifyEmitDiagnostics( // 1.cs(5,26): error CS9214: Collection expression type must have an applicable constructor that can be called with no arguments. // MyCollection x = []; Diagnostic(ErrorCode.ERR_CollectionExpressionMissingConstructor, "[]").WithLocation(5, 26), // 1.cs(6,26): error CS9214: Collection expression type must have an applicable constructor that can be called with no arguments. // MyCollection y = [1, 2, 3]; Diagnostic(ErrorCode.ERR_CollectionExpressionMissingConstructor, "[1, 2, 3]").WithLocation(6, 26), // 1.cs(6,26): error CS1061: 'MyCollection' does not contain a definition for 'Add' and no accessible extension method 'Add' accepting a first argument of type 'MyCollection' could be found (are you missing a using directive or an assembly reference?) // MyCollection y = [1, 2, 3]; Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "[1, 2, 3]").WithArguments("MyCollection", "Add").WithLocation(6, 26)); } [Fact] public void CollectionBuilder_Conversion_ImplicitReference_Generic() { string sourceA = """ using System; using System.Collections; using System.Collections.Generic; using System.Runtime.CompilerServices; [CollectionBuilder(typeof(MyCollectionBuilder), nameof(MyCollectionBuilder.Create))] abstract class MyCollectionBase<T> : IEnumerable<T> { private readonly List<T> _list; protected MyCollectionBase(List<T> list) { _list = list; } public IEnumerator<T> GetEnumerator() => _list.GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); } sealed class MyCollection<T> : MyCollectionBase<T> { public MyCollection(List<T> list) : base(list) { } } class MyCollectionBuilder { public static MyCollection<T> Create<T>(ReadOnlySpan<T> items) { return new MyCollection<T>(new List<T>(items.ToArray())); } } """; string sourceB1 = """ class Program { static void Main() { MyCollectionBase<string> x = []; x.Report(); MyCollectionBase<object> y = [1, 2, null]; y.Report(); } } """; CompileAndVerify( new[] { sourceA, sourceB1, s_collectionExtensions }, targetFramework: TargetFramework.Net80, verify: Verification.Fails, expectedOutput: IncludeExpectedOutput("[], [1, 2, null], ")); string sourceB2 = """ class Program { static void Main() { MyCollection<string> x = []; MyCollection<object> y = [1, 2, null]; } } """; var comp = CreateCompilation( new[] { sourceA, sourceB2 }, targetFramework: TargetFramework.Net80); comp.VerifyEmitDiagnostics( // 1.cs(5,34): error CS9214: Collection expression type must have an applicable constructor that can be called with no arguments. // MyCollection<string> x = []; Diagnostic(ErrorCode.ERR_CollectionExpressionMissingConstructor, "[]").WithLocation(5, 34), // 1.cs(6,34): error CS9214: Collection expression type must have an applicable constructor that can be called with no arguments. // MyCollection<object> y = [1, 2, null]; Diagnostic(ErrorCode.ERR_CollectionExpressionMissingConstructor, "[1, 2, null]").WithLocation(6, 34), // 1.cs(6,34): error CS1061: 'MyCollection<object>' does not contain a definition for 'Add' and no accessible extension method 'Add' accepting a first argument of type 'MyCollection<object>' could be found (are you missing a using directive or an assembly reference?) // MyCollection<object> y = [1, 2, null]; Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "[1, 2, null]").WithArguments("MyCollection<object>", "Add").WithLocation(6, 34)); } [Fact] public void CollectionBuilder_Conversion_ExplicitReference_NonGeneric() { string source = """ using System; using System.Collections; using System.Collections.Generic; using System.Runtime.CompilerServices; abstract class MyCollectionBase : IEnumerable<int> { private readonly List<int> _list; protected MyCollectionBase(List<int> list) { _list = list; } public IEnumerator<int> GetEnumerator() => _list.GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); } [CollectionBuilder(typeof(MyCollectionBuilder), nameof(MyCollectionBuilder.Create))] sealed class MyCollection : MyCollectionBase { public MyCollection(List<int> list) : base(list) { } } class MyCollectionBuilder { public static MyCollectionBase Create(ReadOnlySpan<int> items) { return new MyCollection(new List<int>(items.ToArray())); } } class Program { static void Main() { MyCollection x = []; MyCollection y = [1, 2, 3]; } } """; var comp = CreateCompilation( source, targetFramework: TargetFramework.Net80); comp.VerifyEmitDiagnostics( // (28,26): error CS9187: Could not find an accessible 'Create' method with the expected signature: a static method whose last parameter is of type 'ReadOnlySpan<int>' and return type 'MyCollection'. // MyCollection x = []; Diagnostic(ErrorCode.ERR_CollectionBuilderAttributeMethodNotFound, "[]").WithArguments("Create", "int", "MyCollection").WithLocation(28, 26), // (29,26): error CS9187: Could not find an accessible 'Create' method with the expected signature: a static method whose last parameter is of type 'ReadOnlySpan<int>' and return type 'MyCollection'. // MyCollection y = [1, 2, 3]; Diagnostic(ErrorCode.ERR_CollectionBuilderAttributeMethodNotFound, "[1, 2, 3]").WithArguments("Create", "int", "MyCollection").WithLocation(29, 26)); } [Fact] public void CollectionBuilder_Conversion_ExplicitReference_Generic() { string source = """ using System; using System.Collections; using System.Collections.Generic; using System.Runtime.CompilerServices; abstract class MyCollectionBase<T> : IEnumerable<T> { private readonly List<T> _list; protected MyCollectionBase(List<T> list) { _list = list; } public IEnumerator<T> GetEnumerator() => _list.GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); } [CollectionBuilder(typeof(MyCollectionBuilder), nameof(MyCollectionBuilder.Create))] sealed class MyCollection<T> : MyCollectionBase<T> { public MyCollection(List<T> list) : base(list) { } } class MyCollectionBuilder { public static MyCollectionBase<T> Create<T>(ReadOnlySpan<T> items) { return new MyCollection<T>(new List<T>(items.ToArray())); } } class Program { static void Main() { MyCollection<string> x = []; MyCollection<object> y = [1, 2, null]; } } """; var comp = CreateCompilation( source, targetFramework: TargetFramework.Net80); comp.VerifyEmitDiagnostics( // (28,34): error CS9187: Could not find an accessible 'Create' method with the expected signature: a static method whose last parameter is of type 'ReadOnlySpan<T>' and return type 'MyCollection<T>'. // MyCollection<string> x = []; Diagnostic(ErrorCode.ERR_CollectionBuilderAttributeMethodNotFound, "[]").WithArguments("Create", "T", "MyCollection<T>").WithLocation(28, 34), // (29,34): error CS9187: Could not find an accessible 'Create' method with the expected signature: a static method whose last parameter is of type 'ReadOnlySpan<T>' and return type 'MyCollection<T>'. // MyCollection<object> y = [1, 2, null]; Diagnostic(ErrorCode.ERR_CollectionBuilderAttributeMethodNotFound, "[1, 2, null]").WithArguments("Create", "T", "MyCollection<T>").WithLocation(29, 34)); } [Fact] public void CollectionBuilder_Conversion_Boxing_NonGeneric() { string source = """ using System; using System.Collections; using System.Collections.Generic; using System.Runtime.CompilerServices; [CollectionBuilder(typeof(MyCollectionBuilder), nameof(MyCollectionBuilder.Create))] interface IMyCollection : IEnumerable<object> { } struct MyCollection : IMyCollection { private readonly List<object> _list; public MyCollection(List<object> list) { _list = list; } public IEnumerator<object> GetEnumerator() => _list.GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); } class MyCollectionBuilder { public static MyCollection Create(ReadOnlySpan<object> items) { return new MyCollection(new List<object>(items.ToArray())); } } class Program { static void Main() { IMyCollection x = []; x.Report(); IMyCollection y = [1, 2, null]; y.Report(); } } """; CompileAndVerify( new[] { source, s_collectionExtensions }, targetFramework: TargetFramework.Net80, verify: Verification.Fails, expectedOutput: IncludeExpectedOutput("[], [1, 2, null], ")); } [Fact] public void CollectionBuilder_Conversion_Boxing_Generic() { string source = """ using System; using System.Collections; using System.Collections.Generic; using System.Runtime.CompilerServices; [CollectionBuilder(typeof(MyCollectionBuilder), nameof(MyCollectionBuilder.Create))] interface IMyCollection<T> : IEnumerable<T> { } struct MyCollection<T> : IMyCollection<T> { private readonly List<T> _list; public MyCollection(List<T> list) { _list = list; } public IEnumerator<T> GetEnumerator() => _list.GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); } class MyCollectionBuilder { public static MyCollection<T> Create<T>(ReadOnlySpan<T> items) { return new MyCollection<T>(new List<T>(items.ToArray())); } } class Program { static void Main() { IMyCollection<string> x = []; x.Report(); IMyCollection<object> y = [1, 2, null]; y.Report(); } } """; CompileAndVerify( new[] { source, s_collectionExtensions }, targetFramework: TargetFramework.Net80, verify: Verification.Fails, expectedOutput: IncludeExpectedOutput("[], [1, 2, null], ")); } [Fact] public void CollectionBuilder_Conversion_Unboxing_NonGeneric() { string source = """ using System; using System.Collections; using System.Collections.Generic; using System.Runtime.CompilerServices; interface IMyCollection : IEnumerable<object> { } [CollectionBuilder(typeof(MyCollectionBuilder), nameof(MyCollectionBuilder.Create))] struct MyCollection : IMyCollection { private readonly List<object> _list; public MyCollection(List<object> list) { _list = list; } public IEnumerator<object> GetEnumerator() => _list.GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); } class MyCollectionBuilder { public static IMyCollection Create(ReadOnlySpan<object> items) { return new MyCollection(new List<object>(items.ToArray())); } } class Program { static void Main() { MyCollection x = []; MyCollection y = [1, 2, null]; } } """; var comp = CreateCompilation( source, targetFramework: TargetFramework.Net80); comp.VerifyEmitDiagnostics( // (27,26): error CS9187: Could not find an accessible 'Create' method with the expected signature: a static method whose last parameter is of type 'ReadOnlySpan<object>' and return type 'MyCollection'. // MyCollection x = []; Diagnostic(ErrorCode.ERR_CollectionBuilderAttributeMethodNotFound, "[]").WithArguments("Create", "object", "MyCollection").WithLocation(27, 26), // (28,26): error CS9187: Could not find an accessible 'Create' method with the expected signature: a static method whose last parameter is of type 'ReadOnlySpan<object>' and return type 'MyCollection'. // MyCollection y = [1, 2, null]; Diagnostic(ErrorCode.ERR_CollectionBuilderAttributeMethodNotFound, "[1, 2, null]").WithArguments("Create", "object", "MyCollection").WithLocation(28, 26)); } [Fact] public void CollectionBuilder_Conversion_Unboxing_Generic() { string source = """ using System; using System.Collections; using System.Collections.Generic; using System.Runtime.CompilerServices; interface IMyCollection<T> : IEnumerable<T> { } [CollectionBuilder(typeof(MyCollectionBuilder), nameof(MyCollectionBuilder.Create))] struct MyCollection<T> : IMyCollection<T> { private readonly List<T> _list; public MyCollection(List<T> list) { _list = list; } public IEnumerator<T> GetEnumerator() => _list.GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); } class MyCollectionBuilder { public static IMyCollection<T> Create<T>(ReadOnlySpan<T> items) { return new MyCollection<T>(new List<T>(items.ToArray())); } } class Program { static void Main() { MyCollection<string> x = []; MyCollection<object> y = [1, 2, null]; } } """; var comp = CreateCompilation( source, targetFramework: TargetFramework.Net80); comp.VerifyEmitDiagnostics( // (27,34): error CS9187: Could not find an accessible 'Create' method with the expected signature: a static method whose last parameter is of type 'ReadOnlySpan<T>' and return type 'MyCollection<T>'. // MyCollection<string> x = []; Diagnostic(ErrorCode.ERR_CollectionBuilderAttributeMethodNotFound, "[]").WithArguments("Create", "T", "MyCollection<T>").WithLocation(27, 34), // (28,34): error CS9187: Could not find an accessible 'Create' method with the expected signature: a static method whose last parameter is of type 'ReadOnlySpan<T>' and return type 'MyCollection<T>'. // MyCollection<object> y = [1, 2, null]; Diagnostic(ErrorCode.ERR_CollectionBuilderAttributeMethodNotFound, "[1, 2, null]").WithArguments("Create", "T", "MyCollection<T>").WithLocation(28, 34)); } [Fact] public void CollectionBuilder_Conversion_UserDefined_NonGeneric() { string source = """ using System; using System.Collections; using System.Collections.Generic; using System.Runtime.CompilerServices; [CollectionBuilder(typeof(MyCollectionBuilder), nameof(MyCollectionBuilder.Create))] class MyCollection : IEnumerable<object> { public MyCollection(List<object> list) { } public IEnumerator<object> GetEnumerator() => default; IEnumerator IEnumerable.GetEnumerator() => default; public static implicit operator MyCollection(OtherCollection other) => default; } class OtherCollection { public OtherCollection(List<object> list) { } } class MyCollectionBuilder { public static OtherCollection Create(ReadOnlySpan<object> items) { return new OtherCollection(new List<object>(items.ToArray())); } } class Program { static void Main() { MyCollection x = []; MyCollection y = [1, 2, null]; } } """; var comp = CreateCompilation( source, targetFramework: TargetFramework.Net80); comp.VerifyEmitDiagnostics( // (28,26): error CS9187: Could not find an accessible 'Create' method with the expected signature: a static method whose last parameter is of type 'ReadOnlySpan<object>' and return type 'MyCollection'. // MyCollection x = []; Diagnostic(ErrorCode.ERR_CollectionBuilderAttributeMethodNotFound, "[]").WithArguments("Create", "object", "MyCollection").WithLocation(28, 26), // (29,26): error CS9187: Could not find an accessible 'Create' method with the expected signature: a static method whose last parameter is of type 'ReadOnlySpan<object>' and return type 'MyCollection'. // MyCollection y = [1, 2, null]; Diagnostic(ErrorCode.ERR_CollectionBuilderAttributeMethodNotFound, "[1, 2, null]").WithArguments("Create", "object", "MyCollection").WithLocation(29, 26)); } [Fact] public void CollectionBuilder_Conversion_UserDefined_Generic() { string source = """ using System; using System.Collections; using System.Collections.Generic; using System.Runtime.CompilerServices; [CollectionBuilder(typeof(MyCollectionBuilder), nameof(MyCollectionBuilder.Create))] class MyCollection<T> : IEnumerable<T> { public MyCollection(List<T> list) { } public IEnumerator<T> GetEnumerator() => default; IEnumerator IEnumerable.GetEnumerator() => default; public static implicit operator MyCollection<T>(OtherCollection<T> other) => default; } class OtherCollection<T> { public OtherCollection(List<T> list) { } } class MyCollectionBuilder { public static OtherCollection<T> Create<T>(ReadOnlySpan<T> items) { return new OtherCollection<T>(new List<T>(items.ToArray())); } } class Program { static void Main() { MyCollection<string> x = []; MyCollection<object> y = [1, 2, null]; } } """; var comp = CreateCompilation( source, targetFramework: TargetFramework.Net80); comp.VerifyEmitDiagnostics( // (28,34): error CS9187: Could not find an accessible 'Create' method with the expected signature: a static method whose last parameter is of type 'ReadOnlySpan<T>' and return type 'MyCollection<T>'. // MyCollection<string> x = []; Diagnostic(ErrorCode.ERR_CollectionBuilderAttributeMethodNotFound, "[]").WithArguments("Create", "T", "MyCollection<T>").WithLocation(28, 34), // (29,34): error CS9187: Could not find an accessible 'Create' method with the expected signature: a static method whose last parameter is of type 'ReadOnlySpan<T>' and return type 'MyCollection<T>'. // MyCollection<object> y = [1, 2, null]; Diagnostic(ErrorCode.ERR_CollectionBuilderAttributeMethodNotFound, "[1, 2, null]").WithArguments("Create", "T", "MyCollection<T>").WithLocation(29, 34)); } [Fact] public void CollectionBuilder_Conversion_ExplicitNullable_NonGeneric() { string source = """ using System; using System.Collections; using System.Collections.Generic; using System.Runtime.CompilerServices; [CollectionBuilder(typeof(MyCollectionBuilder), nameof(MyCollectionBuilder.Create))] struct MyCollection { private readonly List<object> _list; public MyCollection(List<object> list) { _list = list; } public IEnumerator<object> GetEnumerator() => _list.GetEnumerator(); } class MyCollectionBuilder { public static MyCollection? Create(ReadOnlySpan<object> items) { return new MyCollection(new List<object>(items.ToArray())); } } class Program { static void Main() { MyCollection x = []; MyCollection y = [1, 2, null]; } } """; var comp = CreateCompilation( source, targetFramework: TargetFramework.Net80); comp.VerifyEmitDiagnostics( // (23,26): error CS9187: Could not find an accessible 'Create' method with the expected signature: a static method whose last parameter is of type 'ReadOnlySpan<object>' and return type 'MyCollection'. // MyCollection x = []; Diagnostic(ErrorCode.ERR_CollectionBuilderAttributeMethodNotFound, "[]").WithArguments("Create", "object", "MyCollection").WithLocation(23, 26), // (24,26): error CS9187: Could not find an accessible 'Create' method with the expected signature: a static method whose last parameter is of type 'ReadOnlySpan<object>' and return type 'MyCollection'. // MyCollection y = [1, 2, null]; Diagnostic(ErrorCode.ERR_CollectionBuilderAttributeMethodNotFound, "[1, 2, null]").WithArguments("Create", "object", "MyCollection").WithLocation(24, 26)); } [Fact] public void CollectionBuilder_Conversion_ExplicitNullable_Generic() { string source = """ using System; using System.Collections; using System.Collections.Generic; using System.Runtime.CompilerServices; [CollectionBuilder(typeof(MyCollectionBuilder), nameof(MyCollectionBuilder.Create))] struct MyCollection<T> { private readonly List<T> _list; public MyCollection(List<T> list) { _list = list; } public IEnumerator<T> GetEnumerator() => _list.GetEnumerator(); } class MyCollectionBuilder { public static MyCollection<T>? Create<T>(ReadOnlySpan<T> items) { return new MyCollection<T>(new List<T>(items.ToArray())); } } class Program { static void Main() { MyCollection<string> x = []; MyCollection<object> y = [1, 2, null]; } } """; var comp = CreateCompilation( source, targetFramework: TargetFramework.Net80); comp.VerifyEmitDiagnostics( // (23,34): error CS9187: Could not find an accessible 'Create' method with the expected signature: a static method whose last parameter is of type 'ReadOnlySpan<T>' and return type 'MyCollection<T>'. // MyCollection<string> x = []; Diagnostic(ErrorCode.ERR_CollectionBuilderAttributeMethodNotFound, "[]").WithArguments("Create", "T", "MyCollection<T>").WithLocation(23, 34), // (24,34): error CS9187: Could not find an accessible 'Create' method with the expected signature: a static method whose last parameter is of type 'ReadOnlySpan<T>' and return type 'MyCollection<T>'. // MyCollection<object> y = [1, 2, null]; Diagnostic(ErrorCode.ERR_CollectionBuilderAttributeMethodNotFound, "[1, 2, null]").WithArguments("Create", "T", "MyCollection<T>").WithLocation(24, 34)); } [Fact] public void CollectionBuilder_Conversion_Dynamic_NonGeneric() { string source = """ using System; using System.Collections; using System.Collections.Generic; using System.Runtime.CompilerServices; [CollectionBuilder(typeof(MyCollectionBuilder), nameof(MyCollectionBuilder.Create))] class MyCollection : IEnumerable<object> { public MyCollection(List<object> list) { } public IEnumerator<object> GetEnumerator() => default; IEnumerator IEnumerable.GetEnumerator() => default; } class MyCollectionBuilder { public static dynamic Create(ReadOnlySpan<object> items) { return new MyCollection(new List<object>(items.ToArray())); } } class Program { static void Main() { MyCollection x = []; MyCollection y = [1, 2, null]; } } """; var comp = CreateCompilation( source, targetFramework: TargetFramework.Net80); comp.VerifyEmitDiagnostics( // (23,26): error CS9187: Could not find an accessible 'Create' method with the expected signature: a static method whose last parameter is of type 'ReadOnlySpan<object>' and return type 'MyCollection'. // MyCollection x = []; Diagnostic(ErrorCode.ERR_CollectionBuilderAttributeMethodNotFound, "[]").WithArguments("Create", "object", "MyCollection").WithLocation(23, 26), // (24,26): error CS9187: Could not find an accessible 'Create' method with the expected signature: a static method whose last parameter is of type 'ReadOnlySpan<object>' and return type 'MyCollection'. // MyCollection y = [1, 2, null]; Diagnostic(ErrorCode.ERR_CollectionBuilderAttributeMethodNotFound, "[1, 2, null]").WithArguments("Create", "object", "MyCollection").WithLocation(24, 26)); } [Fact] public void CollectionBuilder_Conversion_Dynamic_Generic() { string source = """ using System; using System.Collections; using System.Collections.Generic; using System.Runtime.CompilerServices; [CollectionBuilder(typeof(MyCollectionBuilder), nameof(MyCollectionBuilder.Create))] class MyCollection<T> : IEnumerable<T> { public MyCollection(List<T> list) { } public IEnumerator<T> GetEnumerator() => default; IEnumerator IEnumerable.GetEnumerator() => default; } class MyCollectionBuilder { public static dynamic Create<T>(ReadOnlySpan<T> items) { return new MyCollection<T>(new List<T>(items.ToArray())); } } class Program { static void Main() { MyCollection<string> x = []; MyCollection<object> y = [1, 2, null]; } } """; var comp = CreateCompilation( source, targetFramework: TargetFramework.Net80); comp.VerifyEmitDiagnostics( // (23,34): error CS9187: Could not find an accessible 'Create' method with the expected signature: a static method whose last parameter is of type 'ReadOnlySpan<T>' and return type 'MyCollection<T>'. // MyCollection<string> x = []; Diagnostic(ErrorCode.ERR_CollectionBuilderAttributeMethodNotFound, "[]").WithArguments("Create", "T", "MyCollection<T>").WithLocation(23, 34), // (24,34): error CS9187: Could not find an accessible 'Create' method with the expected signature: a static method whose last parameter is of type 'ReadOnlySpan<T>' and return type 'MyCollection<T>'. // MyCollection<object> y = [1, 2, null]; Diagnostic(ErrorCode.ERR_CollectionBuilderAttributeMethodNotFound, "[1, 2, null]").WithArguments("Create", "T", "MyCollection<T>").WithLocation(24, 34)); } [CombinatorialData] [Theory] public void CollectionBuilder_ObsoleteBuilderType_01(bool useCompilationReference) { string sourceA = """ using System; using System.Collections; using System.Collections.Generic; using System.Runtime.CompilerServices; [CollectionBuilder(typeof(MyCollectionBuilder), "Create")] public struct MyCollection<T> : IEnumerable<T> { IEnumerator<T> IEnumerable<T>.GetEnumerator() => default; IEnumerator IEnumerable.GetEnumerator() => default; } [Obsolete] public class MyCollectionBuilder { public static MyCollection<T> Create<T>(ReadOnlySpan<T> items) => default; } """; var comp = CreateCompilation(sourceA, targetFramework: TargetFramework.Net80); var refA = AsReference(comp, useCompilationReference); string sourceB = """ #pragma warning disable 219 class Program { static void Main() { MyCollection<string> x = []; MyCollection<int> y = [1, 2, 3]; MyCollection<object> z = new(); } } """; comp = CreateCompilation(sourceB, references: new[] { refA }, targetFramework: TargetFramework.Net80); comp.VerifyEmitDiagnostics( // (6,34): warning CS0612: 'MyCollectionBuilder' is obsolete // MyCollection<string> x = []; Diagnostic(ErrorCode.WRN_DeprecatedSymbol, "[]").WithArguments("MyCollectionBuilder").WithLocation(6, 34), // (7,31): warning CS0612: 'MyCollectionBuilder' is obsolete // MyCollection<int> y = [1, 2, 3]; Diagnostic(ErrorCode.WRN_DeprecatedSymbol, "[1, 2, 3]").WithArguments("MyCollectionBuilder").WithLocation(7, 31)); } [Fact] public void CollectionBuilder_ObsoleteBuilderType_02() { string sourceA = """ using System; using System.Collections; using System.Collections.Generic; using System.Runtime.CompilerServices; [CollectionBuilder(typeof(MyCollectionBuilder), "Create")] public struct MyCollection<T> : IEnumerable<T> { IEnumerator<T> IEnumerable<T>.GetEnumerator() => default; IEnumerator IEnumerable.GetEnumerator() => default; } [Obsolete("message 2", error: true)] public class MyCollectionBuilder { public static MyCollection<T> Create<T>(ReadOnlySpan<T> items) => default; } """; string sourceB = """ #pragma warning disable 219 class Program { static void Main() { MyCollection<string> x = []; MyCollection<int> y = [1, 2, 3]; MyCollection<object> z = new(); } } """; var comp = CreateCompilation(new[] { sourceA, sourceB }, targetFramework: TargetFramework.Net80); comp.VerifyEmitDiagnostics( // 0.cs(5,27): error CS0619: 'MyCollectionBuilder' is obsolete: 'message 2' // [CollectionBuilder(typeof(MyCollectionBuilder), "Create")] Diagnostic(ErrorCode.ERR_DeprecatedSymbolStr, "MyCollectionBuilder").WithArguments("MyCollectionBuilder", "message 2").WithLocation(5, 27), // 1.cs(6,34): error CS0619: 'MyCollectionBuilder' is obsolete: 'message 2' // MyCollection<string> x = []; Diagnostic(ErrorCode.ERR_DeprecatedSymbolStr, "[]").WithArguments("MyCollectionBuilder", "message 2").WithLocation(6, 34), // 1.cs(7,31): error CS0619: 'MyCollectionBuilder' is obsolete: 'message 2' // MyCollection<int> y = [1, 2, 3]; Diagnostic(ErrorCode.ERR_DeprecatedSymbolStr, "[1, 2, 3]").WithArguments("MyCollectionBuilder", "message 2").WithLocation(7, 31)); } [CombinatorialData] [Theory] public void CollectionBuilder_ObsoleteBuilderMethod_01(bool useCompilationReference) { string sourceA = """ using System; using System.Collections; using System.Collections.Generic; using System.Runtime.CompilerServices; [CollectionBuilder(typeof(MyCollectionBuilder), "Create")] public struct MyCollection<T> : IEnumerable<T> { IEnumerator<T> IEnumerable<T>.GetEnumerator() => default; IEnumerator IEnumerable.GetEnumerator() => default; } public class MyCollectionBuilder { [Obsolete] public static MyCollection<T> Create<T>(ReadOnlySpan<T> items) => default; } """; var comp = CreateCompilation(sourceA, targetFramework: TargetFramework.Net80); var refA = AsReference(comp, useCompilationReference); string sourceB = """ #pragma warning disable 219 class Program { static void Main() { MyCollection<string> x = []; MyCollection<int> y = [1, 2, 3]; MyCollection<object> z = new(); } } """; comp = CreateCompilation(sourceB, references: new[] { refA }, targetFramework: TargetFramework.Net80); comp.VerifyEmitDiagnostics( // (6,34): warning CS0612: 'MyCollectionBuilder.Create<T>(ReadOnlySpan<T>)' is obsolete // MyCollection<string> x = []; Diagnostic(ErrorCode.WRN_DeprecatedSymbol, "[]").WithArguments("MyCollectionBuilder.Create<T>(System.ReadOnlySpan<T>)").WithLocation(6, 34), // (7,31): warning CS0612: 'MyCollectionBuilder.Create<T>(ReadOnlySpan<T>)' is obsolete // MyCollection<int> y = [1, 2, 3]; Diagnostic(ErrorCode.WRN_DeprecatedSymbol, "[1, 2, 3]").WithArguments("MyCollectionBuilder.Create<T>(System.ReadOnlySpan<T>)").WithLocation(7, 31)); } [CombinatorialData] [Theory] public void CollectionBuilder_ObsoleteBuilderMethod_02(bool useCompilationReference) { string sourceA = """ using System; using System.Collections; using System.Collections.Generic; using System.Runtime.CompilerServices; [CollectionBuilder(typeof(MyCollectionBuilder), "Create")] public struct MyCollection<T> : IEnumerable<T> { IEnumerator<T> IEnumerable<T>.GetEnumerator() => default; IEnumerator IEnumerable.GetEnumerator() => default; } public class MyCollectionBuilder { [Obsolete("message 4", error: true)] public static MyCollection<T> Create<T>(ReadOnlySpan<T> items) => default; } """; var comp = CreateCompilation(sourceA, targetFramework: TargetFramework.Net80); var refA = AsReference(comp, useCompilationReference); string sourceB = """ #pragma warning disable 219 class Program { static void Main() { MyCollection<string> x = []; MyCollection<int> y = [1, 2, 3]; MyCollection<object> z = new(); } } """; comp = CreateCompilation(sourceB, references: new[] { refA }, targetFramework: TargetFramework.Net80); comp.VerifyEmitDiagnostics( // (6,34): error CS0619: 'MyCollectionBuilder.Create<T>(ReadOnlySpan<T>)' is obsolete: 'message 4' // MyCollection<string> x = []; Diagnostic(ErrorCode.ERR_DeprecatedSymbolStr, "[]").WithArguments("MyCollectionBuilder.Create<T>(System.ReadOnlySpan<T>)", "message 4").WithLocation(6, 34), // (7,31): error CS0619: 'MyCollectionBuilder.Create<T>(ReadOnlySpan<T>)' is obsolete: 'message 4' // MyCollection<int> y = [1, 2, 3]; Diagnostic(ErrorCode.ERR_DeprecatedSymbolStr, "[1, 2, 3]").WithArguments("MyCollectionBuilder.Create<T>(System.ReadOnlySpan<T>)", "message 4").WithLocation(7, 31)); } [Fact] public void CollectionBuilder_UnmanagedCallersOnly() { string sourceA = """ using System; using System.Collections; using System.Collections.Generic; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; [CollectionBuilder(typeof(MyCollectionBuilder), "Create")] public struct MyCollection<T> : IEnumerable<T> { IEnumerator<T> IEnumerable<T>.GetEnumerator() => default; IEnumerator IEnumerable.GetEnumerator() => default; } public class MyCollectionBuilder { [UnmanagedCallersOnly] public static MyCollection<T> Create<T>(ReadOnlySpan<T> items) => default; } """; string sourceB = """ #pragma warning disable 219 class Program { static void Main() { MyCollection<string> x = []; MyCollection<int> y = [1, 2, 3]; MyCollection<object> z = new(); } } """; var comp = CreateCompilation(new[] { sourceA, sourceB }, targetFramework: TargetFramework.Net80); comp.VerifyEmitDiagnostics( // 1.cs(6,34): error CS8901: 'MyCollectionBuilder.Create<string>(ReadOnlySpan<string>)' is attributed with 'UnmanagedCallersOnly' and cannot be called directly. Obtain a function pointer to this method. // MyCollection<string> x = []; Diagnostic(ErrorCode.ERR_UnmanagedCallersOnlyMethodsCannotBeCalledDirectly, "[]").WithArguments("MyCollectionBuilder.Create<string>(System.ReadOnlySpan<string>)").WithLocation(6, 34), // 1.cs(7,31): error CS8901: 'MyCollectionBuilder.Create<int>(ReadOnlySpan<int>)' is attributed with 'UnmanagedCallersOnly' and cannot be called directly. Obtain a function pointer to this method. // MyCollection<int> y = [1, 2, 3]; Diagnostic(ErrorCode.ERR_UnmanagedCallersOnlyMethodsCannotBeCalledDirectly, "[1, 2, 3]").WithArguments("MyCollectionBuilder.Create<int>(System.ReadOnlySpan<int>)").WithLocation(7, 31), // 0.cs(14,6): error CS8895: Methods attributed with 'UnmanagedCallersOnly' cannot have generic type parameters and cannot be declared in a generic type. // [UnmanagedCallersOnly] Diagnostic(ErrorCode.ERR_UnmanagedCallersOnlyMethodOrTypeCannotBeGeneric, "UnmanagedCallersOnly").WithLocation(14, 6), // 0.cs(15,45): error CS8894: Cannot use 'ReadOnlySpan<T>' as a parameter type on a method attributed with 'UnmanagedCallersOnly'. // public static MyCollection<T> Create<T>(ReadOnlySpan<T> items) => default; Diagnostic(ErrorCode.ERR_CannotUseManagedTypeInUnmanagedCallersOnly, "ReadOnlySpan<T> items").WithArguments("System.ReadOnlySpan<T>", "parameter").WithLocation(15, 45)); } [CombinatorialData] [Theory] public void CollectionBuilder_Constraints_CollectionAndBuilder(bool useCompilationReference) { string sourceA = """ using System; using System.Collections; using System.Collections.Generic; using System.Runtime.CompilerServices; [CollectionBuilder(typeof(MyCollectionBuilder), nameof(MyCollectionBuilder.Create))] public struct MyCollection<T> : IEnumerable<T> where T : new() { private List<T> _list; public MyCollection(List<T> list) { _list = list; } public IEnumerator<T> GetEnumerator() => _list.GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); } public class MyCollectionBuilder { public static MyCollection<T> Create<T>(ReadOnlySpan<T> items) where T : struct => new MyCollection<T>(new List<T>(items.ToArray())); } """; var comp = CreateCompilation(sourceA, targetFramework: TargetFramework.Net80); var refA = AsReference(comp, useCompilationReference); string sourceB1 = """ class Program { static void Main() { MyCollection<int> x = []; x.Report(); MyCollection<int> y = [1, 2, 3]; y.Report(); } } """; CompileAndVerify( new[] { sourceB1, s_collectionExtensions }, references: new[] { refA }, targetFramework: TargetFramework.Net80, verify: Verification.FailsPEVerify, expectedOutput: IncludeExpectedOutput("[], [1, 2, 3], ")); string sourceB2 = """ #pragma warning disable 219 class Program { static void Main() { MyCollection<int?> x = [4, null]; MyCollection<int?> y = new(); } } """; comp = CreateCompilation(sourceB2, references: new[] { refA }, targetFramework: TargetFramework.Net80); comp.VerifyEmitDiagnostics( // (6,32): error CS0453: The type 'int?' must be a non-nullable value type in order to use it as parameter 'T' in the generic type or method 'MyCollectionBuilder.Create<T>(ReadOnlySpan<T>)' // MyCollection<int?> x = [4, null]; Diagnostic(ErrorCode.ERR_ValConstraintNotSatisfied, "[4, null]").WithArguments("MyCollectionBuilder.Create<T>(System.ReadOnlySpan<T>)", "T", "int?").WithLocation(6, 32)); } [CombinatorialData] [Theory] public void CollectionBuilder_Constraints_BuilderOnly(bool useCompilationReference) { string sourceA = """ using System; using System.Collections; using System.Collections.Generic; using System.Runtime.CompilerServices; [CollectionBuilder(typeof(MyCollectionBuilder), nameof(MyCollectionBuilder.Create))] public struct MyCollection<T> : IEnumerable<T> { private List<T> _list; public MyCollection(List<T> list) { _list = list; } public IEnumerator<T> GetEnumerator() => _list.GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); } public class MyCollectionBuilder { public static MyCollection<T> Create<T>(ReadOnlySpan<T> items) where T : struct => new MyCollection<T>(new List<T>(items.ToArray())); } """; var comp = CreateCompilation(sourceA, targetFramework: TargetFramework.Net80); var refA = AsReference(comp, useCompilationReference); string sourceB1 = """ class Program { static void Main() { MyCollection<int> x = []; x.Report(); MyCollection<int> y = [1, 2, 3]; y.Report(); } } """; CompileAndVerify( new[] { sourceB1, s_collectionExtensions }, references: new[] { refA }, targetFramework: TargetFramework.Net80, verify: Verification.FailsPEVerify, expectedOutput: IncludeExpectedOutput("[], [1, 2, 3], ")); string sourceB2 = """ #pragma warning disable 219 class Program { static void Main() { MyCollection<int?> x = [4, null]; MyCollection<int?> y = new(); } } """; comp = CreateCompilation(sourceB2, references: new[] { refA }, targetFramework: TargetFramework.Net80); comp.VerifyEmitDiagnostics( // (6,32): error CS0453: The type 'int?' must be a non-nullable value type in order to use it as parameter 'T' in the generic type or method 'MyCollectionBuilder.Create<T>(ReadOnlySpan<T>)' // MyCollection<int?> x = [4, null]; Diagnostic(ErrorCode.ERR_ValConstraintNotSatisfied, "[4, null]").WithArguments("MyCollectionBuilder.Create<T>(System.ReadOnlySpan<T>)", "T", "int?").WithLocation(6, 32)); } [Fact] public void CollectionBuilder_Constraints_CollectionOnly() { string sourceA = """ using System; using System.Collections; using System.Collections.Generic; using System.Runtime.CompilerServices; [CollectionBuilder(typeof(MyCollectionBuilder), nameof(MyCollectionBuilder.Create))] public struct MyCollection<T> : IEnumerable<T> where T : class { private List<T> _list; public MyCollection(List<T> list) { _list = list; } public IEnumerator<T> GetEnumerator() => _list.GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); } public class MyCollectionBuilder { public static MyCollection<T> Create<T>(ReadOnlySpan<T> items) => default; } """; string sourceB = """ #pragma warning disable 219 class Program { static void Main() { MyCollection<string> x = []; MyCollection<int> y = [1, 2, 3]; MyCollection<string> z = new(); } } """; var comp = CreateCompilation(new[] { sourceA, sourceB }, targetFramework: TargetFramework.Net80); comp.VerifyEmitDiagnostics( // 1.cs(7,22): error CS0452: The type 'int' must be a reference type in order to use it as parameter 'T' in the generic type or method 'MyCollection<T>' // MyCollection<int> y = [1, 2, 3]; Diagnostic(ErrorCode.ERR_RefConstraintNotSatisfied, "int").WithArguments("MyCollection<T>", "T", "int").WithLocation(7, 22), // 0.cs(15,35): error CS0452: The type 'T' must be a reference type in order to use it as parameter 'T' in the generic type or method 'MyCollection<T>' // public static MyCollection<T> Create<T>(ReadOnlySpan<T> items) Diagnostic(ErrorCode.ERR_RefConstraintNotSatisfied, "Create").WithArguments("MyCollection<T>", "T", "T").WithLocation(15, 35)); } [CombinatorialData] [Theory] public void CollectionBuilder_Substituted_01(bool useCompilationReference) { string sourceA = """ using System; using System.Collections; using System.Collections.Generic; using System.Runtime.CompilerServices; [CollectionBuilder(typeof(MyCollectionBuilder), "Create")] public struct MyCollection<T> : IEnumerable<T> { public IEnumerator<T> GetEnumerator() => default; IEnumerator IEnumerable.GetEnumerator() => default; } public class MyCollectionBuilder { public static MyCollection<T> Create<T>(ReadOnlySpan<T> items) => default; } """; var comp = CreateCompilation(sourceA, targetFramework: TargetFramework.Net80); var refA = AsReference(comp, useCompilationReference); string sourceB = """ #pragma warning disable 219 class Program { static void Main() { F([]); F([1, 2, 3]); } static void F(MyCollection<int> c) { } } """; comp = CreateCompilation(sourceB, references: new[] { refA }, targetFramework: TargetFramework.Net80); comp.VerifyEmitDiagnostics(); var collectionType = (NamedTypeSymbol)comp.GetMember<MethodSymbol>("Program.F").Parameters[0].Type; Assert.Equal("MyCollection<System.Int32>", collectionType.ToTestDisplayString()); TypeSymbol builderType; string methodName; Assert.True(collectionType.HasCollectionBuilderAttribute(out builderType, out methodName)); Assert.Equal("MyCollectionBuilder", builderType.ToTestDisplayString()); Assert.Equal("Create", methodName); var originalType = collectionType.OriginalDefinition; Assert.Equal("MyCollection<T>", originalType.ToTestDisplayString()); Assert.True(originalType.HasCollectionBuilderAttribute(out builderType, out methodName)); Assert.Equal("MyCollectionBuilder", builderType.ToTestDisplayString()); Assert.Equal("Create", methodName); } [Fact] public void CollectionBuilder_Substituted_02() { string sourceA = """ using System; using System.Collections; using System.Collections.Generic; using System.Runtime.CompilerServices; [CollectionBuilder(typeof(Container<string>.MyCollectionBuilder), "Create")] public struct MyCollection<T> : IEnumerable<T> { public IEnumerator<T> GetEnumerator() => default; IEnumerator IEnumerable.GetEnumerator() => default; } public class Container<T> { public class MyCollectionBuilder { public static MyCollection<U> Create<U>(ReadOnlySpan<U> items) => default; } } """; string sourceB = """ #pragma warning disable 219 class Program { static void Main() { F([]); F(new()); } static void F(MyCollection<int> c) { } } """; var comp = CreateCompilation(new[] { sourceA, sourceB }, targetFramework: TargetFramework.Net80); comp.VerifyEmitDiagnostics( // 0.cs(5,2): error CS9185: The CollectionBuilderAttribute builder type must be a non-generic class or struct. // [CollectionBuilder(typeof(Container<string>.MyCollectionBuilder), "Create")] Diagnostic(ErrorCode.ERR_CollectionBuilderAttributeInvalidType, "CollectionBuilder").WithLocation(5, 2), // 1.cs(6,11): error CS9187: Could not find an accessible 'Create' method with the expected signature: a static method whose last parameter is of type 'ReadOnlySpan<T>' and return type 'MyCollection<T>'. // F([]); Diagnostic(ErrorCode.ERR_CollectionBuilderAttributeMethodNotFound, "[]").WithArguments("Create", "T", "MyCollection<T>").WithLocation(6, 11)); var collectionType = (NamedTypeSymbol)comp.GetMember<MethodSymbol>("Program.F").Parameters[0].Type; Assert.Equal("MyCollection<System.Int32>", collectionType.ToTestDisplayString()); TypeSymbol builderType; string methodName; Assert.True(collectionType.HasCollectionBuilderAttribute(out builderType, out methodName)); Assert.Equal("Container<System.String>.MyCollectionBuilder", builderType.ToTestDisplayString()); Assert.Equal("Create", methodName); var originalType = collectionType.OriginalDefinition; Assert.Equal("MyCollection<T>", originalType.ToTestDisplayString()); Assert.True(originalType.HasCollectionBuilderAttribute(out builderType, out methodName)); Assert.Equal("Container<System.String>.MyCollectionBuilder", builderType.ToTestDisplayString()); Assert.Equal("Create", methodName); } [Fact] public void CollectionBuilder_Substituted_03() { string source = """ using System; using System.Collections; using System.Collections.Generic; using System.Runtime.CompilerServices; public class Container<T> { [CollectionBuilder(typeof(MyCollectionBuilder), "Create")] public struct MyCollection : IEnumerable<T> { public IEnumerator<T> GetEnumerator() => default; IEnumerator IEnumerable.GetEnumerator() => default; } public class MyCollectionBuilder { public static MyCollection Create(ReadOnlySpan<T> items) => default; } } #pragma warning disable 219 class Program { static void Main() { F([]); F([null]); F(new()); } static void F(Container<string>.MyCollection c) { } } """; var comp = CreateCompilation(source, targetFramework: TargetFramework.Net80); comp.VerifyEmitDiagnostics( // (7,24): error CS0416: 'Container<T>.MyCollectionBuilder': an attribute argument cannot use type parameters // [CollectionBuilder(typeof(MyCollectionBuilder), "Create")] Diagnostic(ErrorCode.ERR_AttrArgWithTypeVars, "typeof(MyCollectionBuilder)").WithArguments("Container<T>.MyCollectionBuilder").WithLocation(7, 24), // (24,11): error CS1061: 'Container<string>.MyCollection' does not contain a definition for 'Add' and no accessible extension method 'Add' accepting a first argument of type 'Container<string>.MyCollection' could be found (are you missing a using directive or an assembly reference?) // F([null]); Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "[null]").WithArguments("Container<string>.MyCollection", "Add").WithLocation(24, 11)); var collectionType = (NamedTypeSymbol)comp.GetMember<MethodSymbol>("Program.F").Parameters[0].Type; Assert.Equal("Container<System.String>.MyCollection", collectionType.ToTestDisplayString()); Assert.False(collectionType.HasCollectionBuilderAttribute(out _, out _)); var originalType = collectionType.OriginalDefinition; Assert.Equal("Container<T>.MyCollection", originalType.ToTestDisplayString()); Assert.False(originalType.HasCollectionBuilderAttribute(out _, out _)); } [Fact] public void CollectionBuilder_Retargeting() { string sourceA = """ using System.Collections; using System.Collections.Generic; using System.Runtime.CompilerServices; [CollectionBuilder(typeof(MyCollectionBuilder), "Create")] public struct MyCollection<T> : IEnumerable<T> { public IEnumerator<T> GetEnumerator() => default; IEnumerator IEnumerable.GetEnumerator() => default; } public class MyCollectionBuilder { public static void Create(int[] items) { } } """; var comp = CreateCompilation(new[] { sourceA, CollectionBuilderAttributeDefinition }, targetFramework: TargetFramework.Mscorlib40); comp.VerifyEmitDiagnostics(); var refA = comp.ToMetadataReference(); string sourceB = """ #pragma warning disable 219 class Program { static void Main() { F([]); F(new()); } static void F(MyCollection<int> c) { } } """; comp = CreateCompilation(sourceB, references: new[] { refA }, targetFramework: TargetFramework.Mscorlib461); comp.VerifyEmitDiagnostics( // (6,11): error CS9187: Could not find an accessible 'Create' method with the expected signature: a static method whose last parameter is of type 'ReadOnlySpan<T>' and return type 'MyCollection<T>'. // F([]); Diagnostic(ErrorCode.ERR_CollectionBuilderAttributeMethodNotFound, "[]").WithArguments("Create", "T", "MyCollection<T>").WithLocation(6, 11)); var collectionType = (NamedTypeSymbol)comp.GetMember<MethodSymbol>("Program.F").Parameters[0].Type; Assert.Equal("MyCollection<System.Int32>", collectionType.ToTestDisplayString()); TypeSymbol builderType; string methodName; Assert.True(collectionType.HasCollectionBuilderAttribute(out builderType, out methodName)); Assert.Equal("MyCollectionBuilder", builderType.ToTestDisplayString()); Assert.Equal("Create", methodName); var retargetingType = (RetargetingNamedTypeSymbol)collectionType.OriginalDefinition; Assert.Equal("MyCollection<T>", retargetingType.ToTestDisplayString()); Assert.True(retargetingType.HasCollectionBuilderAttribute(out builderType, out methodName)); Assert.IsType<RetargetingNamedTypeSymbol>(builderType); Assert.Equal("MyCollectionBuilder", builderType.ToTestDisplayString()); Assert.Equal("Create", methodName); } [Fact] public void CollectionBuilder_ExtensionMethodGetEnumerator_01() { string source = """ using System; using System.Collections.Generic; using System.Runtime.CompilerServices; [CollectionBuilder(typeof(MyCollectionBuilder), nameof(MyCollectionBuilder.Create))] class MyCollection<T> { } class MyCollectionBuilder { public static MyCollection<T> Create<T>(ReadOnlySpan<T> items) => default; } namespace N { static class Extensions { public static IEnumerator<T> GetEnumerator<T>(this MyCollection<T> c) => default; static MyCollection<T> F<T>() => []; } } class Program { static void Main() { MyCollection<int> c = []; } } """; var comp = CreateCompilation(source, targetFramework: TargetFramework.Net80); comp.VerifyEmitDiagnostics( // (17,42): error CS9188: 'MyCollection<T>' has a CollectionBuilderAttribute but no element type. // static MyCollection<T> F<T>() => []; Diagnostic(ErrorCode.ERR_CollectionBuilderNoElementType, "[]").WithArguments("MyCollection<T>").WithLocation(17, 42), // (24,31): error CS9188: 'MyCollection<int>' has a CollectionBuilderAttribute but no element type. // MyCollection<int> c = []; Diagnostic(ErrorCode.ERR_CollectionBuilderNoElementType, "[]").WithArguments("MyCollection<int>").WithLocation(24, 31)); } [CombinatorialData] [Theory] public void CollectionBuilder_ExtensionMethodGetEnumerator_02(bool useCompilationReference) { string sourceA = """ using System; using System.Collections.Generic; using System.Runtime.CompilerServices; [CollectionBuilder(typeof(MyCollectionBuilder), nameof(MyCollectionBuilder.Create))] public class MyCollection<T> { } public class MyCollectionBuilder { public static MyCollection<T> Create<T>(ReadOnlySpan<T> items) => default; } static class Extensions { public static IEnumerator<T> GetEnumerator<T>(this MyCollection<T> c) => default; } """; var comp = CreateCompilation(sourceA, targetFramework: TargetFramework.Net80); comp.VerifyEmitDiagnostics(); var refA = AsReference(comp, useCompilationReference); string sourceB = """ #pragma warning disable 219 class Program { static void Main() { MyCollection<int> c = []; } } """; comp = CreateCompilation(sourceB, references: new[] { refA }, targetFramework: TargetFramework.Net80); comp.VerifyEmitDiagnostics( // (6,31): error CS9188: 'MyCollection<int>' has a CollectionBuilderAttribute but no element type. // MyCollection<int> c = []; Diagnostic(ErrorCode.ERR_CollectionBuilderNoElementType, "[]").WithArguments("MyCollection<int>").WithLocation(6, 31)); } [Fact] public void CollectionBuilder_InaccessibleGetEnumerator() { string source = """ using System; using System.Collections.Generic; using System.Runtime.CompilerServices; [CollectionBuilder(typeof(MyCollectionBuilder), nameof(MyCollectionBuilder.Create))] class MyCollection<T> { internal IEnumerator<T> GetEnumerator() => default; public static MyCollection<T> F() => []; } class MyCollectionBuilder { public static MyCollection<T> Create<T>(ReadOnlySpan<T> items) => default; } class Program { static void Main() { MyCollection<int> c = []; } } """; var comp = CreateCompilation(source, targetFramework: TargetFramework.Net80); comp.VerifyEmitDiagnostics( // (8,42): error CS9188: 'MyCollection<T>' has a CollectionBuilderAttribute but no element type. // public static MyCollection<T> F() => []; Diagnostic(ErrorCode.ERR_CollectionBuilderNoElementType, "[]").WithArguments("MyCollection<T>").WithLocation(8, 42), // (18,31): error CS9188: 'MyCollection<int>' has a CollectionBuilderAttribute but no element type. // MyCollection<int> c = []; Diagnostic(ErrorCode.ERR_CollectionBuilderNoElementType, "[]").WithArguments("MyCollection<int>").WithLocation(18, 31)); } [InlineData("", "", false)] [InlineData("", "", true)] [InlineData("scoped", "", false)] [InlineData("scoped", "", true)] [InlineData("scoped", "scoped", false)] [InlineData("scoped", "scoped", true)] [Theory] public void CollectionBuilder_Scoped(string constructorParameterModifier, string builderParameterModifier, bool useCompilationReference) { string sourceA = $$""" using System; using System.Collections.Generic; using System.Runtime.CompilerServices; [CollectionBuilder(typeof(MyCollectionBuilder), nameof(MyCollectionBuilder.Create))] public ref struct MyCollection<T> { private readonly List<T> _list; public MyCollection({{constructorParameterModifier}} ReadOnlySpan<T> items) { _list = new List<T>(items.ToArray()); } public IEnumerator<T> GetEnumerator() => _list.GetEnumerator(); } public class MyCollectionBuilder { public static MyCollection<T> Create<T>({{builderParameterModifier}} ReadOnlySpan<T> items) => new(items); } """; var comp = CreateCompilation(sourceA, targetFramework: TargetFramework.Net80); comp.VerifyEmitDiagnostics(); var refA = AsReference(comp, useCompilationReference); string sourceB = """ using System.Collections.Generic; class Program { static void Main() { MyCollection<string> x = []; GetItems(x).Report(); MyCollection<int> y = [1, 2, 3]; GetItems(y).Report(); } static List<T> GetItems<T>(MyCollection<T> c) { var list = new List<T>(); foreach (var i in c) list.Add(i); return list; } } """; CompileAndVerify( new[] { sourceB, s_collectionExtensions }, references: new[] { refA }, targetFramework: TargetFramework.Net80, verify: Verification.Skipped, expectedOutput: IncludeExpectedOutput("[], [1, 2, 3], ")); } [Fact] public void CollectionBuilder_ScopedBuilderParameterOnly() { string sourceA = $$""" using System; using System.Collections.Generic; using System.Runtime.CompilerServices; [CollectionBuilder(typeof(MyCollectionBuilder), nameof(MyCollectionBuilder.Create))] public ref struct MyCollection<T> { private readonly List<T> _list; public MyCollection(ReadOnlySpan<T> items) { _list = new List<T>(items.ToArray()); } public IEnumerator<T> GetEnumerator() => _list.GetEnumerator(); } public class MyCollectionBuilder { public static MyCollection<T> Create<T>(scoped ReadOnlySpan<T> items) => new(items); } """; string sourceB = """ #pragma warning disable 219 class Program { static void Main() { MyCollection<string> x = []; MyCollection<int> y = [1, 2, 3]; MyCollection<string> z = new(); } } """; var comp = CreateCompilation(new[] { sourceA, sourceB }, targetFramework: TargetFramework.Net80); comp.VerifyEmitDiagnostics( // 0.cs(16,78): error CS8347: Cannot use a result of 'MyCollection<T>.MyCollection(ReadOnlySpan<T>)' in this context because it may expose variables referenced by parameter 'items' outside of their declaration scope // public static MyCollection<T> Create<T>(scoped ReadOnlySpan<T> items) => new(items); Diagnostic(ErrorCode.ERR_EscapeCall, "new(items)").WithArguments("MyCollection<T>.MyCollection(System.ReadOnlySpan<T>)", "items").WithLocation(16, 78), // 0.cs(16,82): error CS8352: Cannot use variable 'scoped ReadOnlySpan<T> items' in this context because it may expose referenced variables outside of their declaration scope // public static MyCollection<T> Create<T>(scoped ReadOnlySpan<T> items) => new(items); Diagnostic(ErrorCode.ERR_EscapeVariable, "items").WithArguments("scoped System.ReadOnlySpan<T> items").WithLocation(16, 82)); } [CombinatorialData] [Theory] public void CollectionBuilder_MissingInt32(bool useCompilationReference) { string sourceA = """ using System; using System.Collections.Generic; using System.Runtime.CompilerServices; [CollectionBuilder(typeof(MyCollectionBuilder), nameof(MyCollectionBuilder.Create))] public struct MyCollection<T> { public IEnumerator<T> GetEnumerator() => default; } public class MyCollectionBuilder { public static MyCollection<T> Create<T>(ReadOnlySpan<T> items) => default; } """; var comp = CreateCompilation(sourceA, targetFramework: TargetFramework.Net80); var refA = AsReference(comp, useCompilationReference); // https://github.com/dotnet/roslyn/issues/73085 // Test hits an assertion failure when collection-expr with a single element is used here string sourceB = """ #pragma warning disable 219 class Program { static void Main() { MyCollection<string> x = []; MyCollection<string> y = ["2", "3"]; MyCollection<object> z = new(); } } """; comp = CreateCompilation(sourceB, references: new[] { refA }, targetFramework: TargetFramework.Net80); comp.MakeTypeMissing(SpecialType.System_Int32); comp.VerifyEmitDiagnostics( // (7,34): error CS0518: Predefined type 'System.Int32' is not defined or imported // MyCollection<string> y = ["2", "3"]; Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, @"[""2"", ""3""]").WithArguments("System.Int32").WithLocation(7, 34), // (7,34): error CS0518: Predefined type 'System.Int32' is not defined or imported // MyCollection<string> y = ["2", "3"]; Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, @"[""2"", ""3""]").WithArguments("System.Int32").WithLocation(7, 34), // (7,34): error CS0518: Predefined type 'System.Int32' is not defined or imported // MyCollection<string> y = ["2", "3"]; Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, @"[""2"", ""3""]").WithArguments("System.Int32").WithLocation(7, 34), // (7,34): error CS0518: Predefined type 'System.Int32' is not defined or imported // MyCollection<string> y = ["2", "3"]; Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, @"[""2"", ""3""]").WithArguments("System.Int32").WithLocation(7, 34)); } [Fact] public void CollectionBuilder_UseSiteError_Method() { // [CollectionBuilder(typeof(MyCollectionBuilder), "Create")] // public sealed class MyCollection<T> // { // public IEnumerator<T> GetEnumerator() { } // } // public static class MyCollectionBuilder // { // [CompilerFeatureRequired("MyFeature")] // public static MyCollection<T> MyCollectionBuilder.Create<T>(ReadOnlySpan<T>) { } // } string sourceA = """ .assembly extern System.Runtime { .ver 8:0:0:0 .publickeytoken = (B0 3F 5F 7F 11 D5 0A 3A) } .class public sealed MyCollection`1<T> { .custom instance void [System.Runtime]System.Runtime.CompilerServices.CollectionBuilderAttribute::.ctor(class [System.Runtime]System.Type, string) = { type(MyCollectionBuilder) string('Create') } .method public hidebysig specialname rtspecialname instance void .ctor() cil managed { ret } .method public instance class [System.Runtime]System.Collections.Generic.IEnumerator`1<!T> GetEnumerator() { ldnull ret } } .class public abstract sealed MyCollectionBuilder { .method public static class MyCollection`1<!!T> Create<T>(valuetype [System.Runtime]System.ReadOnlySpan`1<!!T> items) { .custom instance void [System.Runtime]System.Runtime.CompilerServices.CompilerFeatureRequiredAttribute::.ctor(string) = { string('MyFeature') } ldnull ret } } """; var refA = CompileIL(sourceA); string sourceB = """ #pragma warning disable 219 class Program { static void Main() { MyCollection<int> x = []; MyCollection<string> y = [null]; MyCollection<object> z = MyCollectionBuilder.Create<object>(default); MyCollection<int> w = [with()]; } } """; var comp = CreateCompilation(sourceB, references: new[] { refA }, targetFramework: TargetFramework.Net80); comp.VerifyEmitDiagnostics( // (6,31): error CS9041: 'MyCollectionBuilder.Create<T>(ReadOnlySpan<T>)' requires compiler feature 'MyFeature', which is not supported by this version of the C# compiler. // MyCollection<int> x = []; Diagnostic(ErrorCode.ERR_UnsupportedCompilerFeature, "[]").WithArguments("MyCollectionBuilder.Create<T>(System.ReadOnlySpan<T>)", "MyFeature").WithLocation(6, 31), // (7,34): error CS9041: 'MyCollectionBuilder.Create<T>(ReadOnlySpan<T>)' requires compiler feature 'MyFeature', which is not supported by this version of the C# compiler. // MyCollection<string> y = [null]; Diagnostic(ErrorCode.ERR_UnsupportedCompilerFeature, "[null]").WithArguments("MyCollectionBuilder.Create<T>(System.ReadOnlySpan<T>)", "MyFeature").WithLocation(7, 34), // (8,54): error CS9041: 'MyCollectionBuilder.Create<T>(ReadOnlySpan<T>)' requires compiler feature 'MyFeature', which is not supported by this version of the C# compiler. // MyCollection<object> z = MyCollectionBuilder.Create<object>(default); Diagnostic(ErrorCode.ERR_UnsupportedCompilerFeature, "Create<object>").WithArguments("MyCollectionBuilder.Create<T>(System.ReadOnlySpan<T>)", "MyFeature").WithLocation(8, 54), // (9,32): error CS9041: 'MyCollectionBuilder.Create<T>(ReadOnlySpan<T>)' requires compiler feature 'MyFeature', which is not supported by this version of the C# compiler. // MyCollection<int> w = [with()]; Diagnostic(ErrorCode.ERR_UnsupportedCompilerFeature, "with()").WithArguments("MyCollectionBuilder.Create<T>(System.ReadOnlySpan<T>)", "MyFeature").WithLocation(9, 32)); } [Fact] public void CollectionBuilder_UseSiteError_ContainingType() { // [CollectionBuilder(typeof(MyCollectionBuilder), "Create")] // public sealed class MyCollection<T> // { // public IEnumerator<T> GetEnumerator() { } // } // [CompilerFeatureRequired("MyFeature")] // public static class MyCollectionBuilder // { // public static MyCollection<T> MyCollectionBuilder.Create<T>(ReadOnlySpan<T>) { } // } string sourceA = """ .assembly extern System.Runtime { .ver 8:0:0:0 .publickeytoken = (B0 3F 5F 7F 11 D5 0A 3A) } .class public sealed MyCollection`1<T> { .custom instance void [System.Runtime]System.Runtime.CompilerServices.CollectionBuilderAttribute::.ctor(class [System.Runtime]System.Type, string) = { type(MyCollectionBuilder) string('Create') } .method public hidebysig specialname rtspecialname instance void .ctor() cil managed { ret } .method public instance class [System.Runtime]System.Collections.Generic.IEnumerator`1<!T> GetEnumerator() { ldnull ret } } .class public abstract sealed MyCollectionBuilder { .custom instance void [System.Runtime]System.Runtime.CompilerServices.CompilerFeatureRequiredAttribute::.ctor(string) = { string('MyFeature') } .method public static class MyCollection`1<!!T> Create<T>(valuetype [System.Runtime]System.ReadOnlySpan`1<!!T> items) { ldnull ret } } """; var refA = CompileIL(sourceA); string sourceB = """ #pragma warning disable 219 class Program { static void Main() { MyCollection<int> x = []; MyCollection<string> y = [null]; MyCollection<object> z = MyCollectionBuilder.Create<object>(default); } } """; var comp = CreateCompilation(sourceB, references: new[] { refA }, targetFramework: TargetFramework.Net80); comp.VerifyEmitDiagnostics( // (6,31): error CS9041: 'MyCollectionBuilder' requires compiler feature 'MyFeature', which is not supported by this version of the C# compiler. // MyCollection<int> x = []; Diagnostic(ErrorCode.ERR_UnsupportedCompilerFeature, "[]").WithArguments("MyCollectionBuilder", "MyFeature").WithLocation(6, 31), // (7,34): error CS9041: 'MyCollectionBuilder' requires compiler feature 'MyFeature', which is not supported by this version of the C# compiler. // MyCollection<string> y = [null]; Diagnostic(ErrorCode.ERR_UnsupportedCompilerFeature, "[null]").WithArguments("MyCollectionBuilder", "MyFeature").WithLocation(7, 34), // (8,34): error CS9041: 'MyCollectionBuilder' requires compiler feature 'MyFeature', which is not supported by this version of the C# compiler. // MyCollection<object> z = MyCollectionBuilder.Create<object>(default); Diagnostic(ErrorCode.ERR_UnsupportedCompilerFeature, "MyCollectionBuilder").WithArguments("MyCollectionBuilder", "MyFeature").WithLocation(8, 34), // (8,54): error CS9041: 'MyCollectionBuilder' requires compiler feature 'MyFeature', which is not supported by this version of the C# compiler. // MyCollection<object> z = MyCollectionBuilder.Create<object>(default); Diagnostic(ErrorCode.ERR_UnsupportedCompilerFeature, "Create<object>").WithArguments("MyCollectionBuilder", "MyFeature").WithLocation(8, 54)); } [Fact] public void CollectionBuilder_UseSiteError_MissingType() { string assemblyA = GetUniqueName(); string sourceA = """ public class MyCollectionBuilderBase { } """; var comp = CreateCompilation(sourceA, assemblyName: assemblyA, targetFramework: TargetFramework.Net80); var refA = comp.EmitToImageReference(); string sourceB = """ using System; using System.Collections.Generic; public class MyCollectionBase<T> { public IEnumerator<T> GetEnumerator() => default; } public class MyCollectionBuilder : MyCollectionBuilderBase { public static MyCollectionBase<T> Create<T>(ReadOnlySpan<T> items) => default; } """; comp = CreateCompilation(sourceB, references: new[] { refA }, targetFramework: TargetFramework.Net80); var refB = comp.EmitToImageReference(); string sourceC = """ using System.Runtime.CompilerServices; [CollectionBuilder(typeof(MyCollectionBuilder), nameof(MyCollectionBuilder.Create))] class MyCollection<T> : MyCollectionBase<T> { } """; comp = CreateCompilation(sourceC, references: new[] { refB }, targetFramework: TargetFramework.Net80); comp.VerifyEmitDiagnostics( // (2,76): error CS0012: The type 'MyCollectionBuilderBase' is defined in an assembly that is not referenced. You must add a reference to assembly '68f94ab1-ebba-43d8-9d59-bdb2eb5e4b9f, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'. // [CollectionBuilder(typeof(MyCollectionBuilder), nameof(MyCollectionBuilder.Create))] Diagnostic(ErrorCode.ERR_NoTypeDef, "Create").WithArguments("MyCollectionBuilderBase", $"{assemblyA}, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null").WithLocation(2, 76)); } [Fact] public void CollectionBuilder_MissingReadOnlySpanConstructor() { string sourceA = """ namespace System { public class Object { } public abstract class ValueType { } public class String { } public class Type { } public struct Void { } public struct Boolean { } public struct Int32 { } public struct Enum { } public class Attribute { } public class AttributeUsageAttribute : Attribute { public AttributeUsageAttribute(AttributeTargets t) { } public bool AllowMultiple { get; set; } public bool Inherited { get; set; } } public enum AttributeTargets { } public ref struct ReadOnlySpan<T> { } } namespace System.Collections { public interface IEnumerator { bool MoveNext(); object Current { get; } } public interface IEnumerable { IEnumerator GetEnumerator(); } } namespace System.Collections.Generic { public interface IEnumerator<T> : IEnumerator { new T Current { get; } } public interface IEnumerable<T> : IEnumerable { new IEnumerator<T> GetEnumerator(); } } namespace System.Runtime.CompilerServices { public class CollectionBuilderAttribute : Attribute { public CollectionBuilderAttribute(Type builderType, string methodName) { } } } """; string sourceB = """ using System; using System.Collections; using System.Collections.Generic; using System.Runtime.CompilerServices; [CollectionBuilder(typeof(MyCollectionBuilder), "Create")] class MyCollection<T> : IEnumerable<T> { IEnumerator<T> IEnumerable<T>.GetEnumerator() => null; IEnumerator IEnumerable.GetEnumerator() => null; } class MyCollectionBuilder { public static MyCollection<T> Create<T>(ReadOnlySpan<T> items) => null; } class Program { static void Main() { MyCollection<object> c = [1, 2, 3]; } } """; var comp = CreateEmptyCompilation(new[] { sourceA, sourceB }); comp.VerifyEmitDiagnostics( // warning CS8021: No value for RuntimeMetadataVersion found. No assembly containing System.Object was found nor was a value for RuntimeMetadataVersion specified through options. Diagnostic(ErrorCode.WRN_NoRuntimeMetadataVersion).WithLocation(1, 1), // 1.cs(19,34): error CS0656: Missing compiler required member 'System.ReadOnlySpan`1..ctor' // MyCollection<object> c = [1, 2, 3]; Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "[1, 2, 3]").WithArguments("System.ReadOnlySpan`1", ".ctor").WithLocation(19, 34)); } [Fact] public void Missing_ListType() { string source = """ using System; using System.Collections.Generic; class Program { static void Main() { IList<int> list = [with(), 1, 2, 3]; } } """; var comp = CreateCompilation(source); comp.MakeTypeMissing(WellKnownType.System_Collections_Generic_List_T); comp.VerifyEmitDiagnostics( // (8,27): error CS0656: Missing compiler required member 'System.Collections.Generic.List`1..ctor' // IList<int> list = [with(), 1, 2, 3]; Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "[with(), 1, 2, 3]").WithArguments("System.Collections.Generic.List`1", ".ctor").WithLocation(8, 27), // (8,27): error CS0656: Missing compiler required member 'System.Collections.Generic.List`1..ctor' // IList<int> list = [with(), 1, 2, 3]; Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "[with(), 1, 2, 3]").WithArguments("System.Collections.Generic.List`1", ".ctor").WithLocation(8, 27), // (8,27): error CS0656: Missing compiler required member 'System.Collections.Generic.List`1.Add' // IList<int> list = [with(), 1, 2, 3]; Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "[with(), 1, 2, 3]").WithArguments("System.Collections.Generic.List`1", "Add").WithLocation(8, 27), // (8,27): error CS0656: Missing compiler required member 'System.Collections.Generic.List`1.ToArray' // IList<int> list = [with(), 1, 2, 3]; Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "[with(), 1, 2, 3]").WithArguments("System.Collections.Generic.List`1", "ToArray").WithLocation(8, 27), // (8,28): error CS1729: 'List<int>' does not contain a constructor that takes 0 arguments // IList<int> list = [with(), 1, 2, 3]; Diagnostic(ErrorCode.ERR_BadCtorArgCount, "with").WithArguments("System.Collections.Generic.List<int>", "0").WithLocation(8, 28), // (8,28): error CS0518: Predefined type 'System.Collections.Generic.List`1' is not defined or imported // IList<int> list = [with(), 1, 2, 3]; Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, "with()").WithArguments("System.Collections.Generic.List`1").WithLocation(8, 28)); } [Fact] public void CollectionBuilder_Async() { string sourceA = """ using System; using System.Collections; using System.Collections.Generic; using System.Runtime.CompilerServices; [CollectionBuilder(typeof(MyCollectionBuilder), nameof(MyCollectionBuilder.Create))] public struct MyCollection<T> : IEnumerable<T> { private readonly List<T> _list; public MyCollection(List<T> list) { _list = list; } public IEnumerator<T> GetEnumerator() => _list.GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); } public class MyCollectionBuilder { public static MyCollection<T> Create<T>(ReadOnlySpan<T> items) { return new MyCollection<T>(new List<T>(items.ToArray())); } } """; string sourceB = """ using System.Collections.Generic; using System.Threading.Tasks; class Program { static async Task Main() { (await CreateCollection()).Report(); } static async Task<MyCollection<int>> CreateCollection() { return [await F(1), 2, await F(3)]; } static async Task<int> F(int i) { await Task.Yield(); return i; } } """; var verifier = CompileAndVerify( new[] { sourceA, sourceB, s_collectionExtensions }, targetFramework: TargetFramework.Net80, verify: Verification.Fails, expectedOutput: IncludeExpectedOutput("[1, 2, 3], ")); verifier.VerifyIL("Program.<CreateCollection>d__1.System.Runtime.CompilerServices.IAsyncStateMachine.MoveNext()", """ { // Code size 324 (0x144) .maxstack 3 .locals init (int V_0, MyCollection<int> V_1, int V_2, int V_3, System.Runtime.CompilerServices.TaskAwaiter<int> V_4, System.Exception V_5) IL_0000: ldarg.0 IL_0001: ldfld "int Program.<CreateCollection>d__1.<>1__state" IL_0006: stloc.0 .try { IL_0007: ldloc.0 IL_0008: brfalse.s IL_0057 IL_000a: ldloc.0 IL_000b: ldc.i4.1 IL_000c: beq IL_00cf IL_0011: ldarg.0 IL_0012: ldflda "<>y__InlineArray3<int> Program.<CreateCollection>d__1.<>7__wrap1" IL_0017: initobj "<>y__InlineArray3<int>" IL_001d: ldc.i4.1 IL_001e: call "System.Threading.Tasks.Task<int> Program.F(int)" IL_0023: callvirt "System.Runtime.CompilerServices.TaskAwaiter<int> System.Threading.Tasks.Task<int>.GetAwaiter()" IL_0028: stloc.s V_4 IL_002a: ldloca.s V_4 IL_002c: call "bool System.Runtime.CompilerServices.TaskAwaiter<int>.IsCompleted.get" IL_0031: brtrue.s IL_0074 IL_0033: ldarg.0 IL_0034: ldc.i4.0 IL_0035: dup IL_0036: stloc.0 IL_0037: stfld "int Program.<CreateCollection>d__1.<>1__state" IL_003c: ldarg.0 IL_003d: ldloc.s V_4 IL_003f: stfld "System.Runtime.CompilerServices.TaskAwaiter<int> Program.<CreateCollection>d__1.<>u__1" IL_0044: ldarg.0 IL_0045: ldflda "System.Runtime.CompilerServices.AsyncTaskMethodBuilder<MyCollection<int>> Program.<CreateCollection>d__1.<>t__builder" IL_004a: ldloca.s V_4 IL_004c: ldarg.0 IL_004d: call "void System.Runtime.CompilerServices.AsyncTaskMethodBuilder<MyCollection<int>>.AwaitUnsafeOnCompleted<System.Runtime.CompilerServices.TaskAwaiter<int>, Program.<CreateCollection>d__1>(ref System.Runtime.CompilerServices.TaskAwaiter<int>, ref Program.<CreateCollection>d__1)" IL_0052: leave IL_0143 IL_0057: ldarg.0 IL_0058: ldfld "System.Runtime.CompilerServices.TaskAwaiter<int> Program.<CreateCollection>d__1.<>u__1" IL_005d: stloc.s V_4 IL_005f: ldarg.0 IL_0060: ldflda "System.Runtime.CompilerServices.TaskAwaiter<int> Program.<CreateCollection>d__1.<>u__1" IL_0065: initobj "System.Runtime.CompilerServices.TaskAwaiter<int>" IL_006b: ldarg.0 IL_006c: ldc.i4.m1 IL_006d: dup IL_006e: stloc.0 IL_006f: stfld "int Program.<CreateCollection>d__1.<>1__state" IL_0074: ldloca.s V_4 IL_0076: call "int System.Runtime.CompilerServices.TaskAwaiter<int>.GetResult()" IL_007b: stloc.2 IL_007c: ldarg.0 IL_007d: ldflda "<>y__InlineArray3<int> Program.<CreateCollection>d__1.<>7__wrap1" IL_0082: ldc.i4.0 IL_0083: call "ref int <PrivateImplementationDetails>.InlineArrayElementRef<<>y__InlineArray3<int>, int>(ref <>y__InlineArray3<int>, int)" IL_0088: ldloc.2 IL_0089: stind.i4 IL_008a: ldarg.0 IL_008b: ldflda "<>y__InlineArray3<int> Program.<CreateCollection>d__1.<>7__wrap1" IL_0090: ldc.i4.1 IL_0091: call "ref int <PrivateImplementationDetails>.InlineArrayElementRef<<>y__InlineArray3<int>, int>(ref <>y__InlineArray3<int>, int)" IL_0096: ldc.i4.2 IL_0097: stind.i4 IL_0098: ldc.i4.3 IL_0099: call "System.Threading.Tasks.Task<int> Program.F(int)" IL_009e: callvirt "System.Runtime.CompilerServices.TaskAwaiter<int> System.Threading.Tasks.Task<int>.GetAwaiter()" IL_00a3: stloc.s V_4 IL_00a5: ldloca.s V_4 IL_00a7: call "bool System.Runtime.CompilerServices.TaskAwaiter<int>.IsCompleted.get" IL_00ac: brtrue.s IL_00ec IL_00ae: ldarg.0 IL_00af: ldc.i4.1 IL_00b0: dup IL_00b1: stloc.0 IL_00b2: stfld "int Program.<CreateCollection>d__1.<>1__state" IL_00b7: ldarg.0 IL_00b8: ldloc.s V_4 IL_00ba: stfld "System.Runtime.CompilerServices.TaskAwaiter<int> Program.<CreateCollection>d__1.<>u__1" IL_00bf: ldarg.0 IL_00c0: ldflda "System.Runtime.CompilerServices.AsyncTaskMethodBuilder<MyCollection<int>> Program.<CreateCollection>d__1.<>t__builder" IL_00c5: ldloca.s V_4 IL_00c7: ldarg.0 IL_00c8: call "void System.Runtime.CompilerServices.AsyncTaskMethodBuilder<MyCollection<int>>.AwaitUnsafeOnCompleted<System.Runtime.CompilerServices.TaskAwaiter<int>, Program.<CreateCollection>d__1>(ref System.Runtime.CompilerServices.TaskAwaiter<int>, ref Program.<CreateCollection>d__1)" IL_00cd: leave.s IL_0143 IL_00cf: ldarg.0 IL_00d0: ldfld "System.Runtime.CompilerServices.TaskAwaiter<int> Program.<CreateCollection>d__1.<>u__1" IL_00d5: stloc.s V_4 IL_00d7: ldarg.0 IL_00d8: ldflda "System.Runtime.CompilerServices.TaskAwaiter<int> Program.<CreateCollection>d__1.<>u__1" IL_00dd: initobj "System.Runtime.CompilerServices.TaskAwaiter<int>" IL_00e3: ldarg.0 IL_00e4: ldc.i4.m1 IL_00e5: dup IL_00e6: stloc.0 IL_00e7: stfld "int Program.<CreateCollection>d__1.<>1__state" IL_00ec: ldloca.s V_4 IL_00ee: call "int System.Runtime.CompilerServices.TaskAwaiter<int>.GetResult()" IL_00f3: stloc.3 IL_00f4: ldarg.0 IL_00f5: ldflda "<>y__InlineArray3<int> Program.<CreateCollection>d__1.<>7__wrap1" IL_00fa: ldc.i4.2 IL_00fb: call "ref int <PrivateImplementationDetails>.InlineArrayElementRef<<>y__InlineArray3<int>, int>(ref <>y__InlineArray3<int>, int)" IL_0100: ldloc.3 IL_0101: stind.i4 IL_0102: ldarg.0 IL_0103: ldflda "<>y__InlineArray3<int> Program.<CreateCollection>d__1.<>7__wrap1" IL_0108: ldc.i4.3 IL_0109: call "System.ReadOnlySpan<int> <PrivateImplementationDetails>.InlineArrayAsReadOnlySpan<<>y__InlineArray3<int>, int>(in <>y__InlineArray3<int>, int)" IL_010e: call "MyCollection<int> MyCollectionBuilder.Create<int>(System.ReadOnlySpan<int>)" IL_0113: stloc.1 IL_0114: leave.s IL_012f } catch System.Exception { IL_0116: stloc.s V_5 IL_0118: ldarg.0 IL_0119: ldc.i4.s -2 IL_011b: stfld "int Program.<CreateCollection>d__1.<>1__state" IL_0120: ldarg.0 IL_0121: ldflda "System.Runtime.CompilerServices.AsyncTaskMethodBuilder<MyCollection<int>> Program.<CreateCollection>d__1.<>t__builder" IL_0126: ldloc.s V_5 IL_0128: call "void System.Runtime.CompilerServices.AsyncTaskMethodBuilder<MyCollection<int>>.SetException(System.Exception)" IL_012d: leave.s IL_0143 } IL_012f: ldarg.0 IL_0130: ldc.i4.s -2 IL_0132: stfld "int Program.<CreateCollection>d__1.<>1__state" IL_0137: ldarg.0 IL_0138: ldflda "System.Runtime.CompilerServices.AsyncTaskMethodBuilder<MyCollection<int>> Program.<CreateCollection>d__1.<>t__builder" IL_013d: ldloc.1 IL_013e: call "void System.Runtime.CompilerServices.AsyncTaskMethodBuilder<MyCollection<int>>.SetResult(MyCollection<int>)" IL_0143: ret } """); } [Fact] public void CollectionBuilder_AttributeCycle() { string source = """ using System; using System.Collections; using System.Collections.Generic; using System.Runtime.CompilerServices; [CollectionBuilder(typeof(MyCollectionBuilder), MyCollectionBuilder.GetName([1, 2, 3]))] class MyCollection<T> : IEnumerable<T> { public void Add(T t) { } public IEnumerator<T> GetEnumerator() => null; IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); } static class MyCollectionBuilder { public static string GetName<T>(MyCollection<T> c) => null; public static MyCollection<T> Create<T>(ReadOnlySpan<T> items) => null; } """; var comp = CreateCompilation(source, targetFramework: TargetFramework.Net80); comp.VerifyEmitDiagnostics( // 0.cs(6,49): error CS0182: An attribute argument must be a constant expression, typeof expression or array creation expression of an attribute parameter type // [CollectionBuilder(typeof(MyCollectionBuilder), MyCollectionBuilder.GetName([1, 2, 3]))] Diagnostic(ErrorCode.ERR_BadAttributeArgument, "MyCollectionBuilder.GetName([1, 2, 3])").WithLocation(6, 49)); } [Fact] public void CollectionBuilder_AttributeCycle_2() { string source = """ using System; using System.Collections; using System.Collections.Generic; using System.Runtime.CompilerServices; [CollectionBuilder(typeof(MyCollectionBuilder), ['h', 'i'])] class MyCollection<T> : IEnumerable<T> { public void Add(T t) { } public IEnumerator<T> GetEnumerator() => null; IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); } static class MyCollectionBuilder { public static string GetName<T>(MyCollection<T> c) => null; public static MyCollection<T> Create<T>(ReadOnlySpan<T> items) => null; } """; var comp = CreateCompilation(source, targetFramework: TargetFramework.Net80); comp.VerifyEmitDiagnostics( // (6,49): error CS1729: 'string' does not contain a constructor that takes 0 arguments // [CollectionBuilder(typeof(MyCollectionBuilder), ['h', 'i'])] Diagnostic(ErrorCode.ERR_BadCtorArgCount, "['h', 'i']").WithArguments("string", "0").WithLocation(6, 49), // (6,49): error CS1061: 'string' does not contain a definition for 'Add' and no accessible extension method 'Add' accepting a first argument of type 'string' could be found (are you missing a using directive or an assembly reference?) // [CollectionBuilder(typeof(MyCollectionBuilder), ['h', 'i'])] Diagnostic(ErrorCode.ERR_NoSuchMemberOrExtension, "['h', 'i']").WithArguments("string", "Add").WithLocation(6, 49)); } [Fact] public void CollectionBuilder_AttributeCycle_3() { string source = """ using System; using System.Collections; using System.Collections.Generic; using System.Runtime.CompilerServices; [CollectionBuilder(typeof(MyCollectionBuilder), nameof(MyCollectionBuilder.Create))] [MyCollection<int>([1, 2, 3])] class MyCollection<T> : Attribute, IEnumerable<T> { public MyCollection(MyCollection<T> mc) { } public void Add(T t) { } public IEnumerator<T> GetEnumerator() => null; IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); } static class MyCollectionBuilder { public static MyCollection<T> Create<T>(ReadOnlySpan<T> items) => null; } """; var comp = CreateCompilation(source, targetFramework: TargetFramework.Net80); comp.VerifyEmitDiagnostics( // (7,2): error CS0181: Attribute constructor parameter 'mc' has type 'MyCollection<int>', which is not a valid attribute parameter type // [MyCollection<int>([1, 2, 3])] Diagnostic(ErrorCode.ERR_BadAttributeParamType, "MyCollection<int>").WithArguments("mc", "MyCollection<int>").WithLocation(7, 2)); } [WorkItem("https://github.com/dotnet/roslyn/issues/69980")] [Fact] public void ElementConversion_CollectionBuilder() { string sourceA = """ using System; using System.Collections; using System.Collections.Generic; using System.Runtime.CompilerServices; [CollectionBuilder(typeof(MyCollectionBuilder), "Create")] class MyCollection<T> : IEnumerable<T> { public IEnumerator<T> GetEnumerator() => default; IEnumerator IEnumerable.GetEnumerator() => default; } class MyCollectionBuilder { public static MyCollection<T> Create<T>(ReadOnlySpan<T> items) => default; } """; string sourceB = """ class Program { static void Main() { MyCollection<int> c = [string.Empty, 2, null]; } } """; var comp = CreateCompilation(new[] { sourceA, sourceB }, targetFramework: TargetFramework.Net80); comp.VerifyEmitDiagnostics( // 1.cs(5,32): error CS0029: Cannot implicitly convert type 'string' to 'int' // MyCollection<int> c = [string.Empty, 2, null]; Diagnostic(ErrorCode.ERR_NoImplicitConv, "string.Empty").WithArguments("string", "int").WithLocation(5, 32), // 1.cs(5,49): error CS0037: Cannot convert null to 'int' because it is a non-nullable value type // MyCollection<int> c = [string.Empty, 2, null]; Diagnostic(ErrorCode.ERR_ValueCantBeNull, "null").WithArguments("int").WithLocation(5, 49)); } [Fact] public void ElementConversion_CollectionInitializer() { string sourceA = """ using System; using System.Collections; using System.Collections.Generic; class MyCollection<T> : IEnumerable<T> { public void Add(T t) { } public IEnumerator<T> GetEnumerator() => default; IEnumerator IEnumerable.GetEnumerator() => default; } """; string sourceB = """ class Program { static void Main() { MyCollection<int> c = [string.Empty, 2, null]; } } """; var comp = CreateCompilation(new[] { sourceA, sourceB }); comp.VerifyEmitDiagnostics( // 1.cs(5,32): error CS0029: Cannot implicitly convert type 'string' to 'int' // MyCollection<int> c = [string.Empty, 2, null]; Diagnostic(ErrorCode.ERR_NoImplicitConv, "string.Empty").WithArguments("string", "int").WithLocation(5, 32), // 1.cs(5,49): error CS0037: Cannot convert null to 'int' because it is a non-nullable value type // MyCollection<int> c = [string.Empty, 2, null]; Diagnostic(ErrorCode.ERR_ValueCantBeNull, "null").WithArguments("int").WithLocation(5, 49)); } [InlineData("int[]")] [InlineData("System.ReadOnlySpan<int>")] [InlineData("System.Collections.Generic.IReadOnlyCollection<int>")] [InlineData("System.Collections.Generic.ICollection<int>")] [Theory] public void ElementConversion_Other(string collectionType) { string source = $$""" class Program { static void Main() { {{collectionType}} c; c = [string.Empty, 2, null]; } } """; var comp = CreateCompilation(source, targetFramework: TargetFramework.Net80); comp.VerifyEmitDiagnostics( // (6,14): error CS0029: Cannot implicitly convert type 'string' to 'int' // c = [string.Empty, 2, null]; Diagnostic(ErrorCode.ERR_NoImplicitConv, "string.Empty").WithArguments("string", "int").WithLocation(6, 14), // (6,31): error CS0037: Cannot convert null to 'int' because it is a non-nullable value type // c = [string.Empty, 2, null]; Diagnostic(ErrorCode.ERR_ValueCantBeNull, "null").WithArguments("int").WithLocation(6, 31)); } [CombinatorialData] [Theory, WorkItem("https://github.com/dotnet/roslyn/issues/71183")] public void ListConstruction_01( [CombinatorialValues(TargetFramework.Net70, TargetFramework.Net80)] TargetFramework targetFramework, [CombinatorialValues("List<object>", "ICollection<object>", "IList<object>")] string targetType) { string source = $$""" using System.Collections.Generic; class Program { static void Main() { var x = F(1, 2, 3); x.Report(); } static {{targetType}} F<T>(T x, T y, T z) => [x, y, z]; } """; var verifier = CompileAndVerify( new[] { source, s_collectionExtensions }, targetFramework: targetFramework, verify: Verification.FailsPEVerify, expectedOutput: IncludeExpectedOutput("[1, 2, 3], ")); if (targetFramework == TargetFramework.Net80) { verifier.VerifyIL("Program.F<T>(T, T, T)", """ { // Code size 68 (0x44) .maxstack 3 .locals init (int V_0, System.Span<object> V_1) IL_0000: ldc.i4.3 IL_0001: stloc.0 IL_0002: ldloc.0 IL_0003: newobj "System.Collections.Generic.List<object>..ctor(int)" IL_0008: dup IL_0009: ldloc.0 IL_000a: call "void System.Runtime.InteropServices.CollectionsMarshal.SetCount<object>(System.Collections.Generic.List<object>, int)" IL_000f: dup IL_0010: call "System.Span<object> System.Runtime.InteropServices.CollectionsMarshal.AsSpan<object>(System.Collections.Generic.List<object>)" IL_0015: stloc.1 IL_0016: ldloca.s V_1 IL_0018: ldc.i4.0 IL_0019: call "ref object System.Span<object>.this[int].get" IL_001e: ldarg.0 IL_001f: box "T" IL_0024: stind.ref IL_0025: ldloca.s V_1 IL_0027: ldc.i4.1 IL_0028: call "ref object System.Span<object>.this[int].get" IL_002d: ldarg.1 IL_002e: box "T" IL_0033: stind.ref IL_0034: ldloca.s V_1 IL_0036: ldc.i4.2 IL_0037: call "ref object System.Span<object>.this[int].get" IL_003c: ldarg.2 IL_003d: box "T" IL_0042: stind.ref IL_0043: ret } """); } else { verifier.VerifyIL("Program.F<T>(T, T, T)", """ { // Code size 43 (0x2b) .maxstack 3 IL_0000: ldc.i4.3 IL_0001: newobj "System.Collections.Generic.List<object>..ctor(int)" IL_0006: dup IL_0007: ldarg.0 IL_0008: box "T" IL_000d: callvirt "void System.Collections.Generic.List<object>.Add(object)" IL_0012: dup IL_0013: ldarg.1 IL_0014: box "T" IL_0019: callvirt "void System.Collections.Generic.List<object>.Add(object)" IL_001e: dup IL_001f: ldarg.2 IL_0020: box "T" IL_0025: callvirt "void System.Collections.Generic.List<object>.Add(object)" IL_002a: ret } """); } } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/71183")] public void ListConstruction_FixedElementsBeforeSpread() { // Test that when there are fixed elements before a spread element, // we use an index variable for ALL elements (not constant indices). string source = """ using System.Collections.Generic; class Program { static void Main() { List<int> y = [5, 6]; List<int> x = [1, 2, ..y]; x.Report(); } } """; var verifier = CompileAndVerify( new[] { source, s_collectionExtensionsWithSpan }, targetFramework: TargetFramework.Net80, verify: Verification.Skipped, expectedOutput: IncludeExpectedOutput("[1, 2, 5, 6], ")); verifier.VerifyDiagnostics(); // Verify that we use an index variable (V_6) for all elements, including the fixed elements before the spread verifier.VerifyIL("Program.Main", """ { // Code size 167 (0xa7) .maxstack 5 .locals init (int V_0, System.Span<int> V_1, int V_2, System.Collections.Generic.List<int> V_3, int V_4, System.Span<int> V_5, int V_6, System.Span<int> V_7) IL_0000: ldc.i4.2 IL_0001: stloc.0 IL_0002: ldloc.0 IL_0003: newobj "System.Collections.Generic.List<int>..ctor(int)" IL_0008: dup IL_0009: ldloc.0 IL_000a: call "void System.Runtime.InteropServices.CollectionsMarshal.SetCount<int>(System.Collections.Generic.List<int>, int)" IL_000f: dup IL_0010: call "System.Span<int> System.Runtime.InteropServices.CollectionsMarshal.AsSpan<int>(System.Collections.Generic.List<int>)" IL_0015: stloc.1 IL_0016: ldloca.s V_1 IL_0018: ldc.i4.0 IL_0019: call "ref int System.Span<int>.this[int].get" IL_001e: ldc.i4.5 IL_001f: stind.i4 IL_0020: ldloca.s V_1 IL_0022: ldc.i4.1 IL_0023: call "ref int System.Span<int>.this[int].get" IL_0028: ldc.i4.6 IL_0029: stind.i4 IL_002a: ldc.i4.1 IL_002b: stloc.0 IL_002c: ldc.i4.2 IL_002d: stloc.2 IL_002e: stloc.3 IL_002f: ldc.i4.2 IL_0030: ldloc.3 IL_0031: callvirt "int System.Collections.Generic.List<int>.Count.get" IL_0036: add IL_0037: stloc.s V_4 IL_0039: ldloc.s V_4 IL_003b: newobj "System.Collections.Generic.List<int>..ctor(int)" IL_0040: dup IL_0041: ldloc.s V_4 IL_0043: call "void System.Runtime.InteropServices.CollectionsMarshal.SetCount<int>(System.Collections.Generic.List<int>, int)" IL_0048: dup IL_0049: call "System.Span<int> System.Runtime.InteropServices.CollectionsMarshal.AsSpan<int>(System.Collections.Generic.List<int>)" IL_004e: stloc.s V_5 IL_0050: ldc.i4.0 IL_0051: stloc.s V_6 IL_0053: ldloca.s V_5 IL_0055: ldloc.s V_6 IL_0057: call "ref int System.Span<int>.this[int].get" IL_005c: ldloc.0 IL_005d: stind.i4 IL_005e: ldloc.s V_6 IL_0060: ldc.i4.1 IL_0061: add IL_0062: stloc.s V_6 IL_0064: ldloca.s V_5 IL_0066: ldloc.s V_6 IL_0068: call "ref int System.Span<int>.this[int].get" IL_006d: ldloc.2 IL_006e: stind.i4 IL_006f: ldloc.s V_6 IL_0071: ldc.i4.1 IL_0072: add IL_0073: stloc.s V_6 IL_0075: ldloc.3 IL_0076: call "System.Span<int> System.Runtime.InteropServices.CollectionsMarshal.AsSpan<int>(System.Collections.Generic.List<int>)" IL_007b: stloc.s V_7 IL_007d: ldloca.s V_7 IL_007f: ldloca.s V_5 IL_0081: ldloc.s V_6 IL_0083: ldloca.s V_7 IL_0085: call "int System.Span<int>.Length.get" IL_008a: call "System.Span<int> System.Span<int>.Slice(int, int)" IL_008f: call "void System.Span<int>.CopyTo(System.Span<int>)" IL_0094: ldloc.s V_6 IL_0096: ldloca.s V_7 IL_0098: call "int System.Span<int>.Length.get" IL_009d: add IL_009e: stloc.s V_6 IL_00a0: ldc.i4.0 IL_00a1: call "void CollectionExtensions.Report(object, bool)" IL_00a6: ret } """); } [CombinatorialData] [Theory] public void ListConstruction_02( [CombinatorialValues(TargetFramework.Net70, TargetFramework.Net80)] TargetFramework targetFramework) { string source = """ using System.Collections.Generic; class Program { static void Main() { var x = F<string>([]); x.Report(); var y = F([1, 2, 3]); y.Report(); } static List<object> F<T>(T[] items) => [..items]; } """; var verifier = CompileAndVerify( new[] { source, s_collectionExtensions }, targetFramework: targetFramework, verify: Verification.FailsPEVerify, expectedOutput: IncludeExpectedOutput("[], [1, 2, 3], ")); if (targetFramework == TargetFramework.Net80) { verifier.VerifyIL("Program.F<T>(T[])", """ { // Code size 82 (0x52) .maxstack 3 .locals init (int V_0, System.Collections.Generic.List<object> V_1, System.Span<object> V_2, int V_3, T[] V_4, int V_5, T V_6) IL_0000: ldarg.0 IL_0001: dup IL_0002: ldlen IL_0003: conv.i4 IL_0004: stloc.0 IL_0005: ldloc.0 IL_0006: newobj "System.Collections.Generic.List<object>..ctor(int)" IL_000b: stloc.1 IL_000c: ldloc.1 IL_000d: ldloc.0 IL_000e: call "void System.Runtime.InteropServices.CollectionsMarshal.SetCount<object>(System.Collections.Generic.List<object>, int)" IL_0013: ldloc.1 IL_0014: call "System.Span<object> System.Runtime.InteropServices.CollectionsMarshal.AsSpan<object>(System.Collections.Generic.List<object>)" IL_0019: stloc.2 IL_001a: ldc.i4.0 IL_001b: stloc.3 IL_001c: stloc.s V_4 IL_001e: ldc.i4.0 IL_001f: stloc.s V_5 IL_0021: br.s IL_0048 IL_0023: ldloc.s V_4 IL_0025: ldloc.s V_5 IL_0027: ldelem "T" IL_002c: stloc.s V_6 IL_002e: ldloca.s V_2 IL_0030: ldloc.3 IL_0031: call "ref object System.Span<object>.this[int].get" IL_0036: ldloc.s V_6 IL_0038: box "T" IL_003d: stind.ref IL_003e: ldloc.3 IL_003f: ldc.i4.1 IL_0040: add IL_0041: stloc.3 IL_0042: ldloc.s V_5 IL_0044: ldc.i4.1 IL_0045: add IL_0046: stloc.s V_5 IL_0048: ldloc.s V_5 IL_004a: ldloc.s V_4 IL_004c: ldlen IL_004d: conv.i4 IL_004e: blt.s IL_0023 IL_0050: ldloc.1 IL_0051: ret } """); } else { verifier.VerifyIL("Program.F<T>(T[])", """ { // Code size 47 (0x2f) .maxstack 2 .locals init (System.Collections.Generic.List<object> V_0, T[] V_1, int V_2, T V_3) IL_0000: ldarg.0 IL_0001: dup IL_0002: ldlen IL_0003: conv.i4 IL_0004: newobj "System.Collections.Generic.List<object>..ctor(int)" IL_0009: stloc.0 IL_000a: stloc.1 IL_000b: ldc.i4.0 IL_000c: stloc.2 IL_000d: br.s IL_0027 IL_000f: ldloc.1 IL_0010: ldloc.2 IL_0011: ldelem "T" IL_0016: stloc.3 IL_0017: ldloc.0 IL_0018: ldloc.3 IL_0019: box "T" IL_001e: callvirt "void System.Collections.Generic.List<object>.Add(object)" IL_0023: ldloc.2 IL_0024: ldc.i4.1 IL_0025: add IL_0026: stloc.2 IL_0027: ldloc.2 IL_0028: ldloc.1 IL_0029: ldlen IL_002a: conv.i4 IL_002b: blt.s IL_000f IL_002d: ldloc.0 IL_002e: ret } """); } } [Fact] public void ListConstruction_03() { string source = """ using System.Collections.Generic; class Program { static void Main() { var x = F([1, 2, 3]); x.Report(); } static List<object> F<T>(IEnumerable<T> items) => [..items, 4]; } """; var verifier = CompileAndVerify( new[] { source, s_collectionExtensions }, targetFramework: TargetFramework.Net80, verify: Verification.FailsPEVerify, expectedOutput: IncludeExpectedOutput("[1, 2, 3, 4], ")); verifier.VerifyIL("Program.F<T>", """ { // Code size 68 (0x44) .maxstack 2 .locals init (System.Collections.Generic.List<object> V_0, System.Collections.Generic.IEnumerator<T> V_1, T V_2) IL_0000: newobj "System.Collections.Generic.List<object>..ctor()" IL_0005: stloc.0 IL_0006: ldarg.0 IL_0007: callvirt "System.Collections.Generic.IEnumerator<T> System.Collections.Generic.IEnumerable<T>.GetEnumerator()" IL_000c: stloc.1 .try { IL_000d: br.s IL_0022 IL_000f: ldloc.1 IL_0010: callvirt "T System.Collections.Generic.IEnumerator<T>.Current.get" IL_0015: stloc.2 IL_0016: ldloc.0 IL_0017: ldloc.2 IL_0018: box "T" IL_001d: callvirt "void System.Collections.Generic.List<object>.Add(object)" IL_0022: ldloc.1 IL_0023: callvirt "bool System.Collections.IEnumerator.MoveNext()" IL_0028: brtrue.s IL_000f IL_002a: leave.s IL_0036 } finally { IL_002c: ldloc.1 IL_002d: brfalse.s IL_0035 IL_002f: ldloc.1 IL_0030: callvirt "void System.IDisposable.Dispose()" IL_0035: endfinally } IL_0036: ldloc.0 IL_0037: ldc.i4.4 IL_0038: box "int" IL_003d: callvirt "void System.Collections.Generic.List<object>.Add(object)" IL_0042: ldloc.0 IL_0043: ret } """); } // Use List<T>..ctor(int capacity) if CollectionsMarshal members are missing. [InlineData(new int[0])] [InlineData(new[] { (int)WellKnownMember.System_Runtime_InteropServices_CollectionsMarshal__AsSpan_T })] [InlineData(new[] { (int)WellKnownMember.System_Runtime_InteropServices_CollectionsMarshal__SetCount_T })] [InlineData(new[] { (int)WellKnownMember.System_Runtime_InteropServices_CollectionsMarshal__AsSpan_T, (int)WellKnownMember.System_Runtime_InteropServices_CollectionsMarshal__SetCount_T })] [Theory] public void ListConstruction_MissingMembers_CollectionsMarshal(int[] missingMembers) { string source = """ using System.Collections.Generic; class Program { static void Main() { var x = F([1, 2, 3]); x.Report(); } static List<T> F<T>(T[] items) => [..items, ..items]; } """; var comp = CreateCompilation( new[] { source, s_collectionExtensions }, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseExe); foreach (int missingMember in missingMembers) { comp.MakeMemberMissing((WellKnownMember)missingMember); } var verifier = CompileAndVerify( comp, verify: Verification.FailsPEVerify, expectedOutput: IncludeExpectedOutput("[1, 2, 3, 1, 2, 3], ")); if (missingMembers.Length == 0) { verifier.VerifyIL("Program.F<T>(T[])", """ { // Code size 122 (0x7a) .maxstack 5 .locals init (T[] V_0, T[] V_1, int V_2, System.Span<T> V_3, int V_4, System.ReadOnlySpan<T> V_5, System.ReadOnlySpan<T> V_6) IL_0000: ldarg.0 IL_0001: stloc.0 IL_0002: ldarg.0 IL_0003: stloc.1 IL_0004: ldloc.0 IL_0005: ldlen IL_0006: conv.i4 IL_0007: ldloc.1 IL_0008: ldlen IL_0009: conv.i4 IL_000a: add IL_000b: stloc.2 IL_000c: ldloc.2 IL_000d: newobj "System.Collections.Generic.List<T>..ctor(int)" IL_0012: dup IL_0013: ldloc.2 IL_0014: call "void System.Runtime.InteropServices.CollectionsMarshal.SetCount<T>(System.Collections.Generic.List<T>, int)" IL_0019: dup IL_001a: call "System.Span<T> System.Runtime.InteropServices.CollectionsMarshal.AsSpan<T>(System.Collections.Generic.List<T>)" IL_001f: stloc.3 IL_0020: ldc.i4.0 IL_0021: stloc.s V_4 IL_0023: ldloca.s V_5 IL_0025: ldloc.0 IL_0026: call "System.ReadOnlySpan<T>..ctor(T[])" IL_002b: ldloca.s V_5 IL_002d: ldloca.s V_3 IL_002f: ldloc.s V_4 IL_0031: ldloca.s V_5 IL_0033: call "int System.ReadOnlySpan<T>.Length.get" IL_0038: call "System.Span<T> System.Span<T>.Slice(int, int)" IL_003d: call "void System.ReadOnlySpan<T>.CopyTo(System.Span<T>)" IL_0042: ldloc.s V_4 IL_0044: ldloca.s V_5 IL_0046: call "int System.ReadOnlySpan<T>.Length.get" IL_004b: add IL_004c: stloc.s V_4 IL_004e: ldloca.s V_6 IL_0050: ldloc.1 IL_0051: call "System.ReadOnlySpan<T>..ctor(T[])" IL_0056: ldloca.s V_6 IL_0058: ldloca.s V_3 IL_005a: ldloc.s V_4 IL_005c: ldloca.s V_6 IL_005e: call "int System.ReadOnlySpan<T>.Length.get" IL_0063: call "System.Span<T> System.Span<T>.Slice(int, int)" IL_0068: call "void System.ReadOnlySpan<T>.CopyTo(System.Span<T>)" IL_006d: ldloc.s V_4 IL_006f: ldloca.s V_6 IL_0071: call "int System.ReadOnlySpan<T>.Length.get" IL_0076: add IL_0077: stloc.s V_4 IL_0079: ret } """); } else { // https://github.com/dotnet/roslyn/issues/71216 // Consider preferring AddRange over CopyTo for collection-expressions of List type verifier.VerifyIL("Program.F<T>(T[])", """ { // Code size 31 (0x1f) .maxstack 3 .locals init (T[] V_0, T[] V_1) IL_0000: ldarg.0 IL_0001: stloc.0 IL_0002: ldarg.0 IL_0003: stloc.1 IL_0004: ldloc.0 IL_0005: ldlen IL_0006: conv.i4 IL_0007: ldloc.1 IL_0008: ldlen IL_0009: conv.i4 IL_000a: add IL_000b: newobj "System.Collections.Generic.List<T>..ctor(int)" IL_0010: dup IL_0011: ldloc.0 IL_0012: callvirt "void System.Collections.Generic.List<T>.AddRange(System.Collections.Generic.IEnumerable<T>)" IL_0017: dup IL_0018: ldloc.1 IL_0019: callvirt "void System.Collections.Generic.List<T>.AddRange(System.Collections.Generic.IEnumerable<T>)" IL_001e: ret } """); } } // List<T> optimizations are not applied to derived types. [CombinatorialData] [Theory] public void ListConstruction_DerivedType_01( [CombinatorialValues(TargetFramework.Net70, TargetFramework.Net80)] TargetFramework targetFramework) { string source = """ using System.Collections.Generic; class MyList<T> : List<T> { } class Program { static void Main() { var x = F(1, 2, 3); x.Report(); } static MyList<object> F<T>(T x, T y, T z) => [x, y, z]; } """; var verifier = CompileAndVerify( new[] { source, s_collectionExtensions }, targetFramework: targetFramework, verify: Verification.FailsPEVerify, expectedOutput: IncludeExpectedOutput("[1, 2, 3], ")); verifier.VerifyIL("Program.F<T>(T, T, T)", """ { // Code size 42 (0x2a) .maxstack 3 IL_0000: newobj "MyList<object>..ctor()" IL_0005: dup IL_0006: ldarg.0 IL_0007: box "T" IL_000c: callvirt "void System.Collections.Generic.List<object>.Add(object)" IL_0011: dup IL_0012: ldarg.1 IL_0013: box "T" IL_0018: callvirt "void System.Collections.Generic.List<object>.Add(object)" IL_001d: dup IL_001e: ldarg.2 IL_001f: box "T" IL_0024: callvirt "void System.Collections.Generic.List<object>.Add(object)" IL_0029: ret } """); } // List<T> optimizations are not applied to derived types. [CombinatorialData] [Theory] public void ListConstruction_DerivedType_02( [CombinatorialValues(TargetFramework.Net70, TargetFramework.Net80)] TargetFramework targetFramework) { string source = """ using System.Collections.Generic; class Program { static void Main() { var x = F<int, List<int>>(1, 2, 3); x.Report(); } static U F<T, U>(T x, T y, T z) where U : List<T>, new() => [x, y, z]; } """; var verifier = CompileAndVerify( new[] { source, s_collectionExtensions }, targetFramework: targetFramework, verify: Verification.FailsPEVerify, expectedOutput: IncludeExpectedOutput("[1, 2, 3], ")); verifier.VerifyIL("Program.F<T, U>(T, T, T)", """ { // Code size 42 (0x2a) .maxstack 3 IL_0000: call "U System.Activator.CreateInstance<U>()" IL_0005: dup IL_0006: box "U" IL_000b: ldarg.0 IL_000c: callvirt "void System.Collections.Generic.List<T>.Add(T)" IL_0011: dup IL_0012: box "U" IL_0017: ldarg.1 IL_0018: callvirt "void System.Collections.Generic.List<T>.Add(T)" IL_001d: dup IL_001e: box "U" IL_0023: ldarg.2 IL_0024: callvirt "void System.Collections.Generic.List<T>.Add(T)" IL_0029: ret } """); } [Fact] public void ListConstruction_DerivedType_03() { string source = """ using System.Collections.Generic; class Program { static void Main() { var x = F<int, List<int>>(1, 2, 3); } static U F<T, U>(T x, T y, T z) where U : IList<T>, new() => [x, y, z]; } """; var comp = CreateCompilation(source); comp.VerifyEmitDiagnostics(); } // List<T> optimizations are skipped when unexpected Add() method is used. [Fact] public void ListConstruction_OtherAddMethod() { string sourceA = """ using System.Collections; namespace System { public class Object { } public abstract class ValueType { } public class String { } public class Type { } public struct Void { } public struct Boolean { } public struct Int32 { } public interface IDisposable { void Dispose(); } } namespace System.Collections { public interface IEnumerator { bool MoveNext(); object Current { get; } } public interface IEnumerable { IEnumerator GetEnumerator(); } } namespace System.Collections.Generic { public interface IEnumerator<T> : IEnumerator { T Current { get; } } public interface IEnumerable<T> : IEnumerable { IEnumerator<T> GetEnumerator(); } public class List<T> : IEnumerable<T> { public List() { } public List(int capacity) { } public void Add(T t) { } public void Add(string s) { } public int Length => 0; IEnumerator<T> IEnumerable<T>.GetEnumerator() => null; IEnumerator IEnumerable.GetEnumerator() => null; } } """; string sourceB = """ using System.Collections.Generic; class Program { static List<object> F1(object x, string y) => [x, y]; static List<object> F2(List<string> z) => [..z]; } """; var comp = CreateEmptyCompilation(new[] { sourceA, sourceB }, parseOptions: TestOptions.RegularPreview.WithNoRefSafetyRulesAttribute()); var verifier = CompileAndVerify(comp, verify: Verification.FailsPEVerify); verifier.VerifyIL("Program.F1", """ { // Code size 20 (0x14) .maxstack 3 IL_0000: newobj "System.Collections.Generic.List<object>..ctor()" IL_0005: dup IL_0006: ldarg.0 IL_0007: callvirt "void System.Collections.Generic.List<object>.Add(object)" IL_000c: dup IL_000d: ldarg.1 IL_000e: callvirt "void System.Collections.Generic.List<object>.Add(string)" IL_0013: ret } """); verifier.VerifyIL("Program.F2", """ { // Code size 58 (0x3a) .maxstack 2 .locals init (System.Collections.Generic.List<object> V_0, System.Collections.Generic.IEnumerator<string> V_1, string V_2, System.IDisposable V_3) IL_0000: newobj "System.Collections.Generic.List<object>..ctor()" IL_0005: stloc.0 IL_0006: ldarg.0 IL_0007: callvirt "System.Collections.Generic.IEnumerator<string> System.Collections.Generic.IEnumerable<string>.GetEnumerator()" IL_000c: stloc.1 .try { IL_000d: br.s IL_001d IL_000f: ldloc.1 IL_0010: callvirt "string System.Collections.Generic.IEnumerator<string>.Current.get" IL_0015: stloc.2 IL_0016: ldloc.0 IL_0017: ldloc.2 IL_0018: callvirt "void System.Collections.Generic.List<object>.Add(string)" IL_001d: ldloc.1 IL_001e: callvirt "bool System.Collections.IEnumerator.MoveNext()" IL_0023: brtrue.s IL_000f IL_0025: leave.s IL_0038 } finally { IL_0027: ldloc.1 IL_0028: isinst "System.IDisposable" IL_002d: stloc.3 IL_002e: ldloc.3 IL_002f: brfalse.s IL_0037 IL_0031: ldloc.3 IL_0032: callvirt "void System.IDisposable.Dispose()" IL_0037: endfinally } IL_0038: ldloc.0 IL_0039: ret } """); } // List<T> optimizations are skipped when List<T> has a [CollectionBuilder] attribute. [Fact] public void ListConstruction_CollectionBuilder() { string sourceA = """ using System.Collections; using System.Runtime.CompilerServices; namespace System { public class Object { } public abstract class ValueType { } public class String { } public class Type { } public struct Void { } public struct Boolean { } public struct Int32 { } public interface IDisposable { void Dispose(); } public struct Enum { } public class Attribute { } public class AttributeUsageAttribute : Attribute { public AttributeUsageAttribute(AttributeTargets t) { } public bool AllowMultiple { get; set; } public bool Inherited { get; set; } } public enum AttributeTargets { } public ref struct ReadOnlySpan<T> { public ReadOnlySpan(T[] array) { } } } namespace System.Collections { public interface IEnumerator { bool MoveNext(); object Current { get; } } public interface IEnumerable { IEnumerator GetEnumerator(); } } namespace System.Collections.Generic { public interface IEnumerator<T> : IEnumerator { new T Current { get; } } public interface IEnumerable<T> : IEnumerable { new IEnumerator<T> GetEnumerator(); } [CollectionBuilder(typeof(ListBuilder), "Create")] public class List<T> : IEnumerable<T> { public List() { } public List(int capacity) { } public void Add(T t) { } public int Length => 0; public T[] ToArray() => null; IEnumerator<T> IEnumerable<T>.GetEnumerator() => null; IEnumerator IEnumerable.GetEnumerator() => null; } public class ListBuilder { public static List<T> Create<T>(ReadOnlySpan<T> items) => null; } } namespace System.Runtime.CompilerServices { public class CollectionBuilderAttribute : Attribute { public CollectionBuilderAttribute(Type builderType, string methodName) { } } } """; string sourceB = """ using System.Collections.Generic; class Program { static List<object> F1(object x) => [x]; static List<object> F2(List<int> y) => [..y]; } """; var comp = CreateEmptyCompilation(new[] { sourceA, sourceB }, parseOptions: TestOptions.RegularPreview.WithNoRefSafetyRulesAttribute()); var verifier = CompileAndVerify(comp, verify: Verification.FailsPEVerify); verifier.VerifyIL("Program.F1", """ { // Code size 21 (0x15) .maxstack 4 IL_0000: ldc.i4.1 IL_0001: newarr "object" IL_0006: dup IL_0007: ldc.i4.0 IL_0008: ldarg.0 IL_0009: stelem.ref IL_000a: newobj "System.ReadOnlySpan<object>..ctor(object[])" IL_000f: call "System.Collections.Generic.List<object> System.Collections.Generic.ListBuilder.Create<object>(System.ReadOnlySpan<object>)" IL_0014: ret } """); verifier.VerifyIL("Program.F2", """ { // Code size 85 (0x55) .maxstack 3 .locals init (int V_0, object[] V_1, System.Collections.Generic.IEnumerator<int> V_2, int V_3, System.IDisposable V_4) IL_0000: ldarg.0 IL_0001: ldc.i4.0 IL_0002: stloc.0 IL_0003: dup IL_0004: callvirt "int System.Collections.Generic.List<int>.Length.get" IL_0009: newarr "object" IL_000e: stloc.1 IL_000f: callvirt "System.Collections.Generic.IEnumerator<int> System.Collections.Generic.IEnumerable<int>.GetEnumerator()" IL_0014: stloc.2 .try { IL_0015: br.s IL_002b IL_0017: ldloc.2 IL_0018: callvirt "int System.Collections.Generic.IEnumerator<int>.Current.get" IL_001d: stloc.3 IL_001e: ldloc.1 IL_001f: ldloc.0 IL_0020: ldloc.3 IL_0021: box "int" IL_0026: stelem.ref IL_0027: ldloc.0 IL_0028: ldc.i4.1 IL_0029: add IL_002a: stloc.0 IL_002b: ldloc.2 IL_002c: callvirt "bool System.Collections.IEnumerator.MoveNext()" IL_0031: brtrue.s IL_0017 IL_0033: leave.s IL_0049 } finally { IL_0035: ldloc.2 IL_0036: isinst "System.IDisposable" IL_003b: stloc.s V_4 IL_003d: ldloc.s V_4 IL_003f: brfalse.s IL_0048 IL_0041: ldloc.s V_4 IL_0043: callvirt "void System.IDisposable.Dispose()" IL_0048: endfinally } IL_0049: ldloc.1 IL_004a: newobj "System.ReadOnlySpan<object>..ctor(object[])" IL_004f: call "System.Collections.Generic.List<object> System.Collections.Generic.ListBuilder.Create<object>(System.ReadOnlySpan<object>)" IL_0054: ret } """); } // List<T> optimizations are skipped in async methods since the optimizations use Span<T>. [CombinatorialData] [Theory] public void ListConstruction_Async( [CombinatorialValues(TargetFramework.Net70, TargetFramework.Net80)] TargetFramework targetFramework) { string source = $$""" using System.Collections.Generic; using System.Threading.Tasks; class Program { static async Task Main() { var x = await F(1, 2, 3); x.Report(); } static async Task<T> Yield<T>(T t) { Task.Yield(); return t; } static async Task<List<T>> F<T>(T x, T y, T z) { return [x, await Yield(y), z]; } } """; var verifier = CompileAndVerify( new[] { source, s_collectionExtensions }, targetFramework: targetFramework, verify: Verification.FailsPEVerify, expectedOutput: IncludeExpectedOutput("[1, 2, 3], ")); verifier.VerifyIL("Program.<F>d__2<T>.System.Runtime.CompilerServices.IAsyncStateMachine.MoveNext()", """ { // Code size 229 (0xe5) .maxstack 3 .locals init (int V_0, System.Collections.Generic.List<T> V_1, T V_2, System.Runtime.CompilerServices.TaskAwaiter<T> V_3, System.Exception V_4) IL_0000: ldarg.0 IL_0001: ldfld "int Program.<F>d__2<T>.<>1__state" IL_0006: stloc.0 .try { IL_0007: ldloc.0 IL_0008: brfalse.s IL_006d IL_000a: ldarg.0 IL_000b: ldc.i4.3 IL_000c: newobj "System.Collections.Generic.List<T>..ctor(int)" IL_0011: stfld "System.Collections.Generic.List<T> Program.<F>d__2<T>.<>7__wrap2" IL_0016: ldarg.0 IL_0017: ldfld "System.Collections.Generic.List<T> Program.<F>d__2<T>.<>7__wrap2" IL_001c: ldarg.0 IL_001d: ldfld "T Program.<F>d__2<T>.x" IL_0022: callvirt "void System.Collections.Generic.List<T>.Add(T)" IL_0027: ldarg.0 IL_0028: ldarg.0 IL_0029: ldfld "System.Collections.Generic.List<T> Program.<F>d__2<T>.<>7__wrap2" IL_002e: stfld "System.Collections.Generic.List<T> Program.<F>d__2<T>.<>7__wrap1" IL_0033: ldarg.0 IL_0034: ldfld "T Program.<F>d__2<T>.y" IL_0039: call "System.Threading.Tasks.Task<T> Program.Yield<T>(T)" IL_003e: callvirt "System.Runtime.CompilerServices.TaskAwaiter<T> System.Threading.Tasks.Task<T>.GetAwaiter()" IL_0043: stloc.3 IL_0044: ldloca.s V_3 IL_0046: call "bool System.Runtime.CompilerServices.TaskAwaiter<T>.IsCompleted.get" IL_004b: brtrue.s IL_0089 IL_004d: ldarg.0 IL_004e: ldc.i4.0 IL_004f: dup IL_0050: stloc.0 IL_0051: stfld "int Program.<F>d__2<T>.<>1__state" IL_0056: ldarg.0 IL_0057: ldloc.3 IL_0058: stfld "System.Runtime.CompilerServices.TaskAwaiter<T> Program.<F>d__2<T>.<>u__1" IL_005d: ldarg.0 IL_005e: ldflda "System.Runtime.CompilerServices.AsyncTaskMethodBuilder<System.Collections.Generic.List<T>> Program.<F>d__2<T>.<>t__builder" IL_0063: ldloca.s V_3 IL_0065: ldarg.0 IL_0066: call "void System.Runtime.CompilerServices.AsyncTaskMethodBuilder<System.Collections.Generic.List<T>>.AwaitUnsafeOnCompleted<System.Runtime.CompilerServices.TaskAwaiter<T>, Program.<F>d__2<T>>(ref System.Runtime.CompilerServices.TaskAwaiter<T>, ref Program.<F>d__2<T>)" IL_006b: leave.s IL_00e4 IL_006d: ldarg.0 IL_006e: ldfld "System.Runtime.CompilerServices.TaskAwaiter<T> Program.<F>d__2<T>.<>u__1" IL_0073: stloc.3 IL_0074: ldarg.0 IL_0075: ldflda "System.Runtime.CompilerServices.TaskAwaiter<T> Program.<F>d__2<T>.<>u__1" IL_007a: initobj "System.Runtime.CompilerServices.TaskAwaiter<T>" IL_0080: ldarg.0 IL_0081: ldc.i4.m1 IL_0082: dup IL_0083: stloc.0 IL_0084: stfld "int Program.<F>d__2<T>.<>1__state" IL_0089: ldloca.s V_3 IL_008b: call "T System.Runtime.CompilerServices.TaskAwaiter<T>.GetResult()" IL_0090: stloc.2 IL_0091: ldarg.0 IL_0092: ldfld "System.Collections.Generic.List<T> Program.<F>d__2<T>.<>7__wrap1" IL_0097: ldloc.2 IL_0098: callvirt "void System.Collections.Generic.List<T>.Add(T)" IL_009d: ldarg.0 IL_009e: ldfld "System.Collections.Generic.List<T> Program.<F>d__2<T>.<>7__wrap2" IL_00a3: ldarg.0 IL_00a4: ldfld "T Program.<F>d__2<T>.z" IL_00a9: callvirt "void System.Collections.Generic.List<T>.Add(T)" IL_00ae: ldarg.0 IL_00af: ldfld "System.Collections.Generic.List<T> Program.<F>d__2<T>.<>7__wrap2" IL_00b4: stloc.1 IL_00b5: leave.s IL_00d0 } catch System.Exception { IL_00b7: stloc.s V_4 IL_00b9: ldarg.0 IL_00ba: ldc.i4.s -2 IL_00bc: stfld "int Program.<F>d__2<T>.<>1__state" IL_00c1: ldarg.0 IL_00c2: ldflda "System.Runtime.CompilerServices.AsyncTaskMethodBuilder<System.Collections.Generic.List<T>> Program.<F>d__2<T>.<>t__builder" IL_00c7: ldloc.s V_4 IL_00c9: call "void System.Runtime.CompilerServices.AsyncTaskMethodBuilder<System.Collections.Generic.List<T>>.SetException(System.Exception)" IL_00ce: leave.s IL_00e4 } IL_00d0: ldarg.0 IL_00d1: ldc.i4.s -2 IL_00d3: stfld "int Program.<F>d__2<T>.<>1__state" IL_00d8: ldarg.0 IL_00d9: ldflda "System.Runtime.CompilerServices.AsyncTaskMethodBuilder<System.Collections.Generic.List<T>> Program.<F>d__2<T>.<>t__builder" IL_00de: ldloc.1 IL_00df: call "void System.Runtime.CompilerServices.AsyncTaskMethodBuilder<System.Collections.Generic.List<T>>.SetResult(System.Collections.Generic.List<T>)" IL_00e4: ret } """); } [Fact] public void ListConstruction_Dynamic_01_DynamicBinding() { string source = $$""" using System.Collections.Generic; class Program { static MyList<object> F1(List<dynamic> e) => [..e]; static MyList<int> F2(List<dynamic> e) => [..e]; static void Main() { F1([1, 2, 3]).Report(); F2([4, 5]).Report(); } } namespace System.Collections.Generic { class MyList<T> : List<T> { public new void Add(T x) => base.Add(x); public void Add(string x) => throw null; } } """; var verifier = CompileAndVerify( new[] { source, s_collectionExtensions }, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseExe, verify: Verification.FailsPEVerify, expectedOutput: IncludeExpectedOutput("[1, 2, 3], [4, 5], ")); verifier.VerifyIL("Program.F1", """ { // Code size 141 (0x8d) .maxstack 9 .locals init (System.Collections.Generic.MyList<object> V_0, System.Collections.Generic.List<dynamic>.Enumerator V_1, object V_2) IL_0000: newobj "System.Collections.Generic.MyList<object>..ctor()" IL_0005: stloc.0 IL_0006: ldarg.0 IL_0007: callvirt "System.Collections.Generic.List<dynamic>.Enumerator System.Collections.Generic.List<dynamic>.GetEnumerator()" IL_000c: stloc.1 .try { IL_000d: br.s IL_0072 IL_000f: ldloca.s V_1 IL_0011: call "dynamic System.Collections.Generic.List<dynamic>.Enumerator.Current.get" IL_0016: stloc.2 IL_0017: ldsfld "System.Runtime.CompilerServices.CallSite<System.Action<System.Runtime.CompilerServices.CallSite, System.Collections.Generic.MyList<object>, dynamic>> Program.<>o__0.<>p__0" IL_001c: brtrue.s IL_005c IL_001e: ldc.i4 0x100 IL_0023: ldstr "Add" IL_0028: ldnull IL_0029: ldtoken "Program" IL_002e: call "System.Type System.Type.GetTypeFromHandle(System.RuntimeTypeHandle)" IL_0033: ldc.i4.2 IL_0034: newarr "Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo" IL_0039: dup IL_003a: ldc.i4.0 IL_003b: ldc.i4.1 IL_003c: ldnull IL_003d: call "Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo.Create(Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfoFlags, string)" IL_0042: stelem.ref IL_0043: dup IL_0044: ldc.i4.1 IL_0045: ldc.i4.0 IL_0046: ldnull IL_0047: call "Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo.Create(Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfoFlags, string)" IL_004c: stelem.ref IL_004d: call "System.Runtime.CompilerServices.CallSiteBinder Microsoft.CSharp.RuntimeBinder.Binder.InvokeMember(Microsoft.CSharp.RuntimeBinder.CSharpBinderFlags, string, System.Collections.Generic.IEnumerable<System.Type>, System.Type, System.Collections.Generic.IEnumerable<Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo>)" IL_0052: call "System.Runtime.CompilerServices.CallSite<System.Action<System.Runtime.CompilerServices.CallSite, System.Collections.Generic.MyList<object>, dynamic>> System.Runtime.CompilerServices.CallSite<System.Action<System.Runtime.CompilerServices.CallSite, System.Collections.Generic.MyList<object>, dynamic>>.Create(System.Runtime.CompilerServices.CallSiteBinder)" IL_0057: stsfld "System.Runtime.CompilerServices.CallSite<System.Action<System.Runtime.CompilerServices.CallSite, System.Collections.Generic.MyList<object>, dynamic>> Program.<>o__0.<>p__0" IL_005c: ldsfld "System.Runtime.CompilerServices.CallSite<System.Action<System.Runtime.CompilerServices.CallSite, System.Collections.Generic.MyList<object>, dynamic>> Program.<>o__0.<>p__0" IL_0061: ldfld "System.Action<System.Runtime.CompilerServices.CallSite, System.Collections.Generic.MyList<object>, dynamic> System.Runtime.CompilerServices.CallSite<System.Action<System.Runtime.CompilerServices.CallSite, System.Collections.Generic.MyList<object>, dynamic>>.Target" IL_0066: ldsfld "System.Runtime.CompilerServices.CallSite<System.Action<System.Runtime.CompilerServices.CallSite, System.Collections.Generic.MyList<object>, dynamic>> Program.<>o__0.<>p__0" IL_006b: ldloc.0 IL_006c: ldloc.2 IL_006d: callvirt "void System.Action<System.Runtime.CompilerServices.CallSite, System.Collections.Generic.MyList<object>, dynamic>.Invoke(System.Runtime.CompilerServices.CallSite, System.Collections.Generic.MyList<object>, dynamic)" IL_0072: ldloca.s V_1 IL_0074: call "bool System.Collections.Generic.List<dynamic>.Enumerator.MoveNext()" IL_0079: brtrue.s IL_000f IL_007b: leave.s IL_008b } finally { IL_007d: ldloca.s V_1 IL_007f: constrained. "System.Collections.Generic.List<dynamic>.Enumerator" IL_0085: callvirt "void System.IDisposable.Dispose()" IL_008a: endfinally } IL_008b: ldloc.0 IL_008c: ret } """); verifier.VerifyIL("Program.F2", """ { // Code size 141 (0x8d) .maxstack 9 .locals init (System.Collections.Generic.MyList<int> V_0, System.Collections.Generic.List<dynamic>.Enumerator V_1, object V_2) IL_0000: newobj "System.Collections.Generic.MyList<int>..ctor()" IL_0005: stloc.0 IL_0006: ldarg.0 IL_0007: callvirt "System.Collections.Generic.List<dynamic>.Enumerator System.Collections.Generic.List<dynamic>.GetEnumerator()" IL_000c: stloc.1 .try { IL_000d: br.s IL_0072 IL_000f: ldloca.s V_1 IL_0011: call "dynamic System.Collections.Generic.List<dynamic>.Enumerator.Current.get" IL_0016: stloc.2 IL_0017: ldsfld "System.Runtime.CompilerServices.CallSite<System.Action<System.Runtime.CompilerServices.CallSite, System.Collections.Generic.MyList<int>, dynamic>> Program.<>o__1.<>p__0" IL_001c: brtrue.s IL_005c IL_001e: ldc.i4 0x100 IL_0023: ldstr "Add" IL_0028: ldnull IL_0029: ldtoken "Program" IL_002e: call "System.Type System.Type.GetTypeFromHandle(System.RuntimeTypeHandle)" IL_0033: ldc.i4.2 IL_0034: newarr "Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo" IL_0039: dup IL_003a: ldc.i4.0 IL_003b: ldc.i4.1 IL_003c: ldnull IL_003d: call "Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo.Create(Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfoFlags, string)" IL_0042: stelem.ref IL_0043: dup IL_0044: ldc.i4.1 IL_0045: ldc.i4.0 IL_0046: ldnull IL_0047: call "Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo.Create(Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfoFlags, string)" IL_004c: stelem.ref IL_004d: call "System.Runtime.CompilerServices.CallSiteBinder Microsoft.CSharp.RuntimeBinder.Binder.InvokeMember(Microsoft.CSharp.RuntimeBinder.CSharpBinderFlags, string, System.Collections.Generic.IEnumerable<System.Type>, System.Type, System.Collections.Generic.IEnumerable<Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo>)" IL_0052: call "System.Runtime.CompilerServices.CallSite<System.Action<System.Runtime.CompilerServices.CallSite, System.Collections.Generic.MyList<int>, dynamic>> System.Runtime.CompilerServices.CallSite<System.Action<System.Runtime.CompilerServices.CallSite, System.Collections.Generic.MyList<int>, dynamic>>.Create(System.Runtime.CompilerServices.CallSiteBinder)" IL_0057: stsfld "System.Runtime.CompilerServices.CallSite<System.Action<System.Runtime.CompilerServices.CallSite, System.Collections.Generic.MyList<int>, dynamic>> Program.<>o__1.<>p__0" IL_005c: ldsfld "System.Runtime.CompilerServices.CallSite<System.Action<System.Runtime.CompilerServices.CallSite, System.Collections.Generic.MyList<int>, dynamic>> Program.<>o__1.<>p__0" IL_0061: ldfld "System.Action<System.Runtime.CompilerServices.CallSite, System.Collections.Generic.MyList<int>, dynamic> System.Runtime.CompilerServices.CallSite<System.Action<System.Runtime.CompilerServices.CallSite, System.Collections.Generic.MyList<int>, dynamic>>.Target" IL_0066: ldsfld "System.Runtime.CompilerServices.CallSite<System.Action<System.Runtime.CompilerServices.CallSite, System.Collections.Generic.MyList<int>, dynamic>> Program.<>o__1.<>p__0" IL_006b: ldloc.0 IL_006c: ldloc.2 IL_006d: callvirt "void System.Action<System.Runtime.CompilerServices.CallSite, System.Collections.Generic.MyList<int>, dynamic>.Invoke(System.Runtime.CompilerServices.CallSite, System.Collections.Generic.MyList<int>, dynamic)" IL_0072: ldloca.s V_1 IL_0074: call "bool System.Collections.Generic.List<dynamic>.Enumerator.MoveNext()" IL_0079: brtrue.s IL_000f IL_007b: leave.s IL_008b } finally { IL_007d: ldloca.s V_1 IL_007f: constrained. "System.Collections.Generic.List<dynamic>.Enumerator" IL_0085: callvirt "void System.IDisposable.Dispose()" IL_008a: endfinally } IL_008b: ldloc.0 IL_008c: ret } """); } [Fact] public void ListConstruction_Dynamic_01_StaticBinding() { string source = $$""" using System.Collections.Generic; class Program { static List<object> F1(List<dynamic> e) => [..e]; static List<int> F2(List<dynamic> e) => [..e]; static void Main() { F1([1, 2, 3]).Report(); F2([4, 5]).Report(); } } """; var verifier = CompileAndVerify( new[] { source, s_collectionExtensions }, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseExe, verify: Verification.FailsPEVerify, expectedOutput: IncludeExpectedOutput("[1, 2, 3], [4, 5], ")); verifier.VerifyIL("Program.F1", """ { // Code size 7 (0x7) .maxstack 1 IL_0000: ldarg.0 IL_0001: call "System.Collections.Generic.List<object> System.Linq.Enumerable.ToList<object>(System.Collections.Generic.IEnumerable<object>)" IL_0006: ret } """); verifier.VerifyIL("Program.F2", """ { // Code size 141 (0x8d) .maxstack 9 .locals init (System.Collections.Generic.List<int> V_0, System.Collections.Generic.List<dynamic>.Enumerator V_1, object V_2) IL_0000: newobj "System.Collections.Generic.List<int>..ctor()" IL_0005: stloc.0 IL_0006: ldarg.0 IL_0007: callvirt "System.Collections.Generic.List<dynamic>.Enumerator System.Collections.Generic.List<dynamic>.GetEnumerator()" IL_000c: stloc.1 .try { IL_000d: br.s IL_0072 IL_000f: ldloca.s V_1 IL_0011: call "dynamic System.Collections.Generic.List<dynamic>.Enumerator.Current.get" IL_0016: stloc.2 IL_0017: ldsfld "System.Runtime.CompilerServices.CallSite<System.Action<System.Runtime.CompilerServices.CallSite, System.Collections.Generic.List<int>, dynamic>> Program.<>o__1.<>p__0" IL_001c: brtrue.s IL_005c IL_001e: ldc.i4 0x100 IL_0023: ldstr "Add" IL_0028: ldnull IL_0029: ldtoken "Program" IL_002e: call "System.Type System.Type.GetTypeFromHandle(System.RuntimeTypeHandle)" IL_0033: ldc.i4.2 IL_0034: newarr "Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo" IL_0039: dup IL_003a: ldc.i4.0 IL_003b: ldc.i4.1 IL_003c: ldnull IL_003d: call "Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo.Create(Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfoFlags, string)" IL_0042: stelem.ref IL_0043: dup IL_0044: ldc.i4.1 IL_0045: ldc.i4.0 IL_0046: ldnull IL_0047: call "Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo.Create(Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfoFlags, string)" IL_004c: stelem.ref IL_004d: call "System.Runtime.CompilerServices.CallSiteBinder Microsoft.CSharp.RuntimeBinder.Binder.InvokeMember(Microsoft.CSharp.RuntimeBinder.CSharpBinderFlags, string, System.Collections.Generic.IEnumerable<System.Type>, System.Type, System.Collections.Generic.IEnumerable<Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo>)" IL_0052: call "System.Runtime.CompilerServices.CallSite<System.Action<System.Runtime.CompilerServices.CallSite, System.Collections.Generic.List<int>, dynamic>> System.Runtime.CompilerServices.CallSite<System.Action<System.Runtime.CompilerServices.CallSite, System.Collections.Generic.List<int>, dynamic>>.Create(System.Runtime.CompilerServices.CallSiteBinder)" IL_0057: stsfld "System.Runtime.CompilerServices.CallSite<System.Action<System.Runtime.CompilerServices.CallSite, System.Collections.Generic.List<int>, dynamic>> Program.<>o__1.<>p__0" IL_005c: ldsfld "System.Runtime.CompilerServices.CallSite<System.Action<System.Runtime.CompilerServices.CallSite, System.Collections.Generic.List<int>, dynamic>> Program.<>o__1.<>p__0" IL_0061: ldfld "System.Action<System.Runtime.CompilerServices.CallSite, System.Collections.Generic.List<int>, dynamic> System.Runtime.CompilerServices.CallSite<System.Action<System.Runtime.CompilerServices.CallSite, System.Collections.Generic.List<int>, dynamic>>.Target" IL_0066: ldsfld "System.Runtime.CompilerServices.CallSite<System.Action<System.Runtime.CompilerServices.CallSite, System.Collections.Generic.List<int>, dynamic>> Program.<>o__1.<>p__0" IL_006b: ldloc.0 IL_006c: ldloc.2 IL_006d: callvirt "void System.Action<System.Runtime.CompilerServices.CallSite, System.Collections.Generic.List<int>, dynamic>.Invoke(System.Runtime.CompilerServices.CallSite, System.Collections.Generic.List<int>, dynamic)" IL_0072: ldloca.s V_1 IL_0074: call "bool System.Collections.Generic.List<dynamic>.Enumerator.MoveNext()" IL_0079: brtrue.s IL_000f IL_007b: leave.s IL_008b } finally { IL_007d: ldloca.s V_1 IL_007f: constrained. "System.Collections.Generic.List<dynamic>.Enumerator" IL_0085: callvirt "void System.IDisposable.Dispose()" IL_008a: endfinally } IL_008b: ldloc.0 IL_008c: ret } """); } [Fact] public void ListConstruction_Dynamic_02() { string source = $$""" using System.Collections.Generic; class Program { static List<object> F1(dynamic e) => [..e]; static void Main() { F1((List<int>)[1, 2, 3]).Report(); } } """; var verifier = CompileAndVerify( new[] { source, s_collectionExtensions }, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseExe, verify: Verification.FailsPEVerify, expectedOutput: IncludeExpectedOutput("[1, 2, 3], ")); verifier.VerifyIL("Program.F1", """ { // Code size 121 (0x79) .maxstack 3 .locals init (System.Collections.Generic.List<object> V_0, System.Collections.IEnumerator V_1, object V_2, System.IDisposable V_3) IL_0000: newobj "System.Collections.Generic.List<object>..ctor()" IL_0005: stloc.0 IL_0006: ldsfld "System.Runtime.CompilerServices.CallSite<System.Func<System.Runtime.CompilerServices.CallSite, dynamic, System.Collections.IEnumerable>> Program.<>o__0.<>p__0" IL_000b: brtrue.s IL_0031 IL_000d: ldc.i4.0 IL_000e: ldtoken "System.Collections.IEnumerable" IL_0013: call "System.Type System.Type.GetTypeFromHandle(System.RuntimeTypeHandle)" IL_0018: ldtoken "Program" IL_001d: call "System.Type System.Type.GetTypeFromHandle(System.RuntimeTypeHandle)" IL_0022: call "System.Runtime.CompilerServices.CallSiteBinder Microsoft.CSharp.RuntimeBinder.Binder.Convert(Microsoft.CSharp.RuntimeBinder.CSharpBinderFlags, System.Type, System.Type)" IL_0027: call "System.Runtime.CompilerServices.CallSite<System.Func<System.Runtime.CompilerServices.CallSite, dynamic, System.Collections.IEnumerable>> System.Runtime.CompilerServices.CallSite<System.Func<System.Runtime.CompilerServices.CallSite, dynamic, System.Collections.IEnumerable>>.Create(System.Runtime.CompilerServices.CallSiteBinder)" IL_002c: stsfld "System.Runtime.CompilerServices.CallSite<System.Func<System.Runtime.CompilerServices.CallSite, dynamic, System.Collections.IEnumerable>> Program.<>o__0.<>p__0" IL_0031: ldsfld "System.Runtime.CompilerServices.CallSite<System.Func<System.Runtime.CompilerServices.CallSite, dynamic, System.Collections.IEnumerable>> Program.<>o__0.<>p__0" IL_0036: ldfld "System.Func<System.Runtime.CompilerServices.CallSite, dynamic, System.Collections.IEnumerable> System.Runtime.CompilerServices.CallSite<System.Func<System.Runtime.CompilerServices.CallSite, dynamic, System.Collections.IEnumerable>>.Target" IL_003b: ldsfld "System.Runtime.CompilerServices.CallSite<System.Func<System.Runtime.CompilerServices.CallSite, dynamic, System.Collections.IEnumerable>> Program.<>o__0.<>p__0" IL_0040: ldarg.0 IL_0041: callvirt "System.Collections.IEnumerable System.Func<System.Runtime.CompilerServices.CallSite, dynamic, System.Collections.IEnumerable>.Invoke(System.Runtime.CompilerServices.CallSite, dynamic)" IL_0046: callvirt "System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()" IL_004b: stloc.1 .try { IL_004c: br.s IL_005c IL_004e: ldloc.1 IL_004f: callvirt "object System.Collections.IEnumerator.Current.get" IL_0054: stloc.2 IL_0055: ldloc.0 IL_0056: ldloc.2 IL_0057: callvirt "void System.Collections.Generic.List<object>.Add(object)" IL_005c: ldloc.1 IL_005d: callvirt "bool System.Collections.IEnumerator.MoveNext()" IL_0062: brtrue.s IL_004e IL_0064: leave.s IL_0077 } finally { IL_0066: ldloc.1 IL_0067: isinst "System.IDisposable" IL_006c: stloc.3 IL_006d: ldloc.3 IL_006e: brfalse.s IL_0076 IL_0070: ldloc.3 IL_0071: callvirt "void System.IDisposable.Dispose()" IL_0076: endfinally } IL_0077: ldloc.0 IL_0078: ret } """); } [WorkItem("https://github.com/dotnet/roslyn/issues/69704")] [Fact] public void ListConstruction_Dynamic_03() { string source = $$""" using System.Collections.Generic; class Program { static List<int> F2(dynamic e) => [..e]; static void Main() { F2((int[])[4, 5]).Report(); } } """; var comp = CreateCompilation( new[] { source, s_collectionExtensions }, targetFramework: TargetFramework.Net80); comp.VerifyEmitDiagnostics( // 0.cs(4,42): error CS0029: Cannot implicitly convert type 'object' to 'int' // static List<int> F2(dynamic e) => [..e]; Diagnostic(ErrorCode.ERR_NoImplicitConv, "e").WithArguments("object", "int").WithLocation(4, 42)); } [Fact] public void ListConstruction_Dynamic_04_DynamicBinding() { string source = $$""" using System.Collections.Generic; class Program { static MyList<object> F1(dynamic d) => [d]; static MyList<int> F2(dynamic d) => [d]; static void Main() { F1(1).Report(); F2(2).Report(); } } namespace System.Collections.Generic { class MyList<T> : List<T> { public new void Add(T x) => base.Add(x); public void Add(string x) => throw null; } } """; var verifier = CompileAndVerify( new[] { source, s_collectionExtensions }, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseExe, verify: Verification.FailsPEVerify, expectedOutput: IncludeExpectedOutput("[1], [2], ")); verifier.VerifyIL("Program.F1", """ { // Code size 99 (0x63) .maxstack 9 .locals init (System.Collections.Generic.MyList<object> V_0) IL_0000: newobj "System.Collections.Generic.MyList<object>..ctor()" IL_0005: stloc.0 IL_0006: ldsfld "System.Runtime.CompilerServices.CallSite<System.Action<System.Runtime.CompilerServices.CallSite, System.Collections.Generic.MyList<object>, dynamic>> Program.<>o__0.<>p__0" IL_000b: brtrue.s IL_004b IL_000d: ldc.i4 0x100 IL_0012: ldstr "Add" IL_0017: ldnull IL_0018: ldtoken "Program" IL_001d: call "System.Type System.Type.GetTypeFromHandle(System.RuntimeTypeHandle)" IL_0022: ldc.i4.2 IL_0023: newarr "Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo" IL_0028: dup IL_0029: ldc.i4.0 IL_002a: ldc.i4.1 IL_002b: ldnull IL_002c: call "Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo.Create(Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfoFlags, string)" IL_0031: stelem.ref IL_0032: dup IL_0033: ldc.i4.1 IL_0034: ldc.i4.0 IL_0035: ldnull IL_0036: call "Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo.Create(Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfoFlags, string)" IL_003b: stelem.ref IL_003c: call "System.Runtime.CompilerServices.CallSiteBinder Microsoft.CSharp.RuntimeBinder.Binder.InvokeMember(Microsoft.CSharp.RuntimeBinder.CSharpBinderFlags, string, System.Collections.Generic.IEnumerable<System.Type>, System.Type, System.Collections.Generic.IEnumerable<Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo>)" IL_0041: call "System.Runtime.CompilerServices.CallSite<System.Action<System.Runtime.CompilerServices.CallSite, System.Collections.Generic.MyList<object>, dynamic>> System.Runtime.CompilerServices.CallSite<System.Action<System.Runtime.CompilerServices.CallSite, System.Collections.Generic.MyList<object>, dynamic>>.Create(System.Runtime.CompilerServices.CallSiteBinder)" IL_0046: stsfld "System.Runtime.CompilerServices.CallSite<System.Action<System.Runtime.CompilerServices.CallSite, System.Collections.Generic.MyList<object>, dynamic>> Program.<>o__0.<>p__0" IL_004b: ldsfld "System.Runtime.CompilerServices.CallSite<System.Action<System.Runtime.CompilerServices.CallSite, System.Collections.Generic.MyList<object>, dynamic>> Program.<>o__0.<>p__0" IL_0050: ldfld "System.Action<System.Runtime.CompilerServices.CallSite, System.Collections.Generic.MyList<object>, dynamic> System.Runtime.CompilerServices.CallSite<System.Action<System.Runtime.CompilerServices.CallSite, System.Collections.Generic.MyList<object>, dynamic>>.Target" IL_0055: ldsfld "System.Runtime.CompilerServices.CallSite<System.Action<System.Runtime.CompilerServices.CallSite, System.Collections.Generic.MyList<object>, dynamic>> Program.<>o__0.<>p__0" IL_005a: ldloc.0 IL_005b: ldarg.0 IL_005c: callvirt "void System.Action<System.Runtime.CompilerServices.CallSite, System.Collections.Generic.MyList<object>, dynamic>.Invoke(System.Runtime.CompilerServices.CallSite, System.Collections.Generic.MyList<object>, dynamic)" IL_0061: ldloc.0 IL_0062: ret } """); verifier.VerifyIL("Program.F2", """ { // Code size 99 (0x63) .maxstack 9 .locals init (System.Collections.Generic.MyList<int> V_0) IL_0000: newobj "System.Collections.Generic.MyList<int>..ctor()" IL_0005: stloc.0 IL_0006: ldsfld "System.Runtime.CompilerServices.CallSite<System.Action<System.Runtime.CompilerServices.CallSite, System.Collections.Generic.MyList<int>, dynamic>> Program.<>o__1.<>p__0" IL_000b: brtrue.s IL_004b IL_000d: ldc.i4 0x100 IL_0012: ldstr "Add" IL_0017: ldnull IL_0018: ldtoken "Program" IL_001d: call "System.Type System.Type.GetTypeFromHandle(System.RuntimeTypeHandle)" IL_0022: ldc.i4.2 IL_0023: newarr "Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo" IL_0028: dup IL_0029: ldc.i4.0 IL_002a: ldc.i4.1 IL_002b: ldnull IL_002c: call "Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo.Create(Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfoFlags, string)" IL_0031: stelem.ref IL_0032: dup IL_0033: ldc.i4.1 IL_0034: ldc.i4.0 IL_0035: ldnull IL_0036: call "Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo.Create(Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfoFlags, string)" IL_003b: stelem.ref IL_003c: call "System.Runtime.CompilerServices.CallSiteBinder Microsoft.CSharp.RuntimeBinder.Binder.InvokeMember(Microsoft.CSharp.RuntimeBinder.CSharpBinderFlags, string, System.Collections.Generic.IEnumerable<System.Type>, System.Type, System.Collections.Generic.IEnumerable<Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo>)" IL_0041: call "System.Runtime.CompilerServices.CallSite<System.Action<System.Runtime.CompilerServices.CallSite, System.Collections.Generic.MyList<int>, dynamic>> System.Runtime.CompilerServices.CallSite<System.Action<System.Runtime.CompilerServices.CallSite, System.Collections.Generic.MyList<int>, dynamic>>.Create(System.Runtime.CompilerServices.CallSiteBinder)" IL_0046: stsfld "System.Runtime.CompilerServices.CallSite<System.Action<System.Runtime.CompilerServices.CallSite, System.Collections.Generic.MyList<int>, dynamic>> Program.<>o__1.<>p__0" IL_004b: ldsfld "System.Runtime.CompilerServices.CallSite<System.Action<System.Runtime.CompilerServices.CallSite, System.Collections.Generic.MyList<int>, dynamic>> Program.<>o__1.<>p__0" IL_0050: ldfld "System.Action<System.Runtime.CompilerServices.CallSite, System.Collections.Generic.MyList<int>, dynamic> System.Runtime.CompilerServices.CallSite<System.Action<System.Runtime.CompilerServices.CallSite, System.Collections.Generic.MyList<int>, dynamic>>.Target" IL_0055: ldsfld "System.Runtime.CompilerServices.CallSite<System.Action<System.Runtime.CompilerServices.CallSite, System.Collections.Generic.MyList<int>, dynamic>> Program.<>o__1.<>p__0" IL_005a: ldloc.0 IL_005b: ldarg.0 IL_005c: callvirt "void System.Action<System.Runtime.CompilerServices.CallSite, System.Collections.Generic.MyList<int>, dynamic>.Invoke(System.Runtime.CompilerServices.CallSite, System.Collections.Generic.MyList<int>, dynamic)" IL_0061: ldloc.0 IL_0062: ret } """); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/71183")] public void ListConstruction_Dynamic_04_StaticBinding() { string source = $$""" using System.Collections.Generic; class Program { static List<object> F1(dynamic d) => [d]; static List<int> F2(dynamic d) => [d]; static void Main() { F1(1).Report(); F2(2).Report(); } } """; var verifier = CompileAndVerify( new[] { source, s_collectionExtensions }, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseExe, verify: Verification.FailsPEVerify, expectedOutput: IncludeExpectedOutput("[1], [2], ")); verifier.VerifyIL("Program.F1", """ { // Code size 99 (0x63) .maxstack 9 .locals init (System.Collections.Generic.List<object> V_0) IL_0000: newobj "System.Collections.Generic.List<object>..ctor()" IL_0005: stloc.0 IL_0006: ldsfld "System.Runtime.CompilerServices.CallSite<System.Action<System.Runtime.CompilerServices.CallSite, System.Collections.Generic.List<object>, dynamic>> Program.<>o__0.<>p__0" IL_000b: brtrue.s IL_004b IL_000d: ldc.i4 0x100 IL_0012: ldstr "Add" IL_0017: ldnull IL_0018: ldtoken "Program" IL_001d: call "System.Type System.Type.GetTypeFromHandle(System.RuntimeTypeHandle)" IL_0022: ldc.i4.2 IL_0023: newarr "Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo" IL_0028: dup IL_0029: ldc.i4.0 IL_002a: ldc.i4.1 IL_002b: ldnull IL_002c: call "Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo.Create(Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfoFlags, string)" IL_0031: stelem.ref IL_0032: dup IL_0033: ldc.i4.1 IL_0034: ldc.i4.0 IL_0035: ldnull IL_0036: call "Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo.Create(Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfoFlags, string)" IL_003b: stelem.ref IL_003c: call "System.Runtime.CompilerServices.CallSiteBinder Microsoft.CSharp.RuntimeBinder.Binder.InvokeMember(Microsoft.CSharp.RuntimeBinder.CSharpBinderFlags, string, System.Collections.Generic.IEnumerable<System.Type>, System.Type, System.Collections.Generic.IEnumerable<Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo>)" IL_0041: call "System.Runtime.CompilerServices.CallSite<System.Action<System.Runtime.CompilerServices.CallSite, System.Collections.Generic.List<object>, dynamic>> System.Runtime.CompilerServices.CallSite<System.Action<System.Runtime.CompilerServices.CallSite, System.Collections.Generic.List<object>, dynamic>>.Create(System.Runtime.CompilerServices.CallSiteBinder)" IL_0046: stsfld "System.Runtime.CompilerServices.CallSite<System.Action<System.Runtime.CompilerServices.CallSite, System.Collections.Generic.List<object>, dynamic>> Program.<>o__0.<>p__0" IL_004b: ldsfld "System.Runtime.CompilerServices.CallSite<System.Action<System.Runtime.CompilerServices.CallSite, System.Collections.Generic.List<object>, dynamic>> Program.<>o__0.<>p__0" IL_0050: ldfld "System.Action<System.Runtime.CompilerServices.CallSite, System.Collections.Generic.List<object>, dynamic> System.Runtime.CompilerServices.CallSite<System.Action<System.Runtime.CompilerServices.CallSite, System.Collections.Generic.List<object>, dynamic>>.Target" IL_0055: ldsfld "System.Runtime.CompilerServices.CallSite<System.Action<System.Runtime.CompilerServices.CallSite, System.Collections.Generic.List<object>, dynamic>> Program.<>o__0.<>p__0" IL_005a: ldloc.0 IL_005b: ldarg.0 IL_005c: callvirt "void System.Action<System.Runtime.CompilerServices.CallSite, System.Collections.Generic.List<object>, dynamic>.Invoke(System.Runtime.CompilerServices.CallSite, System.Collections.Generic.List<object>, dynamic)" IL_0061: ldloc.0 IL_0062: ret } """); verifier.VerifyIL("Program.F2", """ { // Code size 99 (0x63) .maxstack 9 .locals init (System.Collections.Generic.List<int> V_0) IL_0000: newobj "System.Collections.Generic.List<int>..ctor()" IL_0005: stloc.0 IL_0006: ldsfld "System.Runtime.CompilerServices.CallSite<System.Action<System.Runtime.CompilerServices.CallSite, System.Collections.Generic.List<int>, dynamic>> Program.<>o__1.<>p__0" IL_000b: brtrue.s IL_004b IL_000d: ldc.i4 0x100 IL_0012: ldstr "Add" IL_0017: ldnull IL_0018: ldtoken "Program" IL_001d: call "System.Type System.Type.GetTypeFromHandle(System.RuntimeTypeHandle)" IL_0022: ldc.i4.2 IL_0023: newarr "Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo" IL_0028: dup IL_0029: ldc.i4.0 IL_002a: ldc.i4.1 IL_002b: ldnull IL_002c: call "Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo.Create(Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfoFlags, string)" IL_0031: stelem.ref IL_0032: dup IL_0033: ldc.i4.1 IL_0034: ldc.i4.0 IL_0035: ldnull IL_0036: call "Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo.Create(Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfoFlags, string)" IL_003b: stelem.ref IL_003c: call "System.Runtime.CompilerServices.CallSiteBinder Microsoft.CSharp.RuntimeBinder.Binder.InvokeMember(Microsoft.CSharp.RuntimeBinder.CSharpBinderFlags, string, System.Collections.Generic.IEnumerable<System.Type>, System.Type, System.Collections.Generic.IEnumerable<Microsoft.CSharp.RuntimeBinder.CSharpArgumentInfo>)" IL_0041: call "System.Runtime.CompilerServices.CallSite<System.Action<System.Runtime.CompilerServices.CallSite, System.Collections.Generic.List<int>, dynamic>> System.Runtime.CompilerServices.CallSite<System.Action<System.Runtime.CompilerServices.CallSite, System.Collections.Generic.List<int>, dynamic>>.Create(System.Runtime.CompilerServices.CallSiteBinder)" IL_0046: stsfld "System.Runtime.CompilerServices.CallSite<System.Action<System.Runtime.CompilerServices.CallSite, System.Collections.Generic.List<int>, dynamic>> Program.<>o__1.<>p__0" IL_004b: ldsfld "System.Runtime.CompilerServices.CallSite<System.Action<System.Runtime.CompilerServices.CallSite, System.Collections.Generic.List<int>, dynamic>> Program.<>o__1.<>p__0" IL_0050: ldfld "System.Action<System.Runtime.CompilerServices.CallSite, System.Collections.Generic.List<int>, dynamic> System.Runtime.CompilerServices.CallSite<System.Action<System.Runtime.CompilerServices.CallSite, System.Collections.Generic.List<int>, dynamic>>.Target" IL_0055: ldsfld "System.Runtime.CompilerServices.CallSite<System.Action<System.Runtime.CompilerServices.CallSite, System.Collections.Generic.List<int>, dynamic>> Program.<>o__1.<>p__0" IL_005a: ldloc.0 IL_005b: ldarg.0 IL_005c: callvirt "void System.Action<System.Runtime.CompilerServices.CallSite, System.Collections.Generic.List<int>, dynamic>.Invoke(System.Runtime.CompilerServices.CallSite, System.Collections.Generic.List<int>, dynamic)" IL_0061: ldloc.0 IL_0062: ret } """); } [Fact] public void ListConstruction_Dynamic_05() { string source = $$""" using System.Collections.Generic; class Program { static List<object> F1(dynamic[] d) => [d]; static List<object> F2(List<dynamic[]> e) => [..e]; static void Main() { F1([1, 2, 3]).Report(); F2([[4], [5]]).Report(); } } """; var verifier = CompileAndVerify( new[] { source, s_collectionExtensions }, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseExe, verify: Verification.FailsPEVerify, expectedOutput: IncludeExpectedOutput("[[1, 2, 3]], [[4], [5]], ")); verifier.VerifyIL("Program.F1", """ { // Code size 33 (0x21) .maxstack 3 .locals init (int V_0, System.Span<object> V_1) IL_0000: ldc.i4.1 IL_0001: stloc.0 IL_0002: ldloc.0 IL_0003: newobj "System.Collections.Generic.List<object>..ctor(int)" IL_0008: dup IL_0009: ldloc.0 IL_000a: call "void System.Runtime.InteropServices.CollectionsMarshal.SetCount<object>(System.Collections.Generic.List<object>, int)" IL_000f: dup IL_0010: call "System.Span<object> System.Runtime.InteropServices.CollectionsMarshal.AsSpan<object>(System.Collections.Generic.List<object>)" IL_0015: stloc.1 IL_0016: ldloca.s V_1 IL_0018: ldc.i4.0 IL_0019: call "ref object System.Span<object>.this[int].get" IL_001e: ldarg.0 IL_001f: stind.ref IL_0020: ret } """); verifier.VerifyIL("Program.F2", """ { // Code size 91 (0x5b) .maxstack 3 .locals init (int V_0, System.Collections.Generic.List<object> V_1, System.Span<object> V_2, int V_3, System.Collections.Generic.List<dynamic[]>.Enumerator V_4, dynamic[] V_5) IL_0000: ldarg.0 IL_0001: dup IL_0002: callvirt "int System.Collections.Generic.List<dynamic[]>.Count.get" IL_0007: stloc.0 IL_0008: ldloc.0 IL_0009: newobj "System.Collections.Generic.List<object>..ctor(int)" IL_000e: stloc.1 IL_000f: ldloc.1 IL_0010: ldloc.0 IL_0011: call "void System.Runtime.InteropServices.CollectionsMarshal.SetCount<object>(System.Collections.Generic.List<object>, int)" IL_0016: ldloc.1 IL_0017: call "System.Span<object> System.Runtime.InteropServices.CollectionsMarshal.AsSpan<object>(System.Collections.Generic.List<object>)" IL_001c: stloc.2 IL_001d: ldc.i4.0 IL_001e: stloc.3 IL_001f: callvirt "System.Collections.Generic.List<dynamic[]>.Enumerator System.Collections.Generic.List<dynamic[]>.GetEnumerator()" IL_0024: stloc.s V_4 .try { IL_0026: br.s IL_0040 IL_0028: ldloca.s V_4 IL_002a: call "dynamic[] System.Collections.Generic.List<dynamic[]>.Enumerator.Current.get" IL_002f: stloc.s V_5 IL_0031: ldloca.s V_2 IL_0033: ldloc.3 IL_0034: call "ref object System.Span<object>.this[int].get" IL_0039: ldloc.s V_5 IL_003b: stind.ref IL_003c: ldloc.3 IL_003d: ldc.i4.1 IL_003e: add IL_003f: stloc.3 IL_0040: ldloca.s V_4 IL_0042: call "bool System.Collections.Generic.List<dynamic[]>.Enumerator.MoveNext()" IL_0047: brtrue.s IL_0028 IL_0049: leave.s IL_0059 } finally { IL_004b: ldloca.s V_4 IL_004d: constrained. "System.Collections.Generic.List<dynamic[]>.Enumerator" IL_0053: callvirt "void System.IDisposable.Dispose()" IL_0058: endfinally } IL_0059: ldloc.1 IL_005a: ret } """); } [ConditionalFact(typeof(DesktopOnly))] public void RestrictedTypes() { string source = """ using System; class Program { static void Main() { var x = [default(TypedReference)]; var y = [default(ArgIterator)]; var z = [default(RuntimeArgumentHandle)]; } } """; var comp = CreateCompilation(source); comp.VerifyEmitDiagnostics( // (6,17): error CS9176: There is no target type for the collection expression. // var x = [default(TypedReference)]; Diagnostic(ErrorCode.ERR_CollectionExpressionNoTargetType, "[default(TypedReference)]").WithLocation(6, 17), // (7,17): error CS9176: There is no target type for the collection expression. // var y = [default(ArgIterator)]; Diagnostic(ErrorCode.ERR_CollectionExpressionNoTargetType, "[default(ArgIterator)]").WithLocation(7, 17), // (8,17): error CS9176: There is no target type for the collection expression. // var z = [default(RuntimeArgumentHandle)]; Diagnostic(ErrorCode.ERR_CollectionExpressionNoTargetType, "[default(RuntimeArgumentHandle)]").WithLocation(8, 17)); } [Fact] public void RefStruct_01() { string source = """ ref struct R { public R(ref int i) { } } class Program { static void Main() { int i = 0; var x = [default(R)]; var y = [new R(ref i)]; } } """; var comp = CreateCompilation(source); comp.VerifyEmitDiagnostics( // (10,17): error CS9176: There is no target type for the collection expression. // var x = [default(R)]; Diagnostic(ErrorCode.ERR_CollectionExpressionNoTargetType, "[default(R)]").WithLocation(10, 17), // (11,17): error CS9176: There is no target type for the collection expression. // var y = [new R(ref i)]; Diagnostic(ErrorCode.ERR_CollectionExpressionNoTargetType, "[new R(ref i)]").WithLocation(11, 17)); } [Fact] public void RefStruct_02() { string source = """ using System.Collections.Generic; ref struct R { public int _i; public R(ref int i) { _i = i; } public static implicit operator int(R r) => r._i; } class Program { static void Main() { int i = 1; int[] a = [default(R), new R(ref i)]; a.Report(); } } """; CompileAndVerify(new[] { source, s_collectionExtensions }, expectedOutput: "[0, 1], "); } [Fact] public void RefStruct_03() { string source = """ using System.Collections; using System.Collections.Generic; class C : IEnumerable { private List<int> _list = new List<int>(); public void Add(R r) { _list.Add(r._i); } public Enumerator GetEnumerator() => new Enumerator(_list); IEnumerator IEnumerable.GetEnumerator() => _list.GetEnumerator(); } class Enumerator { private readonly List<int> _list; private int _index; public Enumerator(List<int> list) { _list = list; _index = -1; } public bool MoveNext() { if (_index < _list.Count) _index++; return _index < _list.Count; } public R Current => new R(_list[_index]); } ref struct R { public int _i; public R(int i) { _i = i; } public R(ref int i) { _i = i; } } class Program { static void Main() { int i = 1; C c = [default(R), new R(ref i)]; c.Report(); } } """; CompileAndVerify(new[] { source, s_collectionExtensions }, verify: Verification.Skipped, expectedOutput: "[0, 1], "); } [Fact] public void RefStruct_04() { var source = """ using System.Collections; using System.Collections.Generic; dynamic d = null; S s = [d]; ref struct S : IEnumerable<int> { public IEnumerator<int> GetEnumerator() => throw null; IEnumerator IEnumerable.GetEnumerator() => throw null; public void Add<T>(T t) => throw null; } """; 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) ); } [Fact] public void RefStruct_EnsureCopyingSemanticsWhenReadOnlySpanParameterOfCollectionBuilderIsNotScoped() { string source = """ using System; using System.Runtime.CompilerServices; [CollectionBuilder(typeof(SpanWrap), "Create")] ref struct SpanWrap<T> { private readonly ReadOnlySpan<T> _ros; public int Length => _ros.Length; public T this[int index] => _ros[index]; public Enumerator GetEnumerator() => default; public SpanWrap(ReadOnlySpan<T> ros) { _ros = ros; } public ref struct Enumerator { public bool MoveNext() => default; public T Current => default; } } static class SpanWrap { public static SpanWrap<T> Create<T>(ReadOnlySpan<T> values) => new SpanWrap<T>(values); } class Program { static void Main() { int[] arr = { 1, 2, 3 }; ReadOnlySpan<int> span = arr; SpanWrap<int> spanWrap = [.. span]; arr[1] = 4; span.Report(); ReportSpanWrap(spanWrap); } static void ReportSpanWrap(SpanWrap<int> spanWrap) { Console.Write('['); for (int i = 0; i < spanWrap.Length - 1; i++) { Console.Write(spanWrap[i] + ", "); } Console.Write(spanWrap[^1]); Console.Write(']'); } } """; var verifier = CompileAndVerify( [source, s_collectionExtensionsWithSpan], targetFramework: TargetFramework.Net80, verify: Verification.Skipped, expectedOutput: IncludeExpectedOutput("[1, 4, 3], [1, 2, 3]")); verifier.VerifyDiagnostics(); verifier.VerifyIL("Program.Main", """ { // Code size 59 (0x3b) .maxstack 3 .locals init (System.ReadOnlySpan<int> V_0, //span SpanWrap<int> V_1) //spanWrap IL_0000: ldc.i4.3 IL_0001: newarr "int" IL_0006: dup IL_0007: ldtoken "<PrivateImplementationDetails>.__StaticArrayInitTypeSize=12 <PrivateImplementationDetails>.4636993D3E1DA4E9D6B8F87B79E8F7C6D018580D52661950EABC3845C5897A4D" IL_000c: call "void System.Runtime.CompilerServices.RuntimeHelpers.InitializeArray(System.Array, System.RuntimeFieldHandle)" IL_0011: dup IL_0012: call "System.ReadOnlySpan<int> System.ReadOnlySpan<int>.op_Implicit(int[])" IL_0017: stloc.0 IL_0018: ldloca.s V_0 IL_001a: call "int[] System.ReadOnlySpan<int>.ToArray()" IL_001f: newobj "System.ReadOnlySpan<int>..ctor(int[])" IL_0024: call "SpanWrap<int> SpanWrap.Create<int>(System.ReadOnlySpan<int>)" IL_0029: stloc.1 IL_002a: ldc.i4.1 IL_002b: ldc.i4.4 IL_002c: stelem.i4 IL_002d: ldloca.s V_0 IL_002f: call "void CollectionExtensions.Report<int>(in System.ReadOnlySpan<int>)" IL_0034: ldloc.1 IL_0035: call "void Program.ReportSpanWrap(SpanWrap<int>)" IL_003a: ret } """); } [Fact] public void RefStruct_CanSkipCopyingReadOnlySpanWhenCollectionBuilderParameterIsScoped() { string source = """ using System; using System.Runtime.CompilerServices; [CollectionBuilder(typeof(ArrayWrap), "Create")] ref struct ArrayWrap<T> { private readonly T[] _arr; public int Length => _arr.Length; public T this[int index] => _arr[index]; public Enumerator GetEnumerator() => default; public ArrayWrap(T[] arr) { _arr = arr; } public ref struct Enumerator { public bool MoveNext() => default; public T Current => default; } } static class ArrayWrap { public static ArrayWrap<T> Create<T>(scoped ReadOnlySpan<T> values) => new ArrayWrap<T>(values.ToArray()); } class Program { static void Main() { int[] arr = { 1, 2, 3 }; ReadOnlySpan<int> span = arr; ArrayWrap<int> arrWrap = [.. span]; arr[1] = 4; span.Report(); ReportArrayWrap(arrWrap); } static void ReportArrayWrap(ArrayWrap<int> arrWrap) { Console.Write('['); for (int i = 0; i < arrWrap.Length - 1; i++) { Console.Write(arrWrap[i] + ", "); } Console.Write(arrWrap[^1]); Console.Write(']'); } } """; var verifier = CompileAndVerify( [source, s_collectionExtensionsWithSpan], targetFramework: TargetFramework.Net80, verify: Verification.Skipped, expectedOutput: IncludeExpectedOutput("[1, 4, 3], [1, 2, 3]")); verifier.VerifyDiagnostics(); verifier.VerifyIL("Program.Main", """ { // Code size 48 (0x30) .maxstack 3 .locals init (System.ReadOnlySpan<int> V_0, //span ArrayWrap<int> V_1) //arrWrap IL_0000: ldc.i4.3 IL_0001: newarr "int" IL_0006: dup IL_0007: ldtoken "<PrivateImplementationDetails>.__StaticArrayInitTypeSize=12 <PrivateImplementationDetails>.4636993D3E1DA4E9D6B8F87B79E8F7C6D018580D52661950EABC3845C5897A4D" IL_000c: call "void System.Runtime.CompilerServices.RuntimeHelpers.InitializeArray(System.Array, System.RuntimeFieldHandle)" IL_0011: dup IL_0012: call "System.ReadOnlySpan<int> System.ReadOnlySpan<int>.op_Implicit(int[])" IL_0017: stloc.0 IL_0018: ldloc.0 IL_0019: call "ArrayWrap<int> ArrayWrap.Create<int>(scoped System.ReadOnlySpan<int>)" IL_001e: stloc.1 IL_001f: ldc.i4.1 IL_0020: ldc.i4.4 IL_0021: stelem.i4 IL_0022: ldloca.s V_0 IL_0024: call "void CollectionExtensions.Report<int>(in System.ReadOnlySpan<int>)" IL_0029: ldloc.1 IL_002a: call "void Program.ReportArrayWrap(ArrayWrap<int>)" IL_002f: ret } """); } [CombinatorialData] [Theory] public void RefSafety_Return_01([CombinatorialValues(TargetFramework.Net70, TargetFramework.Net80)] TargetFramework targetFramework) { string source = """ using System; class Program { static void Main() { F1<int>().Report(); F2<string>().Report(); } static Span<T> F1<T>() => []; static ReadOnlySpan<T> F2<T>() => []; } """; var verifier = CompileAndVerify( new[] { source, s_collectionExtensionsWithSpan }, targetFramework: targetFramework, verify: Verification.Skipped, expectedOutput: IncludeExpectedOutput("[], [], ")); verifier.VerifyIL("Program.F1<T>", """ { // Code size 10 (0xa) .maxstack 1 .locals init (System.Span<T> V_0) IL_0000: ldloca.s V_0 IL_0002: initobj "System.Span<T>" IL_0008: ldloc.0 IL_0009: ret } """); verifier.VerifyIL("Program.F2<T>", """ { // Code size 10 (0xa) .maxstack 1 .locals init (System.ReadOnlySpan<T> V_0) IL_0000: ldloca.s V_0 IL_0002: initobj "System.ReadOnlySpan<T>" IL_0008: ldloc.0 IL_0009: ret } """); } [CombinatorialData] [Theory] public void RefSafety_Return_02([CombinatorialValues(TargetFramework.Net70, TargetFramework.Net80)] TargetFramework targetFramework) { string source = """ using System; using System.Collections.Generic; class Program { static Span<T> F1<T>(T x, T y) => [x, y]; static ReadOnlySpan<T> F2<T>(T x, T y) => [x, y]; static ReadOnlySpan<T> F3<T>(IEnumerable<T> e) => [..e]; } """; var comp = CreateCompilation(source, targetFramework: targetFramework); comp.VerifyEmitDiagnostics( // (5,39): error CS9203: A collection expression of type 'Span<T>' cannot be used in this context because it may be exposed outside of the current scope. // static Span<T> F1<T>(T x, T y) => [x, y]; Diagnostic(ErrorCode.ERR_CollectionExpressionEscape, "[x, y]").WithArguments("System.Span<T>").WithLocation(5, 39), // (6,47): error CS9203: A collection expression of type 'ReadOnlySpan<T>' cannot be used in this context because it may be exposed outside of the current scope. // static ReadOnlySpan<T> F2<T>(T x, T y) => [x, y]; Diagnostic(ErrorCode.ERR_CollectionExpressionEscape, "[x, y]").WithArguments("System.ReadOnlySpan<T>").WithLocation(6, 47), // (7,55): error CS9203: A collection expression of type 'ReadOnlySpan<T>' cannot be used in this context because it may be exposed outside of the current scope. // static ReadOnlySpan<T> F3<T>(IEnumerable<T> e) => [..e]; Diagnostic(ErrorCode.ERR_CollectionExpressionEscape, "[..e]").WithArguments("System.ReadOnlySpan<T>").WithLocation(7, 55)); } [CombinatorialData] [Theory] public void RefSafety_Return_03([CombinatorialValues(TargetFramework.Net70, TargetFramework.Net80)] TargetFramework targetFramework) { string source = """ using System; class Program { static void Main() { F1<int>(1, 2).Report(); F2<string>("3", null).Report(); } static Span<T> F1<T>(T x, T y) => (T[])[x, y]; static ReadOnlySpan<T> F2<T>(T x, T y) => (T[])[x, y]; } """; var verifier = CompileAndVerify( new[] { source, s_collectionExtensionsWithSpan }, targetFramework: targetFramework, verify: Verification.Skipped, expectedOutput: IncludeExpectedOutput("[1, 2], [3, null], ")); verifier.VerifyIL("Program.F1<T>", """ { // Code size 28 (0x1c) .maxstack 4 IL_0000: ldc.i4.2 IL_0001: newarr "T" IL_0006: dup IL_0007: ldc.i4.0 IL_0008: ldarg.0 IL_0009: stelem "T" IL_000e: dup IL_000f: ldc.i4.1 IL_0010: ldarg.1 IL_0011: stelem "T" IL_0016: call "System.Span<T> System.Span<T>.op_Implicit(T[])" IL_001b: ret } """); verifier.VerifyIL("Program.F2<T>", """ { // Code size 28 (0x1c) .maxstack 4 IL_0000: ldc.i4.2 IL_0001: newarr "T" IL_0006: dup IL_0007: ldc.i4.0 IL_0008: ldarg.0 IL_0009: stelem "T" IL_000e: dup IL_000f: ldc.i4.1 IL_0010: ldarg.1 IL_0011: stelem "T" IL_0016: call "System.ReadOnlySpan<T> System.ReadOnlySpan<T>.op_Implicit(T[])" IL_001b: ret } """); } [Fact] public void RefSafety_Return_04() { string source = """ using System; delegate Span<T> D<T>(); class Program { static void Main() { D<int> d = () => [1, 2, 3]; Span<int> s = d(); } } """; var comp = CreateCompilation(source, targetFramework: TargetFramework.Net80); comp.VerifyEmitDiagnostics( // (7,26): error CS9203: A collection expression of type 'Span<int>' cannot be used in this context because it may be exposed outside of the current scope. // D<int> d = () => [1, 2, 3]; Diagnostic(ErrorCode.ERR_CollectionExpressionEscape, "[1, 2, 3]").WithArguments("System.Span<int>").WithLocation(7, 26)); } [CombinatorialData] [Theory] public void RefSafety_RefStruct( [CombinatorialValues(TargetFramework.Net70, TargetFramework.Net80)] TargetFramework targetFramework, bool useScoped, bool useUnsafe) { string sourceA = $$""" using System; using System.Collections.Generic; using System.Runtime.CompilerServices; [CollectionBuilder(typeof(MyCollectionBuilder), nameof(MyCollectionBuilder.Create))] public ref struct MyCollection<T> { private readonly List<T> _list; public MyCollection(List<T> list) { _list = list; } public IEnumerator<T> GetEnumerator() => _list.GetEnumerator(); } public class MyCollectionBuilder { public static MyCollection<T> Create<T>({{(useScoped ? "scoped" : "")}} ReadOnlySpan<T> items) => new MyCollection<T>(new List<T>(items.ToArray())); } """; var comp = CreateCompilation( targetFramework == TargetFramework.Net80 ? new[] { sourceA } : new[] { sourceA, CollectionBuilderAttributeDefinition }, targetFramework: targetFramework); comp.VerifyEmitDiagnostics(); var refA = comp.EmitToImageReference(); string sourceB = $$""" using System.Collections.Generic; {{(useUnsafe ? "unsafe" : "")}} class Program { static void Main() { MyCollection<object> x = Empty<object>(); MyCollection<object> y = ThreeItems<object>(1, 2, 3); Report(x); Report(y); } static MyCollection<T> Empty<T>() => []; static MyCollection<T> ThreeItems<T>(T x, T y, T z) => [x, y, z]; static void Report<T>(MyCollection<T> c) { var list = new List<T>(); foreach (var i in c) list.Add(i); list.Report(); } } """; comp = CreateCompilation( new[] { sourceB, s_collectionExtensions }, references: new[] { refA }, targetFramework: targetFramework, options: useUnsafe ? TestOptions.UnsafeReleaseExe : TestOptions.ReleaseExe); if (!useScoped) { comp.VerifyEmitDiagnostics( // 0.cs(12,60): error CS9203: A collection expression of type 'MyCollection<T>' cannot be used in this context because it may be exposed outside of the current scope. // static MyCollection<T> ThreeItems<T>(T x, T y, T z) => [x, y, z]; Diagnostic(ErrorCode.ERR_CollectionExpressionEscape, "[x, y, z]").WithArguments("MyCollection<T>").WithLocation(12, 60)); } else { var verifier = CompileAndVerify(comp, verify: Verification.Skipped, expectedOutput: IncludeExpectedOutput("[], [1, 2, 3], ")); verifier.VerifyIL("Program.Empty<T>", """ { // Code size 15 (0xf) .maxstack 1 .locals init (System.ReadOnlySpan<T> V_0) IL_0000: ldloca.s V_0 IL_0002: initobj "System.ReadOnlySpan<T>" IL_0008: ldloc.0 IL_0009: call "MyCollection<T> MyCollectionBuilder.Create<T>(scoped System.ReadOnlySpan<T>)" IL_000e: ret } """); if (targetFramework == TargetFramework.Net80) { verifier.VerifyIL("Program.ThreeItems<T>", """ { // Code size 64 (0x40) .maxstack 2 .locals init (<>y__InlineArray3<T> V_0) IL_0000: ldloca.s V_0 IL_0002: initobj "<>y__InlineArray3<T>" IL_0008: ldloca.s V_0 IL_000a: ldc.i4.0 IL_000b: call "ref T <PrivateImplementationDetails>.InlineArrayElementRef<<>y__InlineArray3<T>, T>(ref <>y__InlineArray3<T>, int)" IL_0010: ldarg.0 IL_0011: stobj "T" IL_0016: ldloca.s V_0 IL_0018: ldc.i4.1 IL_0019: call "ref T <PrivateImplementationDetails>.InlineArrayElementRef<<>y__InlineArray3<T>, T>(ref <>y__InlineArray3<T>, int)" IL_001e: ldarg.1 IL_001f: stobj "T" IL_0024: ldloca.s V_0 IL_0026: ldc.i4.2 IL_0027: call "ref T <PrivateImplementationDetails>.InlineArrayElementRef<<>y__InlineArray3<T>, T>(ref <>y__InlineArray3<T>, int)" IL_002c: ldarg.2 IL_002d: stobj "T" IL_0032: ldloca.s V_0 IL_0034: ldc.i4.3 IL_0035: call "System.ReadOnlySpan<T> <PrivateImplementationDetails>.InlineArrayAsReadOnlySpan<<>y__InlineArray3<T>, T>(in <>y__InlineArray3<T>, int)" IL_003a: call "MyCollection<T> MyCollectionBuilder.Create<T>(scoped System.ReadOnlySpan<T>)" IL_003f: ret } """); } else { verifier.VerifyIL("Program.ThreeItems<T>", """ { // Code size 41 (0x29) .maxstack 4 IL_0000: ldc.i4.3 IL_0001: newarr "T" IL_0006: dup IL_0007: ldc.i4.0 IL_0008: ldarg.0 IL_0009: stelem "T" IL_000e: dup IL_000f: ldc.i4.1 IL_0010: ldarg.1 IL_0011: stelem "T" IL_0016: dup IL_0017: ldc.i4.2 IL_0018: ldarg.2 IL_0019: stelem "T" IL_001e: newobj "System.ReadOnlySpan<T>..ctor(T[])" IL_0023: call "MyCollection<T> MyCollectionBuilder.Create<T>(scoped System.ReadOnlySpan<T>)" IL_0028: ret } """); } } } // As above, but with C#10 ref safety rules. [Theory] [CombinatorialData] public void RefSafety_RefStruct_CSharp10Rules(bool useCompilationReference) { string sourceA = $$""" using System; using System.Collections.Generic; using System.Runtime.CompilerServices; [CollectionBuilder(typeof(MyCollectionBuilder), nameof(MyCollectionBuilder.Create))] public ref struct MyCollection<T> { private readonly List<T> _list; public MyCollection(List<T> list) { _list = list; } public IEnumerator<T> GetEnumerator() => _list.GetEnumerator(); } public class MyCollectionBuilder { public static MyCollection<T> Create<T>(ReadOnlySpan<T> items) => new MyCollection<T>(new List<T>(items.ToArray())); } """; var comp = CreateCompilation(new[] { sourceA, CollectionBuilderAttributeDefinition }, parseOptions: TestOptions.Regular.WithLanguageVersion(LanguageVersion.CSharp10), targetFramework: TargetFramework.Net60); comp.VerifyEmitDiagnostics(); Assert.False(comp.SourceModule.UseUpdatedEscapeRules); var refA = AsReference(comp, useCompilationReference); string sourceB = """ using System.Collections.Generic; class Program { static void Main() { MyCollection<object> x = Empty<object>(); MyCollection<object> y = ThreeItems<object>(1, 2, 3); Report(x); Report(y); } static MyCollection<T> Empty<T>() => []; static MyCollection<T> ThreeItems<T>(T x, T y, T z) => [x, y, z]; static void Report<T>(MyCollection<T> c) { var list = new List<T>(); foreach (var i in c) list.Add(i); list.Report(); } } """; comp = CreateCompilation(new[] { sourceB, s_collectionExtensions }, references: new[] { refA }, targetFramework: TargetFramework.Net60, options: TestOptions.ReleaseExe.WithSpecificDiagnosticOptions(WithSpanAllocWarning)); comp.VerifyEmitDiagnostics( // 0.cs(12,60): error CS9203: A collection expression of type 'MyCollection<T>' cannot be used in this context because it may be exposed outside of the current scope. // static MyCollection<T> ThreeItems<T>(T x, T y, T z) => [x, y, z]; Diagnostic(ErrorCode.ERR_CollectionExpressionEscape, "[x, y, z]").WithArguments("MyCollection<T>").WithLocation(12, 60)); } [Fact] public void Span_SingleElement() { var source = """ using System; class Program { static void Main() => M(1); static void M(int x) { Span<int> y = [x]; x++; Console.Write(y[0]); Console.Write(x); } } """; var verifier = CompileAndVerify(source, targetFramework: TargetFramework.Net80, verify: Verification.Skipped, expectedOutput: IncludeExpectedOutput("12")); verifier.VerifyDiagnostics(); verifier.VerifyIL("Program.M", """ { // Code size 36 (0x24) .maxstack 2 .locals init (System.Span<int> V_0, //y int V_1) IL_0000: ldarg.0 IL_0001: stloc.1 IL_0002: ldloca.s V_1 IL_0004: newobj "System.Span<int>..ctor(ref int)" IL_0009: stloc.0 IL_000a: ldarg.0 IL_000b: ldc.i4.1 IL_000c: add IL_000d: starg.s V_0 IL_000f: ldloca.s V_0 IL_0011: ldc.i4.0 IL_0012: call "ref int System.Span<int>.this[int].get" IL_0017: ldind.i4 IL_0018: call "void System.Console.Write(int)" IL_001d: ldarg.0 IL_001e: call "void System.Console.Write(int)" IL_0023: ret } """); verifier = CompileAndVerify(source, targetFramework: TargetFramework.Net70, verify: Verification.Skipped, expectedOutput: IncludeExpectedOutput("12")); verifier.VerifyDiagnostics(); verifier.VerifyIL("Program.M", """ { // Code size 43 (0x2b) .maxstack 5 .locals init (System.Span<int> V_0) //y IL_0000: ldloca.s V_0 IL_0002: ldc.i4.1 IL_0003: newarr "int" IL_0008: dup IL_0009: ldc.i4.0 IL_000a: ldarg.0 IL_000b: stelem.i4 IL_000c: call "System.Span<int>..ctor(int[])" IL_0011: ldarg.0 IL_0012: ldc.i4.1 IL_0013: add IL_0014: starg.s V_0 IL_0016: ldloca.s V_0 IL_0018: ldc.i4.0 IL_0019: call "ref int System.Span<int>.this[int].get" IL_001e: ldind.i4 IL_001f: call "void System.Console.Write(int)" IL_0024: ldarg.0 IL_0025: call "void System.Console.Write(int)" IL_002a: ret } """); } [Fact] public void Span_SingleElement_TempsAreNotReused() { var source = """ using System; class Program { static void Main() => M(1); static void M(int x) { { Span<int> y = [x]; Console.Write(y[0]); y[0]++; } { Span<int> y = [x]; Console.Write(y[0]); } } } """; var verifier = CompileAndVerify(source, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseExe, verify: Verification.Skipped, expectedOutput: IncludeExpectedOutput("11")); verifier.VerifyDiagnostics(); verifier.VerifyIL("Program.M", """ { // Code size 62 (0x3e) .maxstack 3 .locals init (int V_0, int V_1, System.Span<int> V_2, //y System.Span<int> V_3) //y IL_0000: ldarg.0 IL_0001: stloc.0 IL_0002: ldloca.s V_0 IL_0004: newobj "System.Span<int>..ctor(ref int)" IL_0009: stloc.2 IL_000a: ldloca.s V_2 IL_000c: ldc.i4.0 IL_000d: call "ref int System.Span<int>.this[int].get" IL_0012: ldind.i4 IL_0013: call "void System.Console.Write(int)" IL_0018: ldloca.s V_2 IL_001a: ldc.i4.0 IL_001b: call "ref int System.Span<int>.this[int].get" IL_0020: dup IL_0021: ldind.i4 IL_0022: ldc.i4.1 IL_0023: add IL_0024: stind.i4 IL_0025: ldarg.0 IL_0026: stloc.1 IL_0027: ldloca.s V_1 IL_0029: newobj "System.Span<int>..ctor(ref int)" IL_002e: stloc.3 IL_002f: ldloca.s V_3 IL_0031: ldc.i4.0 IL_0032: call "ref int System.Span<int>.this[int].get" IL_0037: ldind.i4 IL_0038: call "void System.Console.Write(int)" IL_003d: ret } """); verifier = CompileAndVerify(source, targetFramework: TargetFramework.Net70, options: TestOptions.ReleaseExe, verify: Verification.Skipped, expectedOutput: IncludeExpectedOutput("11")); verifier.VerifyDiagnostics(); verifier.VerifyIL("Program.M", """ { // Code size 76 (0x4c) .maxstack 5 .locals init (System.Span<int> V_0, //y System.Span<int> V_1) //y IL_0000: ldloca.s V_0 IL_0002: ldc.i4.1 IL_0003: newarr "int" IL_0008: dup IL_0009: ldc.i4.0 IL_000a: ldarg.0 IL_000b: stelem.i4 IL_000c: call "System.Span<int>..ctor(int[])" IL_0011: ldloca.s V_0 IL_0013: ldc.i4.0 IL_0014: call "ref int System.Span<int>.this[int].get" IL_0019: ldind.i4 IL_001a: call "void System.Console.Write(int)" IL_001f: ldloca.s V_0 IL_0021: ldc.i4.0 IL_0022: call "ref int System.Span<int>.this[int].get" IL_0027: dup IL_0028: ldind.i4 IL_0029: ldc.i4.1 IL_002a: add IL_002b: stind.i4 IL_002c: ldloca.s V_1 IL_002e: ldc.i4.1 IL_002f: newarr "int" IL_0034: dup IL_0035: ldc.i4.0 IL_0036: ldarg.0 IL_0037: stelem.i4 IL_0038: call "System.Span<int>..ctor(int[])" IL_003d: ldloca.s V_1 IL_003f: ldc.i4.0 IL_0040: call "ref int System.Span<int>.this[int].get" IL_0045: ldind.i4 IL_0046: call "void System.Console.Write(int)" IL_004b: ret } """); } [Fact] public void Span_SingleElement_TempsAreNotReused_SameBlock() { var source = """ using System; class Program { static void Main() => M(1); static void M(int x) { Span<int> y = [x]; Console.Write(y[0]); y[0]++; Span<int> z = [x]; Console.Write(z[0]); } } """; var verifier = CompileAndVerify(source, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseExe, verify: Verification.Skipped, expectedOutput: IncludeExpectedOutput("11")); verifier.VerifyDiagnostics(); verifier.VerifyIL("Program.M", """ { // Code size 62 (0x3e) .maxstack 3 .locals init (System.Span<int> V_0, //y System.Span<int> V_1, //z int V_2, int V_3) IL_0000: ldarg.0 IL_0001: stloc.2 IL_0002: ldloca.s V_2 IL_0004: newobj "System.Span<int>..ctor(ref int)" IL_0009: stloc.0 IL_000a: ldloca.s V_0 IL_000c: ldc.i4.0 IL_000d: call "ref int System.Span<int>.this[int].get" IL_0012: ldind.i4 IL_0013: call "void System.Console.Write(int)" IL_0018: ldloca.s V_0 IL_001a: ldc.i4.0 IL_001b: call "ref int System.Span<int>.this[int].get" IL_0020: dup IL_0021: ldind.i4 IL_0022: ldc.i4.1 IL_0023: add IL_0024: stind.i4 IL_0025: ldarg.0 IL_0026: stloc.3 IL_0027: ldloca.s V_3 IL_0029: newobj "System.Span<int>..ctor(ref int)" IL_002e: stloc.1 IL_002f: ldloca.s V_1 IL_0031: ldc.i4.0 IL_0032: call "ref int System.Span<int>.this[int].get" IL_0037: ldind.i4 IL_0038: call "void System.Console.Write(int)" IL_003d: ret } """); verifier = CompileAndVerify(source, targetFramework: TargetFramework.Net70, options: TestOptions.ReleaseExe, verify: Verification.Skipped, expectedOutput: IncludeExpectedOutput("11")); verifier.VerifyDiagnostics(); verifier.VerifyIL("Program.M", """ { // Code size 76 (0x4c) .maxstack 5 .locals init (System.Span<int> V_0, //y System.Span<int> V_1) //z IL_0000: ldloca.s V_0 IL_0002: ldc.i4.1 IL_0003: newarr "int" IL_0008: dup IL_0009: ldc.i4.0 IL_000a: ldarg.0 IL_000b: stelem.i4 IL_000c: call "System.Span<int>..ctor(int[])" IL_0011: ldloca.s V_0 IL_0013: ldc.i4.0 IL_0014: call "ref int System.Span<int>.this[int].get" IL_0019: ldind.i4 IL_001a: call "void System.Console.Write(int)" IL_001f: ldloca.s V_0 IL_0021: ldc.i4.0 IL_0022: call "ref int System.Span<int>.this[int].get" IL_0027: dup IL_0028: ldind.i4 IL_0029: ldc.i4.1 IL_002a: add IL_002b: stind.i4 IL_002c: ldloca.s V_1 IL_002e: ldc.i4.1 IL_002f: newarr "int" IL_0034: dup IL_0035: ldc.i4.0 IL_0036: ldarg.0 IL_0037: stelem.i4 IL_0038: call "System.Span<int>..ctor(int[])" IL_003d: ldloca.s V_1 IL_003f: ldc.i4.0 IL_0040: call "ref int System.Span<int>.this[int].get" IL_0045: ldind.i4 IL_0046: call "void System.Console.Write(int)" IL_004b: ret } """); } [Fact] public void ReadOnlySpan_SingleElement() { var source = """ using System; class Program { static void Main() => M(1); static void M(int x) { ReadOnlySpan<int> y = [x]; x++; Console.Write(y[0]); Console.Write(x); } } """; var verifier = CompileAndVerify(source, targetFramework: TargetFramework.Net80, verify: Verification.Skipped, expectedOutput: IncludeExpectedOutput("12")); verifier.VerifyDiagnostics(); verifier.VerifyIL("Program.M", """ { // Code size 36 (0x24) .maxstack 2 .locals init (System.ReadOnlySpan<int> V_0, //y int V_1) IL_0000: ldarg.0 IL_0001: stloc.1 IL_0002: ldloca.s V_1 IL_0004: newobj "System.ReadOnlySpan<int>..ctor(ref readonly int)" IL_0009: stloc.0 IL_000a: ldarg.0 IL_000b: ldc.i4.1 IL_000c: add IL_000d: starg.s V_0 IL_000f: ldloca.s V_0 IL_0011: ldc.i4.0 IL_0012: call "ref readonly int System.ReadOnlySpan<int>.this[int].get" IL_0017: ldind.i4 IL_0018: call "void System.Console.Write(int)" IL_001d: ldarg.0 IL_001e: call "void System.Console.Write(int)" IL_0023: ret } """); verifier = CompileAndVerify(source, targetFramework: TargetFramework.Net70, verify: Verification.Skipped, expectedOutput: IncludeExpectedOutput("12")); verifier.VerifyDiagnostics(); verifier.VerifyIL("Program.M", """ { // Code size 43 (0x2b) .maxstack 5 .locals init (System.ReadOnlySpan<int> V_0) //y IL_0000: ldloca.s V_0 IL_0002: ldc.i4.1 IL_0003: newarr "int" IL_0008: dup IL_0009: ldc.i4.0 IL_000a: ldarg.0 IL_000b: stelem.i4 IL_000c: call "System.ReadOnlySpan<int>..ctor(int[])" IL_0011: ldarg.0 IL_0012: ldc.i4.1 IL_0013: add IL_0014: starg.s V_0 IL_0016: ldloca.s V_0 IL_0018: ldc.i4.0 IL_0019: call "ref readonly int System.ReadOnlySpan<int>.this[int].get" IL_001e: ldind.i4 IL_001f: call "void System.Console.Write(int)" IL_0024: ldarg.0 IL_0025: call "void System.Console.Write(int)" IL_002a: ret } """); } [CombinatorialData] [Theory] public void SpanArgument_01([CombinatorialValues(TargetFramework.Net70, TargetFramework.Net80)] TargetFramework targetFramework) { string source = """ using System; class Program { static void Main() { F1<object>([1]); F2<int?>([2]); F3<int?>([3]); F4<object>([4]); } static void F1<T>(Span<T> s) { s.Report(); } static void F2<T>(ReadOnlySpan<T> s) { s.Report(); } static void F3<T>(in Span<T> s) { s.Report(); } static void F4<T>(in ReadOnlySpan<T> s) { s.Report(); } } """; var verifier = CompileAndVerify( new[] { source, s_collectionExtensionsWithSpan }, targetFramework: targetFramework, verify: Verification.Skipped, expectedOutput: IncludeExpectedOutput("[1], [2], [3], [4], ")); if (targetFramework == TargetFramework.Net80) { verifier.VerifyIL("Program.Main", """ { // Code size 87 (0x57) .maxstack 2 .locals init (object V_0, int? V_1, int? V_2, object V_3, System.Span<int?> V_4, System.ReadOnlySpan<object> V_5) IL_0000: ldc.i4.1 IL_0001: box "int" IL_0006: stloc.0 IL_0007: ldloca.s V_0 IL_0009: newobj "System.Span<object>..ctor(ref object)" IL_000e: call "void Program.F1<object>(System.Span<object>)" IL_0013: ldloca.s V_1 IL_0015: ldc.i4.2 IL_0016: call "int?..ctor(int)" IL_001b: ldloca.s V_1 IL_001d: newobj "System.ReadOnlySpan<int?>..ctor(ref readonly int?)" IL_0022: call "void Program.F2<int?>(System.ReadOnlySpan<int?>)" IL_0027: ldloca.s V_2 IL_0029: ldc.i4.3 IL_002a: call "int?..ctor(int)" IL_002f: ldloca.s V_2 IL_0031: newobj "System.Span<int?>..ctor(ref int?)" IL_0036: stloc.s V_4 IL_0038: ldloca.s V_4 IL_003a: call "void Program.F3<int?>(in System.Span<int?>)" IL_003f: ldc.i4.4 IL_0040: box "int" IL_0045: stloc.3 IL_0046: ldloca.s V_3 IL_0048: newobj "System.ReadOnlySpan<object>..ctor(ref readonly object)" IL_004d: stloc.s V_5 IL_004f: ldloca.s V_5 IL_0051: call "void Program.F4<object>(in System.ReadOnlySpan<object>)" IL_0056: ret } """); } else { verifier.VerifyIL("Program.Main", """ { // Code size 115 (0x73) .maxstack 4 .locals init (System.Span<int?> V_0, System.ReadOnlySpan<object> V_1) IL_0000: ldc.i4.1 IL_0001: newarr "object" IL_0006: dup IL_0007: ldc.i4.0 IL_0008: ldc.i4.1 IL_0009: box "int" IL_000e: stelem.ref IL_000f: newobj "System.Span<object>..ctor(object[])" IL_0014: call "void Program.F1<object>(System.Span<object>)" IL_0019: ldc.i4.1 IL_001a: newarr "int?" IL_001f: dup IL_0020: ldc.i4.0 IL_0021: ldc.i4.2 IL_0022: newobj "int?..ctor(int)" IL_0027: stelem "int?" IL_002c: newobj "System.ReadOnlySpan<int?>..ctor(int?[])" IL_0031: call "void Program.F2<int?>(System.ReadOnlySpan<int?>)" IL_0036: ldc.i4.1 IL_0037: newarr "int?" IL_003c: dup IL_003d: ldc.i4.0 IL_003e: ldc.i4.3 IL_003f: newobj "int?..ctor(int)" IL_0044: stelem "int?" IL_0049: newobj "System.Span<int?>..ctor(int?[])" IL_004e: stloc.0 IL_004f: ldloca.s V_0 IL_0051: call "void Program.F3<int?>(in System.Span<int?>)" IL_0056: ldc.i4.1 IL_0057: newarr "object" IL_005c: dup IL_005d: ldc.i4.0 IL_005e: ldc.i4.4 IL_005f: box "int" IL_0064: stelem.ref IL_0065: newobj "System.ReadOnlySpan<object>..ctor(object[])" IL_006a: stloc.1 IL_006b: ldloca.s V_1 IL_006d: call "void Program.F4<object>(in System.ReadOnlySpan<object>)" IL_0072: ret } """); } } [Fact] public void SpanArgument_02() { string source = """ using System; struct S { } ref struct R { } class Program { static void Main() { ReturnsStruct<object>([1]); ReturnsRefStruct<object>([2]); ReturnsRef<object>([3]); ReturnsRefReadOnly<object>([4]); } static int _f = 0; static S ReturnsStruct<T>(Span<T> s) { s.Report(); return default; } static R ReturnsRefStruct<T>(Span<T> s) { s.Report(); return default; } static ref int ReturnsRef<T>(Span<T> s) { s.Report(); return ref _f; } static ref readonly int ReturnsRefReadOnly<T>(Span<T> s) { s.Report(); return ref _f; } } """; var verifier = CompileAndVerify( new[] { source, s_collectionExtensionsWithSpan }, targetFramework: TargetFramework.Net80, verify: Verification.Skipped, expectedOutput: IncludeExpectedOutput("[1], [2], [3], [4], ")); verifier.VerifyIL("Program.Main", """ { // Code size 81 (0x51) .maxstack 1 .locals init (object V_0, object V_1, object V_2, object V_3) IL_0000: ldc.i4.1 IL_0001: box "int" IL_0006: stloc.0 IL_0007: ldloca.s V_0 IL_0009: newobj "System.Span<object>..ctor(ref object)" IL_000e: call "S Program.ReturnsStruct<object>(System.Span<object>)" IL_0013: pop IL_0014: ldc.i4.2 IL_0015: box "int" IL_001a: stloc.1 IL_001b: ldloca.s V_1 IL_001d: newobj "System.Span<object>..ctor(ref object)" IL_0022: call "R Program.ReturnsRefStruct<object>(System.Span<object>)" IL_0027: pop IL_0028: ldc.i4.3 IL_0029: box "int" IL_002e: stloc.2 IL_002f: ldloca.s V_2 IL_0031: newobj "System.Span<object>..ctor(ref object)" IL_0036: call "ref int Program.ReturnsRef<object>(System.Span<object>)" IL_003b: pop IL_003c: ldc.i4.4 IL_003d: box "int" IL_0042: stloc.3 IL_0043: ldloca.s V_3 IL_0045: newobj "System.Span<object>..ctor(ref object)" IL_004a: call "ref readonly int Program.ReturnsRefReadOnly<object>(System.Span<object>)" IL_004f: pop IL_0050: ret } """); } [Fact] public void SpanArgument_03() { string source = """ using System; struct S { } ref struct R { } class Program { static void Main() { ReturnsRefStruct<object>([2]); ReturnsRef<object>([3]); ReturnsRefReadOnly<object>([4]); } static int _f = 0; static R ReturnsRefStruct<T>(scoped Span<T> s) { s.Report(); return default; } static ref int ReturnsRef<T>(scoped Span<T> s) { s.Report(); return ref _f; } static ref readonly int ReturnsRefReadOnly<T>(scoped Span<T> s) { s.Report(); return ref _f; } } """; var verifier = CompileAndVerify( new[] { source, s_collectionExtensionsWithSpan }, targetFramework: TargetFramework.Net80, verify: Verification.Skipped, expectedOutput: IncludeExpectedOutput("[2], [3], [4], ")); verifier.VerifyIL("Program.Main", """ { // Code size 61 (0x3d) .maxstack 1 .locals init (object V_0, object V_1, object V_2) IL_0000: ldc.i4.2 IL_0001: box "int" IL_0006: stloc.0 IL_0007: ldloca.s V_0 IL_0009: newobj "System.Span<object>..ctor(ref object)" IL_000e: call "R Program.ReturnsRefStruct<object>(scoped System.Span<object>)" IL_0013: pop IL_0014: ldc.i4.3 IL_0015: box "int" IL_001a: stloc.1 IL_001b: ldloca.s V_1 IL_001d: newobj "System.Span<object>..ctor(ref object)" IL_0022: call "ref int Program.ReturnsRef<object>(scoped System.Span<object>)" IL_0027: pop IL_0028: ldc.i4.4 IL_0029: box "int" IL_002e: stloc.2 IL_002f: ldloca.s V_2 IL_0031: newobj "System.Span<object>..ctor(ref object)" IL_0036: call "ref readonly int Program.ReturnsRefReadOnly<object>(scoped System.Span<object>)" IL_003b: pop IL_003c: ret } """); } [Fact] public void SpanArgument_04() { string source = """ using System; ref struct R1 { public void M(ReadOnlySpan<int?> s) { s.Report(); } public object this[ReadOnlySpan<int?> s] { set { s.Report(); } } } class Program { static void Main() { var r1 = new R1(); r1.M([3]); r1[[4]] = null; } } """; var comp = CreateCompilation(new[] { source, s_collectionExtensionsWithSpan }, targetFramework: TargetFramework.Net80); comp.VerifyEmitDiagnostics( // 0.cs(12,9): error CS8350: This combination of arguments to 'R1.M(ReadOnlySpan<int?>)' is disallowed because it may expose variables referenced by parameter 's' outside of their declaration scope // r1.M([3]); Diagnostic(ErrorCode.ERR_CallArgMixing, "r1.M([3])").WithArguments("R1.M(System.ReadOnlySpan<int?>)", "s").WithLocation(12, 9), // 0.cs(12,14): error CS9203: A collection expression of type 'ReadOnlySpan<int?>' cannot be used in this context because it may be exposed outside of the current scope. // r1.M([3]); Diagnostic(ErrorCode.ERR_CollectionExpressionEscape, "[3]").WithArguments("System.ReadOnlySpan<int?>").WithLocation(12, 14), // 0.cs(13,9): error CS8350: This combination of arguments to 'R1.this[ReadOnlySpan<int?>]' is disallowed because it may expose variables referenced by parameter 's' outside of their declaration scope // r1[[4]] = null; Diagnostic(ErrorCode.ERR_CallArgMixing, "r1[[4]]").WithArguments("R1.this[System.ReadOnlySpan<int?>]", "s").WithLocation(13, 9), // 0.cs(13,12): error CS9203: A collection expression of type 'ReadOnlySpan<int?>' cannot be used in this context because it may be exposed outside of the current scope. // r1[[4]] = null; Diagnostic(ErrorCode.ERR_CollectionExpressionEscape, "[4]").WithArguments("System.ReadOnlySpan<int?>").WithLocation(13, 12)); } [Fact] public void SpanArgument_05() { string source = """ using System; struct S { public void M(ReadOnlySpan<int?> s) { s.Report(); } public object this[ReadOnlySpan<int?> s] { set { s.Report(); } } } ref struct R1 { public void M(ReadOnlySpan<int?> s) { s.Report(); } public object this[ReadOnlySpan<int?> s] { set { s.Report(); } } } ref struct R2 { public void M(scoped ReadOnlySpan<int?> s) { s.Report(); } public object this[scoped ReadOnlySpan<int?> s] { set { s.Report(); } } } class Program { static void Main() { var s = new S(); s.M([1]); s[[2]] = null; scoped var r1 = new R1(); r1.M([3]); r1[[4]] = null; var r2 = new R2(); r2.M([5]); r2[[6]] = null; } } """; var verifier = CompileAndVerify( new[] { source, s_collectionExtensionsWithSpan }, targetFramework: TargetFramework.Net80, verify: Verification.Skipped, expectedOutput: IncludeExpectedOutput("[1], [2], [3], [4], [5], [6], ")); verifier.VerifyIL("Program.Main", """ { // Code size 160 (0xa0) .maxstack 3 .locals init (S V_0, //s R1 V_1, //r1 R2 V_2, //r2 int? V_3, int? V_4, int? V_5, int? V_6, int? V_7, int? V_8) IL_0000: ldloca.s V_0 IL_0002: initobj "S" IL_0008: ldloca.s V_0 IL_000a: ldloca.s V_3 IL_000c: ldc.i4.1 IL_000d: call "int?..ctor(int)" IL_0012: ldloca.s V_3 IL_0014: newobj "System.ReadOnlySpan<int?>..ctor(ref readonly int?)" IL_0019: call "void S.M(System.ReadOnlySpan<int?>)" IL_001e: ldloca.s V_0 IL_0020: ldloca.s V_4 IL_0022: ldc.i4.2 IL_0023: call "int?..ctor(int)" IL_0028: ldloca.s V_4 IL_002a: newobj "System.ReadOnlySpan<int?>..ctor(ref readonly int?)" IL_002f: ldnull IL_0030: call "void S.this[System.ReadOnlySpan<int?>].set" IL_0035: ldloca.s V_1 IL_0037: initobj "R1" IL_003d: ldloca.s V_1 IL_003f: ldloca.s V_5 IL_0041: ldc.i4.3 IL_0042: call "int?..ctor(int)" IL_0047: ldloca.s V_5 IL_0049: newobj "System.ReadOnlySpan<int?>..ctor(ref readonly int?)" IL_004e: call "void R1.M(System.ReadOnlySpan<int?>)" IL_0053: ldloca.s V_1 IL_0055: ldloca.s V_6 IL_0057: ldc.i4.4 IL_0058: call "int?..ctor(int)" IL_005d: ldloca.s V_6 IL_005f: newobj "System.ReadOnlySpan<int?>..ctor(ref readonly int?)" IL_0064: ldnull IL_0065: call "void R1.this[System.ReadOnlySpan<int?>].set" IL_006a: ldloca.s V_2 IL_006c: initobj "R2" IL_0072: ldloca.s V_2 IL_0074: ldloca.s V_7 IL_0076: ldc.i4.5 IL_0077: call "int?..ctor(int)" IL_007c: ldloca.s V_7 IL_007e: newobj "System.ReadOnlySpan<int?>..ctor(ref readonly int?)" IL_0083: call "void R2.M(scoped System.ReadOnlySpan<int?>)" IL_0088: ldloca.s V_2 IL_008a: ldloca.s V_8 IL_008c: ldc.i4.6 IL_008d: call "int?..ctor(int)" IL_0092: ldloca.s V_8 IL_0094: newobj "System.ReadOnlySpan<int?>..ctor(ref readonly int?)" IL_0099: ldnull IL_009a: call "void R2.this[scoped System.ReadOnlySpan<int?>].set" IL_009f: ret } """); } [Fact] public void SpanArgument_ReadOnlyMembers() { string source = """ using System; readonly ref struct R1 { public void M(ReadOnlySpan<int?> s) { s.Report(); } public object this[ReadOnlySpan<int?> s] { get { s.Report(); return null; } } } ref struct R2 { public readonly void M(ReadOnlySpan<int?> s) { s.Report(); } public readonly object this[ReadOnlySpan<int?> s] { get { s.Report(); return null; } } } class Program { static void Main() { var r1 = new R1(); r1.M([3]); _ = r1[[4]]; var r2 = new R2(); r2.M([5]); _ = r2[[6]]; } } """; var verifier = CompileAndVerify( new[] { source, s_collectionExtensionsWithSpan }, targetFramework: TargetFramework.Net80, verify: Verification.Skipped, expectedOutput: IncludeExpectedOutput("[3], [4], [5], [6], ")); verifier.VerifyIL("Program.Main", """ { // Code size 107 (0x6b) .maxstack 3 .locals init (R1 V_0, //r1 R2 V_1, //r2 int? V_2, int? V_3, int? V_4, int? V_5) IL_0000: ldloca.s V_0 IL_0002: initobj "R1" IL_0008: ldloca.s V_0 IL_000a: ldloca.s V_2 IL_000c: ldc.i4.3 IL_000d: call "int?..ctor(int)" IL_0012: ldloca.s V_2 IL_0014: newobj "System.ReadOnlySpan<int?>..ctor(ref readonly int?)" IL_0019: call "void R1.M(System.ReadOnlySpan<int?>)" IL_001e: ldloca.s V_0 IL_0020: ldloca.s V_3 IL_0022: ldc.i4.4 IL_0023: call "int?..ctor(int)" IL_0028: ldloca.s V_3 IL_002a: newobj "System.ReadOnlySpan<int?>..ctor(ref readonly int?)" IL_002f: call "object R1.this[System.ReadOnlySpan<int?>].get" IL_0034: pop IL_0035: ldloca.s V_1 IL_0037: initobj "R2" IL_003d: ldloca.s V_1 IL_003f: ldloca.s V_4 IL_0041: ldc.i4.5 IL_0042: call "int?..ctor(int)" IL_0047: ldloca.s V_4 IL_0049: newobj "System.ReadOnlySpan<int?>..ctor(ref readonly int?)" IL_004e: call "readonly void R2.M(System.ReadOnlySpan<int?>)" IL_0053: ldloca.s V_1 IL_0055: ldloca.s V_5 IL_0057: ldc.i4.6 IL_0058: call "int?..ctor(int)" IL_005d: ldloca.s V_5 IL_005f: newobj "System.ReadOnlySpan<int?>..ctor(ref readonly int?)" IL_0064: call "readonly object R2.this[System.ReadOnlySpan<int?>].get" IL_0069: pop IL_006a: ret } """); } [Fact] public void SpanArgument_Nested() { string source = """ using System; class Program { static void Main() { F1([F1([1]) + 2]); F2([F2([2]) + 2]); } static T F1<T>(Span<T> s) { s.Report(); return s[0]; } static T F2<T>(ReadOnlySpan<T> s) { s.Report(); return s[0]; } } """; var verifier = CompileAndVerify( new[] { source, s_collectionExtensionsWithSpan }, targetFramework: TargetFramework.Net80, verify: Verification.Skipped, expectedOutput: IncludeExpectedOutput("[1], [3], [2], [4], ")); verifier.VerifyIL("Program.Main", """ { // Code size 62 (0x3e) .maxstack 2 .locals init (int V_0, int V_1, int V_2) IL_0000: ldc.i4.1 IL_0001: stloc.0 IL_0002: ldloca.s V_0 IL_0004: newobj "System.Span<int>..ctor(ref int)" IL_0009: call "int Program.F1<int>(System.Span<int>)" IL_000e: ldc.i4.2 IL_000f: add IL_0010: stloc.1 IL_0011: ldloca.s V_1 IL_0013: newobj "System.Span<int>..ctor(ref int)" IL_0018: call "int Program.F1<int>(System.Span<int>)" IL_001d: pop IL_001e: ldtoken "<PrivateImplementationDetails>.__StaticArrayInitTypeSize=4_Align=4 <PrivateImplementationDetails>.26B25D457597A7B0463F9620F666DD10AA2C4373A505967C7C8D70922A2D6ECE4" IL_0023: call "System.ReadOnlySpan<int> System.Runtime.CompilerServices.RuntimeHelpers.CreateSpan<int>(System.RuntimeFieldHandle)" IL_0028: call "int Program.F2<int>(System.ReadOnlySpan<int>)" IL_002d: ldc.i4.2 IL_002e: add IL_002f: stloc.2 IL_0030: ldloca.s V_2 IL_0032: newobj "System.ReadOnlySpan<int>..ctor(ref readonly int)" IL_0037: call "int Program.F2<int>(System.ReadOnlySpan<int>)" IL_003c: pop IL_003d: ret } """); } [Fact] public void SpanArgument_Reordered() { string source = """ using System; class Program { static void Main() { F1<object>(y: [1], x: [2]); F2<object>(y: [3], x: [4]); } static Span<T> F1<T>(Span<T> x, scoped Span<T> y) { x.Report(); y.Report(); return x; } static ReadOnlySpan<T> F2<T>(scoped ReadOnlySpan<T> x, ReadOnlySpan<T> y) { x.Report(); y.Report(); return y; } } """; var verifier = CompileAndVerify( new[] { source, s_collectionExtensionsWithSpan }, targetFramework: TargetFramework.Net80, verify: Verification.Skipped, expectedOutput: IncludeExpectedOutput("[2], [1], [4], [3], ")); verifier.VerifyIL("Program.Main", """ { // Code size 77 (0x4d) .maxstack 2 .locals init (object V_0, object V_1, object V_2, object V_3, System.Span<object> V_4, System.ReadOnlySpan<object> V_5) IL_0000: ldc.i4.1 IL_0001: box "int" IL_0006: stloc.0 IL_0007: ldloca.s V_0 IL_0009: newobj "System.Span<object>..ctor(ref object)" IL_000e: stloc.s V_4 IL_0010: ldc.i4.2 IL_0011: box "int" IL_0016: stloc.1 IL_0017: ldloca.s V_1 IL_0019: newobj "System.Span<object>..ctor(ref object)" IL_001e: ldloc.s V_4 IL_0020: call "System.Span<object> Program.F1<object>(System.Span<object>, scoped System.Span<object>)" IL_0025: pop IL_0026: ldc.i4.3 IL_0027: box "int" IL_002c: stloc.2 IL_002d: ldloca.s V_2 IL_002f: newobj "System.ReadOnlySpan<object>..ctor(ref readonly object)" IL_0034: stloc.s V_5 IL_0036: ldc.i4.4 IL_0037: box "int" IL_003c: stloc.3 IL_003d: ldloca.s V_3 IL_003f: newobj "System.ReadOnlySpan<object>..ctor(ref readonly object)" IL_0044: ldloc.s V_5 IL_0046: call "System.ReadOnlySpan<object> Program.F2<object>(scoped System.ReadOnlySpan<object>, System.ReadOnlySpan<object>)" IL_004b: pop IL_004c: ret } """); } [CombinatorialData] [Theory] public void SpanArgument_Constructor_01( [CombinatorialValues(TargetFramework.Net70, TargetFramework.Net80)] TargetFramework targetFramework, bool useScoped) { string source = $$""" using System; ref struct R<T> { public R(T x, T y, T z) : this([x, y, z]) { } public R(int x, T[] y) : this([..y]) { } public R({{(useScoped ? "scoped" : "")}} Span<T> s) { F = s.ToArray(); } public readonly T[] F; } class Program { static void Main() { R<int> x = new R<int>(1, 2, 3); R<object> y = new R<object>(new object[] { 4, 5 }); x.F.Report(); y.F.Report(); } } """; var comp = CreateCompilation( new[] { source, s_collectionExtensions }, targetFramework: targetFramework, options: TestOptions.ReleaseExe); if (!useScoped) { comp.VerifyEmitDiagnostics( // 0.cs(4,29): error CS8350: This combination of arguments to 'R<T>.R(Span<T>)' is disallowed because it may expose variables referenced by parameter 's' outside of their declaration scope // public R(T x, T y, T z) : this([x, y, z]) Diagnostic(ErrorCode.ERR_CallArgMixing, ": this([x, y, z])").WithArguments("R<T>.R(System.Span<T>)", "s").WithLocation(4, 29), // 0.cs(4,36): error CS9203: A collection expression of type 'Span<T>' cannot be used in this context because it may be exposed outside of the current scope. // public R(T x, T y, T z) : this([x, y, z]) Diagnostic(ErrorCode.ERR_CollectionExpressionEscape, "[x, y, z]").WithArguments("System.Span<T>").WithLocation(4, 36), // 0.cs(7,28): error CS8350: This combination of arguments to 'R<T>.R(Span<T>)' is disallowed because it may expose variables referenced by parameter 's' outside of their declaration scope // public R(int x, T[] y) : this([..y]) Diagnostic(ErrorCode.ERR_CallArgMixing, ": this([..y])").WithArguments("R<T>.R(System.Span<T>)", "s").WithLocation(7, 28), // 0.cs(7,35): error CS9203: A collection expression of type 'Span<T>' cannot be used in this context because it may be exposed outside of the current scope. // public R(int x, T[] y) : this([..y]) Diagnostic(ErrorCode.ERR_CollectionExpressionEscape, "[..y]").WithArguments("System.Span<T>").WithLocation(7, 35)); } else if (targetFramework == TargetFramework.Net80) { var verifier = CompileAndVerify( comp, verify: Verification.Skipped, expectedOutput: IncludeExpectedOutput("[1, 2, 3], [4, 5], ")); verifier.VerifyIL("R<T>..ctor(T, T, T)", """ { // Code size 65 (0x41) .maxstack 3 .locals init (<>y__InlineArray3<T> V_0) IL_0000: ldarg.0 IL_0001: ldloca.s V_0 IL_0003: initobj "<>y__InlineArray3<T>" IL_0009: ldloca.s V_0 IL_000b: ldc.i4.0 IL_000c: call "ref T <PrivateImplementationDetails>.InlineArrayElementRef<<>y__InlineArray3<T>, T>(ref <>y__InlineArray3<T>, int)" IL_0011: ldarg.1 IL_0012: stobj "T" IL_0017: ldloca.s V_0 IL_0019: ldc.i4.1 IL_001a: call "ref T <PrivateImplementationDetails>.InlineArrayElementRef<<>y__InlineArray3<T>, T>(ref <>y__InlineArray3<T>, int)" IL_001f: ldarg.2 IL_0020: stobj "T" IL_0025: ldloca.s V_0 IL_0027: ldc.i4.2 IL_0028: call "ref T <PrivateImplementationDetails>.InlineArrayElementRef<<>y__InlineArray3<T>, T>(ref <>y__InlineArray3<T>, int)" IL_002d: ldarg.3 IL_002e: stobj "T" IL_0033: ldloca.s V_0 IL_0035: ldc.i4.3 IL_0036: call "System.Span<T> <PrivateImplementationDetails>.InlineArrayAsSpan<<>y__InlineArray3<T>, T>(ref <>y__InlineArray3<T>, int)" IL_003b: call "R<T>..ctor(scoped System.Span<T>)" IL_0040: ret } """); verifier.VerifyIL("R<T>..ctor(int, T[])", """ { // Code size 18 (0x12) .maxstack 2 IL_0000: ldarg.0 IL_0001: ldarg.2 IL_0002: call "T[] System.Linq.Enumerable.ToArray<T>(System.Collections.Generic.IEnumerable<T>)" IL_0007: newobj "System.Span<T>..ctor(T[])" IL_000c: call "R<T>..ctor(scoped System.Span<T>)" IL_0011: ret } """); } } [Fact] public void SpanArgument_Constructor_02() { string source = """ using System; record class A<T>(T[] F) { public static T[] ToArray(ReadOnlySpan<T> s) => s.ToArray(); } record class B<T>(T x, T y, T z) : A<T>(ToArray([x, y, z])); class Program { static void Main() { object[] a = F<object>(1, 2, 3); a.Report(); } static T[] F<T>(T x, T y, T z) { B<T> b = new B<T>(x, y, z); return b.F; } } """; var verifier = CompileAndVerify( new[] { source, s_collectionExtensions }, targetFramework: TargetFramework.Net80, verify: Verification.Skipped, expectedOutput: IncludeExpectedOutput("[1, 2, 3], ")); verifier.VerifyIL("B<T>..ctor(T, T, T)", """ { // Code size 91 (0x5b) .maxstack 3 .locals init (<>y__InlineArray3<T> V_0) IL_0000: ldarg.0 IL_0001: ldarg.1 IL_0002: stfld "T B<T>.<x>k__BackingField" IL_0007: ldarg.0 IL_0008: ldarg.2 IL_0009: stfld "T B<T>.<y>k__BackingField" IL_000e: ldarg.0 IL_000f: ldarg.3 IL_0010: stfld "T B<T>.<z>k__BackingField" IL_0015: ldarg.0 IL_0016: ldloca.s V_0 IL_0018: initobj "<>y__InlineArray3<T>" IL_001e: ldloca.s V_0 IL_0020: ldc.i4.0 IL_0021: call "ref T <PrivateImplementationDetails>.InlineArrayElementRef<<>y__InlineArray3<T>, T>(ref <>y__InlineArray3<T>, int)" IL_0026: ldarg.1 IL_0027: stobj "T" IL_002c: ldloca.s V_0 IL_002e: ldc.i4.1 IL_002f: call "ref T <PrivateImplementationDetails>.InlineArrayElementRef<<>y__InlineArray3<T>, T>(ref <>y__InlineArray3<T>, int)" IL_0034: ldarg.2 IL_0035: stobj "T" IL_003a: ldloca.s V_0 IL_003c: ldc.i4.2 IL_003d: call "ref T <PrivateImplementationDetails>.InlineArrayElementRef<<>y__InlineArray3<T>, T>(ref <>y__InlineArray3<T>, int)" IL_0042: ldarg.3 IL_0043: stobj "T" IL_0048: ldloca.s V_0 IL_004a: ldc.i4.3 IL_004b: call "System.ReadOnlySpan<T> <PrivateImplementationDetails>.InlineArrayAsReadOnlySpan<<>y__InlineArray3<T>, T>(in <>y__InlineArray3<T>, int)" IL_0050: call "T[] A<T>.ToArray(System.ReadOnlySpan<T>)" IL_0055: call "A<T>..ctor(T[])" IL_005a: ret } """); } [CombinatorialData] [Theory] public void SpanAssignment_01( [CombinatorialValues(TargetFramework.Net70, TargetFramework.Net80)] TargetFramework targetFramework, [CombinatorialValues("Span<object>", "ReadOnlySpan<object>")] string spanType) { string source = $$""" using System; class Program { static {{spanType}} F1() { {{spanType}} s1 = []; return s1; } static {{spanType}} F2() { {{spanType}} s2 = [2]; return s2; } static {{spanType}} F3() { {{spanType}} s3; s3 = [3]; return s3; } } """; var comp = CreateCompilation(source, targetFramework: targetFramework); comp.VerifyEmitDiagnostics( // (12,16): error CS8352: Cannot use variable 's2' in this context because it may expose referenced variables outside of their declaration scope // return s2; Diagnostic(ErrorCode.ERR_EscapeVariable, "s2").WithArguments("s2").WithLocation(12, 16), // (17,14): error CS9203: A collection expression of type 'Span<object>' cannot be used in this context because it may be exposed outside of the current scope. // s3 = [3]; Diagnostic(ErrorCode.ERR_CollectionExpressionEscape, "[3]").WithArguments($"System.{spanType}").WithLocation(17, 14)); } [Fact] public void SpanAssignment_02() { string source = """ using System; class Program { static void Main() { F1().Report(); F2().Report(); } static object[] F1() { Span<object> s1 = [1]; return s1.ToArray(); } static object[] F2() { ReadOnlySpan<object> s2 = [2]; return s2.ToArray(); } } """; var verifier = CompileAndVerify( new[] { source, s_collectionExtensionsWithSpan }, targetFramework: TargetFramework.Net80, verify: Verification.Skipped, expectedOutput: IncludeExpectedOutput("[1], [2], ")); verifier.VerifyIL("Program.F1", """ { // Code size 23 (0x17) .maxstack 1 .locals init (System.Span<object> V_0, //s1 object V_1) IL_0000: ldc.i4.1 IL_0001: box "int" IL_0006: stloc.1 IL_0007: ldloca.s V_1 IL_0009: newobj "System.Span<object>..ctor(ref object)" IL_000e: stloc.0 IL_000f: ldloca.s V_0 IL_0011: call "object[] System.Span<object>.ToArray()" IL_0016: ret } """); verifier.VerifyIL("Program.F2", """ { // Code size 23 (0x17) .maxstack 1 .locals init (System.ReadOnlySpan<object> V_0, //s2 object V_1) IL_0000: ldc.i4.2 IL_0001: box "int" IL_0006: stloc.1 IL_0007: ldloca.s V_1 IL_0009: newobj "System.ReadOnlySpan<object>..ctor(ref readonly object)" IL_000e: stloc.0 IL_000f: ldloca.s V_0 IL_0011: call "object[] System.ReadOnlySpan<object>.ToArray()" IL_0016: ret } """); } [CombinatorialData] [Theory] public void SpanAssignment_03([CombinatorialValues(TargetFramework.Net70, TargetFramework.Net80)] TargetFramework targetFramework) { string source = """ using System; class Program { static void Main() { scoped Span<object> x; scoped ReadOnlySpan<object> y; x = [1]; y = [2]; x.Report(); y.Report(); } } """; var verifier = CompileAndVerify( new[] { source, s_collectionExtensionsWithSpan }, targetFramework: targetFramework, verify: Verification.Skipped, expectedOutput: IncludeExpectedOutput("[1], [2], ")); if (targetFramework == TargetFramework.Net80) { verifier.VerifyIL("Program.Main", """ { // Code size 50 (0x32) .maxstack 2 .locals init (System.ReadOnlySpan<object> V_0, //y object V_1, object V_2, System.ReadOnlySpan<object> V_3) IL_0000: ldc.i4.1 IL_0001: box "int" IL_0006: stloc.1 IL_0007: ldloca.s V_1 IL_0009: newobj "System.Span<object>..ctor(ref object)" IL_000e: ldc.i4.2 IL_000f: box "int" IL_0014: stloc.2 IL_0015: ldloca.s V_2 IL_0017: newobj "System.ReadOnlySpan<object>..ctor(ref readonly object)" IL_001c: stloc.0 IL_001d: call "System.ReadOnlySpan<object> System.Span<object>.op_Implicit(System.Span<object>)" IL_0022: stloc.3 IL_0023: ldloca.s V_3 IL_0025: call "void CollectionExtensions.Report<object>(in System.ReadOnlySpan<object>)" IL_002a: ldloca.s V_0 IL_002c: call "void CollectionExtensions.Report<object>(in System.ReadOnlySpan<object>)" IL_0031: ret } """); } else { verifier.VerifyIL("Program.Main", """ { // Code size 66 (0x42) .maxstack 5 .locals init (System.Span<object> V_0, //x System.ReadOnlySpan<object> V_1, //y System.ReadOnlySpan<object> V_2) IL_0000: ldloca.s V_0 IL_0002: ldc.i4.1 IL_0003: newarr "object" IL_0008: dup IL_0009: ldc.i4.0 IL_000a: ldc.i4.1 IL_000b: box "int" IL_0010: stelem.ref IL_0011: call "System.Span<object>..ctor(object[])" IL_0016: ldloca.s V_1 IL_0018: ldc.i4.1 IL_0019: newarr "object" IL_001e: dup IL_001f: ldc.i4.0 IL_0020: ldc.i4.2 IL_0021: box "int" IL_0026: stelem.ref IL_0027: call "System.ReadOnlySpan<object>..ctor(object[])" IL_002c: ldloc.0 IL_002d: call "System.ReadOnlySpan<object> System.Span<object>.op_Implicit(System.Span<object>)" IL_0032: stloc.2 IL_0033: ldloca.s V_2 IL_0035: call "void CollectionExtensions.Report<object>(in System.ReadOnlySpan<object>)" IL_003a: ldloca.s V_1 IL_003c: call "void CollectionExtensions.Report<object>(in System.ReadOnlySpan<object>)" IL_0041: ret } """); } } [Fact] public void SpanAssignment_Field_01() { string source = """ using System; ref struct R<T> { public ReadOnlySpan<T> F; } class Program { static void Main() { R<object> r = default; r.F = [1]; } } """; var comp = CreateCompilation(source, targetFramework: TargetFramework.Net80); comp.VerifyEmitDiagnostics( // (11,15): error CS9203: A collection expression of type 'ReadOnlySpan<object>' cannot be used in this context because it may be exposed outside of the current scope. // r.F = [1]; Diagnostic(ErrorCode.ERR_CollectionExpressionEscape, "[1]").WithArguments("System.ReadOnlySpan<object>").WithLocation(11, 15)); } [Fact] public void SpanAssignment_Field_02() { string source = """ using System; ref struct R<T> { public ReadOnlySpan<T> F; } class Program { static void Main() { scoped R<object> x = default; scoped R<object> y = default; x.F = [1]; y.F = [2]; x.F.Report(); y.F.Report(); } } """; var verifier = CompileAndVerify( new[] { source, s_collectionExtensionsWithSpan }, targetFramework: TargetFramework.Net80, verify: Verification.Skipped, expectedOutput: IncludeExpectedOutput("[1], [2], ")); verifier.VerifyIL("Program.Main", """ { // Code size 83 (0x53) .maxstack 2 .locals init (R<object> V_0, //x R<object> V_1, //y object V_2, object V_3) IL_0000: ldloca.s V_0 IL_0002: initobj "R<object>" IL_0008: ldloca.s V_1 IL_000a: initobj "R<object>" IL_0010: ldloca.s V_0 IL_0012: ldc.i4.1 IL_0013: box "int" IL_0018: stloc.2 IL_0019: ldloca.s V_2 IL_001b: newobj "System.ReadOnlySpan<object>..ctor(ref readonly object)" IL_0020: stfld "System.ReadOnlySpan<object> R<object>.F" IL_0025: ldloca.s V_1 IL_0027: ldc.i4.2 IL_0028: box "int" IL_002d: stloc.3 IL_002e: ldloca.s V_3 IL_0030: newobj "System.ReadOnlySpan<object>..ctor(ref readonly object)" IL_0035: stfld "System.ReadOnlySpan<object> R<object>.F" IL_003a: ldloca.s V_0 IL_003c: ldflda "System.ReadOnlySpan<object> R<object>.F" IL_0041: call "void CollectionExtensions.Report<object>(in System.ReadOnlySpan<object>)" IL_0046: ldloca.s V_1 IL_0048: ldflda "System.ReadOnlySpan<object> R<object>.F" IL_004d: call "void CollectionExtensions.Report<object>(in System.ReadOnlySpan<object>)" IL_0052: ret } """); } [Fact] public void SpanAssignment_FieldInitializer_01() { string source = """ using System; ref struct R { public ReadOnlySpan<object> F = [1, 2, 3]; public R() { } } """; var comp = CreateCompilation(source, targetFramework: TargetFramework.Net80); comp.VerifyEmitDiagnostics( // (4,37): error CS9203: A collection expression of type 'ReadOnlySpan<object>' cannot be used in this context because it may be exposed outside of the current scope. // public ReadOnlySpan<object> F = [1, 2, 3]; Diagnostic(ErrorCode.ERR_CollectionExpressionEscape, "[1, 2, 3]").WithArguments("System.ReadOnlySpan<object>").WithLocation(4, 37)); } [Fact] public void SpanAssignment_FieldInitializer_02() { string source = """ using System; class Program { static T[] FromSpan<T>(Span<T> s) => s.ToArray(); static int[] F = FromSpan([1, 2, 3]); static void Main() { F.Report(); } } """; var verifier = CompileAndVerify( new[] { source, s_collectionExtensionsWithSpan }, targetFramework: TargetFramework.Net80, verify: Verification.Skipped, expectedOutput: IncludeExpectedOutput("[1, 2, 3], ")); verifier.VerifyIL("Program..cctor", """ { // Code size 57 (0x39) .maxstack 2 .locals init (<>y__InlineArray3<int> V_0) IL_0000: ldloca.s V_0 IL_0002: initobj "<>y__InlineArray3<int>" IL_0008: ldloca.s V_0 IL_000a: ldc.i4.0 IL_000b: call "ref int <PrivateImplementationDetails>.InlineArrayElementRef<<>y__InlineArray3<int>, int>(ref <>y__InlineArray3<int>, int)" IL_0010: ldc.i4.1 IL_0011: stind.i4 IL_0012: ldloca.s V_0 IL_0014: ldc.i4.1 IL_0015: call "ref int <PrivateImplementationDetails>.InlineArrayElementRef<<>y__InlineArray3<int>, int>(ref <>y__InlineArray3<int>, int)" IL_001a: ldc.i4.2 IL_001b: stind.i4 IL_001c: ldloca.s V_0 IL_001e: ldc.i4.2 IL_001f: call "ref int <PrivateImplementationDetails>.InlineArrayElementRef<<>y__InlineArray3<int>, int>(ref <>y__InlineArray3<int>, int)" IL_0024: ldc.i4.3 IL_0025: stind.i4 IL_0026: ldloca.s V_0 IL_0028: ldc.i4.3 IL_0029: call "System.Span<int> <PrivateImplementationDetails>.InlineArrayAsSpan<<>y__InlineArray3<int>, int>(ref <>y__InlineArray3<int>, int)" IL_002e: call "int[] Program.FromSpan<int>(System.Span<int>)" IL_0033: stsfld "int[] Program.F" IL_0038: ret } """); } [Fact] public void SpanAssignment_FieldInitializer_03() { string source = """ using System; class C { static T[] FromSpan<T>(ReadOnlySpan<T> s) => s.ToArray(); public object[] F = FromSpan<object>([1, 2, 3]); } class Program { static void Main() { (new C()).F.Report(); } } """; var verifier = CompileAndVerify( new[] { source, s_collectionExtensionsWithSpan }, targetFramework: TargetFramework.Net80, verify: Verification.Skipped, expectedOutput: IncludeExpectedOutput("[1, 2, 3], ")); verifier.VerifyIL("C..ctor", """ { // Code size 79 (0x4f) .maxstack 3 .locals init (<>y__InlineArray3<object> V_0) IL_0000: ldarg.0 IL_0001: ldloca.s V_0 IL_0003: initobj "<>y__InlineArray3<object>" IL_0009: ldloca.s V_0 IL_000b: ldc.i4.0 IL_000c: call "ref object <PrivateImplementationDetails>.InlineArrayElementRef<<>y__InlineArray3<object>, object>(ref <>y__InlineArray3<object>, int)" IL_0011: ldc.i4.1 IL_0012: box "int" IL_0017: stind.ref IL_0018: ldloca.s V_0 IL_001a: ldc.i4.1 IL_001b: call "ref object <PrivateImplementationDetails>.InlineArrayElementRef<<>y__InlineArray3<object>, object>(ref <>y__InlineArray3<object>, int)" IL_0020: ldc.i4.2 IL_0021: box "int" IL_0026: stind.ref IL_0027: ldloca.s V_0 IL_0029: ldc.i4.2 IL_002a: call "ref object <PrivateImplementationDetails>.InlineArrayElementRef<<>y__InlineArray3<object>, object>(ref <>y__InlineArray3<object>, int)" IL_002f: ldc.i4.3 IL_0030: box "int" IL_0035: stind.ref IL_0036: ldloca.s V_0 IL_0038: ldc.i4.3 IL_0039: call "System.ReadOnlySpan<object> <PrivateImplementationDetails>.InlineArrayAsReadOnlySpan<<>y__InlineArray3<object>, object>(in <>y__InlineArray3<object>, int)" IL_003e: call "object[] C.FromSpan<object>(System.ReadOnlySpan<object>)" IL_0043: stfld "object[] C.F" IL_0048: ldarg.0 IL_0049: call "object..ctor()" IL_004e: ret } """); } [Fact] public void SpanAssignment_FieldInitializer_04() { string source = """ using System; struct S { static T[] FromSpan<T>(ReadOnlySpan<T> s) => s.ToArray(); public object[] F = FromSpan<object>([1, 2, 3]); public S() { } } class Program { static void Main() { (new S()).F.Report(); } } """; var verifier = CompileAndVerify( new[] { source, s_collectionExtensionsWithSpan }, targetFramework: TargetFramework.Net80, verify: Verification.Skipped, expectedOutput: IncludeExpectedOutput("[1, 2, 3], ")); verifier.VerifyIL("S..ctor", """ { // Code size 73 (0x49) .maxstack 3 .locals init (<>y__InlineArray3<object> V_0) IL_0000: ldarg.0 IL_0001: ldloca.s V_0 IL_0003: initobj "<>y__InlineArray3<object>" IL_0009: ldloca.s V_0 IL_000b: ldc.i4.0 IL_000c: call "ref object <PrivateImplementationDetails>.InlineArrayElementRef<<>y__InlineArray3<object>, object>(ref <>y__InlineArray3<object>, int)" IL_0011: ldc.i4.1 IL_0012: box "int" IL_0017: stind.ref IL_0018: ldloca.s V_0 IL_001a: ldc.i4.1 IL_001b: call "ref object <PrivateImplementationDetails>.InlineArrayElementRef<<>y__InlineArray3<object>, object>(ref <>y__InlineArray3<object>, int)" IL_0020: ldc.i4.2 IL_0021: box "int" IL_0026: stind.ref IL_0027: ldloca.s V_0 IL_0029: ldc.i4.2 IL_002a: call "ref object <PrivateImplementationDetails>.InlineArrayElementRef<<>y__InlineArray3<object>, object>(ref <>y__InlineArray3<object>, int)" IL_002f: ldc.i4.3 IL_0030: box "int" IL_0035: stind.ref IL_0036: ldloca.s V_0 IL_0038: ldc.i4.3 IL_0039: call "System.ReadOnlySpan<object> <PrivateImplementationDetails>.InlineArrayAsReadOnlySpan<<>y__InlineArray3<object>, object>(in <>y__InlineArray3<object>, int)" IL_003e: call "object[] S.FromSpan<object>(System.ReadOnlySpan<object>)" IL_0043: stfld "object[] S.F" IL_0048: ret } """); } [CombinatorialData] [Theory] public void SpanAssignment_RefLocal([CombinatorialValues(TargetFramework.Net70, TargetFramework.Net80)] TargetFramework targetFramework) { string source = """ using System; class Program { static Span<object> F() { Span<object> s = default; ref Span<object> r = ref s; r = new Span<object>(new object[] { 1 }); r = [1]; return r; } } """; var comp = CreateCompilation(source, targetFramework: targetFramework); comp.VerifyEmitDiagnostics( // (9,13): error CS9203: A collection expression of type 'Span<object>' cannot be used in this context because it may be exposed outside of the current scope. // r = [1]; Diagnostic(ErrorCode.ERR_CollectionExpressionEscape, "[1]").WithArguments("System.Span<object>").WithLocation(9, 13)); } [CombinatorialData] [Theory] public void SpanAssignment_NestedScope_01([CombinatorialValues(TargetFramework.Net70, TargetFramework.Net80)] TargetFramework targetFramework) { string source = """ using System; class Program { static void Main() { F(false); F(true); } static void F(bool b) { ReadOnlySpan<object> x = [1]; if (b) { x = [2]; } else { ReadOnlySpan<object> y = [3]; x = y; } ReadOnlySpan<object> z = [4]; x = z; } } """; var comp = CreateCompilation(source, targetFramework: targetFramework); comp.VerifyEmitDiagnostics( // (14,17): error CS9203: A collection expression of type 'ReadOnlySpan<object>' cannot be used in this context because it may be exposed outside of the current scope. // x = [2]; Diagnostic(ErrorCode.ERR_CollectionExpressionEscape, "[2]").WithArguments("System.ReadOnlySpan<object>").WithLocation(14, 17), // (19,17): error CS8352: Cannot use variable 'y' in this context because it may expose referenced variables outside of their declaration scope // x = y; Diagnostic(ErrorCode.ERR_EscapeVariable, "y").WithArguments("y").WithLocation(19, 17)); } [Fact] public void SpanAssignment_NestedScope_02() { string source = """ using System; class Program { static void Main() { M(true, 1, 2, 3, 4); } static void M<T>(bool b, T x, T y, T z, T w) { scoped Span<T> s = default; if (b) { s = [x, y, z]; } if (b) { s = [z, w]; } s.Report(); } } """; var comp = CreateCompilation(new[] { source, s_collectionExtensionsWithSpan }, targetFramework: TargetFramework.Net80); comp.VerifyEmitDiagnostics( // (13,17): error CS9203: A collection expression of type 'Span<T>' cannot be used in this context because it may be exposed outside of the current scope. // s = [x, y, z]; Diagnostic(ErrorCode.ERR_CollectionExpressionEscape, "[x, y, z]").WithArguments("System.Span<T>").WithLocation(13, 17), // (17,17): error CS9203: A collection expression of type 'Span<T>' cannot be used in this context because it may be exposed outside of the current scope. // s = [z, w]; Diagnostic(ErrorCode.ERR_CollectionExpressionEscape, "[z, w]").WithArguments("System.Span<T>").WithLocation(17, 17)); } [Fact] public void SpanAssignment_NestedScope_03() { string source = """ using System; class Program { static void Main() { M<object>(true, [1, null, 3]); } static void M<T>(bool b, T[] a) { scoped Span<T> s = default; if (b) { s = [..a]; } s.Report(); } } """; var comp = CreateCompilation(new[] { source, s_collectionExtensionsWithSpan }, targetFramework: TargetFramework.Net80); comp.VerifyEmitDiagnostics( // (13,17): error CS9203: A collection expression of type 'Span<T>' cannot be used in this context because it may be exposed outside of the current scope. // s = [..a]; Diagnostic(ErrorCode.ERR_CollectionExpressionEscape, "[..a]").WithArguments("System.Span<T>").WithLocation(13, 17)); } [Fact] public void SpanAssignment_NestedScope_04() { string source = """ using System; class Program { static void Main() { F<object>()(true, 1, null, 3); } static Action<bool, T, T, T> F<T>() { return (bool b, T x, T y, T z) => { scoped Span<T> s1 = default; if (b) { Span<T> s2 = [x, y, z]; s1 = s2; } s1.Report(); }; } } """; var comp = CreateCompilation(new[] { source, s_collectionExtensionsWithSpan }, targetFramework: TargetFramework.Net80); comp.VerifyEmitDiagnostics( // (16,26): error CS8352: Cannot use variable 's2' in this context because it may expose referenced variables outside of their declaration scope // s1 = s2; Diagnostic(ErrorCode.ERR_EscapeVariable, "s2").WithArguments("s2").WithLocation(16, 26)); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/80107")] public void SpanAssignment_NestedScope_IssueExample() { // Test case from the issue description string source = """ using System; class Program { static void Main() { scoped Span<int> items1 = default; scoped Span<int> items2 = default; foreach (var x in new[] { 1, 2 }) { Span<int> items = [x]; if (x == 1) items1 = items; if (x == 2) items2 = items; } Console.Write(items1[0]); Console.Write(items2[0]); } } """; var comp = CreateCompilation(source, targetFramework: TargetFramework.Net80); comp.VerifyEmitDiagnostics( // (12,26): error CS8352: Cannot use variable 'items' in this context because it may expose referenced variables outside of their declaration scope // items1 = items; Diagnostic(ErrorCode.ERR_EscapeVariable, "items").WithArguments("items").WithLocation(12, 26), // (15,26): error CS8352: Cannot use variable 'items' in this context because it may expose referenced variables outside of their declaration scope // items2 = items; Diagnostic(ErrorCode.ERR_EscapeVariable, "items").WithArguments("items").WithLocation(15, 26)); } [Fact] public void SpanAssignment_NestedScope_05() { string source = """ using System; class Program { static void Main() { M<object>(1, 2, 3, 4); } static void M<T>(T x, T y, T z, T w) { scoped Span<T> s1; s1 = [x]; s1.Report(); Action a1 = () => { scoped Span<T> s2; s2 = [y]; s2.Report(); void A2() { scoped Span<T> s3; s3 = [z]; s3.Report(); } A2(); s2 = [w]; s2.Report(); }; a1(); s1 = [x]; s1.Report(); } } """; var verifier = CompileAndVerify( new[] { source, s_collectionExtensionsWithSpan }, targetFramework: TargetFramework.Net80, verify: Verification.Skipped, expectedOutput: IncludeExpectedOutput("[1], [2], [3], [4], [1], ")); verifier.VerifyIL("Program.<>c__DisplayClass1_0<T>.<M>g__A2|1()", """ { // Code size 28 (0x1c) .maxstack 1 .locals init (T V_0, System.ReadOnlySpan<T> V_1) IL_0000: ldarg.0 IL_0001: ldfld "T Program.<>c__DisplayClass1_0<T>.z" IL_0006: stloc.0 IL_0007: ldloca.s V_0 IL_0009: newobj "System.Span<T>..ctor(ref T)" IL_000e: call "System.ReadOnlySpan<T> System.Span<T>.op_Implicit(System.Span<T>)" IL_0013: stloc.1 IL_0014: ldloca.s V_1 IL_0016: call "void CollectionExtensions.Report<T>(in System.ReadOnlySpan<T>)" IL_001b: ret } """); } [Fact] public void SpanAssignment_NestedScope_06() { string source = """ using System; class C<T> { public Action<T, T> F = (T x, T y) => { scoped ReadOnlySpan<T> r1; Action<T> a = (T z) => { scoped ReadOnlySpan<T> r2; r2 = [z]; r2.Report(); }; a(y); r1 = [x]; r1.Report(); }; } class Program { static void Main() { var c = new C<string>(); c.F("a", "b"); } } """; var verifier = CompileAndVerify( new[] { source, s_collectionExtensionsWithSpan }, targetFramework: TargetFramework.Net80, verify: Verification.Skipped, expectedOutput: IncludeExpectedOutput("[b], [a], ")); verifier.VerifyIL("C<T>.<>c.<.ctor>b__1_0(T, T)", """ { // Code size 55 (0x37) .maxstack 2 .locals init (System.ReadOnlySpan<T> V_0, //r1 T V_1) IL_0000: ldsfld "System.Action<T> C<T>.<>c.<>9__1_1" IL_0005: dup IL_0006: brtrue.s IL_001f IL_0008: pop IL_0009: ldsfld "C<T>.<>c C<T>.<>c.<>9" IL_000e: ldftn "void C<T>.<>c.<.ctor>b__1_1(T)" IL_0014: newobj "System.Action<T>..ctor(object, System.IntPtr)" IL_0019: dup IL_001a: stsfld "System.Action<T> C<T>.<>c.<>9__1_1" IL_001f: ldarg.2 IL_0020: callvirt "void System.Action<T>.Invoke(T)" IL_0025: ldarg.1 IL_0026: stloc.1 IL_0027: ldloca.s V_1 IL_0029: newobj "System.ReadOnlySpan<T>..ctor(ref readonly T)" IL_002e: stloc.0 IL_002f: ldloca.s V_0 IL_0031: call "void CollectionExtensions.Report<T>(in System.ReadOnlySpan<T>)" IL_0036: ret } """); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/80745")] public void SpanAssignment_ScopedParameter_Constructor_Span() { string source = """ using System; class C { public C(scoped ReadOnlySpan<int> items) { int x = 0; items = new ReadOnlySpan<int>(ref x); } void M(scoped ReadOnlySpan<int> items) { int x = 0; items = new ReadOnlySpan<int>(ref x); } } """; var comp = CreateCompilation(source, targetFramework: TargetFramework.Net80); comp.VerifyEmitDiagnostics(); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/80745")] public void SpanAssignment_ScopedParameter_Constructor_CollectionExpression() { string source = """ using System; class C { public C(scoped ReadOnlySpan<int> items) { int x = 0; items = [x]; } void M(scoped ReadOnlySpan<int> items) { int x = 0; items = [x]; } } """; var comp = CreateCompilation(source, targetFramework: TargetFramework.Net80); comp.VerifyEmitDiagnostics(); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/80745")] public void SpanAssignment_ScopedParameter_PrimaryConstructor_Span() { // Each field initializer can be considered its own local scope as variables declared within it are not visible to the other initializers. string source = """ using System; class C(scoped ReadOnlySpan<int> items) // 1 { private int x = M0(M1(out int x1), items = new ReadOnlySpan<int>(ref x1)); // 2, 3 private int y = x1; // 4 static int M0(int x0, ReadOnlySpan<int> items) => items[0]; static int M1(out int x1) { return x1 = 0; } } """; var comp = CreateCompilation(source, targetFramework: TargetFramework.Net80); comp.VerifyEmitDiagnostics( // (3,34): warning CS9113: Parameter 'items' is unread. // class C(scoped ReadOnlySpan<int> items) // 1 Diagnostic(ErrorCode.WRN_UnreadPrimaryConstructorParameter, "items").WithArguments("items").WithLocation(3, 34), // (5,48): error CS8347: Cannot use a result of 'ReadOnlySpan<int>.ReadOnlySpan(ref readonly int)' in this context because it may expose variables referenced by parameter 'reference' outside of their declaration scope // private int x = M0(M1(out int x1), items = new ReadOnlySpan<int>(ref x1)); // 2, 3 Diagnostic(ErrorCode.ERR_EscapeCall, "new ReadOnlySpan<int>(ref x1)").WithArguments("System.ReadOnlySpan<int>.ReadOnlySpan(ref readonly int)", "reference").WithLocation(5, 48), // (5,74): error CS8352: Cannot use variable 'x1' in this context because it may expose referenced variables outside of their declaration scope // private int x = M0(M1(out int x1), items = new ReadOnlySpan<int>(ref x1)); // 2, 3 Diagnostic(ErrorCode.ERR_EscapeVariable, "x1").WithArguments("x1").WithLocation(5, 74), // (6,21): error CS0103: The name 'x1' does not exist in the current context // private int y = x1; // 4 Diagnostic(ErrorCode.ERR_NameNotInContext, "x1").WithArguments("x1").WithLocation(6, 21)); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/80745")] public void SpanAssignment_StaticFieldInitializer() { // Each field initializer can be considered its own local scope as variables declared within it are not visible to the other initializers. string source = """ using System; class C { private static int x = M0(M1(out ReadOnlySpan<int> items, out int x1), items = new ReadOnlySpan<int>(ref x1)); // 1, 2 private static int y = items[0]; // 3 static int M0(int x0, ReadOnlySpan<int> items) => items[0]; static int M1(out ReadOnlySpan<int> span, out int x1) { span = default; return x1 = 0; } } """; var comp = CreateCompilation(source, targetFramework: TargetFramework.Net80); comp.VerifyEmitDiagnostics( // (5,84): error CS8347: Cannot use a result of 'ReadOnlySpan<int>.ReadOnlySpan(ref readonly int)' in this context because it may expose variables referenced by parameter 'reference' outside of their declaration scope // private static int x = M0(M1(out ReadOnlySpan<int> items, out int x1), items = new ReadOnlySpan<int>(ref x1)); // 1, 2 Diagnostic(ErrorCode.ERR_EscapeCall, "new ReadOnlySpan<int>(ref x1)").WithArguments("System.ReadOnlySpan<int>.ReadOnlySpan(ref readonly int)", "reference").WithLocation(5, 84), // (5,110): error CS8168: Cannot return local 'x1' by reference because it is not a ref local // private static int x = M0(M1(out ReadOnlySpan<int> items, out int x1), items = new ReadOnlySpan<int>(ref x1)); // 1, 2 Diagnostic(ErrorCode.ERR_RefReturnLocal, "x1").WithArguments("x1").WithLocation(5, 110), // (6,28): error CS0103: The name 'items' does not exist in the current context // private static int y = items[0]; // 3 Diagnostic(ErrorCode.ERR_NameNotInContext, "items").WithArguments("items").WithLocation(6, 28)); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/80745")] public void SpanAssignment_ScopedParameter_PrimaryConstructor_CollectionExpression() { // '[M1()]' cannot be assigned to 'items' because its backing storage lives in a narrower scope than 'items'. string source = """ using System; class C(scoped ReadOnlySpan<int> items) { private int x = M0(items = [M1()]); static int M0(ReadOnlySpan<int> x) => x[0]; static int M1() => 0; } """; var comp = CreateCompilation(source, targetFramework: TargetFramework.Net80); comp.VerifyEmitDiagnostics( // (3,34): warning CS9113: Parameter 'items' is unread. // class C(scoped ReadOnlySpan<int> items) Diagnostic(ErrorCode.WRN_UnreadPrimaryConstructorParameter, "items").WithArguments("items").WithLocation(3, 34), // (5,32): error CS9203: A collection expression of type 'ReadOnlySpan<int>' cannot be used in this context because it may be exposed outside of the current scope. // private int x = M0(items = [M1()]); Diagnostic(ErrorCode.ERR_CollectionExpressionEscape, "[M1()]").WithArguments("System.ReadOnlySpan<int>").WithLocation(5, 32) ); } [Fact] public void SpanAssignment_WithUsingDeclaration() { string source = """ using System; class Disposable : IDisposable { void IDisposable.Dispose() { Console.Write("Disposed, "); } } class Program { static void Main() { ReadOnlySpan<object> x = [1]; using var d = new Disposable(); ReadOnlySpan<object> y = [2]; x.Report(); y.Report(); } } """; var verifier = CompileAndVerify( new[] { source, s_collectionExtensionsWithSpan }, targetFramework: TargetFramework.Net80, verify: Verification.Skipped, expectedOutput: IncludeExpectedOutput("[1], [2], Disposed, ")); verifier.VerifyIL("Program.Main", """ { // Code size 64 (0x40) .maxstack 1 .locals init (System.ReadOnlySpan<object> V_0, //x Disposable V_1, //d System.ReadOnlySpan<object> V_2, //y object V_3, object V_4) IL_0000: ldc.i4.1 IL_0001: box "int" IL_0006: stloc.3 IL_0007: ldloca.s V_3 IL_0009: newobj "System.ReadOnlySpan<object>..ctor(ref readonly object)" IL_000e: stloc.0 IL_000f: newobj "Disposable..ctor()" IL_0014: stloc.1 .try { IL_0015: ldc.i4.2 IL_0016: box "int" IL_001b: stloc.s V_4 IL_001d: ldloca.s V_4 IL_001f: newobj "System.ReadOnlySpan<object>..ctor(ref readonly object)" IL_0024: stloc.2 IL_0025: ldloca.s V_0 IL_0027: call "void CollectionExtensions.Report<object>(in System.ReadOnlySpan<object>)" IL_002c: ldloca.s V_2 IL_002e: call "void CollectionExtensions.Report<object>(in System.ReadOnlySpan<object>)" IL_0033: leave.s IL_003f } finally { IL_0035: ldloc.1 IL_0036: brfalse.s IL_003e IL_0038: ldloc.1 IL_0039: callvirt "void System.IDisposable.Dispose()" IL_003e: endfinally } IL_003f: ret } """); } [Fact] public void SpanUpdate() { string source = """ using System; class Program { static void Main() { Span<object> x = [1, 2]; x[0] = null; Span<int> y = [3, 4]; y[1] = 5; x.Report(); y.Report(); } } """; var verifier = CompileAndVerify( new[] { source, s_collectionExtensionsWithSpan }, targetFramework: TargetFramework.Net80, verify: Verification.Skipped, expectedOutput: IncludeExpectedOutput("[null, 2], [3, 5], ")); verifier.VerifyIL("Program.Main", """ { // Code size 135 (0x87) .maxstack 2 .locals init (System.Span<object> V_0, //x System.Span<int> V_1, //y <>y__InlineArray2<object> V_2, <>y__InlineArray2<int> V_3, System.ReadOnlySpan<object> V_4, System.ReadOnlySpan<int> V_5) IL_0000: ldloca.s V_2 IL_0002: initobj "<>y__InlineArray2<object>" IL_0008: ldloca.s V_2 IL_000a: ldc.i4.0 IL_000b: call "ref object <PrivateImplementationDetails>.InlineArrayElementRef<<>y__InlineArray2<object>, object>(ref <>y__InlineArray2<object>, int)" IL_0010: ldc.i4.1 IL_0011: box "int" IL_0016: stind.ref IL_0017: ldloca.s V_2 IL_0019: ldc.i4.1 IL_001a: call "ref object <PrivateImplementationDetails>.InlineArrayElementRef<<>y__InlineArray2<object>, object>(ref <>y__InlineArray2<object>, int)" IL_001f: ldc.i4.2 IL_0020: box "int" IL_0025: stind.ref IL_0026: ldloca.s V_2 IL_0028: ldc.i4.2 IL_0029: call "System.Span<object> <PrivateImplementationDetails>.InlineArrayAsSpan<<>y__InlineArray2<object>, object>(ref <>y__InlineArray2<object>, int)" IL_002e: stloc.0 IL_002f: ldloca.s V_0 IL_0031: ldc.i4.0 IL_0032: call "ref object System.Span<object>.this[int].get" IL_0037: ldnull IL_0038: stind.ref IL_0039: ldloca.s V_3 IL_003b: initobj "<>y__InlineArray2<int>" IL_0041: ldloca.s V_3 IL_0043: ldc.i4.0 IL_0044: call "ref int <PrivateImplementationDetails>.InlineArrayElementRef<<>y__InlineArray2<int>, int>(ref <>y__InlineArray2<int>, int)" IL_0049: ldc.i4.3 IL_004a: stind.i4 IL_004b: ldloca.s V_3 IL_004d: ldc.i4.1 IL_004e: call "ref int <PrivateImplementationDetails>.InlineArrayElementRef<<>y__InlineArray2<int>, int>(ref <>y__InlineArray2<int>, int)" IL_0053: ldc.i4.4 IL_0054: stind.i4 IL_0055: ldloca.s V_3 IL_0057: ldc.i4.2 IL_0058: call "System.Span<int> <PrivateImplementationDetails>.InlineArrayAsSpan<<>y__InlineArray2<int>, int>(ref <>y__InlineArray2<int>, int)" IL_005d: stloc.1 IL_005e: ldloca.s V_1 IL_0060: ldc.i4.1 IL_0061: call "ref int System.Span<int>.this[int].get" IL_0066: ldc.i4.5 IL_0067: stind.i4 IL_0068: ldloc.0 IL_0069: call "System.ReadOnlySpan<object> System.Span<object>.op_Implicit(System.Span<object>)" IL_006e: stloc.s V_4 IL_0070: ldloca.s V_4 IL_0072: call "void CollectionExtensions.Report<object>(in System.ReadOnlySpan<object>)" IL_0077: ldloc.1 IL_0078: call "System.ReadOnlySpan<int> System.Span<int>.op_Implicit(System.Span<int>)" IL_007d: stloc.s V_5 IL_007f: ldloca.s V_5 IL_0081: call "void CollectionExtensions.Report<int>(in System.ReadOnlySpan<int>)" IL_0086: ret } """); } [WorkItem("https://github.com/dotnet/roslyn/issues/71490")] [Fact] public void ExplicitConversion_NoElementConversion() { string source = """ using System; DateTimeOffset d = default; ReadOnlySpan<DateTime> x = [d]; var y = (ReadOnlySpan<DateTime>)[d]; """; var comp = CreateCompilation(source, targetFramework: TargetFramework.Net80); comp.VerifyEmitDiagnostics( // (3,29): error CS0029: Cannot implicitly convert type 'System.DateTimeOffset' to 'System.DateTime' // ReadOnlySpan<DateTime> x = [d]; Diagnostic(ErrorCode.ERR_NoImplicitConv, "d").WithArguments("System.DateTimeOffset", "System.DateTime").WithLocation(3, 29), // (4,34): error CS0029: Cannot implicitly convert type 'System.DateTimeOffset' to 'System.DateTime' // var y = (ReadOnlySpan<DateTime>)[d]; Diagnostic(ErrorCode.ERR_NoImplicitConv, "d").WithArguments("System.DateTimeOffset", "System.DateTime").WithLocation(4, 34)); } [WorkItem("https://github.com/dotnet/roslyn/issues/71490")] [Fact] public void ExplicitConversion_NoElementType() { string source = """ using System; using System.Runtime.CompilerServices; [CollectionBuilder(typeof(MyCollectionBuilder), "Create")] struct MyCollection<T> { } class MyCollectionBuilder { public static MyCollection<T> Create<T>(ReadOnlySpan<T> items) => default; } class Program { static void Main() { MyCollection<object> x = []; var y = (MyCollection<object>)[]; } } """; var comp = CreateCompilation(source, targetFramework: TargetFramework.Net80); comp.VerifyEmitDiagnostics( // (15,34): error CS9188: 'MyCollection<object>' has a CollectionBuilderAttribute but no element type. // MyCollection<object> x = []; Diagnostic(ErrorCode.ERR_CollectionBuilderNoElementType, "[]").WithArguments("MyCollection<object>").WithLocation(15, 34), // (16,39): error CS9188: 'MyCollection<object>' has a CollectionBuilderAttribute but no element type. // var y = (MyCollection<object>)[]; Diagnostic(ErrorCode.ERR_CollectionBuilderNoElementType, "[]").WithArguments("MyCollection<object>").WithLocation(16, 39)); } [Fact] public void TopLevelStatement_01() { string source = """ using System; Span<int?> x = [1, null]; ReadOnlySpan<object> y = [..x, 3]; y.Report(); return y.Length; """; var verifier = CompileAndVerify( new[] { source, s_collectionExtensionsWithSpan }, targetFramework: TargetFramework.Net80, verify: Verification.Skipped, expectedOutput: IncludeExpectedOutput("[1, null, 3], ")); verifier.VerifyIL("<top-level-statements-entry-point>", """ { // Code size 151 (0x97) .maxstack 3 .locals init (System.ReadOnlySpan<object> V_0, //y <>y__InlineArray2<int?> V_1, System.Span<int?> V_2, int V_3, object[] V_4, System.Span<int?>.Enumerator V_5, int? V_6) IL_0000: ldloca.s V_1 IL_0002: initobj "<>y__InlineArray2<int?>" IL_0008: ldloca.s V_1 IL_000a: ldc.i4.0 IL_000b: call "ref int? <PrivateImplementationDetails>.InlineArrayElementRef<<>y__InlineArray2<int?>, int?>(ref <>y__InlineArray2<int?>, int)" IL_0010: ldc.i4.1 IL_0011: newobj "int?..ctor(int)" IL_0016: stobj "int?" IL_001b: ldloca.s V_1 IL_001d: ldc.i4.1 IL_001e: call "ref int? <PrivateImplementationDetails>.InlineArrayElementRef<<>y__InlineArray2<int?>, int?>(ref <>y__InlineArray2<int?>, int)" IL_0023: initobj "int?" IL_0029: ldloca.s V_1 IL_002b: ldc.i4.2 IL_002c: call "System.Span<int?> <PrivateImplementationDetails>.InlineArrayAsSpan<<>y__InlineArray2<int?>, int?>(ref <>y__InlineArray2<int?>, int)" IL_0031: stloc.2 IL_0032: ldc.i4.0 IL_0033: stloc.3 IL_0034: ldc.i4.1 IL_0035: ldloca.s V_2 IL_0037: call "int System.Span<int?>.Length.get" IL_003c: add IL_003d: newarr "object" IL_0042: stloc.s V_4 IL_0044: ldloca.s V_2 IL_0046: call "System.Span<int?>.Enumerator System.Span<int?>.GetEnumerator()" IL_004b: stloc.s V_5 IL_004d: br.s IL_006c IL_004f: ldloca.s V_5 IL_0051: call "ref int? System.Span<int?>.Enumerator.Current.get" IL_0056: ldobj "int?" IL_005b: stloc.s V_6 IL_005d: ldloc.s V_4 IL_005f: ldloc.3 IL_0060: ldloc.s V_6 IL_0062: box "int?" IL_0067: stelem.ref IL_0068: ldloc.3 IL_0069: ldc.i4.1 IL_006a: add IL_006b: stloc.3 IL_006c: ldloca.s V_5 IL_006e: call "bool System.Span<int?>.Enumerator.MoveNext()" IL_0073: brtrue.s IL_004f IL_0075: ldloc.s V_4 IL_0077: ldloc.3 IL_0078: ldc.i4.3 IL_0079: box "int" IL_007e: stelem.ref IL_007f: ldloca.s V_0 IL_0081: ldloc.s V_4 IL_0083: call "System.ReadOnlySpan<object>..ctor(object[])" IL_0088: ldloca.s V_0 IL_008a: call "void CollectionExtensions.Report<object>(in System.ReadOnlySpan<object>)" IL_008f: ldloca.s V_0 IL_0091: call "int System.ReadOnlySpan<object>.Length.get" IL_0096: ret } """); } [Fact] public void TopLevelStatement_02() { string source = """ using System; S.F = [..S.GetSpan(), 3]; struct S { public static Span<int?> GetSpan() => (int?[])[1, null]; public static ReadOnlySpan<object> F; } """; var comp = CreateCompilation(source, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseExe.WithSpecificDiagnosticOptions(WithSpanAllocWarning)); comp.VerifyEmitDiagnostics( // (3,7): warning CS9209: Collection expression of type 'ReadOnlySpan<object>' may incur unexpected heap allocations due to the use of '..' spreads. Consider explicitly creating an array, then converting to 'ReadOnlySpan<object>' to make the allocation explicit. // S.F = [..S.GetSpan(), 3]; Diagnostic(ErrorCode.WRN_CollectionExpressionRefStructSpreadMayAllocate, "[..S.GetSpan(), 3]").WithArguments("System.ReadOnlySpan<object>").WithLocation(3, 7), // (3,7): error CS9203: A collection expression of type 'ReadOnlySpan<object>' cannot be used in this context because it may be exposed outside of the current scope. // S.F = [..S.GetSpan(), 3]; Diagnostic(ErrorCode.ERR_CollectionExpressionEscape, "[..S.GetSpan(), 3]").WithArguments("System.ReadOnlySpan<object>").WithLocation(3, 7), // (8,19): error CS8345: Field or auto-implemented property cannot be of type 'ReadOnlySpan<object>' unless it is an instance member of a ref struct. // public static ReadOnlySpan<object> F; Diagnostic(ErrorCode.ERR_FieldAutoPropCantBeByRefLike, "ReadOnlySpan<object>").WithArguments("System.ReadOnlySpan<object>").WithLocation(8, 19)); } [Fact] public void RuntimeHelpers_CreateSpan_Primitives() { string source = """ using System; class Program { static void Main() { Report<bool>([true]); Report<sbyte>([1]); Report<byte>([2]); Report<short>([3]); Report<ushort>([4]); Report<char>(['5']); Report<int>([6]); Report<uint>([7]); Report<long>([8]); Report<ulong>([9]); Report<float>([10]); Report<double>([11]); } static void Report<T>(ReadOnlySpan<T> s) { s.ToArray().Report(includeType: true); Console.WriteLine(); } } """; var verifier = CompileAndVerify( new[] { source, s_collectionExtensions }, targetFramework: TargetFramework.Net80, verify: Verification.Fails, expectedOutput: IncludeExpectedOutput(""" (System.Boolean[]) [True], (System.SByte[]) [1], (System.Byte[]) [2], (System.Int16[]) [3], (System.UInt16[]) [4], (System.Char[]) [5], (System.Int32[]) [6], (System.UInt32[]) [7], (System.Int64[]) [8], (System.UInt64[]) [9], (System.Single[]) [10], (System.Double[]) [11], """)); verifier.VerifyIL("Program.Main", """ { // Code size 184 (0xb8) .maxstack 2 IL_0000: ldsflda "byte <PrivateImplementationDetails>.4BF5122F344554C53BDE2EBB8CD2B7E3D1600AD631C385A5D7CCE23C7785459A" IL_0005: ldc.i4.1 IL_0006: newobj "System.ReadOnlySpan<bool>..ctor(void*, int)" IL_000b: call "void Program.Report<bool>(System.ReadOnlySpan<bool>)" IL_0010: ldsflda "byte <PrivateImplementationDetails>.4BF5122F344554C53BDE2EBB8CD2B7E3D1600AD631C385A5D7CCE23C7785459A" IL_0015: ldc.i4.1 IL_0016: newobj "System.ReadOnlySpan<sbyte>..ctor(void*, int)" IL_001b: call "void Program.Report<sbyte>(System.ReadOnlySpan<sbyte>)" IL_0020: ldsflda "byte <PrivateImplementationDetails>.DBC1B4C900FFE48D575B5DA5C638040125F65DB0FE3E24494B76EA986457D986" IL_0025: ldc.i4.1 IL_0026: newobj "System.ReadOnlySpan<byte>..ctor(void*, int)" IL_002b: call "void Program.Report<byte>(System.ReadOnlySpan<byte>)" IL_0030: ldtoken "<PrivateImplementationDetails>.__StaticArrayInitTypeSize=2_Align=2 <PrivateImplementationDetails>.9B4FB24EDD6D1D8830E272398263CDBF026B97392CC35387B991DC0248A628F92" IL_0035: call "System.ReadOnlySpan<short> System.Runtime.CompilerServices.RuntimeHelpers.CreateSpan<short>(System.RuntimeFieldHandle)" IL_003a: call "void Program.Report<short>(System.ReadOnlySpan<short>)" IL_003f: ldtoken "<PrivateImplementationDetails>.__StaticArrayInitTypeSize=2_Align=2 <PrivateImplementationDetails>.C0BA8A33AC67F44ABFF5984DFBB6F56C46B880AC2B86E1F23E7FA9C402C53AE72" IL_0044: call "System.ReadOnlySpan<ushort> System.Runtime.CompilerServices.RuntimeHelpers.CreateSpan<ushort>(System.RuntimeFieldHandle)" IL_0049: call "void Program.Report<ushort>(System.ReadOnlySpan<ushort>)" IL_004e: ldtoken "<PrivateImplementationDetails>.__StaticArrayInitTypeSize=2_Align=2 <PrivateImplementationDetails>.166F829E016F2315A8099E3A8D2DBEC6D91572379FF02C760BA4E0335789D47F2" IL_0053: call "System.ReadOnlySpan<char> System.Runtime.CompilerServices.RuntimeHelpers.CreateSpan<char>(System.RuntimeFieldHandle)" IL_0058: call "void Program.Report<char>(System.ReadOnlySpan<char>)" IL_005d: ldtoken "<PrivateImplementationDetails>.__StaticArrayInitTypeSize=4_Align=4 <PrivateImplementationDetails>.7AA8CA4A02506DA9133D8F889678B76F716CE45D02E22FDB7B70A15E56A0EFF84" IL_0062: call "System.ReadOnlySpan<int> System.Runtime.CompilerServices.RuntimeHelpers.CreateSpan<int>(System.RuntimeFieldHandle)" IL_0067: call "void Program.Report<int>(System.ReadOnlySpan<int>)" IL_006c: ldtoken "<PrivateImplementationDetails>.__StaticArrayInitTypeSize=4_Align=4 <PrivateImplementationDetails>.E8613F5A5BC9F9FEEDA32A8E7C80B69DD4878E47B6A91723FB15EB84236B6A2B4" IL_0071: call "System.ReadOnlySpan<uint> System.Runtime.CompilerServices.RuntimeHelpers.CreateSpan<uint>(System.RuntimeFieldHandle)" IL_0076: call "void Program.Report<uint>(System.ReadOnlySpan<uint>)" IL_007b: ldtoken "<PrivateImplementationDetails>.__StaticArrayInitTypeSize=8_Align=8 <PrivateImplementationDetails>.6CC16ABD70EEFB90DC0BA0D14FB088630873B2C6AD943F7442356735984C35A38" IL_0080: call "System.ReadOnlySpan<long> System.Runtime.CompilerServices.RuntimeHelpers.CreateSpan<long>(System.RuntimeFieldHandle)" IL_0085: call "void Program.Report<long>(System.ReadOnlySpan<long>)" IL_008a: ldtoken "<PrivateImplementationDetails>.__StaticArrayInitTypeSize=8_Align=8 <PrivateImplementationDetails>.CBBD5F990C53684D7AE650B40FCB5656E02261B53DA5F6A7D8C819C92F2828F88" IL_008f: call "System.ReadOnlySpan<ulong> System.Runtime.CompilerServices.RuntimeHelpers.CreateSpan<ulong>(System.RuntimeFieldHandle)" IL_0094: call "void Program.Report<ulong>(System.ReadOnlySpan<ulong>)" IL_0099: ldtoken "<PrivateImplementationDetails>.__StaticArrayInitTypeSize=4_Align=4 <PrivateImplementationDetails>.80C8A717CCD70C8809EB78E6A9591C003E11C721FE0CCAF62FD592ABDA1A55934" IL_009e: call "System.ReadOnlySpan<float> System.Runtime.CompilerServices.RuntimeHelpers.CreateSpan<float>(System.RuntimeFieldHandle)" IL_00a3: call "void Program.Report<float>(System.ReadOnlySpan<float>)" IL_00a8: ldtoken "<PrivateImplementationDetails>.__StaticArrayInitTypeSize=8_Align=8 <PrivateImplementationDetails>.9EE2B49423E1506EC86B25B2FEBB317DA93338F594CDCDCD1B38E3A726706DE08" IL_00ad: call "System.ReadOnlySpan<double> System.Runtime.CompilerServices.RuntimeHelpers.CreateSpan<double>(System.RuntimeFieldHandle)" IL_00b2: call "void Program.Report<double>(System.ReadOnlySpan<double>)" IL_00b7: ret } """); } [Fact] public void RuntimeHelpers_CreateSpan_NotPrimitives() { string source = """ using System; class Program { static void Main() { Report<object>(["1"]); Report<string>(["2"]); Report<nint>([3]); Report<nuint>([4]); } static void Report<T>(ReadOnlySpan<T> s) { s.ToArray().Report(includeType: true); Console.WriteLine(); } } """; var verifier = CompileAndVerify( new[] { source, s_collectionExtensions }, targetFramework: TargetFramework.Net80, verify: Verification.Skipped, expectedOutput: IncludeExpectedOutput(""" (System.Object[]) [1], (System.String[]) [2], (System.IntPtr[]) [3], (System.UIntPtr[]) [4], """)); verifier.VerifyIL("Program.Main", """ { // Code size 67 (0x43) .maxstack 1 .locals init (object V_0, string V_1, System.IntPtr V_2, System.UIntPtr V_3) IL_0000: ldstr "1" IL_0005: stloc.0 IL_0006: ldloca.s V_0 IL_0008: newobj "System.ReadOnlySpan<object>..ctor(ref readonly object)" IL_000d: call "void Program.Report<object>(System.ReadOnlySpan<object>)" IL_0012: ldstr "2" IL_0017: stloc.1 IL_0018: ldloca.s V_1 IL_001a: newobj "System.ReadOnlySpan<string>..ctor(ref readonly string)" IL_001f: call "void Program.Report<string>(System.ReadOnlySpan<string>)" IL_0024: ldc.i4.3 IL_0025: conv.i IL_0026: stloc.2 IL_0027: ldloca.s V_2 IL_0029: newobj "System.ReadOnlySpan<System.IntPtr>..ctor(ref readonly System.IntPtr)" IL_002e: call "void Program.Report<System.IntPtr>(System.ReadOnlySpan<System.IntPtr>)" IL_0033: ldc.i4.4 IL_0034: conv.i IL_0035: stloc.3 IL_0036: ldloca.s V_3 IL_0038: newobj "System.ReadOnlySpan<System.UIntPtr>..ctor(ref readonly System.UIntPtr)" IL_003d: call "void Program.Report<System.UIntPtr>(System.ReadOnlySpan<System.UIntPtr>)" IL_0042: ret } """); } [Fact] public void RuntimeHelpers_CreateSpan_Enums() { string source = """ using System; enum E_sbyte : sbyte { A = 1 } enum E_byte : byte { B = 2 } enum E_short : short { C = 3 } enum E_ushort : ushort { D = 4 } enum E_int : int { E = 5 } enum E_uint : uint { F = 6 } enum E_long : long { G = 7 } enum E_ulong : ulong { H = 8 } class Program { static void Main() { Report([E_sbyte.A]); Report([E_byte.B]); Report([E_short.C]); Report([E_ushort.D]); Report([E_int.E]); Report([E_uint.F]); Report([E_long.G]); Report([E_ulong.H]); } static void Report<T>(ReadOnlySpan<T> s) { s.ToArray().Report(includeType: true); Console.WriteLine(); } } """; var verifier = CompileAndVerify( new[] { source, s_collectionExtensions }, targetFramework: TargetFramework.Net80, verify: Verification.Fails, expectedOutput: IncludeExpectedOutput(""" (E_sbyte[]) [A], (E_byte[]) [B], (E_short[]) [C], (E_ushort[]) [D], (E_int[]) [E], (E_uint[]) [F], (E_long[]) [G], (E_ulong[]) [H], """)); verifier.VerifyIL("Program.Main", """ { // Code size 123 (0x7b) .maxstack 2 IL_0000: ldsflda "byte <PrivateImplementationDetails>.4BF5122F344554C53BDE2EBB8CD2B7E3D1600AD631C385A5D7CCE23C7785459A" IL_0005: ldc.i4.1 IL_0006: newobj "System.ReadOnlySpan<E_sbyte>..ctor(void*, int)" IL_000b: call "void Program.Report<E_sbyte>(System.ReadOnlySpan<E_sbyte>)" IL_0010: ldsflda "byte <PrivateImplementationDetails>.DBC1B4C900FFE48D575B5DA5C638040125F65DB0FE3E24494B76EA986457D986" IL_0015: ldc.i4.1 IL_0016: newobj "System.ReadOnlySpan<E_byte>..ctor(void*, int)" IL_001b: call "void Program.Report<E_byte>(System.ReadOnlySpan<E_byte>)" IL_0020: ldtoken "<PrivateImplementationDetails>.__StaticArrayInitTypeSize=2_Align=2 <PrivateImplementationDetails>.9B4FB24EDD6D1D8830E272398263CDBF026B97392CC35387B991DC0248A628F92" IL_0025: call "System.ReadOnlySpan<E_short> System.Runtime.CompilerServices.RuntimeHelpers.CreateSpan<E_short>(System.RuntimeFieldHandle)" IL_002a: call "void Program.Report<E_short>(System.ReadOnlySpan<E_short>)" IL_002f: ldtoken "<PrivateImplementationDetails>.__StaticArrayInitTypeSize=2_Align=2 <PrivateImplementationDetails>.C0BA8A33AC67F44ABFF5984DFBB6F56C46B880AC2B86E1F23E7FA9C402C53AE72" IL_0034: call "System.ReadOnlySpan<E_ushort> System.Runtime.CompilerServices.RuntimeHelpers.CreateSpan<E_ushort>(System.RuntimeFieldHandle)" IL_0039: call "void Program.Report<E_ushort>(System.ReadOnlySpan<E_ushort>)" IL_003e: ldtoken "<PrivateImplementationDetails>.__StaticArrayInitTypeSize=4_Align=4 <PrivateImplementationDetails>.2594B6A92EBFB1C3312DEB7D01C015FB95E9FBE9BD7BC6B527AF07813EC7B9104" IL_0043: call "System.ReadOnlySpan<E_int> System.Runtime.CompilerServices.RuntimeHelpers.CreateSpan<E_int>(System.RuntimeFieldHandle)" IL_0048: call "void Program.Report<E_int>(System.ReadOnlySpan<E_int>)" IL_004d: ldtoken "<PrivateImplementationDetails>.__StaticArrayInitTypeSize=4_Align=4 <PrivateImplementationDetails>.7AA8CA4A02506DA9133D8F889678B76F716CE45D02E22FDB7B70A15E56A0EFF84" IL_0052: call "System.ReadOnlySpan<E_uint> System.Runtime.CompilerServices.RuntimeHelpers.CreateSpan<E_uint>(System.RuntimeFieldHandle)" IL_0057: call "void Program.Report<E_uint>(System.ReadOnlySpan<E_uint>)" IL_005c: ldtoken "<PrivateImplementationDetails>.__StaticArrayInitTypeSize=8_Align=8 <PrivateImplementationDetails>.AAE89FC0F03E2959AE4D701A80CC3915918C950B159F6ABB6C92C1433B1A85348" IL_0061: call "System.ReadOnlySpan<E_long> System.Runtime.CompilerServices.RuntimeHelpers.CreateSpan<E_long>(System.RuntimeFieldHandle)" IL_0066: call "void Program.Report<E_long>(System.ReadOnlySpan<E_long>)" IL_006b: ldtoken "<PrivateImplementationDetails>.__StaticArrayInitTypeSize=8_Align=8 <PrivateImplementationDetails>.6CC16ABD70EEFB90DC0BA0D14FB088630873B2C6AD943F7442356735984C35A38" IL_0070: call "System.ReadOnlySpan<E_ulong> System.Runtime.CompilerServices.RuntimeHelpers.CreateSpan<E_ulong>(System.RuntimeFieldHandle)" IL_0075: call "void Program.Report<E_ulong>(System.ReadOnlySpan<E_ulong>)" IL_007a: ret } """); } [CombinatorialData] [Theory] public void RuntimeHelpers_CreateSpan([CombinatorialValues(TargetFramework.Net60, TargetFramework.Net80)] TargetFramework targetFramework) { string source = """ using System; class Program { static void Main() { F1().Report(); F2().Report(); } static ReadOnlySpan<int> F1() => new[] { 1, 2, 3 }; static ReadOnlySpan<int> F2() => [1, 2, 3]; } """; var verifier = CompileAndVerify( new[] { source, s_collectionExtensionsWithSpan }, targetFramework: targetFramework, verify: Verification.Fails, expectedOutput: IncludeExpectedOutput("[1, 2, 3], [1, 2, 3], ")); string expectedIL = targetFramework == TargetFramework.Net60 ? """ { // Code size 38 (0x26) .maxstack 3 IL_0000: ldsfld "int[] <PrivateImplementationDetails>.4636993D3E1DA4E9D6B8F87B79E8F7C6D018580D52661950EABC3845C5897A4D_A6" IL_0005: dup IL_0006: brtrue.s IL_0020 IL_0008: pop IL_0009: ldc.i4.3 IL_000a: newarr "int" IL_000f: dup IL_0010: ldtoken "<PrivateImplementationDetails>.__StaticArrayInitTypeSize=12 <PrivateImplementationDetails>.4636993D3E1DA4E9D6B8F87B79E8F7C6D018580D52661950EABC3845C5897A4D" IL_0015: call "void System.Runtime.CompilerServices.RuntimeHelpers.InitializeArray(System.Array, System.RuntimeFieldHandle)" IL_001a: dup IL_001b: stsfld "int[] <PrivateImplementationDetails>.4636993D3E1DA4E9D6B8F87B79E8F7C6D018580D52661950EABC3845C5897A4D_A6" IL_0020: newobj "System.ReadOnlySpan<int>..ctor(int[])" IL_0025: ret } """ : """ { // Code size 11 (0xb) .maxstack 1 IL_0000: ldtoken "<PrivateImplementationDetails>.__StaticArrayInitTypeSize=12_Align=4 <PrivateImplementationDetails>.4636993D3E1DA4E9D6B8F87B79E8F7C6D018580D52661950EABC3845C5897A4D4" IL_0005: call "System.ReadOnlySpan<int> System.Runtime.CompilerServices.RuntimeHelpers.CreateSpan<int>(System.RuntimeFieldHandle)" IL_000a: ret } """; verifier.VerifyIL("Program.F1", expectedIL); verifier.VerifyIL("Program.F2", expectedIL); } [CombinatorialData] [Theory] public void RuntimeHelpers_CreateSpan_Byte([CombinatorialValues(TargetFramework.Net60, TargetFramework.Net80)] TargetFramework targetFramework) { string source = """ using System; class Program { static void Main() { F1().Report(); F2().Report(); } static ReadOnlySpan<byte> F1() { ReadOnlySpan<byte> s = new byte[] { 1, 2, 3 }; return s; } static ReadOnlySpan<byte> F2() { ReadOnlySpan<byte> s = [1, 2, 3]; return s; } } """; var verifier = CompileAndVerify( new[] { source, s_collectionExtensionsWithSpan }, targetFramework: targetFramework, verify: Verification.Fails, expectedOutput: IncludeExpectedOutput("[1, 2, 3], [1, 2, 3], ")); string expectedIL = """ { // Code size 12 (0xc) .maxstack 2 IL_0000: ldsflda "<PrivateImplementationDetails>.__StaticArrayInitTypeSize=3 <PrivateImplementationDetails>.039058C6F2C0CB492C533B0A4D14EF77CC0F78ABCCCED5287D84A1A2011CFB81" IL_0005: ldc.i4.3 IL_0006: newobj "System.ReadOnlySpan<byte>..ctor(void*, int)" IL_000b: ret } """; verifier.VerifyIL("Program.F1", expectedIL); verifier.VerifyIL("Program.F2", expectedIL); } [CombinatorialData] [Theory] public void RuntimeHelpers_CreateSpan_NotApplicable_01([CombinatorialValues(TargetFramework.Net60, TargetFramework.Net80)] TargetFramework targetFramework) { string source = """ using System; class Program { static Span<int> NotReadOnlySpan() => [1, 2, 3]; static ReadOnlySpan<int> NotConstants(int c) => [1, 2, c]; } """; var comp = CreateCompilation(source, targetFramework: targetFramework); comp.VerifyEmitDiagnostics( // (4,43): error CS9203: A collection expression of type 'Span<int>' cannot be used in this context because it may be exposed outside of the current scope. // static Span<int> NotReadOnlySpan() => [1, 2, 3]; Diagnostic(ErrorCode.ERR_CollectionExpressionEscape, "[1, 2, 3]").WithArguments("System.Span<int>").WithLocation(4, 43), // (5,53): error CS9203: A collection expression of type 'ReadOnlySpan<int>' cannot be used in this context because it may be exposed outside of the current scope. // static ReadOnlySpan<int> NotConstants(int c) => [1, 2, c]; Diagnostic(ErrorCode.ERR_CollectionExpressionEscape, "[1, 2, c]").WithArguments("System.ReadOnlySpan<int>").WithLocation(5, 53)); } [Fact] public void RuntimeHelpers_CreateSpan_NotApplicable_02() { string source = """ using System; class Program { static void Main() { NotReadOnlySpan(); NotConstants(3); } static void NotReadOnlySpan() { Span<int> s = [1, 2, 3]; s.Report(); } static void NotConstants(int c) { ReadOnlySpan<int> s =[1, 2, c]; s.Report(); } } """; var verifier = CompileAndVerify( new[] { source, s_collectionExtensionsWithSpan }, targetFramework: TargetFramework.Net80, verify: Verification.Fails, expectedOutput: IncludeExpectedOutput("[1, 2, 3], [1, 2, 3], ")); verifier.VerifyIL("Program.NotReadOnlySpan", """ { // Code size 60 (0x3c) .maxstack 2 .locals init (<>y__InlineArray3<int> V_0, System.ReadOnlySpan<int> V_1) IL_0000: ldloca.s V_0 IL_0002: initobj "<>y__InlineArray3<int>" IL_0008: ldloca.s V_0 IL_000a: ldc.i4.0 IL_000b: call "ref int <PrivateImplementationDetails>.InlineArrayElementRef<<>y__InlineArray3<int>, int>(ref <>y__InlineArray3<int>, int)" IL_0010: ldc.i4.1 IL_0011: stind.i4 IL_0012: ldloca.s V_0 IL_0014: ldc.i4.1 IL_0015: call "ref int <PrivateImplementationDetails>.InlineArrayElementRef<<>y__InlineArray3<int>, int>(ref <>y__InlineArray3<int>, int)" IL_001a: ldc.i4.2 IL_001b: stind.i4 IL_001c: ldloca.s V_0 IL_001e: ldc.i4.2 IL_001f: call "ref int <PrivateImplementationDetails>.InlineArrayElementRef<<>y__InlineArray3<int>, int>(ref <>y__InlineArray3<int>, int)" IL_0024: ldc.i4.3 IL_0025: stind.i4 IL_0026: ldloca.s V_0 IL_0028: ldc.i4.3 IL_0029: call "System.Span<int> <PrivateImplementationDetails>.InlineArrayAsSpan<<>y__InlineArray3<int>, int>(ref <>y__InlineArray3<int>, int)" IL_002e: call "System.ReadOnlySpan<int> System.Span<int>.op_Implicit(System.Span<int>)" IL_0033: stloc.1 IL_0034: ldloca.s V_1 IL_0036: call "void CollectionExtensions.Report<int>(in System.ReadOnlySpan<int>)" IL_003b: ret } """); verifier.VerifyIL("Program.NotConstants", """ { // Code size 55 (0x37) .maxstack 2 .locals init (System.ReadOnlySpan<int> V_0, //s <>y__InlineArray3<int> V_1) IL_0000: ldloca.s V_1 IL_0002: initobj "<>y__InlineArray3<int>" IL_0008: ldloca.s V_1 IL_000a: ldc.i4.0 IL_000b: call "ref int <PrivateImplementationDetails>.InlineArrayElementRef<<>y__InlineArray3<int>, int>(ref <>y__InlineArray3<int>, int)" IL_0010: ldc.i4.1 IL_0011: stind.i4 IL_0012: ldloca.s V_1 IL_0014: ldc.i4.1 IL_0015: call "ref int <PrivateImplementationDetails>.InlineArrayElementRef<<>y__InlineArray3<int>, int>(ref <>y__InlineArray3<int>, int)" IL_001a: ldc.i4.2 IL_001b: stind.i4 IL_001c: ldloca.s V_1 IL_001e: ldc.i4.2 IL_001f: call "ref int <PrivateImplementationDetails>.InlineArrayElementRef<<>y__InlineArray3<int>, int>(ref <>y__InlineArray3<int>, int)" IL_0024: ldarg.0 IL_0025: stind.i4 IL_0026: ldloca.s V_1 IL_0028: ldc.i4.3 IL_0029: call "System.ReadOnlySpan<int> <PrivateImplementationDetails>.InlineArrayAsReadOnlySpan<<>y__InlineArray3<int>, int>(in <>y__InlineArray3<int>, int)" IL_002e: stloc.0 IL_002f: ldloca.s V_0 IL_0031: call "void CollectionExtensions.Report<int>(in System.ReadOnlySpan<int>)" IL_0036: ret } """); } [CombinatorialData] [Theory] public void RuntimeHelpers_CreateSpan_RefStruct([CombinatorialValues(TargetFramework.Net60, TargetFramework.Net80)] TargetFramework targetFramework) { string sourceA = $$""" using System; using System.Collections.Generic; using System.Runtime.CompilerServices; [CollectionBuilder(typeof(MyCollectionBuilder), nameof(MyCollectionBuilder.Create))] public ref struct MyCollection<T> { private readonly List<T> _list; public MyCollection(List<T> list) { _list = list; } public IEnumerator<T> GetEnumerator() => _list.GetEnumerator(); } public class MyCollectionBuilder { public static MyCollection<T> Create<T>(ReadOnlySpan<T> items) => new MyCollection<T>(new List<T>(items.ToArray())); } """; var comp = CreateCompilation( targetFramework == TargetFramework.Net80 ? new[] { sourceA } : new[] { sourceA, CollectionBuilderAttributeDefinition }, targetFramework: targetFramework); comp.VerifyEmitDiagnostics(); var refA = comp.EmitToImageReference(); string sourceB = $$""" using System.Collections.Generic; using System; enum E : byte { A = 1, B = 2, C = 3 } class Program { static void Main() { MyCollection<byte> x = F1(); MyCollection<int> y = F2(); MyCollection<E> z = F3(); Report(x); Report(y); Report(z); } static MyCollection<byte> F1() => [1, 2, 3]; static MyCollection<int> F2() => [1, 2, 3]; static MyCollection<E> F3() => [E.A, E.B, E.C]; static void Report<T>(MyCollection<T> c) { var list = new List<T>(); foreach (var i in c) list.Add(i); list.Report(); } } """; var verifier = CompileAndVerify( new[] { sourceB, s_collectionExtensions }, references: new[] { refA }, targetFramework: targetFramework, verify: Verification.Fails, expectedOutput: IncludeExpectedOutput("[1, 2, 3], [1, 2, 3], [A, B, C], ")); verifier.VerifyIL("Program.F1", """ { // Code size 17 (0x11) .maxstack 2 IL_0000: ldsflda "<PrivateImplementationDetails>.__StaticArrayInitTypeSize=3 <PrivateImplementationDetails>.039058C6F2C0CB492C533B0A4D14EF77CC0F78ABCCCED5287D84A1A2011CFB81" IL_0005: ldc.i4.3 IL_0006: newobj "System.ReadOnlySpan<byte>..ctor(void*, int)" IL_000b: call "MyCollection<byte> MyCollectionBuilder.Create<byte>(System.ReadOnlySpan<byte>)" IL_0010: ret } """); if (targetFramework == TargetFramework.Net60) { verifier.VerifyIL("Program.F2", """ { // Code size 43 (0x2b) .maxstack 3 IL_0000: ldsfld "int[] <PrivateImplementationDetails>.4636993D3E1DA4E9D6B8F87B79E8F7C6D018580D52661950EABC3845C5897A4D_A6" IL_0005: dup IL_0006: brtrue.s IL_0020 IL_0008: pop IL_0009: ldc.i4.3 IL_000a: newarr "int" IL_000f: dup IL_0010: ldtoken "<PrivateImplementationDetails>.__StaticArrayInitTypeSize=12 <PrivateImplementationDetails>.4636993D3E1DA4E9D6B8F87B79E8F7C6D018580D52661950EABC3845C5897A4D" IL_0015: call "void System.Runtime.CompilerServices.RuntimeHelpers.InitializeArray(System.Array, System.RuntimeFieldHandle)" IL_001a: dup IL_001b: stsfld "int[] <PrivateImplementationDetails>.4636993D3E1DA4E9D6B8F87B79E8F7C6D018580D52661950EABC3845C5897A4D_A6" IL_0020: newobj "System.ReadOnlySpan<int>..ctor(int[])" IL_0025: call "MyCollection<int> MyCollectionBuilder.Create<int>(System.ReadOnlySpan<int>)" IL_002a: ret } """); } else { verifier.VerifyIL("Program.F2", """ { // Code size 16 (0x10) .maxstack 1 IL_0000: ldtoken "<PrivateImplementationDetails>.__StaticArrayInitTypeSize=12_Align=4 <PrivateImplementationDetails>.4636993D3E1DA4E9D6B8F87B79E8F7C6D018580D52661950EABC3845C5897A4D4" IL_0005: call "System.ReadOnlySpan<int> System.Runtime.CompilerServices.RuntimeHelpers.CreateSpan<int>(System.RuntimeFieldHandle)" IL_000a: call "MyCollection<int> MyCollectionBuilder.Create<int>(System.ReadOnlySpan<int>)" IL_000f: ret } """); } verifier.VerifyIL("Program.F3", """ { // Code size 17 (0x11) .maxstack 2 IL_0000: ldsflda "<PrivateImplementationDetails>.__StaticArrayInitTypeSize=3 <PrivateImplementationDetails>.039058C6F2C0CB492C533B0A4D14EF77CC0F78ABCCCED5287D84A1A2011CFB81" IL_0005: ldc.i4.3 IL_0006: newobj "System.ReadOnlySpan<E>..ctor(void*, int)" IL_000b: call "MyCollection<E> MyCollectionBuilder.Create<E>(System.ReadOnlySpan<E>)" IL_0010: ret } """); } [Fact] public void ReadOnlySpan_Constant_01() { string source = """ using System; class Program { static void Main() { const int x = 3; Report([1, 2, x]); } static void Report<T>(ReadOnlySpan<T> s) { s.ToArray().Report(includeType: true); Console.WriteLine(); } } """; var verifier = CompileAndVerify( new[] { source, s_collectionExtensions }, targetFramework: TargetFramework.Net80, verify: Verification.FailsPEVerify, expectedOutput: IncludeExpectedOutput(""" (System.Int32[]) [1, 2, 3], """)); verifier.VerifyIL("Program.Main", """ { // Code size 16 (0x10) .maxstack 1 IL_0000: ldtoken "<PrivateImplementationDetails>.__StaticArrayInitTypeSize=12_Align=4 <PrivateImplementationDetails>.4636993D3E1DA4E9D6B8F87B79E8F7C6D018580D52661950EABC3845C5897A4D4" IL_0005: call "System.ReadOnlySpan<int> System.Runtime.CompilerServices.RuntimeHelpers.CreateSpan<int>(System.RuntimeFieldHandle)" IL_000a: call "void Program.Report<int>(System.ReadOnlySpan<int>)" IL_000f: ret } """); } [Fact] public void ReadOnlySpan_Constant_02() { string source = """ using System; class Program { static void Main() { Report([null, null, "a" + "b"]); } static void Report<T>(ReadOnlySpan<T> s) { s.ToArray().Report(includeType: true); Console.WriteLine(); } } """; var verifier = CompileAndVerify( new[] { source, s_collectionExtensions }, targetFramework: TargetFramework.Net80, verify: Verification.Fails, expectedOutput: IncludeExpectedOutput(""" (System.String[]) [null, null, ab], """)); verifier.VerifyIL("Program.Main", """ { // Code size 56 (0x38) .maxstack 2 .locals init (<>y__InlineArray3<string> V_0) IL_0000: ldloca.s V_0 IL_0002: initobj "<>y__InlineArray3<string>" IL_0008: ldloca.s V_0 IL_000a: ldc.i4.0 IL_000b: call "ref string <PrivateImplementationDetails>.InlineArrayElementRef<<>y__InlineArray3<string>, string>(ref <>y__InlineArray3<string>, int)" IL_0010: ldnull IL_0011: stind.ref IL_0012: ldloca.s V_0 IL_0014: ldc.i4.1 IL_0015: call "ref string <PrivateImplementationDetails>.InlineArrayElementRef<<>y__InlineArray3<string>, string>(ref <>y__InlineArray3<string>, int)" IL_001a: ldnull IL_001b: stind.ref IL_001c: ldloca.s V_0 IL_001e: ldc.i4.2 IL_001f: call "ref string <PrivateImplementationDetails>.InlineArrayElementRef<<>y__InlineArray3<string>, string>(ref <>y__InlineArray3<string>, int)" IL_0024: ldstr "ab" IL_0029: stind.ref IL_002a: ldloca.s V_0 IL_002c: ldc.i4.3 IL_002d: call "System.ReadOnlySpan<string> <PrivateImplementationDetails>.InlineArrayAsReadOnlySpan<<>y__InlineArray3<string>, string>(in <>y__InlineArray3<string>, int)" IL_0032: call "void Program.Report<string>(System.ReadOnlySpan<string>)" IL_0037: ret } """); } [Fact] public void ReadOnlySpan_EnsureCopyingSemantics() { string source = """ using System; class Program { static void Main() { int[] arr = { 1, 2, 3 }; ReadOnlySpan<int> span1 = arr; ReadOnlySpan<int> span2 = [.. span1]; arr[1] = 4; span1.Report(); span2.Report(); } } """; var verifier = CompileAndVerify( [source, s_collectionExtensionsWithSpan], targetFramework: TargetFramework.Net80, verify: Verification.Skipped, expectedOutput: IncludeExpectedOutput("[1, 4, 3], [1, 2, 3], ")); verifier.VerifyDiagnostics(); } [Theory, CombinatorialData] public void ReadOnlySpan_FromSingleSpread_ToArray_Covariant_Spans(bool readOnlySpan) { var spanType = readOnlySpan ? "ReadOnlySpan" : "Span"; var source = $$""" using System; class Program { static void Main() { {{spanType}}<Derived> d = CreateArray(); ReadOnlySpan<Base> b = [.. d]; b.Report(); } static Derived[] CreateArray() => [new(), new(), new()]; } class Base { private static int _totalCount; private readonly int _id; public Base() { _id = _totalCount++; } public override string ToString() => $"{GetType().Name} {_id}"; } class Derived : Base; """; var verifier = CompileAndVerify( [source, s_collectionExtensionsWithSpan], targetFramework: TargetFramework.Net90, verify: Verification.Skipped, expectedOutput: IncludeExpectedOutput("[Derived 0, Derived 1, Derived 2], ")); verifier.VerifyDiagnostics(); verifier.VerifyIL("Program.Main", $$""" { // Code size 33 (0x21) .maxstack 2 .locals init (System.{{spanType}}<Derived> V_0, //d System.ReadOnlySpan<Base> V_1) //b IL_0000: call "Derived[] Program.CreateArray()" IL_0005: call "System.{{spanType}}<Derived> System.{{spanType}}<Derived>.op_Implicit(Derived[])" IL_000a: stloc.0 IL_000b: ldloca.s V_1 IL_000d: ldloca.s V_0 IL_000f: call "Derived[] System.{{spanType}}<Derived>.ToArray()" IL_0014: call "System.ReadOnlySpan<Base>..ctor(Base[])" IL_0019: ldloca.s V_1 IL_001b: call "void CollectionExtensions.Report<Base>(in System.ReadOnlySpan<Base>)" IL_0020: ret } """); } [Fact] public void ReadOnlySpan_FromSingleSpread_ToArray_Covariant_List() { var source = """ using System; using System.Collections.Generic; class Program { static void Main() { List<Derived> d = CreateList(); ReadOnlySpan<Base> b = [.. d]; b.Report(); } static List<Derived> CreateList() => [new(), new(), new()]; } class Base { private static int _totalCount; private readonly int _id; public Base() { _id = _totalCount++; } public override string ToString() => $"{GetType().Name} {_id}"; } class Derived : Base; """; var verifier = CompileAndVerify( [source, s_collectionExtensionsWithSpan], targetFramework: TargetFramework.Net90, verify: Verification.Skipped, expectedOutput: IncludeExpectedOutput("[Derived 0, Derived 1, Derived 2], ")); verifier.VerifyDiagnostics(); verifier.VerifyIL("Program.Main", """ { // Code size 27 (0x1b) .maxstack 2 .locals init (System.Collections.Generic.List<Derived> V_0, //d System.ReadOnlySpan<Base> V_1) //b IL_0000: call "System.Collections.Generic.List<Derived> Program.CreateList()" IL_0005: stloc.0 IL_0006: ldloca.s V_1 IL_0008: ldloc.0 IL_0009: callvirt "Derived[] System.Collections.Generic.List<Derived>.ToArray()" IL_000e: call "System.ReadOnlySpan<Base>..ctor(Base[])" IL_0013: ldloca.s V_1 IL_0015: call "void CollectionExtensions.Report<Base>(in System.ReadOnlySpan<Base>)" IL_001a: ret } """); } [Theory, CombinatorialData] public void ReadOnlySpan_FromSingleSpread_ToArray_SameRuntimeType_Spans(bool readOnlySpan) { var spanType = readOnlySpan ? "ReadOnlySpan" : "Span"; var source = $$""" using System; class Program { static void Main() { {{spanType}}<object> d = CreateArray(); ReadOnlySpan<dynamic> b = [.. d]; b.Report(); } static object[] CreateArray() => [1, 2, 3]; } """; var verifier = CompileAndVerify( [source, s_collectionExtensionsWithSpan], targetFramework: TargetFramework.Net90, verify: Verification.Skipped, expectedOutput: IncludeExpectedOutput("[1, 2, 3], ")); verifier.VerifyDiagnostics(); verifier.VerifyIL("Program.Main", $$""" { // Code size 33 (0x21) .maxstack 2 .locals init (System.{{spanType}}<object> V_0, //d System.ReadOnlySpan<dynamic> V_1) //b IL_0000: call "object[] Program.CreateArray()" IL_0005: call "System.{{spanType}}<object> System.{{spanType}}<object>.op_Implicit(object[])" IL_000a: stloc.0 IL_000b: ldloca.s V_1 IL_000d: ldloca.s V_0 IL_000f: call "object[] System.{{spanType}}<object>.ToArray()" IL_0014: call "System.ReadOnlySpan<dynamic>..ctor(dynamic[])" IL_0019: ldloca.s V_1 IL_001b: call "void CollectionExtensions.Report<dynamic>(in System.ReadOnlySpan<dynamic>)" IL_0020: ret } """); } [Fact] public void ReadOnlySpan_FromSingleSpread_ToArray_SameRuntimeType_List() { var source = """ using System; using System.Collections.Generic; class Program { static void Main() { List<object> d = CreateList(); ReadOnlySpan<dynamic> b = [.. d]; b.Report(); } static List<object> CreateList() => [1, 2, 3]; } """; var verifier = CompileAndVerify( [source, s_collectionExtensionsWithSpan], targetFramework: TargetFramework.Net90, verify: Verification.Skipped, expectedOutput: IncludeExpectedOutput("[1, 2, 3], ")); verifier.VerifyDiagnostics(); verifier.VerifyIL("Program.Main", """ { // Code size 27 (0x1b) .maxstack 2 .locals init (System.Collections.Generic.List<object> V_0, //d System.ReadOnlySpan<dynamic> V_1) //b IL_0000: call "System.Collections.Generic.List<object> Program.CreateList()" IL_0005: stloc.0 IL_0006: ldloca.s V_1 IL_0008: ldloc.0 IL_0009: callvirt "object[] System.Collections.Generic.List<object>.ToArray()" IL_000e: call "System.ReadOnlySpan<dynamic>..ctor(dynamic[])" IL_0013: ldloca.s V_1 IL_0015: call "void CollectionExtensions.Report<dynamic>(in System.ReadOnlySpan<dynamic>)" IL_001a: ret } """); } [Theory, CombinatorialData] public void ReadOnlySpan_FromSingleSpread_ToArray_Boxing_Spans(bool reaOnlySpan) { var spanType = reaOnlySpan ? "ReadOnlySpan" : "Span"; var source = $$""" using System; class Program { static void Main() { {{spanType}}<int> i = CreateArray(); ReadOnlySpan<object> o = [.. i]; o.Report(); } static int[] CreateArray() => [1, 2, 3]; } """; var verifier = CompileAndVerify( [source, s_collectionExtensionsWithSpan], targetFramework: TargetFramework.Net90, verify: Verification.Skipped, expectedOutput: IncludeExpectedOutput("[1, 2, 3], ")); verifier.VerifyDiagnostics(); verifier.VerifyIL("Program.Main", $$""" { // Code size 86 (0x56) .maxstack 3 .locals init (System.ReadOnlySpan<object> V_0, //o System.{{spanType}}<int> V_1, int V_2, object[] V_3, System.{{spanType}}<int>.Enumerator V_4, int V_5) IL_0000: call "int[] Program.CreateArray()" IL_0005: call "System.{{spanType}}<int> System.{{spanType}}<int>.op_Implicit(int[])" IL_000a: stloc.1 IL_000b: ldc.i4.0 IL_000c: stloc.2 IL_000d: ldloca.s V_1 IL_000f: call "int System.{{spanType}}<int>.Length.get" IL_0014: newarr "object" IL_0019: stloc.3 IL_001a: ldloca.s V_1 IL_001c: call "System.{{spanType}}<int>.Enumerator System.{{spanType}}<int>.GetEnumerator()" IL_0021: stloc.s V_4 IL_0023: br.s IL_003d IL_0025: ldloca.s V_4 IL_0027: call "ref {{(reaOnlySpan ? "readonly " : "")}}int System.{{spanType}}<int>.Enumerator.Current.get" IL_002c: ldind.i4 IL_002d: stloc.s V_5 IL_002f: ldloc.3 IL_0030: ldloc.2 IL_0031: ldloc.s V_5 IL_0033: box "int" IL_0038: stelem.ref IL_0039: ldloc.2 IL_003a: ldc.i4.1 IL_003b: add IL_003c: stloc.2 IL_003d: ldloca.s V_4 IL_003f: call "bool System.{{spanType}}<int>.Enumerator.MoveNext()" IL_0044: brtrue.s IL_0025 IL_0046: ldloca.s V_0 IL_0048: ldloc.3 IL_0049: call "System.ReadOnlySpan<object>..ctor(object[])" IL_004e: ldloca.s V_0 IL_0050: call "void CollectionExtensions.Report<object>(in System.ReadOnlySpan<object>)" IL_0055: ret } """); } [Fact] public void ReadOnlySpan_FromSingleSpread_ToArray_Boxing_List() { var source = """ using System; using System.Collections.Generic; class Program { static void Main() { List<int> i = CreateList(); ReadOnlySpan<object> o = [.. i]; o.Report(); } static List<int> CreateList() => [1, 2, 3]; } """; var verifier = CompileAndVerify( [source, s_collectionExtensionsWithSpan], targetFramework: TargetFramework.Net90, verify: Verification.Skipped, expectedOutput: IncludeExpectedOutput("[1, 2, 3], ")); verifier.VerifyDiagnostics(); verifier.VerifyIL("Program.Main", """ { // Code size 91 (0x5b) .maxstack 3 .locals init (System.ReadOnlySpan<object> V_0, //o int V_1, object[] V_2, System.Collections.Generic.List<int>.Enumerator V_3, int V_4) IL_0000: call "System.Collections.Generic.List<int> Program.CreateList()" IL_0005: ldc.i4.0 IL_0006: stloc.1 IL_0007: dup IL_0008: callvirt "int System.Collections.Generic.List<int>.Count.get" IL_000d: newarr "object" IL_0012: stloc.2 IL_0013: callvirt "System.Collections.Generic.List<int>.Enumerator System.Collections.Generic.List<int>.GetEnumerator()" IL_0018: stloc.3 .try { IL_0019: br.s IL_0032 IL_001b: ldloca.s V_3 IL_001d: call "int System.Collections.Generic.List<int>.Enumerator.Current.get" IL_0022: stloc.s V_4 IL_0024: ldloc.2 IL_0025: ldloc.1 IL_0026: ldloc.s V_4 IL_0028: box "int" IL_002d: stelem.ref IL_002e: ldloc.1 IL_002f: ldc.i4.1 IL_0030: add IL_0031: stloc.1 IL_0032: ldloca.s V_3 IL_0034: call "bool System.Collections.Generic.List<int>.Enumerator.MoveNext()" IL_0039: brtrue.s IL_001b IL_003b: leave.s IL_004b } finally { IL_003d: ldloca.s V_3 IL_003f: constrained. "System.Collections.Generic.List<int>.Enumerator" IL_0045: callvirt "void System.IDisposable.Dispose()" IL_004a: endfinally } IL_004b: ldloca.s V_0 IL_004d: ldloc.2 IL_004e: call "System.ReadOnlySpan<object>..ctor(object[])" IL_0053: ldloca.s V_0 IL_0055: call "void CollectionExtensions.Report<object>(in System.ReadOnlySpan<object>)" IL_005a: ret } """); } [Fact] public void RuntimeHelpers_CreateSpan_MissingCreateSpan() { string source = """ using System; class Program { static void Main() { ReadOnlySpan<int> s = [1, 2, 3]; s.Report(); } } """; var comp = CreateCompilation(new[] { source, s_collectionExtensionsWithSpan }, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseExe); comp.MakeMemberMissing(WellKnownMember.System_Runtime_CompilerServices_RuntimeHelpers__CreateSpanRuntimeFieldHandle); var verifier = CompileAndVerify(comp, verify: Verification.FailsPEVerify, expectedOutput: IncludeExpectedOutput("[1, 2, 3], ")); verifier.VerifyIL("Program.Main", """ { // Code size 46 (0x2e) .maxstack 3 .locals init (System.ReadOnlySpan<int> V_0) //s IL_0000: ldsfld "int[] <PrivateImplementationDetails>.4636993D3E1DA4E9D6B8F87B79E8F7C6D018580D52661950EABC3845C5897A4D_A6" IL_0005: dup IL_0006: brtrue.s IL_0020 IL_0008: pop IL_0009: ldc.i4.3 IL_000a: newarr "int" IL_000f: dup IL_0010: ldtoken "<PrivateImplementationDetails>.__StaticArrayInitTypeSize=12 <PrivateImplementationDetails>.4636993D3E1DA4E9D6B8F87B79E8F7C6D018580D52661950EABC3845C5897A4D" IL_0015: call "void System.Runtime.CompilerServices.RuntimeHelpers.InitializeArray(System.Array, System.RuntimeFieldHandle)" IL_001a: dup IL_001b: stsfld "int[] <PrivateImplementationDetails>.4636993D3E1DA4E9D6B8F87B79E8F7C6D018580D52661950EABC3845C5897A4D_A6" IL_0020: newobj "System.ReadOnlySpan<int>..ctor(int[])" IL_0025: stloc.0 IL_0026: ldloca.s V_0 IL_0028: call "void CollectionExtensions.Report<int>(in System.ReadOnlySpan<int>)" IL_002d: ret } """); } [Fact] public void RuntimeHelpers_CreateSpan_MissingConstructor() { string source = """ using System; class Program { static void Main() { ReadOnlySpan<int> s = [1, 2, 3]; } } """; var comp = CreateCompilation(source, targetFramework: TargetFramework.Net80); comp.MakeMemberMissing(WellKnownMember.System_ReadOnlySpan_T__ctor_Array); comp.VerifyEmitDiagnostics( // (6,31): error CS0656: Missing compiler required member 'System.ReadOnlySpan`1..ctor' // ReadOnlySpan<int> s = [1, 2, 3]; Diagnostic(ErrorCode.ERR_MissingPredefinedMember, "[1, 2, 3]").WithArguments("System.ReadOnlySpan`1", ".ctor").WithLocation(6, 31)); } [Fact] public void ExpressionTrees() { string source = """ using System; using System.Collections; using System.Collections.Generic; using System.Linq.Expressions; interface I<T> : IEnumerable<T> { void Add(T t); } class Program { static Expression<Func<int[]>> Create1() { return () => []; } static Expression<Func<List<object>>> Create2() { return () => [1, 2]; } static Expression<Func<T>> Create3<T, U>(U a, U b) where T : I<U>, new() { return () => [a, b]; } static Expression<Func<List<object>>> Create4() { return () => [with(capacity: 0)]; } } """; var comp = CreateCompilation(source); comp.VerifyEmitDiagnostics( // (13,22): error CS9175: An expression tree may not contain a collection expression. // return () => []; Diagnostic(ErrorCode.ERR_ExpressionTreeContainsCollectionExpression, "[]").WithLocation(13, 22), // (17,22): error CS9175: An expression tree may not contain a collection expression. // return () => [1, 2]; Diagnostic(ErrorCode.ERR_ExpressionTreeContainsCollectionExpression, "[1, 2]").WithLocation(17, 22), // (21,22): error CS9175: An expression tree may not contain a collection expression. // return () => [a, b]; Diagnostic(ErrorCode.ERR_ExpressionTreeContainsCollectionExpression, "[a, b]").WithLocation(21, 22), // (25,22): error CS9175: An expression tree may not contain a collection expression. // return () => [with(capacity: 0)]; Diagnostic(ErrorCode.ERR_ExpressionTreeContainsCollectionExpression, "[with(capacity: 0)]").WithLocation(25, 22)); } [Fact] public void IOperation_01() { string source = """ class Program { static object[] Create(object x, int? y, int z) { return /*<bind>*/[x, y ?? z, z]/*</bind>*/; } } """; var comp = CreateCompilation(source); comp.VerifyEmitDiagnostics(); VerifyOperationTreeForTest<CollectionExpressionSyntax>(comp, """ ICollectionExpressionOperation (3 elements, ConstructMethod: null) (OperationKind.CollectionExpression, Type: System.Object[]) (Syntax: '[x, y ?? z, z]') Elements(3): IParameterReferenceOperation: x (OperationKind.ParameterReference, Type: System.Object) (Syntax: 'x') IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Object, IsImplicit) (Syntax: 'y ?? z') Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) Operand: ICoalesceOperation (OperationKind.Coalesce, Type: System.Int32) (Syntax: 'y ?? z') Expression: IParameterReferenceOperation: y (OperationKind.ParameterReference, Type: System.Int32?) (Syntax: 'y') ValueConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) (Identity) WhenNull: IParameterReferenceOperation: z (OperationKind.ParameterReference, Type: System.Int32) (Syntax: 'z') IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Object, IsImplicit) (Syntax: 'z') Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) Operand: IParameterReferenceOperation: z (OperationKind.ParameterReference, Type: System.Int32) (Syntax: 'z') """); var tree = comp.SyntaxTrees[0]; var method = tree.GetRoot().DescendantNodes().OfType<MethodDeclarationSyntax>().Single(m => m.Identifier.Text == "Create"); VerifyFlowGraph(comp, method, """ Block[B0] - Entry Statements (0) Next (Regular) Block[B1] Entering: {R1} .locals {R1} { CaptureIds: [0] [2] Block[B1] - Block Predecessors: [B0] Statements (1) IFlowCaptureOperation: 0 (OperationKind.FlowCapture, Type: null, IsImplicit) (Syntax: 'x') Value: IParameterReferenceOperation: x (OperationKind.ParameterReference, Type: System.Object) (Syntax: 'x') Next (Regular) Block[B2] Entering: {R2} .locals {R2} { CaptureIds: [1] Block[B2] - Block Predecessors: [B1] Statements (1) IFlowCaptureOperation: 1 (OperationKind.FlowCapture, Type: null, IsImplicit) (Syntax: 'y') Value: IParameterReferenceOperation: y (OperationKind.ParameterReference, Type: System.Int32?) (Syntax: 'y') Jump if True (Regular) to Block[B4] IIsNullOperation (OperationKind.IsNull, Type: System.Boolean, IsImplicit) (Syntax: 'y') Operand: IFlowCaptureReferenceOperation: 1 (OperationKind.FlowCaptureReference, Type: System.Int32?, IsImplicit) (Syntax: 'y') Leaving: {R2} Next (Regular) Block[B3] Block[B3] - Block Predecessors: [B2] Statements (1) IFlowCaptureOperation: 2 (OperationKind.FlowCapture, Type: null, IsImplicit) (Syntax: 'y') Value: IInvocationOperation ( System.Int32 System.Int32?.GetValueOrDefault()) (OperationKind.Invocation, Type: System.Int32, IsImplicit) (Syntax: 'y') Instance Receiver: IFlowCaptureReferenceOperation: 1 (OperationKind.FlowCaptureReference, Type: System.Int32?, IsImplicit) (Syntax: 'y') Arguments(0) Next (Regular) Block[B5] Leaving: {R2} } Block[B4] - Block Predecessors: [B2] Statements (1) IFlowCaptureOperation: 2 (OperationKind.FlowCapture, Type: null, IsImplicit) (Syntax: 'z') Value: IParameterReferenceOperation: z (OperationKind.ParameterReference, Type: System.Int32) (Syntax: 'z') Next (Regular) Block[B5] Block[B5] - Block Predecessors: [B3] [B4] Statements (0) Next (Return) Block[B6] IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Object[], IsImplicit) (Syntax: '[x, y ?? z, z]') Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) (CollectionExpression) Operand: ICollectionExpressionOperation (3 elements, ConstructMethod: null) (OperationKind.CollectionExpression, Type: System.Object[]) (Syntax: '[x, y ?? z, z]') Elements(3): IFlowCaptureReferenceOperation: 0 (OperationKind.FlowCaptureReference, Type: System.Object, IsImplicit) (Syntax: 'x') IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Object, IsImplicit) (Syntax: 'y ?? z') Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) (Boxing) Operand: IFlowCaptureReferenceOperation: 2 (OperationKind.FlowCaptureReference, Type: System.Int32, IsImplicit) (Syntax: 'y ?? z') IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Object, IsImplicit) (Syntax: 'z') Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) (Boxing) Operand: IParameterReferenceOperation: z (OperationKind.ParameterReference, Type: System.Int32) (Syntax: 'z') Leaving: {R1} } Block[B6] - Exit Predecessors: [B5] Statements (0) """); } [Fact] public void IOperation_02() { string source = """ using System.Collections.Generic; class Program { static object[] Create(IEnumerable<object> x, IEnumerable<int> y, IEnumerable<int> z) { return /*<bind>*/[.. x, .. y ?? z, .. z]/*</bind>*/; } } """; var comp = CreateCompilation(source); comp.VerifyEmitDiagnostics(); VerifyOperationTreeForTest<CollectionExpressionSyntax>(comp, """ ICollectionExpressionOperation (3 elements, ConstructMethod: null) (OperationKind.CollectionExpression, Type: System.Object[]) (Syntax: '[.. x, .. y ?? z, .. z]') Elements(3): ISpreadOperation (ElementType: System.Object) (OperationKind.Spread, Type: null) (Syntax: '.. x') Operand: IParameterReferenceOperation: x (OperationKind.ParameterReference, Type: System.Collections.Generic.IEnumerable<System.Object>) (Syntax: 'x') ElementConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) (Identity) ISpreadOperation (ElementType: System.Int32) (OperationKind.Spread, Type: null) (Syntax: '.. y ?? z') Operand: ICoalesceOperation (OperationKind.Coalesce, Type: System.Collections.Generic.IEnumerable<System.Int32>) (Syntax: 'y ?? z') Expression: IParameterReferenceOperation: y (OperationKind.ParameterReference, Type: System.Collections.Generic.IEnumerable<System.Int32>) (Syntax: 'y') ValueConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) (Identity) WhenNull: IParameterReferenceOperation: z (OperationKind.ParameterReference, Type: System.Collections.Generic.IEnumerable<System.Int32>) (Syntax: 'z') ElementConversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) (Boxing) ISpreadOperation (ElementType: System.Int32) (OperationKind.Spread, Type: null) (Syntax: '.. z') Operand: IParameterReferenceOperation: z (OperationKind.ParameterReference, Type: System.Collections.Generic.IEnumerable<System.Int32>) (Syntax: 'z') ElementConversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) (Boxing) """); var tree = comp.SyntaxTrees[0]; var method = tree.GetRoot().DescendantNodes().OfType<MethodDeclarationSyntax>().Single(m => m.Identifier.Text == "Create"); VerifyFlowGraph(comp, method, """ Block[B0] - Entry Statements (0) Next (Regular) Block[B1] Entering: {R1} .locals {R1} { CaptureIds: [0] [2] Block[B1] - Block Predecessors: [B0] Statements (1) IFlowCaptureOperation: 0 (OperationKind.FlowCapture, Type: null, IsImplicit) (Syntax: 'x') Value: IParameterReferenceOperation: x (OperationKind.ParameterReference, Type: System.Collections.Generic.IEnumerable<System.Object>) (Syntax: 'x') Next (Regular) Block[B2] Entering: {R2} .locals {R2} { CaptureIds: [1] Block[B2] - Block Predecessors: [B1] Statements (1) IFlowCaptureOperation: 1 (OperationKind.FlowCapture, Type: null, IsImplicit) (Syntax: 'y') Value: IParameterReferenceOperation: y (OperationKind.ParameterReference, Type: System.Collections.Generic.IEnumerable<System.Int32>) (Syntax: 'y') Jump if True (Regular) to Block[B4] IIsNullOperation (OperationKind.IsNull, Type: System.Boolean, IsImplicit) (Syntax: 'y') Operand: IFlowCaptureReferenceOperation: 1 (OperationKind.FlowCaptureReference, Type: System.Collections.Generic.IEnumerable<System.Int32>, IsImplicit) (Syntax: 'y') Leaving: {R2} Next (Regular) Block[B3] Block[B3] - Block Predecessors: [B2] Statements (1) IFlowCaptureOperation: 2 (OperationKind.FlowCapture, Type: null, IsImplicit) (Syntax: 'y') Value: IFlowCaptureReferenceOperation: 1 (OperationKind.FlowCaptureReference, Type: System.Collections.Generic.IEnumerable<System.Int32>, IsImplicit) (Syntax: 'y') Next (Regular) Block[B5] Leaving: {R2} } Block[B4] - Block Predecessors: [B2] Statements (1) IFlowCaptureOperation: 2 (OperationKind.FlowCapture, Type: null, IsImplicit) (Syntax: 'z') Value: IParameterReferenceOperation: z (OperationKind.ParameterReference, Type: System.Collections.Generic.IEnumerable<System.Int32>) (Syntax: 'z') Next (Regular) Block[B5] Block[B5] - Block Predecessors: [B3] [B4] Statements (0) Next (Return) Block[B6] IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Object[], IsImplicit) (Syntax: '[.. x, .. y ?? z, .. z]') Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) (CollectionExpression) Operand: ICollectionExpressionOperation (3 elements, ConstructMethod: null) (OperationKind.CollectionExpression, Type: System.Object[]) (Syntax: '[.. x, .. y ?? z, .. z]') Elements(3): ISpreadOperation (ElementType: System.Object) (OperationKind.Spread, Type: null) (Syntax: '.. x') Operand: IFlowCaptureReferenceOperation: 0 (OperationKind.FlowCaptureReference, Type: System.Collections.Generic.IEnumerable<System.Object>, IsImplicit) (Syntax: 'x') ElementConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) (Identity) ISpreadOperation (ElementType: System.Int32) (OperationKind.Spread, Type: null) (Syntax: '.. y ?? z') Operand: IFlowCaptureReferenceOperation: 2 (OperationKind.FlowCaptureReference, Type: System.Collections.Generic.IEnumerable<System.Int32>, IsImplicit) (Syntax: 'y ?? z') ElementConversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) (Boxing) ISpreadOperation (ElementType: System.Int32) (OperationKind.Spread, Type: null) (Syntax: '.. z') Operand: IParameterReferenceOperation: z (OperationKind.ParameterReference, Type: System.Collections.Generic.IEnumerable<System.Int32>) (Syntax: 'z') ElementConversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) (Boxing) Leaving: {R1} } Block[B6] - Exit Predecessors: [B5] Statements (0) """); } [Fact] public void IOperation_Array() { string source = """ class Program { static object[] Create(int x, int[] y) { return /*<bind>*/[x, ..y]/*</bind>*/; } } """; var comp = CreateCompilation(source); comp.VerifyEmitDiagnostics(); VerifyOperationTreeForTest<CollectionExpressionSyntax>(comp, """ ICollectionExpressionOperation (2 elements, ConstructMethod: null) (OperationKind.CollectionExpression, Type: System.Object[]) (Syntax: '[x, ..y]') Elements(2): IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Object, IsImplicit) (Syntax: 'x') Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) Operand: IParameterReferenceOperation: x (OperationKind.ParameterReference, Type: System.Int32) (Syntax: 'x') ISpreadOperation (ElementType: System.Int32) (OperationKind.Spread, Type: null) (Syntax: '..y') Operand: IParameterReferenceOperation: y (OperationKind.ParameterReference, Type: System.Int32[]) (Syntax: 'y') ElementConversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) (Boxing) """); var tree = comp.SyntaxTrees[0]; var method = tree.GetRoot().DescendantNodes().OfType<MethodDeclarationSyntax>().Single(m => m.Identifier.Text == "Create"); VerifyFlowGraph(comp, method, """ Block[B0] - Entry Statements (0) Next (Regular) Block[B1] Block[B1] - Block Predecessors: [B0] Statements (0) Next (Return) Block[B2] IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Object[], IsImplicit) (Syntax: '[x, ..y]') Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) (CollectionExpression) Operand: ICollectionExpressionOperation (2 elements, ConstructMethod: null) (OperationKind.CollectionExpression, Type: System.Object[]) (Syntax: '[x, ..y]') Elements(2): IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Object, IsImplicit) (Syntax: 'x') Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) (Boxing) Operand: IParameterReferenceOperation: x (OperationKind.ParameterReference, Type: System.Int32) (Syntax: 'x') ISpreadOperation (ElementType: System.Int32) (OperationKind.Spread, Type: null) (Syntax: '..y') Operand: IParameterReferenceOperation: y (OperationKind.ParameterReference, Type: System.Int32[]) (Syntax: 'y') ElementConversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) (Boxing) Block[B2] - Exit Predecessors: [B1] Statements (0) """); } [Theory] [InlineData("ReadOnlySpan")] [InlineData("Span")] public void IOperation_EmptySpan(string spanType) { string source = $$""" using System; class Program { static void Create<T>() { {{spanType}}<T> s = /*<bind>*/[]/*</bind>*/; } } """; var comp = CreateCompilation(source, targetFramework: TargetFramework.Net80); comp.VerifyEmitDiagnostics(); VerifyOperationTreeForTest<CollectionExpressionSyntax>(comp, $$""" ICollectionExpressionOperation (0 elements, ConstructMethod: null) (OperationKind.CollectionExpression, Type: System.{{spanType}}<T>) (Syntax: '[]') Elements(0) """); var tree = comp.SyntaxTrees[0]; var method = tree.GetRoot().DescendantNodes().OfType<MethodDeclarationSyntax>().Single(m => m.Identifier.Text == "Create"); VerifyFlowGraph(comp, method, $$""" Block[B0] - Entry Statements (0) Next (Regular) Block[B1] Entering: {R1} .locals {R1} { Locals: [System.{{spanType}}<T> s] Block[B1] - Block Predecessors: [B0] Statements (1) ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: System.{{spanType}}<T>, IsImplicit) (Syntax: 's = /*<bind>*/[]') Left: ILocalReferenceOperation: s (IsDeclaration: True) (OperationKind.LocalReference, Type: System.{{spanType}}<T>, IsImplicit) (Syntax: 's = /*<bind>*/[]') Right: IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.{{spanType}}<T>, IsImplicit) (Syntax: '[]') Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) (CollectionExpression) Operand: ICollectionExpressionOperation (0 elements, ConstructMethod: null) (OperationKind.CollectionExpression, Type: System.{{spanType}}<T>) (Syntax: '[]') Elements(0) Next (Regular) Block[B2] Leaving: {R1} } Block[B2] - Exit Predecessors: [B1] Statements (0) """); } [Theory] [InlineData("ReadOnlySpan")] [InlineData("Span")] public void IOperation_Span(string spanType) { string source = $$""" using System; class Program { static void Create(int x, {{spanType}}<int> y) { {{spanType}}<object> s = /*<bind>*/[x, ..y]/*</bind>*/; } } """; var comp = CreateCompilation(source, targetFramework: TargetFramework.Net70, options: TestOptions.ReleaseDll.WithSpecificDiagnosticOptions(WithSpanAllocWarning)); comp.VerifyEmitDiagnostics( // (7,23): warning CS9209: Collection expression of type 'ReadOnlySpan<object>' may incur unexpected heap allocations due to the use of '..' spreads. Consider explicitly creating an array, then converting to 'ReadOnlySpan<object>' to make the allocation explicit. // /*<bind>*/[x, ..y]/*</bind>*/; Diagnostic(ErrorCode.WRN_CollectionExpressionRefStructSpreadMayAllocate, "[x, ..y]").WithArguments($"System.{spanType}<object>").WithLocation(7, 23)); VerifyOperationTreeForTest<CollectionExpressionSyntax>(comp, $$""" ICollectionExpressionOperation (2 elements, ConstructMethod: null) (OperationKind.CollectionExpression, Type: System.{{spanType}}<System.Object>) (Syntax: '[x, ..y]') Elements(2): IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Object, IsImplicit) (Syntax: 'x') Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) Operand: IParameterReferenceOperation: x (OperationKind.ParameterReference, Type: System.Int32) (Syntax: 'x') ISpreadOperation (ElementType: System.Int32) (OperationKind.Spread, Type: null) (Syntax: '..y') Operand: IParameterReferenceOperation: y (OperationKind.ParameterReference, Type: System.{{spanType}}<System.Int32>) (Syntax: 'y') ElementConversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) (Boxing) """); var tree = comp.SyntaxTrees[0]; var method = tree.GetRoot().DescendantNodes().OfType<MethodDeclarationSyntax>().Single(m => m.Identifier.Text == "Create"); VerifyFlowGraph(comp, method, $$""" Block[B0] - Entry Statements (0) Next (Regular) Block[B1] Entering: {R1} .locals {R1} { Locals: [System.{{spanType}}<System.Object> s] Block[B1] - Block Predecessors: [B0] Statements (1) ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: System.{{spanType}}<System.Object>, IsImplicit) (Syntax: 's = ... >*/[x, ..y]') Left: ILocalReferenceOperation: s (IsDeclaration: True) (OperationKind.LocalReference, Type: System.{{spanType}}<System.Object>, IsImplicit) (Syntax: 's = ... >*/[x, ..y]') Right: IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.{{spanType}}<System.Object>, IsImplicit) (Syntax: '[x, ..y]') Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) (CollectionExpression) Operand: ICollectionExpressionOperation (2 elements, ConstructMethod: null) (OperationKind.CollectionExpression, Type: System.{{spanType}}<System.Object>) (Syntax: '[x, ..y]') Elements(2): IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Object, IsImplicit) (Syntax: 'x') Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) (Boxing) Operand: IParameterReferenceOperation: x (OperationKind.ParameterReference, Type: System.Int32) (Syntax: 'x') ISpreadOperation (ElementType: System.Int32) (OperationKind.Spread, Type: null) (Syntax: '..y') Operand: IParameterReferenceOperation: y (OperationKind.ParameterReference, Type: System.{{spanType}}<System.Int32>) (Syntax: 'y') ElementConversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) (Boxing) Next (Regular) Block[B2] Leaving: {R1} } Block[B2] - Exit Predecessors: [B1] Statements (0) """); } [Fact] public void IOperation_CollectionInitializer() { string source = """ using System.Collections; using System.Collections.Generic; struct S<T> : IEnumerable<T> { public void Add(T t) { } IEnumerator<T> IEnumerable<T>.GetEnumerator() => throw null; IEnumerator IEnumerable.GetEnumerator() => throw null; } class Program { static S<object> Create(S<int> x, int y) { return /*<bind>*/[..x, y]/*</bind>*/; } } """; var comp = CreateCompilation(source); comp.VerifyEmitDiagnostics(); VerifyOperationTreeForTest<CollectionExpressionSyntax>(comp, """ ICollectionExpressionOperation (2 elements, ConstructMethod: S<System.Object>..ctor()) (OperationKind.CollectionExpression, Type: S<System.Object>) (Syntax: '[..x, y]') Elements(2): ISpreadOperation (ElementType: System.Int32) (OperationKind.Spread, Type: null) (Syntax: '..x') Operand: IParameterReferenceOperation: x (OperationKind.ParameterReference, Type: S<System.Int32>) (Syntax: 'x') ElementConversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) (Boxing) IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Object, IsImplicit) (Syntax: 'y') Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) Operand: IParameterReferenceOperation: y (OperationKind.ParameterReference, Type: System.Int32) (Syntax: 'y') """); var tree = comp.SyntaxTrees[0]; var method = tree.GetRoot().DescendantNodes().OfType<MethodDeclarationSyntax>().Single(m => m.Identifier.Text == "Create"); VerifyFlowGraph(comp, method, """ Block[B0] - Entry Statements (0) Next (Regular) Block[B1] Block[B1] - Block Predecessors: [B0] Statements (0) Next (Return) Block[B2] IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: S<System.Object>, IsImplicit) (Syntax: '[..x, y]') Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) (CollectionExpression) Operand: ICollectionExpressionOperation (2 elements, ConstructMethod: S<System.Object>..ctor()) (OperationKind.CollectionExpression, Type: S<System.Object>) (Syntax: '[..x, y]') Elements(2): ISpreadOperation (ElementType: System.Int32) (OperationKind.Spread, Type: null) (Syntax: '..x') Operand: IParameterReferenceOperation: x (OperationKind.ParameterReference, Type: S<System.Int32>) (Syntax: 'x') ElementConversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) (Boxing) IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Object, IsImplicit) (Syntax: 'y') Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) (Boxing) Operand: IParameterReferenceOperation: y (OperationKind.ParameterReference, Type: System.Int32) (Syntax: 'y') Block[B2] - Exit Predecessors: [B1] Statements (0) """); } [Fact] public void IOperation_TypeParameter() { string source = """ using System.Collections.Generic; interface I<T> : IEnumerable<T> { void Add(T t); } class Program { static U Create<T, U>(T x, U y) where U : I<T>, new() { return /*<bind>*/[x, ..y]/*</bind>*/; } } """; var comp = CreateCompilation(source); comp.VerifyEmitDiagnostics(); VerifyOperationTreeForTest<CollectionExpressionSyntax>(comp, """ ICollectionExpressionOperation (2 elements, ConstructMethod: null) (OperationKind.CollectionExpression, Type: U) (Syntax: '[x, ..y]') Elements(2): IParameterReferenceOperation: x (OperationKind.ParameterReference, Type: T) (Syntax: 'x') ISpreadOperation (ElementType: T) (OperationKind.Spread, Type: null) (Syntax: '..y') Operand: IParameterReferenceOperation: y (OperationKind.ParameterReference, Type: U) (Syntax: 'y') ElementConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) (Identity) """); var tree = comp.SyntaxTrees[0]; var method = tree.GetRoot().DescendantNodes().OfType<MethodDeclarationSyntax>().Single(m => m.Identifier.Text == "Create"); VerifyFlowGraph(comp, method, """ Block[B0] - Entry Statements (0) Next (Regular) Block[B1] Block[B1] - Block Predecessors: [B0] Statements (0) Next (Return) Block[B2] IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: U, IsImplicit) (Syntax: '[x, ..y]') Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) (CollectionExpression) Operand: ICollectionExpressionOperation (2 elements, ConstructMethod: null) (OperationKind.CollectionExpression, Type: U) (Syntax: '[x, ..y]') Elements(2): IParameterReferenceOperation: x (OperationKind.ParameterReference, Type: T) (Syntax: 'x') ISpreadOperation (ElementType: T) (OperationKind.Spread, Type: null) (Syntax: '..y') Operand: IParameterReferenceOperation: y (OperationKind.ParameterReference, Type: U) (Syntax: 'y') ElementConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) (Identity) Block[B2] - Exit Predecessors: [B1] Statements (0) """); } [Fact] public void IOperation_List() { string source = """ using System.Collections.Generic; class Program { static List<object> Create(List<int> x, int y) { return /*<bind>*/[..x, y]/*</bind>*/; } } """; var comp = CreateCompilation(source); comp.VerifyEmitDiagnostics(); VerifyOperationTreeForTest<CollectionExpressionSyntax>(comp, """ ICollectionExpressionOperation (2 elements, ConstructMethod: System.Collections.Generic.List<System.Object>..ctor()) (OperationKind.CollectionExpression, Type: System.Collections.Generic.List<System.Object>) (Syntax: '[..x, y]') Elements(2): ISpreadOperation (ElementType: System.Int32) (OperationKind.Spread, Type: null) (Syntax: '..x') Operand: IParameterReferenceOperation: x (OperationKind.ParameterReference, Type: System.Collections.Generic.List<System.Int32>) (Syntax: 'x') ElementConversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) (Boxing) IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Object, IsImplicit) (Syntax: 'y') Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) Operand: IParameterReferenceOperation: y (OperationKind.ParameterReference, Type: System.Int32) (Syntax: 'y') """); var tree = comp.SyntaxTrees[0]; var method = tree.GetRoot().DescendantNodes().OfType<MethodDeclarationSyntax>().Single(m => m.Identifier.Text == "Create"); VerifyFlowGraph(comp, method, """ Block[B0] - Entry Statements (0) Next (Regular) Block[B1] Block[B1] - Block Predecessors: [B0] Statements (0) Next (Return) Block[B2] IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Collections.Generic.List<System.Object>, IsImplicit) (Syntax: '[..x, y]') Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) (CollectionExpression) Operand: ICollectionExpressionOperation (2 elements, ConstructMethod: System.Collections.Generic.List<System.Object>..ctor()) (OperationKind.CollectionExpression, Type: System.Collections.Generic.List<System.Object>) (Syntax: '[..x, y]') Elements(2): ISpreadOperation (ElementType: System.Int32) (OperationKind.Spread, Type: null) (Syntax: '..x') Operand: IParameterReferenceOperation: x (OperationKind.ParameterReference, Type: System.Collections.Generic.List<System.Int32>) (Syntax: 'x') ElementConversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) (Boxing) IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Object, IsImplicit) (Syntax: 'y') Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) (Boxing) Operand: IParameterReferenceOperation: y (OperationKind.ParameterReference, Type: System.Int32) (Syntax: 'y') Block[B2] - Exit Predecessors: [B1] Statements (0) """); } [Theory] [InlineData("IEnumerable")] [InlineData("IReadOnlyCollection")] [InlineData("IReadOnlyList")] [InlineData("ICollection")] [InlineData("IList")] public void IOperation_ArrayInterface(string interfaceType) { string source = $$""" using System.Collections.Generic; class Program { static {{interfaceType}}<object> Create({{interfaceType}}<int> x, int y) { return /*<bind>*/[..x, y]/*</bind>*/; } } """; var comp = CreateCompilation(source); comp.VerifyEmitDiagnostics(); VerifyOperationTreeForTest<CollectionExpressionSyntax>(comp, $$""" ICollectionExpressionOperation (2 elements, ConstructMethod: null) (OperationKind.CollectionExpression, Type: System.Collections.Generic.{{interfaceType}}<System.Object>) (Syntax: '[..x, y]') Elements(2): ISpreadOperation (ElementType: System.Int32) (OperationKind.Spread, Type: null) (Syntax: '..x') Operand: IParameterReferenceOperation: x (OperationKind.ParameterReference, Type: System.Collections.Generic.{{interfaceType}}<System.Int32>) (Syntax: 'x') ElementConversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) (Boxing) IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Object, IsImplicit) (Syntax: 'y') Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) Operand: IParameterReferenceOperation: y (OperationKind.ParameterReference, Type: System.Int32) (Syntax: 'y') """); var tree = comp.SyntaxTrees[0]; var method = tree.GetRoot().DescendantNodes().OfType<MethodDeclarationSyntax>().Single(m => m.Identifier.Text == "Create"); VerifyFlowGraph(comp, method, $$""" Block[B0] - Entry Statements (0) Next (Regular) Block[B1] Block[B1] - Block Predecessors: [B0] Statements (0) Next (Return) Block[B2] IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Collections.Generic.{{interfaceType}}<System.Object>, IsImplicit) (Syntax: '[..x, y]') Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) (CollectionExpression) Operand: ICollectionExpressionOperation (2 elements, ConstructMethod: null) (OperationKind.CollectionExpression, Type: System.Collections.Generic.{{interfaceType}}<System.Object>) (Syntax: '[..x, y]') Elements(2): ISpreadOperation (ElementType: System.Int32) (OperationKind.Spread, Type: null) (Syntax: '..x') Operand: IParameterReferenceOperation: x (OperationKind.ParameterReference, Type: System.Collections.Generic.{{interfaceType}}<System.Int32>) (Syntax: 'x') ElementConversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) (Boxing) IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Object, IsImplicit) (Syntax: 'y') Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) (Boxing) Operand: IParameterReferenceOperation: y (OperationKind.ParameterReference, Type: System.Int32) (Syntax: 'y') Block[B2] - Exit Predecessors: [B1] Statements (0) """); } [Fact] public void IOperation_CollectionBuilder_IdentityConversion() { string source = """ using System; using System.Collections; using System.Collections.Generic; using System.Runtime.CompilerServices; [CollectionBuilder(typeof(MyCollectionBuilder), nameof(MyCollectionBuilder.Create))] struct MyCollection<T> : IEnumerable<T> { IEnumerator<T> IEnumerable<T>.GetEnumerator() => null; IEnumerator IEnumerable.GetEnumerator() => null; } class MyCollectionBuilder { public static MyCollection<T> Create<T>(ReadOnlySpan<T> items) => default; } class Program { static MyCollection<object> CreateCollection(MyCollection<int> x, int y) { /*<bind>*/return [..x, y];/*</bind>*/ } } """; var comp = CreateCompilation(source, targetFramework: TargetFramework.Net80); comp.VerifyEmitDiagnostics(); var operation = VerifyOperationTreeForTest<ReturnStatementSyntax>(comp, """ IReturnOperation (OperationKind.Return, Type: null) (Syntax: 'return [..x, y];') ReturnedValue: IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: MyCollection<System.Object>, IsImplicit) (Syntax: '[..x, y]') Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) Operand: ICollectionExpressionOperation (2 elements, ConstructMethod: MyCollection<System.Object> MyCollectionBuilder.Create<System.Object>(System.ReadOnlySpan<System.Object> items)) (OperationKind.CollectionExpression, Type: MyCollection<System.Object>) (Syntax: '[..x, y]') ConstructArguments(1): IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: items) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: '[..x, y]') ICollectionExpressionElementsPlaceholderOperation (OperationKind.CollectionExpressionElementsPlaceholder, Type: System.ReadOnlySpan<System.Object>, IsImplicit) (Syntax: '[..x, y]') InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) Elements(2): ISpreadOperation (ElementType: System.Int32) (OperationKind.Spread, Type: null) (Syntax: '..x') Operand: IParameterReferenceOperation: x (OperationKind.ParameterReference, Type: MyCollection<System.Int32>) (Syntax: 'x') ElementConversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) (Boxing) IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Object, IsImplicit) (Syntax: 'y') Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) Operand: IParameterReferenceOperation: y (OperationKind.ParameterReference, Type: System.Int32) (Syntax: 'y') """); var conversionOperation = (IConversionOperation)((IReturnOperation)operation).ReturnedValue; Conversion conversion = conversionOperation.GetConversion(); Assert.Equal(ConversionKind.CollectionExpression, conversion.Kind); AssertEx.SequenceEqual([Conversion.Boxing, Conversion.Boxing], conversion.UnderlyingConversions); } [Fact] public void IOperation_CollectionBuilder_BoxingConversion() { string source = """ using System; using System.Collections; using System.Collections.Generic; using System.Runtime.CompilerServices; [CollectionBuilder(typeof(MyCollectionBuilder), nameof(MyCollectionBuilder.Create))] interface IMyCollection<T> : IEnumerable<T> { } struct MyCollection<T> : IMyCollection<T> { public MyCollection(ReadOnlySpan<T> span) { } public IEnumerator<T> GetEnumerator() => null; IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); } class MyCollectionBuilder { public static MyCollection<T> Create<T>(ReadOnlySpan<T> items) { return new MyCollection<T>(items); } } class Program { static IMyCollection<T> CreateCollection<T>(T a, T b) { /*<bind>*/return [a, b];/*</bind>*/ } } """; var comp = CreateCompilation(source, targetFramework: TargetFramework.Net80); comp.VerifyEmitDiagnostics(); var operation = VerifyOperationTreeForTest<ReturnStatementSyntax>(comp, """ IReturnOperation (OperationKind.Return, Type: null) (Syntax: 'return [a, b];') ReturnedValue: IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: IMyCollection<T>, IsImplicit) (Syntax: '[a, b]') Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) Operand: ICollectionExpressionOperation (2 elements, ConstructMethod: MyCollection<T> MyCollectionBuilder.Create<T>(System.ReadOnlySpan<T> items)) (OperationKind.CollectionExpression, Type: IMyCollection<T>) (Syntax: '[a, b]') ConstructArguments(1): IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: items) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: '[a, b]') ICollectionExpressionElementsPlaceholderOperation (OperationKind.CollectionExpressionElementsPlaceholder, Type: System.ReadOnlySpan<T>, IsImplicit) (Syntax: '[a, b]') InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) Elements(2): IParameterReferenceOperation: a (OperationKind.ParameterReference, Type: T) (Syntax: 'a') IParameterReferenceOperation: b (OperationKind.ParameterReference, Type: T) (Syntax: 'b') """); var conversionOperation = (IConversionOperation)((IReturnOperation)operation).ReturnedValue; var expectedConversion = new Conversion(ConversionKind.CollectionExpression, nestedConversions: ImmutableArray.Create(Conversion.Identity, Conversion.Identity)); Conversion actual = conversionOperation.GetConversion(); Assert.Equal(ConversionKind.CollectionExpression, actual.Kind); AssertEx.SequenceEqual(ImmutableArray.Create(Conversion.Identity, Conversion.Identity), actual.UnderlyingConversions); } [Fact] public void IOperation_ImmutableArray() { string source = """ using System.Collections.Immutable; class Program { static ImmutableArray<object> Create(ImmutableArray<int> x, int y) { return /*<bind>*/[..x, y]/*</bind>*/; } } """; var comp = CreateCompilation(source, targetFramework: TargetFramework.Net80); comp.VerifyEmitDiagnostics(); VerifyOperationTreeForTest<CollectionExpressionSyntax>(comp, """ ICollectionExpressionOperation (2 elements, ConstructMethod: System.Collections.Immutable.ImmutableArray<System.Object> System.Collections.Immutable.ImmutableArray.Create<System.Object>(System.ReadOnlySpan<System.Object> items)) (OperationKind.CollectionExpression, Type: System.Collections.Immutable.ImmutableArray<System.Object>) (Syntax: '[..x, y]') ConstructArguments(1): IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: items) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: '[..x, y]') ICollectionExpressionElementsPlaceholderOperation (OperationKind.CollectionExpressionElementsPlaceholder, Type: System.ReadOnlySpan<System.Object>, IsImplicit) (Syntax: '[..x, y]') InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) Elements(2): ISpreadOperation (ElementType: System.Int32) (OperationKind.Spread, Type: null) (Syntax: '..x') Operand: IParameterReferenceOperation: x (OperationKind.ParameterReference, Type: System.Collections.Immutable.ImmutableArray<System.Int32>) (Syntax: 'x') ElementConversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) (Boxing) IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Object, IsImplicit) (Syntax: 'y') Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) Operand: IParameterReferenceOperation: y (OperationKind.ParameterReference, Type: System.Int32) (Syntax: 'y') """); var tree = comp.SyntaxTrees[0]; var method = tree.GetRoot().DescendantNodes().OfType<MethodDeclarationSyntax>().Single(m => m.Identifier.Text == "Create"); VerifyFlowGraph(comp, method, """ Block[B0] - Entry Statements (0) Next (Regular) Block[B1] Block[B1] - Block Predecessors: [B0] Statements (0) Next (Return) Block[B2] IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Collections.Immutable.ImmutableArray<System.Object>, IsImplicit) (Syntax: '[..x, y]') Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) (CollectionExpression) Operand: ICollectionExpressionOperation (2 elements, ConstructMethod: System.Collections.Immutable.ImmutableArray<System.Object> System.Collections.Immutable.ImmutableArray.Create<System.Object>(System.ReadOnlySpan<System.Object> items)) (OperationKind.CollectionExpression, Type: System.Collections.Immutable.ImmutableArray<System.Object>) (Syntax: '[..x, y]') ConstructArguments(1): IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: items) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: '[..x, y]') ICollectionExpressionElementsPlaceholderOperation (OperationKind.CollectionExpressionElementsPlaceholder, Type: System.ReadOnlySpan<System.Object>, IsImplicit) (Syntax: '[..x, y]') InConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) OutConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) Elements(2): ISpreadOperation (ElementType: System.Int32) (OperationKind.Spread, Type: null) (Syntax: '..x') Operand: IParameterReferenceOperation: x (OperationKind.ParameterReference, Type: System.Collections.Immutable.ImmutableArray<System.Int32>) (Syntax: 'x') ElementConversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) (Boxing) IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Object, IsImplicit) (Syntax: 'y') Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) (Boxing) Operand: IParameterReferenceOperation: y (OperationKind.ParameterReference, Type: System.Int32) (Syntax: 'y') Block[B2] - Exit Predecessors: [B1] Statements (0) """); } [Fact] public void IOperation_NullableConversion() { string source = """ using System.Collections; using System.Collections.Generic; struct MyCollection<T> : IEnumerable<T> { public void Add(T t) { } IEnumerator<T> IEnumerable<T>.GetEnumerator() => null; IEnumerator IEnumerable.GetEnumerator() => null; } class Program { static MyCollection<T>? CreateCollection<T>(T a, T b) { return /*<bind>*/[a, b]/*</bind>*/; } } """; var comp = CreateCompilation(source, targetFramework: TargetFramework.Net80); comp.VerifyEmitDiagnostics(); VerifyOperationTreeForTest<CollectionExpressionSyntax>(comp, """ ICollectionExpressionOperation (2 elements, ConstructMethod: MyCollection<T>..ctor()) (OperationKind.CollectionExpression, Type: MyCollection<T>) (Syntax: '[a, b]') Elements(2): IParameterReferenceOperation: a (OperationKind.ParameterReference, Type: T) (Syntax: 'a') IParameterReferenceOperation: b (OperationKind.ParameterReference, Type: T) (Syntax: 'b') """); var tree = comp.SyntaxTrees[0]; var method = tree.GetRoot().DescendantNodes().OfType<MethodDeclarationSyntax>().Single(m => m.Identifier.Text == "CreateCollection"); VerifyFlowGraph(comp, method, """ Block[B0] - Entry Statements (0) Next (Regular) Block[B1] Block[B1] - Block Predecessors: [B0] Statements (0) Next (Return) Block[B2] IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: MyCollection<T>?, IsImplicit) (Syntax: '[a, b]') Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) (ImplicitNullable) Operand: ICollectionExpressionOperation (2 elements, ConstructMethod: MyCollection<T>..ctor()) (OperationKind.CollectionExpression, Type: MyCollection<T>) (Syntax: '[a, b]') Elements(2): IParameterReferenceOperation: a (OperationKind.ParameterReference, Type: T) (Syntax: 'a') IParameterReferenceOperation: b (OperationKind.ParameterReference, Type: T) (Syntax: 'b') Block[B2] - Exit Predecessors: [B1] Statements (0) """); } [Fact] public void IOperation_None() { string source = """ class Program { static object Create<T>(T x, T[] y) { return /*<bind>*/[x, ..y]/*</bind>*/; } } """; var comp = CreateCompilation(source); comp.VerifyEmitDiagnostics( // (5,26): error CS9174: Cannot initialize type 'object' with a collection expression because the type is not constructible. // return /*<bind>*/[x, ..y]/*</bind>*/; Diagnostic(ErrorCode.ERR_CollectionExpressionTargetTypeNotConstructible, "[x, ..y]").WithArguments("object").WithLocation(5, 26)); VerifyOperationTreeForTest<CollectionExpressionSyntax>(comp, """ ICollectionExpressionOperation (2 elements, ConstructMethod: null) (OperationKind.CollectionExpression, Type: System.Object, IsInvalid) (Syntax: '[x, ..y]') Elements(2): IParameterReferenceOperation: x (OperationKind.ParameterReference, Type: T, IsInvalid) (Syntax: 'x') ISpreadOperation (ElementType: T) (OperationKind.Spread, Type: null, IsInvalid) (Syntax: '..y') Operand: IParameterReferenceOperation: y (OperationKind.ParameterReference, Type: T[], IsInvalid) (Syntax: 'y') ElementConversion: CommonConversion (Exists: False, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) (NoConversion) """); var tree = comp.SyntaxTrees[0]; var method = tree.GetRoot().DescendantNodes().OfType<MethodDeclarationSyntax>().Single(m => m.Identifier.Text == "Create"); VerifyFlowGraph(comp, method, """ Block[B0] - Entry Statements (0) Next (Regular) Block[B1] Block[B1] - Block Predecessors: [B0] Statements (0) Next (Return) Block[B2] IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Object, IsInvalid, IsImplicit) (Syntax: '[x, ..y]') Conversion: CommonConversion (Exists: False, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) (NoConversion) Operand: ICollectionExpressionOperation (2 elements, ConstructMethod: null) (OperationKind.CollectionExpression, Type: System.Object, IsInvalid) (Syntax: '[x, ..y]') Elements(2): IParameterReferenceOperation: x (OperationKind.ParameterReference, Type: T, IsInvalid) (Syntax: 'x') ISpreadOperation (ElementType: T) (OperationKind.Spread, Type: null, IsInvalid) (Syntax: '..y') Operand: IParameterReferenceOperation: y (OperationKind.ParameterReference, Type: T[], IsInvalid) (Syntax: 'y') ElementConversion: CommonConversion (Exists: False, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) (NoConversion) Block[B2] - Exit Predecessors: [B1] Statements (0) """); } [Fact] public void IOperation_Dynamic() { string source = """ class Program { static void Main() { dynamic x = new[] { 1, 2, 3 }; object[] y = /*<bind>*/[..x]/*</bind>*/; } } """; var comp = CreateCompilation(source, references: new[] { CSharpRef }); comp.VerifyEmitDiagnostics(); VerifyOperationTreeForTest<CollectionExpressionSyntax>(comp, """ ICollectionExpressionOperation (1 elements, ConstructMethod: null) (OperationKind.CollectionExpression, Type: System.Object[]) (Syntax: '[..x]') Elements(1): ISpreadOperation (ElementType: System.Object) (OperationKind.Spread, Type: null) (Syntax: '..x') Operand: ILocalReferenceOperation: x (OperationKind.LocalReference, Type: dynamic) (Syntax: 'x') ElementConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) (Identity) """); } [Fact] public void IOperation_SpreadElementConversion_Boxing() { string source = """ class Program { static void Main() { int[] x = [1, 2, 3]; object[] y = [/*<bind>*/..x/*</bind>*/]; } } """; var comp = CreateCompilation(source); comp.VerifyEmitDiagnostics(); (IOperation operation, _) = GetOperationAndSyntaxForTest<SpreadElementSyntax>(comp); var spread = (ISpreadOperation)operation; Assert.Equal(Conversion.Boxing, spread.GetElementConversion()); } [Fact] public void IOperation_SpreadElementConversion_ImplicitReference() { string source = """ class Program { static void Main() { string[] x = [""]; object[] y = [/*<bind>*/..x/*</bind>*/]; } } """; var comp = CreateCompilation(source); comp.VerifyEmitDiagnostics(); (IOperation operation, _) = GetOperationAndSyntaxForTest<SpreadElementSyntax>(comp); var spread = (ISpreadOperation)operation; Assert.Equal(Conversion.ImplicitReference, spread.GetElementConversion()); } [Fact] public void IOperation_SpreadElementConversion_ArgumentNullException() { ISpreadOperation spread = null; Assert.Throws<ArgumentNullException>(() => spread.GetElementConversion()); } [Fact] public void IOperation_SpreadElementConversion_ArgumentException() { ISpreadOperation spread = new MySpreadOperation(); Assert.Throws<ArgumentException>(() => spread.GetElementConversion()); } class MySpreadOperation : ISpreadOperation { IOperation ISpreadOperation.Operand => throw null; ITypeSymbol ISpreadOperation.ElementType => throw null; CommonConversion ISpreadOperation.ElementConversion => throw null; IOperation IOperation.Parent => throw null; OperationKind IOperation.Kind => throw null; SyntaxNode IOperation.Syntax => throw null; ITypeSymbol IOperation.Type => throw null; Optional<object> IOperation.ConstantValue => throw null; IEnumerable<IOperation> IOperation.Children => throw null; IOperation.OperationList IOperation.ChildOperations => throw null; string IOperation.Language => ""; bool IOperation.IsImplicit => throw null; SemanticModel IOperation.SemanticModel => throw null; void IOperation.Accept(OperationVisitor visitor) { } TResult IOperation.Accept<TArgument, TResult>(OperationVisitor<TArgument, TResult> visitor, TArgument argument) where TResult : default => throw null; } [Fact] public void IOperation_SpreadElement_NoConversion_01() { string source = """ using System.Collections; using System.Collections.Generic; class MyCollection : IEnumerable<int> { private List<int> _list = new(); public void Add(int i) { _list.Add(i); } IEnumerator<int> IEnumerable<int>.GetEnumerator() => _list.GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => _list.GetEnumerator(); } class Program { static void Main() { IEnumerable x = new int[] { 1 }; MyCollection y = /*<bind>*/[..x]/*</bind>*/; } } """; var comp = CreateCompilation(source); comp.VerifyEmitDiagnostics( // (15,39): error CS0029: Cannot implicitly convert type 'object' to 'int' // MyCollection y = /*<bind>*/[..x]/*</bind>*/; Diagnostic(ErrorCode.ERR_NoImplicitConv, "x").WithArguments("object", "int").WithLocation(15, 39)); VerifyOperationTreeForTest<CollectionExpressionSyntax>(comp, """ ICollectionExpressionOperation (1 elements, ConstructMethod: null) (OperationKind.CollectionExpression, Type: MyCollection, IsInvalid) (Syntax: '[..x]') Elements(1): ISpreadOperation (ElementType: System.Object) (OperationKind.Spread, Type: null, IsInvalid) (Syntax: '..x') Operand: ILocalReferenceOperation: x (OperationKind.LocalReference, Type: System.Collections.IEnumerable, IsInvalid) (Syntax: 'x') ElementConversion: CommonConversion (Exists: False, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) (NoConversion) """); } [Fact] public void IOperation_SpreadElement_NoConversion_02() { string source = """ using System.Collections; using System.Collections.Generic; class MyCollection : IEnumerable<int> { private List<int> _list = new(); public void Add(int i) { _list.Add(i); } IEnumerator<int> IEnumerable<int>.GetEnumerator() => _list.GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => _list.GetEnumerator(); } class Program { static void Main() { IEnumerable<object> x = new object[] { 1 }; MyCollection y = /*<bind>*/[..x]/*</bind>*/; } } """; var comp = CreateCompilation(source); comp.VerifyEmitDiagnostics( // (15,39): error CS0029: Cannot implicitly convert type 'object' to 'int' // MyCollection y = /*<bind>*/[..x]/*</bind>*/; Diagnostic(ErrorCode.ERR_NoImplicitConv, "x").WithArguments("object", "int").WithLocation(15, 39)); VerifyOperationTreeForTest<CollectionExpressionSyntax>(comp, """ ICollectionExpressionOperation (1 elements, ConstructMethod: null) (OperationKind.CollectionExpression, Type: MyCollection, IsInvalid) (Syntax: '[..x]') Elements(1): ISpreadOperation (ElementType: System.Object) (OperationKind.Spread, Type: null, IsInvalid) (Syntax: '..x') Operand: ILocalReferenceOperation: x (OperationKind.LocalReference, Type: System.Collections.Generic.IEnumerable<System.Object>, IsInvalid) (Syntax: 'x') ElementConversion: CommonConversion (Exists: False, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) (NoConversion) """); } [WorkItem("https://github.com/dotnet/roslyn/issues/71339")] [Fact] public void IOperation_SpreadElement_NoConversion_03() { string source = """ using System.Collections.Generic; class Test { Dictionary<string, object> Config => /*<bind>*/[ .. GetConfig(), ]/*</bind>*/; private Dictionary<string, object> GetConfig() => new(); } """; var comp = CreateCompilation(source); comp.VerifyEmitDiagnostics( // (4,52): error CS9215: Collection expression type 'Dictionary<string, object>' must have an instance or extension method 'Add' that can be called with a single argument. // Dictionary<string, object> Config => /*<bind>*/[ Diagnostic(ErrorCode.ERR_CollectionExpressionMissingAdd, @"[ .. GetConfig(), ]").WithArguments("System.Collections.Generic.Dictionary<string, object>").WithLocation(4, 52)); VerifyOperationTreeForTest<CollectionExpressionSyntax>(comp, """ ICollectionExpressionOperation (1 elements, ConstructMethod: null) (OperationKind.CollectionExpression, Type: System.Collections.Generic.Dictionary<System.String, System.Object>, IsInvalid) (Syntax: '[ ... ]') Elements(1): ISpreadOperation (ElementType: System.Collections.Generic.KeyValuePair<System.String, System.Object>) (OperationKind.Spread, Type: null, IsInvalid) (Syntax: '.. GetConfig()') Operand: IInvocationOperation ( System.Collections.Generic.Dictionary<System.String, System.Object> Test.GetConfig()) (OperationKind.Invocation, Type: System.Collections.Generic.Dictionary<System.String, System.Object>, IsInvalid) (Syntax: 'GetConfig()') Instance Receiver: IInstanceReferenceOperation (ReferenceKind: ContainingTypeInstance) (OperationKind.InstanceReference, Type: Test, IsInvalid, IsImplicit) (Syntax: 'GetConfig') Arguments(0) ElementConversion: CommonConversion (Exists: False, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) (NoConversion) """); } [Fact] public void IOperation_AmbiguousAdd_01() { string sourceA = """ using System.Collections; interface IA { } interface IB { } class MyCollection : IEnumerable { IEnumerator IEnumerable.GetEnumerator() => throw null; public void Add(IA a) => throw null; public void Add(IB b) => throw null; public void Add(object o) => throw null; } """; string sourceB = """ class C : IA, IB { } class Program { static MyCollection Create(C x, C[] y) { return /*<bind>*/[x, ..y]/*</bind>*/; } } """; var comp = CreateCompilation([sourceB, sourceA]); comp.VerifyEmitDiagnostics( // (6,27): error CS0121: The call is ambiguous between the following methods or properties: 'MyCollection.Add(IA)' and 'MyCollection.Add(IB)' // return /*<bind>*/[x, ..y]/*</bind>*/; Diagnostic(ErrorCode.ERR_AmbigCall, "x").WithArguments("MyCollection.Add(IA)", "MyCollection.Add(IB)").WithLocation(6, 27), // (6,32): error CS0121: The call is ambiguous between the following methods or properties: 'MyCollection.Add(IA)' and 'MyCollection.Add(IB)' // return /*<bind>*/[x, ..y]/*</bind>*/; Diagnostic(ErrorCode.ERR_AmbigCall, "y").WithArguments("MyCollection.Add(IA)", "MyCollection.Add(IB)").WithLocation(6, 32)); VerifyOperationTreeForTest<CollectionExpressionSyntax>(comp, """ ICollectionExpressionOperation (2 elements, ConstructMethod: MyCollection..ctor()) (OperationKind.CollectionExpression, Type: MyCollection, IsInvalid) (Syntax: '[x, ..y]') Elements(2): IParameterReferenceOperation: x (OperationKind.ParameterReference, Type: C, IsInvalid) (Syntax: 'x') ISpreadOperation (ElementType: C) (OperationKind.Spread, Type: null, IsInvalid) (Syntax: '..y') Operand: IParameterReferenceOperation: y (OperationKind.ParameterReference, Type: C[], IsInvalid) (Syntax: 'y') ElementConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) (Identity) """); var tree = comp.SyntaxTrees[0]; var method = tree.GetRoot().DescendantNodes().OfType<MethodDeclarationSyntax>().Single(m => m.Identifier.Text == "Create"); VerifyFlowGraph(comp, method, """ Block[B0] - Entry Statements (0) Next (Regular) Block[B1] Block[B1] - Block Predecessors: [B0] Statements (0) Next (Return) Block[B2] IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: MyCollection, IsInvalid, IsImplicit) (Syntax: '[x, ..y]') Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) (CollectionExpression) Operand: ICollectionExpressionOperation (2 elements, ConstructMethod: MyCollection..ctor()) (OperationKind.CollectionExpression, Type: MyCollection, IsInvalid) (Syntax: '[x, ..y]') Elements(2): IParameterReferenceOperation: x (OperationKind.ParameterReference, Type: C, IsInvalid) (Syntax: 'x') ISpreadOperation (ElementType: C) (OperationKind.Spread, Type: null, IsInvalid) (Syntax: '..y') Operand: IParameterReferenceOperation: y (OperationKind.ParameterReference, Type: C[], IsInvalid) (Syntax: 'y') ElementConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) (Identity) Block[B2] - Exit Predecessors: [B1] Statements (0) """); } [Fact] public void IOperation_AmbiguousAdd_02() { string sourceA = """ using System.Collections; interface IA { } interface IB { } class MyCollection : IEnumerable { IEnumerator IEnumerable.GetEnumerator() => throw null; public void Add(IA a) => throw null; public void Add(IB b) => throw null; } static class Extensions { public static void Add(this MyCollection collection, object o) { } } """; string sourceB = """ class C : IA, IB { } class Program { static MyCollection Create(C x, C[] y) { return /*<bind>*/[x, ..y]/*</bind>*/; } } """; var comp = CreateCompilation([sourceB, sourceA]); comp.VerifyEmitDiagnostics( // (6,27): error CS0121: The call is ambiguous between the following methods or properties: 'MyCollection.Add(IA)' and 'MyCollection.Add(IB)' // return /*<bind>*/[x, ..y]/*</bind>*/; Diagnostic(ErrorCode.ERR_AmbigCall, "x").WithArguments("MyCollection.Add(IA)", "MyCollection.Add(IB)").WithLocation(6, 27), // (6,32): error CS0121: The call is ambiguous between the following methods or properties: 'MyCollection.Add(IA)' and 'MyCollection.Add(IB)' // return /*<bind>*/[x, ..y]/*</bind>*/; Diagnostic(ErrorCode.ERR_AmbigCall, "y").WithArguments("MyCollection.Add(IA)", "MyCollection.Add(IB)").WithLocation(6, 32)); VerifyOperationTreeForTest<CollectionExpressionSyntax>(comp, """ ICollectionExpressionOperation (2 elements, ConstructMethod: MyCollection..ctor()) (OperationKind.CollectionExpression, Type: MyCollection, IsInvalid) (Syntax: '[x, ..y]') Elements(2): IParameterReferenceOperation: x (OperationKind.ParameterReference, Type: C, IsInvalid) (Syntax: 'x') ISpreadOperation (ElementType: C) (OperationKind.Spread, Type: null, IsInvalid) (Syntax: '..y') Operand: IParameterReferenceOperation: y (OperationKind.ParameterReference, Type: C[], IsInvalid) (Syntax: 'y') ElementConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) (Identity) """); var tree = comp.SyntaxTrees[0]; var method = tree.GetRoot().DescendantNodes().OfType<MethodDeclarationSyntax>().Single(m => m.Identifier.Text == "Create"); VerifyFlowGraph(comp, method, """ Block[B0] - Entry Statements (0) Next (Regular) Block[B1] Block[B1] - Block Predecessors: [B0] Statements (0) Next (Return) Block[B2] IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: MyCollection, IsInvalid, IsImplicit) (Syntax: '[x, ..y]') Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) (CollectionExpression) Operand: ICollectionExpressionOperation (2 elements, ConstructMethod: MyCollection..ctor()) (OperationKind.CollectionExpression, Type: MyCollection, IsInvalid) (Syntax: '[x, ..y]') Elements(2): IParameterReferenceOperation: x (OperationKind.ParameterReference, Type: C, IsInvalid) (Syntax: 'x') ISpreadOperation (ElementType: C) (OperationKind.Spread, Type: null, IsInvalid) (Syntax: '..y') Operand: IParameterReferenceOperation: y (OperationKind.ParameterReference, Type: C[], IsInvalid) (Syntax: 'y') ElementConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) (Identity) Block[B2] - Exit Predecessors: [B1] Statements (0) """); } [Fact] public void IOperation_AmbiguousAdd_03() { string sourceA = """ using System.Collections; using System.Collections.Generic; class MyCollection<T> : IEnumerable<T> { IEnumerator<T> IEnumerable<T>.GetEnumerator() => throw null; IEnumerator IEnumerable.GetEnumerator() => throw null; } static class ExtensionsA { public static void Add<T>(this MyCollection<T> collection, string s) { } } static class ExtensionsB { public static void Add<T>(this MyCollection<T> collection, string s) { } } namespace N { static class ExtensionsC { public static void Add<T>(this MyCollection<T> collection, T t) { } } } """; string sourceB = """ using N; class Program { static MyCollection<object> Create(string x, string[] y) { return /*<bind>*/[x, ..y]/*</bind>*/; } } """; var comp = CreateCompilation([sourceB, sourceA]); comp.VerifyEmitDiagnostics( // (6,27): error CS0121: The call is ambiguous between the following methods or properties: 'ExtensionsA.Add<T>(MyCollection<T>, string)' and 'ExtensionsB.Add<T>(MyCollection<T>, string)' // return /*<bind>*/[x, ..y]/*</bind>*/; Diagnostic(ErrorCode.ERR_AmbigCall, "x").WithArguments("ExtensionsA.Add<T>(MyCollection<T>, string)", "ExtensionsB.Add<T>(MyCollection<T>, string)").WithLocation(6, 27), // (6,32): error CS0121: The call is ambiguous between the following methods or properties: 'ExtensionsA.Add<T>(MyCollection<T>, string)' and 'ExtensionsB.Add<T>(MyCollection<T>, string)' // return /*<bind>*/[x, ..y]/*</bind>*/; Diagnostic(ErrorCode.ERR_AmbigCall, "y").WithArguments("ExtensionsA.Add<T>(MyCollection<T>, string)", "ExtensionsB.Add<T>(MyCollection<T>, string)").WithLocation(6, 32)); VerifyOperationTreeForTest<CollectionExpressionSyntax>(comp, """ ICollectionExpressionOperation (2 elements, ConstructMethod: MyCollection<System.Object>..ctor()) (OperationKind.CollectionExpression, Type: MyCollection<System.Object>, IsInvalid) (Syntax: '[x, ..y]') Elements(2): IParameterReferenceOperation: x (OperationKind.ParameterReference, Type: System.String, IsInvalid) (Syntax: 'x') ISpreadOperation (ElementType: System.String) (OperationKind.Spread, Type: null, IsInvalid) (Syntax: '..y') Operand: IParameterReferenceOperation: y (OperationKind.ParameterReference, Type: System.String[], IsInvalid) (Syntax: 'y') ElementConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) (Identity) """); var tree = comp.SyntaxTrees[0]; var method = tree.GetRoot().DescendantNodes().OfType<MethodDeclarationSyntax>().Single(m => m.Identifier.Text == "Create"); VerifyFlowGraph(comp, method, """ Block[B0] - Entry Statements (0) Next (Regular) Block[B1] Block[B1] - Block Predecessors: [B0] Statements (0) Next (Return) Block[B2] IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: MyCollection<System.Object>, IsInvalid, IsImplicit) (Syntax: '[x, ..y]') Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) (CollectionExpression) Operand: ICollectionExpressionOperation (2 elements, ConstructMethod: MyCollection<System.Object>..ctor()) (OperationKind.CollectionExpression, Type: MyCollection<System.Object>, IsInvalid) (Syntax: '[x, ..y]') Elements(2): IParameterReferenceOperation: x (OperationKind.ParameterReference, Type: System.String, IsInvalid) (Syntax: 'x') ISpreadOperation (ElementType: System.String) (OperationKind.Spread, Type: null, IsInvalid) (Syntax: '..y') Operand: IParameterReferenceOperation: y (OperationKind.ParameterReference, Type: System.String[], IsInvalid) (Syntax: 'y') ElementConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) (Identity) Block[B2] - Exit Predecessors: [B1] Statements (0) """); } [Fact] public void IOperation_InvalidAdd_01() { string sourceA = """ using System.Collections; public class MyCollection<T> : IEnumerable { IEnumerator IEnumerable.GetEnumerator() => throw null; public void Add(string s) { } } """; string sourceB = """ class Program { static MyCollection<int> Create() { return /*<bind>*/[F1(), ..F2()]/*</bind>*/; } static int F1() => 1; static int[] F2() => [2, 3]; } """; var comp = CreateCompilation([sourceB, sourceA]); comp.VerifyEmitDiagnostics( // (5,27): error CS1950: The best overloaded Add method 'MyCollection<int>.Add(string)' for the collection initializer has some invalid arguments // return /*<bind>*/[F1(), ..F2()]/*</bind>*/; Diagnostic(ErrorCode.ERR_BadArgTypesForCollectionAdd, "F1()").WithArguments("MyCollection<int>.Add(string)").WithLocation(5, 27), // (5,27): error CS1503: Argument 1: cannot convert from 'int' to 'string' // return /*<bind>*/[F1(), ..F2()]/*</bind>*/; Diagnostic(ErrorCode.ERR_BadArgType, "F1()").WithArguments("1", "int", "string").WithLocation(5, 27), // (5,33): error CS1503: Argument 1: cannot convert from 'int' to 'string' // return /*<bind>*/[F1(), ..F2()]/*</bind>*/; Diagnostic(ErrorCode.ERR_BadArgType, "..F2()").WithArguments("1", "int", "string").WithLocation(5, 33), // (5,35): error CS1950: The best overloaded Add method 'MyCollection<int>.Add(string)' for the collection initializer has some invalid arguments // return /*<bind>*/[F1(), ..F2()]/*</bind>*/; Diagnostic(ErrorCode.ERR_BadArgTypesForCollectionAdd, "F2()").WithArguments("MyCollection<int>.Add(string)").WithLocation(5, 35)); VerifyOperationTreeForTest<CollectionExpressionSyntax>(comp, """ ICollectionExpressionOperation (2 elements, ConstructMethod: MyCollection<System.Int32>..ctor()) (OperationKind.CollectionExpression, Type: MyCollection<System.Int32>, IsInvalid) (Syntax: '[F1(), ..F2()]') Elements(2): IInvocationOperation (System.Int32 Program.F1()) (OperationKind.Invocation, Type: System.Int32, IsInvalid) (Syntax: 'F1()') Instance Receiver: null Arguments(0) ISpreadOperation (ElementType: System.Int32) (OperationKind.Spread, Type: null, IsInvalid) (Syntax: '..F2()') Operand: IInvocationOperation (System.Int32[] Program.F2()) (OperationKind.Invocation, Type: System.Int32[], IsInvalid) (Syntax: 'F2()') Instance Receiver: null Arguments(0) ElementConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) (Identity) """); var tree = comp.SyntaxTrees[0]; var method = tree.GetRoot().DescendantNodes().OfType<MethodDeclarationSyntax>().Single(m => m.Identifier.Text == "Create"); VerifyFlowGraph(comp, method, """ Block[B0] - Entry Statements (0) Next (Regular) Block[B1] Block[B1] - Block Predecessors: [B0] Statements (0) Next (Return) Block[B2] IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: MyCollection<System.Int32>, IsInvalid, IsImplicit) (Syntax: '[F1(), ..F2()]') Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) (CollectionExpression) Operand: ICollectionExpressionOperation (2 elements, ConstructMethod: MyCollection<System.Int32>..ctor()) (OperationKind.CollectionExpression, Type: MyCollection<System.Int32>, IsInvalid) (Syntax: '[F1(), ..F2()]') Elements(2): IInvocationOperation (System.Int32 Program.F1()) (OperationKind.Invocation, Type: System.Int32, IsInvalid) (Syntax: 'F1()') Instance Receiver: null Arguments(0) ISpreadOperation (ElementType: System.Int32) (OperationKind.Spread, Type: null, IsInvalid) (Syntax: '..F2()') Operand: IInvocationOperation (System.Int32[] Program.F2()) (OperationKind.Invocation, Type: System.Int32[], IsInvalid) (Syntax: 'F2()') Instance Receiver: null Arguments(0) ElementConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) (Identity) Block[B2] - Exit Predecessors: [B1] Statements (0) """); } [Fact] public void IOperation_InvalidAdd_02() { string sourceA = """ using System.Collections; public class MyCollection<T> : IEnumerable { IEnumerator IEnumerable.GetEnumerator() => throw null; } public static class Extensions { public static void Add<T>(this MyCollection<T> collection, string s) { } } """; string sourceB = """ class Program { static MyCollection<int> Create() { return /*<bind>*/[F1(), ..F2()]/*</bind>*/; } static int F1() => 1; static int[] F2() => [2, 3]; } """; var comp = CreateCompilation([sourceB, sourceA]); comp.VerifyEmitDiagnostics( // (5,27): error CS1950: The best overloaded Add method 'Extensions.Add<int>(MyCollection<int>, string)' for the collection initializer has some invalid arguments // return /*<bind>*/[F1(), ..F2()]/*</bind>*/; Diagnostic(ErrorCode.ERR_BadArgTypesForCollectionAdd, "F1()").WithArguments("Extensions.Add<int>(MyCollection<int>, string)").WithLocation(5, 27), // (5,27): error CS1503: Argument 2: cannot convert from 'int' to 'string' // return /*<bind>*/[F1(), ..F2()]/*</bind>*/; Diagnostic(ErrorCode.ERR_BadArgType, "F1()").WithArguments("2", "int", "string").WithLocation(5, 27), // (5,33): error CS1503: Argument 2: cannot convert from 'int' to 'string' // return /*<bind>*/[F1(), ..F2()]/*</bind>*/; Diagnostic(ErrorCode.ERR_BadArgType, "..F2()").WithArguments("2", "int", "string").WithLocation(5, 33), // (5,35): error CS1950: The best overloaded Add method 'Extensions.Add<int>(MyCollection<int>, string)' for the collection initializer has some invalid arguments // return /*<bind>*/[F1(), ..F2()]/*</bind>*/; Diagnostic(ErrorCode.ERR_BadArgTypesForCollectionAdd, "F2()").WithArguments("Extensions.Add<int>(MyCollection<int>, string)").WithLocation(5, 35)); VerifyOperationTreeForTest<CollectionExpressionSyntax>(comp, """ ICollectionExpressionOperation (2 elements, ConstructMethod: MyCollection<System.Int32>..ctor()) (OperationKind.CollectionExpression, Type: MyCollection<System.Int32>, IsInvalid) (Syntax: '[F1(), ..F2()]') Elements(2): IInvocationOperation (System.Int32 Program.F1()) (OperationKind.Invocation, Type: System.Int32, IsInvalid) (Syntax: 'F1()') Instance Receiver: null Arguments(0) ISpreadOperation (ElementType: System.Int32) (OperationKind.Spread, Type: null, IsInvalid) (Syntax: '..F2()') Operand: IInvocationOperation (System.Int32[] Program.F2()) (OperationKind.Invocation, Type: System.Int32[], IsInvalid) (Syntax: 'F2()') Instance Receiver: null Arguments(0) ElementConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) (Identity) """); var tree = comp.SyntaxTrees[0]; var method = tree.GetRoot().DescendantNodes().OfType<MethodDeclarationSyntax>().Single(m => m.Identifier.Text == "Create"); VerifyFlowGraph(comp, method, """ Block[B0] - Entry Statements (0) Next (Regular) Block[B1] Block[B1] - Block Predecessors: [B0] Statements (0) Next (Return) Block[B2] IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: MyCollection<System.Int32>, IsInvalid, IsImplicit) (Syntax: '[F1(), ..F2()]') Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) (CollectionExpression) Operand: ICollectionExpressionOperation (2 elements, ConstructMethod: MyCollection<System.Int32>..ctor()) (OperationKind.CollectionExpression, Type: MyCollection<System.Int32>, IsInvalid) (Syntax: '[F1(), ..F2()]') Elements(2): IInvocationOperation (System.Int32 Program.F1()) (OperationKind.Invocation, Type: System.Int32, IsInvalid) (Syntax: 'F1()') Instance Receiver: null Arguments(0) ISpreadOperation (ElementType: System.Int32) (OperationKind.Spread, Type: null, IsInvalid) (Syntax: '..F2()') Operand: IInvocationOperation (System.Int32[] Program.F2()) (OperationKind.Invocation, Type: System.Int32[], IsInvalid) (Syntax: 'F2()') Instance Receiver: null Arguments(0) ElementConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) (Identity) Block[B2] - Exit Predecessors: [B1] Statements (0) """); } [Fact] public void IOperation_InvalidAdd_03() { string sourceA = """ using System.Collections; public class MyCollection<T> : IEnumerable { IEnumerator IEnumerable.GetEnumerator() => throw null; } public static class Extensions { public static void Add<T>(this MyCollection<T> collection, params string[] args) { } } """; string sourceB = """ class Program { static MyCollection<int> Create(int x, int[] y) { return /*<bind>*/[x, ..y]/*</bind>*/; } } """; var comp = CreateCompilation([sourceB, sourceA]); comp.VerifyEmitDiagnostics( // (5,27): error CS1950: The best overloaded Add method 'Extensions.Add<int>(MyCollection<int>, params string[])' for the collection initializer has some invalid arguments // return /*<bind>*/[x, ..y]/*</bind>*/; Diagnostic(ErrorCode.ERR_BadArgTypesForCollectionAdd, "x").WithArguments("Extensions.Add<int>(MyCollection<int>, params string[])").WithLocation(5, 27), // (5,27): error CS1503: Argument 2: cannot convert from 'int' to 'string' // return /*<bind>*/[x, ..y]/*</bind>*/; Diagnostic(ErrorCode.ERR_BadArgType, "x").WithArguments("2", "int", "string").WithLocation(5, 27), // (5,30): error CS1503: Argument 2: cannot convert from 'int' to 'string' // return /*<bind>*/[x, ..y]/*</bind>*/; Diagnostic(ErrorCode.ERR_BadArgType, "..y").WithArguments("2", "int", "string").WithLocation(5, 30), // (5,32): error CS1950: The best overloaded Add method 'Extensions.Add<int>(MyCollection<int>, params string[])' for the collection initializer has some invalid arguments // return /*<bind>*/[x, ..y]/*</bind>*/; Diagnostic(ErrorCode.ERR_BadArgTypesForCollectionAdd, "y").WithArguments("Extensions.Add<int>(MyCollection<int>, params string[])").WithLocation(5, 32)); VerifyOperationTreeForTest<CollectionExpressionSyntax>(comp, """ ICollectionExpressionOperation (2 elements, ConstructMethod: MyCollection<System.Int32>..ctor()) (OperationKind.CollectionExpression, Type: MyCollection<System.Int32>, IsInvalid) (Syntax: '[x, ..y]') Elements(2): IParameterReferenceOperation: x (OperationKind.ParameterReference, Type: System.Int32, IsInvalid) (Syntax: 'x') ISpreadOperation (ElementType: System.Int32) (OperationKind.Spread, Type: null, IsInvalid) (Syntax: '..y') Operand: IParameterReferenceOperation: y (OperationKind.ParameterReference, Type: System.Int32[], IsInvalid) (Syntax: 'y') ElementConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) (Identity) """); var tree = comp.SyntaxTrees[0]; var method = tree.GetRoot().DescendantNodes().OfType<MethodDeclarationSyntax>().Single(m => m.Identifier.Text == "Create"); VerifyFlowGraph(comp, method, """ Block[B0] - Entry Statements (0) Next (Regular) Block[B1] Block[B1] - Block Predecessors: [B0] Statements (0) Next (Return) Block[B2] IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: MyCollection<System.Int32>, IsInvalid, IsImplicit) (Syntax: '[x, ..y]') Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) (CollectionExpression) Operand: ICollectionExpressionOperation (2 elements, ConstructMethod: MyCollection<System.Int32>..ctor()) (OperationKind.CollectionExpression, Type: MyCollection<System.Int32>, IsInvalid) (Syntax: '[x, ..y]') Elements(2): IParameterReferenceOperation: x (OperationKind.ParameterReference, Type: System.Int32, IsInvalid) (Syntax: 'x') ISpreadOperation (ElementType: System.Int32) (OperationKind.Spread, Type: null, IsInvalid) (Syntax: '..y') Operand: IParameterReferenceOperation: y (OperationKind.ParameterReference, Type: System.Int32[], IsInvalid) (Syntax: 'y') ElementConversion: CommonConversion (Exists: True, IsIdentity: True, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) (Identity) Block[B2] - Exit Predecessors: [B1] Statements (0) """); } [Fact] public void IOperation_InvalidAdd_04() { string sourceA = """ using System; using System.Collections; public class MyCollection<T> : IEnumerable { IEnumerator IEnumerable.GetEnumerator() => throw null; public Action<T> Add; } """; string sourceB = """ class Program { static MyCollection<int> Create(int x, int[] y) { return /*<bind>*/[x, ..y]/*</bind>*/; } } """; var comp = CreateCompilation([sourceB, sourceA]); comp.VerifyEmitDiagnostics( // (5,26): error CS0118: 'Add' is a field but is used like a method // return /*<bind>*/[x, ..y]/*</bind>*/; Diagnostic(ErrorCode.ERR_BadSKknown, "[x, ..y]").WithArguments("Add", "field", "method").WithLocation(5, 26)); VerifyOperationTreeForTest<CollectionExpressionSyntax>(comp, """ ICollectionExpressionOperation (2 elements, ConstructMethod: null) (OperationKind.CollectionExpression, Type: MyCollection<System.Int32>, IsInvalid) (Syntax: '[x, ..y]') Elements(2): IParameterReferenceOperation: x (OperationKind.ParameterReference, Type: System.Int32, IsInvalid) (Syntax: 'x') ISpreadOperation (ElementType: System.Int32) (OperationKind.Spread, Type: null, IsInvalid) (Syntax: '..y') Operand: IParameterReferenceOperation: y (OperationKind.ParameterReference, Type: System.Int32[], IsInvalid) (Syntax: 'y') ElementConversion: CommonConversion (Exists: False, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) (NoConversion) """); var tree = comp.SyntaxTrees[0]; var method = tree.GetRoot().DescendantNodes().OfType<MethodDeclarationSyntax>().Single(m => m.Identifier.Text == "Create"); VerifyFlowGraph(comp, method, """ Block[B0] - Entry Statements (0) Next (Regular) Block[B1] Block[B1] - Block Predecessors: [B0] Statements (0) Next (Return) Block[B2] IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: MyCollection<System.Int32>, IsInvalid, IsImplicit) (Syntax: '[x, ..y]') Conversion: CommonConversion (Exists: False, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) (NoConversion) Operand: ICollectionExpressionOperation (2 elements, ConstructMethod: null) (OperationKind.CollectionExpression, Type: MyCollection<System.Int32>, IsInvalid) (Syntax: '[x, ..y]') Elements(2): IParameterReferenceOperation: x (OperationKind.ParameterReference, Type: System.Int32, IsInvalid) (Syntax: 'x') ISpreadOperation (ElementType: System.Int32) (OperationKind.Spread, Type: null, IsInvalid) (Syntax: '..y') Operand: IParameterReferenceOperation: y (OperationKind.ParameterReference, Type: System.Int32[], IsInvalid) (Syntax: 'y') ElementConversion: CommonConversion (Exists: False, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) (NoConversion) Block[B2] - Exit Predecessors: [B1] Statements (0) """); } [WorkItem("https://github.com/dotnet/roslyn/issues/73297")] [Theory] [InlineData("A[]")] [InlineData("System.Collections.Generic.List<A>")] [InlineData("System.Collections.Generic.IEnumerable<A>")] public void OverloadResolutionArgumentErrors_01(string collectionType) { string source = $$""" {{collectionType}} values = [ new A(), new B() // 1 ]; M([ new A(), new B() // 2 ]); void M({{collectionType}} values) { } class A { } class B { } """; var comp = CreateCompilation(source); comp.VerifyEmitDiagnostics( // (3,5): error CS0029: Cannot implicitly convert type 'B' to 'A' // new B() // 1 Diagnostic(ErrorCode.ERR_NoImplicitConv, "new B()").WithArguments("B", "A").WithLocation(3, 5), // (8,5): error CS0029: Cannot implicitly convert type 'B' to 'A' // new B() // 2 Diagnostic(ErrorCode.ERR_NoImplicitConv, "new B()").WithArguments("B", "A").WithLocation(8, 5)); } [WorkItem("https://github.com/dotnet/roslyn/issues/73297")] [Fact] public void OverloadResolutionArgumentErrors_02() { string source = """ B b = [new A()]; M([new A()]); void M(B b) { } class A { } class B { } """; var comp = CreateCompilation(source); comp.VerifyEmitDiagnostics( // (1,7): error CS9174: Cannot initialize type 'B' with a collection expression because the type is not constructible. // B b = [new A()]; Diagnostic(ErrorCode.ERR_CollectionExpressionTargetTypeNotConstructible, "[new A()]").WithArguments("B").WithLocation(1, 7), // (2,3): error CS9174: Cannot initialize type 'B' with a collection expression because the type is not constructible. // M([new A()]); Diagnostic(ErrorCode.ERR_CollectionExpressionTargetTypeNotConstructible, "[new A()]").WithArguments("B").WithLocation(2, 3)); } [WorkItem("https://github.com/dotnet/roslyn/issues/73297")] [Fact] public void OverloadResolutionArgumentErrors_03() { string source = """ using System.Collections; using System.Collections.Generic; B b = [new A()]; M([new A()]); void M(B b) { } class A { } class B : IEnumerable<A> { private B() { } public void Add(A a) { } IEnumerator<A> IEnumerable<A>.GetEnumerator() => default; IEnumerator IEnumerable.GetEnumerator() => default; } """; var comp = CreateCompilation(source); comp.VerifyEmitDiagnostics( // (4,7): error CS0122: 'B.B()' is inaccessible due to its protection level // B b = [new A()]; Diagnostic(ErrorCode.ERR_BadAccess, "[new A()]").WithArguments("B.B()").WithLocation(4, 7), // (5,3): error CS0122: 'B.B()' is inaccessible due to its protection level // M([new A()]); Diagnostic(ErrorCode.ERR_BadAccess, "[new A()]").WithArguments("B.B()").WithLocation(5, 3)); } [Fact] public void Async_01() { string source = """ using System.Collections.Generic; using System.Threading.Tasks; class Program { static async Task Main() { (await CreateArray()).Report(); (await CreateList()).Report(); } static async Task<int[]> CreateArray() { return [await F(1), await F(2)]; } static async Task<List<int>> CreateList() { return [await F(3), await F(4)]; } static async Task<int> F(int i) { await Task.Yield(); return i; } } """; CompileAndVerify(new[] { source, s_collectionExtensions }, expectedOutput: "[1, 2], [3, 4], "); } [Fact] public void Async_02() { string source = """ using System.Collections.Generic; using System.Threading.Tasks; class Program { static async Task Main() { (await F2(F1())).Report(); } static async Task<int[]> F1() { return [await F(1), await F(2)]; } static async Task<int[]> F2(Task<int[]> e) { return [3, .. await e, 4]; } static async Task<T> F<T>(T t) { await Task.Yield(); return t; } } """; CompileAndVerify(new[] { source, s_collectionExtensions }, expectedOutput: "[3, 1, 2, 4], "); } [Fact] public void PostfixIncrementDecrement() { string source = """ using System.Collections.Generic; class Program { static void Main() { []++; []--; } } """; CreateCompilation(source).VerifyEmitDiagnostics( // (7,9): error CS1059: The operand of an increment or decrement operator must be a variable, property or indexer // []++; Diagnostic(ErrorCode.ERR_IncrementLvalueExpected, "[]").WithLocation(7, 9), // (8,9): error CS1059: The operand of an increment or decrement operator must be a variable, property or indexer // []--; Diagnostic(ErrorCode.ERR_IncrementLvalueExpected, "[]").WithLocation(8, 9)); } [Fact] public void PostfixPointerAccess() { string source = """ using System.Collections.Generic; class Program { static void Main() { var v = []->Count; } } """; CreateCompilation(source).VerifyEmitDiagnostics( // (7,17): error CS9176: There is no target type for the collection expression. // var v = []->Count; Diagnostic(ErrorCode.ERR_CollectionExpressionNoTargetType, "[]").WithLocation(7, 17)); } [Fact] public void LeftHandAssignment() { string source = """ using System.Collections.Generic; class Program { static void Main() { [] = null; } } """; CreateCompilation(source).VerifyEmitDiagnostics( // (7,9): error CS0131: The left-hand side of an assignment must be a variable, property or indexer // [] = null; Diagnostic(ErrorCode.ERR_AssgLvalueExpected, "[]").WithLocation(7, 9)); } [Fact] public void BinaryOperator() { string source = """ using System.Collections.Generic; class Program { static void Main(List<int> list) { [] + list; } } """; CreateCompilation(source).VerifyEmitDiagnostics( // (7,9): error CS0019: Operator '+' cannot be applied to operands of type 'collection expression' and 'List<int>' // [] + list; Diagnostic(ErrorCode.ERR_BadBinaryOps, "[] + list").WithArguments("+", "collection expression", "System.Collections.Generic.List<int>").WithLocation(7, 9), // (7,9): error CS0201: Only assignment, call, increment, decrement, await, and new object expressions can be used as a statement // [] + list; Diagnostic(ErrorCode.ERR_IllegalStatement, "[] + list").WithLocation(7, 9)); } [Fact] public void RangeOperator() { string source = """ using System.Collections.Generic; class Program { static void Main(List<int> list) { []..; } } """; CreateCompilationWithIndexAndRangeAndSpan(source).VerifyEmitDiagnostics( // (7,9): error CS9174: Cannot initialize type 'Index' with a collection expression because the type is not constructible. // []..; Diagnostic(ErrorCode.ERR_CollectionExpressionTargetTypeNotConstructible, "[]").WithArguments("System.Index").WithLocation(7, 9), // (7,9): error CS0201: Only assignment, call, increment, decrement, await, and new object expressions can be used as a statement // []..; Diagnostic(ErrorCode.ERR_IllegalStatement, "[]..").WithLocation(7, 9)); } [Fact] public void TopLevelSwitchExpression() { string source = """ using System.Collections.Generic; class Program { static void Main(List<int> list) { [] switch { null => 0 }; } } """; CreateCompilation(source).VerifyEmitDiagnostics( // (7,9): error CS9176: There is no target type for the collection expression. // [] switch Diagnostic(ErrorCode.ERR_CollectionExpressionNoTargetType, "[]").WithLocation(7, 9), // (7,9): error CS0201: Only assignment, call, increment, decrement, await, and new object expressions can be used as a statement // [] switch Diagnostic(ErrorCode.ERR_IllegalStatement, "[] switch { null => 0 }").WithLocation(7, 9)); } [Fact] public void TopLevelWithExpression() { string source = """ using System.Collections.Generic; class Program { static void Main(List<int> list) { [] with { Count = 1, }; } } """; CreateCompilation(source).VerifyEmitDiagnostics( // (7,9): error CS9176: There is no target type for the collection expression. // [] with { Count = 1, }; Diagnostic(ErrorCode.ERR_CollectionExpressionNoTargetType, "[]").WithLocation(7, 9), // (7,9): error CS0201: Only assignment, call, increment, decrement, await, and new object expressions can be used as a statement // [] with { Count = 1, }; Diagnostic(ErrorCode.ERR_IllegalStatement, "[] with { Count = 1, }").WithLocation(7, 9)); } [Fact] public void TopLevelIsExpressions() { string source = """ using System.Collections.Generic; class Program { static void Main(List<int> list) { [] is object; } } """; CreateCompilation(source).VerifyEmitDiagnostics( // (7,9): error CS9176: There is no target type for the collection expression. // [] is object; Diagnostic(ErrorCode.ERR_CollectionExpressionNoTargetType, "[]").WithLocation(7, 9), // (7,9): error CS0201: Only assignment, call, increment, decrement, await, and new object expressions can be used as a statement // [] is object; Diagnostic(ErrorCode.ERR_IllegalStatement, "[] is object").WithLocation(7, 9)); } [Fact] public void TopLevelAsExpressions() { string source = """ using System.Collections.Generic; class Program { static void Main(List<int> list) { [] as List<int>; } } """; CreateCompilation(source).VerifyEmitDiagnostics( // (7,9): error CS9176: There is no target type for the collection expression. // [] as List<int>; Diagnostic(ErrorCode.ERR_CollectionExpressionNoTargetType, "[]").WithLocation(7, 9), // (7,9): error CS0201: Only assignment, call, increment, decrement, await, and new object expressions can be used as a statement // [] as List<int>; Diagnostic(ErrorCode.ERR_IllegalStatement, "[] as List<int>").WithLocation(7, 9)); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/69133")] public void InAttribute() { string source = """ var attr = (XAttribute)System.Attribute.GetCustomAttribute(typeof(C), typeof(XAttribute)); attr._values.Report(); [X([42, 43, 44])] class C { } public class XAttribute : System.Attribute { public int[] _values; public XAttribute(int[] values) { _values = values; } } """; var comp = CreateCompilation(new[] { source, s_collectionExtensions }).VerifyEmitDiagnostics(); CompileAndVerify(comp, expectedOutput: "[42, 43, 44],"); var program = comp.GetMember<NamedTypeSymbol>("C"); var argument = program.GetAttributes().Single().ConstructorArguments.Single(); var values = argument.Values; Assert.Equal(3, values.Length); Assert.Equal(42, values[0].Value); Assert.Equal(43, values[1].Value); Assert.Equal(44, values[2].Value); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/69133")] public void InAttribute_Named() { string source = """ var attr = (XAttribute)System.Attribute.GetCustomAttribute(typeof(C), typeof(XAttribute)); attr.Values.Report(); [X(Values = [42, 43, 44])] class C { } public class XAttribute : System.Attribute { public int[] Values { get; set; } } """; var comp = CreateCompilation(new[] { source, s_collectionExtensions }).VerifyEmitDiagnostics(); CompileAndVerify(comp, expectedOutput: "[42, 43, 44],"); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/69133")] public void InAttribute_Params() { string source = """ var attr = (XAttribute)System.Attribute.GetCustomAttribute(typeof(C), typeof(XAttribute)); attr._values.Report(); [X([42, 43, 44])] class C { } public class XAttribute : System.Attribute { public int[] _values; public XAttribute(params int[] values) { _values = values; } } """; var comp = CreateCompilation(new[] { source, s_collectionExtensions }).VerifyEmitDiagnostics(); CompileAndVerify(comp, expectedOutput: "[42, 43, 44],"); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/69133")] public void InAttribute_StringConstants() { string source = """ var attr = (XAttribute)System.Attribute.GetCustomAttribute(typeof(C), typeof(XAttribute)); attr._values.Report(); [X(["hi", null])] class C { } public class XAttribute : System.Attribute { public string[] _values; public XAttribute(string[] values) { _values = values; } } """; var comp = CreateCompilation(new[] { source, s_collectionExtensions }).VerifyEmitDiagnostics(); CompileAndVerify(comp, expectedOutput: "[hi, null],"); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/69133")] public void InAttribute_NestedArray() { string source = """ [X([[1], [2]])] class C { } public class XAttribute : System.Attribute { public XAttribute(int[][] values) { } } """; CreateCompilation(source).VerifyEmitDiagnostics( // (1,2): error CS0181: Attribute constructor parameter 'values' has type 'int[][]', which is not a valid attribute parameter type // [X([[1], [2]])] Diagnostic(ErrorCode.ERR_BadAttributeParamType, "X").WithArguments("values", "int[][]").WithLocation(1, 2) ); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/69133")] public void InAttribute_NestedArrayAsObject() { string source = """ var attr = (XAttribute)System.Attribute.GetCustomAttribute(typeof(C), typeof(XAttribute)); var inner = (int[])attr._values[0]; inner.Report(); [X([(int[])[1]])] class C { } public class XAttribute : System.Attribute { public object[] _values; public XAttribute(object[] values) { _values = values; } } """; var comp = CreateCompilation(new[] { source, s_collectionExtensions }).VerifyEmitDiagnostics(); CompileAndVerify(comp, expectedOutput: "[1],"); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/69133")] public void InAttribute_ArrayAsObject() { string source = """ var attr = (XAttribute)System.Attribute.GetCustomAttribute(typeof(C), typeof(XAttribute)); var array = (int[])attr._value; array.Report(); [X((int[])[1, 2, 3])] class C { } public class XAttribute : System.Attribute { public object _value; public XAttribute(object value) { _value = value; } } """; var comp = CreateCompilation(new[] { source, s_collectionExtensions }).VerifyEmitDiagnostics(); CompileAndVerify(comp, expectedOutput: "[1, 2, 3],"); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/69133")] public void InAttribute_Empty() { string source = """ var attr = (XAttribute)System.Attribute.GetCustomAttribute(typeof(C), typeof(XAttribute)); attr._values.Report(); [X([])] class C { } public class XAttribute : System.Attribute { public int[] _values; public XAttribute(int[] values) { _values = values; } } """; var comp = CreateCompilation(new[] { source, s_collectionExtensions }).VerifyEmitDiagnostics(); CompileAndVerify(comp, expectedOutput: "[],"); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/69133")] public void InAttribute_NotConstant() { string source = """ [X([1, 2, C.M()])] class C { public static int M() => 0; } public class XAttribute : System.Attribute { public XAttribute(int[] values) { } } """; CreateCompilation(source).VerifyEmitDiagnostics( // (1,11): error CS0182: An attribute argument must be a constant expression, typeof expression or array creation expression of an attribute parameter type // [X([1, 2, C.M()])] Diagnostic(ErrorCode.ERR_BadAttributeArgument, "C.M()").WithLocation(1, 11) ); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/69133")] public void InAttribute_NotConstant_CollectionSpread() { string source = """ [X([1, 2, .. [3]])] class C { } public class XAttribute : System.Attribute { public XAttribute(int[] values) { } } """; CreateCompilation(source).VerifyEmitDiagnostics( // (1,14): error CS9176: There is no target type for the collection expression. // [X([1, 2, .. [3]])] Diagnostic(ErrorCode.ERR_CollectionExpressionNoTargetType, "[3]").WithLocation(1, 14) ); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/69133")] public void InAttribute_NotConstant_ListSpread() { string source = """ using System.Collections.Generic; [X([.. new List<int>()])] class C { } public class XAttribute : System.Attribute { public XAttribute(int[] values) { } } """; CreateCompilation(source).VerifyEmitDiagnostics( // (3,5): error CS0182: An attribute argument must be a constant expression, typeof expression or array creation expression of an attribute parameter type // [X([.. new List<int>()])] Diagnostic(ErrorCode.ERR_BadAttributeArgument, ".. new List<int>()").WithLocation(3, 5) ); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/69133")] public void InAttribute_BadArrayType() { string source = """ [X([1])] class C { } public class XAttribute : System.Attribute { public XAttribute(ERROR[] values) { } } """; CreateCompilation(source).VerifyEmitDiagnostics( // (8,23): error CS0246: The type or namespace name 'ERROR' could not be found (are you missing a using directive or an assembly reference?) // public XAttribute(ERROR[] values) { } Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "ERROR").WithArguments("ERROR").WithLocation(8, 23) ); } [Fact] public void InAssignment_BadArrayType() { string source = """ ERROR[] values = [1]; values = [2]; """; CreateCompilation(source).VerifyEmitDiagnostics( // (1,1): error CS0246: The type or namespace name 'ERROR' could not be found (are you missing a using directive or an assembly reference?) // ERROR[] values = [1]; Diagnostic(ErrorCode.ERR_SingleTypeNameNotFound, "ERROR").WithArguments("ERROR").WithLocation(1, 1)); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/69133")] public void InAttribute_NotArrayType() { string source = """ [X([1])] class C { } public class XAttribute : System.Attribute { public XAttribute(int NOT_ARRAY) { } } """; CreateCompilation(source).VerifyEmitDiagnostics( // (1,4): error CS9174: Cannot initialize type 'int' with a collection expression because the type is not constructible. // [X([1])] Diagnostic(ErrorCode.ERR_CollectionExpressionTargetTypeNotConstructible, "[1]").WithArguments("int").WithLocation(1, 4) ); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/69133")] public void InAttribute_SpanType() { string source = """ [X([1])] class C { } public class XAttribute : System.Attribute { public XAttribute(System.Span<int> s) { } } """; CreateCompilation(source, targetFramework: TargetFramework.Net70, options: TestOptions.ReleaseDll.WithSpecificDiagnosticOptions(WithSpanAllocWarning)).VerifyEmitDiagnostics( // (1,2): error CS0181: Attribute constructor parameter 's' has type 'Span<int>', which is not a valid attribute parameter type // [X([1])] Diagnostic(ErrorCode.ERR_BadAttributeParamType, "X").WithArguments("s", "System.Span<int>").WithLocation(1, 2), // (1,4): warning CS9208: Collection expression of type 'Span<int>' may incur unexpected heap allocations. Consider explicitly creating an array, then converting to 'Span<int>' to make the allocation explicit. // [X([1])] Diagnostic(ErrorCode.WRN_CollectionExpressionRefStructMayAllocate, "[1]").WithArguments("System.Span<int>").WithLocation(1, 4) ); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/69133")] public void InAttribute_ReadOnlySpanType() { string source = """ [X([1])] class C { } public class XAttribute : System.Attribute { public XAttribute(System.ReadOnlySpan<int> s) { } } """; CreateCompilation(source, targetFramework: TargetFramework.Net70).VerifyEmitDiagnostics( // (1,2): error CS0181: Attribute constructor parameter 's' has type 'ReadOnlySpan<int>', which is not a valid attribute parameter type // [X([1])] Diagnostic(ErrorCode.ERR_BadAttributeParamType, "X").WithArguments("s", "System.ReadOnlySpan<int>").WithLocation(1, 2) ); } [Fact] public void InAttribute_CollectionBuilderType() { string sourceA = """ using System; using System.Collections; using System.Collections.Generic; using System.Runtime.CompilerServices; [CollectionBuilder(typeof(MyCollectionBuilder), nameof(MyCollectionBuilder.Create))] public struct MyCollection<T> : IEnumerable<T> { private readonly List<T> _list; public MyCollection(List<T> list) { _list = list; } public IEnumerator<T> GetEnumerator() => _list.GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); } public class MyCollectionBuilder { public static MyCollection<T> Create<T>(ReadOnlySpan<T> items) { return new MyCollection<T>(new List<T>(items.ToArray())); } } [X([1])] class C { } public class XAttribute : System.Attribute { public XAttribute(MyCollection<int> s) { } } """; var comp = CreateCompilation(sourceA, targetFramework: TargetFramework.Net80); comp.VerifyDiagnostics( // (22,2): error CS0181: Attribute constructor parameter 's' has type 'MyCollection<int>', which is not a valid attribute parameter type // [X([1])] Diagnostic(ErrorCode.ERR_BadAttributeParamType, "X").WithArguments("s", "MyCollection<int>").WithLocation(22, 2) ); } [Fact] public void InAttribute_CollectionInitializerType() { string sourceA = """ using System.Collections; using System.Collections.Generic; public class A : IEnumerable<int> { public void Add(int i) { } IEnumerator<int> IEnumerable<int>.GetEnumerator() => null; IEnumerator IEnumerable.GetEnumerator() => null; static A Create1() => []; } [X([1])] class C { } public class XAttribute : System.Attribute { public XAttribute(A a) { } } """; var comp = CreateCompilation(sourceA, targetFramework: TargetFramework.Net80); comp.VerifyDiagnostics( // (12,2): error CS0181: Attribute constructor parameter 'a' has type 'A', which is not a valid attribute parameter type // [X([1])] Diagnostic(ErrorCode.ERR_BadAttributeParamType, "X").WithArguments("a", "A").WithLocation(12, 2) ); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/69521")] public void MissingCtor_GenericIEnumerable() { string source = """ using System.Collections; using System.Collections.Generic; C x = [1]; // 1 C.M([1]); class C : IEnumerable<int> { public C(string s) { } IEnumerator IEnumerable.GetEnumerator() => null; IEnumerator<int> IEnumerable<int>.GetEnumerator() => null; public void Add(int i) { } public static void M(C c) { } public static void M(int[] i) { } } """; var comp = CreateCompilation(source).VerifyEmitDiagnostics( // (4,7): error CS9214: Collection expression type must have an applicable constructor that can be called with no arguments. // C x = [1]; // 1 Diagnostic(ErrorCode.ERR_CollectionExpressionMissingConstructor, "[1]").WithLocation(4, 7)); var tree = comp.SyntaxTrees.First(); var model = comp.GetSemanticModel(tree); var collections = tree.GetRoot().DescendantNodes().OfType<CollectionExpressionSyntax>().ToArray(); var conversion1 = model.GetConversion(collections[0]); Assert.False(conversion1.IsValid); Assert.False(conversion1.Exists); var conversion2 = model.GetConversion(collections[1]); Assert.True(conversion2.IsValid); Assert.True(conversion2.IsCollectionExpression); var typeInfo2 = model.GetTypeInfo(collections[1]); Assert.Null(typeInfo2.Type); Assert.Equal("System.Int32[]", typeInfo2.ConvertedType.ToTestDisplayString()); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/69521")] public void MissingCtor_GenericIEnumerable_Spread() { string source = """ using System.Collections; using System.Collections.Generic; int[] values = new int[] { 1 }; C x = [..values]; // 1 C.M([..values]); class C : IEnumerable<int> { public C(string s) { } IEnumerator IEnumerable.GetEnumerator() => null; IEnumerator<int> IEnumerable<int>.GetEnumerator() => null; public void Add(int i) { } public static void M(C c) { } public static void M(int[] i) { } } """; var comp = CreateCompilation(source).VerifyEmitDiagnostics( // (5,7): error CS9214: Collection expression type must have an applicable constructor that can be called with no arguments. // C x = [..values]; // 1 Diagnostic(ErrorCode.ERR_CollectionExpressionMissingConstructor, "[..values]").WithLocation(5, 7)); var tree = comp.SyntaxTrees.First(); var model = comp.GetSemanticModel(tree); var collections = tree.GetRoot().DescendantNodes().OfType<CollectionExpressionSyntax>().ToArray(); var conversion1 = model.GetConversion(collections[0]); Assert.False(conversion1.IsValid); Assert.False(conversion1.Exists); var conversion2 = model.GetConversion(collections[1]); Assert.True(conversion2.IsValid); Assert.True(conversion2.IsCollectionExpression); var typeInfo2 = model.GetTypeInfo(collections[1]); Assert.Null(typeInfo2.Type); Assert.Equal("System.Int32[]", typeInfo2.ConvertedType.ToTestDisplayString()); } [Fact] public void OverloadResolution_Ambiguity() { string source = """ using System.Collections; using System.Collections.Generic; int[] values = new int[] { 1 }; C.M([1]); // 1 C.M([..values]); // 2 class C : IEnumerable<int> { IEnumerator IEnumerable.GetEnumerator() => null; IEnumerator<int> IEnumerable<int>.GetEnumerator() => null; public void Add(int i) { } public static void M(C c) { } public static void M(int[] i) { } } """; var comp = CreateCompilation(source).VerifyEmitDiagnostics( // (5,3): error CS0121: The call is ambiguous between the following methods or properties: 'C.M(C)' and 'C.M(int[])' // C.M([1]); // 1 Diagnostic(ErrorCode.ERR_AmbigCall, "M").WithArguments("C.M(C)", "C.M(int[])").WithLocation(5, 3), // (6,3): error CS0121: The call is ambiguous between the following methods or properties: 'C.M(C)' and 'C.M(int[])' // C.M([..values]); // 2 Diagnostic(ErrorCode.ERR_AmbigCall, "M").WithArguments("C.M(C)", "C.M(int[])").WithLocation(6, 3)); var tree = comp.SyntaxTrees.First(); var model = comp.GetSemanticModel(tree); var collections = tree.GetRoot().DescendantNodes().OfType<CollectionExpressionSyntax>().ToArray(); // Valid identity conversion matches the behavior for an ambiguous call // with a switch expression argument: C.M(2 switch { _ => default }); var conversion1 = model.GetConversion(collections[0]); Assert.True(conversion1.IsValid); Assert.True(conversion1.IsIdentity); Assert.Null(model.GetTypeInfo(collections[0]).Type); var conversion2 = model.GetConversion(collections[1]); Assert.True(conversion2.IsValid); Assert.True(conversion2.IsIdentity); Assert.Null(model.GetTypeInfo(collections[1]).Type); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/69521")] public void MissingCtor_IEnumerable() { string source = """ using System.Collections; C x = [1]; // 1 C.M([1]); class C : IEnumerable { public C(string s) { } IEnumerator IEnumerable.GetEnumerator() => null; public void Add(int i) { } public static void M(C c) { } public static void M(int[] i) { } } """; var comp = CreateCompilation(source).VerifyEmitDiagnostics( // (3,7): error CS9214: Collection expression type must have an applicable constructor that can be called with no arguments. // C x = [1]; // 1 Diagnostic(ErrorCode.ERR_CollectionExpressionMissingConstructor, "[1]").WithLocation(3, 7) ); var tree = comp.SyntaxTrees.First(); var model = comp.GetSemanticModel(tree); var collections = tree.GetRoot().DescendantNodes().OfType<CollectionExpressionSyntax>().ToArray(); var conversion1 = model.GetConversion(collections[0]); Assert.False(conversion1.IsValid); Assert.False(conversion1.Exists); var conversion2 = model.GetConversion(collections[1]); Assert.True(conversion2.IsValid); Assert.True(conversion2.IsCollectionExpression); var typeInfo2 = model.GetTypeInfo(collections[1]); Assert.Null(typeInfo2.Type); Assert.Equal("System.Int32[]", typeInfo2.ConvertedType.ToTestDisplayString()); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/69521")] public void MissingCtor_IEnumerable_Spread() { string source = """ using System.Collections; int[] values = new int[] { 1 }; C x = [..values]; // 1 C.M([..values]); class C : IEnumerable { public C(string s) { } IEnumerator IEnumerable.GetEnumerator() => null; public void Add(int i) { } public static void M(C c) { } public static void M(int[] i) { } } """; var comp = CreateCompilation(source).VerifyEmitDiagnostics( // (4,7): error CS9214: Collection expression type must have an applicable constructor that can be called with no arguments. // C x = [..values]; // 1 Diagnostic(ErrorCode.ERR_CollectionExpressionMissingConstructor, "[..values]").WithLocation(4, 7) ); var tree = comp.SyntaxTrees.First(); var model = comp.GetSemanticModel(tree); var collections = tree.GetRoot().DescendantNodes().OfType<CollectionExpressionSyntax>().ToArray(); var conversion1 = model.GetConversion(collections[0]); Assert.False(conversion1.IsValid); Assert.False(conversion1.Exists); var conversion2 = model.GetConversion(collections[1]); Assert.True(conversion2.IsValid); Assert.True(conversion2.IsCollectionExpression); var typeInfo2 = model.GetTypeInfo(collections[1]); Assert.Null(typeInfo2.Type); Assert.Equal("System.Int32[]", typeInfo2.ConvertedType.ToTestDisplayString()); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/69521")] public void MissingCtor_IEnumerable_WithWrongGenericIEnumerable() { string source = """ using System.Collections; using System.Collections.Generic; C x = [1]; // 1 class C : IEnumerable<string> { public C(string s) { } IEnumerator<string> IEnumerable<string>.GetEnumerator() => null; IEnumerator IEnumerable.GetEnumerator() => null; public void Add(int i) { } } """; var comp = CreateCompilation(source).VerifyEmitDiagnostics( // (4,7): error CS9214: Collection expression type must have an applicable constructor that can be called with no arguments. // C x = [1]; // 1 Diagnostic(ErrorCode.ERR_CollectionExpressionMissingConstructor, "[1]").WithLocation(4, 7), // (4,8): error CS0029: Cannot implicitly convert type 'int' to 'string' // C x = [1]; // 1 Diagnostic(ErrorCode.ERR_NoImplicitConv, "1").WithArguments("int", "string").WithLocation(4, 8) ); var tree = comp.SyntaxTrees.First(); var model = comp.GetSemanticModel(tree); var collection = tree.GetRoot().DescendantNodes().OfType<CollectionExpressionSyntax>().Single(); var conversion = model.GetConversion(collection); Assert.False(conversion.IsValid); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/69521")] public void MissingCtor_IEnumerable_WithWrongGenericIEnumerable_Spread() { string source = """ using System.Collections; using System.Collections.Generic; int[] values = [1]; C x = [..values]; // 1 class C : IEnumerable<string> { public C(string s) { } IEnumerator<string> IEnumerable<string>.GetEnumerator() => null; IEnumerator IEnumerable.GetEnumerator() => null; public void Add(int i) { } public static void M(C c) { } public static void M(int[] i) { } } """; var comp = CreateCompilation(source).VerifyEmitDiagnostics( // (5,7): error CS9214: Collection expression type must have an applicable constructor that can be called with no arguments. // C x = [..values]; // 1 Diagnostic(ErrorCode.ERR_CollectionExpressionMissingConstructor, "[..values]").WithLocation(5, 7), // (5,10): error CS0029: Cannot implicitly convert type 'int' to 'string' // C x = [..values]; // 1 Diagnostic(ErrorCode.ERR_NoImplicitConv, "values").WithArguments("int", "string").WithLocation(5, 10) ); var tree = comp.SyntaxTrees.First(); var model = comp.GetSemanticModel(tree); var collection = tree.GetRoot().DescendantNodes().OfType<CollectionExpressionSyntax>().Last(); Assert.Equal("[..values]", collection.ToString()); var conversion = model.GetConversion(collection); Assert.False(conversion.IsValid); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/69521")] public void MissingCtor_IEnumerable_WithWrongGenericIEnumerable_Invocation() { string source = """ using System.Collections; using System.Collections.Generic; C.M([1]); class C : IEnumerable<string> { public C(string s) { } IEnumerator<string> IEnumerable<string>.GetEnumerator() => null; IEnumerator IEnumerable.GetEnumerator() => null; public void Add(int i) { } public static void M(C c) { } public static void M(int[] i) { System.Console.Write("RAN"); } } """; var comp = CreateCompilation(source).VerifyEmitDiagnostics(); CompileAndVerify(comp, expectedOutput: "RAN"); var tree = comp.SyntaxTrees.First(); var model = comp.GetSemanticModel(tree); var collection = tree.GetRoot().DescendantNodes().OfType<CollectionExpressionSyntax>().Single(); var conversion = model.GetConversion(collection); Assert.True(conversion.IsValid); Assert.True(conversion.IsCollectionExpression); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/69521")] public void MissingCtor_TypeParameter_GenericIEnumerable_NoElements() { string source = """ using System.Collections; using System.Collections.Generic; interface IAdd<T> { void Add(T t); } class C { static T1 Create1<T1>() where T1 : IEnumerable<int>, IAdd<int> => []; // 1 static T2 Create2<T2>() where T2 : IEnumerable<int>, IAdd<int>, new() => []; static T3 Create3<T3>() where T3 : struct, IEnumerable<int>, IAdd<int> => []; static T4 Create4<T4>() where T4 : class, IEnumerable<int>, IAdd<int> => []; // 2 } """; var comp = CreateCompilation(source).VerifyEmitDiagnostics( // (6,71): error CS0304: Cannot create an instance of the variable type 'T1' because it does not have the new() constraint // static T1 Create1<T1>() where T1 : IEnumerable<int>, IAdd<int> => []; // 1 Diagnostic(ErrorCode.ERR_NoNewTyvar, "[]").WithArguments("T1").WithLocation(6, 71), // (9,78): error CS0304: Cannot create an instance of the variable type 'T4' because it does not have the new() constraint // static T4 Create4<T4>() where T4 : class, IEnumerable<int>, IAdd<int> => []; // 2 Diagnostic(ErrorCode.ERR_NoNewTyvar, "[]").WithArguments("T4").WithLocation(9, 78) ); var tree = comp.SyntaxTrees.First(); var model = comp.GetSemanticModel(tree); var collections = tree.GetRoot().DescendantNodes().OfType<CollectionExpressionSyntax>().ToArray(); var conversion1 = model.GetConversion(collections[0]); Assert.False(conversion1.IsValid); Assert.False(conversion1.Exists); var conversion2 = model.GetConversion(collections[1]); Assert.True(conversion2.IsValid); Assert.True(conversion2.IsCollectionExpression); var typeInfo2 = model.GetTypeInfo(collections[1]); Assert.Null(typeInfo2.Type); Assert.Equal("T2", typeInfo2.ConvertedType.ToTestDisplayString()); var conversion3 = model.GetConversion(collections[2]); Assert.True(conversion3.IsValid); Assert.True(conversion3.IsCollectionExpression); var typeInfo3 = model.GetTypeInfo(collections[2]); Assert.Null(typeInfo3.Type); Assert.Equal("T3", typeInfo3.ConvertedType.ToTestDisplayString()); var conversion4 = model.GetConversion(collections[3]); Assert.False(conversion4.IsValid); Assert.False(conversion4.Exists); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/69521")] public void MissingCtor_TypeParameter_IEnumerable() { string source = """ using System.Collections; interface IAdd { void Add(object o); } class C { static T1 Create1<T1>() where T1 : IEnumerable, IAdd => []; // 1 static T2 Create2<T2>() where T2 : IEnumerable, IAdd, new() => []; static T3 Create3<T3>() where T3 : struct, IEnumerable, IAdd => []; static T4 Create4<T4>() where T4 : class, IEnumerable, IAdd => []; // 2 } """; var comp = CreateCompilation(source).VerifyEmitDiagnostics( // (5,61): error CS0304: Cannot create an instance of the variable type 'T1' because it does not have the new() constraint // static T1 Create1<T1>() where T1 : IEnumerable, IAdd => []; // 1 Diagnostic(ErrorCode.ERR_NoNewTyvar, "[]").WithArguments("T1").WithLocation(5, 61), // (8,68): error CS0304: Cannot create an instance of the variable type 'T4' because it does not have the new() constraint // static T4 Create4<T4>() where T4 : class, IEnumerable, IAdd => []; // 2 Diagnostic(ErrorCode.ERR_NoNewTyvar, "[]").WithArguments("T4").WithLocation(8, 68) ); var tree = comp.SyntaxTrees.First(); var model = comp.GetSemanticModel(tree); var collections = tree.GetRoot().DescendantNodes().OfType<CollectionExpressionSyntax>().ToArray(); var conversion1 = model.GetConversion(collections[0]); Assert.False(conversion1.IsValid); Assert.False(conversion1.Exists); var conversion2 = model.GetConversion(collections[1]); Assert.True(conversion2.IsValid); Assert.True(conversion2.IsCollectionExpression); var typeInfo2 = model.GetTypeInfo(collections[1]); Assert.Null(typeInfo2.Type); Assert.Equal("T2", typeInfo2.ConvertedType.ToTestDisplayString()); var conversion3 = model.GetConversion(collections[2]); Assert.True(conversion3.IsValid); Assert.True(conversion3.IsCollectionExpression); var typeInfo3 = model.GetTypeInfo(collections[2]); Assert.Null(typeInfo3.Type); Assert.Equal("T3", typeInfo3.ConvertedType.ToTestDisplayString()); var conversion4 = model.GetConversion(collections[3]); Assert.False(conversion4.IsValid); Assert.False(conversion4.Exists); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/69521")] public void MissingCtor_TypeParameter_IEnumerable_WithWrongGenericIEnumerable() { string source = """ using System.Collections; using System.Collections.Generic; interface IAdd<T> { void Add(T t); } class C { static T1 Create1<T1>() where T1 : IEnumerable, IEnumerable<string>, IAdd<string> => [1]; // 1 static T2 Create2<T2>() where T2 : IEnumerable, IEnumerable<string>, IAdd<string>, new() => [2]; // 2 static T3 Create3<T3>() where T3 : struct, IEnumerable, IEnumerable<string>, IAdd<string> => [3]; // 3 static T4 Create4<T4>() where T4 : class, IEnumerable, IEnumerable<string>, IAdd<string> => [4]; // 4 } """; var comp = CreateCompilation(source).VerifyEmitDiagnostics( // (6,90): error CS0304: Cannot create an instance of the variable type 'T1' because it does not have the new() constraint // static T1 Create1<T1>() where T1 : IEnumerable, IEnumerable<string>, IAdd<string> => [1]; // 1 Diagnostic(ErrorCode.ERR_NoNewTyvar, "[1]").WithArguments("T1").WithLocation(6, 90), // (6,91): error CS0029: Cannot implicitly convert type 'int' to 'string' // static T1 Create1<T1>() where T1 : IEnumerable, IEnumerable<string>, IAdd<string> => [1]; // 1 Diagnostic(ErrorCode.ERR_NoImplicitConv, "1").WithArguments("int", "string").WithLocation(6, 91), // (7,98): error CS0029: Cannot implicitly convert type 'int' to 'string' // static T2 Create2<T2>() where T2 : IEnumerable, IEnumerable<string>, IAdd<string>, new() => [2]; // 2 Diagnostic(ErrorCode.ERR_NoImplicitConv, "2").WithArguments("int", "string").WithLocation(7, 98), // (8,99): error CS0029: Cannot implicitly convert type 'int' to 'string' // static T3 Create3<T3>() where T3 : struct, IEnumerable, IEnumerable<string>, IAdd<string> => [3]; // 3 Diagnostic(ErrorCode.ERR_NoImplicitConv, "3").WithArguments("int", "string").WithLocation(8, 99), // (9,97): error CS0304: Cannot create an instance of the variable type 'T4' because it does not have the new() constraint // static T4 Create4<T4>() where T4 : class, IEnumerable, IEnumerable<string>, IAdd<string> => [4]; // 4 Diagnostic(ErrorCode.ERR_NoNewTyvar, "[4]").WithArguments("T4").WithLocation(9, 97), // (9,98): error CS0029: Cannot implicitly convert type 'int' to 'string' // static T4 Create4<T4>() where T4 : class, IEnumerable, IEnumerable<string>, IAdd<string> => [4]; // 4 Diagnostic(ErrorCode.ERR_NoImplicitConv, "4").WithArguments("int", "string").WithLocation(9, 98) ); var tree = comp.SyntaxTrees.First(); var model = comp.GetSemanticModel(tree); var collections = tree.GetRoot().DescendantNodes().OfType<CollectionExpressionSyntax>().ToArray(); var conversion1 = model.GetConversion(collections[0]); Assert.False(conversion1.IsValid); var conversion2 = model.GetConversion(collections[1]); Assert.False(conversion2.IsValid); var typeInfo2 = model.GetTypeInfo(collections[1]); Assert.Null(typeInfo2.Type); Assert.Equal("T2", typeInfo2.ConvertedType.ToTestDisplayString()); var conversion3 = model.GetConversion(collections[2]); Assert.False(conversion3.IsValid); var typeInfo3 = model.GetTypeInfo(collections[2]); Assert.Null(typeInfo3.Type); Assert.Equal("T3", typeInfo3.ConvertedType.ToTestDisplayString()); var conversion4 = model.GetConversion(collections[3]); Assert.False(conversion4.IsValid); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/69521")] public void MissingCtor_TypeParameter_WrongGenericIEnumerable() { string source = """ using System.Collections.Generic; interface IAdd<T> { void Add(T t); } class C { static T1 Create1<T1>() where T1 : IEnumerable<string>, IAdd<string> => [1]; // 1 static T2 Create2<T2>() where T2 : IEnumerable<string>, IAdd<string>, new() => [2]; // 2 static T3 Create3<T3>() where T3 : struct, IEnumerable<string>, IAdd<string> => [3]; // 3 static T4 Create4<T4>() where T4 : class, IEnumerable<string>, IAdd<string> => [4]; // 4 } """; var comp = CreateCompilation(source).VerifyEmitDiagnostics( // (5,77): error CS0304: Cannot create an instance of the variable type 'T1' because it does not have the new() constraint // static T1 Create1<T1>() where T1 : IEnumerable<string>, IAdd<string> => [1]; // 1 Diagnostic(ErrorCode.ERR_NoNewTyvar, "[1]").WithArguments("T1").WithLocation(5, 77), // (5,78): error CS0029: Cannot implicitly convert type 'int' to 'string' // static T1 Create1<T1>() where T1 : IEnumerable<string>, IAdd<string> => [1]; // 1 Diagnostic(ErrorCode.ERR_NoImplicitConv, "1").WithArguments("int", "string").WithLocation(5, 78), // (6,85): error CS0029: Cannot implicitly convert type 'int' to 'string' // static T2 Create2<T2>() where T2 : IEnumerable<string>, IAdd<string>, new() => [2]; // 2 Diagnostic(ErrorCode.ERR_NoImplicitConv, "2").WithArguments("int", "string").WithLocation(6, 85), // (7,86): error CS0029: Cannot implicitly convert type 'int' to 'string' // static T3 Create3<T3>() where T3 : struct, IEnumerable<string>, IAdd<string> => [3]; // 3 Diagnostic(ErrorCode.ERR_NoImplicitConv, "3").WithArguments("int", "string").WithLocation(7, 86), // (8,84): error CS0304: Cannot create an instance of the variable type 'T4' because it does not have the new() constraint // static T4 Create4<T4>() where T4 : class, IEnumerable<string>, IAdd<string> => [4]; // 4 Diagnostic(ErrorCode.ERR_NoNewTyvar, "[4]").WithArguments("T4").WithLocation(8, 84), // (8,85): error CS0029: Cannot implicitly convert type 'int' to 'string' // static T4 Create4<T4>() where T4 : class, IEnumerable<string>, IAdd<string> => [4]; // 4 Diagnostic(ErrorCode.ERR_NoImplicitConv, "4").WithArguments("int", "string").WithLocation(8, 85) ); var tree = comp.SyntaxTrees.First(); var model = comp.GetSemanticModel(tree); var collections = tree.GetRoot().DescendantNodes().OfType<CollectionExpressionSyntax>().ToArray(); var conversion1 = model.GetConversion(collections[0]); Assert.False(conversion1.IsValid); var conversion2 = model.GetConversion(collections[1]); Assert.False(conversion2.IsValid); var typeInfo2 = model.GetTypeInfo(collections[1]); Assert.Null(typeInfo2.Type); Assert.Equal("T2", typeInfo2.ConvertedType.ToTestDisplayString()); var conversion3 = model.GetConversion(collections[2]); Assert.False(conversion3.IsValid); var typeInfo3 = model.GetTypeInfo(collections[2]); Assert.Null(typeInfo3.Type); Assert.Equal("T3", typeInfo3.ConvertedType.ToTestDisplayString()); var conversion4 = model.GetConversion(collections[3]); Assert.False(conversion4.IsValid); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/69521")] public void OptionalParameterCtor() { string source = """ using System.Collections; using System.Collections.Generic; C x = []; class C : IEnumerable<int> { public C(string s = null) { } IEnumerator IEnumerable.GetEnumerator() => null; IEnumerator<int> IEnumerable<int>.GetEnumerator() => null; public void Add(int i) { } } """; CreateCompilation(source).VerifyEmitDiagnostics(); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/69521")] public void MissingCtor_AbstractCollectionType() { string source = """ using System.Collections; using System.Collections.Generic; abstract class AbstractCollection : IEnumerable<int> { IEnumerator IEnumerable.GetEnumerator() => null; IEnumerator<int> IEnumerable<int>.GetEnumerator() => null; public void Add(int i) { } } class Program { static void F(AbstractCollection c) { } static void Main() { F([]); } } """; var comp = CreateCompilation(source).VerifyEmitDiagnostics( // (17,11): error CS0144: Cannot create an instance of the abstract type or interface 'AbstractCollection' // F([]); Diagnostic(ErrorCode.ERR_NoNewAbstract, "[]").WithArguments("AbstractCollection").WithLocation(17, 11)); var tree = comp.SyntaxTrees.First(); var model = comp.GetSemanticModel(tree); var collection = tree.GetRoot().DescendantNodes().OfType<CollectionExpressionSyntax>().Single(); Assert.Equal("[]", collection.ToString()); var conversion1 = model.GetConversion(collection); Assert.False(conversion1.IsValid); Assert.False(conversion1.Exists); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/69521")] public void MissingCtor_EmptyCollection() { string source = """ using System.Collections; using System.Collections.Generic; class NoConstructorCollection : IEnumerable<int> { static NoConstructorCollection _c = [1, 2, 3]; private NoConstructorCollection() { } IEnumerator IEnumerable.GetEnumerator() => null; IEnumerator<int> IEnumerable<int>.GetEnumerator() => null; public void Add(int i) { } } class Program { static void F(NoConstructorCollection c) { } static void Main() { F([]); } } """; var comp = CreateCompilation(source).VerifyEmitDiagnostics( // (20,11): error CS0122: 'NoConstructorCollection.NoConstructorCollection()' is inaccessible due to its protection level // F([]); Diagnostic(ErrorCode.ERR_BadAccess, "[]").WithArguments("NoConstructorCollection.NoConstructorCollection()").WithLocation(20, 11) ); var tree = comp.SyntaxTrees.First(); var model = comp.GetSemanticModel(tree); var collections = tree.GetRoot().DescendantNodes().OfType<CollectionExpressionSyntax>().ToArray(); Assert.Equal("[1, 2, 3]", collections[0].ToString()); var conversion1 = model.GetConversion(collections[0]); Assert.True(conversion1.IsValid); Assert.True(conversion1.IsCollectionExpression); Assert.Equal("[]", collections[1].ToString()); var conversion2 = model.GetConversion(collections[1]); Assert.False(conversion2.IsValid); Assert.False(conversion2.Exists); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/69521")] public void MissingCtor_NonEmptyCollection() { string source = """ using System.Collections; using System.Collections.Generic; class NoConstructorCollection : IEnumerable<int> { static NoConstructorCollection field = []; private NoConstructorCollection() { } IEnumerator IEnumerable.GetEnumerator() => null; IEnumerator<int> IEnumerable<int>.GetEnumerator() => null; public void Add(int i) { } } class Program { static void F(NoConstructorCollection c) { } static void Main() { F([1, 2, 3]); } } """; var comp = CreateCompilation(source).VerifyEmitDiagnostics( // (19,11): error CS0122: 'NoConstructorCollection.NoConstructorCollection()' is inaccessible due to its protection level // F([1, 2, 3]); Diagnostic(ErrorCode.ERR_BadAccess, "[1, 2, 3]").WithArguments("NoConstructorCollection.NoConstructorCollection()").WithLocation(19, 11) ); var tree = comp.SyntaxTrees.First(); var model = comp.GetSemanticModel(tree); var collection = tree.GetRoot().DescendantNodes().OfType<CollectionExpressionSyntax>().Last(); Assert.Equal("[1, 2, 3]", collection.ToString()); var conversion = model.GetConversion(collection); Assert.False(conversion.IsValid); Assert.False(conversion.Exists); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/69521")] public void MissingCtor_OverloadResolution() { string source = """ using System; using System.Collections; using System.Collections.Generic; abstract class AbstractCollection : IEnumerable<int> { IEnumerator IEnumerable.GetEnumerator() => null; IEnumerator<int> IEnumerable<int>.GetEnumerator() => null; public void Add(int i) { } } class NoConstructorCollection : IEnumerable<int> { private NoConstructorCollection() { } IEnumerator IEnumerable.GetEnumerator() => null; IEnumerator<int> IEnumerable<int>.GetEnumerator() => null; public void Add(int i) { } } class Program { static void F(AbstractCollection c) { } static void F(NoConstructorCollection c) { } static void F(List<int> c) { Console.WriteLine("List<int>"); } static void Main() { F([]); } } """; var comp = CreateCompilation(source, options: TestOptions.ReleaseExe); comp.VerifyEmitDiagnostics(); CompileAndVerify(comp, expectedOutput: "List<int>"); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/69521")] public void CollectionBuilder_MissingCtor() { string source = """ using System; using System.Collections; using System.Collections.Generic; using System.Runtime.CompilerServices; C.M().Report(); class C { public static MyCollection<object> M() { return [1, 2, 3]; } } [CollectionBuilder(typeof(MyCollectionBuilder), nameof(MyCollectionBuilder.Create))] public class MyCollection<T> : IEnumerable<T> { private readonly List<T> _list; public MyCollection(List<T> list) { _list = list; } public IEnumerator<T> GetEnumerator() => _list.GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); } public class MyCollectionBuilder { public static MyCollection<T> Create<T>(ReadOnlySpan<T> items) { return new MyCollection<T>(new List<T>(items.ToArray())); } } """; var comp = CreateCompilation(new[] { source, s_collectionExtensions }, targetFramework: TargetFramework.Net80); comp.VerifyEmitDiagnostics(); var verifier = CompileAndVerify(comp, verify: Verification.Fails, expectedOutput: IncludeExpectedOutput("[1, 2, 3],")); verifier.VerifyIL("C.M", """ { // Code size 67 (0x43) .maxstack 2 .locals init (<>y__InlineArray3<object> V_0) IL_0000: ldloca.s V_0 IL_0002: initobj "<>y__InlineArray3<object>" IL_0008: ldloca.s V_0 IL_000a: ldc.i4.0 IL_000b: call "ref object <PrivateImplementationDetails>.InlineArrayElementRef<<>y__InlineArray3<object>, object>(ref <>y__InlineArray3<object>, int)" IL_0010: ldc.i4.1 IL_0011: box "int" IL_0016: stind.ref IL_0017: ldloca.s V_0 IL_0019: ldc.i4.1 IL_001a: call "ref object <PrivateImplementationDetails>.InlineArrayElementRef<<>y__InlineArray3<object>, object>(ref <>y__InlineArray3<object>, int)" IL_001f: ldc.i4.2 IL_0020: box "int" IL_0025: stind.ref IL_0026: ldloca.s V_0 IL_0028: ldc.i4.2 IL_0029: call "ref object <PrivateImplementationDetails>.InlineArrayElementRef<<>y__InlineArray3<object>, object>(ref <>y__InlineArray3<object>, int)" IL_002e: ldc.i4.3 IL_002f: box "int" IL_0034: stind.ref IL_0035: ldloca.s V_0 IL_0037: ldc.i4.3 IL_0038: call "System.ReadOnlySpan<object> <PrivateImplementationDetails>.InlineArrayAsReadOnlySpan<<>y__InlineArray3<object>, object>(in <>y__InlineArray3<object>, int)" IL_003d: call "MyCollection<object> MyCollectionBuilder.Create<object>(System.ReadOnlySpan<object>)" IL_0042: ret } """); } [Fact] public void OverloadResolution_ReadOnlySpanPreferredOverList() { string source = """ using System; using System.Collections.Generic; C.M([1, 2, 3]); class C { public static void M(ReadOnlySpan<int> values) { System.Console.Write($"{values[0]} {values[1]} {values[2]}"); } public static void M(List<int> values) => throw null; } """; var comp = CreateCompilation(source, targetFramework: TargetFramework.Net70); comp.VerifyEmitDiagnostics( // (4,3): error CS0121: The call is ambiguous between the following methods or properties: 'C.M(ReadOnlySpan<int>)' and 'C.M(List<int>)' // C.M([1, 2, 3]); Diagnostic(ErrorCode.ERR_AmbigCall, "M").WithArguments("C.M(System.ReadOnlySpan<int>)", "C.M(System.Collections.Generic.List<int>)").WithLocation(4, 3)); } [Fact] public void OverloadResolution_ListPreferredOverIEnumerable() { string source = """ using System.Collections.Generic; C.M([1, 2, 3]); class C { public static void M(List<int> values) { System.Console.Write($"{values[0]} {values[1]} {values[2]}"); } public static void M(IEnumerable<int> values) => throw null; } """; var comp = CreateCompilation(source, targetFramework: TargetFramework.Net70); comp.VerifyEmitDiagnostics(); CompileAndVerify(comp, expectedOutput: IncludeExpectedOutput("1 2 3"), verify: Verification.FailsPEVerify); } [Fact] public void OverloadResolution_ListOfRightTypePreferred() { string source = """ using System.Collections.Generic; C.M([1, 2, 3]); class C { public static void M(List<int> values) { System.Console.Write($"{values[0]} {values[1]} {values[2]}"); } public static void M(List<string> values) => throw null; } """; var comp = CreateCompilation(source, targetFramework: TargetFramework.Net70); comp.VerifyEmitDiagnostics(); CompileAndVerify(comp, expectedOutput: IncludeExpectedOutput("1 2 3"), verify: Verification.FailsPEVerify); } [Fact] public void OverloadResolution_StringVsReadOnlySpanChar() { string source = """ using System; C.M(['a', 'b', 'c']); class C { public static void M(string s) => throw null; public static void M(ReadOnlySpan<char> s) { System.Console.Write($"{s[0]} {s[1]} {s[2]}"); } } """; var comp = CreateCompilation(source, targetFramework: TargetFramework.Net70); comp.VerifyEmitDiagnostics(); CompileAndVerify(comp, expectedOutput: IncludeExpectedOutput("a b c"), verify: Verification.FailsPEVerify); } [Fact] public void OverloadResolution_StringVsSpanInt() { string source = """ using System; C.M(['a', 'b', 'c']); class C { public static void M(string s) => throw null; public static void M(ReadOnlySpan<int> s) { System.Console.Write($"{s[0]} {s[1]} {s[2]}"); } } """; var comp = CreateCompilation(source, targetFramework: TargetFramework.Net70); comp.VerifyEmitDiagnostics(); CompileAndVerify(comp, expectedOutput: IncludeExpectedOutput("97 98 99"), verify: Verification.FailsPEVerify); } [Fact] public void GenericIEnumerable_NarrowerConversionToAdd() { string source = """ using System.Collections; using System.Collections.Generic; MyCollection1<int> x = [1, 2, 3]; x.Report(); MyCollection2<object, int> y = [1, 2, 3]; y.Report(); class MyCollection1<T> : IEnumerable { private List<T> _list = new(); IEnumerator IEnumerable.GetEnumerator() => _list.GetEnumerator(); public void Add(T t) { _list.Add(t); } } class MyCollection2<TElement, TAdd> : IEnumerable<TElement> where TAdd : TElement { private List<TElement> _list = new(); IEnumerator IEnumerable.GetEnumerator() => _list.GetEnumerator(); IEnumerator<TElement> IEnumerable<TElement>.GetEnumerator() => _list.GetEnumerator(); public void Add(TAdd u) { _list.Add(u); } } """; CompileAndVerify( new[] { source, s_collectionExtensions }, targetFramework: TargetFramework.Net70, verify: Verification.FailsPEVerify, expectedOutput: IncludeExpectedOutput("[1, 2, 3], [1, 2, 3], ")); } [Fact] public void GenericIEnumerable_NarrowerConversionToAdd_WiderElements() { string source = """ using System.Collections; using System.Collections.Generic; MyCollection2<object, int> y = [new object()]; class MyCollection2<TElement, TAdd> : IEnumerable<TElement> where TAdd : TElement { private List<TElement> _list = new(); IEnumerator IEnumerable.GetEnumerator() => _list.GetEnumerator(); IEnumerator<TElement> IEnumerable<TElement>.GetEnumerator() => _list.GetEnumerator(); public void Add(TAdd u) { _list.Add(u); } } """; var comp = CreateCompilation(source, targetFramework: TargetFramework.Net70); comp.VerifyEmitDiagnostics( // (4,33): error CS1950: The best overloaded Add method 'MyCollection2<object, int>.Add(int)' for the collection initializer has some invalid arguments // MyCollection2<object, int> y = [new object()]; Diagnostic(ErrorCode.ERR_BadArgTypesForCollectionAdd, "new object()").WithArguments("MyCollection2<object, int>.Add(int)").WithLocation(4, 33), // (4,33): error CS1503: Argument 1: cannot convert from 'object' to 'int' // MyCollection2<object, int> y = [new object()]; Diagnostic(ErrorCode.ERR_BadArgType, "new object()").WithArguments("1", "object", "int").WithLocation(4, 33) ); } [Fact] public void GenericIEnumerable_DifferentConversionToAdd() { // For purpose of conversion, we rely on the existence of an Add method. // But for purpose of construction, we rely on conversion from numeric literal to sbyte (from Add(sbyte)) string source = """ using System.Collections; using System.Collections.Generic; MyCollection x = [1, 2, 3]; x.Report(); class MyCollection : IEnumerable<uint> { private List<uint> _list = new(); IEnumerator IEnumerable.GetEnumerator() => _list.GetEnumerator(); IEnumerator<uint> IEnumerable<uint>.GetEnumerator() => _list.GetEnumerator(); public void Add(sbyte s) { _list.Add((uint)s); } } """; CompileAndVerify( new[] { source, s_collectionExtensions }, targetFramework: TargetFramework.Net70, verify: Verification.FailsPEVerify, expectedOutput: IncludeExpectedOutput("[1, 2, 3], ")); } [Fact] public void GenericIEnumerable_NoConversionToAdd() { // For purpose of conversion, we rely on the existence of an Add method. // But for purpose of construction, there is no conversion from uint to sbyte (from Add(sbyte)) string source = """ using System.Collections; using System.Collections.Generic; MyCollection x = [uint.MaxValue]; class MyCollection : IEnumerable<uint> { private List<uint> _list = new(); IEnumerator IEnumerable.GetEnumerator() => _list.GetEnumerator(); IEnumerator<uint> IEnumerable<uint>.GetEnumerator() => _list.GetEnumerator(); public void Add(sbyte s) { _list.Add((uint)s); } } """; var comp = CreateCompilation(new[] { source, s_collectionExtensions }, targetFramework: TargetFramework.Net70); comp.VerifyEmitDiagnostics( // 0.cs(4,19): error CS1950: The best overloaded Add method 'MyCollection.Add(sbyte)' for the collection initializer has some invalid arguments // MyCollection x = [uint.MaxValue]; Diagnostic(ErrorCode.ERR_BadArgTypesForCollectionAdd, "uint.MaxValue").WithArguments("MyCollection.Add(sbyte)").WithLocation(4, 19), // 0.cs(4,19): error CS1503: Argument 1: cannot convert from 'uint' to 'sbyte' // MyCollection x = [uint.MaxValue]; Diagnostic(ErrorCode.ERR_BadArgType, "uint.MaxValue").WithArguments("1", "uint", "sbyte").WithLocation(4, 19) ); } [Theory, WorkItem("https://github.com/dotnet/roslyn/issues/69521")] [InlineData("IEnumerable<int>, IEnumerable<string>")] [InlineData("IEnumerable<string>, IEnumerable<int>")] [InlineData("IString, IInteger")] [InlineData("IInteger, IString")] public void GenericIEnumerable(string interfaces) { string source = $$""" using System.Collections; using System.Collections.Generic; Collection c = [1]; interface IString : IEnumerable<string> { } interface IInteger : IEnumerable<int> { } class Collection : {{interfaces}} { IEnumerator IEnumerable.GetEnumerator() => null; IEnumerator<string> IEnumerable<string>.GetEnumerator() => null; IEnumerator<int> IEnumerable<int>.GetEnumerator() => null; public void Add(int i) { System.Console.Write("RAN"); } public void Add(string s) => throw null; } """; CreateCompilation(source).VerifyEmitDiagnostics( // (4,16): error CS9213: Collection expression target 'Collection' has no element type. // Collection c = [1]; Diagnostic(ErrorCode.ERR_CollectionExpressionTargetNoElementType, "[1]").WithArguments("Collection").WithLocation(4, 16)); } [Theory, WorkItem("https://github.com/dotnet/roslyn/issues/69521")] [InlineData("IEnumerable<int>, IEnumerable<long>")] [InlineData("IEnumerable<long>, IEnumerable<int>")] [InlineData("ILong, IInteger")] [InlineData("IInteger, ILong")] public void GenericIEnumerable_MultipleValid(string interfaces) { string source = $$""" using System.Collections; using System.Collections.Generic; Collection c = [1]; interface ILong : IEnumerable<long> { } interface IInteger : IEnumerable<int> { } class Collection : {{interfaces}} { IEnumerator IEnumerable.GetEnumerator() => null; IEnumerator<long> IEnumerable<long>.GetEnumerator() => null; IEnumerator<int> IEnumerable<int>.GetEnumerator() => null; public void Add(int i) { System.Console.Write("RAN"); } public void Add(long s) => throw null; } """; CreateCompilation(source).VerifyEmitDiagnostics( // (4,16): error CS9213: Collection expression target 'Collection' has no element type. // Collection c = [1]; Diagnostic(ErrorCode.ERR_CollectionExpressionTargetNoElementType, "[1]").WithArguments("Collection").WithLocation(4, 16)); } [Theory] [InlineData("class")] [InlineData("struct")] public void GenericIEnumerable_MultipleInvalid(string typeKind) { string source = $$""" using System.Collections; using System.Collections.Generic; Collection c = ["hi"]; {{typeKind}} Collection : IEnumerable<int>, IEnumerable<long> { IEnumerator IEnumerable.GetEnumerator() => null; IEnumerator<long> IEnumerable<long>.GetEnumerator() => null; IEnumerator<int> IEnumerable<int>.GetEnumerator() => null; public void Add(int i) { System.Console.Write("RAN"); } public void Add(long s) => throw null; } """; CreateCompilation(source).VerifyEmitDiagnostics( // (4,16): error CS9213: Collection expression target 'Collection' has no element type. // Collection c = ["hi"]; Diagnostic(ErrorCode.ERR_CollectionExpressionTargetNoElementType, @"[""hi""]").WithArguments("Collection").WithLocation(4, 16) ); } [Theory] [InlineData("class")] [InlineData("struct")] public void GenericIEnumerable_SingleInvalid(string typeKind) { string source = $$""" using System.Collections; using System.Collections.Generic; Collection c = ["hi", null]; {{typeKind}} Collection : IEnumerable<int> { IEnumerator IEnumerable.GetEnumerator() => null; IEnumerator<int> IEnumerable<int>.GetEnumerator() => null; public void Add(int i) { System.Console.Write("RAN"); } } """; CreateCompilation(source).VerifyEmitDiagnostics( // (4,17): error CS0029: Cannot implicitly convert type 'string' to 'int' // Collection c = ["hi", null]; Diagnostic(ErrorCode.ERR_NoImplicitConv, @"""hi""").WithArguments("string", "int").WithLocation(4, 17), // (4,23): error CS0037: Cannot convert null to 'int' because it is a non-nullable value type // Collection c = ["hi", null]; Diagnostic(ErrorCode.ERR_ValueCantBeNull, "null").WithArguments("int").WithLocation(4, 23) ); } [Fact] public void GenericIEnumerable_SingleInvalid_TypeParameter() { string source = """ using System.Collections; using System.Collections.Generic; class C { T M<T>() where T : IColl { return ["hi", null]; } } interface IColl : IEnumerable<int> { public void Add(int i); } """; CreateCompilation(source, targetFramework: TargetFramework.Net80).VerifyEmitDiagnostics( // (8,16): error CS0304: Cannot create an instance of the variable type 'T' because it does not have the new() constraint // return ["hi", null]; Diagnostic(ErrorCode.ERR_NoNewTyvar, @"[""hi"", null]").WithArguments("T").WithLocation(8, 16), // (8,17): error CS0029: Cannot implicitly convert type 'string' to 'int' // return ["hi", null]; Diagnostic(ErrorCode.ERR_NoImplicitConv, @"""hi""").WithArguments("string", "int").WithLocation(8, 17), // (8,23): error CS0037: Cannot convert null to 'int' because it is a non-nullable value type // return ["hi", null]; Diagnostic(ErrorCode.ERR_ValueCantBeNull, "null").WithArguments("int").WithLocation(8, 23) ); } [Fact] public void GenericIEnumerable_TwoCompatibleInterfaces() { string source = """ using System.Collections; using System.Collections.Generic; Collection c = [new C()]; interface I1 { } interface I2 { } class C : I1, I2 { } class Collection : IEnumerable<I1>, IEnumerable<I2> { IEnumerator IEnumerable.GetEnumerator() => null; IEnumerator<I1> IEnumerable<I1>.GetEnumerator() => null; IEnumerator<I2> IEnumerable<I2>.GetEnumerator() => null; public void Add(I1 i) => throw null; public void Add(I2 i) => throw null; } """; CreateCompilation(source).VerifyEmitDiagnostics( // (4,16): error CS9213: Collection expression target 'Collection' has no element type. // Collection c = [new C()]; Diagnostic(ErrorCode.ERR_CollectionExpressionTargetNoElementType, "[new C()]").WithArguments("Collection").WithLocation(4, 16)); } [Fact] public void NonGenericIEnumerable_TwoCompatibleInterfaces_01() { string source = """ using System.Collections; using System.Collections.Generic; Collection c = [new C()]; interface I1 { } interface I2 { } class C : I1, I2 { } class Collection : IEnumerable { IEnumerator IEnumerable.GetEnumerator() => null; public void Add(I1 i) => throw null; public void Add(I2 i) => throw null; } """; CreateCompilation(source).VerifyEmitDiagnostics( // (4,17): error CS0121: The call is ambiguous between the following methods or properties: 'Collection.Add(I1)' and 'Collection.Add(I2)' // Collection c = [new C()]; Diagnostic(ErrorCode.ERR_AmbigCall, "new C()").WithArguments("Collection.Add(I1)", "Collection.Add(I2)").WithLocation(4, 17)); } [Fact] public void NonGenericIEnumerable_TwoCompatibleInterfaces_02() { string source = """ using System.Collections; using System.Collections.Generic; Collection c = [new C()]; interface I1 { } interface I2 { } class C : I1, I2 { } class Collection : IEnumerable { IEnumerator IEnumerable.GetEnumerator() => null; public void Add(object o) => throw null; public void Add(I1 i) => throw null; public void Add(I2 i) => throw null; } """; CreateCompilation(source).VerifyEmitDiagnostics( // (4,17): error CS0121: The call is ambiguous between the following methods or properties: 'Collection.Add(I1)' and 'Collection.Add(I2)' // Collection c = [new C()]; Diagnostic(ErrorCode.ERR_AmbigCall, "new C()").WithArguments("Collection.Add(I1)", "Collection.Add(I2)").WithLocation(4, 17) ); } [Theory] [InlineData("I1, I2")] [InlineData("I2, I1")] public void GenericIEnumerable_TwoCompatibleInterfaces_SingleAdd(string interfaces) { string source = $$""" using System.Collections; using System.Collections.Generic; Collection c = [new C()]; interface I1 { } interface I2 { } class C : {{interfaces}} { } class Collection : IEnumerable<I1>, IEnumerable<I2> { IEnumerator IEnumerable.GetEnumerator() => null; IEnumerator<I1> IEnumerable<I1>.GetEnumerator() => null; IEnumerator<I2> IEnumerable<I2>.GetEnumerator() => null; public void Add(I1 i) { System.Console.Write("RAN"); } } """; CreateCompilation(source).VerifyEmitDiagnostics( // (4,16): error CS9213: Collection expression target 'Collection' has no element type. // Collection c = [new C()]; Diagnostic(ErrorCode.ERR_CollectionExpressionTargetNoElementType, "[new C()]").WithArguments("Collection").WithLocation(4, 16)); } [Fact] public void GenericIEnumerable_ImplicitConversionFromSourceType() { string source = $$""" using System.Collections; using System.Collections.Generic; Collection c = [new Source()]; class Source { public static implicit operator Destination(Source s) { System.Console.Write("RAN "); return new Destination(); } } class Destination { } class Collection : IEnumerable<Destination> { IEnumerator IEnumerable.GetEnumerator() => null; IEnumerator<Destination> IEnumerable<Destination>.GetEnumerator() => null; public void Add(Destination d) { System.Console.Write("RAN2"); } } """; var comp = CreateCompilation(source).VerifyEmitDiagnostics(); CompileAndVerify(comp, expectedOutput: IncludeExpectedOutput("RAN RAN2")); } [Fact] public void GenericIEnumerable_ExplicitConversionFromSourceType() { string source = $$""" using System.Collections; using System.Collections.Generic; Collection c = [new Source()]; class Source { public static explicit operator Destination(Source s) => throw null; } class Destination { } class Collection : IEnumerable<Destination> { IEnumerator IEnumerable.GetEnumerator() => null; IEnumerator<Destination> IEnumerable<Destination>.GetEnumerator() => null; public void Add(Destination d) => throw null; } """; CreateCompilation(source).VerifyEmitDiagnostics( // (4,17): error CS0029: Cannot implicitly convert type 'Source' to 'Destination' // Collection c = [new Source()]; Diagnostic(ErrorCode.ERR_NoImplicitConv, "new Source()").WithArguments("Source", "Destination").WithLocation(4, 17) ); } [Fact] public void GenericIEnumerable_ImplicitConversionToDestinationType() { string source = $$""" using System.Collections; using System.Collections.Generic; Collection c = [new Source()]; class Source { } class Destination { public static implicit operator Destination(Source s) { System.Console.Write("RAN "); return new Destination(); } } class Collection : IEnumerable<Destination> { IEnumerator IEnumerable.GetEnumerator() => null; IEnumerator<Destination> IEnumerable<Destination>.GetEnumerator() => null; public void Add(Destination d) { System.Console.Write("RAN2"); } } """; var comp = CreateCompilation(source).VerifyEmitDiagnostics(); CompileAndVerify(comp, expectedOutput: IncludeExpectedOutput("RAN RAN2")); } [Fact] public void GenericIEnumerable_ExplicitConversionToDestinationType() { string source = $$""" using System.Collections; using System.Collections.Generic; Collection c = [new Source()]; class Source { } class Destination { public static explicit operator Destination(Source s) => throw null; } class Collection : IEnumerable<Destination> { IEnumerator IEnumerable.GetEnumerator() => null; IEnumerator<Destination> IEnumerable<Destination>.GetEnumerator() => null; public void Add(Destination d) => throw null; } """; CreateCompilation(source).VerifyEmitDiagnostics( // (4,17): error CS0029: Cannot implicitly convert type 'Source' to 'Destination' // Collection c = [new Source()]; Diagnostic(ErrorCode.ERR_NoImplicitConv, "new Source()").WithArguments("Source", "Destination").WithLocation(4, 17) ); } [Fact] public void ImmutableArray_CollectionBuilder() { string sourceA = """ using System.Collections.Immutable; class Program { static void Main() { int x = 1, y = 2, z = 3; ImmutableArray<int> arr = [x, y, z]; arr.Report(); } } """; var comp = CreateCompilation(new[] { sourceA, s_collectionExtensions }, targetFramework: TargetFramework.Net80, options: ExecutionConditionUtil.IsMonoOrCoreClr ? TestOptions.DebugExe : TestOptions.DebugDll); comp.MakeMemberMissing(WellKnownMember.System_Runtime_InteropServices_ImmutableCollectionsMarshal__AsImmutableArray_T); var verifier = CompileAndVerify(comp, expectedOutput: IncludeExpectedOutput("[1, 2, 3],"), verify: Verification.Skipped); verifier.VerifyDiagnostics(); verifier.VerifyIL("Program.Main", """ { // Code size 73 (0x49) .maxstack 2 .locals init (int V_0, //x int V_1, //y int V_2, //z System.Collections.Immutable.ImmutableArray<int> V_3, //arr <>y__InlineArray3<int> V_4) IL_0000: nop IL_0001: ldc.i4.1 IL_0002: stloc.0 IL_0003: ldc.i4.2 IL_0004: stloc.1 IL_0005: ldc.i4.3 IL_0006: stloc.2 IL_0007: ldloca.s V_4 IL_0009: initobj "<>y__InlineArray3<int>" IL_000f: ldloca.s V_4 IL_0011: ldc.i4.0 IL_0012: call "ref int <PrivateImplementationDetails>.InlineArrayElementRef<<>y__InlineArray3<int>, int>(ref <>y__InlineArray3<int>, int)" IL_0017: ldloc.0 IL_0018: stind.i4 IL_0019: ldloca.s V_4 IL_001b: ldc.i4.1 IL_001c: call "ref int <PrivateImplementationDetails>.InlineArrayElementRef<<>y__InlineArray3<int>, int>(ref <>y__InlineArray3<int>, int)" IL_0021: ldloc.1 IL_0022: stind.i4 IL_0023: ldloca.s V_4 IL_0025: ldc.i4.2 IL_0026: call "ref int <PrivateImplementationDetails>.InlineArrayElementRef<<>y__InlineArray3<int>, int>(ref <>y__InlineArray3<int>, int)" IL_002b: ldloc.2 IL_002c: stind.i4 IL_002d: ldloca.s V_4 IL_002f: ldc.i4.3 IL_0030: call "System.ReadOnlySpan<int> <PrivateImplementationDetails>.InlineArrayAsReadOnlySpan<<>y__InlineArray3<int>, int>(in <>y__InlineArray3<int>, int)" IL_0035: call "System.Collections.Immutable.ImmutableArray<int> System.Collections.Immutable.ImmutableArray.Create<int>(System.ReadOnlySpan<int>)" IL_003a: stloc.3 IL_003b: ldloc.3 IL_003c: box "System.Collections.Immutable.ImmutableArray<int>" IL_0041: ldc.i4.0 IL_0042: call "void CollectionExtensions.Report(object, bool)" IL_0047: nop IL_0048: ret } """); var collectionType = comp.GetWellKnownType(WellKnownType.System_Collections_Immutable_ImmutableArray_T).Construct(comp.GetSpecialType(SpecialType.System_Int32)); Assert.Equal(CollectionExpressionTypeKind.CollectionBuilder, ConversionsBase.GetCollectionExpressionTypeKind(comp, collectionType, out var elementType)); Assert.False(elementType.HasType); } [Fact] public void ImmutableArray_01() { string sourceA = """ using System.Collections.Immutable; class Program { static void Main() { int x = 1, y = 2, z = 3; ImmutableArray<int> arr = [x, y, z]; arr.Report(); } } """; var verifier = CompileAndVerify(new[] { sourceA, s_collectionExtensions }, targetFramework: TargetFramework.Net80, expectedOutput: IncludeExpectedOutput("[1, 2, 3],"), verify: Verification.Skipped); verifier.VerifyDiagnostics(); verifier.VerifyIL("Program.Main", """ { // Code size 41 (0x29) .maxstack 4 .locals init (int V_0, //x int V_1, //y int V_2) //z IL_0000: ldc.i4.1 IL_0001: stloc.0 IL_0002: ldc.i4.2 IL_0003: stloc.1 IL_0004: ldc.i4.3 IL_0005: stloc.2 IL_0006: ldc.i4.3 IL_0007: newarr "int" IL_000c: dup IL_000d: ldc.i4.0 IL_000e: ldloc.0 IL_000f: stelem.i4 IL_0010: dup IL_0011: ldc.i4.1 IL_0012: ldloc.1 IL_0013: stelem.i4 IL_0014: dup IL_0015: ldc.i4.2 IL_0016: ldloc.2 IL_0017: stelem.i4 IL_0018: call "System.Collections.Immutable.ImmutableArray<int> System.Runtime.InteropServices.ImmutableCollectionsMarshal.AsImmutableArray<int>(int[])" IL_001d: box "System.Collections.Immutable.ImmutableArray<int>" IL_0022: ldc.i4.0 IL_0023: call "void CollectionExtensions.Report(object, bool)" IL_0028: ret } """); var comp = (CSharpCompilation)verifier.Compilation; var collectionType = comp.GetWellKnownType(WellKnownType.System_Collections_Immutable_ImmutableArray_T).Construct(comp.GetSpecialType(SpecialType.System_Int32)); Assert.Equal(CollectionExpressionTypeKind.CollectionBuilder, ConversionsBase.GetCollectionExpressionTypeKind(comp, collectionType, out _)); } [Fact] public void ImmutableArray_02() { string sourceA = """ using System.Collections.Immutable; class Program { static void Main() { ImmutableArray<ImmutableArray<int>> arr = [[1, 2, 3]]; arr.Report(); } } """; var verifier = CompileAndVerify(new[] { sourceA, s_collectionExtensions }, targetFramework: TargetFramework.Net80, expectedOutput: IncludeExpectedOutput("[[1, 2, 3]],"), verify: Verification.Skipped); verifier.VerifyDiagnostics(); verifier.VerifyIL("Program.Main", """ { // Code size 52 (0x34) .maxstack 6 IL_0000: ldc.i4.1 IL_0001: newarr "System.Collections.Immutable.ImmutableArray<int>" IL_0006: dup IL_0007: ldc.i4.0 IL_0008: ldc.i4.3 IL_0009: newarr "int" IL_000e: dup IL_000f: ldtoken "<PrivateImplementationDetails>.__StaticArrayInitTypeSize=12 <PrivateImplementationDetails>.4636993D3E1DA4E9D6B8F87B79E8F7C6D018580D52661950EABC3845C5897A4D" IL_0014: call "void System.Runtime.CompilerServices.RuntimeHelpers.InitializeArray(System.Array, System.RuntimeFieldHandle)" IL_0019: call "System.Collections.Immutable.ImmutableArray<int> System.Runtime.InteropServices.ImmutableCollectionsMarshal.AsImmutableArray<int>(int[])" IL_001e: stelem "System.Collections.Immutable.ImmutableArray<int>" IL_0023: call "System.Collections.Immutable.ImmutableArray<System.Collections.Immutable.ImmutableArray<int>> System.Runtime.InteropServices.ImmutableCollectionsMarshal.AsImmutableArray<System.Collections.Immutable.ImmutableArray<int>>(System.Collections.Immutable.ImmutableArray<int>[])" IL_0028: box "System.Collections.Immutable.ImmutableArray<System.Collections.Immutable.ImmutableArray<int>>" IL_002d: ldc.i4.0 IL_002e: call "void CollectionExtensions.Report(object, bool)" IL_0033: ret } """); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/71159")] public void ImmutableArray_Empty() { string sourceA = """ using System.Collections.Immutable; class Program { static void Main() { ImmutableArray<int> arr = []; arr.Report(); } } """; var verifier = CompileAndVerify([sourceA, s_collectionExtensions], targetFramework: TargetFramework.Net80, expectedOutput: IncludeExpectedOutput("[],"), verify: Verification.Skipped); verifier.VerifyDiagnostics(); verifier.VerifyIL("Program.Main", """ { // Code size 17 (0x11) .maxstack 2 IL_0000: ldsfld "System.Collections.Immutable.ImmutableArray<int> System.Collections.Immutable.ImmutableArray<int>.Empty" IL_0005: box "System.Collections.Immutable.ImmutableArray<int>" IL_000a: ldc.i4.0 IL_000b: call "void CollectionExtensions.Report(object, bool)" IL_0010: ret } """); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/71159")] public void ImmutableArray_Empty_MissingKnownSingleton() { string sourceA = """ using System.Collections.Immutable; class Program { static void Main() { ImmutableArray<int> arr = []; arr.Report(); } } """; var comp = CreateCompilation([sourceA, s_collectionExtensions], options: TestOptions.ReleaseExe, targetFramework: TargetFramework.Net80); comp.MakeMemberMissing(WellKnownMember.System_Collections_Immutable_ImmutableArray_T__Empty); var verifier = CompileAndVerify(comp, expectedOutput: IncludeExpectedOutput("[],"), verify: Verification.Skipped); verifier.VerifyDiagnostics(); verifier.VerifyIL("Program.Main", """ { // Code size 22 (0x16) .maxstack 2 IL_0000: call "int[] System.Array.Empty<int>()" IL_0005: call "System.Collections.Immutable.ImmutableArray<int> System.Runtime.InteropServices.ImmutableCollectionsMarshal.AsImmutableArray<int>(int[])" IL_000a: box "System.Collections.Immutable.ImmutableArray<int>" IL_000f: ldc.i4.0 IL_0010: call "void CollectionExtensions.Report(object, bool)" IL_0015: ret } """); } [Fact] public void ImmutableArray_04() { string sourceA = """ using System.Collections.Immutable; class Program { static void Main() { int[] arr = [1, 2, 3]; ImmutableArray<int> arr1 = [..arr]; arr1.Report(); } } """; var verifier = CompileAndVerify(new[] { sourceA, s_collectionExtensions }, targetFramework: TargetFramework.Net80, expectedOutput: IncludeExpectedOutput("[1, 2, 3],"), verify: Verification.Skipped); verifier.VerifyDiagnostics(); verifier.VerifyIL("Program.Main", """ { // Code size 39 (0x27) .maxstack 3 IL_0000: ldc.i4.3 IL_0001: newarr "int" IL_0006: dup IL_0007: ldtoken "<PrivateImplementationDetails>.__StaticArrayInitTypeSize=12 <PrivateImplementationDetails>.4636993D3E1DA4E9D6B8F87B79E8F7C6D018580D52661950EABC3845C5897A4D" IL_000c: call "void System.Runtime.CompilerServices.RuntimeHelpers.InitializeArray(System.Array, System.RuntimeFieldHandle)" IL_0011: call "int[] System.Linq.Enumerable.ToArray<int>(System.Collections.Generic.IEnumerable<int>)" IL_0016: call "System.Collections.Immutable.ImmutableArray<int> System.Runtime.InteropServices.ImmutableCollectionsMarshal.AsImmutableArray<int>(int[])" IL_001b: box "System.Collections.Immutable.ImmutableArray<int>" IL_0020: ldc.i4.0 IL_0021: call "void CollectionExtensions.Report(object, bool)" IL_0026: ret } """); } [Fact] public void ImmutableArray_05() { string sourceA = """ using System.Collections.Generic; using System.Collections.Immutable; class Program { static void Main() { IEnumerable<int> arr = [1, 2, 3]; ImmutableArray<int> arr1 = [..arr]; arr1.Report(); } } """; var verifier = CompileAndVerify(new[] { sourceA, s_collectionExtensions }, targetFramework: TargetFramework.Net80, expectedOutput: IncludeExpectedOutput("[1, 2, 3],"), verify: Verification.Skipped); verifier.VerifyDiagnostics(); verifier.VerifyIL("Program.Main", """ { // Code size 44 (0x2c) .maxstack 3 IL_0000: ldc.i4.3 IL_0001: newarr "int" IL_0006: dup IL_0007: ldtoken "<PrivateImplementationDetails>.__StaticArrayInitTypeSize=12 <PrivateImplementationDetails>.4636993D3E1DA4E9D6B8F87B79E8F7C6D018580D52661950EABC3845C5897A4D" IL_000c: call "void System.Runtime.CompilerServices.RuntimeHelpers.InitializeArray(System.Array, System.RuntimeFieldHandle)" IL_0011: newobj "<>z__ReadOnlyArray<int>..ctor(int[])" IL_0016: call "int[] System.Linq.Enumerable.ToArray<int>(System.Collections.Generic.IEnumerable<int>)" IL_001b: call "System.Collections.Immutable.ImmutableArray<int> System.Runtime.InteropServices.ImmutableCollectionsMarshal.AsImmutableArray<int>(int[])" IL_0020: box "System.Collections.Immutable.ImmutableArray<int>" IL_0025: ldc.i4.0 IL_0026: call "void CollectionExtensions.Report(object, bool)" IL_002b: ret } """); } [Fact] public void ImmutableArray_06() { string sourceA = """ using System.Collections.Immutable; class Program { static void Main() { ImmutableArray<int> arr = [1, 2, 3]; } } """; var comp = CreateCompilation(sourceA, targetFramework: TargetFramework.Net60); comp.VerifyEmitDiagnostics( // 0.cs(7,35): error CS9210: This version of 'ImmutableArray<T>' cannot be used with collection expressions. // ImmutableArray<int> arr = [1, 2, 3]; Diagnostic(ErrorCode.ERR_CollectionExpressionImmutableArray, "[1, 2, 3]").WithArguments("System.Collections.Immutable.ImmutableArray<T>").WithLocation(7, 35)); var collectionType = comp.GetWellKnownType(WellKnownType.System_Collections_Immutable_ImmutableArray_T).Construct(comp.GetSpecialType(SpecialType.System_Int32)); Assert.Equal(CollectionExpressionTypeKind.ImplementsIEnumerable, ConversionsBase.GetCollectionExpressionTypeKind(comp, collectionType, out _)); // ImmutableCollectionsMarshal.AsImmutableArray is not sufficient to optimize collection expressions // targeting ImmutableArray<T>. ImmutableArray<T> must also have a [CollectionBuilder] attribute. 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<T> AsImmutableArray<T>(T[] array) => ImmutableArray.Create(array); } } """; comp = CreateCompilation(new[] { sourceA, sourceB }, targetFramework: TargetFramework.Net60); comp.VerifyEmitDiagnostics( // 0.cs(7,35): error CS9210: This version of 'ImmutableArray<T>' cannot be used with collection expressions. // ImmutableArray<int> arr = [1, 2, 3]; Diagnostic(ErrorCode.ERR_CollectionExpressionImmutableArray, "[1, 2, 3]").WithArguments("System.Collections.Immutable.ImmutableArray<T>").WithLocation(7, 35)); collectionType = comp.GetWellKnownType(WellKnownType.System_Collections_Immutable_ImmutableArray_T).Construct(comp.GetSpecialType(SpecialType.System_Int32)); Assert.Equal(CollectionExpressionTypeKind.ImplementsIEnumerable, ConversionsBase.GetCollectionExpressionTypeKind(comp, collectionType, out _)); } [Fact] public void ImmutableArray_IEnumerableOnly() { // Test an ImmutableArray<T> which implements only non-generic IEnumerable. string sourceA = """ using System.Collections.Immutable; class Program { static void Main() { ImmutableArray<int> arr = [1, 2, 3]; } } namespace System.Collections.Immutable { struct ImmutableArray<T> : IEnumerable { public void Add(object o) { } IEnumerator IEnumerable.GetEnumerator() => null; } } """; var comp = CreateCompilation(sourceA, targetFramework: TargetFramework.Mscorlib40); comp.VerifyEmitDiagnostics( // 0.cs(7,35): error CS9210: This version of 'ImmutableArray<T>' cannot be used with collection expressions. // ImmutableArray<int> arr = [1, 2, 3]; Diagnostic(ErrorCode.ERR_CollectionExpressionImmutableArray, "[1, 2, 3]").WithArguments("System.Collections.Immutable.ImmutableArray<T>").WithLocation(7, 35)); var collectionType = comp.GetWellKnownType(WellKnownType.System_Collections_Immutable_ImmutableArray_T).Construct(comp.GetSpecialType(SpecialType.System_Int32)); Assert.Equal(CollectionExpressionTypeKind.ImplementsIEnumerable, ConversionsBase.GetCollectionExpressionTypeKind(comp, collectionType, out _)); // With ImmutableCollectionsMarshal.AsImmutableArray. string sourceB = """ using System.Collections.Immutable; namespace System.Runtime.InteropServices { static class ImmutableCollectionsMarshal { public static ImmutableArray<T> AsImmutableArray<T>(T[] array) => default; } } """; comp = CreateCompilation(new[] { sourceA, sourceB }, targetFramework: TargetFramework.Mscorlib40); comp.VerifyEmitDiagnostics( // 0.cs(7,35): error CS9210: This version of 'ImmutableArray<T>' cannot be used with collection expressions. // ImmutableArray<int> arr = [1, 2, 3]; Diagnostic(ErrorCode.ERR_CollectionExpressionImmutableArray, "[1, 2, 3]").WithArguments("System.Collections.Immutable.ImmutableArray<T>").WithLocation(7, 35)); collectionType = comp.GetWellKnownType(WellKnownType.System_Collections_Immutable_ImmutableArray_T).Construct(comp.GetSpecialType(SpecialType.System_Int32)); Assert.Equal(CollectionExpressionTypeKind.ImplementsIEnumerable, ConversionsBase.GetCollectionExpressionTypeKind(comp, collectionType, out _)); } [Fact] public void ImmutableArray_NoInterfaces() { string sourceA = """ using System.Collections.Immutable; class Program { static void Main() { ImmutableArray<int> arr = [1, 2, 3]; } } namespace System.Collections.Immutable { struct ImmutableArray<T> { } } """; var comp = CreateCompilation(sourceA, targetFramework: TargetFramework.Mscorlib40); comp.VerifyEmitDiagnostics( // (7,35): error CS9174: Cannot initialize type 'ImmutableArray<int>' with a collection expression because the type is not constructible. // ImmutableArray<int> arr = [1, 2, 3]; Diagnostic(ErrorCode.ERR_CollectionExpressionTargetTypeNotConstructible, "[1, 2, 3]").WithArguments("System.Collections.Immutable.ImmutableArray<int>").WithLocation(7, 35)); var collectionType = comp.GetWellKnownType(WellKnownType.System_Collections_Immutable_ImmutableArray_T).Construct(comp.GetSpecialType(SpecialType.System_Int32)); Assert.Equal(CollectionExpressionTypeKind.None, ConversionsBase.GetCollectionExpressionTypeKind(comp, collectionType, out _)); // With ImmutableCollectionsMarshal.AsImmutableArray. string sourceB = """ using System.Collections.Immutable; namespace System.Runtime.InteropServices { static class ImmutableCollectionsMarshal { public static ImmutableArray<T> AsImmutableArray<T>(T[] array) => default; } } """; comp = CreateCompilation(new[] { sourceA, sourceB }, targetFramework: TargetFramework.Mscorlib40); comp.VerifyEmitDiagnostics( // (7,35): error CS9174: Cannot initialize type 'ImmutableArray<int>' with a collection expression because the type is not constructible. // ImmutableArray<int> arr = [1, 2, 3]; Diagnostic(ErrorCode.ERR_CollectionExpressionTargetTypeNotConstructible, "[1, 2, 3]").WithArguments("System.Collections.Immutable.ImmutableArray<int>").WithLocation(7, 35)); collectionType = comp.GetWellKnownType(WellKnownType.System_Collections_Immutable_ImmutableArray_T).Construct(comp.GetSpecialType(SpecialType.System_Int32)); Assert.Equal(CollectionExpressionTypeKind.None, ConversionsBase.GetCollectionExpressionTypeKind(comp, collectionType, out _)); } [Fact] public void ImmutableArray_Dynamic_01() { string source = """ using System.Collections.Generic; using System.Collections.Immutable; class Program { static ImmutableArray<int> F1(dynamic d) => [d]; static ImmutableArray<int> F2(List<dynamic> e) => [..e]; static ImmutableArray<object> F3(dynamic[] d) => [d]; static void Main() { F1(1).Report(); F2([2, 3]).Report(); F3([4, 5]).Report(); } } """; var verifier = CompileAndVerify( new[] { source, s_collectionExtensions }, targetFramework: TargetFramework.Net80, expectedOutput: IncludeExpectedOutput("[1], [2, 3], [[4, 5]], "), verify: Verification.Skipped); verifier.VerifyIL("Program.F1", """ { // Code size 79 (0x4f) .maxstack 6 IL_0000: ldc.i4.1 IL_0001: newarr "int" IL_0006: dup IL_0007: ldc.i4.0 IL_0008: ldsfld "System.Runtime.CompilerServices.CallSite<System.Func<System.Runtime.CompilerServices.CallSite, dynamic, int>> Program.<>o__0.<>p__0" IL_000d: brtrue.s IL_0033 IL_000f: ldc.i4.0 IL_0010: ldtoken "int" IL_0015: call "System.Type System.Type.GetTypeFromHandle(System.RuntimeTypeHandle)" IL_001a: ldtoken "Program" IL_001f: call "System.Type System.Type.GetTypeFromHandle(System.RuntimeTypeHandle)" IL_0024: call "System.Runtime.CompilerServices.CallSiteBinder Microsoft.CSharp.RuntimeBinder.Binder.Convert(Microsoft.CSharp.RuntimeBinder.CSharpBinderFlags, System.Type, System.Type)" IL_0029: call "System.Runtime.CompilerServices.CallSite<System.Func<System.Runtime.CompilerServices.CallSite, dynamic, int>> System.Runtime.CompilerServices.CallSite<System.Func<System.Runtime.CompilerServices.CallSite, dynamic, int>>.Create(System.Runtime.CompilerServices.CallSiteBinder)" IL_002e: stsfld "System.Runtime.CompilerServices.CallSite<System.Func<System.Runtime.CompilerServices.CallSite, dynamic, int>> Program.<>o__0.<>p__0" IL_0033: ldsfld "System.Runtime.CompilerServices.CallSite<System.Func<System.Runtime.CompilerServices.CallSite, dynamic, int>> Program.<>o__0.<>p__0" IL_0038: ldfld "System.Func<System.Runtime.CompilerServices.CallSite, dynamic, int> System.Runtime.CompilerServices.CallSite<System.Func<System.Runtime.CompilerServices.CallSite, dynamic, int>>.Target" IL_003d: ldsfld "System.Runtime.CompilerServices.CallSite<System.Func<System.Runtime.CompilerServices.CallSite, dynamic, int>> Program.<>o__0.<>p__0" IL_0042: ldarg.0 IL_0043: callvirt "int System.Func<System.Runtime.CompilerServices.CallSite, dynamic, int>.Invoke(System.Runtime.CompilerServices.CallSite, dynamic)" IL_0048: stelem.i4 IL_0049: call "System.Collections.Immutable.ImmutableArray<int> System.Runtime.InteropServices.ImmutableCollectionsMarshal.AsImmutableArray<int>(int[])" IL_004e: ret } """); verifier.VerifyIL("Program.F2", """ { // Code size 134 (0x86) .maxstack 5 .locals init (int V_0, int[] V_1, System.Collections.Generic.List<dynamic>.Enumerator V_2, object V_3) IL_0000: ldarg.0 IL_0001: ldc.i4.0 IL_0002: stloc.0 IL_0003: dup IL_0004: callvirt "int System.Collections.Generic.List<dynamic>.Count.get" IL_0009: newarr "int" IL_000e: stloc.1 IL_000f: callvirt "System.Collections.Generic.List<dynamic>.Enumerator System.Collections.Generic.List<dynamic>.GetEnumerator()" IL_0014: stloc.2 .try { IL_0015: br.s IL_0066 IL_0017: ldloca.s V_2 IL_0019: call "dynamic System.Collections.Generic.List<dynamic>.Enumerator.Current.get" IL_001e: stloc.3 IL_001f: ldloc.1 IL_0020: ldloc.0 IL_0021: ldsfld "System.Runtime.CompilerServices.CallSite<System.Func<System.Runtime.CompilerServices.CallSite, dynamic, int>> Program.<>o__1.<>p__0" IL_0026: brtrue.s IL_004c IL_0028: ldc.i4.0 IL_0029: ldtoken "int" IL_002e: call "System.Type System.Type.GetTypeFromHandle(System.RuntimeTypeHandle)" IL_0033: ldtoken "Program" IL_0038: call "System.Type System.Type.GetTypeFromHandle(System.RuntimeTypeHandle)" IL_003d: call "System.Runtime.CompilerServices.CallSiteBinder Microsoft.CSharp.RuntimeBinder.Binder.Convert(Microsoft.CSharp.RuntimeBinder.CSharpBinderFlags, System.Type, System.Type)" IL_0042: call "System.Runtime.CompilerServices.CallSite<System.Func<System.Runtime.CompilerServices.CallSite, dynamic, int>> System.Runtime.CompilerServices.CallSite<System.Func<System.Runtime.CompilerServices.CallSite, dynamic, int>>.Create(System.Runtime.CompilerServices.CallSiteBinder)" IL_0047: stsfld "System.Runtime.CompilerServices.CallSite<System.Func<System.Runtime.CompilerServices.CallSite, dynamic, int>> Program.<>o__1.<>p__0" IL_004c: ldsfld "System.Runtime.CompilerServices.CallSite<System.Func<System.Runtime.CompilerServices.CallSite, dynamic, int>> Program.<>o__1.<>p__0" IL_0051: ldfld "System.Func<System.Runtime.CompilerServices.CallSite, dynamic, int> System.Runtime.CompilerServices.CallSite<System.Func<System.Runtime.CompilerServices.CallSite, dynamic, int>>.Target" IL_0056: ldsfld "System.Runtime.CompilerServices.CallSite<System.Func<System.Runtime.CompilerServices.CallSite, dynamic, int>> Program.<>o__1.<>p__0" IL_005b: ldloc.3 IL_005c: callvirt "int System.Func<System.Runtime.CompilerServices.CallSite, dynamic, int>.Invoke(System.Runtime.CompilerServices.CallSite, dynamic)" IL_0061: stelem.i4 IL_0062: ldloc.0 IL_0063: ldc.i4.1 IL_0064: add IL_0065: stloc.0 IL_0066: ldloca.s V_2 IL_0068: call "bool System.Collections.Generic.List<dynamic>.Enumerator.MoveNext()" IL_006d: brtrue.s IL_0017 IL_006f: leave.s IL_007f } finally { IL_0071: ldloca.s V_2 IL_0073: constrained. "System.Collections.Generic.List<dynamic>.Enumerator" IL_0079: callvirt "void System.IDisposable.Dispose()" IL_007e: endfinally } IL_007f: ldloc.1 IL_0080: call "System.Collections.Immutable.ImmutableArray<int> System.Runtime.InteropServices.ImmutableCollectionsMarshal.AsImmutableArray<int>(int[])" IL_0085: ret } """); verifier.VerifyIL("Program.F3", """ { // Code size 16 (0x10) .maxstack 4 IL_0000: ldc.i4.1 IL_0001: newarr "object" IL_0006: dup IL_0007: ldc.i4.0 IL_0008: ldarg.0 IL_0009: stelem.ref IL_000a: call "System.Collections.Immutable.ImmutableArray<object> System.Runtime.InteropServices.ImmutableCollectionsMarshal.AsImmutableArray<object>(object[])" IL_000f: ret } """); } [Fact] public void ImmutableArray_08() { string sourceA = """ using System.Collections.Immutable; class Program { static void Main() { ImmutableArray<int> arr = [1, 2, 3]; ImmutableArray<int> arr1 = [..arr]; arr1.Report(); } } """; var verifier = CompileAndVerify(new[] { sourceA, s_collectionExtensions }, targetFramework: TargetFramework.Net80, expectedOutput: IncludeExpectedOutput("[1, 2, 3],"), verify: Verification.Skipped); verifier.VerifyDiagnostics(); verifier.VerifyIL("Program.Main", """ { // Code size 55 (0x37) .maxstack 3 .locals init (System.Collections.Immutable.ImmutableArray<int> V_0, //arr System.ReadOnlySpan<int> V_1) IL_0000: ldc.i4.3 IL_0001: newarr "int" IL_0006: dup IL_0007: ldtoken "<PrivateImplementationDetails>.__StaticArrayInitTypeSize=12 <PrivateImplementationDetails>.4636993D3E1DA4E9D6B8F87B79E8F7C6D018580D52661950EABC3845C5897A4D" IL_000c: call "void System.Runtime.CompilerServices.RuntimeHelpers.InitializeArray(System.Array, System.RuntimeFieldHandle)" IL_0011: call "System.Collections.Immutable.ImmutableArray<int> System.Runtime.InteropServices.ImmutableCollectionsMarshal.AsImmutableArray<int>(int[])" IL_0016: stloc.0 IL_0017: ldloca.s V_0 IL_0019: call "System.ReadOnlySpan<int> System.Collections.Immutable.ImmutableArray<int>.AsSpan()" IL_001e: stloc.1 IL_001f: ldloca.s V_1 IL_0021: call "int[] System.ReadOnlySpan<int>.ToArray()" IL_0026: call "System.Collections.Immutable.ImmutableArray<int> System.Runtime.InteropServices.ImmutableCollectionsMarshal.AsImmutableArray<int>(int[])" IL_002b: box "System.Collections.Immutable.ImmutableArray<int>" IL_0030: ldc.i4.0 IL_0031: call "void CollectionExtensions.Report(object, bool)" IL_0036: ret } """); } [Fact] public void ImmutableArray_09() { string sourceA = """ using System; using System.Collections.Immutable; class Program { static void Main() { M([1, 2, 3]).Report(); } static ImmutableArray<int> M(ReadOnlySpan<int> span) => [.. span]; } """; var verifier = CompileAndVerify([sourceA, s_collectionExtensions], targetFramework: TargetFramework.Net80, expectedOutput: IncludeExpectedOutput("[1, 2, 3],"), verify: Verification.Skipped); verifier.VerifyDiagnostics(); verifier.VerifyIL("Program.M", """ { // Code size 7 (0x7) .maxstack 1 IL_0000: ldarg.0 IL_0001: call "System.Collections.Immutable.ImmutableArray<int> System.Collections.Immutable.ImmutableArray.Create<int>(System.ReadOnlySpan<int>)" IL_0006: ret } """); } [Fact] public void ImmutableArray_10() { string sourceA = """ using System; using System.Collections.Immutable; class Program { static void Main() { M([1, 2, 3]).Report(); } static ImmutableArray<object> M(ReadOnlySpan<int> span) => [.. span]; } """; var verifier = CompileAndVerify([sourceA, s_collectionExtensions], targetFramework: TargetFramework.Net80, expectedOutput: IncludeExpectedOutput("[1, 2, 3],"), verify: Verification.Skipped); verifier.VerifyDiagnostics(); verifier.VerifyIL("Program.M", """ { // Code size 67 (0x43) .maxstack 3 .locals init (System.ReadOnlySpan<int> V_0, int V_1, object[] V_2, System.ReadOnlySpan<int>.Enumerator V_3, int V_4) IL_0000: ldarg.0 IL_0001: stloc.0 IL_0002: ldc.i4.0 IL_0003: stloc.1 IL_0004: ldloca.s V_0 IL_0006: call "int System.ReadOnlySpan<int>.Length.get" IL_000b: newarr "object" IL_0010: stloc.2 IL_0011: ldloca.s V_0 IL_0013: call "System.ReadOnlySpan<int>.Enumerator System.ReadOnlySpan<int>.GetEnumerator()" IL_0018: stloc.3 IL_0019: br.s IL_0033 IL_001b: ldloca.s V_3 IL_001d: call "ref readonly int System.ReadOnlySpan<int>.Enumerator.Current.get" IL_0022: ldind.i4 IL_0023: stloc.s V_4 IL_0025: ldloc.2 IL_0026: ldloc.1 IL_0027: ldloc.s V_4 IL_0029: box "int" IL_002e: stelem.ref IL_002f: ldloc.1 IL_0030: ldc.i4.1 IL_0031: add IL_0032: stloc.1 IL_0033: ldloca.s V_3 IL_0035: call "bool System.ReadOnlySpan<int>.Enumerator.MoveNext()" IL_003a: brtrue.s IL_001b IL_003c: ldloc.2 IL_003d: call "System.Collections.Immutable.ImmutableArray<object> System.Runtime.InteropServices.ImmutableCollectionsMarshal.AsImmutableArray<object>(object[])" IL_0042: ret } """); } [Theory, CombinatorialData] public void ImmutableArray_11(bool readOnlySpan) { var spanType = readOnlySpan ? "ReadOnlySpan" : "Span"; var source = $$""" using System; using System.Collections.Immutable; class Program { static void Main() { M([new(), new(), new()]).Report(); } static ImmutableArray<Base> M({{spanType}}<Derived> span) => [.. span]; } class Base { private static int _totalCount; private readonly int _id; public Base() { _id = _totalCount++; } public override string ToString() => $"{GetType().Name} {_id}"; } class Derived : Base; """; var verifier = CompileAndVerify( [source, s_collectionExtensions], targetFramework: TargetFramework.Net90, verify: Verification.Skipped, expectedOutput: IncludeExpectedOutput("[Derived 0, Derived 1, Derived 2], ")); verifier.VerifyDiagnostics(); verifier.VerifyIL("Program.M", $$""" { // Code size 13 (0xd) .maxstack 1 IL_0000: ldarga.s V_0 IL_0002: call "Derived[] System.{{spanType}}<Derived>.ToArray()" IL_0007: call "System.Collections.Immutable.ImmutableArray<Base> System.Runtime.InteropServices.ImmutableCollectionsMarshal.AsImmutableArray<Base>(Base[])" IL_000c: ret } """); } [Fact] public void ImmutableArray_12() { var source = """ using System.Collections.Generic; using System.Collections.Immutable; class Program { static void Main() { M([new(), new(), new()]).Report(); } static ImmutableArray<Base> M(List<Derived> list) => [.. list]; } class Base { private static int _totalCount; private readonly int _id; public Base() { _id = _totalCount++; } public override string ToString() => $"{GetType().Name} {_id}"; } class Derived : Base; """; var verifier = CompileAndVerify( [source, s_collectionExtensions], targetFramework: TargetFramework.Net90, verify: Verification.Skipped, expectedOutput: IncludeExpectedOutput("[Derived 0, Derived 1, Derived 2], ")); verifier.VerifyDiagnostics(); verifier.VerifyIL("Program.M", """ { // Code size 12 (0xc) .maxstack 1 IL_0000: ldarg.0 IL_0001: callvirt "Derived[] System.Collections.Generic.List<Derived>.ToArray()" IL_0006: call "System.Collections.Immutable.ImmutableArray<Base> System.Runtime.InteropServices.ImmutableCollectionsMarshal.AsImmutableArray<Base>(Base[])" IL_000b: ret } """); } [Fact] public void ImmutableArray_13() { var source = """ using System; using System.Collections.Immutable; class Program { static void Main() { M([1, 2, 3]).Report(); } static ImmutableArray<dynamic> M(Span<object> span) => [.. span]; } """; var verifier = CompileAndVerify( [source, s_collectionExtensions], targetFramework: TargetFramework.Net90, verify: Verification.Skipped, expectedOutput: IncludeExpectedOutput("[1, 2, 3], ")); verifier.VerifyDiagnostics(); verifier.VerifyIL("Program.M", """ { // Code size 13 (0xd) .maxstack 1 IL_0000: ldarga.s V_0 IL_0002: call "object[] System.Span<object>.ToArray()" IL_0007: call "System.Collections.Immutable.ImmutableArray<dynamic> System.Runtime.InteropServices.ImmutableCollectionsMarshal.AsImmutableArray<dynamic>(dynamic[])" IL_000c: ret } """); } [Fact] public void ImmutableArray_14() { var source = """ using System; using System.Collections.Immutable; class Program { static void Main() { M([1, 2, 3]).Report(); } static ImmutableArray<dynamic> M(ReadOnlySpan<object> span) => [.. span]; } """; var verifier = CompileAndVerify( [source, s_collectionExtensions], targetFramework: TargetFramework.Net90, verify: Verification.Skipped, expectedOutput: IncludeExpectedOutput("[1, 2, 3], ")); verifier.VerifyDiagnostics(); verifier.VerifyIL("Program.M", """ { // Code size 7 (0x7) .maxstack 1 IL_0000: ldarg.0 IL_0001: call "System.Collections.Immutable.ImmutableArray<dynamic> System.Collections.Immutable.ImmutableArray.Create<dynamic>(params System.ReadOnlySpan<dynamic>)" IL_0006: ret } """); } [Fact] public void ImmutableArray_15() { var source = """ using System.Collections.Generic; using System.Collections.Immutable; class Program { static void Main() { M([1, 2, 3]).Report(); } static ImmutableArray<dynamic> M(List<object> list) => [.. list]; } """; var verifier = CompileAndVerify( [source, s_collectionExtensions], targetFramework: TargetFramework.Net90, verify: Verification.Skipped, expectedOutput: IncludeExpectedOutput("[1, 2, 3], ")); verifier.VerifyDiagnostics(); verifier.VerifyIL("Program.M", """ { // Code size 12 (0xc) .maxstack 1 IL_0000: ldarg.0 IL_0001: callvirt "object[] System.Collections.Generic.List<object>.ToArray()" IL_0006: call "System.Collections.Immutable.ImmutableArray<dynamic> System.Runtime.InteropServices.ImmutableCollectionsMarshal.AsImmutableArray<dynamic>(dynamic[])" IL_000b: ret } """); } [Theory, CombinatorialData] public void ImmutableArray_16(bool readOnlySpan) { var spanType = readOnlySpan ? "ReadOnlySpan" : "Span"; var source = $$""" using System; using System.Collections.Immutable; class Program { static void Main() { M([1, 2, 3]).Report(); } static ImmutableArray<object> M({{spanType}}<int> span) => [.. span]; } """; var verifier = CompileAndVerify( [source, s_collectionExtensions], targetFramework: TargetFramework.Net90, verify: Verification.Skipped, expectedOutput: IncludeExpectedOutput("[1, 2, 3], ")); verifier.VerifyDiagnostics(); verifier.VerifyIL("Program.M", $$""" { // Code size 67 (0x43) .maxstack 3 .locals init (System.{{spanType}}<int> V_0, int V_1, object[] V_2, System.{{spanType}}<int>.Enumerator V_3, int V_4) IL_0000: ldarg.0 IL_0001: stloc.0 IL_0002: ldc.i4.0 IL_0003: stloc.1 IL_0004: ldloca.s V_0 IL_0006: call "int System.{{spanType}}<int>.Length.get" IL_000b: newarr "object" IL_0010: stloc.2 IL_0011: ldloca.s V_0 IL_0013: call "System.{{spanType}}<int>.Enumerator System.{{spanType}}<int>.GetEnumerator()" IL_0018: stloc.3 IL_0019: br.s IL_0033 IL_001b: ldloca.s V_3 IL_001d: call "ref {{(readOnlySpan ? "readonly " : "")}}int System.{{spanType}}<int>.Enumerator.Current.get" IL_0022: ldind.i4 IL_0023: stloc.s V_4 IL_0025: ldloc.2 IL_0026: ldloc.1 IL_0027: ldloc.s V_4 IL_0029: box "int" IL_002e: stelem.ref IL_002f: ldloc.1 IL_0030: ldc.i4.1 IL_0031: add IL_0032: stloc.1 IL_0033: ldloca.s V_3 IL_0035: call "bool System.{{spanType}}<int>.Enumerator.MoveNext()" IL_003a: brtrue.s IL_001b IL_003c: ldloc.2 IL_003d: call "System.Collections.Immutable.ImmutableArray<object> System.Runtime.InteropServices.ImmutableCollectionsMarshal.AsImmutableArray<object>(object[])" IL_0042: ret } """); } [Fact] public void ImmutableArray_17() { var source = """ using System.Collections.Generic; using System.Collections.Immutable; class Program { static void Main() { M([1, 2, 3]).Report(); } static ImmutableArray<object> M(List<int> span) => [.. span]; } """; var verifier = CompileAndVerify( [source, s_collectionExtensions], targetFramework: TargetFramework.Net90, verify: Verification.Skipped, expectedOutput: IncludeExpectedOutput("[1, 2, 3], ")); verifier.VerifyDiagnostics(); verifier.VerifyIL("Program.M", """ { // Code size 76 (0x4c) .maxstack 3 .locals init (int V_0, object[] V_1, System.Collections.Generic.List<int>.Enumerator V_2, int V_3) IL_0000: ldarg.0 IL_0001: ldc.i4.0 IL_0002: stloc.0 IL_0003: dup IL_0004: callvirt "int System.Collections.Generic.List<int>.Count.get" IL_0009: newarr "object" IL_000e: stloc.1 IL_000f: callvirt "System.Collections.Generic.List<int>.Enumerator System.Collections.Generic.List<int>.GetEnumerator()" IL_0014: stloc.2 .try { IL_0015: br.s IL_002c IL_0017: ldloca.s V_2 IL_0019: call "int System.Collections.Generic.List<int>.Enumerator.Current.get" IL_001e: stloc.3 IL_001f: ldloc.1 IL_0020: ldloc.0 IL_0021: ldloc.3 IL_0022: box "int" IL_0027: stelem.ref IL_0028: ldloc.0 IL_0029: ldc.i4.1 IL_002a: add IL_002b: stloc.0 IL_002c: ldloca.s V_2 IL_002e: call "bool System.Collections.Generic.List<int>.Enumerator.MoveNext()" IL_0033: brtrue.s IL_0017 IL_0035: leave.s IL_0045 } finally { IL_0037: ldloca.s V_2 IL_0039: constrained. "System.Collections.Generic.List<int>.Enumerator" IL_003f: callvirt "void System.IDisposable.Dispose()" IL_0044: endfinally } IL_0045: ldloc.1 IL_0046: call "System.Collections.Immutable.ImmutableArray<object> System.Runtime.InteropServices.ImmutableCollectionsMarshal.AsImmutableArray<object>(object[])" IL_004b: ret } """); } [Fact] public void SpanImplicitAllocationWarning_01() { var source = """ using System; class Program { static void M() { Span<int> s1 = [1]; // 1 Span<int> s2 = (int[])[1]; } } """; var verifier = CompileAndVerify(source, targetFramework: TargetFramework.Net70, verify: Verification.Skipped, options: TestOptions.ReleaseDll.WithSpecificDiagnosticOptions(WithSpanAllocWarning)); verifier.VerifyDiagnostics( // (7,24): warning CS9208: Collection expression of type 'Span<int>' may incur unexpected heap allocations. Consider explicitly creating an array, then converting to 'Span<int>' to make the allocation explicit. // Span<int> s1 = [1]; // 1 Diagnostic(ErrorCode.WRN_CollectionExpressionRefStructMayAllocate, "[1]").WithArguments("System.Span<int>").WithLocation(7, 24)); verifier.VerifyIL("Program.M", """ { // Code size 33 (0x21) .maxstack 4 IL_0000: ldc.i4.1 IL_0001: newarr "int" IL_0006: dup IL_0007: ldc.i4.0 IL_0008: ldc.i4.1 IL_0009: stelem.i4 IL_000a: newobj "System.Span<int>..ctor(int[])" IL_000f: pop IL_0010: ldc.i4.1 IL_0011: newarr "int" IL_0016: dup IL_0017: ldc.i4.0 IL_0018: ldc.i4.1 IL_0019: stelem.i4 IL_001a: call "System.Span<int> System.Span<int>.op_Implicit(int[])" IL_001f: pop IL_0020: ret } """); verifier = CompileAndVerify(source, targetFramework: TargetFramework.Net80, verify: Verification.Skipped); verifier.VerifyDiagnostics(); verifier.VerifyIL("Program.M", """ { // Code size 27 (0x1b) .maxstack 4 .locals init (int V_0) IL_0000: ldc.i4.1 IL_0001: stloc.0 IL_0002: ldloca.s V_0 IL_0004: newobj "System.Span<int>..ctor(ref int)" IL_0009: pop IL_000a: ldc.i4.1 IL_000b: newarr "int" IL_0010: dup IL_0011: ldc.i4.0 IL_0012: ldc.i4.1 IL_0013: stelem.i4 IL_0014: call "System.Span<int> System.Span<int>.op_Implicit(int[])" IL_0019: pop IL_001a: ret } """); } [WorkItem("https://github.com/dotnet/roslyn/issues/70638")] [Fact] public void RefStruct_ImplementsIEnumerable() { string source = """ using System.Collections; ref struct S : IEnumerable { public void Add(params S[] x) => throw null; public Enumerator GetEnumerator() => new Enumerator(); IEnumerator IEnumerable.GetEnumerator() => throw null; } class Enumerator { public bool MoveNext() => false; public S Current => default; } class Program { static S F() => [[]]; } """; var comp = CreateCompilation(source); comp.VerifyEmitDiagnostics( // (4,28): error CS0611: Array elements cannot be of type 'S' // public void Add(params S[] x) => throw null; Diagnostic(ErrorCode.ERR_ArrayElementCantBeRefAny, "S").WithArguments("S").WithLocation(4, 28), // (15,22): error CS9358: Element type of this collection may not be a ref struct or a type parameter allowing ref structs // static S F() => [[]]; Diagnostic(ErrorCode.ERR_CollectionRefLikeElementType, "[]").WithLocation(15, 22)); } [WorkItem("https://github.com/dotnet/roslyn/issues/70638")] [Fact] public void RefStruct_ImplementsIEnumerableT() { string source = """ using System.Collections; using System.Collections.Generic; ref struct S<T> : IEnumerable<T> { public void Add(T t) => throw null; public IEnumerator<T> GetEnumerator() => throw null; IEnumerator IEnumerable.GetEnumerator() => throw null; } class Program { static S<int> F() => [1, 2, 3]; } """; var comp = CreateCompilation(source); comp.VerifyEmitDiagnostics(); } [WorkItem("https://github.com/dotnet/roslyn/issues/70638")] [Fact] public void RefStruct_ImmutableArray_01() { string sourceA = """ #pragma warning disable 436 using System; using System.Collections.Generic; using System.Collections.Immutable; namespace System.Collections.Immutable { public ref struct ImmutableArray<T> { public IEnumerator<T> GetEnumerator() => null; } } namespace System.Runtime.InteropServices { public static class ImmutableCollectionsMarshal { public static ImmutableArray<T> AsImmutableArray<T>(T[] array) => default; } } """; string sourceB = """ #pragma warning disable 436 using System.Collections.Immutable; class Program { static ImmutableArray<int> F1() => [1, 2, 3]; static ImmutableArray<int> F2(int x, int y) => [x, y]; } """; var comp = CreateCompilation(new[] { sourceA, sourceB }, targetFramework: TargetFramework.Net80); comp.VerifyEmitDiagnostics( // 1.cs(5,40): error CS9174: Cannot initialize type 'ImmutableArray<int>' with a collection expression because the type is not constructible. // static ImmutableArray<int> F1() => [1, 2, 3]; Diagnostic(ErrorCode.ERR_CollectionExpressionTargetTypeNotConstructible, "[1, 2, 3]").WithArguments("System.Collections.Immutable.ImmutableArray<int>").WithLocation(5, 40), // 1.cs(6,52): error CS9174: Cannot initialize type 'ImmutableArray<int>' with a collection expression because the type is not constructible. // static ImmutableArray<int> F2(int x, int y) => [x, y]; Diagnostic(ErrorCode.ERR_CollectionExpressionTargetTypeNotConstructible, "[x, y]").WithArguments("System.Collections.Immutable.ImmutableArray<int>").WithLocation(6, 52)); } [WorkItem("https://github.com/dotnet/roslyn/issues/70638")] [Fact] public void RefStruct_ImmutableArray_02() { string sourceA = """ #pragma warning disable 436 using System; using System.Collections.Generic; using System.Collections.Immutable; using System.Runtime.CompilerServices; namespace System.Collections.Immutable { [CollectionBuilder(typeof(ImmutableArray), "Create")] public ref struct ImmutableArray<T> { public IEnumerator<T> GetEnumerator() => null; } public static class ImmutableArray { public static ImmutableArray<T> Create<T>(ReadOnlySpan<T> items) => default; } } namespace System.Runtime.InteropServices { public static class ImmutableCollectionsMarshal { public static ImmutableArray<T> AsImmutableArray<T>(T[] array) => default; } } """; string sourceB = """ #pragma warning disable 436 using System.Collections.Immutable; class Program { static ImmutableArray<int> F1() => [1, 2, 3]; static ImmutableArray<int> F2(int x, int y) => [x, y]; } """; var comp = CreateCompilation(new[] { sourceA, sourceB }, targetFramework: TargetFramework.Net80); comp.VerifyEmitDiagnostics( // 1.cs(6,52): error CS9203: A collection expression of type 'ImmutableArray<int>' cannot be used in this context because it may be exposed outside of the current scope. // static ImmutableArray<int> F2(int x, int y) => [x, y]; Diagnostic(ErrorCode.ERR_CollectionExpressionEscape, "[x, y]").WithArguments("System.Collections.Immutable.ImmutableArray<int>").WithLocation(6, 52)); } [Fact] public void ElementNullability_ArrayCollection() { string src = """ #nullable enable string[] x1 = [null]; string?[] x2 = [null]; #nullable disable string[] #nullable enable x3 = [null]; """; CreateCompilation(src).VerifyEmitDiagnostics( // (2,16): warning CS8625: Cannot convert null literal to non-nullable reference type. // string[] x1 = [null]; Diagnostic(ErrorCode.WRN_NullAsNonNullable, "null").WithLocation(2, 16) ); } [Fact] public void ElementNullability_ArrayCollection_InferredLocal() { string src = """ #nullable enable var x1 = M1(); x1 = [null]; // 1 var x2 = M2(); x2 = [null]; var x3 = M3(); x3 = [null]; string[] M1() => throw null!; string?[] M2() => throw null!; #nullable disable string[] #nullable enable M3() => throw null!; """; CreateCompilation(src).VerifyEmitDiagnostics( // (3,7): warning CS8625: Cannot convert null literal to non-nullable reference type. // x1 = [null]; // 1 Diagnostic(ErrorCode.WRN_NullAsNonNullable, "null").WithLocation(3, 7) ); } [Fact] public void ElementNullability_ArrayCollection_Nested() { string src = """ #nullable enable string[][] x1 = [[null]]; string?[][] x2 = [[null]]; #nullable disable string[][] #nullable enable x3 = [[null]]; """; CreateCompilation(src).VerifyEmitDiagnostics( // (2,19): warning CS8625: Cannot convert null literal to non-nullable reference type. // string[][] x1 = [[null]]; Diagnostic(ErrorCode.WRN_NullAsNonNullable, "null").WithLocation(2, 19) ); } [Fact] public void ElementNullability_ArrayCollection_NullableValueTypes() { string src = """ #nullable enable int[] x1 = [null]; int?[] x2 = [null]; #nullable disable int[] #nullable enable x3 = [null]; """; CreateCompilation(src).VerifyEmitDiagnostics( // (2,13): error CS0037: Cannot convert null to 'int' because it is a non-nullable value type // int[] x1 = [null]; Diagnostic(ErrorCode.ERR_ValueCantBeNull, "null").WithArguments("int").WithLocation(2, 13), // (8,11): error CS0037: Cannot convert null to 'int' because it is a non-nullable value type // x3 = [null]; Diagnostic(ErrorCode.ERR_ValueCantBeNull, "null").WithArguments("int").WithLocation(8, 11) ); } [Theory] [InlineData("System.Span")] [InlineData("System.ReadOnlySpan")] public void ElementNullability_SpanCollection(string spanType) { string src = $$""" #nullable enable {{spanType}}<string?> x1 = [null]; {{spanType}}<string> x2 = [null]; #nullable disable {{spanType}}<string> #nullable enable x3 = [null]; """; CreateCompilation(src, targetFramework: TargetFramework.Net70, options: TestOptions.ReleaseExe.WithSpecificDiagnosticOptions(WithSpanAllocWarning)).VerifyEmitDiagnostics( // (3,7): warning CS9208: Collection expression of type 'ReadOnlySpan<string?>' may incur unexpected heap allocations. Consider explicitly creating an array, then converting to 'ReadOnlySpan<string?>' to make the allocation explicit. // = [null]; Diagnostic(ErrorCode.WRN_CollectionExpressionRefStructMayAllocate, "[null]").WithArguments($"{spanType}<string?>").WithLocation(3, 7), // (5,7): warning CS9208: Collection expression of type 'ReadOnlySpan<string>' may incur unexpected heap allocations. Consider explicitly creating an array, then converting to 'ReadOnlySpan<string>' to make the allocation explicit. // = [null]; Diagnostic(ErrorCode.WRN_CollectionExpressionRefStructMayAllocate, "[null]").WithArguments($"{spanType}<string>").WithLocation(5, 7), // (5,8): warning CS8625: Cannot convert null literal to non-nullable reference type. // = [null]; Diagnostic(ErrorCode.WRN_NullAsNonNullable, "null").WithLocation(5, 8), // (10,10): warning CS9208: Collection expression of type 'ReadOnlySpan<string>' may incur unexpected heap allocations. Consider explicitly creating an array, then converting to 'ReadOnlySpan<string>' to make the allocation explicit. // x3 = [null]; Diagnostic(ErrorCode.WRN_CollectionExpressionRefStructMayAllocate, "[null]").WithArguments($"{spanType}<string>").WithLocation(10, 10) ); } [Fact] public void ElementNullability_CollectionBuilderCollection_Generic() { string src = """ using System; using System.Collections; using System.Collections.Generic; using System.Runtime.CompilerServices; #nullable enable MyCollection<string?> x1 = [null]; MyCollection<string> x2 = [null]; #nullable disable MyCollection<string> #nullable enable x3 = [null]; [CollectionBuilder(typeof(MyCollectionBuilder), nameof(MyCollectionBuilder.Create))] public struct MyCollection<T> : IEnumerable<T> { private readonly List<T> _list; public MyCollection(List<T> list) { _list = list; } public IEnumerator<T> GetEnumerator() => _list.GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); } public class MyCollectionBuilder { public static MyCollection<T> Create<T>(ReadOnlySpan<T> items) { return new MyCollection<T>(new List<T>(items.ToArray())); } } """; CreateCompilation(src, targetFramework: TargetFramework.Net80).VerifyEmitDiagnostics( // (8,28): warning CS8625: Cannot convert null literal to non-nullable reference type. // MyCollection<string> x2 = [null]; Diagnostic(ErrorCode.WRN_NullAsNonNullable, "null").WithLocation(8, 28) ); } [Fact] public void ElementNullability_CollectionBuilderCollection_Nullable() { string src = """ using System; using System.Collections; using System.Collections.Generic; using System.Runtime.CompilerServices; #nullable enable MyCollection<string?>? x1 = [null]; MyCollection<string>? x2 = [null]; #nullable disable MyCollection<string>? #nullable enable x3 = [null]; [CollectionBuilder(typeof(MyCollectionBuilder), nameof(MyCollectionBuilder.Create))] public struct MyCollection<T> : IEnumerable<T> { private readonly List<T> _list; public MyCollection(List<T> list) { _list = list; } public IEnumerator<T> GetEnumerator() => _list.GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); } public class MyCollectionBuilder { public static MyCollection<T> Create<T>(ReadOnlySpan<T> items) { return new MyCollection<T>(new List<T>(items.ToArray())); } } """; CreateCompilation(src, targetFramework: TargetFramework.Net80).VerifyEmitDiagnostics( // (8,29): warning CS8625: Cannot convert null literal to non-nullable reference type. // MyCollection<string>? x2 = [null]; Diagnostic(ErrorCode.WRN_NullAsNonNullable, "null").WithLocation(8, 29) ); } [Fact] public void ElementNullability_CollectionBuilderCollection_NullableElements() { string src = """ using System; using System.Collections; using System.Collections.Generic; using System.Runtime.CompilerServices; #nullable enable MyCollection<int?>? x1 = [null]; MyCollection<int>? x2 = [0]; [CollectionBuilder(typeof(MyCollectionBuilder), nameof(MyCollectionBuilder.Create))] public struct MyCollection<T> : IEnumerable<T> { private readonly List<T> _list; public MyCollection(List<T> list) { _list = list; } public IEnumerator<T> GetEnumerator() => _list.GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); } public class MyCollectionBuilder { public static MyCollection<T> Create<T>(ReadOnlySpan<T> items) { return new MyCollection<T>(new List<T>(items.ToArray())); } } """; CreateCompilation(src, targetFramework: TargetFramework.Net80).VerifyEmitDiagnostics(); } [Fact] public void ElementNullability_CollectionBuilderCollection_NonNullableString() { string src = """ using System; using System.Collections; using System.Collections.Generic; using System.Runtime.CompilerServices; #nullable enable MyCollection x1 = [null]; [CollectionBuilder(typeof(MyCollectionBuilder), nameof(MyCollectionBuilder.Create))] public struct MyCollection : IEnumerable<string> { private readonly List<string> _list; public MyCollection(List<string> list) { _list = list; } public IEnumerator<string> GetEnumerator() => _list.GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); } public class MyCollectionBuilder { public static MyCollection Create(ReadOnlySpan<string> items) // non-nullable string { return new MyCollection(new List<string>(items.ToArray())); } } """; CreateCompilation(src, targetFramework: TargetFramework.Net80).VerifyEmitDiagnostics( // (7,20): warning CS8625: Cannot convert null literal to non-nullable reference type. // MyCollection x1 = [null]; Diagnostic(ErrorCode.WRN_NullAsNonNullable, "null").WithLocation(7, 20) ); } [Fact] public void ElementNullability_CollectionBuilderCollection_NullableString() { string src = """ using System; using System.Collections; using System.Collections.Generic; using System.Runtime.CompilerServices; #nullable enable MyCollection x1 = [null]; [CollectionBuilder(typeof(MyCollectionBuilder), nameof(MyCollectionBuilder.Create))] public struct MyCollection : IEnumerable<string?> { private readonly List<string?> _list; public MyCollection(List<string?> list) { _list = list; } public IEnumerator<string?> GetEnumerator() => _list.GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); } public class MyCollectionBuilder { public static MyCollection Create(ReadOnlySpan<string?> items) // nullable string { return new MyCollection(new List<string?>(items.ToArray())); } } """; CreateCompilation(src, targetFramework: TargetFramework.Net80).VerifyEmitDiagnostics(); } [Fact] public void ElementNullability_CollectionBuilderCollection_ConstrainedCreate() { string src = """ using System; using System.Collections; using System.Collections.Generic; using System.Runtime.CompilerServices; #nullable enable MyCollection<string?> x1 = [null]; // 1 MyCollection<string> x2 = [null]; // 2 #nullable disable MyCollection<string> #nullable enable x3 = [null]; // 3 [CollectionBuilder(typeof(MyCollectionBuilder), nameof(MyCollectionBuilder.Create))] public struct MyCollection<T> : IEnumerable<T> { private readonly List<T> _list; public MyCollection(List<T> list) { _list = list; } public IEnumerator<T> GetEnumerator() => _list.GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); } public class MyCollectionBuilder { public static MyCollection<T> Create<T>(ReadOnlySpan<T> items) where T : notnull { return new MyCollection<T>(new List<T>(items.ToArray())); } } """; CreateCompilation(src, targetFramework: TargetFramework.Net80).VerifyEmitDiagnostics( // (7,28): warning CS8714: The type 'string?' cannot be used as type parameter 'T' in the generic type or method 'MyCollectionBuilder.Create<T>(ReadOnlySpan<T>)'. Nullability of type argument 'string?' doesn't match 'notnull' constraint. // MyCollection<string?> x1 = [null]; // 1 Diagnostic(ErrorCode.WRN_NullabilityMismatchInTypeParameterNotNullConstraint, "[null]").WithArguments("MyCollectionBuilder.Create<T>(System.ReadOnlySpan<T>)", "T", "string?").WithLocation(7, 28), // (8,28): warning CS8625: Cannot convert null literal to non-nullable reference type. // MyCollection<string> x2 = [null]; // 2 Diagnostic(ErrorCode.WRN_NullAsNonNullable, "null").WithLocation(8, 28)); } [Fact] public void ElementNullability_CollectionBuilderCollection_NotNullCreate() { string src = """ using System; using System.Collections; using System.Collections.Generic; using System.Runtime.CompilerServices; #nullable enable MyCollection<string?> x1 = [null]; MyCollection<string> x2 = [null]; #nullable disable MyCollection<string> #nullable enable x3 = [null]; [CollectionBuilder(typeof(MyCollectionBuilder), nameof(MyCollectionBuilder.Create))] public struct MyCollection<T> : IEnumerable<T> { private readonly List<T> _list; public MyCollection(List<T> list) { _list = list; } public IEnumerator<T> GetEnumerator() => _list.GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); } public class MyCollectionBuilder { public static MyCollection<T> Create<T>(ReadOnlySpan<T?> items) where T : notnull { return new MyCollection<T>(new List<T>(items.ToArray()!)); } } """; CreateCompilation(src, targetFramework: TargetFramework.Net80).VerifyEmitDiagnostics( // (7,28): warning CS8714: The type 'string?' cannot be used as type parameter 'T' in the generic type or method 'MyCollectionBuilder.Create<T>(ReadOnlySpan<T?>)'. Nullability of type argument 'string?' doesn't match 'notnull' constraint. // MyCollection<string?> x1 = [null]; Diagnostic(ErrorCode.WRN_NullabilityMismatchInTypeParameterNotNullConstraint, "[null]").WithArguments("MyCollectionBuilder.Create<T>(System.ReadOnlySpan<T?>)", "T", "string?").WithLocation(7, 28), // (8,28): warning CS8625: Cannot convert null literal to non-nullable reference type. // MyCollection<string> x2 = [null]; Diagnostic(ErrorCode.WRN_NullAsNonNullable, "null").WithLocation(8, 28)); } [Fact] public void ElementNullability_Inference_CollectionBuilderCollection() { string src = """ using System; using System.Collections; using System.Collections.Generic; using System.Runtime.CompilerServices; #nullable enable M("hi", [null]); M((string?)null, [null]); void M<T>(T t, MyCollection<T> mc) { } [CollectionBuilder(typeof(MyCollectionBuilder), nameof(MyCollectionBuilder.Create))] public struct MyCollection<T> : IEnumerable<T> { private readonly List<T> _list; public MyCollection(List<T> list) { _list = list; } public IEnumerator<T> GetEnumerator() => _list.GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); } public class MyCollectionBuilder { public static MyCollection<T> Create<T>(ReadOnlySpan<T> items) { return new MyCollection<T>(new List<T>(items.ToArray())); } } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net80).VerifyEmitDiagnostics(); var tree = comp.SyntaxTrees.Single(); var model = comp.GetSemanticModel(tree); var invocation0 = GetSyntax<InvocationExpressionSyntax>(tree, """M("hi", [null])"""); Assert.Equal("void M<System.String?>(System.String? t, MyCollection<System.String?> mc)", model.GetSymbolInfo(invocation0).Symbol.ToTestDisplayString(includeNonNullable: true)); var invocation1 = GetSyntax<InvocationExpressionSyntax>(tree, "M((string?)null, [null])"); Assert.Equal("void M<System.String?>(System.String? t, MyCollection<System.String?> mc)", model.GetSymbolInfo(invocation1).Symbol.ToTestDisplayString(includeNonNullable: true)); } [Fact] public void ElementNullability_Inference_CollectionBuilderCollection_NullableValueType() { string src = """ using System; using System.Collections; using System.Collections.Generic; using System.Runtime.CompilerServices; #nullable enable M("hi", [null]); M((string?)null, [null]); M((string?)null, null); void M<T>(T t, MyCollection<T>? mc) { } [CollectionBuilder(typeof(MyCollectionBuilder), nameof(MyCollectionBuilder.Create))] public struct MyCollection<T> : IEnumerable<T> { private readonly List<T> _list; public MyCollection(List<T> list) { _list = list; } public IEnumerator<T> GetEnumerator() => _list.GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); } public class MyCollectionBuilder { public static MyCollection<T> Create<T>(ReadOnlySpan<T> items) { return new MyCollection<T>(new List<T>(items.ToArray())); } } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net80).VerifyEmitDiagnostics(); var tree = comp.SyntaxTrees.Single(); var model = comp.GetSemanticModel(tree); var invocation0 = GetSyntax<InvocationExpressionSyntax>(tree, """M("hi", [null])"""); Assert.Equal("void M<System.String?>(System.String? t, MyCollection<System.String?>? mc)", model.GetSymbolInfo(invocation0).Symbol.ToTestDisplayString(includeNonNullable: true)); var invocation1 = GetSyntax<InvocationExpressionSyntax>(tree, "M((string?)null, [null])"); Assert.Equal("void M<System.String?>(System.String? t, MyCollection<System.String?>? mc)", model.GetSymbolInfo(invocation1).Symbol.ToTestDisplayString(includeNonNullable: true)); var invocation2 = GetSyntax<InvocationExpressionSyntax>(tree, "M((string?)null, null)"); Assert.Equal("void M<System.String?>(System.String? t, MyCollection<System.String?>? mc)", model.GetSymbolInfo(invocation2).Symbol.ToTestDisplayString(includeNonNullable: true)); } [Fact] public void ElementNullability_CollectionBuilderCollection_InInference_WithExactBounds() { string src = """ using System; using System.Collections; using System.Collections.Generic; using System.Runtime.CompilerServices; #nullable enable string notNull = "hi"; M(ref notNull, [null]); // 1 string? maybeNull = null; M(ref maybeNull, [null]); void M<T>(ref T t, MyCollection<T> mc) { } [CollectionBuilder(typeof(MyCollectionBuilder), nameof(MyCollectionBuilder.Create))] public struct MyCollection<T> : IEnumerable<T> { private readonly List<T> _list; public MyCollection(List<T> list) { _list = list; } public IEnumerator<T> GetEnumerator() => _list.GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); } public class MyCollectionBuilder { public static MyCollection<T> Create<T>(ReadOnlySpan<T> items) { return new MyCollection<T>(new List<T>(items.ToArray())); } } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net80).VerifyEmitDiagnostics( // (8,7): warning CS8600: Converting null literal or possible null value to non-nullable type. // M(ref notNull, [null]); // 1 Diagnostic(ErrorCode.WRN_ConvertingNullableToNonNullable, "notNull").WithLocation(8, 7) ); var tree = comp.SyntaxTrees.Single(); var model = comp.GetSemanticModel(tree); var invocation0 = GetSyntax<InvocationExpressionSyntax>(tree, "M(ref notNull, [null])"); Assert.Equal("void M<System.String?>(ref System.String? t, MyCollection<System.String?> mc)", model.GetSymbolInfo(invocation0).Symbol.ToTestDisplayString(includeNonNullable: true)); var invocation1 = GetSyntax<InvocationExpressionSyntax>(tree, "M(ref maybeNull, [null])"); Assert.Equal("void M<System.String?>(ref System.String? t, MyCollection<System.String?> mc)", model.GetSymbolInfo(invocation1).Symbol.ToTestDisplayString(includeNonNullable: true)); } [Fact] public void ElementNullability_CollectionBuilderCollection_Var() { string src = """ using System; using System.Collections; using System.Collections.Generic; using System.Runtime.CompilerServices; #nullable enable string notNullString = "hi"; var notNull = M(notNullString); notNull = [null]; // 1 string? maybeNullString = null; var maybeNull = M(maybeNullString); maybeNull = [null]; #nullable disable string obliviousString = null; #nullable enable var oblivious = M(obliviousString); oblivious = [null]; MyCollection<T> M<T>(T t) => throw null!; [CollectionBuilder(typeof(MyCollectionBuilder), nameof(MyCollectionBuilder.Create))] public struct MyCollection<T> : IEnumerable<T> { private readonly List<T> _list; public MyCollection(List<T> list) { _list = list; } public IEnumerator<T> GetEnumerator() => _list.GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); } public class MyCollectionBuilder { public static MyCollection<T> Create<T>(ReadOnlySpan<T> items) { return new MyCollection<T>(new List<T>(items.ToArray())); } } """; CreateCompilation(src, targetFramework: TargetFramework.Net80).VerifyEmitDiagnostics( // (9,12): warning CS8625: Cannot convert null literal to non-nullable reference type. // notNull = [null]; // 1 Diagnostic(ErrorCode.WRN_NullAsNonNullable, "null").WithLocation(9, 12) ); } [Fact] public void ElementNullability_Inference_CollectionBuilderCollection_SingleParameter() { string src = """ using System; using System.Collections; using System.Collections.Generic; using System.Runtime.CompilerServices; #nullable enable M([(string?)null]); M(["hi"]); M(["hi", null]); void M<T>(MyCollection<T> mc) { } [CollectionBuilder(typeof(MyCollectionBuilder), nameof(MyCollectionBuilder.Create))] public struct MyCollection<T> : IEnumerable<T> { private readonly List<T> _list; public MyCollection(List<T> list) { _list = list; } public IEnumerator<T> GetEnumerator() => _list.GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); } public class MyCollectionBuilder { public static MyCollection<T> Create<T>(ReadOnlySpan<T> items) { return new MyCollection<T>(new List<T>(items.ToArray())); } } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net80).VerifyEmitDiagnostics(); var tree = comp.SyntaxTrees.Single(); var model = comp.GetSemanticModel(tree); var invocation0 = GetSyntax<InvocationExpressionSyntax>(tree, "M([(string?)null])"); Assert.Equal("void M<System.String?>(MyCollection<System.String?> mc)", model.GetSymbolInfo(invocation0).Symbol.ToTestDisplayString(includeNonNullable: true)); var invocation1 = GetSyntax<InvocationExpressionSyntax>(tree, """M(["hi"])"""); Assert.Equal("void M<System.String!>(MyCollection<System.String!> mc)", model.GetSymbolInfo(invocation1).Symbol.ToTestDisplayString(includeNonNullable: true)); var invocation2 = GetSyntax<InvocationExpressionSyntax>(tree, """M(["hi", null])"""); Assert.Equal("void M<System.String?>(MyCollection<System.String?> mc)", model.GetSymbolInfo(invocation2).Symbol.ToTestDisplayString(includeNonNullable: true)); } [Fact] public void ElementNullability_CollectionBuilderCollection_NullReturningCreate() { string src = """ using System; using System.Collections; using System.Collections.Generic; using System.Runtime.CompilerServices; #nullable enable MyCollection<string?> x1 = [null]; MyCollection<string> x2 = [null]; #nullable disable MyCollection<string> #nullable enable x3 = [null]; [CollectionBuilder(typeof(MyCollectionBuilder), nameof(MyCollectionBuilder.Create))] public class MyCollection<T> : IEnumerable<T> { private readonly List<T> _list; public MyCollection(List<T> list) { _list = list; } public IEnumerator<T> GetEnumerator() => _list.GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); } public class MyCollectionBuilder { public static MyCollection<T>? Create<T>(ReadOnlySpan<T> items) { return new MyCollection<T>(new List<T>(items.ToArray()!)); } } """; CreateCompilation(src, targetFramework: TargetFramework.Net80).VerifyEmitDiagnostics( // (7,28): warning CS8600: Converting null literal or possible null value to non-nullable type. // MyCollection<string?> x1 = [null]; Diagnostic(ErrorCode.WRN_ConvertingNullableToNonNullable, "[null]").WithLocation(7, 28), // (8,27): warning CS8600: Converting null literal or possible null value to non-nullable type. // MyCollection<string> x2 = [null]; Diagnostic(ErrorCode.WRN_ConvertingNullableToNonNullable, "[null]").WithLocation(8, 27), // (8,28): warning CS8625: Cannot convert null literal to non-nullable reference type. // MyCollection<string> x2 = [null]; Diagnostic(ErrorCode.WRN_NullAsNonNullable, "null").WithLocation(8, 28) ); } [Fact] public void ElementNullability_Inference_CollectionBuilderCollection_NullReturningCreate() { string src = """ using System; using System.Collections; using System.Collections.Generic; using System.Runtime.CompilerServices; #nullable enable string? maybeNull = null; string notNull = ""; M(ref maybeNull, [maybeNull]); // 1 M(ref notNull, [maybeNull]); // 2 M(ref maybeNull, [notNull]); // 3 void M<T>(ref T t, MyCollection<T> c) { } [CollectionBuilder(typeof(MyCollectionBuilder), nameof(MyCollectionBuilder.Create))] public class MyCollection<T> : IEnumerable<T> { private readonly List<T> _list; public MyCollection(List<T> list) { _list = list; } public IEnumerator<T> GetEnumerator() => _list.GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); } public class MyCollectionBuilder { public static MyCollection<T>? Create<T>(ReadOnlySpan<T> items) { return new MyCollection<T>(new List<T>(items.ToArray()!)); } } """; CreateCompilation(src, targetFramework: TargetFramework.Net80).VerifyEmitDiagnostics( // (10,18): warning CS8604: Possible null reference argument for parameter 'c' in 'void M<string?>(ref string? t, MyCollection<string?> c)'. // M(ref maybeNull, [maybeNull]); // 1 Diagnostic(ErrorCode.WRN_NullReferenceArgument, "[maybeNull]").WithArguments("c", "void M<string?>(ref string? t, MyCollection<string?> c)").WithLocation(10, 18), // (11,7): warning CS8600: Converting null literal or possible null value to non-nullable type. // M(ref notNull, [maybeNull]); // 2 Diagnostic(ErrorCode.WRN_ConvertingNullableToNonNullable, "notNull").WithLocation(11, 7), // (11,16): warning CS8604: Possible null reference argument for parameter 'c' in 'void M<string?>(ref string? t, MyCollection<string?> c)'. // M(ref notNull, [maybeNull]); // 2 Diagnostic(ErrorCode.WRN_NullReferenceArgument, "[maybeNull]").WithArguments("c", "void M<string?>(ref string? t, MyCollection<string?> c)").WithLocation(11, 16), // (12,18): warning CS8604: Possible null reference argument for parameter 'c' in 'void M<string?>(ref string? t, MyCollection<string?> c)'. // M(ref maybeNull, [notNull]); // 3 Diagnostic(ErrorCode.WRN_NullReferenceArgument, "[notNull]").WithArguments("c", "void M<string?>(ref string? t, MyCollection<string?> c)").WithLocation(12, 18) ); } [Fact] public void ElementNullability_Inference_CollectionBuilderCollection_ConstrainedCreate() { string src = """ using System; using System.Collections; using System.Collections.Generic; using System.Runtime.CompilerServices; #nullable enable string? maybeNull = null; M(ref maybeNull, [maybeNull]).ToString(); T M<T>(ref T t, MyCollection<T> c) => throw null!; [CollectionBuilder(typeof(MyCollectionBuilder), nameof(MyCollectionBuilder.Create))] public class MyCollection<T> : IEnumerable<T> { private readonly List<T> _list; public MyCollection(List<T> list) { _list = list; } public IEnumerator<T> GetEnumerator() => _list.GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); } public class MyCollectionBuilder { public static MyCollection<T> Create<T>(ReadOnlySpan<T> items) where T : notnull { return new MyCollection<T>(new List<T>(items.ToArray()!)); } } """; CreateCompilation(src, targetFramework: TargetFramework.Net80).VerifyEmitDiagnostics( // (9,1): warning CS8602: Dereference of a possibly null reference. // M(ref maybeNull, [maybeNull]).ToString(); Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "M(ref maybeNull, [maybeNull])").WithLocation(9, 1), // (9,18): warning CS8714: The type 'string?' cannot be used as type parameter 'T' in the generic type or method 'MyCollectionBuilder.Create<T>(ReadOnlySpan<T>)'. Nullability of type argument 'string?' doesn't match 'notnull' constraint. // M(ref maybeNull, [maybeNull]).ToString(); Diagnostic(ErrorCode.WRN_NullabilityMismatchInTypeParameterNotNullConstraint, "[maybeNull]").WithArguments("MyCollectionBuilder.Create<T>(System.ReadOnlySpan<T>)", "T", "string?").WithLocation(9, 18)); } [Fact] public void ElementNullability_Inference_CollectionBuilderCollection_ConstrainedCreate_NullableValueType() { string src = """ using System; using System.Collections; using System.Collections.Generic; using System.Runtime.CompilerServices; #nullable enable string? maybeNull = null; M(ref maybeNull, [maybeNull]).ToString(); T M<T>(ref T t, MyCollection<T>? c) => throw null!; [CollectionBuilder(typeof(MyCollectionBuilder), nameof(MyCollectionBuilder.Create))] public class MyCollection<T> : IEnumerable<T> { private readonly List<T> _list; public MyCollection(List<T> list) { _list = list; } public IEnumerator<T> GetEnumerator() => _list.GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); } public class MyCollectionBuilder { public static MyCollection<T> Create<T>(ReadOnlySpan<T> items) where T : notnull { return new MyCollection<T>(new List<T>(items.ToArray()!)); } } """; CreateCompilation(src, targetFramework: TargetFramework.Net80).VerifyEmitDiagnostics( // (9,1): warning CS8602: Dereference of a possibly null reference. // M(ref maybeNull, [maybeNull]).ToString(); Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "M(ref maybeNull, [maybeNull])").WithLocation(9, 1), // (9,18): warning CS8714: The type 'string?' cannot be used as type parameter 'T' in the generic type or method 'MyCollectionBuilder.Create<T>(ReadOnlySpan<T>)'. Nullability of type argument 'string?' doesn't match 'notnull' constraint. // M(ref maybeNull, [maybeNull]).ToString(); Diagnostic(ErrorCode.WRN_NullabilityMismatchInTypeParameterNotNullConstraint, "[maybeNull]").WithArguments("MyCollectionBuilder.Create<T>(System.ReadOnlySpan<T>)", "T", "string?").WithLocation(9, 18)); } [Fact] public void ElementNullability_CollectionBuilderCollection_NullReturningCreate_WithAttribute() { string src = """ using System; using System.Collections; using System.Collections.Generic; using System.Runtime.CompilerServices; #nullable enable MyCollection<string?> x1 = [null]; MyCollection<string> x2 = [null]; #nullable disable MyCollection<string> #nullable enable x3 = [null]; [CollectionBuilder(typeof(MyCollectionBuilder), nameof(MyCollectionBuilder.Create))] public class MyCollection<T> : IEnumerable<T> { private readonly List<T> _list; public MyCollection(List<T> list) { _list = list; } public IEnumerator<T> GetEnumerator() => _list.GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); } public class MyCollectionBuilder { [return: System.Diagnostics.CodeAnalysis.NotNull] public static MyCollection<T>? Create<T>(ReadOnlySpan<T> items) { return new MyCollection<T>(new List<T>(items.ToArray()!)); } } """; CreateCompilation(src, targetFramework: TargetFramework.Net80).VerifyEmitDiagnostics( // (8,28): warning CS8625: Cannot convert null literal to non-nullable reference type. // MyCollection<string> x2 = [null]; Diagnostic(ErrorCode.WRN_NullAsNonNullable, "null").WithLocation(8, 28) ); } [Fact] public void ElementNullability_IEnumerableCollection() { string src = """ using System.Collections; #nullable enable CNotNull x1 = [null]; CNullable x2 = [null]; COblivious x3 = [null]; class CNotNull : IEnumerable { public CNotNull() { } IEnumerator IEnumerable.GetEnumerator() => throw null!; public void Add(object o) { } } class CNullable : IEnumerable { public CNullable() { } IEnumerator IEnumerable.GetEnumerator() => throw null!; public void Add(object? o) { } } class COblivious : IEnumerable { public COblivious() { } IEnumerator IEnumerable.GetEnumerator() => throw null!; #nullable disable public void Add(object o) { } } """; CreateCompilation(src).VerifyEmitDiagnostics( // (5,16): warning CS8625: Cannot convert null literal to non-nullable reference type. // CNotNull x1 = [null]; Diagnostic(ErrorCode.WRN_NullAsNonNullable, "null").WithLocation(5, 16)); } [Fact] public void ElementNullability_Inference_IEnumerableCollection_AnalyzeAddMethods() { string source = """ using System.Collections; #nullable enable object? maybeNull = null; object? notNull = new object(); M(CreateAnnotated(maybeNull), [maybeNull]); M(CreateAnnotated(maybeNull), [notNull]); M(CreateAnnotated(notNull), [maybeNull]); M(CreateAnnotated(notNull), [notNull]); M(CreateUnannotated(maybeNull), [maybeNull]); M(CreateUnannotated(maybeNull), [notNull]); M(CreateUnannotated(notNull), [maybeNull]); // 1 M(CreateUnannotated(notNull), [notNull]); void M<T>(T t1, T t2) { } CAnnotated<T> CreateAnnotated<T>(T t) => throw null!; CUnannotated<T> CreateUnannotated<T>(T t) => throw null!; class CAnnotated<T> : IEnumerable { IEnumerator IEnumerable.GetEnumerator() => throw null!; public void Add(T? t) { } } class CUnannotated<T> : IEnumerable { IEnumerator IEnumerable.GetEnumerator() => throw null!; public void Add(T t) { } } """; CreateCompilation(source).VerifyEmitDiagnostics( // (17,32): warning CS8604: Possible null reference argument for parameter 't' in 'void CUnannotated<object>.Add(object t)'. // M(CreateUnannotated(notNull), [maybeNull]); Diagnostic(ErrorCode.WRN_NullReferenceArgument, "maybeNull").WithArguments("t", "void CUnannotated<object>.Add(object t)").WithLocation(17, 32)); } [Fact] public void ElementNullability_Inference_IEnumerableCollection_AnalyzeAddMethods_Constraints() { string source = """ using System.Collections; #nullable enable object? maybeNull = null; object? notNull = new object(); M(new C(), [maybeNull]); C c1 = [maybeNull]; M(new C(), [notNull]); C c2 = [notNull]; void M<T>(T t1, T t2) { } class C : IEnumerable { IEnumerator IEnumerable.GetEnumerator() => throw null!; public void Add<T>(T t) where T : notnull { } } """; CreateCompilation(source).VerifyEmitDiagnostics( // (8,13): warning CS8604: Possible null reference argument for parameter 't' in 'void C.Add<object>(object t)'. // M(new C(), [maybeNull]); Diagnostic(ErrorCode.WRN_NullReferenceArgument, "maybeNull").WithArguments("t", "void C.Add<object>(object t)").WithLocation(8, 13)); } [Fact] public void ElementNullability_Inference_IEnumerableTCollection_AnalyzeAddMethods() { string source = """ using System.Collections.Generic; #nullable enable object? maybeNull = null; object? notNull = new object(); M(CreateAnnotated(maybeNull), [maybeNull]); M(CreateAnnotated(maybeNull), [notNull]); M(CreateAnnotated(notNull), [maybeNull]); M(CreateAnnotated(notNull), [notNull]); M(CreateUnannotated(maybeNull), [maybeNull]); M(CreateUnannotated(maybeNull), [notNull]); M(CreateUnannotated(notNull), [maybeNull]); // 1 M(CreateUnannotated(notNull), [notNull]); void M<T>(T t1, T t2) { } CAnnotated<T> CreateAnnotated<T>(T t) => throw null!; CUnannotated<T> CreateUnannotated<T>(T t) => throw null!; class CAnnotated<T> : IEnumerable<T> { System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() => throw null!; IEnumerator<T> IEnumerable<T>.GetEnumerator() => throw null!; public void Add(T? t) { } } class CUnannotated<T> : IEnumerable<T> { System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() => throw null!; IEnumerator<T> IEnumerable<T>.GetEnumerator() => throw null!; public void Add(T t) { } } """; CreateCompilation(source).VerifyEmitDiagnostics( // (17,32): warning CS8604: Possible null reference argument for parameter 't' in 'void CUnannotated<object>.Add(object t)'. // M(CreateUnannotated(notNull), [maybeNull]); // 1 Diagnostic(ErrorCode.WRN_NullReferenceArgument, "maybeNull").WithArguments("t", "void CUnannotated<object>.Add(object t)").WithLocation(17, 32)); } [Fact] public void ElementNullability_Inference_IEnumerableTCollection_AnalyzeAddMethods_Constraints() { string source = """ using System.Collections.Generic; #nullable enable object? maybeNull = null; object? notNull = new object(); M(new C(), [maybeNull]); // 1 C c1 = [maybeNull]; M(new C(), [notNull]); C c2 = [notNull]; void M<T>(T t1, T t2) { } class C : IEnumerable<object?> { System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() => throw null!; IEnumerator<object?> IEnumerable<object?>.GetEnumerator() => throw null!; public void Add<T>(T t) where T : notnull { } } """; CreateCompilation(source).VerifyEmitDiagnostics( // (8,13): warning CS8604: Possible null reference argument for parameter 't' in 'void C.Add<object>(object t)'. // M(new C(), [maybeNull]); // 1 Diagnostic(ErrorCode.WRN_NullReferenceArgument, "maybeNull").WithArguments("t", "void C.Add<object>(object t)").WithLocation(8, 13)); } [Fact] public void ElementNullability_IEnumerableTCollection_AnalyzeAddMethods() { string source = """ using System.Collections.Generic; #nullable enable object? maybeNull = null; object notNull = new object(); CAnnotated<object> c1 = [maybeNull]; CAnnotated<object?> c2 = [maybeNull]; CAnnotated<object> c3 = [notNull]; CAnnotated<object?> c4 = [notNull]; CUnannotated<object> c5 = [maybeNull]; // 1 CUnannotated<object?> c6 = [maybeNull]; CUnannotated<object> c7 = [notNull]; CUnannotated<object?> c8 = [notNull]; class CAnnotated<T> : IEnumerable<T> { System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() => throw null!; IEnumerator<T> IEnumerable<T>.GetEnumerator() => throw null!; public void Add(T? t) { } } class CUnannotated<T> : IEnumerable<T> { System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() => throw null!; IEnumerator<T> IEnumerable<T>.GetEnumerator() => throw null!; public void Add(T t) { } } """; CreateCompilation(source).VerifyEmitDiagnostics( // (14,28): warning CS8604: Possible null reference argument for parameter 't' in 'void CUnannotated<object>.Add(object t)'. // CUnannotated<object> c5 = [maybeNull]; // 1 Diagnostic(ErrorCode.WRN_NullReferenceArgument, "maybeNull").WithArguments("t", "void CUnannotated<object>.Add(object t)").WithLocation(14, 28)); } [Fact] public void ElementNullability_Inference_IEnumerableTCollection_AnalyzeAddMethods_GenericWithNotNullConstraint() { string source = """ using System.Collections.Generic; #nullable enable object? maybeNull = null; object? notNull = new object(); M(CreateAnnotated(maybeNull), [maybeNull]); M(CreateAnnotated(maybeNull), [notNull]); M(CreateAnnotated(notNull), [maybeNull]); M(CreateAnnotated(notNull), [notNull]); M(CreateUnannotated(maybeNull), [maybeNull]); // 1 M(CreateUnannotated(maybeNull), [notNull]); M(CreateUnannotated(notNull), [maybeNull]); // 1 M(CreateUnannotated(notNull), [notNull]); void M<T>(T t1, T t2) { } CAnnotated<T> CreateAnnotated<T>(T t) => throw null!; CUnannotated<T> CreateUnannotated<T>(T t) => throw null!; class CAnnotated<T> : IEnumerable<T> { System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() => throw null!; IEnumerator<T> IEnumerable<T>.GetEnumerator() => throw null!; public void Add(T? t) { } } class CUnannotated<T> : IEnumerable<T> { System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() => throw null!; IEnumerator<T> IEnumerable<T>.GetEnumerator() => throw null!; public void Add<U>(U u) where U : notnull { } } """; CreateCompilation(source).VerifyEmitDiagnostics( // (14,34): warning CS8604: Possible null reference argument for parameter 'u' in 'void CUnannotated<object?>.Add<object>(object u)'. // M(CreateUnannotated(maybeNull), [maybeNull]); Diagnostic(ErrorCode.WRN_NullReferenceArgument, "maybeNull").WithArguments("u", "void CUnannotated<object?>.Add<object>(object u)").WithLocation(14, 34)); } [Fact] public void ElementNullability_InterfaceTypeCollection() { string src = """ using System.Collections.Generic; #nullable enable IEnumerable<string?> x1 = [null]; IEnumerable<string> x2 = [null]; #nullable disable IEnumerable<string> #nullable enable x3 = [null]; """; CreateCompilation(src, targetFramework: TargetFramework.Net80).VerifyEmitDiagnostics( // (5,27): warning CS8625: Cannot convert null literal to non-nullable reference type. // IEnumerable<string> x2 = [null]; Diagnostic(ErrorCode.WRN_NullAsNonNullable, "null").WithLocation(5, 27) ); } [Fact] public void ElementNullability_NoConversion() { string src = """ #nullable enable int[] x = [null]; int y = null; """; CreateCompilation(src).VerifyEmitDiagnostics( // (2,12): error CS0037: Cannot convert null to 'int' because it is a non-nullable value type // int[] x = [null]; Diagnostic(ErrorCode.ERR_ValueCantBeNull, "null").WithArguments("int").WithLocation(2, 12), // (3,9): error CS0037: Cannot convert null to 'int' because it is a non-nullable value type // int y = null; Diagnostic(ErrorCode.ERR_ValueCantBeNull, "null").WithArguments("int").WithLocation(3, 9) ); } [Fact] public void ElementNullability_CheckExpressions() { string src = """ #nullable enable object? o = null; string[] x = [o.ToString()]; """; CreateCompilation(src).VerifyEmitDiagnostics( // (3,15): warning CS8602: Dereference of a possibly null reference. // string[] x = [o.ToString()]; Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "o").WithLocation(3, 15) ); } [Fact] public void ElementNullability_Inference_MaybeNullElement() { string src = """ using System.Collections.Generic; #nullable enable string element = null; // 1 var collection = IdList([element]); collection[0].ToString(); // 2 List<T> IdList<T>(List<T> l) => l; """; var comp = CreateCompilation(src).VerifyEmitDiagnostics( // (4,18): warning CS8600: Converting null literal or possible null value to non-nullable type. // string element = null; // 1 Diagnostic(ErrorCode.WRN_ConvertingNullableToNonNullable, "null").WithLocation(4, 18), // (6,1): warning CS8602: Dereference of a possibly null reference. // collection[0].ToString(); // 2 Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "collection[0]").WithLocation(6, 1) ); var tree = comp.SyntaxTrees.First(); var invocation = GetSyntax<InvocationExpressionSyntax>(tree, "IdList([element])"); var model = comp.GetSemanticModel(tree); Assert.Equal("System.Collections.Generic.List<System.String?> IdList<System.String?>(System.Collections.Generic.List<System.String?> l)", model.GetSymbolInfo(invocation).Symbol.ToTestDisplayString()); var collection = tree.GetRoot().DescendantNodes().OfType<CollectionExpressionSyntax>().Single(); Assert.Equal("[element]", collection.ToFullString()); var collectionConversion = model.GetConversion(collection); Assert.True(collectionConversion.IsValid); Assert.True(collectionConversion.IsCollectionExpression); var element = collection.Elements.Single(); Assert.Equal("element", element.ToFullString()); var elementConversion = model.GetConversion(element); Assert.True(elementConversion.IsValid); Assert.True(elementConversion.IsIdentity); } [Fact] public void ElementNullability_Inference_NotNullElement() { string src = """ using System.Collections.Generic; #nullable enable string? element = ""; var collection = IdList([element]); collection[0].ToString(); List<T> IdList<T>(List<T> l) => l; """; var comp = CreateCompilation(src).VerifyEmitDiagnostics(); var tree = comp.SyntaxTrees.First(); var invocation = GetSyntax<InvocationExpressionSyntax>(tree, "IdList([element])"); var model = comp.GetSemanticModel(tree); Assert.Equal("System.Collections.Generic.List<System.String> IdList<System.String>(System.Collections.Generic.List<System.String> l)", model.GetSymbolInfo(invocation).Symbol.ToTestDisplayString()); } [Fact] public void ElementNullability_Inference_TwoElements() { string src = """ using System.Collections.Generic; #nullable enable string element1 = null; // 1 string element2 = "hi"; var collection = IdList([element1, element2]); collection[0].ToString(); // 2 List<T> IdList<T>(List<T> l) => l; """; var comp = CreateCompilation(src).VerifyEmitDiagnostics( // (4,19): warning CS8600: Converting null literal or possible null value to non-nullable type. // string element1 = null; // 1 Diagnostic(ErrorCode.WRN_ConvertingNullableToNonNullable, "null").WithLocation(4, 19), // (7,1): warning CS8602: Dereference of a possibly null reference. // collection[0].ToString(); // 2 Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "collection[0]").WithLocation(7, 1) ); var tree = comp.SyntaxTrees.First(); var invocation = GetSyntax<InvocationExpressionSyntax>(tree, "IdList([element1, element2])"); var model = comp.GetSemanticModel(tree); Assert.Equal("System.Collections.Generic.List<System.String?> IdList<System.String?>(System.Collections.Generic.List<System.String?> l)", model.GetSymbolInfo(invocation).Symbol.ToTestDisplayString()); } [Fact] public void ElementNullability_Inference_TwoElements_DifferentTypes() { string src = """ using System.Collections.Generic; #nullable enable string? element1 = null; object element2 = "hi"; var collection = IdList([element1, element2]); collection[0].ToString(); // 1 List<T> IdList<T>(List<T> l) => l; """; var comp = CreateCompilation(src).VerifyEmitDiagnostics( // (7,1): warning CS8602: Dereference of a possibly null reference. // collection[0].ToString(); // 1 Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "collection[0]").WithLocation(7, 1) ); var tree = comp.SyntaxTrees.First(); var invocation = GetSyntax<InvocationExpressionSyntax>(tree, "IdList([element1, element2])"); var model = comp.GetSemanticModel(tree); Assert.Equal("System.Collections.Generic.List<System.Object?> IdList<System.Object?>(System.Collections.Generic.List<System.Object?> l)", model.GetSymbolInfo(invocation).Symbol.ToTestDisplayString()); var collection = tree.GetRoot().DescendantNodes().OfType<CollectionExpressionSyntax>().Single(); var element1 = collection.Elements.First(); Assert.Equal("element1", element1.ToFullString()); var elementConversion1 = model.GetConversion(element1); Assert.True(elementConversion1.IsValid); Assert.True(elementConversion1.IsIdentity); var element2 = collection.Elements.Last(); Assert.Equal("element2", element2.ToFullString()); var elementConversion2 = model.GetConversion(element2); Assert.True(elementConversion2.IsValid); Assert.True(elementConversion2.IsIdentity); } [Fact] public void ElementNullability_Inference_TwoElements_DifferentTypes_ExplicitConversions() { string src = """ using System.Collections.Generic; #nullable enable object? element1 = (string?)null; object element2 = "hi"; var collection = IdList([(string?)element1, element2]); collection[0].ToString(); // 1 List<T> IdList<T>(List<T> l) => l; """; var comp = CreateCompilation(src).VerifyEmitDiagnostics( // (7,1): warning CS8602: Dereference of a possibly null reference. // collection[0].ToString(); // 1 Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "collection[0]").WithLocation(7, 1) ); var tree = comp.SyntaxTrees.First(); var invocation = GetSyntax<InvocationExpressionSyntax>(tree, "IdList([(string?)element1, element2])"); var model = comp.GetSemanticModel(tree); Assert.Equal("System.Collections.Generic.List<System.Object?> IdList<System.Object?>(System.Collections.Generic.List<System.Object?> l)", model.GetSymbolInfo(invocation).Symbol.ToTestDisplayString()); var collection = tree.GetRoot().DescendantNodes().OfType<CollectionExpressionSyntax>().Single(); var element1 = collection.Elements.First(); Assert.Equal("(string?)element1", element1.ToFullString()); var elementConversion1 = model.GetConversion(element1); Assert.True(elementConversion1.IsValid); Assert.True(elementConversion1.IsIdentity); var element2 = collection.Elements.Last(); Assert.Equal("element2", element2.ToFullString()); var elementConversion2 = model.GetConversion(element2); Assert.True(elementConversion2.IsValid); Assert.True(elementConversion2.IsIdentity); } [Fact] public void ElementNullability_Inference_TwoElements_DifferentGenericTypes() { string src = """ using System.Collections.Generic; #nullable enable var element1 = new Container<string?>(); var element2 = new Container<string>(); var collection = IdList([element1, element2]); // 1 collection[0].Element.ToString(); List<T> IdList<T>(List<T> l) => l; public class Container<T> { public T Element = default!; } """; var comp = CreateCompilation(src).VerifyEmitDiagnostics( // (6,26): warning CS8620: Argument of type 'Container<string?>' cannot be used for parameter 'item' of type 'Container<string>' in 'void List<Container<string>>.Add(Container<string> item)' due to differences in the nullability of reference types. // var collection = IdList([element1, element2]); // 1 Diagnostic(ErrorCode.WRN_NullabilityMismatchInArgument, "element1").WithArguments("Container<string?>", "Container<string>", "item", "void List<Container<string>>.Add(Container<string> item)").WithLocation(6, 26)); var tree = comp.SyntaxTrees.First(); var invocation = GetSyntax<InvocationExpressionSyntax>(tree, "IdList([element1, element2])"); var model = comp.GetSemanticModel(tree); Assert.Equal("System.Collections.Generic.List<Container<System.String>> IdList<Container<System.String>>(System.Collections.Generic.List<Container<System.String>> l)", model.GetSymbolInfo(invocation).Symbol.ToTestDisplayString()); var collection = tree.GetRoot().DescendantNodes().OfType<CollectionExpressionSyntax>().Single(); var element1 = collection.Elements.First(); Assert.Equal("element1", element1.ToFullString()); var elementConversion1 = model.GetConversion(element1); Assert.True(elementConversion1.IsValid); Assert.True(elementConversion1.IsIdentity); var element2 = collection.Elements.Last(); Assert.Equal("element2", element2.ToFullString()); var elementConversion2 = model.GetConversion(element2); Assert.True(elementConversion2.IsValid); Assert.True(elementConversion2.IsIdentity); } [Fact] public void ElementNullability_Inference_ArrayCollection() { string src = """ #nullable enable public class C { void M1(string notNull) { M(ref notNull, [null]); // 1 } void M2(string? maybeNull, string notNull) { M(ref notNull, [maybeNull]); // 2 } void M3(string? maybeNull, string notNull) { M(ref notNull, [notNull, ""]); } void M4(string? maybeNull, string notNull) { M(ref notNull, [maybeNull, ""]); // 3 } void M5(string? maybeNull, string notNull) { M(ref maybeNull, [notNull, maybeNull, ""]); } void M<T>(ref T t, T[] a) { } } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net80).VerifyEmitDiagnostics( // (6,15): warning CS8600: Converting null literal or possible null value to non-nullable type. // M(ref notNull, [null]); // 1 Diagnostic(ErrorCode.WRN_ConvertingNullableToNonNullable, "notNull").WithLocation(6, 15), // (10,15): warning CS8600: Converting null literal or possible null value to non-nullable type. // M(ref notNull, [maybeNull]); // 2 Diagnostic(ErrorCode.WRN_ConvertingNullableToNonNullable, "notNull").WithLocation(10, 15), // (18,15): warning CS8600: Converting null literal or possible null value to non-nullable type. // M(ref notNull, [maybeNull, ""]); // 3 Diagnostic(ErrorCode.WRN_ConvertingNullableToNonNullable, "notNull").WithLocation(18, 15) ); var tree = comp.SyntaxTrees.First(); var model = comp.GetSemanticModel(tree); var invocation0 = GetSyntax<InvocationExpressionSyntax>(tree, "M(ref notNull, [null])"); Assert.Equal("void C.M<System.String?>(ref System.String? t, System.String?[]! a)", model.GetSymbolInfo(invocation0).Symbol.ToTestDisplayString(includeNonNullable: true)); var invocation1 = GetSyntax<InvocationExpressionSyntax>(tree, "M(ref notNull, [maybeNull])"); Assert.Equal("void C.M<System.String?>(ref System.String? t, System.String?[]! a)", model.GetSymbolInfo(invocation1).Symbol.ToTestDisplayString(includeNonNullable: true)); var invocation2 = GetSyntax<InvocationExpressionSyntax>(tree, """M(ref notNull, [notNull, ""])"""); Assert.Equal("void C.M<System.String!>(ref System.String! t, System.String![]! a)", model.GetSymbolInfo(invocation2).Symbol.ToTestDisplayString(includeNonNullable: true)); var invocation3 = GetSyntax<InvocationExpressionSyntax>(tree, """M(ref notNull, [maybeNull, ""])"""); Assert.Equal("void C.M<System.String?>(ref System.String? t, System.String?[]! a)", model.GetSymbolInfo(invocation3).Symbol.ToTestDisplayString(includeNonNullable: true)); var invocation4 = GetSyntax<InvocationExpressionSyntax>(tree, """M(ref maybeNull, [notNull, maybeNull, ""])"""); Assert.Equal("void C.M<System.String?>(ref System.String? t, System.String?[]! a)", model.GetSymbolInfo(invocation4).Symbol.ToTestDisplayString(includeNonNullable: true)); } [Fact] public void ElementNullability_Inference_ArrayCollection_ExplicitCast() { string src = """ #nullable enable string element1 = null; // 1 string element2 = "hi"; var collection = IdList((string[])[element1, element2]); // 2 collection[0].ToString(); var collection2 = IdList([element1, element2]); collection2[0].ToString(); // 3 T[] IdList<T>(T[] l) => l; """; CreateCompilation(src).VerifyEmitDiagnostics( // (2,19): warning CS8600: Converting null literal or possible null value to non-nullable type. // string element1 = null; // 1 Diagnostic(ErrorCode.WRN_ConvertingNullableToNonNullable, "null").WithLocation(2, 19), // (4,36): warning CS8601: Possible null reference assignment. // var collection = IdList((string[])[element1, element2]); // 2 Diagnostic(ErrorCode.WRN_NullReferenceAssignment, "element1").WithLocation(4, 36), // (8,1): warning CS8602: Dereference of a possibly null reference. // collection2[0].ToString(); // 3 Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "collection2[0]").WithLocation(8, 1) ); } [Fact] public void ElementNullability_Inference_ArrayCollection_Nested() { string src = """ #nullable enable public class C { void M1(string notNull) { M(ref notNull, [[null]]); // 1 } void M2(string? maybeNull, string notNull) { M(ref notNull, [[maybeNull]]); // 2 } void M3(string? maybeNull, string notNull) { M(ref notNull, [[notNull, ""]]); } void M4(string? maybeNull, string notNull) { M(ref notNull, [[maybeNull, ""]]); // 3 } void M5(string? maybeNull, string notNull) { M(ref maybeNull, [[notNull, maybeNull, ""]]); } void M<T>(ref T t, T[][] a) { } } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net80).VerifyEmitDiagnostics( // (6,15): warning CS8600: Converting null literal or possible null value to non-nullable type. // M(ref notNull, [[null]]); // 1 Diagnostic(ErrorCode.WRN_ConvertingNullableToNonNullable, "notNull").WithLocation(6, 15), // (10,15): warning CS8600: Converting null literal or possible null value to non-nullable type. // M(ref notNull, [[maybeNull]]); // 2 Diagnostic(ErrorCode.WRN_ConvertingNullableToNonNullable, "notNull").WithLocation(10, 15), // (18,15): warning CS8600: Converting null literal or possible null value to non-nullable type. // M(ref notNull, [[maybeNull, ""]]); // 3 Diagnostic(ErrorCode.WRN_ConvertingNullableToNonNullable, "notNull").WithLocation(18, 15) ); var tree = comp.SyntaxTrees.First(); var model = comp.GetSemanticModel(tree); var invocation0 = GetSyntax<InvocationExpressionSyntax>(tree, "M(ref notNull, [[null]])"); Assert.Equal("void C.M<System.String?>(ref System.String? t, System.String?[]![]! a)", model.GetSymbolInfo(invocation0).Symbol.ToTestDisplayString(includeNonNullable: true)); var invocation1 = GetSyntax<InvocationExpressionSyntax>(tree, "M(ref notNull, [[maybeNull]])"); Assert.Equal("void C.M<System.String?>(ref System.String? t, System.String?[]![]! a)", model.GetSymbolInfo(invocation1).Symbol.ToTestDisplayString(includeNonNullable: true)); var invocation2 = GetSyntax<InvocationExpressionSyntax>(tree, """M(ref notNull, [[notNull, ""]])"""); Assert.Equal("void C.M<System.String!>(ref System.String! t, System.String![]![]! a)", model.GetSymbolInfo(invocation2).Symbol.ToTestDisplayString(includeNonNullable: true)); var invocation3 = GetSyntax<InvocationExpressionSyntax>(tree, """M(ref notNull, [[maybeNull, ""]])"""); Assert.Equal("void C.M<System.String?>(ref System.String? t, System.String?[]![]! a)", model.GetSymbolInfo(invocation3).Symbol.ToTestDisplayString(includeNonNullable: true)); var invocation4 = GetSyntax<InvocationExpressionSyntax>(tree, """M(ref maybeNull, [[notNull, maybeNull, ""]])"""); Assert.Equal("void C.M<System.String?>(ref System.String? t, System.String?[]![]! a)", model.GetSymbolInfo(invocation4).Symbol.ToTestDisplayString(includeNonNullable: true)); } [Fact] public void ElementNullability_Inference_ArrayCollection_WithNotNullConstraint() { string src = """ #nullable enable public class C { void M1(string notNull) { M(ref notNull, [null]); // 1, 2 } void M2(string? maybeNull, string notNull) { M(ref notNull, [maybeNull]); // 3, 4 } void M3(string notNull) { M(ref notNull, [notNull, ""]); } void M4(string? maybeNull, string notNull) { M(ref maybeNull, [notNull, maybeNull, ""]); // 5 } void M<T>(ref T t, T[] a) where T : notnull { } } """; CreateCompilation(src, targetFramework: TargetFramework.Net80).VerifyEmitDiagnostics( // (6,9): warning CS8714: The type 'string?' cannot be used as type parameter 'T' in the generic type or method 'C.M<T>(ref T, T[])'. Nullability of type argument 'string?' doesn't match 'notnull' constraint. // M(ref notNull, [null]); // 1, 2 Diagnostic(ErrorCode.WRN_NullabilityMismatchInTypeParameterNotNullConstraint, "M").WithArguments("C.M<T>(ref T, T[])", "T", "string?").WithLocation(6, 9), // (6,15): warning CS8600: Converting null literal or possible null value to non-nullable type. // M(ref notNull, [null]); // 1, 2 Diagnostic(ErrorCode.WRN_ConvertingNullableToNonNullable, "notNull").WithLocation(6, 15), // (10,9): warning CS8714: The type 'string?' cannot be used as type parameter 'T' in the generic type or method 'C.M<T>(ref T, T[])'. Nullability of type argument 'string?' doesn't match 'notnull' constraint. // M(ref notNull, [maybeNull]); // 3, 4 Diagnostic(ErrorCode.WRN_NullabilityMismatchInTypeParameterNotNullConstraint, "M").WithArguments("C.M<T>(ref T, T[])", "T", "string?").WithLocation(10, 9), // (10,15): warning CS8600: Converting null literal or possible null value to non-nullable type. // M(ref notNull, [maybeNull]); // 3, 4 Diagnostic(ErrorCode.WRN_ConvertingNullableToNonNullable, "notNull").WithLocation(10, 15), // (18,9): warning CS8714: The type 'string?' cannot be used as type parameter 'T' in the generic type or method 'C.M<T>(ref T, T[])'. Nullability of type argument 'string?' doesn't match 'notnull' constraint. // M(ref maybeNull, [notNull, maybeNull, ""]); // 5 Diagnostic(ErrorCode.WRN_NullabilityMismatchInTypeParameterNotNullConstraint, "M").WithArguments("C.M<T>(ref T, T[])", "T", "string?").WithLocation(18, 9) ); } [Fact] public void ElementNullability_Inference_ArrayCollection_Lambda() { // Analyze captured variables at the location lambda is converted to a delegate string src = """ #nullable enable public class C { void M1(string? s) { Method([() => s]).ToString(); // 1 if (s is null) return; Method([() => s]).ToString(); } void M2(string? s) { Method2(s = "", [() => s]).ToString(); Method2(s = null, [() => s]).ToString(); // 2 } void M3(string? s) { s = ""; Method([Method3(s = null), () => s.ToString()]); // 3 } T Method<T>(System.Func<T>[] a) => throw null!; U Method2<T, U>(T t, System.Func<U>[] a) => throw null!; System.Func<string> Method3<T>(T t) => throw null!; } """; CreateCompilation(src, targetFramework: TargetFramework.Net80).VerifyEmitDiagnostics( // (6,9): warning CS8602: Dereference of a possibly null reference. // Method([() => s]).ToString(); // 1 Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "Method([() => s])").WithLocation(6, 9), // (13,9): warning CS8602: Dereference of a possibly null reference. // Method2(s = null, [() => s]).ToString(); // 2 Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "Method2(s = null, [() => s])").WithLocation(13, 9), // (18,42): warning CS8602: Dereference of a possibly null reference. // Method([Method3(s = null), () => s.ToString()]); // 3 Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "s").WithLocation(18, 42) ); } [Fact] public void ElementNullability_Inference_ArrayCollection_Lambda_WithConditional() { string src = """ #nullable enable public class C { void M1(string? s) { bool b = true; Method([b ? (() => s) : (() => s)]).ToString(); // 1 Method2(b ? (() => s) : (() => s)).ToString(); // 2 } T Method<T>(System.Func<T>[] a) => throw null!; T Method2<T>(System.Func<T> a) => throw null!; } """; CreateCompilation(src, targetFramework: TargetFramework.Net80).VerifyEmitDiagnostics( // (7,9): error CS0411: The type arguments for method 'C.Method<T>(Func<T>[])' cannot be inferred from the usage. Try specifying the type arguments explicitly. // Method([b ? (() => s) : (() => s)]).ToString(); // 1 Diagnostic(ErrorCode.ERR_CantInferMethTypeArgs, "Method").WithArguments("C.Method<T>(System.Func<T>[])").WithLocation(7, 9), // (8,9): error CS0411: The type arguments for method 'C.Method2<T>(Func<T>)' cannot be inferred from the usage. Try specifying the type arguments explicitly. // Method2(b ? (() => s) : (() => s)).ToString(); // 2 Diagnostic(ErrorCode.ERR_CantInferMethTypeArgs, "Method2").WithArguments("C.Method2<T>(System.Func<T>)").WithLocation(8, 9) ); } [Fact] public void ElementNullability_Inference_ArrayCollection_Lambda_WithSwitch() { string src = """ #nullable enable public class C { void M1(string? s) { bool b = true; Method([b switch { true => () => s, _ => () => s }]).ToString(); // 1 Method2(b switch { true => () => s, _ => () => s }).ToString(); // 2 } T Method<T>(System.Func<T>[] a) => throw null!; T Method2<T>(System.Func<T> a) => throw null!; } """; CreateCompilation(src, targetFramework: TargetFramework.Net80).VerifyEmitDiagnostics( // (7,9): error CS0411: The type arguments for method 'C.Method<T>(Func<T>[])' cannot be inferred from the usage. Try specifying the type arguments explicitly. // Method([b switch { true => () => s, _ => () => s }]).ToString(); // 1 Diagnostic(ErrorCode.ERR_CantInferMethTypeArgs, "Method").WithArguments("C.Method<T>(System.Func<T>[])").WithLocation(7, 9), // (8,9): error CS0411: The type arguments for method 'C.Method2<T>(Func<T>)' cannot be inferred from the usage. Try specifying the type arguments explicitly. // Method2(b switch { true => () => s, _ => () => s }).ToString(); // 2 Diagnostic(ErrorCode.ERR_CantInferMethTypeArgs, "Method2").WithArguments("C.Method2<T>(System.Func<T>)").WithLocation(8, 9) ); } [Fact] public void TupleElementNullability_Inference_Lambda() { // Analyze captured variables at the location lambda is converted to a delegate string src = """ #nullable enable public class C { void M1(string? s) { Method((42, () => s)).ToString(); // 1 if (s is null) return; Method((42, () => s)).ToString(); } void M2(string? s) { Method2(s = "", (42, () => s)).ToString(); Method2(s = null, (42, () => s)).ToString(); // 2 } void M3(string? s) { s = ""; Method((Method3(s = null), () => s.ToString())); // 3 Method((Method3(s = ""), () => s.ToString())); } T Method<T>((int, System.Func<T>) a) => throw null!; U Method2<T, U>(T t, (int, System.Func<U>) a) => throw null!; int Method3<T>(T t) => throw null!; } """; // Tuples should be analyzed as-if they were flattened (but they current are not) // Tracked by https://github.com/dotnet/roslyn/issues/71242 CreateCompilation(src, targetFramework: TargetFramework.Net80).VerifyEmitDiagnostics( // (18,42): warning CS8602: Dereference of a possibly null reference. // Method((Method3(s = null), () => s.ToString())); // 3 Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "s").WithLocation(18, 42) ); string src2 = """ #nullable enable public class C { void M1(string? s) { Method(42, () => s).ToString(); // 1 if (s is null) return; Method(42, () => s).ToString(); } void M2(string? s) { Method2(s = "", 42, () => s).ToString(); Method2(s = null, 42, () => s).ToString(); // 2 } void M3(string? s) { s = ""; Method(Method3(s = null), () => s.ToString()); // 3 Method(Method3(s = ""), () => s.ToString()); } T Method<T>(int a, System.Func<T> b) => throw null!; U Method2<T, U>(T t, int a, System.Func<U> b) => throw null!; int Method3<T>(T t) => throw null!; } """; CreateCompilation(src2, targetFramework: TargetFramework.Net80).VerifyEmitDiagnostics( // (6,9): warning CS8602: Dereference of a possibly null reference. // Method(42, () => s).ToString(); // 1 Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "Method(42, () => s)").WithLocation(6, 9), // (13,9): warning CS8602: Dereference of a possibly null reference. // Method2(s = null, 42, () => s).ToString(); // 2 Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "Method2(s = null, 42, () => s)").WithLocation(13, 9), // (18,41): warning CS8602: Dereference of a possibly null reference. // Method(Method3(s = null), () => s.ToString()); // 3 Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "s").WithLocation(18, 41) ); } [Fact] public void ElementNullability_Inference_ArrayCollection_Lambda_ContainingTuples() { // Analyze captured variables at the location lambda is converted to a delegate string src = """ #nullable enable public class C { void M1(string? s) { Method([(42, () => s)]).ToString(); // 1 if (s is null) return; Method([(42, () => s)]).ToString(); } void M2(string? s) { Method2(s = "", [(42, () => s)]).ToString(); Method2(s = null, [(42, () => s)]).ToString(); // 2 } void M3(string? s) { s = ""; Method([(42, Method3(s = null)), (42, () => s.ToString())]); // 3 } T Method<T>((int, System.Func<T>)[] a) => throw null!; U Method2<T, U>(T t, (int, System.Func<U>)[] a) => throw null!; System.Func<string> Method3<T>(T t) => throw null!; } """; // Tuples should be analyzed as-if they were flattened (but they current are not) // Tracked by https://github.com/dotnet/roslyn/issues/71242 CreateCompilation(src, targetFramework: TargetFramework.Net80).VerifyEmitDiagnostics( // (18,53): warning CS8602: Dereference of a possibly null reference. // Method([(42, Method3(s = null)), (42, () => s.ToString())]); // 3 Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "s").WithLocation(18, 53) ); } [Fact] public void ElementNullability_Inference_ArrayCollection_Lambda_InsideTuples() { // Analyze captured variables at the location lambda is converted to a delegate string src = """ #nullable enable public class C { void M1(string? s) { Method((42, [() => s])).ToString(); // 1 if (s is null) return; Method((42, [() => s])).ToString(); } void M2(string? s) { Method2(s = "", (42, [() => s])).ToString(); Method2(s = null, (42, [() => s])).ToString(); // 2 } void M3(string? s) { s = ""; Method((42, [Method3(s = null), () => s.ToString()])); // 3 } T Method<T>((int, System.Func<T>[]) a) => throw null!; U Method2<T, U>(T t, (int, System.Func<U>[]) a) => throw null!; System.Func<string> Method3<T>(T t) => throw null!; } """; // Tuples should be analyzed as-if they were flattened (but they current are not) // Tracked by https://github.com/dotnet/roslyn/issues/71242 CreateCompilation(src, targetFramework: TargetFramework.Net80).VerifyEmitDiagnostics( // (18,47): warning CS8602: Dereference of a possibly null reference. // Method((42, [Method3(s = null), () => s.ToString()])); // 3 Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "s").WithLocation(18, 47) ); } [Fact] public void ElementNullability_Inference_ArrayCollection_Lambda_Constraint() { string src = """ #nullable enable public class C { void M1(string notNull) { M([() => notNull]).ToString(); } void M2(string? maybeNull) { M([() => maybeNull]).ToString(); // 1, 2, 3 } T M<T>(System.Func<T>[] a) where T : notnull => throw null!; } """; CreateCompilation(src, targetFramework: TargetFramework.Net80).VerifyEmitDiagnostics( // (10,9): warning CS8714: The type 'string?' cannot be used as type parameter 'T' in the generic type or method 'C.M<T>(Func<T>[])'. Nullability of type argument 'string?' doesn't match 'notnull' constraint. // M([() => maybeNull]).ToString(); // 1, 2, 3 Diagnostic(ErrorCode.WRN_NullabilityMismatchInTypeParameterNotNullConstraint, "M").WithArguments("C.M<T>(System.Func<T>[])", "T", "string?").WithLocation(10, 9), // (10,9): warning CS8602: Dereference of a possibly null reference. // M([() => maybeNull]).ToString(); // 1, 2, 3 Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "M([() => maybeNull])").WithLocation(10, 9), // (10,12): warning CS8621: Nullability of reference types in return type of 'lambda expression' doesn't match the target delegate 'Func<string?>' (possibly because of nullability attributes). // M([() => maybeNull]).ToString(); // 1, 2, 3 Diagnostic(ErrorCode.WRN_NullabilityMismatchInReturnTypeOfTargetDelegate, "() =>").WithArguments("lambda expression", "System.Func<string?>").WithLocation(10, 12) ); } [Fact] public void ElementNullability_Inference_ArrayCollection_Chained() { string src = """ #nullable enable public class C { void M1(string? maybeNull) { var result = M([Copy(maybeNull, out var maybeNull2), maybeNull2]); result.ToString(); } void M2(string? maybeNull) { M([Copy(maybeNull, out var maybeNull2), maybeNull2.ToString()]); } T M<T>(T[] a) => throw null!; object Copy<T>(T t, out T t2) => throw null!; } """; var comp = CreateCompilation(src, targetFramework: TargetFramework.Net80).VerifyEmitDiagnostics( // (7,9): warning CS8602: Dereference of a possibly null reference. // result.ToString(); Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "result").WithLocation(7, 9), // (11,49): warning CS8602: Dereference of a possibly null reference. // M([Copy(maybeNull, out var maybeNull2), maybeNull2.ToString()]); Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "maybeNull2").WithLocation(11, 49) ); var tree = comp.SyntaxTrees.First(); var model = comp.GetSemanticModel(tree); var invocation0 = GetSyntax<InvocationExpressionSyntax>(tree, "M([Copy(maybeNull, out var maybeNull2), maybeNull2])"); Assert.Equal("System.Object? C.M<System.Object?>(System.Object?[]! a)", model.GetSymbolInfo(invocation0).Symbol.ToTestDisplayString(includeNonNullable: true)); var invocation1 = GetSyntax<InvocationExpressionSyntax>(tree, "M([Copy(maybeNull, out var maybeNull2), maybeNull2.ToString()])"); Assert.Equal("System.Object! C.M<System.Object!>(System.Object![]! a)", model.GetSymbolInfo(invocation1).Symbol.ToTestDisplayString(includeNonNullable: true)); } [Fact] public void ElementNullability_Inference_ImmutableArrayCollection() { string src = """ using System.Collections.Immutable; #nullable enable public class C { void M1(string? maybeNull, string notNull) { M(ref notNull, [null]); // 1 } void M2(string? maybeNull, string notNull) { M(ref notNull, [maybeNull]); // 2 } void M3(string? maybeNull, string notNull) { M(ref notNull, [notNull, ""]); } void M4(string? maybeNull, string notNull) { M(ref maybeNull, [notNull, maybeNull, ""]); } void M<T>(ref T t, ImmutableArray<T> a) { } } """; CreateCompilation(src, targetFramework: TargetFramework.Net80).VerifyEmitDiagnostics( // (8,15): warning CS8600: Converting null literal or possible null value to non-nullable type. // M(ref notNull, [null]); // 1 Diagnostic(ErrorCode.WRN_ConvertingNullableToNonNullable, "notNull").WithLocation(8, 15), // (12,15): warning CS8600: Converting null literal or possible null value to non-nullable type. // M(ref notNull, [maybeNull]); // 2 Diagnostic(ErrorCode.WRN_ConvertingNullableToNonNullable, "notNull").WithLocation(12, 15) ); } [Fact] public void ElementNullability_Inference_ImmutableArrayCollection_NestedInArray() { string src = """ using System.Collections.Immutable; #nullable enable public class C { void M1(string? maybeNull, string notNull) { M(ref notNull, [[null]]); // 1 } void M2(string? maybeNull, string notNull) { M(ref notNull, [[maybeNull]]); // 2 } void M3(string? maybeNull, string notNull) { M(ref notNull, [[notNull, ""]]); } void M4(string? maybeNull, string notNull) { M(ref maybeNull, [[notNull, maybeNull, ""]]); } void M<T>(ref T t, ImmutableArray<T>[] a) { } } """; CreateCompilation(src, targetFramework: TargetFramework.Net80).VerifyEmitDiagnostics( // (8,15): warning CS8600: Converting null literal or possible null value to non-nullable type. // M(ref notNull, [null]); // 1 Diagnostic(ErrorCode.WRN_ConvertingNullableToNonNullable, "notNull").WithLocation(8, 15), // (12,15): warning CS8600: Converting null literal or possible null value to non-nullable type. // M(ref notNull, [maybeNull]); // 2 Diagnostic(ErrorCode.WRN_ConvertingNullableToNonNullable, "notNull").WithLocation(12, 15) ); } [Theory, CombinatorialData] public void ElementNullability_Inference_SpanCollection([CombinatorialValues("Span", "ReadOnlySpan")] string spanType) { string src = $$""" using System; #nullable enable public class C { void M1(string? maybeNull, string notNull) { M(ref notNull, [null]); // 1 } void M2(string? maybeNull, string notNull) { M(ref notNull, [maybeNull]); // 2 } void M3(string? maybeNull, string notNull) { M(ref notNull, [notNull, ""]); } void M4(string? maybeNull, string notNull) { M(ref maybeNull, [notNull, maybeNull, ""]); } void M<T>(ref T t, {{spanType}}<T> a) { } } """; CreateCompilation(src, targetFramework: TargetFramework.Net80).VerifyEmitDiagnostics( // (8,15): warning CS8600: Converting null literal or possible null value to non-nullable type. // M(ref notNull, [null]); // 1 Diagnostic(ErrorCode.WRN_ConvertingNullableToNonNullable, "notNull").WithLocation(8, 15), // (12,15): warning CS8600: Converting null literal or possible null value to non-nullable type. // M(ref notNull, [maybeNull]); // 2 Diagnostic(ErrorCode.WRN_ConvertingNullableToNonNullable, "notNull").WithLocation(12, 15) ); } [Fact] public void ElementNullability_Inference_ListCollection() { string src = """ using System.Collections.Generic; #nullable enable public class C { void M1(string? maybeNull, string notNull) { M(ref notNull, [null]).ToString(); // 1 } void M2(string? maybeNull, string notNull) { M(ref notNull, [maybeNull]).ToString(); // 2 } void M3(string? maybeNull, string notNull) { M(ref notNull, [notNull, ""]).ToString(); } void M4(string? maybeNull, string notNull) { M(ref maybeNull, [notNull, maybeNull, ""]).ToString(); // 3 } T M<T>(ref T t, List<T> a) => throw null!; } """; // The diagnostics on `notNull` are a bit unclear, but they match the behavior // for non-collection expression scenarios (see below). CreateCompilation(src, targetFramework: TargetFramework.Net80).VerifyEmitDiagnostics( // (8,9): warning CS8602: Dereference of a possibly null reference. // M(ref notNull, [null]).ToString(); // 1 Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "M(ref notNull, [null])").WithLocation(8, 9), // (8,15): warning CS8600: Converting null literal or possible null value to non-nullable type. // M(ref notNull, [null]).ToString(); // 1 Diagnostic(ErrorCode.WRN_ConvertingNullableToNonNullable, "notNull").WithLocation(8, 15), // (12,9): warning CS8602: Dereference of a possibly null reference. // M(ref notNull, [maybeNull]).ToString(); // 2 Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "M(ref notNull, [maybeNull])").WithLocation(12, 9), // (12,15): warning CS8600: Converting null literal or possible null value to non-nullable type. // M(ref notNull, [maybeNull]).ToString(); // 2 Diagnostic(ErrorCode.WRN_ConvertingNullableToNonNullable, "notNull").WithLocation(12, 15), // (20,9): warning CS8602: Dereference of a possibly null reference. // M(ref maybeNull, [notNull, maybeNull, ""]).ToString(); // 3 Diagnostic(ErrorCode.WRN_NullReferenceReceiver, @"M(ref maybeNull, [notNull, maybeNull, """"])").WithLocation(20, 9)); src = """ #nullable enable public class C { void MA(string? maybeNull, string notNull) { M1(ref notNull, null).ToString(); // 1 } void MB(string? maybeNull, string notNull) { M1(ref notNull, maybeNull).ToString(); // 2 } void MC(string? maybeNull, string notNull) { M2(ref notNull, notNull, "").ToString(); } void MD(string? maybeNull, string notNull) { M3(ref maybeNull, notNull, maybeNull, "").ToString(); // 3 } T M1<T>(ref T t, T a) => throw null!; T M2<T>(ref T t, T a, T b) => throw null!; T M3<T>(ref T t, T a, T b, T c) => throw null!; } """; CreateCompilation(src, targetFramework: TargetFramework.Net80).VerifyEmitDiagnostics( // (6,9): warning CS8602: Dereference of a possibly null reference. // M1(ref notNull, null).ToString(); // 1 Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "M1(ref notNull, null)").WithLocation(6, 9), // (6,16): warning CS8600: Converting null literal or possible null value to non-nullable type. // M1(ref notNull, null).ToString(); // 1 Diagnostic(ErrorCode.WRN_ConvertingNullableToNonNullable, "notNull").WithLocation(6, 16), // (10,9): warning CS8602: Dereference of a possibly null reference. // M1(ref notNull, maybeNull).ToString(); // 2 Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "M1(ref notNull, maybeNull)").WithLocation(10, 9), // (10,16): warning CS8600: Converting null literal or possible null value to non-nullable type. // M1(ref notNull, maybeNull).ToString(); // 2 Diagnostic(ErrorCode.WRN_ConvertingNullableToNonNullable, "notNull").WithLocation(10, 16), // (18,9): warning CS8602: Dereference of a possibly null reference. // M3(ref maybeNull, notNull, maybeNull, "").ToString(); // 3 Diagnostic(ErrorCode.WRN_NullReferenceReceiver, @"M3(ref maybeNull, notNull, maybeNull, """")").WithLocation(18, 9)); } [Theory] [InlineData("System.Collections.Generic.IEnumerable")] [InlineData("System.Collections.Generic.IReadOnlyCollection")] [InlineData("System.Collections.Generic.IReadOnlyList")] [InlineData("System.Collections.Generic.ICollection")] [InlineData("System.Collections.Generic.IList")] public void ElementNullability_Inference_InterfaceCollection(string interfaceType) { string src = $$""" #nullable enable public class C { void M1(string? maybeNull, string notNull) { M(ref notNull, [null]); // 1 } void M2(string? maybeNull, string notNull) { M(ref notNull, [maybeNull]); // 2 } void M3(string? maybeNull, string notNull) { M(ref notNull, [notNull, ""]); } void M4(string? maybeNull, string notNull) { M(ref maybeNull, [notNull, maybeNull, ""]); } void M<T>(ref T t, {{interfaceType}}<T> a) { } } """; CreateCompilation(src, targetFramework: TargetFramework.Net80).VerifyEmitDiagnostics( // (6,15): warning CS8600: Converting null literal or possible null value to non-nullable type. // M(ref notNull, [null]); // 1 Diagnostic(ErrorCode.WRN_ConvertingNullableToNonNullable, "notNull").WithLocation(6, 15), // (10,15): warning CS8600: Converting null literal or possible null value to non-nullable type. // M(ref notNull, [maybeNull]); // 2 Diagnostic(ErrorCode.WRN_ConvertingNullableToNonNullable, "notNull").WithLocation(10, 15) ); } [Fact] public void ElementNullability_Inference_BadCall() { // This scenario illustrates a limitation in `NullableWalker.VisitArguments`. // We expect that every target-typed expression with a pending completion // should be able to be completed. // However, in this case, the nullable walker's logic for finding the corresponding // parameter type (ie. target-type for the conversion) lags behind the one we use // in the binder (`Binder.BuildArgumentsForErrorRecovery`). string src = """ #nullable enable using System; class C { void M() { byte[] a = [1, 2]; a.AsSpan().SequenceEqual([0, 1]); } } """; CreateCompilation(src, targetFramework: TargetFramework.Net80).VerifyEmitDiagnostics( // (9,20): error CS0411: The type arguments for method 'MemoryExtensions.SequenceEqual<T>(ReadOnlySpan<T>, ReadOnlySpan<T>)' cannot be inferred from the usage. Try specifying the type arguments explicitly. // a.AsSpan().SequenceEqual([0, 1]); Diagnostic(ErrorCode.ERR_CantInferMethTypeArgs, "SequenceEqual").WithArguments("System.MemoryExtensions.SequenceEqual<T>(System.ReadOnlySpan<T>, System.ReadOnlySpan<T>)").WithLocation(9, 20) ); } [Fact] public void ElementNullability_Inference_BadCall_02() { string src = """ #nullable enable using System; class C { void M() { byte[] a = [1, 2]; a.AsSpan().SequenceEqual<byte>([0, 1]); } } """; CreateCompilation(src, targetFramework: TargetFramework.Net80).VerifyEmitDiagnostics(); } [Fact] public void SpreadNullability() { string src = """ #nullable enable string[]? x1 = null; string[] x2 = new string[0]; string[] y1 = [.. x1]; string[] y2 = [.. x2]; """; CreateCompilation(src).VerifyEmitDiagnostics( // (5,19): warning CS8602: Dereference of a possibly null reference. // string[] y1 = [.. x1]; Diagnostic(ErrorCode.WRN_NullReferenceReceiver, "x1").WithLocation(5, 19)); } [Fact] public void SpreadNullability_CheckExpressions() { string src = """ #nullable enable string[] y1 = [.. M(null)]; string[] y2 = [.. M(new object())]; string[] M(object o) => throw null!; """; CreateCompilation(src).VerifyEmitDiagnostics( // (2,21): warning CS8625: Cannot convert null literal to non-nullable reference type. // string[] y1 = [.. M(null)]; Diagnostic(ErrorCode.WRN_NullAsNonNullable, "null").WithLocation(2, 21) ); } [Fact] public void SpreadNullability_SplitExpression() { string src = """ #nullable enable object x = ""; object[] y1 = [..(x is null)]; """; CreateCompilation(src).VerifyEmitDiagnostics( // (3,18): error CS9212: Spread operator '..' cannot operate on variables of type 'bool' because 'bool' does not contain a public instance or extension definition for 'GetEnumerator' // object[] y1 = [..(x is null)]; Diagnostic(ErrorCode.ERR_SpreadMissingMember, "(x is null)").WithArguments("bool", "GetEnumerator").WithLocation(3, 18) ); } [Theory] [InlineData("scoped ")] [InlineData("")] public void CollectionBuilderRefStructAllocWarning(string scopedModifier) { var source = $$""" using System; using System.Collections.Generic; using System.Runtime.CompilerServices; [CollectionBuilder(typeof(RefStructCollectionBuilder), nameof(RefStructCollectionBuilder.Create))] ref struct RefStructCollection<T> { public IEnumerator<T> GetEnumerator() => null; } static class RefStructCollectionBuilder { public static RefStructCollection<T> Create<T>({{scopedModifier}}ReadOnlySpan<T> items) => default; } public class Program { public void M() { RefStructCollection<int> rs = [1]; } } """; var comp = CreateCompilation(new[] { source, CollectionBuilderAttributeDefinition }, targetFramework: TargetFramework.Net70, options: TestOptions.ReleaseDll.WithSpecificDiagnosticOptions(WithSpanAllocWarning)); comp.VerifyEmitDiagnostics(); } [ConditionalFact(typeof(WindowsOrLinuxOnly)), WorkItem("https://github.com/dotnet/roslyn/issues/70381")] public void ExtremelyNestedCollectionExpressionDoesNotOverflow_1() { var code = $$""" #nullable enable using System; using System.Collections; using System.Collections.Generic; class MyCollection : IEnumerable { void M() { MyCollection collection = {{new string('[', 64)}}{{new string(']', 64)}}; } public void Add(MyCollection c) { } public IEnumerator<MyCollection> GetEnumerator() => throw new NotImplementedException(); IEnumerator IEnumerable.GetEnumerator() => throw new NotImplementedException(); } """; var compilation = CreateCompilation(code); CompileAndVerify(compilation); var tree = compilation.SyntaxTrees[0]; var model = compilation.GetSemanticModel(tree); var collectionExpression = tree.GetRoot().DescendantNodes().OfType<CollectionExpressionSyntax>().First(); Assert.NotNull(model.GetOperation(collectionExpression)); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/70381")] public void ExtremelyNestedCollectionExpressionDoesNotOverflow_2() { var code = $$""" #nullable enable using System; using System.Collections; using System.Collections.Generic; class MyCollection : IEnumerable { void M() { MyCollection collection = {{new string('[', 65)}}{{new string(']', 65)}}; } public void Add(MyCollection c) { } public IEnumerator<MyCollection> GetEnumerator() => throw new NotImplementedException(); IEnumerator IEnumerable.GetEnumerator() => throw new NotImplementedException(); } """; var compilation = CreateCompilation(code); compilation.VerifyDiagnostics( // (10,99): error CS8078: An expression is too long or complex to compile // MyCollection collection = [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]; Diagnostic(ErrorCode.ERR_InsufficientStack, "[]").WithLocation(10, 99) ); var tree = compilation.SyntaxTrees[0]; var model = compilation.GetSemanticModel(tree); var collectionExpression = tree.GetRoot().DescendantNodes().OfType<CollectionExpressionSyntax>().First(); Assert.NotNull(model.GetOperation(collectionExpression)); } [Fact] public void SingleSpread_ListToArray() { var source = """ using System.Collections.Generic; class C { static void Main() { List<int> li = [1, 2, 3]; li.Report(); int[] arr = [..li]; arr.Report(); } } """; var verifier = CompileAndVerify(new[] { source, s_collectionExtensions }, expectedOutput: "[1, 2, 3], [1, 2, 3],"); verifier.VerifyDiagnostics(); verifier.VerifyIL("C.Main", """ { // Code size 46 (0x2e) .maxstack 3 IL_0000: ldc.i4.3 IL_0001: newobj "System.Collections.Generic.List<int>..ctor(int)" IL_0006: dup IL_0007: ldc.i4.1 IL_0008: callvirt "void System.Collections.Generic.List<int>.Add(int)" IL_000d: dup IL_000e: ldc.i4.2 IL_000f: callvirt "void System.Collections.Generic.List<int>.Add(int)" IL_0014: dup IL_0015: ldc.i4.3 IL_0016: callvirt "void System.Collections.Generic.List<int>.Add(int)" IL_001b: dup IL_001c: ldc.i4.0 IL_001d: call "void CollectionExtensions.Report(object, bool)" IL_0022: callvirt "int[] System.Collections.Generic.List<int>.ToArray()" IL_0027: ldc.i4.0 IL_0028: call "void CollectionExtensions.Report(object, bool)" IL_002d: ret } """); } [Fact] public void SingleSpread_SpanToArray() { var source = """ using System; class C { static void Main() { Span<int> li = [1, 2, 3]; li.Report(); int[] arr = [..li]; arr.Report(); } } """; var verifier = CompileAndVerify(new[] { source, s_collectionExtensionsWithSpan }, expectedOutput: IncludeExpectedOutput("[1, 2, 3], [1, 2, 3],"), targetFramework: TargetFramework.Net80, verify: Verification.Skipped); verifier.VerifyDiagnostics(); verifier.VerifyIL("C.Main", """ { // Code size 82 (0x52) .maxstack 2 .locals init (System.Span<int> V_0, //li <>y__InlineArray3<int> V_1, System.ReadOnlySpan<int> V_2) IL_0000: ldloca.s V_1 IL_0002: initobj "<>y__InlineArray3<int>" IL_0008: ldloca.s V_1 IL_000a: ldc.i4.0 IL_000b: call "ref int <PrivateImplementationDetails>.InlineArrayElementRef<<>y__InlineArray3<int>, int>(ref <>y__InlineArray3<int>, int)" IL_0010: ldc.i4.1 IL_0011: stind.i4 IL_0012: ldloca.s V_1 IL_0014: ldc.i4.1 IL_0015: call "ref int <PrivateImplementationDetails>.InlineArrayElementRef<<>y__InlineArray3<int>, int>(ref <>y__InlineArray3<int>, int)" IL_001a: ldc.i4.2 IL_001b: stind.i4 IL_001c: ldloca.s V_1 IL_001e: ldc.i4.2 IL_001f: call "ref int <PrivateImplementationDetails>.InlineArrayElementRef<<>y__InlineArray3<int>, int>(ref <>y__InlineArray3<int>, int)" IL_0024: ldc.i4.3 IL_0025: stind.i4 IL_0026: ldloca.s V_1 IL_0028: ldc.i4.3 IL_0029: call "System.Span<int> <PrivateImplementationDetails>.InlineArrayAsSpan<<>y__InlineArray3<int>, int>(ref <>y__InlineArray3<int>, int)" IL_002e: stloc.0 IL_002f: ldloc.0 IL_0030: call "System.ReadOnlySpan<int> System.Span<int>.op_Implicit(System.Span<int>)" IL_0035: stloc.2 IL_0036: ldloca.s V_2 IL_0038: call "void CollectionExtensions.Report<int>(in System.ReadOnlySpan<int>)" IL_003d: ldloca.s V_0 IL_003f: call "int[] System.Span<int>.ToArray()" IL_0044: call "System.ReadOnlySpan<int> System.ReadOnlySpan<int>.op_Implicit(int[])" IL_0049: stloc.2 IL_004a: ldloca.s V_2 IL_004c: call "void CollectionExtensions.Report<int>(in System.ReadOnlySpan<int>)" IL_0051: ret } """); } [Fact] public void SingleSpread_ReadOnlySpanToArray() { var source = """ using System; class C { static void Main() { ReadOnlySpan<int> li = [1, 2, 3]; li.Report(); int[] arr = [..li]; arr.Report(); } } """; var verifier = CompileAndVerify(new[] { source, s_collectionExtensionsWithSpan }, expectedOutput: IncludeExpectedOutput("[1, 2, 3], [1, 2, 3],"), targetFramework: TargetFramework.Net80, verify: Verification.Skipped); verifier.VerifyDiagnostics(); verifier.VerifyIL("C.Main", """ { // Code size 39 (0x27) .maxstack 1 .locals init (System.ReadOnlySpan<int> V_0, //li System.ReadOnlySpan<int> V_1) IL_0000: ldtoken "<PrivateImplementationDetails>.__StaticArrayInitTypeSize=12_Align=4 <PrivateImplementationDetails>.4636993D3E1DA4E9D6B8F87B79E8F7C6D018580D52661950EABC3845C5897A4D4" IL_0005: call "System.ReadOnlySpan<int> System.Runtime.CompilerServices.RuntimeHelpers.CreateSpan<int>(System.RuntimeFieldHandle)" IL_000a: stloc.0 IL_000b: ldloca.s V_0 IL_000d: call "void CollectionExtensions.Report<int>(in System.ReadOnlySpan<int>)" IL_0012: ldloca.s V_0 IL_0014: call "int[] System.ReadOnlySpan<int>.ToArray()" IL_0019: call "System.ReadOnlySpan<int> System.ReadOnlySpan<int>.op_Implicit(int[])" IL_001e: stloc.1 IL_001f: ldloca.s V_1 IL_0021: call "void CollectionExtensions.Report<int>(in System.ReadOnlySpan<int>)" IL_0026: ret } """); } [Fact] public void TwoSpreads_ReadOnlySpanToArray() { var source = """ using System; class C { static void Main() { ReadOnlySpan<int> li1 = [1, 2, 3]; ReadOnlySpan<int> li2 = [4, 5, 6]; li1.Report(); li2.Report(); int[] arr = [..li1, ..li2]; arr.Report(); } } """; var verifier = CompileAndVerify(new[] { source, s_collectionExtensionsWithSpan }, expectedOutput: IncludeExpectedOutput("[1, 2, 3], [4, 5, 6], [1, 2, 3, 4, 5, 6],"), verify: Verification.Skipped, targetFramework: TargetFramework.Net80); verifier.VerifyDiagnostics(); verifier.VerifyIL("C.Main", """ { // Code size 169 (0xa9) .maxstack 4 .locals init (System.ReadOnlySpan<int> V_0, //li1 System.ReadOnlySpan<int> V_1, //li2 System.ReadOnlySpan<int> V_2, System.ReadOnlySpan<int> V_3, int V_4, int[] V_5, System.Span<int> V_6, System.Span<int> V_7) IL_0000: ldtoken "<PrivateImplementationDetails>.__StaticArrayInitTypeSize=12_Align=4 <PrivateImplementationDetails>.4636993D3E1DA4E9D6B8F87B79E8F7C6D018580D52661950EABC3845C5897A4D4" IL_0005: call "System.ReadOnlySpan<int> System.Runtime.CompilerServices.RuntimeHelpers.CreateSpan<int>(System.RuntimeFieldHandle)" IL_000a: stloc.0 IL_000b: ldtoken "<PrivateImplementationDetails>.__StaticArrayInitTypeSize=12_Align=4 <PrivateImplementationDetails>.8CA6EE1043DEFCFD05AA29DEE581CBC519E783E414A687D7C26AC6070D3F6DEE4" IL_0010: call "System.ReadOnlySpan<int> System.Runtime.CompilerServices.RuntimeHelpers.CreateSpan<int>(System.RuntimeFieldHandle)" IL_0015: stloc.1 IL_0016: ldloca.s V_0 IL_0018: call "void CollectionExtensions.Report<int>(in System.ReadOnlySpan<int>)" IL_001d: ldloca.s V_1 IL_001f: call "void CollectionExtensions.Report<int>(in System.ReadOnlySpan<int>)" IL_0024: ldloc.0 IL_0025: stloc.2 IL_0026: ldloc.1 IL_0027: stloc.3 IL_0028: ldc.i4.0 IL_0029: stloc.s V_4 IL_002b: ldloca.s V_2 IL_002d: call "int System.ReadOnlySpan<int>.Length.get" IL_0032: ldloca.s V_3 IL_0034: call "int System.ReadOnlySpan<int>.Length.get" IL_0039: add IL_003a: newarr "int" IL_003f: stloc.s V_5 IL_0041: ldloca.s V_2 IL_0043: ldloc.s V_5 IL_0045: newobj "System.Span<int>..ctor(int[])" IL_004a: stloc.s V_6 IL_004c: ldloca.s V_6 IL_004e: ldloc.s V_4 IL_0050: ldloca.s V_2 IL_0052: call "int System.ReadOnlySpan<int>.Length.get" IL_0057: call "System.Span<int> System.Span<int>.Slice(int, int)" IL_005c: call "void System.ReadOnlySpan<int>.CopyTo(System.Span<int>)" IL_0061: ldloc.s V_4 IL_0063: ldloca.s V_2 IL_0065: call "int System.ReadOnlySpan<int>.Length.get" IL_006a: add IL_006b: stloc.s V_4 IL_006d: ldloca.s V_3 IL_006f: ldloc.s V_5 IL_0071: newobj "System.Span<int>..ctor(int[])" IL_0076: stloc.s V_7 IL_0078: ldloca.s V_7 IL_007a: ldloc.s V_4 IL_007c: ldloca.s V_3 IL_007e: call "int System.ReadOnlySpan<int>.Length.get" IL_0083: call "System.Span<int> System.Span<int>.Slice(int, int)" IL_0088: call "void System.ReadOnlySpan<int>.CopyTo(System.Span<int>)" IL_008d: ldloc.s V_4 IL_008f: ldloca.s V_3 IL_0091: call "int System.ReadOnlySpan<int>.Length.get" IL_0096: add IL_0097: stloc.s V_4 IL_0099: ldloc.s V_5 IL_009b: call "System.ReadOnlySpan<int> System.ReadOnlySpan<int>.op_Implicit(int[])" IL_00a0: stloc.3 IL_00a1: ldloca.s V_3 IL_00a3: call "void CollectionExtensions.Report<int>(in System.ReadOnlySpan<int>)" IL_00a8: ret } """); } [Fact] public void TwoSpreads_Covariance() { var source = """ using System; class C { static void Main() { ReadOnlySpan<D> li1 = [new D()]; ReadOnlySpan<D> li2 = [new D()]; C[] arr = [..li1, ..li2]; arr.Report(); } } class D : C { } """; var verifier = CompileAndVerify(new[] { source, s_collectionExtensionsWithSpan }, expectedOutput: IncludeExpectedOutput("[D, D],"), verify: Verification.Skipped, targetFramework: TargetFramework.Net80); verifier.VerifyDiagnostics(); verifier.VerifyIL("C.Main", """ { // Code size 159 (0x9f) .maxstack 3 .locals init (System.ReadOnlySpan<D> V_0, //li1 D V_1, D V_2, System.ReadOnlySpan<D> V_3, System.ReadOnlySpan<D> V_4, int V_5, C[] V_6, System.ReadOnlySpan<D>.Enumerator V_7, D V_8, System.ReadOnlySpan<D>.Enumerator V_9, System.ReadOnlySpan<C> V_10) IL_0000: newobj "D..ctor()" IL_0005: stloc.1 IL_0006: ldloca.s V_1 IL_0008: newobj "System.ReadOnlySpan<D>..ctor(ref readonly D)" IL_000d: stloc.0 IL_000e: newobj "D..ctor()" IL_0013: stloc.2 IL_0014: ldloca.s V_2 IL_0016: newobj "System.ReadOnlySpan<D>..ctor(ref readonly D)" IL_001b: ldloc.0 IL_001c: stloc.3 IL_001d: stloc.s V_4 IL_001f: ldc.i4.0 IL_0020: stloc.s V_5 IL_0022: ldloca.s V_3 IL_0024: call "int System.ReadOnlySpan<D>.Length.get" IL_0029: ldloca.s V_4 IL_002b: call "int System.ReadOnlySpan<D>.Length.get" IL_0030: add IL_0031: newarr "C" IL_0036: stloc.s V_6 IL_0038: ldloca.s V_3 IL_003a: call "System.ReadOnlySpan<D>.Enumerator System.ReadOnlySpan<D>.GetEnumerator()" IL_003f: stloc.s V_7 IL_0041: br.s IL_005a IL_0043: ldloca.s V_7 IL_0045: call "ref readonly D System.ReadOnlySpan<D>.Enumerator.Current.get" IL_004a: ldind.ref IL_004b: stloc.s V_8 IL_004d: ldloc.s V_6 IL_004f: ldloc.s V_5 IL_0051: ldloc.s V_8 IL_0053: stelem.ref IL_0054: ldloc.s V_5 IL_0056: ldc.i4.1 IL_0057: add IL_0058: stloc.s V_5 IL_005a: ldloca.s V_7 IL_005c: call "bool System.ReadOnlySpan<D>.Enumerator.MoveNext()" IL_0061: brtrue.s IL_0043 IL_0063: ldloca.s V_4 IL_0065: call "System.ReadOnlySpan<D>.Enumerator System.ReadOnlySpan<D>.GetEnumerator()" IL_006a: stloc.s V_9 IL_006c: br.s IL_0085 IL_006e: ldloca.s V_9 IL_0070: call "ref readonly D System.ReadOnlySpan<D>.Enumerator.Current.get" IL_0075: ldind.ref IL_0076: stloc.s V_8 IL_0078: ldloc.s V_6 IL_007a: ldloc.s V_5 IL_007c: ldloc.s V_8 IL_007e: stelem.ref IL_007f: ldloc.s V_5 IL_0081: ldc.i4.1 IL_0082: add IL_0083: stloc.s V_5 IL_0085: ldloca.s V_9 IL_0087: call "bool System.ReadOnlySpan<D>.Enumerator.MoveNext()" IL_008c: brtrue.s IL_006e IL_008e: ldloc.s V_6 IL_0090: call "System.ReadOnlySpan<C> System.ReadOnlySpan<C>.op_Implicit(C[])" IL_0095: stloc.s V_10 IL_0097: ldloca.s V_10 IL_0099: call "void CollectionExtensions.Report<C>(in System.ReadOnlySpan<C>)" IL_009e: ret } """); } [Fact] public void SpanToList_Spreads() { var source = """ using System; using System.Collections.Generic; class C { static void Main() { M([1, 2, 3], [4, 5, 6]); } static void M(Span<int> e1, Span<int> e2) { List<int> result = [..e1, ..e2]; result.Report(); } } """; var verifier = CompileAndVerify(new[] { source, s_collectionExtensionsWithSpan }, verify: Verification.Fails, expectedOutput: IncludeExpectedOutput("[1, 2, 3, 4, 5, 6],"), targetFramework: TargetFramework.Net80); verifier.VerifyDiagnostics(); verifier.VerifyIL("C.M", """ { // Code size 120 (0x78) .maxstack 5 .locals init (System.Span<int> V_0, System.Span<int> V_1, int V_2, System.Span<int> V_3, int V_4) IL_0000: ldarg.0 IL_0001: stloc.0 IL_0002: ldarg.1 IL_0003: stloc.1 IL_0004: ldloca.s V_0 IL_0006: call "int System.Span<int>.Length.get" IL_000b: ldloca.s V_1 IL_000d: call "int System.Span<int>.Length.get" IL_0012: add IL_0013: stloc.2 IL_0014: ldloc.2 IL_0015: newobj "System.Collections.Generic.List<int>..ctor(int)" IL_001a: dup IL_001b: ldloc.2 IL_001c: call "void System.Runtime.InteropServices.CollectionsMarshal.SetCount<int>(System.Collections.Generic.List<int>, int)" IL_0021: dup IL_0022: call "System.Span<int> System.Runtime.InteropServices.CollectionsMarshal.AsSpan<int>(System.Collections.Generic.List<int>)" IL_0027: stloc.3 IL_0028: ldc.i4.0 IL_0029: stloc.s V_4 IL_002b: ldloca.s V_0 IL_002d: ldloca.s V_3 IL_002f: ldloc.s V_4 IL_0031: ldloca.s V_0 IL_0033: call "int System.Span<int>.Length.get" IL_0038: call "System.Span<int> System.Span<int>.Slice(int, int)" IL_003d: call "void System.Span<int>.CopyTo(System.Span<int>)" IL_0042: ldloc.s V_4 IL_0044: ldloca.s V_0 IL_0046: call "int System.Span<int>.Length.get" IL_004b: add IL_004c: stloc.s V_4 IL_004e: ldloca.s V_1 IL_0050: ldloca.s V_3 IL_0052: ldloc.s V_4 IL_0054: ldloca.s V_1 IL_0056: call "int System.Span<int>.Length.get" IL_005b: call "System.Span<int> System.Span<int>.Slice(int, int)" IL_0060: call "void System.Span<int>.CopyTo(System.Span<int>)" IL_0065: ldloc.s V_4 IL_0067: ldloca.s V_1 IL_0069: call "int System.Span<int>.Length.get" IL_006e: add IL_006f: stloc.s V_4 IL_0071: ldc.i4.0 IL_0072: call "void CollectionExtensions.Report(object, bool)" IL_0077: ret } """); } [Fact] public void ReadOnlySpanToList_Spreads() { var source = """ using System; using System.Collections.Generic; class C { static void Main() { M([1, 2, 3], [4, 5, 6]); } static void M(ReadOnlySpan<int> e1, ReadOnlySpan<int> e2) { List<int> result = [..e1, ..e2]; result.Report(); } } """; var verifier = CompileAndVerify(new[] { source, s_collectionExtensionsWithSpan }, verify: Verification.FailsPEVerify, expectedOutput: IncludeExpectedOutput("[1, 2, 3, 4, 5, 6],"), targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseExe); verifier.VerifyDiagnostics(); verifier.VerifyIL("C.M", """ { // Code size 120 (0x78) .maxstack 5 .locals init (System.ReadOnlySpan<int> V_0, System.ReadOnlySpan<int> V_1, int V_2, System.Span<int> V_3, int V_4) IL_0000: ldarg.0 IL_0001: stloc.0 IL_0002: ldarg.1 IL_0003: stloc.1 IL_0004: ldloca.s V_0 IL_0006: call "int System.ReadOnlySpan<int>.Length.get" IL_000b: ldloca.s V_1 IL_000d: call "int System.ReadOnlySpan<int>.Length.get" IL_0012: add IL_0013: stloc.2 IL_0014: ldloc.2 IL_0015: newobj "System.Collections.Generic.List<int>..ctor(int)" IL_001a: dup IL_001b: ldloc.2 IL_001c: call "void System.Runtime.InteropServices.CollectionsMarshal.SetCount<int>(System.Collections.Generic.List<int>, int)" IL_0021: dup IL_0022: call "System.Span<int> System.Runtime.InteropServices.CollectionsMarshal.AsSpan<int>(System.Collections.Generic.List<int>)" IL_0027: stloc.3 IL_0028: ldc.i4.0 IL_0029: stloc.s V_4 IL_002b: ldloca.s V_0 IL_002d: ldloca.s V_3 IL_002f: ldloc.s V_4 IL_0031: ldloca.s V_0 IL_0033: call "int System.ReadOnlySpan<int>.Length.get" IL_0038: call "System.Span<int> System.Span<int>.Slice(int, int)" IL_003d: call "void System.ReadOnlySpan<int>.CopyTo(System.Span<int>)" IL_0042: ldloc.s V_4 IL_0044: ldloca.s V_0 IL_0046: call "int System.ReadOnlySpan<int>.Length.get" IL_004b: add IL_004c: stloc.s V_4 IL_004e: ldloca.s V_1 IL_0050: ldloca.s V_3 IL_0052: ldloc.s V_4 IL_0054: ldloca.s V_1 IL_0056: call "int System.ReadOnlySpan<int>.Length.get" IL_005b: call "System.Span<int> System.Span<int>.Slice(int, int)" IL_0060: call "void System.ReadOnlySpan<int>.CopyTo(System.Span<int>)" IL_0065: ldloc.s V_4 IL_0067: ldloca.s V_1 IL_0069: call "int System.ReadOnlySpan<int>.Length.get" IL_006e: add IL_006f: stloc.s V_4 IL_0071: ldc.i4.0 IL_0072: call "void CollectionExtensions.Report(object, bool)" IL_0077: ret } """); } [Fact] public void ArrayToList_Spreads() { var source = """ using System.Collections.Generic; class C { static void Main() { M([1, 2, 3], [4, 5, 6]); } static void M(int[] e1, int[] e2) { List<int> result = [..e1, ..e2]; result.Report(); } } """; var verifier = CompileAndVerify(new[] { source, s_collectionExtensionsWithSpan }, verify: Verification.FailsPEVerify, expectedOutput: IncludeExpectedOutput("[1, 2, 3, 4, 5, 6],"), targetFramework: TargetFramework.Net80); verifier.VerifyDiagnostics(); verifier.VerifyIL("C.M", """ { // Code size 128 (0x80) .maxstack 5 .locals init (int[] V_0, int[] V_1, int V_2, System.Span<int> V_3, int V_4, System.ReadOnlySpan<int> V_5, System.ReadOnlySpan<int> V_6) IL_0000: ldarg.0 IL_0001: stloc.0 IL_0002: ldarg.1 IL_0003: stloc.1 IL_0004: ldloc.0 IL_0005: ldlen IL_0006: conv.i4 IL_0007: ldloc.1 IL_0008: ldlen IL_0009: conv.i4 IL_000a: add IL_000b: stloc.2 IL_000c: ldloc.2 IL_000d: newobj "System.Collections.Generic.List<int>..ctor(int)" IL_0012: dup IL_0013: ldloc.2 IL_0014: call "void System.Runtime.InteropServices.CollectionsMarshal.SetCount<int>(System.Collections.Generic.List<int>, int)" IL_0019: dup IL_001a: call "System.Span<int> System.Runtime.InteropServices.CollectionsMarshal.AsSpan<int>(System.Collections.Generic.List<int>)" IL_001f: stloc.3 IL_0020: ldc.i4.0 IL_0021: stloc.s V_4 IL_0023: ldloca.s V_5 IL_0025: ldloc.0 IL_0026: call "System.ReadOnlySpan<int>..ctor(int[])" IL_002b: ldloca.s V_5 IL_002d: ldloca.s V_3 IL_002f: ldloc.s V_4 IL_0031: ldloca.s V_5 IL_0033: call "int System.ReadOnlySpan<int>.Length.get" IL_0038: call "System.Span<int> System.Span<int>.Slice(int, int)" IL_003d: call "void System.ReadOnlySpan<int>.CopyTo(System.Span<int>)" IL_0042: ldloc.s V_4 IL_0044: ldloca.s V_5 IL_0046: call "int System.ReadOnlySpan<int>.Length.get" IL_004b: add IL_004c: stloc.s V_4 IL_004e: ldloca.s V_6 IL_0050: ldloc.1 IL_0051: call "System.ReadOnlySpan<int>..ctor(int[])" IL_0056: ldloca.s V_6 IL_0058: ldloca.s V_3 IL_005a: ldloc.s V_4 IL_005c: ldloca.s V_6 IL_005e: call "int System.ReadOnlySpan<int>.Length.get" IL_0063: call "System.Span<int> System.Span<int>.Slice(int, int)" IL_0068: call "void System.ReadOnlySpan<int>.CopyTo(System.Span<int>)" IL_006d: ldloc.s V_4 IL_006f: ldloca.s V_6 IL_0071: call "int System.ReadOnlySpan<int>.Length.get" IL_0076: add IL_0077: stloc.s V_4 IL_0079: ldc.i4.0 IL_007a: call "void CollectionExtensions.Report(object, bool)" IL_007f: ret } """); } [Theory] [CombinatorialData] public void ArrayToArray_Covariant_SingleSpread(bool missingReadOnlySpanCtor) { var source = """ class Base { } class Derived : Base { } class C { static void Main() { Base[] array = new Derived[] { new Derived() }; array.Report(); Base[] copy = [..array]; copy.Report(); } } """; var comp = CreateCompilation(new[] { source, s_collectionExtensionsWithSpan }, targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseExe); if (missingReadOnlySpanCtor) comp.MakeMemberMissing(WellKnownMember.System_ReadOnlySpan_T__ctor_Array); var verifier = CompileAndVerify(comp, verify: Verification.FailsPEVerify, expectedOutput: IncludeExpectedOutput("[Derived], [Derived], ")); verifier.VerifyDiagnostics(); verifier.VerifyIL("C.Main", """ { // Code size 49 (0x31) .maxstack 4 .locals init (Base[] V_0, System.ReadOnlySpan<Base> V_1) IL_0000: ldc.i4.1 IL_0001: newarr "Derived" IL_0006: dup IL_0007: ldc.i4.0 IL_0008: newobj "Derived..ctor()" IL_000d: stelem.ref IL_000e: stloc.0 IL_000f: ldloc.0 IL_0010: dup IL_0011: call "System.ReadOnlySpan<Base> System.ReadOnlySpan<Base>.op_Implicit(Base[])" IL_0016: stloc.1 IL_0017: ldloca.s V_1 IL_0019: call "void CollectionExtensions.Report<Base>(in System.ReadOnlySpan<Base>)" IL_001e: call "Base[] System.Linq.Enumerable.ToArray<Base>(System.Collections.Generic.IEnumerable<Base>)" IL_0023: call "System.ReadOnlySpan<Base> System.ReadOnlySpan<Base>.op_Implicit(Base[])" IL_0028: stloc.1 IL_0029: ldloca.s V_1 IL_002b: call "void CollectionExtensions.Report<Base>(in System.ReadOnlySpan<Base>)" IL_0030: ret } """); } [Fact] public void ArrayToArray_Covariant_MultipleSpreads() { var source = """ class Base { } class Derived : Base { } class C { static void Main() { Base[] array = new Derived[] { new Derived() }; array.Report(); Base[] copy = [..array, ..array]; copy.Report(); } } """; var verifier = CompileAndVerify(new[] { source, s_collectionExtensionsWithSpan }, verify: Verification.FailsPEVerify, expectedOutput: IncludeExpectedOutput("[Derived], [Derived, Derived],"), targetFramework: TargetFramework.Net80); verifier.VerifyDiagnostics(); verifier.VerifyIL("C.Main", """ { // Code size 164 (0xa4) .maxstack 4 .locals init (Base[] V_0, System.ReadOnlySpan<Base> V_1, Base[] V_2, int V_3, Base[] V_4, System.ReadOnlySpan<Base> V_5, System.Span<Base> V_6, System.Span<Base> V_7) IL_0000: ldc.i4.1 IL_0001: newarr "Derived" IL_0006: dup IL_0007: ldc.i4.0 IL_0008: newobj "Derived..ctor()" IL_000d: stelem.ref IL_000e: stloc.0 IL_000f: ldloc.0 IL_0010: dup IL_0011: call "System.ReadOnlySpan<Base> System.ReadOnlySpan<Base>.op_Implicit(Base[])" IL_0016: stloc.1 IL_0017: ldloca.s V_1 IL_0019: call "void CollectionExtensions.Report<Base>(in System.ReadOnlySpan<Base>)" IL_001e: dup IL_001f: stloc.0 IL_0020: stloc.2 IL_0021: ldc.i4.0 IL_0022: stloc.3 IL_0023: ldloc.0 IL_0024: ldlen IL_0025: conv.i4 IL_0026: ldloc.2 IL_0027: ldlen IL_0028: conv.i4 IL_0029: add IL_002a: newarr "Base" IL_002f: stloc.s V_4 IL_0031: ldloca.s V_1 IL_0033: ldloc.0 IL_0034: call "System.ReadOnlySpan<Base>..ctor(Base[])" IL_0039: ldloca.s V_1 IL_003b: ldloc.s V_4 IL_003d: newobj "System.Span<Base>..ctor(Base[])" IL_0042: stloc.s V_6 IL_0044: ldloca.s V_6 IL_0046: ldloc.3 IL_0047: ldloca.s V_1 IL_0049: call "int System.ReadOnlySpan<Base>.Length.get" IL_004e: call "System.Span<Base> System.Span<Base>.Slice(int, int)" IL_0053: call "void System.ReadOnlySpan<Base>.CopyTo(System.Span<Base>)" IL_0058: ldloc.3 IL_0059: ldloca.s V_1 IL_005b: call "int System.ReadOnlySpan<Base>.Length.get" IL_0060: add IL_0061: stloc.3 IL_0062: ldloca.s V_5 IL_0064: ldloc.2 IL_0065: call "System.ReadOnlySpan<Base>..ctor(Base[])" IL_006a: ldloca.s V_5 IL_006c: ldloc.s V_4 IL_006e: newobj "System.Span<Base>..ctor(Base[])" IL_0073: stloc.s V_7 IL_0075: ldloca.s V_7 IL_0077: ldloc.3 IL_0078: ldloca.s V_5 IL_007a: call "int System.ReadOnlySpan<Base>.Length.get" IL_007f: call "System.Span<Base> System.Span<Base>.Slice(int, int)" IL_0084: call "void System.ReadOnlySpan<Base>.CopyTo(System.Span<Base>)" IL_0089: ldloc.3 IL_008a: ldloca.s V_5 IL_008c: call "int System.ReadOnlySpan<Base>.Length.get" IL_0091: add IL_0092: stloc.3 IL_0093: ldloc.s V_4 IL_0095: call "System.ReadOnlySpan<Base> System.ReadOnlySpan<Base>.op_Implicit(Base[])" IL_009a: stloc.s V_5 IL_009c: ldloca.s V_5 IL_009e: call "void CollectionExtensions.Report<Base>(in System.ReadOnlySpan<Base>)" IL_00a3: ret } """); } [Fact] public void MDArrayToList_Spreads() { var source = """ using System.Collections.Generic; class C { static void Main() { M(new [,] { { 1, 2, 3 } }, new [,] { { 4, 5, 6 } }); } static void M(int[,] e1, int[,] e2) { List<int> result = [..e1, ..e2]; result.Report(); } } """; var verifier = CompileAndVerify(new[] { source, s_collectionExtensionsWithSpan }, verify: Verification.FailsPEVerify, expectedOutput: IncludeExpectedOutput("[1, 2, 3, 4, 5, 6],"), targetFramework: TargetFramework.Net80); verifier.VerifyDiagnostics(); verifier.VerifyIL("C.M", """ { // Code size 252 (0xfc) .maxstack 3 .locals init (int[,] V_0, int V_1, System.Collections.Generic.List<int> V_2, System.Span<int> V_3, int V_4, int[,] V_5, int V_6, int V_7, int V_8, int V_9, int V_10) IL_0000: ldarg.0 IL_0001: ldarg.1 IL_0002: stloc.0 IL_0003: dup IL_0004: callvirt "int System.Array.Length.get" IL_0009: ldloc.0 IL_000a: callvirt "int System.Array.Length.get" IL_000f: add IL_0010: stloc.1 IL_0011: ldloc.1 IL_0012: newobj "System.Collections.Generic.List<int>..ctor(int)" IL_0017: stloc.2 IL_0018: ldloc.2 IL_0019: ldloc.1 IL_001a: call "void System.Runtime.InteropServices.CollectionsMarshal.SetCount<int>(System.Collections.Generic.List<int>, int)" IL_001f: ldloc.2 IL_0020: call "System.Span<int> System.Runtime.InteropServices.CollectionsMarshal.AsSpan<int>(System.Collections.Generic.List<int>)" IL_0025: stloc.3 IL_0026: ldc.i4.0 IL_0027: stloc.s V_4 IL_0029: stloc.s V_5 IL_002b: ldloc.s V_5 IL_002d: ldc.i4.0 IL_002e: callvirt "int System.Array.GetUpperBound(int)" IL_0033: stloc.s V_6 IL_0035: ldloc.s V_5 IL_0037: ldc.i4.1 IL_0038: callvirt "int System.Array.GetUpperBound(int)" IL_003d: stloc.s V_7 IL_003f: ldloc.s V_5 IL_0041: ldc.i4.0 IL_0042: callvirt "int System.Array.GetLowerBound(int)" IL_0047: stloc.s V_8 IL_0049: br.s IL_0088 IL_004b: ldloc.s V_5 IL_004d: ldc.i4.1 IL_004e: callvirt "int System.Array.GetLowerBound(int)" IL_0053: stloc.s V_9 IL_0055: br.s IL_007c IL_0057: ldloc.s V_5 IL_0059: ldloc.s V_8 IL_005b: ldloc.s V_9 IL_005d: call "int[*,*].Get" IL_0062: stloc.s V_10 IL_0064: ldloca.s V_3 IL_0066: ldloc.s V_4 IL_0068: call "ref int System.Span<int>.this[int].get" IL_006d: ldloc.s V_10 IL_006f: stind.i4 IL_0070: ldloc.s V_4 IL_0072: ldc.i4.1 IL_0073: add IL_0074: stloc.s V_4 IL_0076: ldloc.s V_9 IL_0078: ldc.i4.1 IL_0079: add IL_007a: stloc.s V_9 IL_007c: ldloc.s V_9 IL_007e: ldloc.s V_7 IL_0080: ble.s IL_0057 IL_0082: ldloc.s V_8 IL_0084: ldc.i4.1 IL_0085: add IL_0086: stloc.s V_8 IL_0088: ldloc.s V_8 IL_008a: ldloc.s V_6 IL_008c: ble.s IL_004b IL_008e: ldloc.0 IL_008f: stloc.s V_5 IL_0091: ldloc.s V_5 IL_0093: ldc.i4.0 IL_0094: callvirt "int System.Array.GetUpperBound(int)" IL_0099: stloc.s V_7 IL_009b: ldloc.s V_5 IL_009d: ldc.i4.1 IL_009e: callvirt "int System.Array.GetUpperBound(int)" IL_00a3: stloc.s V_6 IL_00a5: ldloc.s V_5 IL_00a7: ldc.i4.0 IL_00a8: callvirt "int System.Array.GetLowerBound(int)" IL_00ad: stloc.s V_8 IL_00af: br.s IL_00ee IL_00b1: ldloc.s V_5 IL_00b3: ldc.i4.1 IL_00b4: callvirt "int System.Array.GetLowerBound(int)" IL_00b9: stloc.s V_9 IL_00bb: br.s IL_00e2 IL_00bd: ldloc.s V_5 IL_00bf: ldloc.s V_8 IL_00c1: ldloc.s V_9 IL_00c3: call "int[*,*].Get" IL_00c8: stloc.s V_10 IL_00ca: ldloca.s V_3 IL_00cc: ldloc.s V_4 IL_00ce: call "ref int System.Span<int>.this[int].get" IL_00d3: ldloc.s V_10 IL_00d5: stind.i4 IL_00d6: ldloc.s V_4 IL_00d8: ldc.i4.1 IL_00d9: add IL_00da: stloc.s V_4 IL_00dc: ldloc.s V_9 IL_00de: ldc.i4.1 IL_00df: add IL_00e0: stloc.s V_9 IL_00e2: ldloc.s V_9 IL_00e4: ldloc.s V_6 IL_00e6: ble.s IL_00bd IL_00e8: ldloc.s V_8 IL_00ea: ldc.i4.1 IL_00eb: add IL_00ec: stloc.s V_8 IL_00ee: ldloc.s V_8 IL_00f0: ldloc.s V_7 IL_00f2: ble.s IL_00b1 IL_00f4: ldloc.2 IL_00f5: ldc.i4.0 IL_00f6: call "void CollectionExtensions.Report(object, bool)" IL_00fb: ret } """); } [Fact] public void ListToList_Spreads() { var source = """ using System.Collections.Generic; class C { static void Main() { M([1, 2, 3], [4, 5, 6]); } static void M(List<int> e1, List<int> e2) { List<int> result = [..e1, ..e2]; result.Report(); } } """; var verifier = CompileAndVerify(new[] { source, s_collectionExtensionsWithSpan }, expectedOutput: IncludeExpectedOutput("[1, 2, 3, 4, 5, 6],"), targetFramework: TargetFramework.Net80, verify: Verification.Skipped); verifier.VerifyDiagnostics(); verifier.VerifyIL("C.M", """ { // Code size 134 (0x86) .maxstack 4 .locals init (System.Collections.Generic.List<int> V_0, int V_1, System.Collections.Generic.List<int> V_2, System.Span<int> V_3, int V_4, System.Span<int> V_5, System.Span<int> V_6) IL_0000: ldarg.0 IL_0001: ldarg.1 IL_0002: stloc.0 IL_0003: dup IL_0004: callvirt "int System.Collections.Generic.List<int>.Count.get" IL_0009: ldloc.0 IL_000a: callvirt "int System.Collections.Generic.List<int>.Count.get" IL_000f: add IL_0010: stloc.1 IL_0011: ldloc.1 IL_0012: newobj "System.Collections.Generic.List<int>..ctor(int)" IL_0017: stloc.2 IL_0018: ldloc.2 IL_0019: ldloc.1 IL_001a: call "void System.Runtime.InteropServices.CollectionsMarshal.SetCount<int>(System.Collections.Generic.List<int>, int)" IL_001f: ldloc.2 IL_0020: call "System.Span<int> System.Runtime.InteropServices.CollectionsMarshal.AsSpan<int>(System.Collections.Generic.List<int>)" IL_0025: stloc.3 IL_0026: ldc.i4.0 IL_0027: stloc.s V_4 IL_0029: call "System.Span<int> System.Runtime.InteropServices.CollectionsMarshal.AsSpan<int>(System.Collections.Generic.List<int>)" IL_002e: stloc.s V_5 IL_0030: ldloca.s V_5 IL_0032: ldloca.s V_3 IL_0034: ldloc.s V_4 IL_0036: ldloca.s V_5 IL_0038: call "int System.Span<int>.Length.get" IL_003d: call "System.Span<int> System.Span<int>.Slice(int, int)" IL_0042: call "void System.Span<int>.CopyTo(System.Span<int>)" IL_0047: ldloc.s V_4 IL_0049: ldloca.s V_5 IL_004b: call "int System.Span<int>.Length.get" IL_0050: add IL_0051: stloc.s V_4 IL_0053: ldloc.0 IL_0054: call "System.Span<int> System.Runtime.InteropServices.CollectionsMarshal.AsSpan<int>(System.Collections.Generic.List<int>)" IL_0059: stloc.s V_6 IL_005b: ldloca.s V_6 IL_005d: ldloca.s V_3 IL_005f: ldloc.s V_4 IL_0061: ldloca.s V_6 IL_0063: call "int System.Span<int>.Length.get" IL_0068: call "System.Span<int> System.Span<int>.Slice(int, int)" IL_006d: call "void System.Span<int>.CopyTo(System.Span<int>)" IL_0072: ldloc.s V_4 IL_0074: ldloca.s V_6 IL_0076: call "int System.Span<int>.Length.get" IL_007b: add IL_007c: stloc.s V_4 IL_007e: ldloc.2 IL_007f: ldc.i4.0 IL_0080: call "void CollectionExtensions.Report(object, bool)" IL_0085: ret } """); } [Fact] public void List_Spread_Mutation() { var source = """ using System.Collections.Generic; class C { static void Main() { M([1, 2, 3]); } static List<int> Pop(List<int> e1, out int i) { i = e1[e1.Count-1]; e1.RemoveAt(e1.Count-1); return e1; } static void M(List<int> e1) { List<int> result = [..e1, ..Pop(e1, out var i), i]; result.Report(); } } """; var expectedOutput = IncludeExpectedOutput("[1, 2, 1, 2, 3],"); var verifier = CompileAndVerify(new[] { source, s_collectionExtensionsWithSpan }, expectedOutput: expectedOutput, targetFramework: TargetFramework.Net80, verify: Verification.Skipped); verifier.VerifyDiagnostics(); verifier.VerifyIL("C.M", """ { // Code size 155 (0x9b) .maxstack 5 .locals init (int V_0, //i System.Collections.Generic.List<int> V_1, System.Collections.Generic.List<int> V_2, int V_3, System.Span<int> V_4, int V_5, System.Span<int> V_6, System.Span<int> V_7) IL_0000: ldarg.0 IL_0001: stloc.1 IL_0002: ldarg.0 IL_0003: ldloca.s V_0 IL_0005: call "System.Collections.Generic.List<int> C.Pop(System.Collections.Generic.List<int>, out int)" IL_000a: stloc.2 IL_000b: ldc.i4.1 IL_000c: ldloc.1 IL_000d: callvirt "int System.Collections.Generic.List<int>.Count.get" IL_0012: ldloc.2 IL_0013: callvirt "int System.Collections.Generic.List<int>.Count.get" IL_0018: add IL_0019: add IL_001a: stloc.3 IL_001b: ldloc.3 IL_001c: newobj "System.Collections.Generic.List<int>..ctor(int)" IL_0021: dup IL_0022: ldloc.3 IL_0023: call "void System.Runtime.InteropServices.CollectionsMarshal.SetCount<int>(System.Collections.Generic.List<int>, int)" IL_0028: dup IL_0029: call "System.Span<int> System.Runtime.InteropServices.CollectionsMarshal.AsSpan<int>(System.Collections.Generic.List<int>)" IL_002e: stloc.s V_4 IL_0030: ldc.i4.0 IL_0031: stloc.s V_5 IL_0033: ldloc.1 IL_0034: call "System.Span<int> System.Runtime.InteropServices.CollectionsMarshal.AsSpan<int>(System.Collections.Generic.List<int>)" IL_0039: stloc.s V_6 IL_003b: ldloca.s V_6 IL_003d: ldloca.s V_4 IL_003f: ldloc.s V_5 IL_0041: ldloca.s V_6 IL_0043: call "int System.Span<int>.Length.get" IL_0048: call "System.Span<int> System.Span<int>.Slice(int, int)" IL_004d: call "void System.Span<int>.CopyTo(System.Span<int>)" IL_0052: ldloc.s V_5 IL_0054: ldloca.s V_6 IL_0056: call "int System.Span<int>.Length.get" IL_005b: add IL_005c: stloc.s V_5 IL_005e: ldloc.2 IL_005f: call "System.Span<int> System.Runtime.InteropServices.CollectionsMarshal.AsSpan<int>(System.Collections.Generic.List<int>)" IL_0064: stloc.s V_7 IL_0066: ldloca.s V_7 IL_0068: ldloca.s V_4 IL_006a: ldloc.s V_5 IL_006c: ldloca.s V_7 IL_006e: call "int System.Span<int>.Length.get" IL_0073: call "System.Span<int> System.Span<int>.Slice(int, int)" IL_0078: call "void System.Span<int>.CopyTo(System.Span<int>)" IL_007d: ldloc.s V_5 IL_007f: ldloca.s V_7 IL_0081: call "int System.Span<int>.Length.get" IL_0086: add IL_0087: stloc.s V_5 IL_0089: ldloca.s V_4 IL_008b: ldloc.s V_5 IL_008d: call "ref int System.Span<int>.this[int].get" IL_0092: ldloc.0 IL_0093: stind.i4 IL_0094: ldc.i4.0 IL_0095: call "void CollectionExtensions.Report(object, bool)" IL_009a: ret } """); var comp = CreateCompilation(new[] { source, s_collectionExtensionsWithSpan }, options: TestOptions.ReleaseExe, targetFramework: TargetFramework.Net80); comp.MakeMemberMissing(WellKnownMember.System_Runtime_InteropServices_CollectionsMarshal__AsSpan_T); verifier = CompileAndVerify(comp, expectedOutput: expectedOutput, verify: Verification.Skipped); verifier.VerifyIL("C.M", """ { // Code size 59 (0x3b) .maxstack 3 .locals init (int V_0, //i System.Collections.Generic.List<int> V_1, System.Collections.Generic.List<int> V_2) IL_0000: ldarg.0 IL_0001: stloc.1 IL_0002: ldarg.0 IL_0003: ldloca.s V_0 IL_0005: call "System.Collections.Generic.List<int> C.Pop(System.Collections.Generic.List<int>, out int)" IL_000a: stloc.2 IL_000b: ldc.i4.1 IL_000c: ldloc.1 IL_000d: callvirt "int System.Collections.Generic.List<int>.Count.get" IL_0012: ldloc.2 IL_0013: callvirt "int System.Collections.Generic.List<int>.Count.get" IL_0018: add IL_0019: add IL_001a: newobj "System.Collections.Generic.List<int>..ctor(int)" IL_001f: dup IL_0020: ldloc.1 IL_0021: callvirt "void System.Collections.Generic.List<int>.AddRange(System.Collections.Generic.IEnumerable<int>)" IL_0026: dup IL_0027: ldloc.2 IL_0028: callvirt "void System.Collections.Generic.List<int>.AddRange(System.Collections.Generic.IEnumerable<int>)" IL_002d: dup IL_002e: ldloc.0 IL_002f: callvirt "void System.Collections.Generic.List<int>.Add(int)" IL_0034: ldc.i4.0 IL_0035: call "void CollectionExtensions.Report(object, bool)" IL_003a: ret } """); } [Theory] [CombinatorialData] public void SingleSpread_ArrayToArray_WellKnownMemberMissing_01(bool readOnlySpanMembersMissing) { var source = """ class C { static void Main() { int[] arr = [1, 2, 3]; arr.Report(); int[] arr1 = [..arr]; arr1.Report(); } } """; var comp = CreateCompilation([source, s_collectionExtensions], targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseExe); if (readOnlySpanMembersMissing) { comp.MakeMemberMissing(WellKnownMember.System_ReadOnlySpan_T__ctor_Array); comp.MakeMemberMissing(WellKnownMember.System_ReadOnlySpan_T__ToArray); } var verifier = CompileAndVerify(comp, verify: Verification.Skipped, expectedOutput: IncludeExpectedOutput("[1, 2, 3], [1, 2, 3],")); verifier.VerifyDiagnostics(); verifier.VerifyIL("C.Main", """ { // Code size 36 (0x24) .maxstack 3 IL_0000: ldc.i4.3 IL_0001: newarr "int" IL_0006: dup IL_0007: ldtoken "<PrivateImplementationDetails>.__StaticArrayInitTypeSize=12 <PrivateImplementationDetails>.4636993D3E1DA4E9D6B8F87B79E8F7C6D018580D52661950EABC3845C5897A4D" IL_000c: call "void System.Runtime.CompilerServices.RuntimeHelpers.InitializeArray(System.Array, System.RuntimeFieldHandle)" IL_0011: dup IL_0012: ldc.i4.0 IL_0013: call "void CollectionExtensions.Report(object, bool)" IL_0018: call "int[] System.Linq.Enumerable.ToArray<int>(System.Collections.Generic.IEnumerable<int>)" IL_001d: ldc.i4.0 IL_001e: call "void CollectionExtensions.Report(object, bool)" IL_0023: ret } """); } [Fact] public void SingleSpread_ArrayToArray_WellKnownMemberMissing_02() { var source = """ class C { static void Main() { int[] arr = [1, 2, 3]; arr.Report(); int[] arr1 = [..arr]; arr1.Report(); } } """; var comp = CreateCompilation([source, s_collectionExtensions], targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseExe); comp.MakeMemberMissing(WellKnownMember.System_Linq_Enumerable__ToArray); var verifier = CompileAndVerify(comp, verify: Verification.Skipped, expectedOutput: IncludeExpectedOutput("[1, 2, 3], [1, 2, 3],")); verifier.VerifyDiagnostics(); verifier.VerifyIL("C.Main", """ { // Code size 44 (0x2c) .maxstack 3 .locals init (System.ReadOnlySpan<int> V_0) IL_0000: ldc.i4.3 IL_0001: newarr "int" IL_0006: dup IL_0007: ldtoken "<PrivateImplementationDetails>.__StaticArrayInitTypeSize=12 <PrivateImplementationDetails>.4636993D3E1DA4E9D6B8F87B79E8F7C6D018580D52661950EABC3845C5897A4D" IL_000c: call "void System.Runtime.CompilerServices.RuntimeHelpers.InitializeArray(System.Array, System.RuntimeFieldHandle)" IL_0011: dup IL_0012: ldc.i4.0 IL_0013: call "void CollectionExtensions.Report(object, bool)" IL_0018: newobj "System.ReadOnlySpan<int>..ctor(int[])" IL_001d: stloc.0 IL_001e: ldloca.s V_0 IL_0020: call "int[] System.ReadOnlySpan<int>.ToArray()" IL_0025: ldc.i4.0 IL_0026: call "void CollectionExtensions.Report(object, bool)" IL_002b: ret } """); } [Fact] public void SingleSpread_ArrayToArray_WellKnownMemberMissing_03() { var source = """ class C { static void Main() { int[] arr = [1, 2, 3]; arr.Report(); int[] arr1 = [..arr]; arr1.Report(); } } """; var comp = CreateCompilation([source, s_collectionExtensions], targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseExe); comp.MakeMemberMissing(WellKnownMember.System_Linq_Enumerable__ToArray); comp.MakeMemberMissing(WellKnownMember.System_ReadOnlySpan_T__ctor_Array); comp.MakeMemberMissing(WellKnownMember.System_ReadOnlySpan_T__ToArray); var verifier = CompileAndVerify(comp, verify: Verification.Skipped, expectedOutput: IncludeExpectedOutput("[1, 2, 3], [1, 2, 3],")); verifier.VerifyDiagnostics(); verifier.VerifyIL("C.Main", """ { // Code size 72 (0x48) .maxstack 3 .locals init (int V_0, int[] V_1, int[] V_2, int V_3, int V_4) IL_0000: ldc.i4.3 IL_0001: newarr "int" IL_0006: dup IL_0007: ldtoken "<PrivateImplementationDetails>.__StaticArrayInitTypeSize=12 <PrivateImplementationDetails>.4636993D3E1DA4E9D6B8F87B79E8F7C6D018580D52661950EABC3845C5897A4D" IL_000c: call "void System.Runtime.CompilerServices.RuntimeHelpers.InitializeArray(System.Array, System.RuntimeFieldHandle)" IL_0011: dup IL_0012: ldc.i4.0 IL_0013: call "void CollectionExtensions.Report(object, bool)" IL_0018: ldc.i4.0 IL_0019: stloc.0 IL_001a: dup IL_001b: ldlen IL_001c: conv.i4 IL_001d: newarr "int" IL_0022: stloc.1 IL_0023: stloc.2 IL_0024: ldc.i4.0 IL_0025: stloc.3 IL_0026: br.s IL_003a IL_0028: ldloc.2 IL_0029: ldloc.3 IL_002a: ldelem.i4 IL_002b: stloc.s V_4 IL_002d: ldloc.1 IL_002e: ldloc.0 IL_002f: ldloc.s V_4 IL_0031: stelem.i4 IL_0032: ldloc.0 IL_0033: ldc.i4.1 IL_0034: add IL_0035: stloc.0 IL_0036: ldloc.3 IL_0037: ldc.i4.1 IL_0038: add IL_0039: stloc.3 IL_003a: ldloc.3 IL_003b: ldloc.2 IL_003c: ldlen IL_003d: conv.i4 IL_003e: blt.s IL_0028 IL_0040: ldloc.1 IL_0041: ldc.i4.0 IL_0042: call "void CollectionExtensions.Report(object, bool)" IL_0047: ret } """); } [Fact] public void SingleSpread_ArrayToArray_Covariant() { var source = """ using System; Console.Write(C.M(["a"])[0]); class C { public static object[] M(string[] arr) => [..arr]; } """; var verifier = CompileAndVerify(source, expectedOutput: "a"); verifier.VerifyIL("C.M", """ { // Code size 7 (0x7) .maxstack 1 IL_0000: ldarg.0 IL_0001: call "object[] System.Linq.Enumerable.ToArray<object>(System.Collections.Generic.IEnumerable<object>)" IL_0006: ret } """); } [Fact] public void SingleSpread_ArrayToSpan_CovariantVersusInvariant() { var source = """ using System; string[] arr = ["a"]; C.M1(arr); C.M2(arr); class C { public static void M1(string[] arr) { Span<object> span = [..arr]; span.Report(); } public static void M2(object[] arr) { Span<object> span = [..arr]; span.Report(); } } """; var verifier = CompileAndVerify([source, s_collectionExtensionsWithSpan], expectedOutput: IncludeExpectedOutput("[a], [a], "), targetFramework: TargetFramework.Net80, verify: Verification.Skipped); var expectedIL = """ { // Code size 25 (0x19) .maxstack 1 .locals init (System.ReadOnlySpan<object> V_0) IL_0000: ldarg.0 IL_0001: call "object[] System.Linq.Enumerable.ToArray<object>(System.Collections.Generic.IEnumerable<object>)" IL_0006: newobj "System.Span<object>..ctor(object[])" IL_000b: call "System.ReadOnlySpan<object> System.Span<object>.op_Implicit(System.Span<object>)" IL_0010: stloc.0 IL_0011: ldloca.s V_0 IL_0013: call "void CollectionExtensions.Report<object>(in System.ReadOnlySpan<object>)" IL_0018: ret } """; verifier.VerifyIL("C.M1", expectedIL); verifier.VerifyIL("C.M2", expectedIL); } [Fact] public void SingleSpread_ArrayToReadOnlySpan_CovariantVersusInvariant() { var source = """ using System; string[] arr = ["a"]; C.M1(arr); C.M2(arr); class C { public static void M1(string[] arr) { ReadOnlySpan<object> span = [..arr]; span.Report(); } public static void M2(object[] arr) { ReadOnlySpan<object> span = [..arr]; span.Report(); } } """; var verifier = CompileAndVerify([source, s_collectionExtensionsWithSpan], expectedOutput: IncludeExpectedOutput("[a], [a], "), targetFramework: TargetFramework.Net80, verify: Verification.Skipped); var expectedIL = """ { // Code size 21 (0x15) .maxstack 2 .locals init (System.ReadOnlySpan<object> V_0) //span IL_0000: ldloca.s V_0 IL_0002: ldarg.0 IL_0003: call "object[] System.Linq.Enumerable.ToArray<object>(System.Collections.Generic.IEnumerable<object>)" IL_0008: call "System.ReadOnlySpan<object>..ctor(object[])" IL_000d: ldloca.s V_0 IL_000f: call "void CollectionExtensions.Report<object>(in System.ReadOnlySpan<object>)" IL_0014: ret } """; verifier.VerifyIL("C.M1", expectedIL); verifier.VerifyIL("C.M2", expectedIL); } [Fact] public void SingleSpread_ListToArray_Covariant() { // Linq ToArray method is applicable, but we do not use it in this case, // because the List<string> has a struct enumerator and doesn't implement ICollection<object>. // If we could get a Span<string> out of the List, then covariant-convert, we could use ReadOnlySpan<object>.ToArray() here. // https://github.com/dotnet/roslyn/issues/71106 var source = """ using System; using System.Collections.Generic; List<string> l = ["a"]; var o = C.M(l); Console.Write(o[0]); o[0] = 1; Console.Write(o[0]); class C { public static object[] M(List<string> list) => [..list]; } """; var verifier = CompileAndVerify(source, expectedOutput: "a1"); verifier.VerifyIL("C.M", """ { // Code size 66 (0x42) .maxstack 3 .locals init (int V_0, object[] V_1, System.Collections.Generic.List<string>.Enumerator V_2, string V_3) IL_0000: ldarg.0 IL_0001: ldc.i4.0 IL_0002: stloc.0 IL_0003: dup IL_0004: callvirt "int System.Collections.Generic.List<string>.Count.get" IL_0009: newarr "object" IL_000e: stloc.1 IL_000f: callvirt "System.Collections.Generic.List<string>.Enumerator System.Collections.Generic.List<string>.GetEnumerator()" IL_0014: stloc.2 .try { IL_0015: br.s IL_0027 IL_0017: ldloca.s V_2 IL_0019: call "string System.Collections.Generic.List<string>.Enumerator.Current.get" IL_001e: stloc.3 IL_001f: ldloc.1 IL_0020: ldloc.0 IL_0021: ldloc.3 IL_0022: stelem.ref IL_0023: ldloc.0 IL_0024: ldc.i4.1 IL_0025: add IL_0026: stloc.0 IL_0027: ldloca.s V_2 IL_0029: call "bool System.Collections.Generic.List<string>.Enumerator.MoveNext()" IL_002e: brtrue.s IL_0017 IL_0030: leave.s IL_0040 } finally { IL_0032: ldloca.s V_2 IL_0034: constrained. "System.Collections.Generic.List<string>.Enumerator" IL_003a: callvirt "void System.IDisposable.Dispose()" IL_003f: endfinally } IL_0040: ldloc.1 IL_0041: ret } """); } [Fact] public void SingleSpread_ListToArray_SameRuntimeTypes() { var source = """ using System; using System.Collections.Generic; List<object> l = ["a"]; var d = C.M(l); Console.Write(d[0]); d[0] = 1; Console.Write(d[0]); class C { public static dynamic[] M(List<object> list) => [..list]; } """; var verifier = CompileAndVerify(source, targetFramework: TargetFramework.Net80, expectedOutput: IncludeExpectedOutput("a1"), verify: Verification.Skipped); verifier.VerifyIL("C.M", """ { // Code size 7 (0x7) .maxstack 1 IL_0000: ldarg.0 IL_0001: callvirt "object[] System.Collections.Generic.List<object>.ToArray()" IL_0006: ret } """); } [Fact] public void SingleSpread_ListToArray_Boxing() { var source = """ using System; using System.Collections.Generic; List<int> l = [1]; var o = C.M(l); Console.Write(o[0]); o[0] = "a"; Console.Write(o[0]); class C { public static object[] M(List<int> list) => [..list]; } """; var verifier = CompileAndVerify(source, expectedOutput: "1a"); verifier.VerifyIL("C.M", """ { // Code size 71 (0x47) .maxstack 3 .locals init (int V_0, object[] V_1, System.Collections.Generic.List<int>.Enumerator V_2, int V_3) IL_0000: ldarg.0 IL_0001: ldc.i4.0 IL_0002: stloc.0 IL_0003: dup IL_0004: callvirt "int System.Collections.Generic.List<int>.Count.get" IL_0009: newarr "object" IL_000e: stloc.1 IL_000f: callvirt "System.Collections.Generic.List<int>.Enumerator System.Collections.Generic.List<int>.GetEnumerator()" IL_0014: stloc.2 .try { IL_0015: br.s IL_002c IL_0017: ldloca.s V_2 IL_0019: call "int System.Collections.Generic.List<int>.Enumerator.Current.get" IL_001e: stloc.3 IL_001f: ldloc.1 IL_0020: ldloc.0 IL_0021: ldloc.3 IL_0022: box "int" IL_0027: stelem.ref IL_0028: ldloc.0 IL_0029: ldc.i4.1 IL_002a: add IL_002b: stloc.0 IL_002c: ldloca.s V_2 IL_002e: call "bool System.Collections.Generic.List<int>.Enumerator.MoveNext()" IL_0033: brtrue.s IL_0017 IL_0035: leave.s IL_0045 } finally { IL_0037: ldloca.s V_2 IL_0039: constrained. "System.Collections.Generic.List<int>.Enumerator" IL_003f: callvirt "void System.IDisposable.Dispose()" IL_0044: endfinally } IL_0045: ldloc.1 IL_0046: ret } """); } [Fact] public void SingleSpread_ListToIEnumerable_Covariant() { var source = """ using System; using System.Collections.Generic; foreach (var item in C.M(["a"])) Console.Write(item); class C { public static IEnumerable<object> M(List<string> list) => [..list]; } """; var verifier = CompileAndVerify(source, expectedOutput: "a"); verifier.VerifyIL("C.M", """ { // Code size 12 (0xc) .maxstack 1 IL_0000: ldarg.0 IL_0001: callvirt "string[] System.Collections.Generic.List<string>.ToArray()" IL_0006: newobj "<>z__ReadOnlyArray<object>..ctor(object[])" IL_000b: ret } """); } [Fact] public void SingleSpread_ListToIEnumerable_SameRuntimeType() { var source = """ using System; using System.Collections.Generic; foreach (var item in C.M(["a"])) Console.Write(item); class C { public static IEnumerable<dynamic> M(List<object> list) => [..list]; } """; var verifier = CompileAndVerify(source, targetFramework: TargetFramework.Net80, expectedOutput: IncludeExpectedOutput("a"), verify: Verification.Skipped); verifier.VerifyIL("C.M", """ { // Code size 12 (0xc) .maxstack 1 IL_0000: ldarg.0 IL_0001: callvirt "object[] System.Collections.Generic.List<object>.ToArray()" IL_0006: newobj "<>z__ReadOnlyArray<dynamic>..ctor(dynamic[])" IL_000b: ret } """); } [Fact] public void SingleSpread_ListToIEnumerable_Boxing() { var source = """ using System; using System.Collections.Generic; foreach (var item in C.M([1])) Console.Write(item); class C { public static IEnumerable<object> M(List<int> list) => [..list]; } """; var verifier = CompileAndVerify(source, expectedOutput: "1"); verifier.VerifyIL("C.M", """ { // Code size 76 (0x4c) .maxstack 3 .locals init (int V_0, object[] V_1, System.Collections.Generic.List<int>.Enumerator V_2, int V_3) IL_0000: ldarg.0 IL_0001: ldc.i4.0 IL_0002: stloc.0 IL_0003: dup IL_0004: callvirt "int System.Collections.Generic.List<int>.Count.get" IL_0009: newarr "object" IL_000e: stloc.1 IL_000f: callvirt "System.Collections.Generic.List<int>.Enumerator System.Collections.Generic.List<int>.GetEnumerator()" IL_0014: stloc.2 .try { IL_0015: br.s IL_002c IL_0017: ldloca.s V_2 IL_0019: call "int System.Collections.Generic.List<int>.Enumerator.Current.get" IL_001e: stloc.3 IL_001f: ldloc.1 IL_0020: ldloc.0 IL_0021: ldloc.3 IL_0022: box "int" IL_0027: stelem.ref IL_0028: ldloc.0 IL_0029: ldc.i4.1 IL_002a: add IL_002b: stloc.0 IL_002c: ldloca.s V_2 IL_002e: call "bool System.Collections.Generic.List<int>.Enumerator.MoveNext()" IL_0033: brtrue.s IL_0017 IL_0035: leave.s IL_0045 } finally { IL_0037: ldloca.s V_2 IL_0039: constrained. "System.Collections.Generic.List<int>.Enumerator" IL_003f: callvirt "void System.IDisposable.Dispose()" IL_0044: endfinally } IL_0045: ldloc.1 IL_0046: newobj "<>z__ReadOnlyArray<object>..ctor(object[])" IL_004b: ret } """); } [Fact] public void SingleSpread_ArrayToArray_Boxing() { var source = """ using System; Console.Write(C.M([1])[0]); class C { public static object[] M(int[] arr) => [..arr]; } """; var verifier = CompileAndVerify(source, expectedOutput: "1"); verifier.VerifyIL("C.M", """ { // Code size 48 (0x30) .maxstack 3 .locals init (int V_0, object[] V_1, int[] V_2, int V_3, int V_4) IL_0000: ldarg.0 IL_0001: ldc.i4.0 IL_0002: stloc.0 IL_0003: dup IL_0004: ldlen IL_0005: conv.i4 IL_0006: newarr "object" IL_000b: stloc.1 IL_000c: stloc.2 IL_000d: ldc.i4.0 IL_000e: stloc.3 IL_000f: br.s IL_0028 IL_0011: ldloc.2 IL_0012: ldloc.3 IL_0013: ldelem.i4 IL_0014: stloc.s V_4 IL_0016: ldloc.1 IL_0017: ldloc.0 IL_0018: ldloc.s V_4 IL_001a: box "int" IL_001f: stelem.ref IL_0020: ldloc.0 IL_0021: ldc.i4.1 IL_0022: add IL_0023: stloc.0 IL_0024: ldloc.3 IL_0025: ldc.i4.1 IL_0026: add IL_0027: stloc.3 IL_0028: ldloc.3 IL_0029: ldloc.2 IL_002a: ldlen IL_002b: conv.i4 IL_002c: blt.s IL_0011 IL_002e: ldloc.1 IL_002f: ret } """); } [Fact] public void SingleSpread_ArrayToIEnumerable_Covariant() { var source = """ using System; using System.Collections.Generic; foreach (var item in C.M(["a"])) Console.Write(item); class C { public static IEnumerable<object> M(string[] arr) => [..arr]; } """; var verifier = CompileAndVerify(source, expectedOutput: "a"); verifier.VerifyIL("C.M", """ { // Code size 12 (0xc) .maxstack 1 IL_0000: ldarg.0 IL_0001: call "object[] System.Linq.Enumerable.ToArray<object>(System.Collections.Generic.IEnumerable<object>)" IL_0006: newobj "<>z__ReadOnlyArray<object>..ctor(object[])" IL_000b: ret } """); } [Fact] public void SingleSpread_IEnumerableToIEnumerable_Covariant() { var source = """ using System; using System.Collections.Generic; foreach (var item in C.M(["a"])) Console.Write(item); class C { public static IEnumerable<object> M(IEnumerable<string> enumerable) => [..enumerable]; } """; // Note: We could use the Linq ToArray method here and save an allocation compared to making a List. // However, it's not that significant, since the current codegen doesn't redundantly copy the elements themselves. var verifier = CompileAndVerify(source, expectedOutput: "a"); verifier.VerifyIL("C.M", """ { // Code size 18 (0x12) .maxstack 3 IL_0000: newobj "System.Collections.Generic.List<object>..ctor()" IL_0005: dup IL_0006: ldarg.0 IL_0007: callvirt "void System.Collections.Generic.List<object>.AddRange(System.Collections.Generic.IEnumerable<object>)" IL_000c: newobj "<>z__ReadOnlyList<object>..ctor(System.Collections.Generic.List<object>)" IL_0011: ret } """); } [Fact] public void MultipleSpreads_WellKnownMemberMissing() { var source = """ class C { static void Main() { int[] arr = [1, 2]; arr.Report(); int[] arr1 = [..arr, ..arr]; arr1.Report(); } } """; var comp = CreateCompilation([source, s_collectionExtensions], targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseExe); var verifier = CompileAndVerify(comp, verify: Verification.Skipped, expectedOutput: IncludeExpectedOutput("[1, 2], [1, 2, 1, 2],")); verifier.VerifyDiagnostics(); verifier.VerifyIL("C.Main", """ { // Code size 143 (0x8f) .maxstack 4 .locals init (int[] V_0, int[] V_1, int V_2, int[] V_3, System.ReadOnlySpan<int> V_4, System.ReadOnlySpan<int> V_5, System.Span<int> V_6, System.Span<int> V_7) IL_0000: ldc.i4.2 IL_0001: newarr "int" IL_0006: dup IL_0007: ldc.i4.0 IL_0008: ldc.i4.1 IL_0009: stelem.i4 IL_000a: dup IL_000b: ldc.i4.1 IL_000c: ldc.i4.2 IL_000d: stelem.i4 IL_000e: dup IL_000f: ldc.i4.0 IL_0010: call "void CollectionExtensions.Report(object, bool)" IL_0015: dup IL_0016: stloc.0 IL_0017: stloc.1 IL_0018: ldc.i4.0 IL_0019: stloc.2 IL_001a: ldloc.0 IL_001b: ldlen IL_001c: conv.i4 IL_001d: ldloc.1 IL_001e: ldlen IL_001f: conv.i4 IL_0020: add IL_0021: newarr "int" IL_0026: stloc.3 IL_0027: ldloca.s V_4 IL_0029: ldloc.0 IL_002a: call "System.ReadOnlySpan<int>..ctor(int[])" IL_002f: ldloca.s V_4 IL_0031: ldloc.3 IL_0032: newobj "System.Span<int>..ctor(int[])" IL_0037: stloc.s V_6 IL_0039: ldloca.s V_6 IL_003b: ldloc.2 IL_003c: ldloca.s V_4 IL_003e: call "int System.ReadOnlySpan<int>.Length.get" IL_0043: call "System.Span<int> System.Span<int>.Slice(int, int)" IL_0048: call "void System.ReadOnlySpan<int>.CopyTo(System.Span<int>)" IL_004d: ldloc.2 IL_004e: ldloca.s V_4 IL_0050: call "int System.ReadOnlySpan<int>.Length.get" IL_0055: add IL_0056: stloc.2 IL_0057: ldloca.s V_5 IL_0059: ldloc.1 IL_005a: call "System.ReadOnlySpan<int>..ctor(int[])" IL_005f: ldloca.s V_5 IL_0061: ldloc.3 IL_0062: newobj "System.Span<int>..ctor(int[])" IL_0067: stloc.s V_7 IL_0069: ldloca.s V_7 IL_006b: ldloc.2 IL_006c: ldloca.s V_5 IL_006e: call "int System.ReadOnlySpan<int>.Length.get" IL_0073: call "System.Span<int> System.Span<int>.Slice(int, int)" IL_0078: call "void System.ReadOnlySpan<int>.CopyTo(System.Span<int>)" IL_007d: ldloc.2 IL_007e: ldloca.s V_5 IL_0080: call "int System.ReadOnlySpan<int>.Length.get" IL_0085: add IL_0086: stloc.2 IL_0087: ldloc.3 IL_0088: ldc.i4.0 IL_0089: call "void CollectionExtensions.Report(object, bool)" IL_008e: ret } """); verifyMissing(WellKnownMember.System_ReadOnlySpan_T__ctor_Array); verifyMissing(WellKnownMember.System_ReadOnlySpan_T__get_Length); verifyMissing(WellKnownMember.System_ReadOnlySpan_T__CopyTo_Span_T); verifyMissing(WellKnownMember.System_Span_T__ctor_Array); verifyMissing(WellKnownMember.System_Span_T__Slice_Int_Int); void verifyMissing(WellKnownMember wellKnownMember) { var comp = CreateCompilation([source, s_collectionExtensions], targetFramework: TargetFramework.Net80, options: TestOptions.ReleaseExe); comp.MakeMemberMissing(wellKnownMember); var verifier = CompileAndVerify(comp, verify: Verification.Skipped, expectedOutput: IncludeExpectedOutput("[1, 2], [1, 2, 1, 2],")); verifier.VerifyDiagnostics(); verifier.VerifyIL("C.Main", """ { // Code size 123 (0x7b) .maxstack 4 .locals init (int[] V_0, int[] V_1, int V_2, int[] V_3, int[] V_4, int V_5, int V_6) IL_0000: ldc.i4.2 IL_0001: newarr "int" IL_0006: dup IL_0007: ldc.i4.0 IL_0008: ldc.i4.1 IL_0009: stelem.i4 IL_000a: dup IL_000b: ldc.i4.1 IL_000c: ldc.i4.2 IL_000d: stelem.i4 IL_000e: dup IL_000f: ldc.i4.0 IL_0010: call "void CollectionExtensions.Report(object, bool)" IL_0015: dup IL_0016: stloc.0 IL_0017: stloc.1 IL_0018: ldc.i4.0 IL_0019: stloc.2 IL_001a: ldloc.0 IL_001b: ldlen IL_001c: conv.i4 IL_001d: ldloc.1 IL_001e: ldlen IL_001f: conv.i4 IL_0020: add IL_0021: newarr "int" IL_0026: stloc.3 IL_0027: ldloc.0 IL_0028: stloc.s V_4 IL_002a: ldc.i4.0 IL_002b: stloc.s V_5 IL_002d: br.s IL_0045 IL_002f: ldloc.s V_4 IL_0031: ldloc.s V_5 IL_0033: ldelem.i4 IL_0034: stloc.s V_6 IL_0036: ldloc.3 IL_0037: ldloc.2 IL_0038: ldloc.s V_6 IL_003a: stelem.i4 IL_003b: ldloc.2 IL_003c: ldc.i4.1 IL_003d: add IL_003e: stloc.2 IL_003f: ldloc.s V_5 IL_0041: ldc.i4.1 IL_0042: add IL_0043: stloc.s V_5 IL_0045: ldloc.s V_5 IL_0047: ldloc.s V_4 IL_0049: ldlen IL_004a: conv.i4 IL_004b: blt.s IL_002f IL_004d: ldloc.1 IL_004e: stloc.s V_4 IL_0050: ldc.i4.0 IL_0051: stloc.s V_5 IL_0053: br.s IL_006b IL_0055: ldloc.s V_4 IL_0057: ldloc.s V_5 IL_0059: ldelem.i4 IL_005a: stloc.s V_6 IL_005c: ldloc.3 IL_005d: ldloc.2 IL_005e: ldloc.s V_6 IL_0060: stelem.i4 IL_0061: ldloc.2 IL_0062: ldc.i4.1 IL_0063: add IL_0064: stloc.2 IL_0065: ldloc.s V_5 IL_0067: ldc.i4.1 IL_0068: add IL_0069: stloc.s V_5 IL_006b: ldloc.s V_5 IL_006d: ldloc.s V_4 IL_006f: ldlen IL_0070: conv.i4 IL_0071: blt.s IL_0055 IL_0073: ldloc.3 IL_0074: ldc.i4.0 IL_0075: call "void CollectionExtensions.Report(object, bool)" IL_007a: ret } """); } } [Fact] public void Spread_RuntimeEquivalentElement() { var source = """ using System; using System.Collections.Generic; class C { System.IntPtr[] M1(List<nint> list) { return [..list]; } List<nint> M2(System.IntPtr[] list) { return [..list]; } nint[] M3(List<IntPtr> list) { return [..list]; } } """; var verifier = CompileAndVerify(source, targetFramework: TargetFramework.Net80, verify: Verification.Skipped); verifier.VerifyDiagnostics(); verifier.VerifyIL("C.M1", """ { // Code size 7 (0x7) .maxstack 1 IL_0000: ldarg.1 IL_0001: callvirt "System.IntPtr[] System.Collections.Generic.List<System.IntPtr>.ToArray()" IL_0006: ret } """); verifier.VerifyIL("C.M2", """ { // Code size 7 (0x7) .maxstack 1 IL_0000: ldarg.1 IL_0001: call "System.Collections.Generic.List<System.IntPtr> System.Linq.Enumerable.ToList<System.IntPtr>(System.Collections.Generic.IEnumerable<System.IntPtr>)" IL_0006: ret } """); verifier.VerifyIL("C.M3", """ { // Code size 7 (0x7) .maxstack 1 IL_0000: ldarg.1 IL_0001: callvirt "System.IntPtr[] System.Collections.Generic.List<System.IntPtr>.ToArray()" IL_0006: ret } """); } [Fact] public void Spread_Array_Await() { var source = """ using System.Threading.Tasks; using System.Collections.Generic; class C { static async Task Main() { List<int> items = [1]; int[] items1 = [..items, ..await M2(), (await M2())[0]]; items1.Report(); } static async Task<int[]> M2() { await Task.Yield(); return new[] { 2 }; } } """; var verifier = CompileAndVerify(new[] { source, s_collectionExtensionsWithSpan }, expectedOutput: IncludeExpectedOutput("[1, 2, 2],"), targetFramework: TargetFramework.Net80, verify: Verification.Skipped); verifier.VerifyDiagnostics(); verifier.VerifyIL("C.<Main>d__0.System.Runtime.CompilerServices.IAsyncStateMachine.MoveNext()", """ { // Code size 498 (0x1f2) .maxstack 4 .locals init (int V_0, System.Collections.Generic.List<int> V_1, //items int[] V_2, int[] V_3, int V_4, System.Span<int> V_5, System.ReadOnlySpan<int> V_6, System.Runtime.CompilerServices.TaskAwaiter<int[]> V_7, System.Span<int> V_8, System.Span<int> V_9, System.Exception V_10) IL_0000: ldarg.0 IL_0001: ldfld "int C.<Main>d__0.<>1__state" IL_0006: stloc.0 .try { IL_0007: ldloc.0 IL_0008: brfalse.s IL_005f IL_000a: ldloc.0 IL_000b: ldc.i4.1 IL_000c: beq IL_0165 IL_0011: ldc.i4.1 IL_0012: newobj "System.Collections.Generic.List<int>..ctor(int)" IL_0017: dup IL_0018: ldc.i4.1 IL_0019: callvirt "void System.Collections.Generic.List<int>.Add(int)" IL_001e: stloc.1 IL_001f: ldarg.0 IL_0020: ldloc.1 IL_0021: stfld "System.Collections.Generic.List<int> C.<Main>d__0.<>7__wrap3" IL_0026: call "System.Threading.Tasks.Task<int[]> C.M2()" IL_002b: callvirt "System.Runtime.CompilerServices.TaskAwaiter<int[]> System.Threading.Tasks.Task<int[]>.GetAwaiter()" IL_0030: stloc.s V_7 IL_0032: ldloca.s V_7 IL_0034: call "bool System.Runtime.CompilerServices.TaskAwaiter<int[]>.IsCompleted.get" IL_0039: brtrue.s IL_007c IL_003b: ldarg.0 IL_003c: ldc.i4.0 IL_003d: dup IL_003e: stloc.0 IL_003f: stfld "int C.<Main>d__0.<>1__state" IL_0044: ldarg.0 IL_0045: ldloc.s V_7 IL_0047: stfld "System.Runtime.CompilerServices.TaskAwaiter<int[]> C.<Main>d__0.<>u__1" IL_004c: ldarg.0 IL_004d: ldflda "System.Runtime.CompilerServices.AsyncTaskMethodBuilder C.<Main>d__0.<>t__builder" IL_0052: ldloca.s V_7 IL_0054: ldarg.0 IL_0055: call "void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.AwaitUnsafeOnCompleted<System.Runtime.CompilerServices.TaskAwaiter<int[]>, C.<Main>d__0>(ref System.Runtime.CompilerServices.TaskAwaiter<int[]>, ref C.<Main>d__0)" IL_005a: leave IL_01f1 IL_005f: ldarg.0 IL_0060: ldfld "System.Runtime.CompilerServices.TaskAwaiter<int[]> C.<Main>d__0.<>u__1" IL_0065: stloc.s V_7 IL_0067: ldarg.0 IL_0068: ldflda "System.Runtime.CompilerServices.TaskAwaiter<int[]> C.<Main>d__0.<>u__1" IL_006d: initobj "System.Runtime.CompilerServices.TaskAwaiter<int[]>" IL_0073: ldarg.0 IL_0074: ldc.i4.m1 IL_0075: dup IL_0076: stloc.0 IL_0077: stfld "int C.<Main>d__0.<>1__state" IL_007c: ldloca.s V_7 IL_007e: call "int[] System.Runtime.CompilerServices.TaskAwaiter<int[]>.GetResult()" IL_0083: stloc.3 IL_0084: ldc.i4.0 IL_0085: stloc.s V_4 IL_0087: ldarg.0 IL_0088: ldc.i4.1 IL_0089: ldarg.0 IL_008a: ldfld "System.Collections.Generic.List<int> C.<Main>d__0.<>7__wrap3" IL_008f: callvirt "int System.Collections.Generic.List<int>.Count.get" IL_0094: ldloc.3 IL_0095: ldlen IL_0096: conv.i4 IL_0097: add IL_0098: add IL_0099: newarr "int" IL_009e: stfld "int[] C.<Main>d__0.<>7__wrap4" IL_00a3: ldarg.0 IL_00a4: ldfld "System.Collections.Generic.List<int> C.<Main>d__0.<>7__wrap3" IL_00a9: call "System.Span<int> System.Runtime.InteropServices.CollectionsMarshal.AsSpan<int>(System.Collections.Generic.List<int>)" IL_00ae: stloc.s V_5 IL_00b0: ldloca.s V_5 IL_00b2: ldarg.0 IL_00b3: ldfld "int[] C.<Main>d__0.<>7__wrap4" IL_00b8: newobj "System.Span<int>..ctor(int[])" IL_00bd: stloc.s V_8 IL_00bf: ldloca.s V_8 IL_00c1: ldloc.s V_4 IL_00c3: ldloca.s V_5 IL_00c5: call "int System.Span<int>.Length.get" IL_00ca: call "System.Span<int> System.Span<int>.Slice(int, int)" IL_00cf: call "void System.Span<int>.CopyTo(System.Span<int>)" IL_00d4: ldloc.s V_4 IL_00d6: ldloca.s V_5 IL_00d8: call "int System.Span<int>.Length.get" IL_00dd: add IL_00de: stloc.s V_4 IL_00e0: ldloc.3 IL_00e1: newobj "System.ReadOnlySpan<int>..ctor(int[])" IL_00e6: stloc.s V_6 IL_00e8: ldloca.s V_6 IL_00ea: ldarg.0 IL_00eb: ldfld "int[] C.<Main>d__0.<>7__wrap4" IL_00f0: newobj "System.Span<int>..ctor(int[])" IL_00f5: stloc.s V_9 IL_00f7: ldloca.s V_9 IL_00f9: ldloc.s V_4 IL_00fb: ldloca.s V_6 IL_00fd: call "int System.ReadOnlySpan<int>.Length.get" IL_0102: call "System.Span<int> System.Span<int>.Slice(int, int)" IL_0107: call "void System.ReadOnlySpan<int>.CopyTo(System.Span<int>)" IL_010c: ldloc.s V_4 IL_010e: ldloca.s V_6 IL_0110: call "int System.ReadOnlySpan<int>.Length.get" IL_0115: add IL_0116: stloc.s V_4 IL_0118: ldarg.0 IL_0119: ldarg.0 IL_011a: ldfld "int[] C.<Main>d__0.<>7__wrap4" IL_011f: stfld "int[] C.<Main>d__0.<>7__wrap1" IL_0124: ldarg.0 IL_0125: ldloc.s V_4 IL_0127: stfld "int C.<Main>d__0.<>7__wrap2" IL_012c: call "System.Threading.Tasks.Task<int[]> C.M2()" IL_0131: callvirt "System.Runtime.CompilerServices.TaskAwaiter<int[]> System.Threading.Tasks.Task<int[]>.GetAwaiter()" IL_0136: stloc.s V_7 IL_0138: ldloca.s V_7 IL_013a: call "bool System.Runtime.CompilerServices.TaskAwaiter<int[]>.IsCompleted.get" IL_013f: brtrue.s IL_0182 IL_0141: ldarg.0 IL_0142: ldc.i4.1 IL_0143: dup IL_0144: stloc.0 IL_0145: stfld "int C.<Main>d__0.<>1__state" IL_014a: ldarg.0 IL_014b: ldloc.s V_7 IL_014d: stfld "System.Runtime.CompilerServices.TaskAwaiter<int[]> C.<Main>d__0.<>u__1" IL_0152: ldarg.0 IL_0153: ldflda "System.Runtime.CompilerServices.AsyncTaskMethodBuilder C.<Main>d__0.<>t__builder" IL_0158: ldloca.s V_7 IL_015a: ldarg.0 IL_015b: call "void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.AwaitUnsafeOnCompleted<System.Runtime.CompilerServices.TaskAwaiter<int[]>, C.<Main>d__0>(ref System.Runtime.CompilerServices.TaskAwaiter<int[]>, ref C.<Main>d__0)" IL_0160: leave IL_01f1 IL_0165: ldarg.0 IL_0166: ldfld "System.Runtime.CompilerServices.TaskAwaiter<int[]> C.<Main>d__0.<>u__1" IL_016b: stloc.s V_7 IL_016d: ldarg.0 IL_016e: ldflda "System.Runtime.CompilerServices.TaskAwaiter<int[]> C.<Main>d__0.<>u__1" IL_0173: initobj "System.Runtime.CompilerServices.TaskAwaiter<int[]>" IL_0179: ldarg.0 IL_017a: ldc.i4.m1 IL_017b: dup IL_017c: stloc.0 IL_017d: stfld "int C.<Main>d__0.<>1__state" IL_0182: ldloca.s V_7 IL_0184: call "int[] System.Runtime.CompilerServices.TaskAwaiter<int[]>.GetResult()" IL_0189: stloc.2 IL_018a: ldarg.0 IL_018b: ldfld "int[] C.<Main>d__0.<>7__wrap1" IL_0190: ldarg.0 IL_0191: ldfld "int C.<Main>d__0.<>7__wrap2" IL_0196: ldloc.2 IL_0197: ldc.i4.0 IL_0198: ldelem.i4 IL_0199: stelem.i4 IL_019a: ldarg.0 IL_019b: ldfld "int[] C.<Main>d__0.<>7__wrap4" IL_01a0: ldarg.0 IL_01a1: ldnull IL_01a2: stfld "int[] C.<Main>d__0.<>7__wrap1" IL_01a7: ldarg.0 IL_01a8: ldnull IL_01a9: stfld "System.Collections.Generic.List<int> C.<Main>d__0.<>7__wrap3" IL_01ae: ldarg.0 IL_01af: ldnull IL_01b0: stfld "int[] C.<Main>d__0.<>7__wrap4" IL_01b5: call "System.ReadOnlySpan<int> System.ReadOnlySpan<int>.op_Implicit(int[])" IL_01ba: stloc.s V_6 IL_01bc: ldloca.s V_6 IL_01be: call "void CollectionExtensions.Report<int>(in System.ReadOnlySpan<int>)" IL_01c3: leave.s IL_01de } catch System.Exception { IL_01c5: stloc.s V_10 IL_01c7: ldarg.0 IL_01c8: ldc.i4.s -2 IL_01ca: stfld "int C.<Main>d__0.<>1__state" IL_01cf: ldarg.0 IL_01d0: ldflda "System.Runtime.CompilerServices.AsyncTaskMethodBuilder C.<Main>d__0.<>t__builder" IL_01d5: ldloc.s V_10 IL_01d7: call "void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.SetException(System.Exception)" IL_01dc: leave.s IL_01f1 } IL_01de: ldarg.0 IL_01df: ldc.i4.s -2 IL_01e1: stfld "int C.<Main>d__0.<>1__state" IL_01e6: ldarg.0 IL_01e7: ldflda "System.Runtime.CompilerServices.AsyncTaskMethodBuilder C.<Main>d__0.<>t__builder" IL_01ec: call "void System.Runtime.CompilerServices.AsyncTaskMethodBuilder.SetResult()" IL_01f1: ret } """); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/71183")] public void Spread_TargetSpan() { var source = """ using System; class C { static void Main() { int[] arr = [1, 2, 3]; arr.Report(); Span<int> span = [..arr]; span.Report(); } } """; var verifier = CompileAndVerify(new[] { source, s_collectionExtensionsWithSpan }, expectedOutput: IncludeExpectedOutput("[1, 2, 3], [1, 2, 3],"), targetFramework: TargetFramework.Net80, verify: Verification.Skipped); verifier.VerifyDiagnostics(); verifier.VerifyIL("C.Main", """ { // Code size 57 (0x39) .maxstack 3 .locals init (int[] V_0, //arr System.ReadOnlySpan<int> V_1) IL_0000: ldc.i4.3 IL_0001: newarr "int" IL_0006: dup IL_0007: ldtoken "<PrivateImplementationDetails>.__StaticArrayInitTypeSize=12 <PrivateImplementationDetails>.4636993D3E1DA4E9D6B8F87B79E8F7C6D018580D52661950EABC3845C5897A4D" IL_000c: call "void System.Runtime.CompilerServices.RuntimeHelpers.InitializeArray(System.Array, System.RuntimeFieldHandle)" IL_0011: stloc.0 IL_0012: ldloc.0 IL_0013: call "System.ReadOnlySpan<int> System.ReadOnlySpan<int>.op_Implicit(int[])" IL_0018: stloc.1 IL_0019: ldloca.s V_1 IL_001b: call "void CollectionExtensions.Report<int>(in System.ReadOnlySpan<int>)" IL_0020: ldloc.0 IL_0021: call "int[] System.Linq.Enumerable.ToArray<int>(System.Collections.Generic.IEnumerable<int>)" IL_0026: newobj "System.Span<int>..ctor(int[])" IL_002b: call "System.ReadOnlySpan<int> System.Span<int>.op_Implicit(System.Span<int>)" IL_0030: stloc.1 IL_0031: ldloca.s V_1 IL_0033: call "void CollectionExtensions.Report<int>(in System.ReadOnlySpan<int>)" IL_0038: ret } """); } [Fact] public void Spread_TargetIEnumerable() { var source = """ using System.Collections.Generic; class C { static void Main() { int[] arr = [1, 2, 3]; arr.Report(); IEnumerable<int> enumerable = [..arr]; enumerable.Report(); } } """; var verifier = CompileAndVerify(new[] { source, s_collectionExtensionsWithSpan }, expectedOutput: IncludeExpectedOutput("[1, 2, 3], [1, 2, 3],"), targetFramework: TargetFramework.Net80, verify: Verification.Skipped); verifier.VerifyDiagnostics(); verifier.VerifyIL("C.Main", """ { // Code size 48 (0x30) .maxstack 3 .locals init (System.ReadOnlySpan<int> V_0) IL_0000: ldc.i4.3 IL_0001: newarr "int" IL_0006: dup IL_0007: ldtoken "<PrivateImplementationDetails>.__StaticArrayInitTypeSize=12 <PrivateImplementationDetails>.4636993D3E1DA4E9D6B8F87B79E8F7C6D018580D52661950EABC3845C5897A4D" IL_000c: call "void System.Runtime.CompilerServices.RuntimeHelpers.InitializeArray(System.Array, System.RuntimeFieldHandle)" IL_0011: dup IL_0012: call "System.ReadOnlySpan<int> System.ReadOnlySpan<int>.op_Implicit(int[])" IL_0017: stloc.0 IL_0018: ldloca.s V_0 IL_001a: call "void CollectionExtensions.Report<int>(in System.ReadOnlySpan<int>)" IL_001f: call "int[] System.Linq.Enumerable.ToArray<int>(System.Collections.Generic.IEnumerable<int>)" IL_0024: newobj "<>z__ReadOnlyArray<int>..ctor(int[])" IL_0029: ldc.i4.0 IL_002a: call "void CollectionExtensions.Report(object, bool)" IL_002f: ret } """); } [Fact] public void Spread_TargetICollection() { var source = """ using System.Collections.Generic; class C { static void Main() { List<int> list = [1, 2, 3]; list.Report(); ICollection<int> icollection = [..list]; icollection.Report(); } } """; var verifier = CompileAndVerify(new[] { source, s_collectionExtensionsWithSpan }, expectedOutput: IncludeExpectedOutput("[1, 2, 3], [1, 2, 3],"), targetFramework: TargetFramework.Net80, verify: Verification.Skipped); verifier.VerifyDiagnostics(); verifier.VerifyIL("C.Main", """ { // Code size 140 (0x8c) .maxstack 4 .locals init (int V_0, System.Span<int> V_1, System.Collections.Generic.List<int> V_2, System.Span<int> V_3, int V_4, System.Span<int> V_5) IL_0000: ldc.i4.3 IL_0001: stloc.0 IL_0002: ldloc.0 IL_0003: newobj "System.Collections.Generic.List<int>..ctor(int)" IL_0008: dup IL_0009: ldloc.0 IL_000a: call "void System.Runtime.InteropServices.CollectionsMarshal.SetCount<int>(System.Collections.Generic.List<int>, int)" IL_000f: dup IL_0010: call "System.Span<int> System.Runtime.InteropServices.CollectionsMarshal.AsSpan<int>(System.Collections.Generic.List<int>)" IL_0015: stloc.1 IL_0016: ldloca.s V_1 IL_0018: ldc.i4.0 IL_0019: call "ref int System.Span<int>.this[int].get" IL_001e: ldc.i4.1 IL_001f: stind.i4 IL_0020: ldloca.s V_1 IL_0022: ldc.i4.1 IL_0023: call "ref int System.Span<int>.this[int].get" IL_0028: ldc.i4.2 IL_0029: stind.i4 IL_002a: ldloca.s V_1 IL_002c: ldc.i4.2 IL_002d: call "ref int System.Span<int>.this[int].get" IL_0032: ldc.i4.3 IL_0033: stind.i4 IL_0034: dup IL_0035: ldc.i4.0 IL_0036: call "void CollectionExtensions.Report(object, bool)" IL_003b: dup IL_003c: callvirt "int System.Collections.Generic.List<int>.Count.get" IL_0041: stloc.0 IL_0042: ldloc.0 IL_0043: newobj "System.Collections.Generic.List<int>..ctor(int)" IL_0048: stloc.2 IL_0049: ldloc.2 IL_004a: ldloc.0 IL_004b: call "void System.Runtime.InteropServices.CollectionsMarshal.SetCount<int>(System.Collections.Generic.List<int>, int)" IL_0050: ldloc.2 IL_0051: call "System.Span<int> System.Runtime.InteropServices.CollectionsMarshal.AsSpan<int>(System.Collections.Generic.List<int>)" IL_0056: stloc.3 IL_0057: ldc.i4.0 IL_0058: stloc.s V_4 IL_005a: call "System.Span<int> System.Runtime.InteropServices.CollectionsMarshal.AsSpan<int>(System.Collections.Generic.List<int>)" IL_005f: stloc.s V_5 IL_0061: ldloca.s V_5 IL_0063: ldloca.s V_3 IL_0065: ldloc.s V_4 IL_0067: ldloca.s V_5 IL_0069: call "int System.Span<int>.Length.get" IL_006e: call "System.Span<int> System.Span<int>.Slice(int, int)" IL_0073: call "void System.Span<int>.CopyTo(System.Span<int>)" IL_0078: ldloc.s V_4 IL_007a: ldloca.s V_5 IL_007c: call "int System.Span<int>.Length.get" IL_0081: add IL_0082: stloc.s V_4 IL_0084: ldloc.2 IL_0085: ldc.i4.0 IL_0086: call "void CollectionExtensions.Report(object, bool)" IL_008b: ret } """); } [Fact] public void Spread_ExtensionGetEnumerator_NonGeneric() { var source = """ using System.Collections; using System.Collections.Generic; class MyCollection<T> { public readonly List<T> Items; public MyCollection(IEnumerable<T> items) { Items = new(items); } } static class Extensions { public static IEnumerator GetEnumerator<T>(this MyCollection<T> c) => c.Items.GetEnumerator(); } class Program { static void Main() { MyCollection<int> x = new([1, 2, 3]); object[] y = Convert(x); y.Report(); } static object[] Convert<T>(MyCollection<T> c) => [..c]; } """; var verifier = CompileAndVerify(new[] { source, s_collectionExtensions }, expectedOutput: IncludeExpectedOutput("[1, 2, 3], ")); verifier.VerifyDiagnostics(); verifier.VerifyIL("Program.Convert<T>", """ { // Code size 63 (0x3f) .maxstack 2 .locals init (System.Collections.Generic.List<object> V_0, System.Collections.IEnumerator V_1, object V_2, System.IDisposable V_3) IL_0000: newobj "System.Collections.Generic.List<object>..ctor()" IL_0005: stloc.0 IL_0006: ldarg.0 IL_0007: call "System.Collections.IEnumerator Extensions.GetEnumerator<T>(MyCollection<T>)" IL_000c: stloc.1 .try { IL_000d: br.s IL_001d IL_000f: ldloc.1 IL_0010: callvirt "object System.Collections.IEnumerator.Current.get" IL_0015: stloc.2 IL_0016: ldloc.0 IL_0017: ldloc.2 IL_0018: callvirt "void System.Collections.Generic.List<object>.Add(object)" IL_001d: ldloc.1 IL_001e: callvirt "bool System.Collections.IEnumerator.MoveNext()" IL_0023: brtrue.s IL_000f IL_0025: leave.s IL_0038 } finally { IL_0027: ldloc.1 IL_0028: isinst "System.IDisposable" IL_002d: stloc.3 IL_002e: ldloc.3 IL_002f: brfalse.s IL_0037 IL_0031: ldloc.3 IL_0032: callvirt "void System.IDisposable.Dispose()" IL_0037: endfinally } IL_0038: ldloc.0 IL_0039: callvirt "object[] System.Collections.Generic.List<object>.ToArray()" IL_003e: ret } """); } [Fact] public void Spread_ExtensionGetEnumerator_Generic() { var source = """ using System.Collections.Generic; class MyCollection<T> { public readonly List<T> Items; public MyCollection(IEnumerable<T> items) { Items = new(items); } } static class Extensions { public static IEnumerator<T> GetEnumerator<T>(this MyCollection<T> c) => c.Items.GetEnumerator(); } class Program { static void Main() { MyCollection<int> x = new([1, 2, 3]); int[] y = Convert(x); y.Report(); } static T[] Convert<T>(MyCollection<T> c) => [..c]; } """; var verifier = CompileAndVerify(new[] { source, s_collectionExtensions }, expectedOutput: IncludeExpectedOutput("[1, 2, 3], ")); verifier.VerifyDiagnostics(); verifier.VerifyIL("Program.Convert<T>", """ { // Code size 56 (0x38) .maxstack 2 .locals init (System.Collections.Generic.List<T> V_0, System.Collections.Generic.IEnumerator<T> V_1, T V_2) IL_0000: newobj "System.Collections.Generic.List<T>..ctor()" IL_0005: stloc.0 IL_0006: ldarg.0 IL_0007: call "System.Collections.Generic.IEnumerator<T> Extensions.GetEnumerator<T>(MyCollection<T>)" IL_000c: stloc.1 .try { IL_000d: br.s IL_001d IL_000f: ldloc.1 IL_0010: callvirt "T System.Collections.Generic.IEnumerator<T>.Current.get" IL_0015: stloc.2 IL_0016: ldloc.0 IL_0017: ldloc.2 IL_0018: callvirt "void System.Collections.Generic.List<T>.Add(T)" IL_001d: ldloc.1 IL_001e: callvirt "bool System.Collections.IEnumerator.MoveNext()" IL_0023: brtrue.s IL_000f IL_0025: leave.s IL_0031 } finally { IL_0027: ldloc.1 IL_0028: brfalse.s IL_0030 IL_002a: ldloc.1 IL_002b: callvirt "void System.IDisposable.Dispose()" IL_0030: endfinally } IL_0031: ldloc.0 IL_0032: callvirt "T[] System.Collections.Generic.List<T>.ToArray()" IL_0037: ret } """); } [Fact] public void Spread_ExtensionGetEnumerator_Pattern() { var source = """ using System.Collections.Generic; class MyCollection<T> { public readonly List<T> Items; public MyCollection(IEnumerable<T> items) { Items = new(items); } } struct MyEnumerator<T> { private IEnumerator<T> _enumerator; public MyEnumerator(IEnumerator<T> enumerator) { _enumerator = enumerator; } public bool MoveNext() => _enumerator.MoveNext(); public T Current => _enumerator.Current; } static class Extensions { public static MyEnumerator<T> GetEnumerator<T>(this MyCollection<T> c) => new(c.Items.GetEnumerator()); } class Program { static void Main() { MyCollection<int> x = new([1, 2, 3]); int[] y = Convert(x); y.Report(); } static T[] Convert<T>(MyCollection<T> c) => [..c]; } """; var verifier = CompileAndVerify(new[] { source, s_collectionExtensions }, expectedOutput: IncludeExpectedOutput("[1, 2, 3], ")); verifier.VerifyDiagnostics(); verifier.VerifyIL("Program.Convert<T>", """ { // Code size 46 (0x2e) .maxstack 2 .locals init (System.Collections.Generic.List<T> V_0, MyEnumerator<T> V_1, T V_2) IL_0000: newobj "System.Collections.Generic.List<T>..ctor()" IL_0005: stloc.0 IL_0006: ldarg.0 IL_0007: call "MyEnumerator<T> Extensions.GetEnumerator<T>(MyCollection<T>)" IL_000c: stloc.1 IL_000d: br.s IL_001e IL_000f: ldloca.s V_1 IL_0011: call "T MyEnumerator<T>.Current.get" IL_0016: stloc.2 IL_0017: ldloc.0 IL_0018: ldloc.2 IL_0019: callvirt "void System.Collections.Generic.List<T>.Add(T)" IL_001e: ldloca.s V_1 IL_0020: call "bool MyEnumerator<T>.MoveNext()" IL_0025: brtrue.s IL_000f IL_0027: ldloc.0 IL_0028: callvirt "T[] System.Collections.Generic.List<T>.ToArray()" IL_002d: ret } """); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/71217")] public void List_SingleSpread_IEnumerable() { var source = """ using System.Collections.Generic; class C { static void Main() { M([1, 2, 3]).Report(); } static List<int> M(IEnumerable<int> e) => [..e]; } """; var verifier = CompileAndVerify([source, s_collectionExtensions], expectedOutput: "[1, 2, 3],", verify: Verification.Skipped); verifier.VerifyDiagnostics(); verifier.VerifyIL("C.M", """ { // Code size 7 (0x7) .maxstack 1 IL_0000: ldarg.0 IL_0001: call "System.Collections.Generic.List<int> System.Linq.Enumerable.ToList<int>(System.Collections.Generic.IEnumerable<int>)" IL_0006: ret } """); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/71217")] public void List_SingleSpread_IEnumerable_Covariant() { var source = """ using System.Collections.Generic; class C { static void Main() { M(["a", "b", "c"]).Report(); } static List<object> M(IEnumerable<string> e) => [..e]; } """; var verifier = CompileAndVerify([source, s_collectionExtensions], expectedOutput: "[a, b, c],", verify: Verification.Skipped); verifier.VerifyDiagnostics(); verifier.VerifyIL("C.M", """ { // Code size 7 (0x7) .maxstack 1 IL_0000: ldarg.0 IL_0001: call "System.Collections.Generic.List<object> System.Linq.Enumerable.ToList<object>(System.Collections.Generic.IEnumerable<object>)" IL_0006: ret } """); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/71217")] public void List_SingleSpread_IEnumerable_ElementConversions_Generics() { var source = """ using System; using System.Collections.Generic; class C { static void Main() { M1<object, string>(["a", "b", "c"]).Report(); M2<IEquatable<int>, int>([1, 2, 3]).Report(); M3<IEquatable<string>, string>(["a", "b", "c"]).Report(); M4<int>([1, 2, 3]).Report(); } static List<T> M1<T, U>(IEnumerable<U> e) where U : T => [..e]; static List<T> M2<T, U>(IEnumerable<U> e) where U : struct, T => [..e]; static List<T> M3<T, U>(IEnumerable<U> e) where U : class, T => [..e]; static List<T?> M4<T>(IEnumerable<T> e) where T : struct => [..e]; } """; var verifier = CompileAndVerify([source, s_collectionExtensions], expectedOutput: "[a, b, c], [1, 2, 3], [a, b, c], [1, 2, 3], ", verify: Verification.Skipped); verifier.VerifyDiagnostics(); var expectedILWiwthBoxingConversion = """ { // Code size 61 (0x3d) .maxstack 2 .locals init (System.Collections.Generic.List<T> V_0, System.Collections.Generic.IEnumerator<U> V_1, U V_2) IL_0000: newobj "System.Collections.Generic.List<T>..ctor()" IL_0005: stloc.0 IL_0006: ldarg.0 IL_0007: callvirt "System.Collections.Generic.IEnumerator<U> System.Collections.Generic.IEnumerable<U>.GetEnumerator()" IL_000c: stloc.1 .try { IL_000d: br.s IL_0027 IL_000f: ldloc.1 IL_0010: callvirt "U System.Collections.Generic.IEnumerator<U>.Current.get" IL_0015: stloc.2 IL_0016: ldloc.0 IL_0017: ldloc.2 IL_0018: box "U" IL_001d: unbox.any "T" IL_0022: callvirt "void System.Collections.Generic.List<T>.Add(T)" IL_0027: ldloc.1 IL_0028: callvirt "bool System.Collections.IEnumerator.MoveNext()" IL_002d: brtrue.s IL_000f IL_002f: leave.s IL_003b } finally { IL_0031: ldloc.1 IL_0032: brfalse.s IL_003a IL_0034: ldloc.1 IL_0035: callvirt "void System.IDisposable.Dispose()" IL_003a: endfinally } IL_003b: ldloc.0 IL_003c: ret } """; verifier.VerifyIL("C.M1<T, U>", expectedILWiwthBoxingConversion); verifier.VerifyIL("C.M2<T, U>", expectedILWiwthBoxingConversion); verifier.VerifyIL("C.M3<T, U>", """ { // Code size 7 (0x7) .maxstack 1 IL_0000: ldarg.0 IL_0001: call "System.Collections.Generic.List<T> System.Linq.Enumerable.ToList<T>(System.Collections.Generic.IEnumerable<T>)" IL_0006: ret } """); verifier.VerifyIL("C.M4<T>", """ { // Code size 56 (0x38) .maxstack 2 .locals init (System.Collections.Generic.List<T?> V_0, System.Collections.Generic.IEnumerator<T> V_1, T V_2) IL_0000: newobj "System.Collections.Generic.List<T?>..ctor()" IL_0005: stloc.0 IL_0006: ldarg.0 IL_0007: callvirt "System.Collections.Generic.IEnumerator<T> System.Collections.Generic.IEnumerable<T>.GetEnumerator()" IL_000c: stloc.1 .try { IL_000d: br.s IL_0022 IL_000f: ldloc.1 IL_0010: callvirt "T System.Collections.Generic.IEnumerator<T>.Current.get" IL_0015: stloc.2 IL_0016: ldloc.0 IL_0017: ldloc.2 IL_0018: newobj "T?..ctor(T)" IL_001d: callvirt "void System.Collections.Generic.List<T?>.Add(T?)" IL_0022: ldloc.1 IL_0023: callvirt "bool System.Collections.IEnumerator.MoveNext()" IL_0028: brtrue.s IL_000f IL_002a: leave.s IL_0036 } finally { IL_002c: ldloc.1 IL_002d: brfalse.s IL_0035 IL_002f: ldloc.1 IL_0030: callvirt "void System.IDisposable.Dispose()" IL_0035: endfinally } IL_0036: ldloc.0 IL_0037: ret } """); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/71217")] public void List_SingleSpread_IEnumerable_ClassConstraint() { var source = """ using System.Collections.Generic; class C { static void Main() { M<IEnumerable<string>, string>(["a", "b", "c"]).Report(); } static List<U> M<T, U>(T t) where T : class, IEnumerable<U> => [..t]; } """; var verifier = CompileAndVerify([source, s_collectionExtensions], expectedOutput: "[a, b, c],", verify: Verification.Skipped); verifier.VerifyDiagnostics(); verifier.VerifyIL("C.M<T, U>", """ { // Code size 7 (0x7) .maxstack 1 IL_0000: ldarg.0 IL_0001: call "System.Collections.Generic.List<U> System.Linq.Enumerable.ToList<U>(System.Collections.Generic.IEnumerable<U>)" IL_0006: ret } """); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/71217")] public void List_SingleSpread_IEnumerable_MissingToList() { var source = """ using System.Collections.Generic; class C { static void Main() { M([1, 2, 3]).Report(); } static List<int> M(IEnumerable<int> e) => [..e]; } """; var comp = CreateCompilation([source, s_collectionExtensions], options: TestOptions.ReleaseExe); comp.MakeMemberMissing(WellKnownMember.System_Linq_Enumerable__ToList); var verifier = CompileAndVerify(comp, expectedOutput: "[1, 2, 3],", verify: Verification.Skipped); verifier.VerifyDiagnostics(); verifier.VerifyIL("C.M", """ { // Code size 13 (0xd) .maxstack 3 IL_0000: newobj "System.Collections.Generic.List<int>..ctor()" IL_0005: dup IL_0006: ldarg.0 IL_0007: callvirt "void System.Collections.Generic.List<int>.AddRange(System.Collections.Generic.IEnumerable<int>)" IL_000c: ret } """); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/71217")] public void List_SingleSpread_IEnumerable_NonGeneric() { var source = """ using System.Collections; using System.Collections.Generic; class C { static void Main() { M(new[] { "a", "b", "c" }).Report(); } static List<object> M(IEnumerable e) => [..e]; } """; var verifier = CompileAndVerify([source, s_collectionExtensions], expectedOutput: "[a, b, c],", verify: Verification.Skipped); verifier.VerifyDiagnostics(); verifier.VerifyIL("C.M", """ { // Code size 58 (0x3a) .maxstack 2 .locals init (System.Collections.Generic.List<object> V_0, System.Collections.IEnumerator V_1, object V_2, System.IDisposable V_3) IL_0000: newobj "System.Collections.Generic.List<object>..ctor()" IL_0005: stloc.0 IL_0006: ldarg.0 IL_0007: callvirt "System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()" IL_000c: stloc.1 .try { IL_000d: br.s IL_001d IL_000f: ldloc.1 IL_0010: callvirt "object System.Collections.IEnumerator.Current.get" IL_0015: stloc.2 IL_0016: ldloc.0 IL_0017: ldloc.2 IL_0018: callvirt "void System.Collections.Generic.List<object>.Add(object)" IL_001d: ldloc.1 IL_001e: callvirt "bool System.Collections.IEnumerator.MoveNext()" IL_0023: brtrue.s IL_000f IL_0025: leave.s IL_0038 } finally { IL_0027: ldloc.1 IL_0028: isinst "System.IDisposable" IL_002d: stloc.3 IL_002e: ldloc.3 IL_002f: brfalse.s IL_0037 IL_0031: ldloc.3 IL_0032: callvirt "void System.IDisposable.Dispose()" IL_0037: endfinally } IL_0038: ldloc.0 IL_0039: ret } """); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/71217")] public void List_SingleSpread_IEnumerable_PropertyAccess() { var source = """ using System.Collections.Generic; class C { private IEnumerable<int> E { get; set; } static void Main() { var c = new C() { E = [1, 2, 3] }; M(c).Report(); } static List<int> M(C c) => [.. c.E]; } """; var verifier = CompileAndVerify([source, s_collectionExtensions], expectedOutput: "[1, 2, 3],", verify: Verification.Skipped); verifier.VerifyDiagnostics(); verifier.VerifyIL("C.M", """ { // Code size 12 (0xc) .maxstack 1 IL_0000: ldarg.0 IL_0001: callvirt "System.Collections.Generic.IEnumerable<int> C.E.get" IL_0006: call "System.Collections.Generic.List<int> System.Linq.Enumerable.ToList<int>(System.Collections.Generic.IEnumerable<int>)" IL_000b: ret } """); } [Theory, WorkItem("https://github.com/dotnet/roslyn/issues/71217")] [InlineData("ICollection<int>")] [InlineData("IList<int>")] [InlineData("List<int>")] [InlineData("HashSet<int>")] [InlineData("int[]")] public void List_SingleSpread_ICollectionAndItsDerivatives(string type) { var source = $$""" using System.Collections.Generic; class C { static void Main() { M([1, 2, 3]).Report(); } static List<int> M({{type}} c) => [..c]; } """; var verifier = CompileAndVerify([source, s_collectionExtensions], expectedOutput: "[1, 2, 3],", verify: Verification.Skipped); verifier.VerifyDiagnostics(); verifier.VerifyIL("C.M", """ { // Code size 7 (0x7) .maxstack 1 IL_0000: ldarg.0 IL_0001: call "System.Collections.Generic.List<int> System.Linq.Enumerable.ToList<int>(System.Collections.Generic.IEnumerable<int>)" IL_0006: ret } """); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/71217")] public void List_SingleSpread_ImmutableArray() { var source = """ using System.Collections.Generic; using System.Collections.Immutable; class C { static void Main() { M([1, 2, 3]).Report(); } static List<int> M(ImmutableArray<int> a) => [..a]; } """; var verifier = CompileAndVerify([source, s_collectionExtensions], expectedOutput: IncludeExpectedOutput("[1, 2, 3],"), targetFramework: TargetFramework.Net80, verify: Verification.Skipped); verifier.VerifyDiagnostics(); verifier.VerifyIL("C.M", """ { // Code size 74 (0x4a) .maxstack 5 .locals init (System.Collections.Immutable.ImmutableArray<int> V_0, int V_1, System.Span<int> V_2, int V_3, System.ReadOnlySpan<int> V_4) IL_0000: ldarg.0 IL_0001: stloc.0 IL_0002: ldloca.s V_0 IL_0004: call "int System.Collections.Immutable.ImmutableArray<int>.Length.get" IL_0009: stloc.1 IL_000a: ldloc.1 IL_000b: newobj "System.Collections.Generic.List<int>..ctor(int)" IL_0010: dup IL_0011: ldloc.1 IL_0012: call "void System.Runtime.InteropServices.CollectionsMarshal.SetCount<int>(System.Collections.Generic.List<int>, int)" IL_0017: dup IL_0018: call "System.Span<int> System.Runtime.InteropServices.CollectionsMarshal.AsSpan<int>(System.Collections.Generic.List<int>)" IL_001d: stloc.2 IL_001e: ldc.i4.0 IL_001f: stloc.3 IL_0020: ldloca.s V_0 IL_0022: call "System.ReadOnlySpan<int> System.Collections.Immutable.ImmutableArray<int>.AsSpan()" IL_0027: stloc.s V_4 IL_0029: ldloca.s V_4 IL_002b: ldloca.s V_2 IL_002d: ldloc.3 IL_002e: ldloca.s V_4 IL_0030: call "int System.ReadOnlySpan<int>.Length.get" IL_0035: call "System.Span<int> System.Span<int>.Slice(int, int)" IL_003a: call "void System.ReadOnlySpan<int>.CopyTo(System.Span<int>)" IL_003f: ldloc.3 IL_0040: ldloca.s V_4 IL_0042: call "int System.ReadOnlySpan<int>.Length.get" IL_0047: add IL_0048: stloc.3 IL_0049: ret } """); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/71217")] public void List_SingleSpread_CustomCollection_NotICollectionAndNoStructEnumerator() { var source = """ using System.Collections; using System.Collections.Generic; class MyCollection(List<int> list) : IEnumerable<int> { public IEnumerator<int> GetEnumerator() => list.GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); } class C { static void Main() { M(new([1, 2, 3])).Report(); } static List<int> M(MyCollection c) => [..c]; } """; var verifier = CompileAndVerify([source, s_collectionExtensions], expectedOutput: "[1, 2, 3],", verify: Verification.Skipped); verifier.VerifyDiagnostics(); verifier.VerifyIL("C.M", """ { // Code size 7 (0x7) .maxstack 1 IL_0000: ldarg.0 IL_0001: call "System.Collections.Generic.List<int> System.Linq.Enumerable.ToList<int>(System.Collections.Generic.IEnumerable<int>)" IL_0006: ret } """); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/71217")] public void List_SingleSpread_CustomCollection_NotICollectionAndNoStructEnumerator_PublicAndExplicitGetEnumerator() { var source = """ using System; using System.Collections; using System.Collections.Generic; class MyCollection(List<int> list) : IEnumerable<int> { public IEnumerator<int> GetEnumerator() { Console.WriteLine("Public"); return list.GetEnumerator(); } IEnumerator<int> IEnumerable<int>.GetEnumerator() { Console.WriteLine("Explicit"); return list.GetEnumerator(); } IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); } class C { static void Main() { M(new([1, 2, 3])).Report(); } static List<int> M(MyCollection c) => [..c]; } """; var expectedOutput = """ Explicit [1, 2, 3], """; var verifier = CompileAndVerify([source, s_collectionExtensions], expectedOutput: expectedOutput, verify: Verification.Skipped); verifier.VerifyDiagnostics(); verifier.VerifyIL("C.M", """ { // Code size 7 (0x7) .maxstack 1 IL_0000: ldarg.0 IL_0001: call "System.Collections.Generic.List<int> System.Linq.Enumerable.ToList<int>(System.Collections.Generic.IEnumerable<int>)" IL_0006: ret } """); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/71217")] public void List_SingleSpread_CustomCollection_ICollectionAndNoStructEnumerator() { var source = """ using System.Collections; using System.Collections.Generic; class MyCollection(List<int> list) : ICollection<int> { public int Count => list.Count; public bool IsReadOnly => false; public void Add(int item) => list.Add(item); public void Clear() => list.Clear(); public bool Contains(int item) => list.Contains(item); public void CopyTo(int[] array, int arrayIndex) => list.CopyTo(array, arrayIndex); public bool Remove(int item) => list.Remove(item); public IEnumerator<int> GetEnumerator() => list.GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); } class C { static void Main() { M(new([1, 2, 3])).Report(); } static List<int> M(MyCollection c) => [..c]; } """; var verifier = CompileAndVerify([source, s_collectionExtensions], expectedOutput: "[1, 2, 3],", verify: Verification.Skipped); verifier.VerifyDiagnostics(); verifier.VerifyIL("C.M", """ { // Code size 7 (0x7) .maxstack 1 IL_0000: ldarg.0 IL_0001: call "System.Collections.Generic.List<int> System.Linq.Enumerable.ToList<int>(System.Collections.Generic.IEnumerable<int>)" IL_0006: ret } """); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/74615")] public void List_SingleSpread_CustomCollection_NotICollectionAndStructEnumerator() { var source = """ using System.Collections; using System.Collections.Generic; class MyCollection(List<int> list) : IEnumerable<int> { public Enumerator GetEnumerator() => new(list.GetEnumerator()); IEnumerator<int> IEnumerable<int>.GetEnumerator() => GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); public struct Enumerator(List<int>.Enumerator enumerator) : IEnumerator<int> { public int Current => enumerator.Current; object IEnumerator.Current => Current; public bool MoveNext() => enumerator.MoveNext(); public void Dispose() => enumerator.Dispose(); public void Reset() { } } } class C { static void Main() { M(new([1, 2, 3])).Report(); } static List<int> M(MyCollection c) => [..c]; } """; var verifier = CompileAndVerify([source, s_collectionExtensions], expectedOutput: "[1, 2, 3],", verify: Verification.Skipped); verifier.VerifyDiagnostics(); verifier.VerifyIL("C.M", """ { // Code size 57 (0x39) .maxstack 2 .locals init (System.Collections.Generic.List<int> V_0, MyCollection.Enumerator V_1, int V_2) IL_0000: newobj "System.Collections.Generic.List<int>..ctor()" IL_0005: stloc.0 IL_0006: ldarg.0 IL_0007: callvirt "MyCollection.Enumerator MyCollection.GetEnumerator()" IL_000c: stloc.1 .try { IL_000d: br.s IL_001e IL_000f: ldloca.s V_1 IL_0011: call "int MyCollection.Enumerator.Current.get" IL_0016: stloc.2 IL_0017: ldloc.0 IL_0018: ldloc.2 IL_0019: callvirt "void System.Collections.Generic.List<int>.Add(int)" IL_001e: ldloca.s V_1 IL_0020: call "bool MyCollection.Enumerator.MoveNext()" IL_0025: brtrue.s IL_000f IL_0027: leave.s IL_0037 } finally { IL_0029: ldloca.s V_1 IL_002b: constrained. "MyCollection.Enumerator" IL_0031: callvirt "void System.IDisposable.Dispose()" IL_0036: endfinally } IL_0037: ldloc.0 IL_0038: ret } """); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/74615")] public void Array_SingleSpread_CustomCollection_NotICollectionAndStructEnumerator() { var source = """ using System.Collections; using System.Collections.Generic; class MyCollection(List<int> list) : IEnumerable<int> { public Enumerator GetEnumerator() => new(list.GetEnumerator()); IEnumerator<int> IEnumerable<int>.GetEnumerator() => GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); public int Count => list.Count; public struct Enumerator(List<int>.Enumerator enumerator) : IEnumerator<int> { public int Current => enumerator.Current; object IEnumerator.Current => Current; public bool MoveNext() => enumerator.MoveNext(); public void Dispose() => enumerator.Dispose(); public void Reset() { } } } class C { static void Main() { M(new([1, 2, 3])).Report(); } static int[] M(MyCollection c) => [..c]; } """; var verifier = CompileAndVerify([source, s_collectionExtensions], expectedOutput: "[1, 2, 3],", verify: Verification.Skipped); verifier.VerifyDiagnostics(); verifier.VerifyIL("C.M", """ { // Code size 66 (0x42) .maxstack 3 .locals init (int V_0, int[] V_1, MyCollection.Enumerator V_2, int V_3) IL_0000: ldarg.0 IL_0001: ldc.i4.0 IL_0002: stloc.0 IL_0003: dup IL_0004: callvirt "int MyCollection.Count.get" IL_0009: newarr "int" IL_000e: stloc.1 IL_000f: callvirt "MyCollection.Enumerator MyCollection.GetEnumerator()" IL_0014: stloc.2 .try { IL_0015: br.s IL_0027 IL_0017: ldloca.s V_2 IL_0019: call "int MyCollection.Enumerator.Current.get" IL_001e: stloc.3 IL_001f: ldloc.1 IL_0020: ldloc.0 IL_0021: ldloc.3 IL_0022: stelem.i4 IL_0023: ldloc.0 IL_0024: ldc.i4.1 IL_0025: add IL_0026: stloc.0 IL_0027: ldloca.s V_2 IL_0029: call "bool MyCollection.Enumerator.MoveNext()" IL_002e: brtrue.s IL_0017 IL_0030: leave.s IL_0040 } finally { IL_0032: ldloca.s V_2 IL_0034: constrained. "MyCollection.Enumerator" IL_003a: callvirt "void System.IDisposable.Dispose()" IL_003f: endfinally } IL_0040: ldloc.1 IL_0041: ret } """); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/74615")] public void Array_SingleSpread_CustomCollection_NotICollectionAndStructEnumerator_NoCountProperty() { var source = """ using System.Collections; using System.Collections.Generic; class MyCollection(List<int> list) : IEnumerable<int> { public Enumerator GetEnumerator() => new(list.GetEnumerator()); IEnumerator<int> IEnumerable<int>.GetEnumerator() => GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); public struct Enumerator(List<int>.Enumerator enumerator) : IEnumerator<int> { public int Current => enumerator.Current; object IEnumerator.Current => Current; public bool MoveNext() => enumerator.MoveNext(); public void Dispose() => enumerator.Dispose(); public void Reset() { } } } class C { static void Main() { M(new([1, 2, 3])).Report(); } static int[] M(MyCollection c) => [..c]; } """; var verifier = CompileAndVerify([source, s_collectionExtensions], expectedOutput: "[1, 2, 3],", verify: Verification.Skipped); verifier.VerifyDiagnostics(); verifier.VerifyIL("C.M", """ { // Code size 62 (0x3e) .maxstack 2 .locals init (System.Collections.Generic.List<int> V_0, MyCollection.Enumerator V_1, int V_2) IL_0000: newobj "System.Collections.Generic.List<int>..ctor()" IL_0005: stloc.0 IL_0006: ldarg.0 IL_0007: callvirt "MyCollection.Enumerator MyCollection.GetEnumerator()" IL_000c: stloc.1 .try { IL_000d: br.s IL_001e IL_000f: ldloca.s V_1 IL_0011: call "int MyCollection.Enumerator.Current.get" IL_0016: stloc.2 IL_0017: ldloc.0 IL_0018: ldloc.2 IL_0019: callvirt "void System.Collections.Generic.List<int>.Add(int)" IL_001e: ldloca.s V_1 IL_0020: call "bool MyCollection.Enumerator.MoveNext()" IL_0025: brtrue.s IL_000f IL_0027: leave.s IL_0037 } finally { IL_0029: ldloca.s V_1 IL_002b: constrained. "MyCollection.Enumerator" IL_0031: callvirt "void System.IDisposable.Dispose()" IL_0036: endfinally } IL_0037: ldloc.0 IL_0038: callvirt "int[] System.Collections.Generic.List<int>.ToArray()" IL_003d: ret } """); } [Fact] public void SingleSpread_ToArray_MustLowerSpreadOperand() { var source = """ using System; using System.Collections.Generic; class C { static Action[] M1() { return [..new[] { () => Console.Write(1) }]; } static Action[] M2() { return [..new List<Action> { () => Console.Write(2) }]; } static Action[] M3() { return [..new Span<Action>(new[] { () => Console.Write(3) })]; } static Action[] M4() { return [..new ReadOnlySpan<Action>(new[] { () => Console.Write(4) })]; } static void Main() { M1()[0](); M2()[0](); M3()[0](); M4()[0](); } } """; var verifier = CompileAndVerify(source, expectedOutput: IncludeExpectedOutput("1234"), targetFramework: TargetFramework.Net80, verify: Verification.Skipped); verifier.VerifyDiagnostics(); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/74615")] public void List_SingleSpread_CustomCollection_NotICollectionAndStructEnumerator_MissingICollectionOfTType() { var source = """ using System.Collections; using System.Collections.Generic; class MyCollection(List<int> list) : IEnumerable<int> { public Enumerator GetEnumerator() => new(list.GetEnumerator()); IEnumerator<int> IEnumerable<int>.GetEnumerator() => GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); public struct Enumerator(List<int>.Enumerator enumerator) : IEnumerator<int> { public int Current => enumerator.Current; object IEnumerator.Current => Current; public bool MoveNext() => enumerator.MoveNext(); public void Dispose() => enumerator.Dispose(); public void Reset() { } } } class C { static void Main() { M(new([1, 2, 3])).Report(); } static List<int> M(MyCollection c) => [..c]; } """; var comp = CreateCompilation([source, s_collectionExtensions], options: TestOptions.ReleaseExe); comp.MakeTypeMissing(SpecialType.System_Collections_Generic_ICollection_T); var verifier = CompileAndVerify(comp, expectedOutput: "[1, 2, 3],", verify: Verification.Skipped); verifier.VerifyDiagnostics(); verifier.VerifyIL("C.M", """ { // Code size 57 (0x39) .maxstack 2 .locals init (System.Collections.Generic.List<int> V_0, MyCollection.Enumerator V_1, int V_2) IL_0000: newobj "System.Collections.Generic.List<int>..ctor()" IL_0005: stloc.0 IL_0006: ldarg.0 IL_0007: callvirt "MyCollection.Enumerator MyCollection.GetEnumerator()" IL_000c: stloc.1 .try { IL_000d: br.s IL_001e IL_000f: ldloca.s V_1 IL_0011: call "int MyCollection.Enumerator.Current.get" IL_0016: stloc.2 IL_0017: ldloc.0 IL_0018: ldloc.2 IL_0019: callvirt "void System.Collections.Generic.List<int>.Add(int)" IL_001e: ldloca.s V_1 IL_0020: call "bool MyCollection.Enumerator.MoveNext()" IL_0025: brtrue.s IL_000f IL_0027: leave.s IL_0037 } finally { IL_0029: ldloca.s V_1 IL_002b: constrained. "MyCollection.Enumerator" IL_0031: callvirt "void System.IDisposable.Dispose()" IL_0036: endfinally } IL_0037: ldloc.0 IL_0038: ret } """); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/74615")] public void List_CustomCollection_NotICollectionAndStructEnumerator_MixedWithOtherAddRangeSpread() { var source = """ using System.Collections; using System.Collections.Generic; class MyCollection(List<int> list) : IEnumerable<int> { public Enumerator GetEnumerator() => new(list.GetEnumerator()); IEnumerator<int> IEnumerable<int>.GetEnumerator() => GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); public struct Enumerator(List<int>.Enumerator enumerator) : IEnumerator<int> { public int Current => enumerator.Current; object IEnumerator.Current => Current; public bool MoveNext() => enumerator.MoveNext(); public void Dispose() => enumerator.Dispose(); public void Reset() { } } } class C { static void Main() { M(new([1, 2, 3]), [4, 5, 6]).Report(); } static List<int> M(MyCollection c, IEnumerable<int> e) => [..c, ..e]; } """; var verifier = CompileAndVerify([source, s_collectionExtensions], expectedOutput: "[1, 2, 3, 4, 5, 6],", verify: Verification.Skipped); verifier.VerifyDiagnostics(); verifier.VerifyIL("C.M", """ { // Code size 64 (0x40) .maxstack 2 .locals init (System.Collections.Generic.List<int> V_0, MyCollection.Enumerator V_1, int V_2) IL_0000: newobj "System.Collections.Generic.List<int>..ctor()" IL_0005: stloc.0 IL_0006: ldarg.0 IL_0007: callvirt "MyCollection.Enumerator MyCollection.GetEnumerator()" IL_000c: stloc.1 .try { IL_000d: br.s IL_001e IL_000f: ldloca.s V_1 IL_0011: call "int MyCollection.Enumerator.Current.get" IL_0016: stloc.2 IL_0017: ldloc.0 IL_0018: ldloc.2 IL_0019: callvirt "void System.Collections.Generic.List<int>.Add(int)" IL_001e: ldloca.s V_1 IL_0020: call "bool MyCollection.Enumerator.MoveNext()" IL_0025: brtrue.s IL_000f IL_0027: leave.s IL_0037 } finally { IL_0029: ldloca.s V_1 IL_002b: constrained. "MyCollection.Enumerator" IL_0031: callvirt "void System.IDisposable.Dispose()" IL_0036: endfinally } IL_0037: ldloc.0 IL_0038: ldarg.1 IL_0039: callvirt "void System.Collections.Generic.List<int>.AddRange(System.Collections.Generic.IEnumerable<int>)" IL_003e: ldloc.0 IL_003f: ret } """); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/71217")] public void List_SingleSpread_CustomCollection_ICollectionAndStructEnumerator() { var source = """ using System.Collections; using System.Collections.Generic; class MyCollection<T>(List<T> list) : ICollection<T> { public int Count => list.Count; public bool IsReadOnly => false; public void Add(T item) => list.Add(item); public void Clear() => list.Clear(); public bool Contains(T item) => list.Contains(item); public void CopyTo(T[] array, int arrayIndex) => list.CopyTo(array, arrayIndex); public bool Remove(T item) => list.Remove(item); public Enumerator GetEnumerator() => new(list.GetEnumerator()); IEnumerator<T> IEnumerable<T>.GetEnumerator() => GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); public struct Enumerator(List<T>.Enumerator enumerator) : IEnumerator<T> { public T Current => enumerator.Current; object IEnumerator.Current => Current; public bool MoveNext() => enumerator.MoveNext(); public void Dispose() => enumerator.Dispose(); public void Reset() { } } } class C { static void Main() { M1(new([1, 2, 3])).Report(); M2(new([1, 2, 3])).Report(); M3(new([1, 2, 3])).Report(); } static List<int> M1(MyCollection<int> c) => [..c]; #nullable enable static List<object> M2(MyCollection<object> c) => [..c]; static List<object?> M3(MyCollection<object> c) => [..c]; } """; var verifier = CompileAndVerify([source, s_collectionExtensions], expectedOutput: "[1, 2, 3], [1, 2, 3], [1, 2, 3], ", verify: Verification.Skipped); verifier.VerifyDiagnostics(); verifier.VerifyIL("C.M1", """ { // Code size 7 (0x7) .maxstack 1 IL_0000: ldarg.0 IL_0001: call "System.Collections.Generic.List<int> System.Linq.Enumerable.ToList<int>(System.Collections.Generic.IEnumerable<int>)" IL_0006: ret } """); var expectedILForObject = """ { // Code size 7 (0x7) .maxstack 1 IL_0000: ldarg.0 IL_0001: call "System.Collections.Generic.List<object> System.Linq.Enumerable.ToList<object>(System.Collections.Generic.IEnumerable<object>)" IL_0006: ret } """; verifier.VerifyIL("C.M2", expectedILForObject); verifier.VerifyIL("C.M3", expectedILForObject); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/71217")] public void List_SingleSpread_Generics_Nullability() { var source = """ using System.Collections.Generic; interface IMyEnumerable<T> : IEnumerable<T> { new MyEnumerator<T> GetEnumerator(); } interface IMyCollection<T> : ICollection<T> { new MyEnumerator<T> GetEnumerator(); } struct MyEnumerator<T> { private readonly List<T> _list; private int _index; public MyEnumerator(List<T> list) { _list = list; _index = -1; } public T Current => _list[_index]; public bool MoveNext() { if (_index < _list.Count) _index++; return _index < _list.Count; } } class MyEnumerable<T> : List<T>, IMyEnumerable<T> { MyEnumerator<T> IMyEnumerable<T>.GetEnumerator() => new(this); } class MyCollection<T> : List<T>, IMyCollection<T> { MyEnumerator<T> IMyCollection<T>.GetEnumerator() => new(this); } class Program { static void Main() { MyEnumerable<string> e = ["a", "b", "c"]; MyCollection<string> c = ["a", "b", "c"]; M1<MyEnumerable<string>, string>(e).Report(); M2<MyEnumerable<string>, string>(e).Report(); M3<MyCollection<string>, string>(c).Report(); M4<MyCollection<string>, string>(c).Report(); } #nullable enable static List<U> M1<T, U>(T c) where T : class, IMyEnumerable<U> where U : class { return [..c]; } static List<U?> M2<T, U>(T c) where T : class, IMyEnumerable<U> where U : class { return [..c]; } static List<U> M3<T, U>(T c) where T : class, IMyCollection<U> where U : class { return [..c]; } static List<U?> M4<T, U>(T c) where T : class, IMyCollection<U> where U : class { return [..c]; } } """; var verifier = CompileAndVerify([source, s_collectionExtensions], expectedOutput: "[a, b, c], [a, b, c], [a, b, c], [a, b, c], ", verify: Verification.Skipped); verifier.VerifyDiagnostics(); var expectedILForMyEnumerator = """ { // Code size 46 (0x2e) .maxstack 2 .locals init (System.Collections.Generic.List<U> V_0, MyEnumerator<U> V_1, U V_2) IL_0000: newobj "System.Collections.Generic.List<U>..ctor()" IL_0005: stloc.0 IL_0006: ldarg.0 IL_0007: box "T" IL_000c: callvirt "MyEnumerator<U> IMyEnumerable<U>.GetEnumerator()" IL_0011: stloc.1 IL_0012: br.s IL_0023 IL_0014: ldloca.s V_1 IL_0016: call "U MyEnumerator<U>.Current.get" IL_001b: stloc.2 IL_001c: ldloc.0 IL_001d: ldloc.2 IL_001e: callvirt "void System.Collections.Generic.List<U>.Add(U)" IL_0023: ldloca.s V_1 IL_0025: call "bool MyEnumerator<U>.MoveNext()" IL_002a: brtrue.s IL_0014 IL_002c: ldloc.0 IL_002d: ret } """; verifier.VerifyIL("Program.M1<T, U>", expectedILForMyEnumerator); verifier.VerifyIL("Program.M2<T, U>", expectedILForMyEnumerator); var expectedILForMyCollection = """ { // Code size 7 (0x7) .maxstack 1 IL_0000: ldarg.0 IL_0001: call "System.Collections.Generic.List<U> System.Linq.Enumerable.ToList<U>(System.Collections.Generic.IEnumerable<U>)" IL_0006: ret } """; verifier.VerifyIL("Program.M3<T, U>", expectedILForMyCollection); verifier.VerifyIL("Program.M4<T, U>", expectedILForMyCollection); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/74894")] public void List_Spread_StructEnumerator() { var source = """ using System.Collections.Generic; interface IMyEnumerable<T> : IEnumerable<T> { new MyEnumerator<T> GetEnumerator(); } interface IMyCollection<T> : ICollection<T> { new MyEnumerator<T> GetEnumerator(); } struct MyEnumerator<T> { private readonly List<T> _list; private int _index; public MyEnumerator(List<T> list) { _list = list; _index = -1; } public T Current => _list[_index]; public bool MoveNext() { if (_index < _list.Count) _index++; return _index < _list.Count; } } class MyEnumerable<T> : List<T>, IMyEnumerable<T> { MyEnumerator<T> IMyEnumerable<T>.GetEnumerator() => new(this); } class MyCollection<T> : List<T>, IMyCollection<T> { MyEnumerator<T> IMyCollection<T>.GetEnumerator() => new(this); } class Program { static void Main() { MyEnumerable<object> x = [1, 2]; MyCollection<object> y = [1, 2]; object z = 3; M1(x, z).Report(); M2(y, z).Report(); M3(x, z).Report(); M4(y, z).Report(); } static List<object> M1(IMyEnumerable<object> x, object y) { return [..x, y]; } static List<object> M2(IMyCollection<object> x, object y) { return [..x, y]; } #nullable enable static List<U?> M3<T, U>(T x, U y) where T : class, IMyEnumerable<U> where U : class { return [..x, y]; } static List<U?> M4<T, U>(T x, U y) where T : class, IMyCollection<U> where U : class { return [..x, y]; } } """; var verifier = CompileAndVerify( [source, s_collectionExtensions], targetFramework: TargetFramework.Standard, expectedOutput: "[1, 2, 3], [1, 2, 3], [1, 2, 3], [1, 2, 3], ", verify: Verification.Skipped); verifier.VerifyDiagnostics(); verifier.VerifyIL("Program.M1", """ { // Code size 48 (0x30) .maxstack 2 .locals init (System.Collections.Generic.List<object> V_0, MyEnumerator<object> V_1, object V_2) IL_0000: newobj "System.Collections.Generic.List<object>..ctor()" IL_0005: stloc.0 IL_0006: ldarg.0 IL_0007: callvirt "MyEnumerator<object> IMyEnumerable<object>.GetEnumerator()" IL_000c: stloc.1 IL_000d: br.s IL_001e IL_000f: ldloca.s V_1 IL_0011: call "object MyEnumerator<object>.Current.get" IL_0016: stloc.2 IL_0017: ldloc.0 IL_0018: ldloc.2 IL_0019: callvirt "void System.Collections.Generic.List<object>.Add(object)" IL_001e: ldloca.s V_1 IL_0020: call "bool MyEnumerator<object>.MoveNext()" IL_0025: brtrue.s IL_000f IL_0027: ldloc.0 IL_0028: ldarg.1 IL_0029: callvirt "void System.Collections.Generic.List<object>.Add(object)" IL_002e: ldloc.0 IL_002f: ret } """); verifier.VerifyIL("Program.M2", """ { // Code size 30 (0x1e) .maxstack 3 .locals init (IMyCollection<object> V_0) IL_0000: ldarg.0 IL_0001: stloc.0 IL_0002: ldc.i4.1 IL_0003: ldloc.0 IL_0004: callvirt "int System.Collections.Generic.ICollection<object>.Count.get" IL_0009: add IL_000a: newobj "System.Collections.Generic.List<object>..ctor(int)" IL_000f: dup IL_0010: ldloc.0 IL_0011: callvirt "void System.Collections.Generic.List<object>.AddRange(System.Collections.Generic.IEnumerable<object>)" IL_0016: dup IL_0017: ldarg.1 IL_0018: callvirt "void System.Collections.Generic.List<object>.Add(object)" IL_001d: ret } """); verifier.VerifyIL("Program.M3<T, U>(T, U)", """ { // Code size 53 (0x35) .maxstack 2 .locals init (System.Collections.Generic.List<U> V_0, MyEnumerator<U> V_1, U V_2) IL_0000: newobj "System.Collections.Generic.List<U>..ctor()" IL_0005: stloc.0 IL_0006: ldarg.0 IL_0007: box "T" IL_000c: callvirt "MyEnumerator<U> IMyEnumerable<U>.GetEnumerator()" IL_0011: stloc.1 IL_0012: br.s IL_0023 IL_0014: ldloca.s V_1 IL_0016: call "U MyEnumerator<U>.Current.get" IL_001b: stloc.2 IL_001c: ldloc.0 IL_001d: ldloc.2 IL_001e: callvirt "void System.Collections.Generic.List<U>.Add(U)" IL_0023: ldloca.s V_1 IL_0025: call "bool MyEnumerator<U>.MoveNext()" IL_002a: brtrue.s IL_0014 IL_002c: ldloc.0 IL_002d: ldarg.1 IL_002e: callvirt "void System.Collections.Generic.List<U>.Add(U)" IL_0033: ldloc.0 IL_0034: ret } """); verifier.VerifyIL("Program.M4<T, U>(T, U)", """ { // Code size 35 (0x23) .maxstack 3 .locals init (T V_0) IL_0000: ldarg.0 IL_0001: stloc.0 IL_0002: ldc.i4.1 IL_0003: ldloc.0 IL_0004: box "T" IL_0009: callvirt "int System.Collections.Generic.ICollection<U>.Count.get" IL_000e: add IL_000f: newobj "System.Collections.Generic.List<U>..ctor(int)" IL_0014: dup IL_0015: ldloc.0 IL_0016: callvirt "void System.Collections.Generic.List<U>.AddRange(System.Collections.Generic.IEnumerable<U>)" IL_001b: dup IL_001c: ldarg.1 IL_001d: callvirt "void System.Collections.Generic.List<U>.Add(U)" IL_0022: ret } """); } [Fact] public void List_AddRange_IEnumerable() { var source = """ using System.Collections.Generic; class C { static void Main() { IEnumerable<int> e = [1, 2, 3]; e.Report(); List<int> list = [..e, 4]; list.Report(); } } """; var verifier = CompileAndVerify(new[] { source, s_collectionExtensionsWithSpan }, expectedOutput: IncludeExpectedOutput("[1, 2, 3], [1, 2, 3, 4],"), targetFramework: TargetFramework.Net80, verify: Verification.Skipped); verifier.VerifyDiagnostics(); // https://github.com/dotnet/roslyn/issues/71273 // It's strange that using a *less* specific type can result in *better* codegen when using a List target type. verifier.VerifyIL("C.Main", """ { // Code size 56 (0x38) .maxstack 3 .locals init (System.Collections.Generic.IEnumerable<int> V_0) //e IL_0000: ldc.i4.3 IL_0001: newarr "int" IL_0006: dup IL_0007: ldtoken "<PrivateImplementationDetails>.__StaticArrayInitTypeSize=12 <PrivateImplementationDetails>.4636993D3E1DA4E9D6B8F87B79E8F7C6D018580D52661950EABC3845C5897A4D" IL_000c: call "void System.Runtime.CompilerServices.RuntimeHelpers.InitializeArray(System.Array, System.RuntimeFieldHandle)" IL_0011: newobj "<>z__ReadOnlyArray<int>..ctor(int[])" IL_0016: stloc.0 IL_0017: ldloc.0 IL_0018: ldc.i4.0 IL_0019: call "void CollectionExtensions.Report(object, bool)" IL_001e: newobj "System.Collections.Generic.List<int>..ctor()" IL_0023: dup IL_0024: ldloc.0 IL_0025: callvirt "void System.Collections.Generic.List<int>.AddRange(System.Collections.Generic.IEnumerable<int>)" IL_002a: dup IL_002b: ldc.i4.4 IL_002c: callvirt "void System.Collections.Generic.List<int>.Add(int)" IL_0031: ldc.i4.0 IL_0032: call "void CollectionExtensions.Report(object, bool)" IL_0037: ret } """); } [Fact] public void List_AddRange_IEnumerable_Covariant() { var source = """ using System.Collections.Generic; class C { static void Main() { IEnumerable<string> e = ["a", "b", "c"]; e.Report(); List<object> list = [..e, "d"]; list.Report(); } } """; var verifier = CompileAndVerify(new[] { source, s_collectionExtensionsWithSpan }, expectedOutput: IncludeExpectedOutput("[a, b, c], [a, b, c, d],"), targetFramework: TargetFramework.Net80, verify: Verification.Skipped); verifier.VerifyDiagnostics(); verifier.VerifyIL("C.Main", """ { // Code size 73 (0x49) .maxstack 4 .locals init (System.Collections.Generic.IEnumerable<string> V_0) //e IL_0000: ldc.i4.3 IL_0001: newarr "string" IL_0006: dup IL_0007: ldc.i4.0 IL_0008: ldstr "a" IL_000d: stelem.ref IL_000e: dup IL_000f: ldc.i4.1 IL_0010: ldstr "b" IL_0015: stelem.ref IL_0016: dup IL_0017: ldc.i4.2 IL_0018: ldstr "c" IL_001d: stelem.ref IL_001e: newobj "<>z__ReadOnlyArray<string>..ctor(string[])" IL_0023: stloc.0 IL_0024: ldloc.0 IL_0025: ldc.i4.0 IL_0026: call "void CollectionExtensions.Report(object, bool)" IL_002b: newobj "System.Collections.Generic.List<object>..ctor()" IL_0030: dup IL_0031: ldloc.0 IL_0032: callvirt "void System.Collections.Generic.List<object>.AddRange(System.Collections.Generic.IEnumerable<object>)" IL_0037: dup IL_0038: ldstr "d" IL_003d: callvirt "void System.Collections.Generic.List<object>.Add(object)" IL_0042: ldc.i4.0 IL_0043: call "void CollectionExtensions.Report(object, bool)" IL_0048: ret } """); } [Theory] [InlineData("where T : IEnumerable<U>")] [InlineData("where T : struct, IEnumerable<U>")] public void List_AddRange_IEnumerable_Constraint(string constraints) { var source = $$""" using System.Collections.Generic; using System.Collections.Immutable; class C { static void Main() { ImmutableArray<string> e = ["a", "b", "c"]; e.Report(); M<ImmutableArray<string>, string>(e); } static void M<T, U>(T t) {{constraints}} { List<U> list = [..t]; list.Report(); } } """; var verifier = CompileAndVerify(new[] { source, s_collectionExtensionsWithSpan }, expectedOutput: IncludeExpectedOutput("[a, b, c], [a, b, c],"), targetFramework: TargetFramework.Net80, verify: Verification.Skipped); verifier.VerifyDiagnostics(); verifier.VerifyIL("C.M<T, U>", """ { // Code size 64 (0x40) .maxstack 2 .locals init (System.Collections.Generic.List<U> V_0, System.Collections.Generic.IEnumerator<U> V_1, U V_2) IL_0000: newobj "System.Collections.Generic.List<U>..ctor()" IL_0005: stloc.0 IL_0006: ldarga.s V_0 IL_0008: constrained. "T" IL_000e: callvirt "System.Collections.Generic.IEnumerator<U> System.Collections.Generic.IEnumerable<U>.GetEnumerator()" IL_0013: stloc.1 .try { IL_0014: br.s IL_0024 IL_0016: ldloc.1 IL_0017: callvirt "U System.Collections.Generic.IEnumerator<U>.Current.get" IL_001c: stloc.2 IL_001d: ldloc.0 IL_001e: ldloc.2 IL_001f: callvirt "void System.Collections.Generic.List<U>.Add(U)" IL_0024: ldloc.1 IL_0025: callvirt "bool System.Collections.IEnumerator.MoveNext()" IL_002a: brtrue.s IL_0016 IL_002c: leave.s IL_0038 } finally { IL_002e: ldloc.1 IL_002f: brfalse.s IL_0037 IL_0031: ldloc.1 IL_0032: callvirt "void System.IDisposable.Dispose()" IL_0037: endfinally } IL_0038: ldloc.0 IL_0039: ldc.i4.0 IL_003a: call "void CollectionExtensions.Report(object, bool)" IL_003f: ret } """); } [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/71183")] public void List_AddRange_IEnumerable_ClassConstraint() { var source = """ using System.Collections.Generic; class C { static void Main() { IEnumerable<string> e = ["a", "b", "c"]; e.Report(); M<IEnumerable<string>, string>(e); } static void M<T, U>(T t) where T : class, IEnumerable<U> { List<U> list = [..t, ..t]; list.Report(); } } """; var verifier = CompileAndVerify(new[] { source, s_collectionExtensionsWithSpan }, expectedOutput: IncludeExpectedOutput("[a, b, c], [a, b, c, a, b, c],"), targetFramework: TargetFramework.Net80, verify: Verification.Skipped); verifier.VerifyDiagnostics(); verifier.VerifyIL("C.M<T, U>", """ { // Code size 26 (0x1a) .maxstack 3 IL_0000: newobj "System.Collections.Generic.List<U>..ctor()" IL_0005: dup IL_0006: ldarg.0 IL_0007: callvirt "void System.Collections.Generic.List<U>.AddRange(System.Collections.Generic.IEnumerable<U>)" IL_000c: dup IL_000d: ldarg.0 IL_000e: callvirt "void System.Collections.Generic.List<U>.AddRange(System.Collections.Generic.IEnumerable<U>)" IL_0013: ldc.i4.0 IL_0014: call "void CollectionExtensions.Report(object, bool)" IL_0019: ret } """); } [Fact] public void List_AddRange_ICollection() { var source = """ using System.Collections.Generic; class C { static void Main() { ICollection<int> e = [1, 2, 3]; e.Report(); List<int> list = [..e, 4]; list.Report(); } } """; var verifier = CompileAndVerify(new[] { source, s_collectionExtensionsWithSpan }, expectedOutput: IncludeExpectedOutput("[1, 2, 3], [1, 2, 3, 4],"), targetFramework: TargetFramework.Net80, verify: Verification.Skipped); verifier.VerifyDiagnostics(); // https://github.com/dotnet/roslyn/issues/71273 // Ideally we'd like to be able to use *both* something like AddRange, *and* AsSpan/CopyTo/etc. while building the same target collection verifier.VerifyIL("C.Main", """ { // Code size 173 (0xad) .maxstack 3 .locals init (int V_0, System.Span<int> V_1, System.Collections.Generic.ICollection<int> V_2, System.Collections.Generic.List<int> V_3, System.Span<int> V_4, int V_5, System.Collections.Generic.IEnumerator<int> V_6, int V_7) IL_0000: ldc.i4.3 IL_0001: stloc.0 IL_0002: ldloc.0 IL_0003: newobj "System.Collections.Generic.List<int>..ctor(int)" IL_0008: dup IL_0009: ldloc.0 IL_000a: call "void System.Runtime.InteropServices.CollectionsMarshal.SetCount<int>(System.Collections.Generic.List<int>, int)" IL_000f: dup IL_0010: call "System.Span<int> System.Runtime.InteropServices.CollectionsMarshal.AsSpan<int>(System.Collections.Generic.List<int>)" IL_0015: stloc.1 IL_0016: ldloca.s V_1 IL_0018: ldc.i4.0 IL_0019: call "ref int System.Span<int>.this[int].get" IL_001e: ldc.i4.1 IL_001f: stind.i4 IL_0020: ldloca.s V_1 IL_0022: ldc.i4.1 IL_0023: call "ref int System.Span<int>.this[int].get" IL_0028: ldc.i4.2 IL_0029: stind.i4 IL_002a: ldloca.s V_1 IL_002c: ldc.i4.2 IL_002d: call "ref int System.Span<int>.this[int].get" IL_0032: ldc.i4.3 IL_0033: stind.i4 IL_0034: dup IL_0035: ldc.i4.0 IL_0036: call "void CollectionExtensions.Report(object, bool)" IL_003b: stloc.2 IL_003c: ldc.i4.1 IL_003d: ldloc.2 IL_003e: callvirt "int System.Collections.Generic.ICollection<int>.Count.get" IL_0043: add IL_0044: stloc.0 IL_0045: ldloc.0 IL_0046: newobj "System.Collections.Generic.List<int>..ctor(int)" IL_004b: stloc.3 IL_004c: ldloc.3 IL_004d: ldloc.0 IL_004e: call "void System.Runtime.InteropServices.CollectionsMarshal.SetCount<int>(System.Collections.Generic.List<int>, int)" IL_0053: ldloc.3 IL_0054: call "System.Span<int> System.Runtime.InteropServices.CollectionsMarshal.AsSpan<int>(System.Collections.Generic.List<int>)" IL_0059: stloc.s V_4 IL_005b: ldc.i4.0 IL_005c: stloc.s V_5 IL_005e: ldloc.2 IL_005f: callvirt "System.Collections.Generic.IEnumerator<int> System.Collections.Generic.IEnumerable<int>.GetEnumerator()" IL_0064: stloc.s V_6 .try { IL_0066: br.s IL_0083 IL_0068: ldloc.s V_6 IL_006a: callvirt "int System.Collections.Generic.IEnumerator<int>.Current.get" IL_006f: stloc.s V_7 IL_0071: ldloca.s V_4 IL_0073: ldloc.s V_5 IL_0075: call "ref int System.Span<int>.this[int].get" IL_007a: ldloc.s V_7 IL_007c: stind.i4 IL_007d: ldloc.s V_5 IL_007f: ldc.i4.1 IL_0080: add IL_0081: stloc.s V_5 IL_0083: ldloc.s V_6 IL_0085: callvirt "bool System.Collections.IEnumerator.MoveNext()" IL_008a: brtrue.s IL_0068 IL_008c: leave.s IL_009a } finally { IL_008e: ldloc.s V_6 IL_0090: brfalse.s IL_0099 IL_0092: ldloc.s V_6 IL_0094: callvirt "void System.IDisposable.Dispose()" IL_0099: endfinally } IL_009a: ldloca.s V_4 IL_009c: ldloc.s V_5 IL_009e: call "ref int System.Span<int>.this[int].get" IL_00a3: ldc.i4.4 IL_00a4: stind.i4 IL_00a5: ldloc.3 IL_00a6: ldc.i4.0 IL_00a7: call "void CollectionExtensions.Report(object, bool)" IL_00ac: ret } """); } [Fact] public void TypeInference_LambdaReturn() { var source = """ #nullable enable using System; class Program { static void Main() { object x = new object(); object y = null; object[] z = new[] { x }; F(z, () => [x]); F(z, () => [y]); } static void F<T>(T t, Func<T> f) { } } """; var comp = CreateCompilation(source); comp.VerifyEmitDiagnostics( // (8,20): warning CS8600: Converting null literal or possible null value to non-nullable type. // object y = null; Diagnostic(ErrorCode.WRN_ConvertingNullableToNonNullable, "null").WithLocation(8, 20), // (11,21): warning CS8601: Possible null reference assignment. // F(z, () => [y]); Diagnostic(ErrorCode.WRN_NullReferenceAssignment, "y").WithLocation(11, 21)); } [Fact] public void TargetTypedElement_PublicAPI_List() { var source = """ using System.Collections.Generic; class C { static void Main() { List<object> items = [new()]; } } """; var comp = CreateCompilation(source); comp.VerifyDiagnostics(); var tree = comp.SyntaxTrees[0]; var model = comp.GetSemanticModel(tree); var node = tree.GetRoot().DescendantNodes().OfType<ImplicitObjectCreationExpressionSyntax>().Single(); var info = model.GetSymbolInfo(node); Assert.Equal("object.Object()", info.Symbol.ToDisplayString()); model.VerifyOperationTree(tree.GetRoot().DescendantNodes().OfType<CollectionExpressionSyntax>().Single(), """ ICollectionExpressionOperation (1 elements, ConstructMethod: System.Collections.Generic.List<System.Object>..ctor()) (OperationKind.CollectionExpression, Type: System.Collections.Generic.List<System.Object>) (Syntax: '[new()]') Elements(1): IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Object, IsImplicit) (Syntax: 'new()') Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) Operand: IObjectCreationOperation (Constructor: System.Object..ctor()) (OperationKind.ObjectCreation, Type: System.Object) (Syntax: 'new()') Arguments(0) Initializer: null """); } [Fact] public void TargetTypedElement_PublicAPI_Array() { var source = """ class C { static void Main() { object[] items = [new()]; } } """; var comp = CreateCompilation(source); comp.VerifyDiagnostics(); var tree = comp.SyntaxTrees[0]; var model = comp.GetSemanticModel(tree); var node = tree.GetRoot().DescendantNodes().OfType<ImplicitObjectCreationExpressionSyntax>().Single(); var info = model.GetSymbolInfo(node); Assert.Equal("object.Object()", info.Symbol.ToDisplayString()); model.VerifyOperationTree(tree.GetRoot().DescendantNodes().OfType<CollectionExpressionSyntax>().Single(), """ ICollectionExpressionOperation (1 elements, ConstructMethod: null) (OperationKind.CollectionExpression, Type: System.Object[]) (Syntax: '[new()]') Elements(1): IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Object, IsImplicit) (Syntax: 'new()') Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) Operand: IObjectCreationOperation (Constructor: System.Object..ctor()) (OperationKind.ObjectCreation, Type: System.Object) (Syntax: 'new()') Arguments(0) Initializer: null """); } [Fact] public void TargetTypedElement_PublicAPI_Span() { var source = """ using System; class C { static void Main() { Span<object> items = [new()]; } } """; var comp = CreateCompilation(source, targetFramework: TargetFramework.Net80); comp.VerifyDiagnostics(); var tree = comp.SyntaxTrees[0]; var model = comp.GetSemanticModel(tree); var node = tree.GetRoot().DescendantNodes().OfType<ImplicitObjectCreationExpressionSyntax>().Single(); var info = model.GetSymbolInfo(node); Assert.Equal("object.Object()", info.Symbol.ToDisplayString()); model.VerifyOperationTree(tree.GetRoot().DescendantNodes().OfType<CollectionExpressionSyntax>().Single(), """ ICollectionExpressionOperation (1 elements, ConstructMethod: null) (OperationKind.CollectionExpression, Type: System.Span<System.Object>) (Syntax: '[new()]') Elements(1): IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Object, IsImplicit) (Syntax: 'new()') Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) Operand: IObjectCreationOperation (Constructor: System.Object..ctor()) (OperationKind.ObjectCreation, Type: System.Object) (Syntax: 'new()') Arguments(0) Initializer: null """); } [Fact] public void TargetTypedElement_PublicAPI_ReadOnlySpan() { var source = """ using System; class C { static void Main() { ReadOnlySpan<object> items = [new()]; } } """; var comp = CreateCompilation(source, targetFramework: TargetFramework.Net80); comp.VerifyDiagnostics(); var tree = comp.SyntaxTrees[0]; var model = comp.GetSemanticModel(tree); var node = tree.GetRoot().DescendantNodes().OfType<ImplicitObjectCreationExpressionSyntax>().Single(); var info = model.GetSymbolInfo(node); Assert.Equal("object.Object()", info.Symbol.ToDisplayString()); model.VerifyOperationTree(tree.GetRoot().DescendantNodes().OfType<CollectionExpressionSyntax>().Single(), """ ICollectionExpressionOperation (1 elements, ConstructMethod: null) (OperationKind.CollectionExpression, Type: System.ReadOnlySpan<System.Object>) (Syntax: '[new()]') Elements(1): IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Object, IsImplicit) (Syntax: 'new()') Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) Operand: IObjectCreationOperation (Constructor: System.Object..ctor()) (OperationKind.ObjectCreation, Type: System.Object) (Syntax: 'new()') Arguments(0) Initializer: null """); } [Fact] public void TargetTypedElement_PublicAPI_ImmutableArray() { var source = """ using System.Collections.Immutable; class C { static void Main() { ImmutableArray<object> items = [new()]; } } """; var comp = CreateCompilation(source, targetFramework: TargetFramework.Net80); comp.VerifyDiagnostics(); var tree = comp.SyntaxTrees[0]; var model = comp.GetSemanticModel(tree); var node = tree.GetRoot().DescendantNodes().OfType<ImplicitObjectCreationExpressionSyntax>().Single(); var info = model.GetSymbolInfo(node); Assert.Equal("object.Object()", info.Symbol.ToDisplayString()); model.VerifyOperationTree(tree.GetRoot().DescendantNodes().OfType<CollectionExpressionSyntax>().Single(), """ ICollectionExpressionOperation (1 elements, ConstructMethod: System.Collections.Immutable.ImmutableArray<System.Object> System.Collections.Immutable.ImmutableArray.Create<System.Object>(System.ReadOnlySpan<System.Object> items)) (OperationKind.CollectionExpression, Type: System.Collections.Immutable.Im