From eb7fcc3df8a1a29f6cd34b213114d9086f826f76 Mon Sep 17 00:00:00 2001 From: Charles Stoner <10732005+cston@users.noreply.github.com> Date: Tue, 7 Mar 2023 09:48:17 -0800 Subject: [PATCH] Collection literals natural type --- .../Portable/Binder/Binder.ValueChecks.cs | 9 +- .../Portable/Binder/Binder_Conversions.cs | 239 ++++-- .../Portable/Binder/Binder_Expressions.cs | 10 +- .../Portable/BoundTree/BoundExpression.cs | 1 + .../CSharp/Portable/BoundTree/BoundNodes.xml | 4 +- .../CSharp/Portable/CSharpResources.resx | 3 + .../CSharp/Portable/Errors/ErrorCode.cs | 1 + .../CSharp/Portable/Errors/ErrorFacts.cs | 1 + .../Generated/BoundNodes.xml.Generated.cs | 17 +- .../Portable/xlf/CSharpResources.cs.xlf | 5 + .../Portable/xlf/CSharpResources.de.xlf | 5 + .../Portable/xlf/CSharpResources.es.xlf | 5 + .../Portable/xlf/CSharpResources.fr.xlf | 5 + .../Portable/xlf/CSharpResources.it.xlf | 5 + .../Portable/xlf/CSharpResources.ja.xlf | 5 + .../Portable/xlf/CSharpResources.ko.xlf | 5 + .../Portable/xlf/CSharpResources.pl.xlf | 5 + .../Portable/xlf/CSharpResources.pt-BR.xlf | 5 + .../Portable/xlf/CSharpResources.ru.xlf | 5 + .../Portable/xlf/CSharpResources.tr.xlf | 5 + .../Portable/xlf/CSharpResources.zh-Hans.xlf | 5 + .../Portable/xlf/CSharpResources.zh-Hant.xlf | 5 + ...nTests_IArrayElementReferenceExpression.cs | 25 +- .../Semantics/CollectionLiteralTests.cs | 768 +++++++++++++----- 24 files changed, 863 insertions(+), 280 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs b/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs index 4eb8f05be9181..3ac9bbf781aa5 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs @@ -409,7 +409,14 @@ private BoundExpression CheckValue(BoundExpression expr, BindValueKind valueKind expr = BindIndexerDefaultArguments((BoundIndexerAccess)expr, valueKind, diagnostics); break; - case BoundKind.UnconvertedObjectCreationExpression: + case BoundKind.UnconvertedObjectCreationExpression: // PROTOTYPE: What is the effect of commenting out this case? Add a corresponding test for collection literals (next case). + if (valueKind == BindValueKind.RValue) + { + return expr; + } + break; + + case BoundKind.UnconvertedCollectionLiteralExpression: if (valueKind == BindValueKind.RValue) { return expr; diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Conversions.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Conversions.cs index ad1825936f54f..de838cbe7ea36 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Conversions.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Conversions.cs @@ -82,6 +82,7 @@ static bool filterConversion(Conversion conversion) return !conversion.IsInterpolatedString && !conversion.IsInterpolatedStringHandler && !conversion.IsSwitchExpression && + !conversion.IsCollectionLiteral && !(conversion.IsTupleLiteralConversion || (conversion.IsNullable && conversion.UnderlyingConversions[0].IsTupleLiteralConversion)) && (!conversion.IsUserDefined || filterConversion(conversion.UserDefinedFromConversion)); } @@ -426,7 +427,6 @@ private BoundExpression ConvertCollectionLiteralExpression( BindingDiagnosticBag diagnostics) { var syntax = (CSharpSyntaxNode)node.Syntax; - var implicitReceiver = new BoundObjectOrCollectionValuePlaceholder(syntax, isNewInstance: true, targetType) { WasCompilerGenerated = true }; TypeSymbol? elementType; BoundCollectionLiteralExpression collectionLiteral; @@ -436,21 +436,62 @@ private BoundExpression ConvertCollectionLiteralExpression( { Error(diagnostics, ErrorCode.ERR_CollectionLiteralTargetTypeNotConstructible, syntax, targetType); } - collectionLiteral = bindArrayOrSpan(syntax, spanConstructor: null, implicitReceiver, node.Initializers, arrayType.ElementType, diagnostics); + collectionLiteral = bindArrayOrSpan(syntax, targetType, spanConstructor: null, node.Initializers, arrayType.ElementType, diagnostics); } else if (isSpanType(targetType, WellKnownType.System_Span_T, out elementType)) { var spanConstructor = getSpanConstructor(syntax, targetType, WellKnownMember.System_Span_T__ctor_Array, diagnostics); - collectionLiteral = bindArrayOrSpan(syntax, spanConstructor, implicitReceiver, node.Initializers, elementType, diagnostics); + collectionLiteral = bindArrayOrSpan(syntax, targetType, spanConstructor, node.Initializers, elementType, diagnostics); } else if (isSpanType(targetType, WellKnownType.System_ReadOnlySpan_T, out elementType)) { var spanConstructor = getSpanConstructor(syntax, targetType, WellKnownMember.System_ReadOnlySpan_T__ctor_Array, diagnostics); - collectionLiteral = bindArrayOrSpan(syntax, spanConstructor, implicitReceiver, node.Initializers, elementType, diagnostics); + collectionLiteral = bindArrayOrSpan(syntax, targetType, spanConstructor, node.Initializers, elementType, diagnostics); + } + else if (targetType.IsErrorType()) + { + collectionLiteral = BindCollectionInitializerCollectionLiteral(node, targetType, wasTargetTyped: true, hasEnumerableInitializerType: false, wasCompilerGenerated: wasCompilerGenerated, diagnostics); } else { - collectionLiteral = bindCollectionInitializer(syntax, implicitReceiver, node.Initializers, diagnostics); + bool hasEnumerableInitializerType = collectionTypeImplementsIEnumerable(targetType, syntax, diagnostics); + if (!hasEnumerableInitializerType) + { + // Target type is not constructible. Use collection literal natural type if available, instead. + if (node.Type is { } collectionType) + { + var expr = BindCollectionInitializerCollectionLiteral( + node, + collectionType, + wasTargetTyped: false, + hasEnumerableInitializerType: true, + wasCompilerGenerated: node.WasCompilerGenerated, + diagnostics); + Debug.Assert(expr.Type is { }); + // PROTOTYPE: Test various conversions, including conversions expected to fail. + var useSiteInfo = GetNewCompoundUseSiteInfo(diagnostics); + conversion = Conversions.ClassifyImplicitConversionFromType(expr.Type, targetType, ref useSiteInfo); + // PROTOTYPE: Test. + diagnostics.Add(syntax, useSiteInfo); + bool hasErrors = !conversion.IsValid; + if (hasErrors) + { + GenerateImplicitConversionError(diagnostics, syntax, conversion, expr, targetType); + } + return CreateConversion( + syntax, + expr, + conversion, + isCast: false, + conversionGroupOpt: null, + wasCompilerGenerated: wasCompilerGenerated, + targetType, + diagnostics, + hasErrors: hasErrors); + } + Error(diagnostics, ErrorCode.ERR_CollectionLiteralTargetTypeNotConstructible, syntax, targetType); + } + collectionLiteral = BindCollectionInitializerCollectionLiteral(node, targetType, wasTargetTyped: true, hasEnumerableInitializerType: hasEnumerableInitializerType, wasCompilerGenerated: wasCompilerGenerated, diagnostics); } return new BoundConversion( @@ -477,12 +518,13 @@ bool isSpanType(TypeSymbol targetType, WellKnownType spanType, [NotNullWhen(true BoundArrayOrSpanCollectionLiteralExpression bindArrayOrSpan( CSharpSyntaxNode syntax, + TypeSymbol targetType, MethodSymbol? spanConstructor, - BoundObjectOrCollectionValuePlaceholder implicitReceiver, ImmutableArray elements, TypeSymbol elementType, BindingDiagnosticBag diagnostics) { + var implicitReceiver = new BoundObjectOrCollectionValuePlaceholder(syntax, isNewInstance: true, targetType) { WasCompilerGenerated = true }; var builder = ArrayBuilder.GetInstance(elements.Length); foreach (var element in elements) { @@ -499,62 +541,6 @@ BoundArrayOrSpanCollectionLiteralExpression bindArrayOrSpan( { WasCompilerGenerated = wasCompilerGenerated }; } - BoundCollectionInitializerCollectionLiteralExpression bindCollectionInitializer( - CSharpSyntaxNode syntax, - BoundObjectOrCollectionValuePlaceholder implicitReceiver, - ImmutableArray elements, - BindingDiagnosticBag diagnostics) - { - BoundExpression collectionCreation; - if (targetType is NamedTypeSymbol namedType) - { - var analyzedArguments = AnalyzedArguments.GetInstance(); - collectionCreation = BindClassCreationExpression(syntax, namedType.Name, syntax, namedType, analyzedArguments, diagnostics); - collectionCreation.WasCompilerGenerated = true; - analyzedArguments.Free(); - } - else if (targetType is TypeParameterSymbol typeParameter) - { - var arguments = AnalyzedArguments.GetInstance(); - collectionCreation = BindTypeParameterCreationExpression(syntax, typeParameter, arguments, initializerOpt: null, typeSyntax: syntax, wasTargetTyped: true, diagnostics); - arguments.Free(); - } - else - { - collectionCreation = new BoundBadExpression(syntax, LookupResultKind.NotCreatable, ImmutableArray.Empty, ImmutableArray.Empty, targetType); - } - - bool hasEnumerableInitializerType = collectionTypeImplementsIEnumerable(targetType, syntax, diagnostics); - if (!hasEnumerableInitializerType && !targetType.IsErrorType()) - { - Error(diagnostics, ErrorCode.ERR_CollectionLiteralTargetTypeNotConstructible, syntax, targetType); - } - - var collectionInitializerAddMethodBinder = this.WithAdditionalFlags(BinderFlags.CollectionInitializerAddMethod); - var builder = ArrayBuilder.GetInstance(node.Initializers.Length); - foreach (var element in node.Initializers) - { - var result = BindCollectionInitializerElementAddMethod( - (ExpressionSyntax)element.Syntax, - ImmutableArray.Create(element), - hasEnumerableInitializerType, - collectionInitializerAddMethodBinder, - diagnostics, - implicitReceiver); - result.WasCompilerGenerated = true; - builder.Add(result); - } - return new BoundCollectionInitializerCollectionLiteralExpression( - syntax, - collectionCreation, - naturalTypeOpt: null, // PROTOTYPE: Support natural type. - wasTargetTyped: true, // PROTOTYPE: Support natural type. - implicitReceiver, - builder.ToImmutableAndFree(), - targetType) - { WasCompilerGenerated = wasCompilerGenerated }; - } - MethodSymbol? getSpanConstructor(CSharpSyntaxNode syntax, TypeSymbol spanType, WellKnownMember spanMember, BindingDiagnosticBag diagnostics) { return (MethodSymbol?)GetWellKnownTypeMember(spanMember, diagnostics, syntax: syntax)?.SymbolAsMember((NamedTypeSymbol)spanType); @@ -574,7 +560,7 @@ bool collectionTypeImplementsIEnumerable(TypeSymbol targetType, CSharpSyntaxNode BoundExpression convertArrayElement(BoundExpression element, TypeSymbol elementType, BindingDiagnosticBag diagnostics) { - CompoundUseSiteInfo useSiteInfo = GetNewCompoundUseSiteInfo(diagnostics); + var useSiteInfo = GetNewCompoundUseSiteInfo(diagnostics); var conversion = Conversions.ClassifyImplicitConversionFromExpression(element, elementType, ref useSiteInfo); diagnostics.Add(element.Syntax, useSiteInfo); bool hasErrors = !conversion.IsValid; @@ -590,13 +576,138 @@ BoundExpression convertArrayElement(BoundExpression element, TypeSymbol elementT conversionGroupOpt: null, wasCompilerGenerated: true, destination: elementType, - diagnostics: diagnostics, + diagnostics, hasErrors: hasErrors); result.WasCompilerGenerated = true; return result; } } + private BoundCollectionInitializerCollectionLiteralExpression BindCollectionInitializerCollectionLiteral( + BoundUnconvertedCollectionLiteralExpression node, + TypeSymbol targetType, + bool wasTargetTyped, + bool hasEnumerableInitializerType, + bool wasCompilerGenerated, + BindingDiagnosticBag diagnostics) + { + Debug.Assert(wasTargetTyped || + targetType.IsErrorType() || + targetType.Equals(node.Type, TypeCompareKind.ConsiderEverything)); + + var syntax = node.Syntax; + + BoundExpression collectionCreation; + if (targetType is NamedTypeSymbol namedType) + { + var analyzedArguments = AnalyzedArguments.GetInstance(); + // PROTOTYPE: Use List(int capacity) constructor when size is known, and when using natural type. + // What about when target-typed to List? We should still use the int capacity constructor in that case. + // Review with LDM. + collectionCreation = BindClassCreationExpression(syntax, namedType.Name, syntax, namedType, analyzedArguments, diagnostics); + collectionCreation.WasCompilerGenerated = true; + analyzedArguments.Free(); + } + else if (targetType is TypeParameterSymbol typeParameter) + { + var arguments = AnalyzedArguments.GetInstance(); + collectionCreation = BindTypeParameterCreationExpression(syntax, typeParameter, arguments, initializerOpt: null, typeSyntax: syntax, wasTargetTyped: true, diagnostics); + arguments.Free(); + } + else + { + collectionCreation = new BoundBadExpression(syntax, LookupResultKind.NotCreatable, ImmutableArray.Empty, ImmutableArray.Empty, targetType); + } + + var implicitReceiver = new BoundObjectOrCollectionValuePlaceholder(syntax, isNewInstance: true, targetType) { WasCompilerGenerated = true }; + var collectionInitializerAddMethodBinder = this.WithAdditionalFlags(BinderFlags.CollectionInitializerAddMethod); + var builder = ArrayBuilder.GetInstance(node.Initializers.Length); + foreach (var element in node.Initializers) + { + var result = BindCollectionInitializerElementAddMethod( + element.Syntax, + ImmutableArray.Create(element), + hasEnumerableInitializerType, + collectionInitializerAddMethodBinder, + diagnostics, + implicitReceiver); + result.WasCompilerGenerated = true; + builder.Add(result); + } + return new BoundCollectionInitializerCollectionLiteralExpression( + syntax, + collectionCreation, + naturalTypeOpt: node.Type, + wasTargetTyped: wasTargetTyped, + implicitReceiver, + builder.ToImmutableAndFree(), + targetType) + { WasCompilerGenerated = wasCompilerGenerated }; + } + + // PROTOTYPE: Inline method if there just a single caller. + private TypeSymbol? InferCollectionLiteralNaturalType(ImmutableArray initializers) + { + // PROTOTYPE: Ensure use-site error is reported, and only reported when the natural type is used. + // Where is the use-site error reported from BoundUnconvertedSwitchExpression? + var useSiteInfo = CompoundUseSiteInfo.Discarded; + // PROTOTYPE: Confirm with LDM that we use the element expressions rather than + // the element types to infer best common type. + var commonType = BestTypeInferrer.InferBestType(initializers, Conversions, ref useSiteInfo, out _); + if (commonType is null) + { + return null; + } + + /* PROTOTYPE: From BindImplicitArrayCreationExpression(). Are these reported always, even if the natural type is not used? + if ((object)bestType == null || bestType.IsVoidType()) // Dev10 also reports ERR_ImplicitlyTypedArrayNoBestType for void. + { + Error(diagnostics, ErrorCode.ERR_ImplicitlyTypedArrayNoBestType, node); + bestType = CreateErrorType(); + } + + if (bestType.IsRestrictedType()) + { + // CS0611: Array elements cannot be of type '{0}' + Error(diagnostics, ErrorCode.ERR_ArrayElementCantBeRefAny, node, bestType); + } + */ + + // PROTOTYPE: When using the natural type, verify each element can be converted to the + // common type, even if there is an Add() overload for the unconverted value. + + // PROTOTYPE: Ensure use-site error is reported when List is missing. See Binder.GetWellKnownType(). + // Clone test EnumType_02() and remove type List for instance. + return Compilation.GetWellKnownType(WellKnownType.System_Collections_Generic_List_T).Construct(commonType); + } + + // PROTOTYPE: Inline method if there just a single caller. + private BoundExpression ConvertCollectionLiteralExpressionToNaturalType( + BoundUnconvertedCollectionLiteralExpression node, + BindingDiagnosticBag diagnostics) + { + bool hasErrors = node.HasErrors; + var syntax = (CollectionCreationExpressionSyntax)node.Syntax; + var collectionType = node.Type; + if (collectionType is null) + { + diagnostics.Add(ErrorCode.ERR_ImplicitlyTypedCollectionLiteralNoBestType, syntax.Location); + collectionType = CreateErrorType(); + hasErrors = true; + } + + // PROTOTYPE: We're dropping hasErrors. + // PROTOTYPE: Do we need a Conversion here? For a BoundConvertedSwitchExpression, + // we're adding an Identity conversion when using the natural type rather than target type. + return BindCollectionInitializerCollectionLiteral( + node, + collectionType, + wasTargetTyped: false, + hasEnumerableInitializerType: true, + wasCompilerGenerated: node.WasCompilerGenerated, + diagnostics); + } + /// /// Rewrite the subexpressions in a conditional expression to convert the whole thing to the destination type. /// diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs index 64222d1f7db87..741e7b029fd0f 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs @@ -386,6 +386,9 @@ internal BoundExpression BindToNaturalType(BoundExpression expression, BindingDi result = RebindSimpleBinaryOperatorAsConverted(unconvertedBinaryOperator, diagnostics); } break; + case BoundUnconvertedCollectionLiteralExpression collectionLiteral: + result = ConvertCollectionLiteralExpressionToNaturalType(collectionLiteral, diagnostics); + break; default: result = expression; break; @@ -4490,7 +4493,10 @@ private BoundExpression BindCollectionLiteralExpression(CollectionCreationExpres { builder.Add(bindElement(element, diagnostics)); } - return new BoundUnconvertedCollectionLiteralExpression(syntax, builder.ToImmutableAndFree(), this); + + var initializers = builder.ToImmutableAndFree(); + var naturalType = InferCollectionLiteralNaturalType(initializers); + return new BoundUnconvertedCollectionLiteralExpression(syntax, initializers, this, type: naturalType); BoundExpression bindElement(CollectionElementSyntax syntax, BindingDiagnosticBag diagnostics) { @@ -5510,7 +5516,7 @@ private BoundExpression BindUnexpectedComplexElementInitializer(InitializerExpre } private BoundExpression BindCollectionInitializerElementAddMethod( - ExpressionSyntax elementInitializer, + SyntaxNode elementInitializer, ImmutableArray boundElementInitializerExpressions, bool hasEnumerableInitializerType, Binder collectionInitializerAddMethodBinder, diff --git a/src/Compilers/CSharp/Portable/BoundTree/BoundExpression.cs b/src/Compilers/CSharp/Portable/BoundTree/BoundExpression.cs index 320953b548abe..0f161a2936e1f 100644 --- a/src/Compilers/CSharp/Portable/BoundTree/BoundExpression.cs +++ b/src/Compilers/CSharp/Portable/BoundTree/BoundExpression.cs @@ -60,6 +60,7 @@ internal bool NeedsToBeConverted() case BoundKind.UnconvertedConditionalOperator: case BoundKind.DefaultLiteral: case BoundKind.UnconvertedInterpolatedString: + case BoundKind.UnconvertedCollectionLiteralExpression: return true; case BoundKind.StackAllocArrayCreation: // A BoundStackAllocArrayCreation is given a null type when it is in a diff --git a/src/Compilers/CSharp/Portable/BoundTree/BoundNodes.xml b/src/Compilers/CSharp/Portable/BoundTree/BoundNodes.xml index 523c2fcc16961..dc9e450093dba 100644 --- a/src/Compilers/CSharp/Portable/BoundTree/BoundNodes.xml +++ b/src/Compilers/CSharp/Portable/BoundTree/BoundNodes.xml @@ -1867,12 +1867,10 @@ - - diff --git a/src/Compilers/CSharp/Portable/CSharpResources.resx b/src/Compilers/CSharp/Portable/CSharpResources.resx index 5e929dd058c11..8125e3cef39f5 100644 --- a/src/Compilers/CSharp/Portable/CSharpResources.resx +++ b/src/Compilers/CSharp/Portable/CSharpResources.resx @@ -6789,6 +6789,9 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ Support for collection literal dictionary and spread elements has not been implemented. + + No best type found for implicitly-typed collection literal. + Record equality contract property '{0}' must have a get accessor. diff --git a/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs b/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs index f1b25b90547e1..6d76b272df881 100644 --- a/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs +++ b/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs @@ -2190,6 +2190,7 @@ internal enum ErrorCode ERR_CollectionLiteralTargetTypeNotConstructible = 9500, // PROTOTYPE: Update error numbers. ERR_ExpressionTreeContainsCollectionLiteral = 9501, ERR_CollectionLiteralElementNotImplemented = 9502, // PROTOTYPE: Temporary error until feature has been implemented. + ERR_ImplicitlyTypedCollectionLiteralNoBestType = 9503, #endregion diff --git a/src/Compilers/CSharp/Portable/Errors/ErrorFacts.cs b/src/Compilers/CSharp/Portable/Errors/ErrorFacts.cs index 1cf9037f420d1..bc933c82035de 100644 --- a/src/Compilers/CSharp/Portable/Errors/ErrorFacts.cs +++ b/src/Compilers/CSharp/Portable/Errors/ErrorFacts.cs @@ -2310,6 +2310,7 @@ internal static bool IsBuildOnlyDiagnostic(ErrorCode code) case ErrorCode.ERR_CollectionLiteralTargetTypeNotConstructible: case ErrorCode.ERR_ExpressionTreeContainsCollectionLiteral: case ErrorCode.ERR_CollectionLiteralElementNotImplemented: + case ErrorCode.ERR_ImplicitlyTypedCollectionLiteralNoBestType: return false; default: // NOTE: All error codes must be explicitly handled in this switch statement diff --git a/src/Compilers/CSharp/Portable/Generated/BoundNodes.xml.Generated.cs b/src/Compilers/CSharp/Portable/Generated/BoundNodes.xml.Generated.cs index d0a65a076783b..80b597a4c9e2a 100644 --- a/src/Compilers/CSharp/Portable/Generated/BoundNodes.xml.Generated.cs +++ b/src/Compilers/CSharp/Portable/Generated/BoundNodes.xml.Generated.cs @@ -6279,8 +6279,8 @@ public BoundObjectCreationExpression Update(MethodSymbol constructor, ImmutableA internal sealed partial class BoundUnconvertedCollectionLiteralExpression : BoundExpression { - public BoundUnconvertedCollectionLiteralExpression(SyntaxNode syntax, ImmutableArray initializers, Binder binder, bool hasErrors = false) - : base(BoundKind.UnconvertedCollectionLiteralExpression, syntax, null, hasErrors || initializers.HasErrors()) + public BoundUnconvertedCollectionLiteralExpression(SyntaxNode syntax, ImmutableArray initializers, Binder binder, TypeSymbol? type, bool hasErrors = false) + : base(BoundKind.UnconvertedCollectionLiteralExpression, syntax, type, hasErrors || initializers.HasErrors()) { RoslynDebug.Assert(!initializers.IsDefault, "Field 'initializers' cannot be null (use Null=\"allow\" in BoundNodes.xml to remove this check)"); @@ -6290,18 +6290,17 @@ public BoundUnconvertedCollectionLiteralExpression(SyntaxNode syntax, ImmutableA this.Binder = binder; } - public new TypeSymbol? Type => base.Type; public ImmutableArray Initializers { get; } public Binder Binder { get; } [DebuggerStepThrough] public override BoundNode? Accept(BoundTreeVisitor visitor) => visitor.VisitUnconvertedCollectionLiteralExpression(this); - public BoundUnconvertedCollectionLiteralExpression Update(ImmutableArray initializers, Binder binder) + public BoundUnconvertedCollectionLiteralExpression Update(ImmutableArray initializers, Binder binder, TypeSymbol? type) { - if (initializers != this.Initializers || binder != this.Binder) + if (initializers != this.Initializers || binder != this.Binder || !TypeSymbol.Equals(type, this.Type, TypeCompareKind.ConsiderEverything)) { - var result = new BoundUnconvertedCollectionLiteralExpression(this.Syntax, initializers, binder, this.HasErrors); + var result = new BoundUnconvertedCollectionLiteralExpression(this.Syntax, initializers, binder, type, this.HasErrors); result.CopyAttributes(this); return result; } @@ -11523,7 +11522,7 @@ internal abstract partial class BoundTreeRewriter : BoundTreeVisitor { ImmutableArray initializers = this.VisitList(node.Initializers); TypeSymbol? type = this.VisitType(node.Type); - return node.Update(initializers, node.Binder); + return node.Update(initializers, node.Binder, type); } public override BoundNode? VisitArrayOrSpanCollectionLiteralExpression(BoundArrayOrSpanCollectionLiteralExpression node) { @@ -13729,12 +13728,12 @@ public NullabilityRewriter(ImmutableDictionaryVolání implicitního indexeru rozsahů nemůže pojmenovat argument. + + No best type found for implicitly-typed collection literal. + No best type found for implicitly-typed collection literal. + + Implicitly typed lambda parameter '{0}' cannot have a default value. Implicitně zadaný parametr lambda {0} nemůže mít výchozí hodnotu. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf index f39e734ac4ca6..9064d89a30fa7 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.de.xlf @@ -817,6 +817,11 @@ Durch den Aufruf des impliziten Bereichsindexers kann das Argument nicht benannt werden. + + No best type found for implicitly-typed collection literal. + No best type found for implicitly-typed collection literal. + + Implicitly typed lambda parameter '{0}' cannot have a default value. Der implizit eingegebene Parameter „{0}“ der Lambdafunktion darf keinen Standardwert aufweisen. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf index 4dcb205282aed..baac57326abc0 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.es.xlf @@ -817,6 +817,11 @@ La invocación del indizador de rangos implícito no puede nombrar el argumento. + + No best type found for implicitly-typed collection literal. + No best type found for implicitly-typed collection literal. + + Implicitly typed lambda parameter '{0}' cannot have a default value. El parámetro lambda con tipo implícito “{0}” no puede tener un valor predeterminado. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf index 83805ce68ee62..fae9ceffaef74 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.fr.xlf @@ -817,6 +817,11 @@ L'appel de l'indexeur de plage implicite ne peut pas nommer l'argument. + + No best type found for implicitly-typed collection literal. + No best type found for implicitly-typed collection literal. + + Implicitly typed lambda parameter '{0}' cannot have a default value. Le paramètre d’expression lambda implicitement typé '{0}' ne peut pas avoir de valeur par défaut. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf index b3c68f7a01c9f..b4971c1745bb6 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.it.xlf @@ -817,6 +817,11 @@ La chiamata dell'indicizzatore di intervallo implicito non può assegnare un nome all'argomento. + + No best type found for implicitly-typed collection literal. + No best type found for implicitly-typed collection literal. + + Implicitly typed lambda parameter '{0}' cannot have a default value. Il parametro lambda tipizzato in modo implicito '{0}' non può avere un valore predefinito. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf index cb4b508127c84..8b75ca670e4fe 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf @@ -817,6 +817,11 @@ 暗黙的な範囲インデクサーの呼び出しでは、引数に名前を付けることはできません。 + + No best type found for implicitly-typed collection literal. + No best type found for implicitly-typed collection literal. + + Implicitly typed lambda parameter '{0}' cannot have a default value. 暗黙的に型指定されたラムダパラメーター '{0}' に既定値を指定することはできません。 diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf index f3ab074749c37..24998bd77740d 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ko.xlf @@ -817,6 +817,11 @@ 암시적 범위 인덱서 호출로 인수 이름을 지정할 수 없습니다. + + No best type found for implicitly-typed collection literal. + No best type found for implicitly-typed collection literal. + + Implicitly typed lambda parameter '{0}' cannot have a default value. 암시적 형식 람다 매개 변수 '{0}'은(는) 기본값을 가질 수 없습니다. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf index bcc80eff82ab8..1f539906f526c 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pl.xlf @@ -817,6 +817,11 @@ W wywołaniu niejawnego indeksatora zakresu nie może być nazwy argumentu. + + No best type found for implicitly-typed collection literal. + No best type found for implicitly-typed collection literal. + + Implicitly typed lambda parameter '{0}' cannot have a default value. Niejawnie typizowany parametr wyrażenia lambda „{0}” nie może mieć wartości domyślnej. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf index c2286c06930e8..1bea5380245bc 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.pt-BR.xlf @@ -817,6 +817,11 @@ A invocação do Indexador de Intervalo implícito não pode nomear o argumento. + + No best type found for implicitly-typed collection literal. + No best type found for implicitly-typed collection literal. + + Implicitly typed lambda parameter '{0}' cannot have a default value. O parâmetro lambda '{0}' digitado implicitamente não pode ter um valor padrão. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf index 879430e307914..6ad99a6ac9eef 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.ru.xlf @@ -817,6 +817,11 @@ Вызов неявного индексатора для диапазона не может присвоить аргументу имя. + + No best type found for implicitly-typed collection literal. + No best type found for implicitly-typed collection literal. + + Implicitly typed lambda parameter '{0}' cannot have a default value. Неявно типизованный параметр "{0}" не может иметь значение по умолчанию. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf index 31746a11ca113..36f330eb2b048 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.tr.xlf @@ -817,6 +817,11 @@ Örtük Aralık Dizin Oluşturucu'nun çağrılması bağımsız değişkeni adlandıramaz. + + No best type found for implicitly-typed collection literal. + No best type found for implicitly-typed collection literal. + + Implicitly typed lambda parameter '{0}' cannot have a default value. Türü örtük olarak belirlenmiş lambda '{0}' parametresi varsayılan bir değere sahip olamaz. diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf index 6709039f3263d..3bd6c2b09696d 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hans.xlf @@ -817,6 +817,11 @@ 无法通过对隐式范围索引器的调用为参数命名。 + + No best type found for implicitly-typed collection literal. + No best type found for implicitly-typed collection literal. + + Implicitly typed lambda parameter '{0}' cannot have a default value. 隐式键入的 lambda 参数 "{0}" 不能具有默认值。 diff --git a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf index 96706726d6601..1b1b10ceef4eb 100644 --- a/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf +++ b/src/Compilers/CSharp/Portable/xlf/CSharpResources.zh-Hant.xlf @@ -817,6 +817,11 @@ 隱含 Range 索引子的引動過程無法為引數命名。 + + No best type found for implicitly-typed collection literal. + No best type found for implicitly-typed collection literal. + + Implicitly typed lambda parameter '{0}' cannot have a default value. 隱含輸入的 Lambda 參數 '{0}' 不能有預設值。 diff --git a/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_IArrayElementReferenceExpression.cs b/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_IArrayElementReferenceExpression.cs index 69f8647de670f..4a69fc49034b5 100644 --- a/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_IArrayElementReferenceExpression.cs +++ b/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_IArrayElementReferenceExpression.cs @@ -519,16 +519,21 @@ public void F() "; string expectedOperationTree = @" -IInvalidOperation (OperationKind.Invalid, Type: null, IsInvalid) (Syntax: '[0]') - Children(1): - ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 0, IsInvalid) (Syntax: '0') -"; - var expectedDiagnostics = new DiagnosticDescription[] - { - // (6,13): error CS0815: Cannot assign collection literals to an implicitly-typed variable - // var a = /**/[0]/**/; - Diagnostic(ErrorCode.ERR_ImplicitlyTypedVariableAssignedBadValue, "a = /**/[0]").WithArguments("collection literals").WithLocation(6, 13) - }; +IObjectCreationOperation (Constructor: System.Collections.Generic.List..ctor()) (OperationKind.ObjectCreation, Type: System.Collections.Generic.List) (Syntax: '[0]') + Arguments(0) + Initializer: + IObjectOrCollectionInitializerOperation (OperationKind.ObjectOrCollectionInitializer, Type: System.Collections.Generic.List, IsImplicit) (Syntax: '[0]') + Initializers(1): + IInvocationOperation ( void System.Collections.Generic.List.Add(System.Int32 item)) (OperationKind.Invocation, Type: System.Void, IsImplicit) (Syntax: '0') + Instance Receiver: + IInstanceReferenceOperation (ReferenceKind: ImplicitReceiver) (OperationKind.InstanceReference, Type: System.Collections.Generic.List, IsImplicit) (Syntax: '[0]') + Arguments(1): + IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: item) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: '0') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 0) (Syntax: '0') + 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) +"; + var expectedDiagnostics = new DiagnosticDescription[0]; VerifyOperationTreeAndDiagnosticsForTest(source, expectedOperationTree, expectedDiagnostics); } diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/CollectionLiteralTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/CollectionLiteralTests.cs index beb63a89e101f..166f0519eb261 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/CollectionLiteralTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/CollectionLiteralTests.cs @@ -15,68 +15,93 @@ namespace Microsoft.CodeAnalysis.CSharp.UnitTests { public class CollectionLiteralTests : CSharpTestBase { - private static string GetCollectionExtensions(bool includeSpan = false) - { - string source = """ - using System; - using System.Collections; - using System.Collections.Generic; - using System.Text; - static partial class CollectionExtensions + private 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) { - private static StringBuilder Begin() + if (!isFirst) builder.Append(", "); + if (value is IEnumerable e && value is not string) { - var builder = new StringBuilder(); - builder.Append("["); - return builder; + AppendCollection(builder, e); } - private static string End(StringBuilder builder) + else { - builder.Append("]"); - return builder.ToString(); + builder.Append(value is null ? "null" : value.ToString()); } - private static void Append(StringBuilder builder, object value) + } + private static void AppendCollection(StringBuilder builder, IEnumerable e) + { + builder.Append("["); + bool isFirst = true; + foreach (var i in e) { - if (builder.Length > 1) builder.Append(", "); - builder.Append(value is null ? "null" : value.ToString()); + Append(builder, isFirst, i); + isFirst = false; } - internal static void Report(this IEnumerable e) + 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(GetTypeName(o.GetType())); + Console.Write(builder.ToString()); + Console.Write(", "); + } + private static string GetTypeName(Type type) + { + if (type.IsArray) { - var b = Begin(); - foreach (var i in e) Append(b, i); - Console.Write(End(b)); - Console.Write(", "); + return GetTypeName(type.GetElementType()) + "[]"; } - internal static void Report(this object o) + string typeName = type.Name; + int index = typeName.LastIndexOf('`'); + if (index >= 0) { - var b = Begin(); - foreach (var i in ((IEnumerable)o)) Append(b, i); - Console.Write(End(b)); - Console.Write(", "); + typeName = typeName.Substring(0, index); } + typeName = Concat(type.Namespace, typeName); + if (!type.IsGenericType) + { + return typeName; + } + return $"{typeName}<{string.Join(", ", type.GetGenericArguments().Select(GetTypeName))}>"; } - """; - if (includeSpan) + private static string Concat(string container, string name) + { + return string.IsNullOrEmpty(container) ? name : container + "." + name; + } + } + """; + private const string s_collectionExtensionsWithSpan = s_collectionExtensions + + """ + static partial class CollectionExtensions { - source += """ - static partial class CollectionExtensions + internal static void Report(this in Span s) + { + Report((ReadOnlySpan)s); + } + internal static void Report(this in ReadOnlySpan s) + { + var builder = new StringBuilder(); + builder.Append("["); + bool isFirst = true; + foreach (var i in s) { - internal static void Report(this in Span s) - { - Report((ReadOnlySpan)s); - } - internal static void Report(this in ReadOnlySpan s) - { - var b = Begin(); - foreach (var i in s) Append(b, i); - Console.Write(End(b)); - Console.Write(", "); - } + Append(builder, isFirst, i); + isFirst = false; } - """; + builder.Append("]"); + Console.Write(builder.ToString()); + Console.Write(", "); + } } - return source; - } + """; [Theory] [InlineData(LanguageVersion.CSharp11)] @@ -133,6 +158,7 @@ static void Main() } """; var comp = CreateCompilation(source); + // PROTOTYPE: Should report ERR_ImplicitlyTypedCollectionLiteralNoBestType for x, y, z. comp.VerifyEmitDiagnostics( // (5,20): error CS9500: Cannot initialize type 'object' with a collection literal because the type is not constructible. // object x = []; @@ -140,9 +166,9 @@ static void Main() // (6,21): error CS9500: Cannot initialize type 'dynamic' with a collection literal because the type is not constructible. // dynamic y = []; Diagnostic(ErrorCode.ERR_CollectionLiteralTargetTypeNotConstructible, "[]").WithArguments("dynamic").WithLocation(6, 21), - // (7,13): error CS0815: Cannot assign collection literals to an implicitly-typed variable + // (7,17): error CS9503: No best type found for implicitly-typed collection literal. // var z = []; - Diagnostic(ErrorCode.ERR_ImplicitlyTypedVariableAssignedBadValue, "z = []").WithArguments("collection literals").WithLocation(7, 13)); + Diagnostic(ErrorCode.ERR_ImplicitlyTypedCollectionLiteralNoBestType, "[]").WithLocation(7, 17)); } [Fact] @@ -156,24 +182,114 @@ static void Main() object x = [1]; dynamic y = [2]; var z = [3]; + x.Report(includeType: true); + ((object)y).Report(includeType: true); + z.Report(includeType: true); + } + } + """; + CompileAndVerify(new[] { source, s_collectionExtensions }, references: new[] { CSharpRef }, expectedOutput: + "System.Collections.Generic.List[1], System.Collections.Generic.List[2], System.Collections.Generic.List[3], "); + + // PROTOTYPE: Use semantic model to verify types and converted types. See SemanticModel() test below. + } + + [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); + // PROTOTYPE: Should report ERR_ImplicitlyTypedCollectionLiteralNoBestType for x and y. comp.VerifyEmitDiagnostics( // (5,20): error CS9500: Cannot initialize type 'object' with a collection literal because the type is not constructible. - // object x = [1]; - Diagnostic(ErrorCode.ERR_CollectionLiteralTargetTypeNotConstructible, "[1]").WithArguments("object").WithLocation(5, 20), + // object x = [1, ""]; + Diagnostic(ErrorCode.ERR_CollectionLiteralTargetTypeNotConstructible, @"[1, """"]").WithArguments("object").WithLocation(5, 20), // (6,21): error CS9500: Cannot initialize type 'dynamic' with a collection literal because the type is not constructible. - // dynamic y = [2]; - Diagnostic(ErrorCode.ERR_CollectionLiteralTargetTypeNotConstructible, "[2]").WithArguments("dynamic").WithLocation(6, 21), - // (7,13): error CS0815: Cannot assign collection literals to an implicitly-typed variable - // var z = [3]; - Diagnostic(ErrorCode.ERR_ImplicitlyTypedVariableAssignedBadValue, "z = [3]").WithArguments("collection literals").WithLocation(7, 13)); + // dynamic y = [2, ""]; + Diagnostic(ErrorCode.ERR_CollectionLiteralTargetTypeNotConstructible, @"[2, """"]").WithArguments("dynamic").WithLocation(6, 21), + // (7,17): error CS9503: No best type found for implicitly-typed collection literal. + // var z = [3, ""]; + Diagnostic(ErrorCode.ERR_ImplicitlyTypedCollectionLiteralNoBestType, @"[3, """"]").WithLocation(7, 17)); } [Fact] - public void NaturalType_03() + 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); + // PROTOTYPE: Should report ERR_ImplicitlyTypedCollectionLiteralNoBestType for x and y. + comp.VerifyEmitDiagnostics( + // (5,20): error CS9500: Cannot initialize type 'object' with a collection literal because the type is not constructible. + // object x = [null]; + Diagnostic(ErrorCode.ERR_CollectionLiteralTargetTypeNotConstructible, "[null]").WithArguments("object").WithLocation(5, 20), + // (6,21): error CS9500: Cannot initialize type 'dynamic' with a collection literal because the type is not constructible. + // dynamic y = [null]; + Diagnostic(ErrorCode.ERR_CollectionLiteralTargetTypeNotConstructible, "[null]").WithArguments("dynamic").WithLocation(6, 21), + // (7,17): error CS9503: No best type found for implicitly-typed collection literal. + // var z = [null]; + Diagnostic(ErrorCode.ERR_ImplicitlyTypedCollectionLiteralNoBestType, "[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,24): error CS1950: The best overloaded Add method 'List.Add(int)' for the collection initializer has some invalid arguments + // var x = [1, 2, null]; + Diagnostic(ErrorCode.ERR_BadArgTypesForCollectionAdd, "null").WithArguments("System.Collections.Generic.List.Add(int)").WithLocation(5, 24), + // (5,24): error CS1503: Argument 1: cannot convert from '' to 'int' + // var x = [1, 2, null]; + Diagnostic(ErrorCode.ERR_BadArgType, "null").WithArguments("1", "", "int").WithLocation(5, 24), + // (6,27): error CS1950: The best overloaded Add method 'List.Add(int)' for the collection initializer has some invalid arguments + // object y = [1, 2, null]; + Diagnostic(ErrorCode.ERR_BadArgTypesForCollectionAdd, "null").WithArguments("System.Collections.Generic.List.Add(int)").WithLocation(6, 27), + // (6,27): error CS1503: Argument 1: cannot convert from '' to 'int' + // object y = [1, 2, null]; + Diagnostic(ErrorCode.ERR_BadArgType, "null").WithArguments("1", "", "int").WithLocation(6, 27), + // (7,28): error CS1950: The best overloaded Add method 'List.Add(int)' for the collection initializer has some invalid arguments + // dynamic z = [1, 2, null]; + Diagnostic(ErrorCode.ERR_BadArgTypesForCollectionAdd, "null").WithArguments("System.Collections.Generic.List.Add(int)").WithLocation(7, 28), + // (7,28): error CS1503: Argument 1: cannot convert from '' to 'int' + // dynamic z = [1, 2, null]; + Diagnostic(ErrorCode.ERR_BadArgType, "null").WithArguments("1", "", "int").WithLocation(7, 28)); + } + + [Fact] + public void NaturalType_06() { string source = """ class Program @@ -181,7 +297,6 @@ class Program static void Main() { object[] x = [[]]; - object[] y = [[2]]; } } """; @@ -189,14 +304,43 @@ static void Main() comp.VerifyEmitDiagnostics( // (5,23): error CS9500: Cannot initialize type 'object' with a collection literal because the type is not constructible. // object[] x = [[]]; - Diagnostic(ErrorCode.ERR_CollectionLiteralTargetTypeNotConstructible, "[]").WithArguments("object").WithLocation(5, 23), - // (6,23): error CS9500: Cannot initialize type 'object' with a collection literal because the type is not constructible. - // object[] y = [[2]]; - Diagnostic(ErrorCode.ERR_CollectionLiteralTargetTypeNotConstructible, "[2]").WithArguments("object").WithLocation(6, 23)); + Diagnostic(ErrorCode.ERR_CollectionLiteralTargetTypeNotConstructible, "[]").WithArguments("object").WithLocation(5, 23)); } [Fact] - public void NaturalType_04() + public void NaturalType_07() + { + string source = """ + class Program + { + static void Main() + { + object[] y = [[2]]; + y.Report(includeType: true); + } + } + """; + CompileAndVerify(new[] { source, s_collectionExtensions }, expectedOutput: "System.Object[][[2]], "); + } + + [Fact] + public void NaturalType_08() + { + string source = """ + class Program + { + static void Main() + { + var z = [[3]]; + z.Report(includeType: true); + } + } + """; + CompileAndVerify(new[] { source, s_collectionExtensions }, expectedOutput: "System.Collections.Generic.List>[[3]], "); + } + + [Fact] + public void NaturalType_09() { string source = """ class Program @@ -220,7 +364,7 @@ static void Main() } [Fact] - public void NaturalType_05() + public void NaturalType_10() { string source = """ class Program @@ -233,19 +377,11 @@ static void Main() static T F(T t) => t; } """; - // PROTOTYPE: Verify expectedOutput: "..." when natural type is supported. - var comp = CreateCompilation(new[] { source, GetCollectionExtensions() }); - comp.VerifyEmitDiagnostics( - // (5,9): error CS0411: The type arguments for method 'Program.F(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)").WithLocation(5, 9), - // (6,9): error CS0411: The type arguments for method 'Program.F(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)").WithLocation(6, 9)); + CompileAndVerify(new[] { source, s_collectionExtensions }, expectedOutput: "[1, 2], [[3, 4]], "); } [Fact] - public void NaturalType_06() + public void NaturalType_11() { string source = """ using System; @@ -266,7 +402,7 @@ static void Main() } [Fact] - public void NaturalType_07() + public void NaturalType_12() { string source = """ using System.Collections; @@ -301,6 +437,198 @@ static void Main() Diagnostic(ErrorCode.ERR_NoNewAbstract, "[]").WithArguments("System.Collections.Generic.IDictionary").WithLocation(10, 38)); } + [Fact] + public void NaturalType_13() + { + string source = """ + #nullable enable + class Program + { + static void Main() + { + var x = [null, ""]; + var y = [new object(), (string?)null]; + var z = [new object(), ""]; + x[0] = null; + y[0] = null; + z[0] = null; // 1 + } + } + """; + var comp = CreateCompilation(source); + // PROTOTYPE: Should report warning for // 1. + comp.VerifyEmitDiagnostics(); + } + + // PROTOTYPE: Test common type with inferred function types. + // PROTOTYPE: Test common type with one or more error types. + + // PROTOTYPE: Test conversions of each element. Test diagnostics are only reported when not target-typed to constructible type. + // PROTOTYPE: Test use-site error on best common type. Test diagnostics are only reported when not target-typed to constructible type. + // PROTOTYPE: Test collections where the element types differ by dynamic/object, tuple element names, nint/IntPtr, nullable. + + // PROTOTYPE: Test natural type where List.Add() method is missing or has unexpected parameter type. + + // PROTOTYPE: Test member access: [1, 2].Length, [3, 4]?.Length + // PROTOTYPE: Test invocaton: [1, 2]() + // PROTOTYPE: Test as argument to overloads, where overloads differ by collection type. + // PROTOTYPE: Test as method type inference argument. + // PROTOTYPE: Test in RHS of tuple conversion. For instance, (var x, var y) = ([1, 2], []); + // PROTOTYPE: Test all cases where we call BindToNaturalType(expr). For instance, BindThrowExpression(). + + [Fact] + public void NaturalType_14() + { + string source = """ + class Program + { + static void Main() + { + F(false); + } + static void F(bool b) + { + var x = b ? [1] : []; + var y = b? [] : [2]; + x.Report(includeType: true); + y.Report(includeType: true); + } + } + """; + CompileAndVerify(new[] { source, s_collectionExtensions }, expectedOutput: + "System.Collections.Generic.List[], System.Collections.Generic.List[2], "); + + // PROTOTYPE: Test the same with: switch expression; lambda returns. + } + + [Fact] + public void NaturalType_15() + { + string source = """ + class Program + { + static void Main() + { + F(false, 3); + } + static void F(bool b, object o) + { + var x = b ? [1] : [o]; + var y = b? [o] : [2]; + x.Report(includeType: true); + y.Report(includeType: true); + } + } + """; + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics( + // (9,17): error CS0172: Type of conditional expression cannot be determined because 'List' and 'List' implicitly convert to one another + // var x = b ? [1] : [o]; + Diagnostic(ErrorCode.ERR_AmbigQM, "b ? [1] : [o]").WithArguments("System.Collections.Generic.List", "System.Collections.Generic.List").WithLocation(9, 17), + // (10,17): error CS0172: Type of conditional expression cannot be determined because 'List' and 'List' implicitly convert to one another + // var y = b? [o] : [2]; + Diagnostic(ErrorCode.ERR_AmbigQM, "b? [o] : [2]").WithArguments("System.Collections.Generic.List", "System.Collections.Generic.List").WithLocation(10, 17)); + } + + [Fact] + public void NaturalType_16() + { + string source = """ + class Program + { + static void F(bool b) + { + var x = b ? [1] : [null]; + var y = b? [null] : [2]; + } + } + """; + var comp = CreateCompilation(source); + // PROTOTYPE: Should report "error CS0173: Type of conditional expression cannot be determined because ..." + // for x and y. Compare with equivalent test with switch expressions. + comp.VerifyEmitDiagnostics( + // (5,28): error CS1950: The best overloaded Add method 'List.Add(int)' for the collection initializer has some invalid arguments + // var x = b ? [1] : [null]; + Diagnostic(ErrorCode.ERR_BadArgTypesForCollectionAdd, "null").WithArguments("System.Collections.Generic.List.Add(int)").WithLocation(5, 28), + // (5,28): error CS1503: Argument 1: cannot convert from '' to 'int' + // var x = b ? [1] : [null]; + Diagnostic(ErrorCode.ERR_BadArgType, "null").WithArguments("1", "", "int").WithLocation(5, 28), + // (6,21): error CS1950: The best overloaded Add method 'List.Add(int)' for the collection initializer has some invalid arguments + // var y = b? [null] : [2]; + Diagnostic(ErrorCode.ERR_BadArgTypesForCollectionAdd, "null").WithArguments("System.Collections.Generic.List.Add(int)").WithLocation(6, 21), + // (6,21): error CS1503: Argument 1: cannot convert from '' to 'int' + // var y = b? [null] : [2]; + Diagnostic(ErrorCode.ERR_BadArgType, "null").WithArguments("1", "", "int").WithLocation(6, 21)); + } + + [Fact] + public void NaturalType_17() + { + string source = """ + class Program + { + static void Main() + { + F(false); + } + static void F(bool b) + { + var x = b ? ["1"] : [null]; + var y = b? [null] : ["2"]; + x.Report(includeType: true); + y.Report(includeType: true); + } + } + """; + CompileAndVerify(new[] { source, s_collectionExtensions }, expectedOutput: + "System.Collections.Generic.List[null], System.Collections.Generic.List[2], "); + } + + [Fact] + public void NaturalType_18() + { + string source = """ + class Program + { + static void Main() + { + int n = [].Count; + var v = [][0]; + } + } + """; + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics( + // (5,17): error CS9503: No best type found for implicitly-typed collection literal. + // int n = [].Count; + Diagnostic(ErrorCode.ERR_ImplicitlyTypedCollectionLiteralNoBestType, "[]").WithLocation(5, 17), + // (6,17): error CS9503: No best type found for implicitly-typed collection literal. + // var v = [][0]; + Diagnostic(ErrorCode.ERR_ImplicitlyTypedCollectionLiteralNoBestType, "[]").WithLocation(6, 17)); + } + + [Fact] + public void NaturalType_19() + { + string source = """ + using System; + class Program + { + static void Main() + { + int n = [1, 2, 3].Count; + var v = [1, 2, 3][0]; + Console.WriteLine($"{n}, {v}"); + } + } + """; + CompileAndVerify(source, expectedOutput: "3, 1"); + } + + // PROTOTYPE: Test natural type with missing List type. + // PROTOTYPE: Test natural type with missing List constructor. + // PROTOTYPE: Test natural type with missing List type use site error (perhaps missing interface). + [Fact] public void Array_01() { @@ -320,7 +648,7 @@ static void Main() } } """; - var verifier = CompileAndVerify(new[] { source, GetCollectionExtensions() }, expectedOutput: "[], [1, 2], [3, 4, 5], [null, 7], "); + var verifier = CompileAndVerify(new[] { source, s_collectionExtensions }, expectedOutput: "[], [1, 2], [3, 4, 5], [null, 7], "); verifier.VerifyIL("Program.Create1", """ { // Code size 7 (0x7) @@ -487,7 +815,7 @@ static void Main() } } """; - CompileAndVerify(new[] { source, GetCollectionExtensions() }, expectedOutput: "[], [null, 2], "); + CompileAndVerify(new[] { source, s_collectionExtensions }, expectedOutput: "[], [null, 2], "); } [Fact] @@ -533,12 +861,12 @@ static void Main() // (5,20): error CS9500: Cannot initialize type 'int[*,*]' with a collection literal because the type is not constructible. // int[,] z = [[1, 2], [3, 4]]; Diagnostic(ErrorCode.ERR_CollectionLiteralTargetTypeNotConstructible, "[[1, 2], [3, 4]]").WithArguments("int[*,*]").WithLocation(5, 20), - // (5,21): error CS9500: Cannot initialize type 'int' with a collection literal because the type is not constructible. + // (5,21): error CS0029: Cannot implicitly convert type 'System.Collections.Generic.List' to 'int' // int[,] z = [[1, 2], [3, 4]]; - Diagnostic(ErrorCode.ERR_CollectionLiteralTargetTypeNotConstructible, "[1, 2]").WithArguments("int").WithLocation(5, 21), - // (5,29): error CS9500: Cannot initialize type 'int' with a collection literal because the type is not constructible. + Diagnostic(ErrorCode.ERR_NoImplicitConv, "[1, 2]").WithArguments("System.Collections.Generic.List", "int").WithLocation(5, 21), + // (5,29): error CS0029: Cannot implicitly convert type 'System.Collections.Generic.List' to 'int' // int[,] z = [[1, 2], [3, 4]]; - Diagnostic(ErrorCode.ERR_CollectionLiteralTargetTypeNotConstructible, "[3, 4]").WithArguments("int").WithLocation(5, 29)); + Diagnostic(ErrorCode.ERR_NoImplicitConv, "[3, 4]").WithArguments("System.Collections.Generic.List", "int").WithLocation(5, 29)); } [ConditionalTheory(typeof(CoreClrOnly))] @@ -563,7 +891,7 @@ static void Main() } } """; - var verifier = CompileAndVerify(new[] { source, GetCollectionExtensions(includeSpan: true) }, targetFramework: TargetFramework.Net70, verify: Verification.Skipped, expectedOutput: "[], [1, 2], [3, 4, 5], [null, 7], "); + var verifier = CompileAndVerify(new[] { source, s_collectionExtensionsWithSpan }, targetFramework: TargetFramework.Net70, verify: Verification.Skipped, expectedOutput: "[], [1, 2], [3, 4, 5], [null, 7], "); verifier.VerifyIL("Program.Create1", $$""" { // Code size 12 (0xc) @@ -658,7 +986,7 @@ static void Main() } } """; - CompileAndVerify(new[] { source, GetCollectionExtensions(includeSpan: true) }, targetFramework: TargetFramework.Net70, verify: Verification.Skipped, expectedOutput: "[], [1, 2, 3], "); + CompileAndVerify(new[] { source, s_collectionExtensionsWithSpan }, targetFramework: TargetFramework.Net70, verify: Verification.Skipped, expectedOutput: "[], [1, 2, 3], "); } [ConditionalTheory(typeof(CoreClrOnly))] @@ -679,7 +1007,7 @@ static void Main() } } """; - CompileAndVerify(new[] { source, GetCollectionExtensions(includeSpan: true) }, targetFramework: TargetFramework.Net70, verify: Verification.Skipped, expectedOutput: "[], [1, 2, 3], "); + CompileAndVerify(new[] { source, s_collectionExtensionsWithSpan }, targetFramework: TargetFramework.Net70, verify: Verification.Skipped, expectedOutput: "[], [1, 2, 3], "); } [ConditionalTheory(typeof(CoreClrOnly))] @@ -828,15 +1156,16 @@ static void Main() // (7,13): error CS9500: Cannot initialize type 'E' with a collection literal because the type is not constructible. // e = []; Diagnostic(ErrorCode.ERR_CollectionLiteralTargetTypeNotConstructible, "[]").WithArguments("E").WithLocation(7, 13), - // (8,13): error CS9500: Cannot initialize type 'E' with a collection literal because the type is not constructible. + // (8,13): error CS0029: Cannot implicitly convert type 'System.Collections.Generic.List' to 'E' // e = [1, 2]; - Diagnostic(ErrorCode.ERR_CollectionLiteralTargetTypeNotConstructible, "[1, 2]").WithArguments("E").WithLocation(8, 13)); + Diagnostic(ErrorCode.ERR_NoImplicitConv, "[1, 2]").WithArguments("System.Collections.Generic.List", "E").WithLocation(8, 13)); } [Fact] public void EnumType_02() { string sourceA = """ + using System.Collections; namespace System { public class Object { } @@ -846,12 +1175,16 @@ public class Type { } public struct Void { } public struct Boolean { } public struct Int32 { } - public struct Enum : System.Collections.IEnumerable { } + public struct Enum : IEnumerable { } } namespace System.Collections { public interface IEnumerable { } } + namespace System.Collections.Generic + { + public class List : IEnumerable { } + } """; string sourceB = """ enum E { } @@ -894,24 +1227,22 @@ static void Main() """; var comp = CreateCompilation(source); comp.VerifyEmitDiagnostics( - // (7,13): error CS7036: There is no argument given that corresponds to the required parameter 'object' of 'D.D(object, IntPtr)' - // d = []; - Diagnostic(ErrorCode.ERR_NoCorrespondingArgument, "[]").WithArguments("object", "D.D(object, System.IntPtr)").WithLocation(7, 13), // (7,13): error CS9500: Cannot initialize type 'D' with a collection literal because the type is not constructible. // d = []; Diagnostic(ErrorCode.ERR_CollectionLiteralTargetTypeNotConstructible, "[]").WithArguments("D").WithLocation(7, 13), - // (8,13): error CS7036: There is no argument given that corresponds to the required parameter 'object' of 'D.D(object, IntPtr)' - // d = [1, 2]; - Diagnostic(ErrorCode.ERR_NoCorrespondingArgument, "[1, 2]").WithArguments("object", "D.D(object, System.IntPtr)").WithLocation(8, 13), - // (8,13): error CS9500: Cannot initialize type 'D' with a collection literal because the type is not constructible. + // (7,13): error CS7036: There is no argument given that corresponds to the required parameter 'object' of 'D.D(object, IntPtr)' + // d = []; + Diagnostic(ErrorCode.ERR_NoCorrespondingArgument, "[]").WithArguments("object", "D.D(object, System.IntPtr)").WithLocation(7, 13), + // (8,13): error CS0029: Cannot implicitly convert type 'System.Collections.Generic.List' to 'D' // d = [1, 2]; - Diagnostic(ErrorCode.ERR_CollectionLiteralTargetTypeNotConstructible, "[1, 2]").WithArguments("D").WithLocation(8, 13)); + Diagnostic(ErrorCode.ERR_NoImplicitConv, "[1, 2]").WithArguments("System.Collections.Generic.List", "D").WithLocation(8, 13)); } [Fact] public void DelegateType_02() { string sourceA = """ + using System.Collections; namespace System { public class Object { } @@ -922,13 +1253,17 @@ public struct Void { } public struct Boolean { } public struct Int32 { } public struct IntPtr { } - public abstract class Delegate : System.Collections.IEnumerable { } + public abstract class Delegate : IEnumerable { } public abstract class MulticastDelegate : Delegate { } } namespace System.Collections { public interface IEnumerable { } } + namespace System.Collections.Generic + { + public class List : IEnumerable { } + } """; string sourceB = """ delegate void D(); @@ -979,12 +1314,12 @@ unsafe static void Main() // (5,18): error CS9500: Cannot initialize type 'int*' with a collection literal because the type is not constructible. // int* x = []; Diagnostic(ErrorCode.ERR_CollectionLiteralTargetTypeNotConstructible, "[]").WithArguments("int*").WithLocation(5, 18), - // (6,18): error CS9500: Cannot initialize type 'int*' with a collection literal because the type is not constructible. + // (6,18): error CS0029: Cannot implicitly convert type 'System.Collections.Generic.List' to 'int*' // int* y = [1, 2]; - Diagnostic(ErrorCode.ERR_CollectionLiteralTargetTypeNotConstructible, "[1, 2]").WithArguments("int*").WithLocation(6, 18), - // (7,23): error CS9500: Cannot initialize type 'int*' with a collection literal because the type is not constructible. + Diagnostic(ErrorCode.ERR_NoImplicitConv, "[1, 2]").WithArguments("System.Collections.Generic.List", "int*").WithLocation(6, 18), + // (7,23): error CS0029: Cannot implicitly convert type 'System.Collections.Generic.List' to 'int*' // var z = (int*)[3]; - Diagnostic(ErrorCode.ERR_CollectionLiteralTargetTypeNotConstructible, "[3]").WithArguments("int*").WithLocation(7, 23)); + Diagnostic(ErrorCode.ERR_NoImplicitConv, "[3]").WithArguments("System.Collections.Generic.List", "int*").WithLocation(7, 23)); } [Fact] @@ -1006,12 +1341,12 @@ unsafe static void Main() // (5,29): error CS9500: Cannot initialize type 'delegate*' with a collection literal because the type is not constructible. // delegate* x = []; Diagnostic(ErrorCode.ERR_CollectionLiteralTargetTypeNotConstructible, "[]").WithArguments("delegate*").WithLocation(5, 29), - // (6,29): error CS9500: Cannot initialize type 'delegate*' with a collection literal because the type is not constructible. + // (6,29): error CS0029: Cannot implicitly convert type 'System.Collections.Generic.List' to 'delegate*' // delegate* y = [1, 2]; - Diagnostic(ErrorCode.ERR_CollectionLiteralTargetTypeNotConstructible, "[1, 2]").WithArguments("delegate*").WithLocation(6, 29), - // (7,34): error CS9500: Cannot initialize type 'delegate*' with a collection literal because the type is not constructible. + Diagnostic(ErrorCode.ERR_NoImplicitConv, "[1, 2]").WithArguments("System.Collections.Generic.List", "delegate*").WithLocation(6, 29), + // (7,34): error CS0029: Cannot implicitly convert type 'System.Collections.Generic.List' to 'delegate*' // var z = (delegate*)[3]; - Diagnostic(ErrorCode.ERR_CollectionLiteralTargetTypeNotConstructible, "[3]").WithArguments("delegate*").WithLocation(7, 34)); + Diagnostic(ErrorCode.ERR_NoImplicitConv, "[3]").WithArguments("System.Collections.Generic.List", "delegate*").WithLocation(7, 34)); } [Fact] @@ -1034,7 +1369,7 @@ static void Main() } } """; - var verifier = CompileAndVerify(new[] { source, GetCollectionExtensions() }, expectedOutput: "[], [1, 2], [3, 4, 5], [null, 7], "); + var verifier = CompileAndVerify(new[] { source, s_collectionExtensions }, expectedOutput: "[], [1, 2], [3, 4, 5], [null, 7], "); verifier.VerifyIL("Program.Create1", """ { // Code size 6 (0x6) @@ -1111,9 +1446,9 @@ struct S { } // (2,5): error CS9500: Cannot initialize type 'S' with a collection literal because the type is not constructible. // s = []; Diagnostic(ErrorCode.ERR_CollectionLiteralTargetTypeNotConstructible, "[]").WithArguments("S").WithLocation(2, 5), - // (3,5): error CS9500: Cannot initialize type 'S' with a collection literal because the type is not constructible. + // (3,5): error CS0029: Cannot implicitly convert type 'System.Collections.Generic.List' to 'S' // s = [1, 2]; - Diagnostic(ErrorCode.ERR_CollectionLiteralTargetTypeNotConstructible, "[1, 2]").WithArguments("S").WithLocation(3, 5)); + Diagnostic(ErrorCode.ERR_NoImplicitConv, "[1, 2]").WithArguments("System.Collections.Generic.List", "S").WithLocation(3, 5)); } [Fact] @@ -1153,28 +1488,25 @@ struct S : IEnumerable public void CollectionInitializerType_04() { string source = """ + using System.Collections; C c; c = []; c = [1, 2]; - class C + class C : IEnumerable { C(object o) { } + IEnumerator IEnumerable.GetEnumerator() => throw null; + public void Add(int i) { } } """; var comp = CreateCompilation(source); comp.VerifyEmitDiagnostics( - // (2,5): error CS1729: 'C' does not contain a constructor that takes 0 arguments - // c = []; - Diagnostic(ErrorCode.ERR_BadCtorArgCount, "[]").WithArguments("C", "0").WithLocation(2, 5), - // (2,5): error CS9500: Cannot initialize type 'C' with a collection literal because the type is not constructible. - // c = []; - Diagnostic(ErrorCode.ERR_CollectionLiteralTargetTypeNotConstructible, "[]").WithArguments("C").WithLocation(2, 5), // (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(3, 5), - // (3,5): error CS9500: Cannot initialize type 'C' with a collection literal because the type is not constructible. - // c = [1, 2]; - Diagnostic(ErrorCode.ERR_CollectionLiteralTargetTypeNotConstructible, "[1, 2]").WithArguments("C").WithLocation(3, 5)); + Diagnostic(ErrorCode.ERR_BadCtorArgCount, "[1, 2]").WithArguments("C", "0").WithLocation(4, 5)); } [Fact] @@ -1232,7 +1564,7 @@ static void Main() } } """; - CompileAndVerify(new[] { source, GetCollectionExtensions() }, expectedOutput: "[], [], [1, 2], [3, 4], "); + CompileAndVerify(new[] { source, s_collectionExtensions }, expectedOutput: "[], [], [1, 2], [3, 4], "); } [Fact] @@ -1266,7 +1598,7 @@ static void Main() } } """; - CompileAndVerify(new[] { source, GetCollectionExtensions() }, expectedOutput: "[], [], [1, 2], [3, 4], "); + CompileAndVerify(new[] { source, s_collectionExtensions }, expectedOutput: "[], [], [1, 2], [3, 4], "); } [Fact] @@ -1300,7 +1632,7 @@ static void Main() } } """; - CompileAndVerify(new[] { source, GetCollectionExtensions() }, expectedOutput: "[], [], [1, 2], [3, 4], "); + CompileAndVerify(new[] { source, s_collectionExtensions }, expectedOutput: "[], [], [1, 2], [3, 4], "); } [Fact] @@ -1556,7 +1888,7 @@ static void Main() } } """; - CompileAndVerify(new[] { source, GetCollectionExtensions() }, expectedOutput: "[], [i=1, o=2], "); + CompileAndVerify(new[] { source, s_collectionExtensions }, expectedOutput: "[], [i=1, o=2], "); } [Fact] @@ -1639,7 +1971,7 @@ static void Main() } } """; - CompileAndVerify(new[] { source, GetCollectionExtensions() }, expectedOutput: "[1, 2], "); + CompileAndVerify(new[] { source, s_collectionExtensions }, expectedOutput: "[1, 2], "); } [Fact] @@ -1663,7 +1995,7 @@ static void Main() } } """; - CompileAndVerify(new[] { source, GetCollectionExtensions() }, expectedOutput: "[1, 2], "); + CompileAndVerify(new[] { source, s_collectionExtensions }, expectedOutput: "[1, 2], "); } [Fact] @@ -1687,10 +2019,10 @@ static void Main() } } """; - var verifier = CompileAndVerify(new[] { source, GetCollectionExtensions() }, expectedOutput: "[1, 2, 3], "); + var verifier = CompileAndVerify(new[] { source, s_collectionExtensions }, expectedOutput: "[1, 2, 3], "); verifier.VerifyIL("Program.Main", """ { - // Code size 61 (0x3d) + // Code size 62 (0x3e) .maxstack 5 .locals init (C V_0) IL_0000: newobj "C..ctor()" @@ -1720,8 +2052,9 @@ .locals init (C V_0) IL_0030: stelem.i4 IL_0031: callvirt "void C.Add(params int[])" IL_0036: ldloc.0 - IL_0037: call "void CollectionExtensions.Report(object)" - IL_003c: ret + IL_0037: ldc.i4.0 + IL_0038: call "void CollectionExtensions.Report(object, bool)" + IL_003d: ret } """); } @@ -1753,6 +2086,33 @@ class Program Diagnostic(ErrorCode.ERR_BadArgType, "y").WithArguments("1", "U", "T").WithLocation(11, 50)); } + [Fact] + public void CollectionInitializerType_19() + { + 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,14): 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, 14)); + } + [Theory] [InlineData("class")] [InlineData("struct")] @@ -1796,7 +2156,7 @@ static void Main() } } """; - var verifier = CompileAndVerify(new[] { source, GetCollectionExtensions() }, expectedOutput: "[], [null, 2], "); + var verifier = CompileAndVerify(new[] { source, s_collectionExtensions }, expectedOutput: "[], [null, 2], "); verifier.VerifyIL("Program.CreateEmpty", """ { // Code size 6 (0x6) @@ -1934,9 +2294,9 @@ static void Main() // (9,23): error CS0518: Predefined type 'System.Collections.IEnumerable' is not defined or imported // object o = (S)[1, 2]; Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, "[1, 2]").WithArguments("System.Collections.IEnumerable").WithLocation(9, 23), - // (9,23): error CS9500: Cannot initialize type 'S' with a collection literal because the type is not constructible. + // (9,23): error CS0029: Cannot implicitly convert type 'System.Collections.Generic.List' to 'S' // object o = (S)[1, 2]; - Diagnostic(ErrorCode.ERR_CollectionLiteralTargetTypeNotConstructible, "[1, 2]").WithArguments("S").WithLocation(9, 23)); + Diagnostic(ErrorCode.ERR_NoImplicitConv, "[1, 2]").WithArguments("System.Collections.Generic.List", "S").WithLocation(9, 23)); } [Fact] @@ -1988,9 +2348,9 @@ static void Main() } } """; - CompileAndVerify(new[] { sourceC, GetCollectionExtensions() }, references: new[] { refA, refB }, expectedOutput: "[], [1, 2], [], [3, 4], "); + CompileAndVerify(new[] { sourceC, s_collectionExtensions }, references: new[] { refA, refB }, expectedOutput: "[], [1, 2], [], [3, 4], "); - comp = CreateCompilation(new[] { sourceC, GetCollectionExtensions() }, references: new[] { refB }); + comp = CreateCompilation(new[] { sourceC, s_collectionExtensions }, references: new[] { refB }); comp.VerifyEmitDiagnostics( // (6,13): error CS0012: The type 'A1' is defined in an assembly that is not referenced. You must add a reference to assembly 'a897d975-a839-4fff-828b-deccf9495adc, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'. // x = []; @@ -2032,30 +2392,65 @@ static void Main() Diagnostic(ErrorCode.ERR_CollectionLiteralElementNotImplemented, "3:4").WithLocation(9, 14)); } - [Fact] - public void SpreadElement_01() + [ConditionalTheory(typeof(CoreClrOnly))] + [InlineData("int[]")] + [InlineData("System.Collections.Generic.List")] + [InlineData("System.Span")] + [InlineData("System.ReadOnlySpan")] + public void SpreadElement_01(string collectionType) { - string source = """ + string source = $$""" class Program { static void Main() { - int[] a; - a = []; - a = [..a, ..[1, 2]]; + {{collectionType}} c; + c = []; + c = [..c, ..[1, 2]]; } } """; - var comp = CreateCompilation(source); + var comp = CreateCompilation(source, targetFramework: TargetFramework.Net70); comp.VerifyEmitDiagnostics( // (7,14): error CS9502: Support for collection literal dictionary and spread elements has not been implemented. - // a = [..a, ..[1, 2]]; - Diagnostic(ErrorCode.ERR_CollectionLiteralElementNotImplemented, "..a").WithLocation(7, 14), + // c = [..c, ..[1, 2]]; + Diagnostic(ErrorCode.ERR_CollectionLiteralElementNotImplemented, "..c").WithLocation(7, 14), // (7,19): error CS9502: Support for collection literal dictionary and spread elements has not been implemented. - // a = [..a, ..[1, 2]]; + // c = [..c, ..[1, 2]]; Diagnostic(ErrorCode.ERR_CollectionLiteralElementNotImplemented, "..[1, 2]").WithLocation(7, 19)); } + [Fact] + public void SpreadElement_02() + { + string source = """ + using System.Collections; + struct S : IEnumerable + { + public void Add(T t) { } + public T this[int index] => default; + IEnumerator IEnumerable.GetEnumerator() => null; + } + class Program + { + static void Main() + { + S s; + s = []; + s = [..s, ..[1, 2]]; + } + } + """; + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics( + // (14,14): error CS9502: Support for collection literal dictionary and spread elements has not been implemented. + // s = [..s, ..[1, 2]]; + Diagnostic(ErrorCode.ERR_CollectionLiteralElementNotImplemented, "..s").WithLocation(14, 14), + // (14,19): error CS9502: Support for collection literal dictionary and spread elements has not been implemented. + // s = [..s, ..[1, 2]]; + Diagnostic(ErrorCode.ERR_CollectionLiteralElementNotImplemented, "..[1, 2]").WithLocation(14, 19)); + } + [Fact] public void Nullable_01() { @@ -2191,12 +2586,12 @@ static void Main() // (14,13): error CS9500: Cannot initialize type 'S?' with a collection literal because the type is not constructible. // x = []; Diagnostic(ErrorCode.ERR_CollectionLiteralTargetTypeNotConstructible, "[]").WithArguments("S?").WithLocation(14, 13), - // (15,24): error CS9500: Cannot initialize type 'S?' with a collection literal because the type is not constructible. + // (15,24): error CS0029: Cannot implicitly convert type 'System.Collections.Generic.List' to 'S?' // S? y = [1]; - Diagnostic(ErrorCode.ERR_CollectionLiteralTargetTypeNotConstructible, "[1]").WithArguments("S?").WithLocation(15, 24), - // (16,13): error CS9500: Cannot initialize type 'S?' with a collection literal because the type is not constructible. + Diagnostic(ErrorCode.ERR_NoImplicitConv, "[1]").WithArguments("System.Collections.Generic.List", "S?").WithLocation(15, 24), + // (16,13): error CS0029: Cannot implicitly convert type 'System.Collections.Generic.List' to 'S?' // y = [2]; - Diagnostic(ErrorCode.ERR_CollectionLiteralTargetTypeNotConstructible, "[2]").WithArguments("S?").WithLocation(16, 13)); + Diagnostic(ErrorCode.ERR_NoImplicitConv, "[2]").WithArguments("System.Collections.Generic.List", "S?").WithLocation(16, 13)); } [Fact] @@ -2271,12 +2666,12 @@ static void Main() // (9,15): error CS9500: Cannot initialize type 'S' with a collection literal because the type is not constructible. // S s = []; Diagnostic(ErrorCode.ERR_CollectionLiteralTargetTypeNotConstructible, "[]").WithArguments("S").WithLocation(9, 15), - // (10,13): error CS9500: Cannot initialize type 'S' with a collection literal because the type is not constructible. + // (10,13): error CS0029: Cannot implicitly convert type 'System.Collections.Generic.List' to 'S' // s = [1, 2]; - Diagnostic(ErrorCode.ERR_CollectionLiteralTargetTypeNotConstructible, "[1, 2]").WithArguments("S").WithLocation(10, 13), - // (11,16): error CS9500: Cannot initialize type 'S' with a collection literal because the type is not constructible. + Diagnostic(ErrorCode.ERR_NoImplicitConv, "[1, 2]").WithArguments("System.Collections.Generic.List", "S").WithLocation(10, 13), + // (11,16): error CS0029: Cannot implicitly convert type 'System.Collections.Generic.List' to 'S' // s = (S)[3, 4]; - Diagnostic(ErrorCode.ERR_CollectionLiteralTargetTypeNotConstructible, "[3, 4]").WithArguments("S").WithLocation(11, 16)); + Diagnostic(ErrorCode.ERR_NoImplicitConv, "[3, 4]").WithArguments("System.Collections.Generic.List", "S").WithLocation(11, 16)); } [Fact] @@ -2302,12 +2697,12 @@ static void Main() // (9,15): error CS9500: Cannot initialize type 'S' with a collection literal because the type is not constructible. // S s = []; Diagnostic(ErrorCode.ERR_CollectionLiteralTargetTypeNotConstructible, "[]").WithArguments("S").WithLocation(9, 15), - // (10,13): error CS9500: Cannot initialize type 'S' with a collection literal because the type is not constructible. + // (10,13): error CS0029: Cannot implicitly convert type 'System.Collections.Generic.List' to 'S' // s = [1, 2]; - Diagnostic(ErrorCode.ERR_CollectionLiteralTargetTypeNotConstructible, "[1, 2]").WithArguments("S").WithLocation(10, 13), - // (11,16): error CS9500: Cannot initialize type 'S' with a collection literal because the type is not constructible. + Diagnostic(ErrorCode.ERR_NoImplicitConv, "[1, 2]").WithArguments("System.Collections.Generic.List", "S").WithLocation(10, 13), + // (11,16): error CS0029: Cannot implicitly convert type 'System.Collections.Generic.List' to 'S' // s = (S)[3, 4]; - Diagnostic(ErrorCode.ERR_CollectionLiteralTargetTypeNotConstructible, "[3, 4]").WithArguments("S").WithLocation(11, 16)); + Diagnostic(ErrorCode.ERR_NoImplicitConv, "[3, 4]").WithArguments("System.Collections.Generic.List", "S").WithLocation(11, 16)); } [Fact] @@ -2327,7 +2722,7 @@ static void Main() } """; - var comp = CreateCompilation(new[] { source, GetCollectionExtensions() }, options: TestOptions.ReleaseExe); + 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) @@ -2378,7 +2773,7 @@ static void Main() } """; - var comp = CreateCompilation(new[] { source, GetCollectionExtensions() }, options: TestOptions.ReleaseExe); + 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) @@ -2438,7 +2833,7 @@ static void Main() } """; - var comp = CreateCompilation(new[] { source, GetCollectionExtensions() }, options: TestOptions.ReleaseExe); + 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]) @@ -2530,6 +2925,8 @@ static void Main() verifyTypes(collections[10], "S1", ConversionKind.CollectionLiteral); verifyTypes(collections[11], "S2", ConversionKind.CollectionLiteral); + // PROTOTYPE: Test cases that are not target typed. + void verifyTypes(ExpressionSyntax expr, string expectedConvertedType, ConversionKind expectedConversionKind) { var typeInfo = model.GetTypeInfo(expr); @@ -2987,29 +3384,26 @@ static void Main() IInstanceReferenceOperation (ReferenceKind: ImplicitReceiver) (OperationKind.InstanceReference, Type: System.Collections.Generic.List>, IsImplicit) (Syntax: '[[Get(1)]]') Arguments(1): IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: item) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: '[Get(1)]') - IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Collections.Generic.List, IsImplicit) (Syntax: '[Get(1)]') - Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) - Operand: - IObjectCreationOperation (Constructor: System.Collections.Generic.List..ctor()) (OperationKind.ObjectCreation, Type: System.Collections.Generic.List) (Syntax: '[Get(1)]') - Arguments(0) - Initializer: - IObjectOrCollectionInitializerOperation (OperationKind.ObjectOrCollectionInitializer, Type: System.Collections.Generic.List, IsImplicit) (Syntax: '[Get(1)]') - Initializers(1): - IInvocationOperation ( void System.Collections.Generic.List.Add(System.Int32 item)) (OperationKind.Invocation, Type: System.Void, IsImplicit) (Syntax: 'Get(1)') - Instance Receiver: - IInstanceReferenceOperation (ReferenceKind: ImplicitReceiver) (OperationKind.InstanceReference, Type: System.Collections.Generic.List, IsImplicit) (Syntax: '[Get(1)]') - Arguments(1): - IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: item) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: 'Get(1)') - IInvocationOperation (System.Int32 Program.Get(System.Int32 value)) (OperationKind.Invocation, Type: System.Int32) (Syntax: 'Get(1)') - Instance Receiver: - null - Arguments(1): - IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: value) (OperationKind.Argument, Type: null) (Syntax: '1') - ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1) (Syntax: '1') - 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) - 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) + IObjectCreationOperation (Constructor: System.Collections.Generic.List..ctor()) (OperationKind.ObjectCreation, Type: System.Collections.Generic.List) (Syntax: '[Get(1)]') + Arguments(0) + Initializer: + IObjectOrCollectionInitializerOperation (OperationKind.ObjectOrCollectionInitializer, Type: System.Collections.Generic.List, IsImplicit) (Syntax: '[Get(1)]') + Initializers(1): + IInvocationOperation ( void System.Collections.Generic.List.Add(System.Int32 item)) (OperationKind.Invocation, Type: System.Void, IsImplicit) (Syntax: 'Get(1)') + Instance Receiver: + IInstanceReferenceOperation (ReferenceKind: ImplicitReceiver) (OperationKind.InstanceReference, Type: System.Collections.Generic.List, IsImplicit) (Syntax: '[Get(1)]') + Arguments(1): + IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: item) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: 'Get(1)') + IInvocationOperation (System.Int32 Program.Get(System.Int32 value)) (OperationKind.Invocation, Type: System.Int32) (Syntax: 'Get(1)') + Instance Receiver: + null + Arguments(1): + IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: value) (OperationKind.Argument, Type: null) (Syntax: '1') + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 1) (Syntax: '1') + 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) + 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) 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) "); @@ -3068,11 +3462,7 @@ static void Main() IFlowCaptureReferenceOperation: 0 (OperationKind.FlowCaptureReference, Type: System.Collections.Generic.List>, IsImplicit) (Syntax: '[[Get(1)]]') Arguments(1): IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: item) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: '[Get(1)]') - IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Collections.Generic.List, IsImplicit) (Syntax: '[Get(1)]') - Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) - (CollectionLiteral) - Operand: - IFlowCaptureReferenceOperation: 1 (OperationKind.FlowCaptureReference, Type: System.Collections.Generic.List, IsImplicit) (Syntax: '[Get(1)]') + IFlowCaptureReferenceOperation: 1 (OperationKind.FlowCaptureReference, Type: System.Collections.Generic.List, IsImplicit) (Syntax: '[Get(1)]') 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) Next (Regular) Block[B3] @@ -3085,11 +3475,7 @@ static void Main() Left: ILocalReferenceOperation: x (IsDeclaration: True) (OperationKind.LocalReference, Type: System.Collections.Generic.List>, IsImplicit) (Syntax: 'x = /**/[[Get(1)]]') Right: - IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: System.Collections.Generic.List>, IsImplicit) (Syntax: '[[Get(1)]]') - Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) - (CollectionLiteral) - Operand: - IFlowCaptureReferenceOperation: 0 (OperationKind.FlowCaptureReference, Type: System.Collections.Generic.List>, IsImplicit) (Syntax: '[[Get(1)]]') + IFlowCaptureReferenceOperation: 0 (OperationKind.FlowCaptureReference, Type: System.Collections.Generic.List>, IsImplicit) (Syntax: '[[Get(1)]]') Next (Regular) Block[B4] Leaving: {R1} } @@ -3127,7 +3513,7 @@ static async Task F(int i) } } """; - var verifier = CompileAndVerify(new[] { source, GetCollectionExtensions() }, expectedOutput: "[1, 2], [3, 4], "); + var verifier = CompileAndVerify(new[] { source, s_collectionExtensions }, expectedOutput: "[1, 2], [3, 4], "); verifier.VerifyIL("Program.CreateArray", """ { // Code size 47 (0x2f)