From 93de7e746d2e10b2c4874f1245f5ad3bbd74d575 Mon Sep 17 00:00:00 2001 From: Charles Stoner <10732005+cston@users.noreply.github.com> Date: Wed, 18 Jan 2023 09:13:21 -0800 Subject: [PATCH 01/18] Initial binding and lowering of collection literals with single-valued elements --- .../Portable/Binder/Binder.ValueChecks.cs | 6 + .../Portable/Binder/Binder_Conversions.cs | 166 ++ .../Portable/Binder/Binder_Expressions.cs | 40 +- .../Semantics/Conversions/Conversion.cs | 6 + .../Semantics/Conversions/ConversionKind.cs | 1 + .../Conversions/ConversionKindExtensions.cs | 1 + .../Semantics/Conversions/ConversionsBase.cs | 3 + .../CSharp/Portable/BoundTree/BoundNodes.xml | 24 + .../CSharp/Portable/BoundTree/Formatting.cs | 6 + .../CSharp/Portable/CSharpResources.resx | 9 + .../CSharp/Portable/Errors/ErrorCode.cs | 3 + .../CSharp/Portable/Errors/ErrorFacts.cs | 2 + .../CSharp/Portable/Errors/MessageID.cs | 2 + .../Portable/FlowAnalysis/AbstractFlowPass.cs | 15 +- .../Portable/FlowAnalysis/NullableWalker.cs | 37 + .../Generated/BoundNodes.xml.Generated.cs | 160 ++ .../DiagnosticsPass_ExpressionTrees.cs | 10 + .../LocalRewriter_CollectionLiteral.cs | 112 ++ .../LocalRewriter/LocalRewriter_Conversion.cs | 5 + ...ObjectOrCollectionInitializerExpression.cs | 2 +- .../Operations/CSharpOperationFactory.cs | 63 + .../CSharp/Portable/PublicAPI.Unshipped.txt | 1 + .../Portable/xlf/CSharpResources.cs.xlf | 15 + .../Portable/xlf/CSharpResources.de.xlf | 15 + .../Portable/xlf/CSharpResources.es.xlf | 15 + .../Portable/xlf/CSharpResources.fr.xlf | 15 + .../Portable/xlf/CSharpResources.it.xlf | 15 + .../Portable/xlf/CSharpResources.ja.xlf | 15 + .../Portable/xlf/CSharpResources.ko.xlf | 15 + .../Portable/xlf/CSharpResources.pl.xlf | 15 + .../Portable/xlf/CSharpResources.pt-BR.xlf | 15 + .../Portable/xlf/CSharpResources.ru.xlf | 15 + .../Portable/xlf/CSharpResources.tr.xlf | 15 + .../Portable/xlf/CSharpResources.zh-Hans.xlf | 15 + .../Portable/xlf/CSharpResources.zh-Hant.xlf | 15 + ...nTests_IArrayElementReferenceExpression.cs | 11 +- .../Semantics/CollectionLiteralTests.cs | 1756 +++++++++++++++++ .../Semantic/Semantics/LocalFunctionTests.cs | 11 +- .../Semantics/OverloadResolutionTests.cs | 7 +- .../Semantic/Semantics/RecordStructTests.cs | 3 + .../Generated/OperationKind.Generated.cs | 2 + .../Generated/Operations.Generated.cs | 100 + .../Operations/ControlFlowGraphBuilder.cs | 17 + .../Operations/OperationInterfaces.xml | 21 + .../Core/Portable/PublicAPI.Unshipped.txt | 6 + .../Compilation/ControlFlowGraphVerifier.cs | 1 + .../Core/Compilation/TestOperationVisitor.cs | 12 + 47 files changed, 2804 insertions(+), 12 deletions(-) create mode 100644 src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_CollectionLiteral.cs create mode 100644 src/Compilers/CSharp/Test/Semantic/Semantics/CollectionLiteralTests.cs diff --git a/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs b/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs index 937906c7640cb..1a5919f7eb905 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs @@ -3706,6 +3706,9 @@ internal uint GetValEscape(BoundExpression expr, uint scopeOfTheContainingExpres var switchExpr = (BoundSwitchExpression)expr; return GetValEscape(switchExpr.SwitchArms.SelectAsArray(a => a.Value), scopeOfTheContainingExpression); + case BoundKind.CollectionLiteralExpression: + return CallingMethodScope; + default: // in error situations some unexpected nodes could make here // returning "scopeOfTheContainingExpression" seems safer than throwing. @@ -4184,6 +4187,9 @@ internal bool CheckValEscape(SyntaxNode node, BoundExpression expr, uint escapeF return true; + case BoundKind.CollectionLiteralExpression: + return true; + default: // in error situations some unexpected nodes could make here // returning "false" seems safer than throwing. diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Conversions.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Conversions.cs index 98ec8469d0bb2..e900d2bcc0a38 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Conversions.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Conversions.cs @@ -230,6 +230,18 @@ BoundExpression createConversion( return ConvertObjectCreationExpression(syntax, (BoundUnconvertedObjectCreationExpression)source, conversion, isCast, destination, conversionGroupOpt, wasCompilerGenerated, diagnostics); } + if (conversion.IsCollectionLiteral) + { + return ConvertCollectionLiteralExpression( + (BoundUnconvertedCollectionLiteralExpression)source, + conversion, + isCast, + destination, + conversionGroupOpt, + wasCompilerGenerated, + diagnostics); + } + if (source.Kind == BoundKind.UnconvertedConditionalOperator) { Debug.Assert(source.Type is null); @@ -404,6 +416,160 @@ static BoundExpression bindObjectCreationExpression( } } + private BoundExpression ConvertCollectionLiteralExpression( + BoundUnconvertedCollectionLiteralExpression node, + Conversion conversion, + bool isCast, + TypeSymbol targetType, + ConversionGroup? conversionGroupOpt, + bool wasCompilerGenerated, + BindingDiagnosticBag diagnostics) + { + var syntax = (CSharpSyntaxNode)node.Syntax; + var implicitReceiver = new BoundObjectOrCollectionValuePlaceholder(syntax, isNewInstance: true, targetType) { WasCompilerGenerated = true }; + BoundExpression? collectionCreation = null; + MethodSymbol? spanConstructor = null; + ImmutableArray elements; + TypeSymbol? elementType; + + if (targetType is ArrayTypeSymbol arrayType) + { + if (!arrayType.IsSZArray) + { + Error(diagnostics, ErrorCode.ERR_CollectionLiteralTargetTypeNotConstructible, syntax, targetType); + } + elements = convertArrayElements(node.Initializers, arrayType.ElementType, diagnostics); + } + else if (isSpanType(targetType, WellKnownType.System_Span_T, out elementType)) + { + spanConstructor = getSpanConstructor(syntax, targetType, WellKnownMember.System_Span_T__ctor_Array, diagnostics); + elements = convertArrayElements(node.Initializers, elementType, diagnostics); + } + else if (isSpanType(targetType, WellKnownType.System_ReadOnlySpan_T, out elementType)) + { + spanConstructor = getSpanConstructor(syntax, targetType, WellKnownMember.System_ReadOnlySpan_T__ctor_Array, diagnostics); + elements = convertArrayElements(node.Initializers, elementType, diagnostics); + } + else + { + if (targetType is NamedTypeSymbol namedType) + { + var analyzedArguments = AnalyzedArguments.GetInstance(); + collectionCreation = BindClassCreationExpression(syntax, namedType.Name, syntax, namedType, analyzedArguments, diagnostics); + collectionCreation.WasCompilerGenerated = true; + analyzedArguments.Free(); + } + + bool hasEnumerableInitializerType = collectionTypeImplementsIEnumerable(targetType, syntax, diagnostics); + if (!hasEnumerableInitializerType + && !syntax.HasErrors + && !targetType.IsErrorType() + && collectionCreation?.HasErrors != true) + { + 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); + } + elements = builder.ToImmutableAndFree(); + } + + var expr = new BoundCollectionLiteralExpression( + syntax, + collectionCreation, + spanConstructor, + implicitReceiver, + elements, + targetType) + { WasCompilerGenerated = wasCompilerGenerated }; + + return new BoundConversion( + syntax, + expr, + conversion, + node.Binder.CheckOverflowAtRuntime, + explicitCastInCode: isCast && !wasCompilerGenerated, + conversionGroupOpt, + expr.ConstantValueOpt, + targetType); + + bool isSpanType(TypeSymbol targetType, WellKnownType spanType, [NotNullWhen(true)] out TypeSymbol? elementType) + { + if (targetType is NamedTypeSymbol { TypeArgumentsWithAnnotationsNoUseSiteDiagnostics: [var typeArgument] } namedType + && TypeSymbol.Equals(namedType.OriginalDefinition, Compilation.GetWellKnownType(spanType), TypeCompareKind.AllIgnoreOptions)) + { + elementType = typeArgument.Type; + return true; + } + elementType = default; + return false; + } + + MethodSymbol? getSpanConstructor(CSharpSyntaxNode syntax, TypeSymbol spanType, WellKnownMember spanMember, BindingDiagnosticBag diagnostics) + { + return (MethodSymbol?)GetWellKnownTypeMember(spanMember, diagnostics, syntax: syntax)?.SymbolAsMember((NamedTypeSymbol)spanType); + } + + bool collectionTypeImplementsIEnumerable(TypeSymbol targetType, CSharpSyntaxNode syntax, BindingDiagnosticBag diagnostics) + { + // This implementation differs from CollectionInitializerTypeImplementsIEnumerable(). + // That method checks for an implicit conversion from IEnumerable to the collection type, + // but that would allow: Nullable s = []; + var ienumerableType = GetSpecialType(SpecialType.System_Collections_IEnumerable, diagnostics, syntax); // PROTOTYPE: Test missing type. + var allInterfaces = targetType is TypeParameterSymbol typeParameter + ? typeParameter.AllEffectiveInterfacesNoUseSiteDiagnostics + : targetType.AllInterfacesNoUseSiteDiagnostics; + return allInterfaces.Any(t => ienumerableType.Equals(t, TypeCompareKind.AllIgnoreOptions)); + } + + ImmutableArray convertArrayElements(ImmutableArray elements, TypeSymbol elementType, BindingDiagnosticBag diagnostics) + { + var builder = ArrayBuilder.GetInstance(elements.Length); + foreach (var element in elements) + { + builder.Add(convertArrayElement(element, elementType, diagnostics)); + } + return builder.ToImmutableAndFree(); + } + + BoundExpression convertArrayElement(BoundExpression element, TypeSymbol elementType, BindingDiagnosticBag diagnostics) + { + CompoundUseSiteInfo useSiteInfo = GetNewCompoundUseSiteInfo(diagnostics); + var conversion = Conversions.ClassifyImplicitConversionFromExpression(element, elementType, ref useSiteInfo); + diagnostics.Add(element.Syntax, useSiteInfo); + bool hasErrors = !conversion.IsValid; + if (hasErrors) + { + GenerateImplicitConversionError(diagnostics, element.Syntax, conversion, element, elementType); + } + var result = CreateConversion( + element.Syntax, + element, + conversion, + isCast: false, + conversionGroupOpt: null, + wasCompilerGenerated: true, + destination: elementType, + diagnostics: diagnostics, + hasErrors: hasErrors); + result.WasCompilerGenerated = true; + return result; + } + } + /// /// 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 ac512410387f0..f7239e0d56557 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs @@ -755,8 +755,7 @@ private BoundExpression BindExpressionInternal(ExpressionSyntax node, BindingDia return BadExpression(node); case SyntaxKind.CollectionCreationExpression: - // PROTOTYPE: Implement binding for this. - return BadExpression(node); + return BindCollectionCreationExpression((CollectionCreationExpressionSyntax)node, diagnostics); case SyntaxKind.NullableType: // Not reachable during method body binding, but @@ -4342,6 +4341,43 @@ BoundExpression bindObjectCreationExpression(ObjectCreationExpressionSyntax node } } + private BoundExpression BindCollectionCreationExpression(CollectionCreationExpressionSyntax syntax, BindingDiagnosticBag diagnostics) + { + MessageID.IDS_FeatureCollectionLiterals.CheckFeatureAvailability(diagnostics, syntax, syntax.OpenBracketToken.GetLocation()); + + var builder = ArrayBuilder.GetInstance(syntax.Elements.Count); + foreach (var element in syntax.Elements) + { + builder.Add(bindElement(element, diagnostics)); + } + return new BoundUnconvertedCollectionLiteralExpression(syntax, builder.ToImmutableAndFree(), this); + + BoundExpression bindElement(CollectionElementSyntax syntax, BindingDiagnosticBag diagnostics) + { + switch (syntax) + { + case ExpressionElementSyntax expressionElementSyntax: + return BindValue(expressionElementSyntax.Expression, diagnostics, BindValueKind.RValue); + + case DictionaryElementSyntax dictionaryElementSyntax: + _ = BindValue(dictionaryElementSyntax.KeyExpression, diagnostics, BindValueKind.RValue); + _ = BindValue(dictionaryElementSyntax.ValueExpression, diagnostics, BindValueKind.RValue); + // PROTOTYPE: Temporary error until feature is implemented. + Error(diagnostics, ErrorCode.ERR_InvalidExprTerm, syntax, syntax); + return BadExpression(syntax); + + case SpreadElementSyntax spreadElementSyntax: + _ = BindValue(spreadElementSyntax.Expression, diagnostics, BindValueKind.RValue); + // PROTOTYPE: Temporary error until feature is implemented. + Error(diagnostics, ErrorCode.ERR_InvalidExprTerm, syntax, syntax); + return BadExpression(syntax); + + default: + throw ExceptionUtilities.UnexpectedValue(syntax.Kind()); + } + } + } + private BoundExpression BindDelegateCreationExpression(ObjectCreationExpressionSyntax node, NamedTypeSymbol type, BindingDiagnosticBag diagnostics) { AnalyzedArguments analyzedArguments = AnalyzedArguments.GetInstance(); diff --git a/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/Conversion.cs b/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/Conversion.cs index ff2fe9c168f7d..f5061030c1c1e 100644 --- a/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/Conversion.cs +++ b/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/Conversion.cs @@ -231,6 +231,7 @@ internal static Conversion GetTrivialConversion(ConversionKind kind) internal static Conversion ImplicitEnumeration => new Conversion(ConversionKind.ImplicitEnumeration); internal static Conversion ImplicitThrow => new Conversion(ConversionKind.ImplicitThrow); internal static Conversion ObjectCreation => new Conversion(ConversionKind.ObjectCreation); + internal static Conversion CollectionLiteral => new Conversion(ConversionKind.CollectionLiteral); internal static Conversion AnonymousFunction => new Conversion(ConversionKind.AnonymousFunction); internal static Conversion Boxing => new Conversion(ConversionKind.Boxing); internal static Conversion NullLiteral => new Conversion(ConversionKind.NullLiteral); @@ -631,6 +632,11 @@ public bool IsObjectCreation } } + /// + /// Returns true if the conversion is an implicit collection creation expression conversion. + /// + public bool IsCollectionLiteral => Kind == ConversionKind.CollectionLiteral; + /// /// Returns true if the conversion is an implicit switch expression conversion. /// diff --git a/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/ConversionKind.cs b/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/ConversionKind.cs index 7656918721763..499878083c7a5 100644 --- a/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/ConversionKind.cs +++ b/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/ConversionKind.cs @@ -63,6 +63,7 @@ internal enum ConversionKind : byte DefaultLiteral, // a conversion from a `default` literal to any type ObjectCreation, // a conversion from a `new()` expression to any type + CollectionLiteral, // a conversion from a collection literal to any type InterpolatedStringHandler, // A conversion from an interpolated string literal to a type attributed with InterpolatedStringBuilderAttribute } diff --git a/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/ConversionKindExtensions.cs b/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/ConversionKindExtensions.cs index 08ebed29d4a48..ed3e5f029b635 100644 --- a/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/ConversionKindExtensions.cs +++ b/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/ConversionKindExtensions.cs @@ -51,6 +51,7 @@ public static bool IsImplicitConversion(this ConversionKind conversionKind) case StackAllocToSpanType: case ImplicitPointer: case ObjectCreation: + case CollectionLiteral: return true; case ExplicitNumeric: diff --git a/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/ConversionsBase.cs b/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/ConversionsBase.cs index ea9a3976650c8..d741b7e6d8619 100644 --- a/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/ConversionsBase.cs +++ b/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/ConversionsBase.cs @@ -1094,6 +1094,9 @@ private Conversion ClassifyImplicitBuiltInConversionFromExpression(BoundExpressi case BoundKind.UnconvertedObjectCreationExpression: return Conversion.ObjectCreation; + + case BoundKind.UnconvertedCollectionLiteralExpression: + return Conversion.CollectionLiteral; } return Conversion.NoConversion; diff --git a/src/Compilers/CSharp/Portable/BoundTree/BoundNodes.xml b/src/Compilers/CSharp/Portable/BoundTree/BoundNodes.xml index c42355980fe3d..1eefbc1b2ac48 100644 --- a/src/Compilers/CSharp/Portable/BoundTree/BoundNodes.xml +++ b/src/Compilers/CSharp/Portable/BoundTree/BoundNodes.xml @@ -1830,6 +1830,30 @@ + + + + + + + + + + + + + + + + + + + + + - - - - + + + + + + + + + + From 0badeaae1e87d6e57090a98da867d293d4f910d4 Mon Sep 17 00:00:00 2001 From: Charles Stoner <10732005+cston@users.noreply.github.com> Date: Tue, 28 Feb 2023 22:47:13 -0800 Subject: [PATCH 04/18] Respond to feedback --- .../Portable/Binder/Binder_Conversions.cs | 2 +- .../LocalRewriter_CollectionLiteral.cs | 1 + .../Operations/CSharpOperationFactory.cs | 45 +++++++++---------- 3 files changed, 24 insertions(+), 24 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Conversions.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Conversions.cs index 46e53ba45f9e9..32cbbf871f04f 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Conversions.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Conversions.cs @@ -479,7 +479,7 @@ BoundArrayOrSpanCollectionLiteralExpression bindArrayOrSpan( CSharpSyntaxNode syntax, MethodSymbol? spanConstructor, BoundObjectOrCollectionValuePlaceholder implicitReceiver, - ImmutableArray< BoundExpression> elements, + ImmutableArray elements, TypeSymbol elementType, BindingDiagnosticBag diagnostics) { diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_CollectionLiteral.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_CollectionLiteral.cs index ef4efaddc7ad0..816ab3850e118 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_CollectionLiteral.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_CollectionLiteral.cs @@ -33,6 +33,7 @@ internal sealed partial class LocalRewriter var initializers = node.Initializers; int arrayLength = initializers.Length; + // PROTOTYPE: Should [] be emitted as Array.Empty()? var initialization = (arrayLength == 0) ? null : new BoundArrayInitialization( diff --git a/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory.cs b/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory.cs index e85ea085bbcb8..d198e0e07ee0c 100644 --- a/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory.cs +++ b/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory.cs @@ -1225,27 +1225,36 @@ private IOperation CreateBoundArrayOrSpanCollectionLiteralExpression(BoundArrayO ITypeSymbol? collectionType = boundCollectionLiteralExpression.GetPublicTypeSymbol(); bool isImplicit = boundCollectionLiteralExpression.WasCompilerGenerated; ImmutableArray elementValues = CreateFromArray(boundCollectionLiteralExpression.Initializers); - IOperation collectionCreation; - if (collectionType is IArrayTypeSymbol arrayType) - { - collectionCreation = createArray(arrayType); - } - else + + var spanConstructor = boundCollectionLiteralExpression.SpanConstructor?.GetPublicSymbol(); + var arrayType = (IArrayTypeSymbol)(spanConstructor is null ? collectionType : spanConstructor.Parameters[0].Type); + IOperation size = new LiteralOperation( + _semanticModel, + syntax, + _semanticModel.Compilation.GetSpecialType(SpecialType.System_Int32), + ConstantValue.Create(boundCollectionLiteralExpression.Initializers.Length), + isImplicit: true); + IOperation collectionCreation = new ArrayCreationOperation( + ImmutableArray.Create(size), + initializer: null, + _semanticModel, + syntax, + arrayType, + isImplicit: true); + + if (spanConstructor is { }) { - var spanConstructor = boundCollectionLiteralExpression.SpanConstructor; - Debug.Assert(spanConstructor is { }); - var constructorParameter = spanConstructor.Parameters[0].GetPublicSymbol(); IArgumentOperation constructorArgument = new ArgumentOperation( ArgumentKind.Explicit, - constructorParameter, - createArray(constructorParameter.Type), + spanConstructor.Parameters[0], + collectionCreation, OperationFactory.IdentityConversion, OperationFactory.IdentityConversion, _semanticModel, syntax, isImplicit: true); collectionCreation = new ObjectCreationOperation( - spanConstructor.GetPublicSymbol(), + spanConstructor, initializer: null, ImmutableArray.Create(constructorArgument), _semanticModel, @@ -1254,18 +1263,8 @@ private IOperation CreateBoundArrayOrSpanCollectionLiteralExpression(BoundArrayO constantValue: null, isImplicit: true); } - return new CollectionLiteralOperation(collectionCreation, elementValues, _semanticModel, syntax, collectionType, isImplicit: isImplicit); - ArrayCreationOperation createArray(ITypeSymbol arrayType) - { - var size = new LiteralOperation( - _semanticModel, - syntax, - _semanticModel.Compilation.GetSpecialType(SpecialType.System_Int32), - ConstantValue.Create(boundCollectionLiteralExpression.Initializers.Length), - isImplicit: true); - return new ArrayCreationOperation(ImmutableArray.Create(size), initializer: null, _semanticModel, syntax, arrayType, isImplicit: true); - } + return new CollectionLiteralOperation(collectionCreation, elementValues, _semanticModel, syntax, collectionType, isImplicit: isImplicit); } private IOperation CreateBoundCollectionInitializerCollectionLiteralExpression(BoundCollectionInitializerCollectionLiteralExpression boundCollectionLiteralExpression) From f77c0957314b63e9b7f58b60655097814801e0eb Mon Sep 17 00:00:00 2001 From: Charles Stoner <10732005+cston@users.noreply.github.com> Date: Wed, 1 Mar 2023 07:45:27 -0800 Subject: [PATCH 05/18] Fix build; update error ids --- .../CSharp/Portable/Errors/ErrorCode.cs | 7 ++-- .../CSharp/Portable/Errors/MessageID.cs | 2 +- .../Operations/CSharpOperationFactory.cs | 3 +- .../Semantics/CollectionLiteralTests.cs | 42 +++++++++---------- 4 files changed, 28 insertions(+), 26 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs b/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs index c7589a165092f..f666dcefbd23c 100644 --- a/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs +++ b/src/Compilers/CSharp/Portable/Errors/ErrorCode.cs @@ -2162,9 +2162,10 @@ internal enum ErrorCode ERR_UnscopedRefAttributeInterfaceImplementation = 9102, ERR_UnrecognizedRefSafetyRulesAttributeVersion = 9103, ERR_BadSpecialByRefUsing = 9104, - ERR_CollectionLiteralTargetTypeNotConstructible = 9105, - ERR_ExpressionTreeContainsCollectionLiteral = 9106, - ERR_CollectionLiteralElementNotImplemented = 9107, // PROTOTYPE: Temporary error until feature has been implemented. + + ERR_CollectionLiteralTargetTypeNotConstructible = 9500, // PROTOTYPE: Update error numbers. + ERR_ExpressionTreeContainsCollectionLiteral = 9501, + ERR_CollectionLiteralElementNotImplemented = 9502, // PROTOTYPE: Temporary error until feature has been implemented. #endregion diff --git a/src/Compilers/CSharp/Portable/Errors/MessageID.cs b/src/Compilers/CSharp/Portable/Errors/MessageID.cs index 3bb3657c03e9b..19f64d3cb5b48 100644 --- a/src/Compilers/CSharp/Portable/Errors/MessageID.cs +++ b/src/Compilers/CSharp/Portable/Errors/MessageID.cs @@ -262,7 +262,7 @@ internal enum MessageID IDS_Missing = MessageBase + 12830, IDS_FeatureLambdaOptionalParameters = MessageBase + 12831, IDS_FeatureLambdaParamsArray = MessageBase + 12832, - IDS_FeatureCollectionLiterals = MessageBase + 12833, + IDS_FeatureCollectionLiterals = MessageBase + 12900, // PROTOTYPE: Update message number. } // Message IDs may refer to strings that need to be localized. diff --git a/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory.cs b/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory.cs index d198e0e07ee0c..1783a220a7781 100644 --- a/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory.cs +++ b/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory.cs @@ -1222,9 +1222,10 @@ private IArrayInitializerOperation CreateBoundArrayInitializationOperation(Bound private IOperation CreateBoundArrayOrSpanCollectionLiteralExpression(BoundArrayOrSpanCollectionLiteralExpression boundCollectionLiteralExpression) { SyntaxNode syntax = boundCollectionLiteralExpression.Syntax; - ITypeSymbol? collectionType = boundCollectionLiteralExpression.GetPublicTypeSymbol(); bool isImplicit = boundCollectionLiteralExpression.WasCompilerGenerated; ImmutableArray elementValues = CreateFromArray(boundCollectionLiteralExpression.Initializers); + ITypeSymbol? collectionType = boundCollectionLiteralExpression.GetPublicTypeSymbol(); + Debug.Assert(collectionType is { }); var spanConstructor = boundCollectionLiteralExpression.SpanConstructor?.GetPublicSymbol(); var arrayType = (IArrayTypeSymbol)(spanConstructor is null ? collectionType : spanConstructor.Parameters[0].Type); diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/CollectionLiteralTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/CollectionLiteralTests.cs index 6c8021bb609a1..a9be6d7aa3547 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/CollectionLiteralTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/CollectionLiteralTests.cs @@ -133,10 +133,10 @@ static void Main() """; var comp = CreateCompilation(source); comp.VerifyEmitDiagnostics( - // (5,20): error CS9105: Cannot initialize type 'object' with a collection literal because the type is not constructible. + // (5,20): 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, 20), - // (6,21): error CS9105: Cannot initialize type 'dynamic' with a collection literal because the type is not constructible. + // (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 @@ -160,10 +160,10 @@ static void Main() """; var comp = CreateCompilation(source); comp.VerifyEmitDiagnostics( - // (5,20): error CS9105: Cannot initialize type 'object' with a collection literal because the type is not constructible. + // (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), - // (6,21): error CS9105: Cannot initialize type 'dynamic' with a collection literal because the type is not constructible. + // (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 @@ -186,10 +186,10 @@ static void Main() """; var comp = CreateCompilation(source); comp.VerifyEmitDiagnostics( - // (5,23): error CS9105: Cannot initialize type 'object' with a collection literal because the type is not constructible. + // (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 CS9105: Cannot initialize type 'object' with a collection literal because the type is not constructible. + // (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)); } @@ -501,10 +501,10 @@ static void Main() """; var comp = CreateCompilation(source); comp.VerifyEmitDiagnostics( - // (5,23): error CS9105: Cannot initialize type 'object[*,*]' with a collection literal because the type is not constructible. + // (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,20): error CS9105: Cannot initialize type 'int[*,*]' with a collection literal because the type is not constructible. + // (6,20): error CS9500: Cannot initialize type 'int[*,*]' with a collection literal because the type is not constructible. // int[,] y = [null, 2]; Diagnostic(ErrorCode.ERR_CollectionLiteralTargetTypeNotConstructible, "[null, 2]").WithArguments("int[*,*]").WithLocation(6, 20), // (6,21): error CS0037: Cannot convert null to 'int' because it is a non-nullable value type @@ -806,10 +806,10 @@ struct S { } """; var comp = CreateCompilation(source); comp.VerifyEmitDiagnostics( - // (2,5): error CS9105: Cannot initialize type 'S' with a collection literal because the type is not constructible. + // (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 CS9105: Cannot initialize type 'S' with a collection literal because the type is not constructible. + // (3,5): error CS9500: Cannot initialize type 'S' with a collection literal because the type is not constructible. // s = [1, 2]; Diagnostic(ErrorCode.ERR_CollectionLiteralTargetTypeNotConstructible, "[1, 2]").WithArguments("S").WithLocation(3, 5)); } @@ -1290,7 +1290,7 @@ class Program """; var comp = CreateCompilation(source); comp.VerifyEmitDiagnostics( - // (16,57): error CS9105: Cannot initialize type 'T?' with a collection literal because the type is not constructible. + // (16,57): error CS9500: Cannot initialize type 'T?' with a collection literal because the type is not constructible. // static T? Create2() where T : struct, I => []; Diagnostic(ErrorCode.ERR_CollectionLiteralTargetTypeNotConstructible, "[]").WithArguments("T?").WithLocation(16, 57)); } @@ -1318,7 +1318,7 @@ static void Main() // (8,14): error CS7036: There is no argument given that corresponds to the required parameter 'value' of 'Dictionary.Add(int, int)' // d = [new KeyValuePair(1, 2)]; Diagnostic(ErrorCode.ERR_NoCorrespondingArgument, "new KeyValuePair(1, 2)").WithArguments("value", "System.Collections.Generic.Dictionary.Add(int, int)").WithLocation(8, 14), - // (9,14): error CS9107: Support for collection literal dictionary and spread elements has not been implemented. + // (9,14): error CS9502: Support for collection literal dictionary and spread elements has not been implemented. // d = [3:4]; Diagnostic(ErrorCode.ERR_CollectionLiteralElementNotImplemented, "3:4").WithLocation(9, 14)); } @@ -1339,10 +1339,10 @@ static void Main() """; var comp = CreateCompilation(source); comp.VerifyEmitDiagnostics( - // (7,14): error CS9107: Support for collection literal dictionary and spread elements has not been implemented. + // (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), - // (7,19): error CS9107: Support for collection literal dictionary and spread elements has not been implemented. + // (7,19): error CS9502: Support for collection literal dictionary and spread elements has not been implemented. // a = [..a, ..[1, 2]]; Diagnostic(ErrorCode.ERR_CollectionLiteralElementNotImplemented, "..[1, 2]").WithLocation(7, 19)); } @@ -1476,16 +1476,16 @@ static void Main() """; var comp = CreateCompilation(source); comp.VerifyEmitDiagnostics( - // (13,24): error CS9105: Cannot initialize type 'S?' with a collection literal because the type is not constructible. + // (13,24): error CS9500: Cannot initialize type 'S?' with a collection literal because the type is not constructible. // S? x = []; Diagnostic(ErrorCode.ERR_CollectionLiteralTargetTypeNotConstructible, "[]").WithArguments("S?").WithLocation(13, 24), - // (14,13): error CS9105: Cannot initialize type 'S?' with a collection literal because the type is not constructible. + // (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 CS9105: Cannot initialize type 'S?' with a collection literal because the type is not constructible. + // (15,24): error CS9500: Cannot initialize type 'S?' with a collection literal because the type is not constructible. // S? y = [1]; Diagnostic(ErrorCode.ERR_CollectionLiteralTargetTypeNotConstructible, "[1]").WithArguments("S?").WithLocation(15, 24), - // (16,13): error CS9105: Cannot initialize type 'S?' with a collection literal because the type is not constructible. + // (16,13): error CS9500: Cannot initialize type 'S?' with a collection literal because the type is not constructible. // y = [2]; Diagnostic(ErrorCode.ERR_CollectionLiteralTargetTypeNotConstructible, "[2]").WithArguments("S?").WithLocation(16, 13)); } @@ -1619,13 +1619,13 @@ static Expression>> Create2() """; var comp = CreateCompilation(source); comp.VerifyEmitDiagnostics( - // (13,22): error CS9106: An expression tree may not contain a collection literal. + // (13,22): error CS9501: An expression tree may not contain a collection literal. // return () => []; Diagnostic(ErrorCode.ERR_ExpressionTreeContainsCollectionLiteral, "[]").WithLocation(13, 22), - // (17,22): error CS9106: An expression tree may not contain a collection literal. + // (17,22): error CS9501: An expression tree may not contain a collection literal. // return () => [1, 2]; Diagnostic(ErrorCode.ERR_ExpressionTreeContainsCollectionLiteral, "[1, 2]").WithLocation(17, 22), - // (21,22): error CS9106: An expression tree may not contain a collection literal. + // (21,22): error CS9501: An expression tree may not contain a collection literal. // return () => [a, b]; Diagnostic(ErrorCode.ERR_ExpressionTreeContainsCollectionLiteral, "[a, b]").WithLocation(21, 22)); } From b21b373adba9f3e9461bf5a11c461d120787d316 Mon Sep 17 00:00:00 2001 From: Charles Stoner <10732005+cston@users.noreply.github.com> Date: Fri, 3 Mar 2023 15:59:35 -0800 Subject: [PATCH 06/18] Address feedback --- .../Portable/Binder/Binder_Conversions.cs | 4 + .../Semantics/Conversions/ConversionsBase.cs | 1 + .../CSharp/Portable/BoundTree/BoundNodes.xml | 3 + .../Compilation/CSharpSemanticModel.cs | 19 + .../Generated/BoundNodes.xml.Generated.cs | 46 +- .../Operations/CSharpOperationFactory.cs | 76 +-- .../Semantics/CollectionLiteralTests.cs | 469 ++++++++++++++++-- .../Generated/OperationKind.Generated.cs | 2 - .../Generated/Operations.Generated.cs | 100 ---- .../Operations/ControlFlowGraphBuilder.cs | 17 - .../Operations/OperationInterfaces.xml | 22 - .../Compilation/ControlFlowGraphVerifier.cs | 1 - .../Core/Compilation/TestOperationVisitor.cs | 12 - 13 files changed, 542 insertions(+), 230 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Conversions.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Conversions.cs index 32cbbf871f04f..42ee9857be60c 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Conversions.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Conversions.cs @@ -491,6 +491,8 @@ BoundArrayOrSpanCollectionLiteralExpression bindArrayOrSpan( return new BoundArrayOrSpanCollectionLiteralExpression( syntax, spanConstructor, + naturalTypeOpt: null, // PROTOTYPE: Support natural type. + wasTargetTyped: true, // PROTOTYPE: Support natural type. implicitReceiver, builder.ToImmutableAndFree(), targetType) @@ -538,6 +540,8 @@ BoundCollectionInitializerCollectionLiteralExpression bindCollectionInitializer( return new BoundCollectionInitializerCollectionLiteralExpression( syntax, collectionCreation, + naturalTypeOpt: null, // PROTOTYPE: Support natural type. + wasTargetTyped: true, // PROTOTYPE: Support natural type. implicitReceiver, builder.ToImmutableAndFree(), targetType) diff --git a/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/ConversionsBase.cs b/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/ConversionsBase.cs index d741b7e6d8619..1eaff9c66d5ac 100644 --- a/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/ConversionsBase.cs +++ b/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/ConversionsBase.cs @@ -571,6 +571,7 @@ public Conversion ClassifyStandardConversion(BoundExpression sourceExpression, T return Conversion.NoConversion; } + // PROTOTYPE: Ensure collection literal conversions are not considered standard implicit conversions. private static bool IsStandardImplicitConversionFromExpression(ConversionKind kind) { if (IsStandardImplicitConversionFromType(kind)) diff --git a/src/Compilers/CSharp/Portable/BoundTree/BoundNodes.xml b/src/Compilers/CSharp/Portable/BoundTree/BoundNodes.xml index 532c683c80a0d..6c9a8eaa13d47 100644 --- a/src/Compilers/CSharp/Portable/BoundTree/BoundNodes.xml +++ b/src/Compilers/CSharp/Portable/BoundTree/BoundNodes.xml @@ -1845,6 +1845,9 @@ + + + diff --git a/src/Compilers/CSharp/Portable/Compilation/CSharpSemanticModel.cs b/src/Compilers/CSharp/Portable/Compilation/CSharpSemanticModel.cs index 8588bac47319b..29bf2a8e0e388 100644 --- a/src/Compilers/CSharp/Portable/Compilation/CSharpSemanticModel.cs +++ b/src/Compilers/CSharp/Portable/Compilation/CSharpSemanticModel.cs @@ -2183,6 +2183,25 @@ internal CSharpTypeInfo GetTypeInfoForNode( conversion = Conversion.Identity; } } + else if (boundExpr is BoundCollectionLiteralExpression { WasTargetTyped: true } convertedCollection) + { + if (highestBoundExpr is BoundConversion { ConversionKind: ConversionKind.CollectionLiteral, Conversion: var convertedCollectionConversion }) + { + // There was an implicit cast. + type = convertedCollection.NaturalTypeOpt; + convertedType = convertedCollection.Type; + convertedNullability = convertedCollection.TopLevelNullability; + conversion = convertedCollectionConversion.IsValid ? convertedCollectionConversion : Conversion.NoConversion; + } + else + { + Debug.Assert(false); // PROTOTYPE: Are we hitting this path? + // There was an explicit cast on top of this + type = convertedCollection.NaturalTypeOpt; + (convertedType, convertedNullability) = (type, nullability); + conversion = Conversion.Identity; + } + } else if (highestBoundExpr != null && highestBoundExpr != boundExpr && highestBoundExpr.HasExpressionType()) { (convertedType, convertedNullability) = getTypeAndNullability(highestBoundExpr); diff --git a/src/Compilers/CSharp/Portable/Generated/BoundNodes.xml.Generated.cs b/src/Compilers/CSharp/Portable/Generated/BoundNodes.xml.Generated.cs index ef2b9dee9fd7d..e2e2a14a446f8 100644 --- a/src/Compilers/CSharp/Portable/Generated/BoundNodes.xml.Generated.cs +++ b/src/Compilers/CSharp/Portable/Generated/BoundNodes.xml.Generated.cs @@ -6150,7 +6150,7 @@ public BoundUnconvertedCollectionLiteralExpression Update(ImmutableArray initializers, TypeSymbol type, bool hasErrors = false) + protected BoundCollectionLiteralExpression(BoundKind kind, SyntaxNode syntax, TypeSymbol? naturalTypeOpt, bool wasTargetTyped, BoundObjectOrCollectionValuePlaceholder placeholder, ImmutableArray initializers, TypeSymbol type, bool hasErrors = false) : base(kind, syntax, type, hasErrors) { @@ -6158,19 +6158,23 @@ protected BoundCollectionLiteralExpression(BoundKind kind, SyntaxNode syntax, Bo RoslynDebug.Assert(!initializers.IsDefault, "Field 'initializers' cannot be null (use Null=\"allow\" in BoundNodes.xml to remove this check)"); RoslynDebug.Assert(type is object, "Field 'type' cannot be null (make the type nullable in BoundNodes.xml to remove this check)"); + this.NaturalTypeOpt = naturalTypeOpt; + this.WasTargetTyped = wasTargetTyped; this.Placeholder = placeholder; this.Initializers = initializers; } public new TypeSymbol Type => base.Type!; + public TypeSymbol? NaturalTypeOpt { get; } + public bool WasTargetTyped { get; } public BoundObjectOrCollectionValuePlaceholder Placeholder { get; } public ImmutableArray Initializers { get; } } internal sealed partial class BoundArrayOrSpanCollectionLiteralExpression : BoundCollectionLiteralExpression { - public BoundArrayOrSpanCollectionLiteralExpression(SyntaxNode syntax, MethodSymbol? spanConstructor, BoundObjectOrCollectionValuePlaceholder placeholder, ImmutableArray initializers, TypeSymbol type, bool hasErrors = false) - : base(BoundKind.ArrayOrSpanCollectionLiteralExpression, syntax, placeholder, initializers, type, hasErrors || placeholder.HasErrors() || initializers.HasErrors()) + public BoundArrayOrSpanCollectionLiteralExpression(SyntaxNode syntax, MethodSymbol? spanConstructor, TypeSymbol? naturalTypeOpt, bool wasTargetTyped, BoundObjectOrCollectionValuePlaceholder placeholder, ImmutableArray initializers, TypeSymbol type, bool hasErrors = false) + : base(BoundKind.ArrayOrSpanCollectionLiteralExpression, syntax, naturalTypeOpt, wasTargetTyped, placeholder, initializers, type, hasErrors || placeholder.HasErrors() || initializers.HasErrors()) { RoslynDebug.Assert(placeholder is object, "Field 'placeholder' cannot be null (make the type nullable in BoundNodes.xml to remove this check)"); @@ -6185,11 +6189,11 @@ public BoundArrayOrSpanCollectionLiteralExpression(SyntaxNode syntax, MethodSymb [DebuggerStepThrough] public override BoundNode? Accept(BoundTreeVisitor visitor) => visitor.VisitArrayOrSpanCollectionLiteralExpression(this); - public BoundArrayOrSpanCollectionLiteralExpression Update(MethodSymbol? spanConstructor, BoundObjectOrCollectionValuePlaceholder placeholder, ImmutableArray initializers, TypeSymbol type) + public BoundArrayOrSpanCollectionLiteralExpression Update(MethodSymbol? spanConstructor, TypeSymbol? naturalTypeOpt, bool wasTargetTyped, BoundObjectOrCollectionValuePlaceholder placeholder, ImmutableArray initializers, TypeSymbol type) { - if (!Symbols.SymbolEqualityComparer.ConsiderEverything.Equals(spanConstructor, this.SpanConstructor) || placeholder != this.Placeholder || initializers != this.Initializers || !TypeSymbol.Equals(type, this.Type, TypeCompareKind.ConsiderEverything)) + if (!Symbols.SymbolEqualityComparer.ConsiderEverything.Equals(spanConstructor, this.SpanConstructor) || !TypeSymbol.Equals(naturalTypeOpt, this.NaturalTypeOpt, TypeCompareKind.ConsiderEverything) || wasTargetTyped != this.WasTargetTyped || placeholder != this.Placeholder || initializers != this.Initializers || !TypeSymbol.Equals(type, this.Type, TypeCompareKind.ConsiderEverything)) { - var result = new BoundArrayOrSpanCollectionLiteralExpression(this.Syntax, spanConstructor, placeholder, initializers, type, this.HasErrors); + var result = new BoundArrayOrSpanCollectionLiteralExpression(this.Syntax, spanConstructor, naturalTypeOpt, wasTargetTyped, placeholder, initializers, type, this.HasErrors); result.CopyAttributes(this); return result; } @@ -6199,8 +6203,8 @@ public BoundArrayOrSpanCollectionLiteralExpression Update(MethodSymbol? spanCons internal sealed partial class BoundCollectionInitializerCollectionLiteralExpression : BoundCollectionLiteralExpression { - public BoundCollectionInitializerCollectionLiteralExpression(SyntaxNode syntax, BoundExpression? collectionCreation, BoundObjectOrCollectionValuePlaceholder placeholder, ImmutableArray initializers, TypeSymbol type, bool hasErrors = false) - : base(BoundKind.CollectionInitializerCollectionLiteralExpression, syntax, placeholder, initializers, type, hasErrors || collectionCreation.HasErrors() || placeholder.HasErrors() || initializers.HasErrors()) + public BoundCollectionInitializerCollectionLiteralExpression(SyntaxNode syntax, BoundExpression? collectionCreation, TypeSymbol? naturalTypeOpt, bool wasTargetTyped, BoundObjectOrCollectionValuePlaceholder placeholder, ImmutableArray initializers, TypeSymbol type, bool hasErrors = false) + : base(BoundKind.CollectionInitializerCollectionLiteralExpression, syntax, naturalTypeOpt, wasTargetTyped, placeholder, initializers, type, hasErrors || collectionCreation.HasErrors() || placeholder.HasErrors() || initializers.HasErrors()) { RoslynDebug.Assert(placeholder is object, "Field 'placeholder' cannot be null (make the type nullable in BoundNodes.xml to remove this check)"); @@ -6215,11 +6219,11 @@ public BoundCollectionInitializerCollectionLiteralExpression(SyntaxNode syntax, [DebuggerStepThrough] public override BoundNode? Accept(BoundTreeVisitor visitor) => visitor.VisitCollectionInitializerCollectionLiteralExpression(this); - public BoundCollectionInitializerCollectionLiteralExpression Update(BoundExpression? collectionCreation, BoundObjectOrCollectionValuePlaceholder placeholder, ImmutableArray initializers, TypeSymbol type) + public BoundCollectionInitializerCollectionLiteralExpression Update(BoundExpression? collectionCreation, TypeSymbol? naturalTypeOpt, bool wasTargetTyped, BoundObjectOrCollectionValuePlaceholder placeholder, ImmutableArray initializers, TypeSymbol type) { - if (collectionCreation != this.CollectionCreation || placeholder != this.Placeholder || initializers != this.Initializers || !TypeSymbol.Equals(type, this.Type, TypeCompareKind.ConsiderEverything)) + if (collectionCreation != this.CollectionCreation || !TypeSymbol.Equals(naturalTypeOpt, this.NaturalTypeOpt, TypeCompareKind.ConsiderEverything) || wasTargetTyped != this.WasTargetTyped || placeholder != this.Placeholder || initializers != this.Initializers || !TypeSymbol.Equals(type, this.Type, TypeCompareKind.ConsiderEverything)) { - var result = new BoundCollectionInitializerCollectionLiteralExpression(this.Syntax, collectionCreation, placeholder, initializers, type, this.HasErrors); + var result = new BoundCollectionInitializerCollectionLiteralExpression(this.Syntax, collectionCreation, naturalTypeOpt, wasTargetTyped, placeholder, initializers, type, this.HasErrors); result.CopyAttributes(this); return result; } @@ -11309,16 +11313,18 @@ internal abstract partial class BoundTreeRewriter : BoundTreeVisitor { BoundObjectOrCollectionValuePlaceholder placeholder = node.Placeholder; ImmutableArray initializers = this.VisitList(node.Initializers); + TypeSymbol? naturalTypeOpt = this.VisitType(node.NaturalTypeOpt); TypeSymbol? type = this.VisitType(node.Type); - return node.Update(node.SpanConstructor, placeholder, initializers, type); + return node.Update(node.SpanConstructor, naturalTypeOpt, node.WasTargetTyped, placeholder, initializers, type); } public override BoundNode? VisitCollectionInitializerCollectionLiteralExpression(BoundCollectionInitializerCollectionLiteralExpression node) { BoundExpression? collectionCreation = node.CollectionCreation; BoundObjectOrCollectionValuePlaceholder placeholder = node.Placeholder; ImmutableArray initializers = this.VisitList(node.Initializers); + TypeSymbol? naturalTypeOpt = this.VisitType(node.NaturalTypeOpt); TypeSymbol? type = this.VisitType(node.Type); - return node.Update(collectionCreation, placeholder, initializers, type); + return node.Update(collectionCreation, naturalTypeOpt, node.WasTargetTyped, placeholder, initializers, type); } public override BoundNode? VisitTupleLiteral(BoundTupleLiteral node) { @@ -13463,24 +13469,26 @@ public NullabilityRewriter(ImmutableDictionary initializers = this.VisitList(node.Initializers); BoundArrayOrSpanCollectionLiteralExpression updatedNode; if (_updatedNullabilities.TryGetValue(node, out (NullabilityInfo Info, TypeSymbol? Type) infoAndType)) { - updatedNode = node.Update(spanConstructor, placeholder, initializers, infoAndType.Type!); + updatedNode = node.Update(spanConstructor, naturalTypeOpt, node.WasTargetTyped, placeholder, initializers, infoAndType.Type!); updatedNode.TopLevelNullability = infoAndType.Info; } else { - updatedNode = node.Update(spanConstructor, placeholder, initializers, node.Type); + updatedNode = node.Update(spanConstructor, naturalTypeOpt, node.WasTargetTyped, placeholder, initializers, node.Type); } return updatedNode; } public override BoundNode? VisitCollectionInitializerCollectionLiteralExpression(BoundCollectionInitializerCollectionLiteralExpression node) { + TypeSymbol? naturalTypeOpt = GetUpdatedSymbol(node, node.NaturalTypeOpt); BoundExpression? collectionCreation = node.CollectionCreation; BoundObjectOrCollectionValuePlaceholder placeholder = node.Placeholder; ImmutableArray initializers = this.VisitList(node.Initializers); @@ -13488,12 +13496,12 @@ public NullabilityRewriter(ImmutableDictionary new TreeDumperNode("arrayOrSpanCollectionLiteralExpression", null, new TreeDumperNode[] { new TreeDumperNode("spanConstructor", node.SpanConstructor, null), + new TreeDumperNode("naturalTypeOpt", node.NaturalTypeOpt, null), + new TreeDumperNode("wasTargetTyped", node.WasTargetTyped, null), new TreeDumperNode("placeholder", null, new TreeDumperNode[] { Visit(node.Placeholder, null) }), new TreeDumperNode("initializers", null, from x in node.Initializers select Visit(x, null)), new TreeDumperNode("type", node.Type, null), @@ -15829,6 +15839,8 @@ private BoundTreeDumperNodeProducer() public override TreeDumperNode VisitCollectionInitializerCollectionLiteralExpression(BoundCollectionInitializerCollectionLiteralExpression node, object? arg) => new TreeDumperNode("collectionInitializerCollectionLiteralExpression", null, new TreeDumperNode[] { new TreeDumperNode("collectionCreation", null, new TreeDumperNode[] { Visit(node.CollectionCreation, null) }), + new TreeDumperNode("naturalTypeOpt", node.NaturalTypeOpt, null), + new TreeDumperNode("wasTargetTyped", node.WasTargetTyped, null), new TreeDumperNode("placeholder", null, new TreeDumperNode[] { Visit(node.Placeholder, null) }), new TreeDumperNode("initializers", null, from x in node.Initializers select Visit(x, null)), new TreeDumperNode("type", node.Type, null), diff --git a/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory.cs b/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory.cs index 1783a220a7781..f8a12e288aeca 100644 --- a/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory.cs +++ b/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory.cs @@ -1222,39 +1222,49 @@ private IArrayInitializerOperation CreateBoundArrayInitializationOperation(Bound private IOperation CreateBoundArrayOrSpanCollectionLiteralExpression(BoundArrayOrSpanCollectionLiteralExpression boundCollectionLiteralExpression) { SyntaxNode syntax = boundCollectionLiteralExpression.Syntax; - bool isImplicit = boundCollectionLiteralExpression.WasCompilerGenerated; - ImmutableArray elementValues = CreateFromArray(boundCollectionLiteralExpression.Initializers); ITypeSymbol? collectionType = boundCollectionLiteralExpression.GetPublicTypeSymbol(); - Debug.Assert(collectionType is { }); - - var spanConstructor = boundCollectionLiteralExpression.SpanConstructor?.GetPublicSymbol(); - var arrayType = (IArrayTypeSymbol)(spanConstructor is null ? collectionType : spanConstructor.Parameters[0].Type); - IOperation size = new LiteralOperation( + bool isImplicit = boundCollectionLiteralExpression.WasCompilerGenerated; + var spanConstructor = boundCollectionLiteralExpression.SpanConstructor.GetPublicSymbol(); + var initializer = new ArrayInitializerOperation( + CreateFromArray(boundCollectionLiteralExpression.Initializers), _semanticModel, syntax, - _semanticModel.Compilation.GetSpecialType(SpecialType.System_Int32), - ConstantValue.Create(boundCollectionLiteralExpression.Initializers.Length), isImplicit: true); - IOperation collectionCreation = new ArrayCreationOperation( - ImmutableArray.Create(size), - initializer: null, + ImmutableArray dimensionSizes = ImmutableArray.Create(new LiteralOperation( _semanticModel, syntax, - arrayType, - isImplicit: true); - - if (spanConstructor is { }) + _semanticModel.Compilation.GetSpecialType(SpecialType.System_Int32), + ConstantValue.Create(boundCollectionLiteralExpression.Initializers.Length), + isImplicit: true)); + if (spanConstructor is null) + { + return new ArrayCreationOperation( + dimensionSizes, + initializer, + _semanticModel, + syntax, + collectionType, + isImplicit: isImplicit); + } + else { + var array = new ArrayCreationOperation( + dimensionSizes, + initializer, + _semanticModel, + syntax, + spanConstructor.Parameters[0].Type, + isImplicit: true); IArgumentOperation constructorArgument = new ArgumentOperation( ArgumentKind.Explicit, spanConstructor.Parameters[0], - collectionCreation, + array, OperationFactory.IdentityConversion, OperationFactory.IdentityConversion, _semanticModel, syntax, isImplicit: true); - collectionCreation = new ObjectCreationOperation( + return new ObjectCreationOperation( spanConstructor, initializer: null, ImmutableArray.Create(constructorArgument), @@ -1262,10 +1272,8 @@ private IOperation CreateBoundArrayOrSpanCollectionLiteralExpression(BoundArrayO syntax, collectionType, constantValue: null, - isImplicit: true); + isImplicit); } - - return new CollectionLiteralOperation(collectionCreation, elementValues, _semanticModel, syntax, collectionType, isImplicit: isImplicit); } private IOperation CreateBoundCollectionInitializerCollectionLiteralExpression(BoundCollectionInitializerCollectionLiteralExpression boundCollectionLiteralExpression) @@ -1273,22 +1281,34 @@ private IOperation CreateBoundCollectionInitializerCollectionLiteralExpression(B SyntaxNode syntax = boundCollectionLiteralExpression.Syntax; ITypeSymbol? collectionType = boundCollectionLiteralExpression.GetPublicTypeSymbol(); bool isImplicit = boundCollectionLiteralExpression.WasCompilerGenerated; - ImmutableArray elementValues = CreateFromArray(boundCollectionLiteralExpression.Initializers); - IOperation collectionCreation; - if (collectionType is ITypeParameterSymbol typeParameter) + var initializer = new ObjectOrCollectionInitializerOperation( + CreateFromArray(boundCollectionLiteralExpression.Initializers), + _semanticModel, + syntax, + collectionType, + isImplicit: true); + if (collectionType is ITypeParameterSymbol) { - collectionCreation = new TypeParameterObjectCreationOperation(initializer: null, _semanticModel, syntax, typeParameter, isImplicit: true); + return new TypeParameterObjectCreationOperation(initializer, _semanticModel, syntax, collectionType, isImplicit: isImplicit); } - else if (boundCollectionLiteralExpression.CollectionCreation is { }) + else if (boundCollectionLiteralExpression.CollectionCreation is BoundObjectCreationExpression collectionCreation) { - collectionCreation = Create(boundCollectionLiteralExpression.CollectionCreation); + Debug.Assert(collectionCreation.Arguments.IsEmpty); + return new ObjectCreationOperation( + collectionCreation.Constructor.GetPublicSymbol(), + initializer, + arguments: ImmutableArray.Empty, + _semanticModel, + syntax, + collectionType, + constantValue: null, + isImplicit: isImplicit); } else { // PROTOTYPE: Temporary until natural type is supported. return new NoneOperation(ImmutableArray.Empty, _semanticModel, syntax, type: collectionType, constantValue: null, isImplicit: isImplicit); } - return new CollectionLiteralOperation(collectionCreation, elementValues, _semanticModel, syntax, collectionType, isImplicit: isImplicit); } private IDefaultValueOperation CreateBoundDefaultLiteralOperation(BoundDefaultLiteral boundDefaultLiteral) diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/CollectionLiteralTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/CollectionLiteralTests.cs index a9be6d7aa3547..5d8785442233c 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/CollectionLiteralTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/CollectionLiteralTests.cs @@ -5,6 +5,7 @@ #nullable disable using System.Linq; +using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.CSharp.Test.Utilities; using Microsoft.CodeAnalysis.Test.Utilities; using Roslyn.Test.Utilities; @@ -816,6 +817,39 @@ struct S { } [Fact] public void CollectionInitializerType_03() + { + string source = """ + using System.Collections; + S s; + s = []; + struct S : IEnumerable + { + IEnumerator IEnumerable.GetEnumerator() => throw null; + } + """; + CompileAndVerify(source, expectedOutput: ""); + + source = """ + using System.Collections; + S s; + s = [1, 2]; + struct S : IEnumerable + { + IEnumerator IEnumerable.GetEnumerator() => throw null; + } + """; + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics( + // (3,6): 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").WithArguments("S", "Add").WithLocation(3, 6), + // (3,9): 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, "2").WithArguments("S", "Add").WithLocation(3, 9)); + } + + [Fact] + public void CollectionInitializerType_04() { string source = """ C c; @@ -837,7 +871,7 @@ class C } [Fact] - public void CollectionInitializerType_04() + public void CollectionInitializerType_05() { string source = """ using System.Collections; @@ -863,7 +897,7 @@ class B } [Fact] - public void CollectionInitializerType_05() + public void CollectionInitializerType_06() { string source = """ using System.Collections; @@ -965,7 +999,7 @@ static void Main() // PROTOTYPE: Test constructor use-site error. [Fact] - public void CollectionInitializerType_06() + public void CollectionInitializerType_07() { string source = """ using System.Collections; @@ -994,7 +1028,7 @@ static void Main() } [Fact] - public void CollectionInitializerType_07() + public void CollectionInitializerType_08() { string source = """ using System; @@ -1099,7 +1133,7 @@ .locals init (S2 V_0) } [Fact] - public void CollectionInitializerType_08() + public void CollectionInitializerType_09() { string source = """ using System.Collections; @@ -1125,7 +1159,7 @@ static void Main() } [Fact] - public void CollectionInitializerType_09() + public void CollectionInitializerType_10() { string source = """ using System.Collections; @@ -1157,7 +1191,7 @@ static void Main() } [Fact] - public void CollectionInitializerType_10() + public void CollectionInitializerType_11() { string source = """ using System.Collections.Generic; @@ -1537,6 +1571,104 @@ Add C`1[System.Int32] """); } + // Ensure collection literal conversions are not standard implicit conversions + // and, as a result, are ignored when determining user-defined conversions. + [Fact] + public void UserDefinedConversion() + { + string source = """ + struct S + { + public static implicit operator S(int[] a) => default; + } + class Program + { + static void Main() + { + S s = []; + s = [1, 2]; + } + } + """; + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics( + // (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. + // s = [1, 2]; + Diagnostic(ErrorCode.ERR_CollectionLiteralTargetTypeNotConstructible, "[1, 2]").WithArguments("S").WithLocation(10, 13)); + } + + [ConditionalFact(typeof(CoreClrOnly))] + public void SemanticModel() + { + string source = """ + using System; + using System.Collections; + using System.Collections.Generic; + struct S1 : IEnumerable + { + IEnumerator IEnumerable.GetEnumerator() => throw null; + } + struct S2 + { + } + class Program + { + static void Main() + { + int[] v1 = []; + List v2 = []; + Span v3 = []; + ReadOnlySpan v4 = []; + S1 v5 = []; + S2 v6 = []; + var v7 = (int[])[]; + var v8 = (List)[]; + var v9 = (Span)[]; + var v10 = (ReadOnlySpan)[]; + var v11 = (S1)[]; + var v12 = (S2)[]; + } + } + """; + var comp = CreateCompilation(source, targetFramework: TargetFramework.Net70); + comp.VerifyEmitDiagnostics( + // (20,17): error CS9500: Cannot initialize type 'S2' with a collection literal because the type is not constructible. + // S2 v6 = []; + Diagnostic(ErrorCode.ERR_CollectionLiteralTargetTypeNotConstructible, "[]").WithArguments("S2").WithLocation(20, 17), + // (26,23): error CS9500: Cannot initialize type 'S2' with a collection literal because the type is not constructible. + // var v12 = (S2)[]; + Diagnostic(ErrorCode.ERR_CollectionLiteralTargetTypeNotConstructible, "[]").WithArguments("S2").WithLocation(26, 23)); + + var tree = comp.SyntaxTrees[0]; + var model = comp.GetSemanticModel(tree); + var collections = tree.GetRoot().DescendantNodes().OfType().ToArray(); + Assert.Equal(12, collections.Length); + verifyTypes(collections[0], "System.Int32[]", ConversionKind.CollectionLiteral); + verifyTypes(collections[1], "System.Collections.Generic.List", ConversionKind.CollectionLiteral); + verifyTypes(collections[2], "System.Span", ConversionKind.CollectionLiteral); + verifyTypes(collections[3], "System.ReadOnlySpan", ConversionKind.CollectionLiteral); + verifyTypes(collections[4], "S1", ConversionKind.CollectionLiteral); + verifyTypes(collections[5], "S2", ConversionKind.CollectionLiteral); + verifyTypes(collections[6], "System.Int32[]", ConversionKind.CollectionLiteral); + verifyTypes(collections[7], "System.Collections.Generic.List", ConversionKind.CollectionLiteral); + verifyTypes(collections[8], "System.Span", ConversionKind.CollectionLiteral); + verifyTypes(collections[9], "System.ReadOnlySpan", ConversionKind.CollectionLiteral); + verifyTypes(collections[10], "S1", ConversionKind.CollectionLiteral); + verifyTypes(collections[11], "S2", ConversionKind.CollectionLiteral); + + void verifyTypes(ExpressionSyntax expr, string expectedConvertedType, ConversionKind expectedConversionKind) + { + var typeInfo = model.GetTypeInfo(expr); + var conversion = model.GetConversion(expr); + Assert.Null(typeInfo.Type); + Assert.Equal(expectedConvertedType, typeInfo.ConvertedType.ToTestDisplayString()); + Assert.Equal(expectedConversionKind, conversion.Kind); + } + } + [ConditionalFact(typeof(DesktopOnly))] public void RestrictedTypes_01() { @@ -1631,10 +1763,235 @@ static Expression>> Create2() } [Fact] - public void IOperation_TypeParameter() + public void IOperation_Array() + { + string source = """ + class Program + { + static T[] Create(T a, T b) + { + return /**/[a, b]/**/; + } + } + """; + + var comp = CreateCompilation(source); + comp.VerifyDiagnostics(); + + VerifyOperationTreeForTest(comp, +@"IArrayCreationOperation (OperationKind.ArrayCreation, Type: T[]) (Syntax: '[a, b]') + Dimension Sizes(1): + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 2, IsImplicit) (Syntax: '[a, b]') + Initializer: + IArrayInitializerOperation (2 elements) (OperationKind.ArrayInitializer, Type: null, IsImplicit) (Syntax: '[a, b]') + Element Values(2): + IParameterReferenceOperation: a (OperationKind.ParameterReference, Type: T, IsImplicit) (Syntax: 'a') + IParameterReferenceOperation: b (OperationKind.ParameterReference, Type: T, IsImplicit) (Syntax: 'b') +"); + + var tree = comp.SyntaxTrees[0]; + var method = tree.GetRoot().DescendantNodes().OfType().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: T[], IsImplicit) (Syntax: '[a, b]') + Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + (CollectionLiteral) + Operand: + IArrayCreationOperation (OperationKind.ArrayCreation, Type: T[]) (Syntax: '[a, b]') + Dimension Sizes(1): + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 2, IsImplicit) (Syntax: '[a, b]') + Initializer: + IArrayInitializerOperation (2 elements) (OperationKind.ArrayInitializer, Type: null, IsImplicit) (Syntax: '[a, b]') + Element Values(2): + IParameterReferenceOperation: a (OperationKind.ParameterReference, Type: T, IsImplicit) (Syntax: 'a') + IParameterReferenceOperation: b (OperationKind.ParameterReference, Type: T, IsImplicit) (Syntax: 'b') +Block[B2] - Exit + Predecessors: [B1] + Statements (0) +"); + } + + [ConditionalFact(typeof(CoreClrOnly))] + public void IOperation_Span() { string source = """ using System; + class Program + { + static Span Create(T a, T b) + { + return /**/[a, b]/**/; + } + } + """; + + var comp = CreateCompilation(source, targetFramework: TargetFramework.Net70); + comp.VerifyDiagnostics(); + + VerifyOperationTreeForTest(comp, +@"IObjectCreationOperation (Constructor: System.Span..ctor(T[]? array)) (OperationKind.ObjectCreation, Type: System.Span) (Syntax: '[a, b]') + Arguments(1): + IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: array) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: '[a, b]') + IArrayCreationOperation (OperationKind.ArrayCreation, Type: T[]?, IsImplicit) (Syntax: '[a, b]') + Dimension Sizes(1): + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 2, IsImplicit) (Syntax: '[a, b]') + Initializer: + IArrayInitializerOperation (2 elements) (OperationKind.ArrayInitializer, Type: null, IsImplicit) (Syntax: '[a, b]') + Element Values(2): + IParameterReferenceOperation: a (OperationKind.ParameterReference, Type: T, IsImplicit) (Syntax: 'a') + IParameterReferenceOperation: b (OperationKind.ParameterReference, Type: T, IsImplicit) (Syntax: '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) + Initializer: + null +"); + + var tree = comp.SyntaxTrees[0]; + var method = tree.GetRoot().DescendantNodes().OfType().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.Span, IsImplicit) (Syntax: '[a, b]') + Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + (CollectionLiteral) + Operand: + IObjectCreationOperation (Constructor: System.Span..ctor(T[]? array)) (OperationKind.ObjectCreation, Type: System.Span) (Syntax: '[a, b]') + Arguments(1): + IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: array) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: '[a, b]') + IArrayCreationOperation (OperationKind.ArrayCreation, Type: T[]?, IsImplicit) (Syntax: '[a, b]') + Dimension Sizes(1): + ILiteralOperation (OperationKind.Literal, Type: System.Int32, Constant: 2, IsImplicit) (Syntax: '[a, b]') + Initializer: + IArrayInitializerOperation (2 elements) (OperationKind.ArrayInitializer, Type: null, IsImplicit) (Syntax: '[a, b]') + Element Values(2): + IParameterReferenceOperation: a (OperationKind.ParameterReference, Type: T, IsImplicit) (Syntax: 'a') + IParameterReferenceOperation: b (OperationKind.ParameterReference, Type: T, IsImplicit) (Syntax: '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) + Initializer: + null +Block[B2] - Exit + Predecessors: [B1] + Statements (0) +"); + } + + [Fact] + public void IOperation_CollectionInitializer() + { + string source = """ + using System.Collections; + using System.Collections.Generic; + interface I : IEnumerable + { + void Add(T t); + } + struct S : I + { + public void Add(T t) { } + IEnumerator IEnumerable.GetEnumerator() => throw null; + IEnumerator IEnumerable.GetEnumerator() => throw null; + } + class Program + { + static S Create(T a, T b) + { + return /**/[a, b]/**/; + } + } + """; + + var comp = CreateCompilation(source); + comp.VerifyDiagnostics(); + + VerifyOperationTreeForTest(comp, +@"IObjectCreationOperation (Constructor: S..ctor()) (OperationKind.ObjectCreation, Type: S) (Syntax: '[a, b]') + Arguments(0) + Initializer: + IObjectOrCollectionInitializerOperation (OperationKind.ObjectOrCollectionInitializer, Type: S, IsImplicit) (Syntax: '[a, b]') + Initializers(2): + IInvocationOperation ( void S.Add(T t)) (OperationKind.Invocation, Type: System.Void, IsImplicit) (Syntax: 'a') + Instance Receiver: + IInstanceReferenceOperation (ReferenceKind: ImplicitReceiver) (OperationKind.InstanceReference, Type: S, IsImplicit) (Syntax: '[a, b]') + Arguments(1): + IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: t) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: 'a') + IParameterReferenceOperation: a (OperationKind.ParameterReference, Type: T) (Syntax: 'a') + 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) + IInvocationOperation ( void S.Add(T t)) (OperationKind.Invocation, Type: System.Void, IsImplicit) (Syntax: 'b') + Instance Receiver: + IInstanceReferenceOperation (ReferenceKind: ImplicitReceiver) (OperationKind.InstanceReference, Type: S, IsImplicit) (Syntax: '[a, b]') + Arguments(1): + IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: t) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: 'b') + IParameterReferenceOperation: b (OperationKind.ParameterReference, Type: T) (Syntax: '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) +"); + + var tree = comp.SyntaxTrees[0]; + var method = tree.GetRoot().DescendantNodes().OfType().Single(m => m.Identifier.Text == "Create"); + VerifyFlowGraph(comp, method, +@"Block[B0] - Entry + Statements (0) + Next (Regular) Block[B1] + Entering: {R1} +.locals {R1} +{ + CaptureIds: [0] + Block[B1] - Block + Predecessors: [B0] + Statements (3) + IFlowCaptureOperation: 0 (OperationKind.FlowCapture, Type: null, IsImplicit) (Syntax: '[a, b]') + Value: + IObjectCreationOperation (Constructor: S..ctor()) (OperationKind.ObjectCreation, Type: S) (Syntax: '[a, b]') + Arguments(0) + Initializer: + null + IInvocationOperation ( void S.Add(T t)) (OperationKind.Invocation, Type: System.Void, IsImplicit) (Syntax: 'a') + Instance Receiver: + IFlowCaptureReferenceOperation: 0 (OperationKind.FlowCaptureReference, Type: S, IsImplicit) (Syntax: '[a, b]') + Arguments(1): + IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: t) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: 'a') + IParameterReferenceOperation: a (OperationKind.ParameterReference, Type: T) (Syntax: 'a') + 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) + IInvocationOperation ( void S.Add(T t)) (OperationKind.Invocation, Type: System.Void, IsImplicit) (Syntax: 'b') + Instance Receiver: + IFlowCaptureReferenceOperation: 0 (OperationKind.FlowCaptureReference, Type: S, IsImplicit) (Syntax: '[a, b]') + Arguments(1): + IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: t) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: 'b') + IParameterReferenceOperation: b (OperationKind.ParameterReference, Type: T) (Syntax: '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) + Next (Return) Block[B2] + IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: S, IsImplicit) (Syntax: '[a, b]') + Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + (CollectionLiteral) + Operand: + IFlowCaptureReferenceOperation: 0 (OperationKind.FlowCaptureReference, Type: S, IsImplicit) (Syntax: '[a, b]') + Leaving: {R1} +} +Block[B2] - Exit + Predecessors: [B1] + Statements (0) +"); + } + + [Fact] + public void IOperation_TypeParameter() + { + string source = """ using System.Collections; using System.Collections.Generic; interface I : IEnumerable @@ -1655,29 +2012,79 @@ class Program } } """; - // PROTOTYPE: Where is the ICollectionLiteralOperation node? - string expectedOperationTree = -@"ITypeParameterObjectCreationOperation (OperationKind.TypeParameterObjectCreation, Type: T, IsImplicit) (Syntax: '[a, b]') - Initializer: - null - IInvocationOperation (virtual void I.Add(U t)) (OperationKind.Invocation, Type: System.Void, IsImplicit) (Syntax: 'a') - Instance Receiver: - IInstanceReferenceOperation (ReferenceKind: ImplicitReceiver) (OperationKind.InstanceReference, Type: T, IsImplicit) (Syntax: '[a, b]') - Arguments(1): - IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: t) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: 'a') - IParameterReferenceOperation: a (OperationKind.ParameterReference, Type: U) (Syntax: 'a') - 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) - IInvocationOperation (virtual void I.Add(U t)) (OperationKind.Invocation, Type: System.Void, IsImplicit) (Syntax: 'b') - Instance Receiver: - IInstanceReferenceOperation (ReferenceKind: ImplicitReceiver) (OperationKind.InstanceReference, Type: T, IsImplicit) (Syntax: '[a, b]') - Arguments(1): - IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: t) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: 'b') - IParameterReferenceOperation: b (OperationKind.ParameterReference, Type: U) (Syntax: '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) -"; - VerifyOperationTreeForTest(source, expectedOperationTree); + + var comp = CreateCompilation(source); + comp.VerifyDiagnostics(); + + VerifyOperationTreeForTest(comp, +@"ITypeParameterObjectCreationOperation (OperationKind.TypeParameterObjectCreation, Type: T) (Syntax: '[a, b]') + Initializer: + IObjectOrCollectionInitializerOperation (OperationKind.ObjectOrCollectionInitializer, Type: T, IsImplicit) (Syntax: '[a, b]') + Initializers(2): + IInvocationOperation (virtual void I.Add(U t)) (OperationKind.Invocation, Type: System.Void, IsImplicit) (Syntax: 'a') + Instance Receiver: + IInstanceReferenceOperation (ReferenceKind: ImplicitReceiver) (OperationKind.InstanceReference, Type: T, IsImplicit) (Syntax: '[a, b]') + Arguments(1): + IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: t) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: 'a') + IParameterReferenceOperation: a (OperationKind.ParameterReference, Type: U) (Syntax: 'a') + 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) + IInvocationOperation (virtual void I.Add(U t)) (OperationKind.Invocation, Type: System.Void, IsImplicit) (Syntax: 'b') + Instance Receiver: + IInstanceReferenceOperation (ReferenceKind: ImplicitReceiver) (OperationKind.InstanceReference, Type: T, IsImplicit) (Syntax: '[a, b]') + Arguments(1): + IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: t) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: 'b') + IParameterReferenceOperation: b (OperationKind.ParameterReference, Type: U) (Syntax: '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) +"); + + var tree = comp.SyntaxTrees[0]; + var method = tree.GetRoot().DescendantNodes().OfType().Single(m => m.Identifier.Text == "Create"); + VerifyFlowGraph(comp, method, +@"Block[B0] - Entry + Statements (0) + Next (Regular) Block[B1] + Entering: {R1} +.locals {R1} +{ + CaptureIds: [0] + Block[B1] - Block + Predecessors: [B0] + Statements (3) + IFlowCaptureOperation: 0 (OperationKind.FlowCapture, Type: null, IsImplicit) (Syntax: '[a, b]') + Value: + ITypeParameterObjectCreationOperation (OperationKind.TypeParameterObjectCreation, Type: T) (Syntax: '[a, b]') + Initializer: + null + IInvocationOperation (virtual void I.Add(U t)) (OperationKind.Invocation, Type: System.Void, IsImplicit) (Syntax: 'a') + Instance Receiver: + IFlowCaptureReferenceOperation: 0 (OperationKind.FlowCaptureReference, Type: T, IsImplicit) (Syntax: '[a, b]') + Arguments(1): + IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: t) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: 'a') + IParameterReferenceOperation: a (OperationKind.ParameterReference, Type: U) (Syntax: 'a') + 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) + IInvocationOperation (virtual void I.Add(U t)) (OperationKind.Invocation, Type: System.Void, IsImplicit) (Syntax: 'b') + Instance Receiver: + IFlowCaptureReferenceOperation: 0 (OperationKind.FlowCaptureReference, Type: T, IsImplicit) (Syntax: '[a, b]') + Arguments(1): + IArgumentOperation (ArgumentKind.Explicit, Matching Parameter: t) (OperationKind.Argument, Type: null, IsImplicit) (Syntax: 'b') + IParameterReferenceOperation: b (OperationKind.ParameterReference, Type: U) (Syntax: '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) + Next (Return) Block[B2] + IConversionOperation (TryCast: False, Unchecked) (OperationKind.Conversion, Type: T, IsImplicit) (Syntax: '[a, b]') + Conversion: CommonConversion (Exists: True, IsIdentity: False, IsNumeric: False, IsReference: False, IsUserDefined: False) (MethodSymbol: null) + (CollectionLiteral) + Operand: + IFlowCaptureReferenceOperation: 0 (OperationKind.FlowCaptureReference, Type: T, IsImplicit) (Syntax: '[a, b]') + Leaving: {R1} +} +Block[B2] - Exit + Predecessors: [B1] + Statements (0) +"); } [Fact] diff --git a/src/Compilers/Core/Portable/Generated/OperationKind.Generated.cs b/src/Compilers/Core/Portable/Generated/OperationKind.Generated.cs index 30fd6c7a5781e..5a68010063f9e 100644 --- a/src/Compilers/Core/Portable/Generated/OperationKind.Generated.cs +++ b/src/Compilers/Core/Portable/Generated/OperationKind.Generated.cs @@ -279,7 +279,5 @@ public enum OperationKind Utf8String = 0x7c, /// Indicates an . Attribute = 0x7d, - /// Indicates an . - CollectionLiteral = 0x7e, } } diff --git a/src/Compilers/Core/Portable/Generated/Operations.Generated.cs b/src/Compilers/Core/Portable/Generated/Operations.Generated.cs index aabc09cf4981a..249b1af87b2e4 100644 --- a/src/Compilers/Core/Portable/Generated/Operations.Generated.cs +++ b/src/Compilers/Core/Portable/Generated/Operations.Generated.cs @@ -3620,32 +3620,6 @@ public interface IAttributeOperation : IOperation /// IOperation Operation { get; } } - /// - /// Represents a collection literal. - /// - /// Current usage: - /// (1) C# collection literal. - /// - /// - /// - /// This node is associated with the following operation kinds: - /// - /// - /// - /// This interface is reserved for implementation by its associated APIs. We reserve the right to - /// change it in the future. - /// - public interface ICollectionLiteralOperation : IOperation - { - /// - /// Collection creation. - /// - IOperation CollectionCreation { get; } - /// - /// Values to initialize collection literal elements. - /// - ImmutableArray ElementValues { get; } - } #endregion #region Implementations @@ -10256,73 +10230,6 @@ internal override (bool hasNext, int nextSlot, int nextIndex) MoveNextReversed(i public override void Accept(OperationVisitor visitor) => visitor.VisitAttribute(this); public override TResult? Accept(OperationVisitor visitor, TArgument argument) where TResult : default => visitor.VisitAttribute(this, argument); } - internal sealed partial class CollectionLiteralOperation : Operation, ICollectionLiteralOperation - { - internal CollectionLiteralOperation(IOperation collectionCreation, ImmutableArray elementValues, SemanticModel? semanticModel, SyntaxNode syntax, ITypeSymbol? type, bool isImplicit) - : base(semanticModel, syntax, isImplicit) - { - CollectionCreation = SetParentOperation(collectionCreation, this); - ElementValues = SetParentOperation(elementValues, this); - Type = type; - } - public IOperation CollectionCreation { get; } - public ImmutableArray ElementValues { get; } - internal override int ChildOperationsCount => - (CollectionCreation is null ? 0 : 1) + - ElementValues.Length; - internal override IOperation GetCurrent(int slot, int index) - => slot switch - { - 0 when CollectionCreation != null - => CollectionCreation, - 1 when index < ElementValues.Length - => ElementValues[index], - _ => throw ExceptionUtilities.UnexpectedValue((slot, index)), - }; - internal override (bool hasNext, int nextSlot, int nextIndex) MoveNext(int previousSlot, int previousIndex) - { - switch (previousSlot) - { - case -1: - if (CollectionCreation != null) return (true, 0, 0); - else goto case 0; - case 0: - if (!ElementValues.IsEmpty) return (true, 1, 0); - else goto case 1; - case 1 when previousIndex + 1 < ElementValues.Length: - return (true, 1, previousIndex + 1); - case 1: - case 2: - return (false, 2, 0); - default: - throw ExceptionUtilities.UnexpectedValue((previousSlot, previousIndex)); - } - } - internal override (bool hasNext, int nextSlot, int nextIndex) MoveNextReversed(int previousSlot, int previousIndex) - { - switch (previousSlot) - { - case int.MaxValue: - if (!ElementValues.IsEmpty) return (true, 1, ElementValues.Length - 1); - else goto case 1; - case 1 when previousIndex > 0: - return (true, 1, previousIndex - 1); - case 1: - if (CollectionCreation != null) return (true, 0, 0); - else goto case 0; - case 0: - case -1: - return (false, -1, 0); - default: - throw ExceptionUtilities.UnexpectedValue((previousSlot, previousIndex)); - } - } - public override ITypeSymbol? Type { get; } - internal override ConstantValue? OperationConstantValue => null; - public override OperationKind Kind => OperationKind.CollectionLiteral; - public override void Accept(OperationVisitor visitor) => visitor.VisitCollectionLiteral(this); - public override TResult? Accept(OperationVisitor visitor, TArgument argument) where TResult : default => visitor.VisitCollectionLiteral(this, argument); - } #endregion #region Cloner internal sealed partial class OperationCloner : OperationVisitor @@ -10931,11 +10838,6 @@ public override IOperation VisitAttribute(IAttributeOperation operation, object? var internalOperation = (AttributeOperation)operation; return new AttributeOperation(Visit(internalOperation.Operation), internalOperation.OwningSemanticModel, internalOperation.Syntax, internalOperation.IsImplicit); } - public override IOperation VisitCollectionLiteral(ICollectionLiteralOperation operation, object? argument) - { - var internalOperation = (CollectionLiteralOperation)operation; - return new CollectionLiteralOperation(Visit(internalOperation.CollectionCreation), VisitArray(internalOperation.ElementValues), internalOperation.OwningSemanticModel, internalOperation.Syntax, internalOperation.Type, internalOperation.IsImplicit); - } } #endregion @@ -11075,7 +10977,6 @@ internal virtual void VisitNoneOperation(IOperation operation) { /* no-op */ } public virtual void VisitImplicitIndexerReference(IImplicitIndexerReferenceOperation operation) => DefaultVisit(operation); public virtual void VisitUtf8String(IUtf8StringOperation operation) => DefaultVisit(operation); public virtual void VisitAttribute(IAttributeOperation operation) => DefaultVisit(operation); - public virtual void VisitCollectionLiteral(ICollectionLiteralOperation operation) => DefaultVisit(operation); } public abstract partial class OperationVisitor { @@ -11212,7 +11113,6 @@ public abstract partial class OperationVisitor public virtual TResult? VisitImplicitIndexerReference(IImplicitIndexerReferenceOperation operation, TArgument argument) => DefaultVisit(operation, argument); public virtual TResult? VisitUtf8String(IUtf8StringOperation operation, TArgument argument) => DefaultVisit(operation, argument); public virtual TResult? VisitAttribute(IAttributeOperation operation, TArgument argument) => DefaultVisit(operation, argument); - public virtual TResult? VisitCollectionLiteral(ICollectionLiteralOperation operation, TArgument argument) => DefaultVisit(operation, argument); } #endregion } diff --git a/src/Compilers/Core/Portable/Operations/ControlFlowGraphBuilder.cs b/src/Compilers/Core/Portable/Operations/ControlFlowGraphBuilder.cs index 3ff57652d36da..3e6af546519de 100644 --- a/src/Compilers/Core/Portable/Operations/ControlFlowGraphBuilder.cs +++ b/src/Compilers/Core/Portable/Operations/ControlFlowGraphBuilder.cs @@ -6340,23 +6340,6 @@ IArrayInitializerOperation popAndAssembleArrayInitializerValues(IArrayInitialize } } - // PROTOTYPE: Add tests. - public override IOperation? VisitCollectionLiteral(ICollectionLiteralOperation operation, int? argument) - { - EvalStackFrame frame = PushStackFrame(); - var rewrittenCollectionCreation = Visit(operation.CollectionCreation); - Debug.Assert(rewrittenCollectionCreation is { }); - - var previousInstance = _currentImplicitInstance; - _currentImplicitInstance = new ImplicitInstanceInfo(rewrittenCollectionCreation); - ImmutableArray rewrittenElementValues = VisitArray(operation.ElementValues); - _currentImplicitInstance = previousInstance; - - var rewrittenCollectionLiteral = new CollectionLiteralOperation(rewrittenCollectionCreation, rewrittenElementValues, semanticModel: null, operation.Syntax, operation.Type, IsImplicit(operation)); - PopStackFrame(frame); - return rewrittenCollectionLiteral; - } - public override IOperation VisitInstanceReference(IInstanceReferenceOperation operation, int? captureIdForResult) { switch (operation.ReferenceKind) diff --git a/src/Compilers/Core/Portable/Operations/OperationInterfaces.xml b/src/Compilers/Core/Portable/Operations/OperationInterfaces.xml index 0aa1ffe98503f..3dea22c6f4959 100644 --- a/src/Compilers/Core/Portable/Operations/OperationInterfaces.xml +++ b/src/Compilers/Core/Portable/Operations/OperationInterfaces.xml @@ -3366,26 +3366,4 @@ - - - - - Represents a collection literal. - - Current usage: - (1) C# collection literal. - - - - - - Collection creation. - - - - - Values to initialize collection literal elements. - - - diff --git a/src/Compilers/Test/Core/Compilation/ControlFlowGraphVerifier.cs b/src/Compilers/Test/Core/Compilation/ControlFlowGraphVerifier.cs index 252ad782d5fd2..334629d357d4f 100644 --- a/src/Compilers/Test/Core/Compilation/ControlFlowGraphVerifier.cs +++ b/src/Compilers/Test/Core/Compilation/ControlFlowGraphVerifier.cs @@ -2001,7 +2001,6 @@ propertyReference.Parent is ISimpleAssignmentOperation simpleAssignment && case OperationKind.ListPattern: case OperationKind.ImplicitIndexerReference: case OperationKind.Attribute: - case OperationKind.CollectionLiteral: return true; } diff --git a/src/Compilers/Test/Core/Compilation/TestOperationVisitor.cs b/src/Compilers/Test/Core/Compilation/TestOperationVisitor.cs index b77b4d01848bd..72080aa86cdff 100644 --- a/src/Compilers/Test/Core/Compilation/TestOperationVisitor.cs +++ b/src/Compilers/Test/Core/Compilation/TestOperationVisitor.cs @@ -1135,18 +1135,6 @@ public override void VisitArrayInitializer(IArrayInitializerOperation operation) AssertEx.Equal(operation.ElementValues, operation.ChildOperations); } - public override void VisitCollectionLiteral(ICollectionLiteralOperation operation) - { - Assert.Equal(OperationKind.CollectionLiteral, operation.Kind); - Assert.NotNull(operation.Type); - var children = operation.ElementValues; - if (operation.CollectionCreation != null) - { - children = ImmutableArray.Create(operation.CollectionCreation).Concat(children); - } - AssertEx.Equal(children, operation.ChildOperations); - } - private void VisitAssignment(IAssignmentOperation operation) { AssertEx.Equal(new[] { operation.Target, operation.Value }, operation.ChildOperations); From 939e7dd66f3b307b76cd5ae2662af02a3ab20cb6 Mon Sep 17 00:00:00 2001 From: Charles Stoner <10732005+cston@users.noreply.github.com> Date: Sun, 5 Mar 2023 20:41:42 -0800 Subject: [PATCH 07/18] Update public API --- src/Compilers/Core/Portable/PublicAPI.Unshipped.txt | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt b/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt index 3f8fe76b199df..046cf4e840012 100644 --- a/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt +++ b/src/Compilers/Core/Portable/PublicAPI.Unshipped.txt @@ -1,16 +1,10 @@ Microsoft.CodeAnalysis.OperationKind.Attribute = 125 -> Microsoft.CodeAnalysis.OperationKind -Microsoft.CodeAnalysis.OperationKind.CollectionLiteral = 126 -> Microsoft.CodeAnalysis.OperationKind Microsoft.CodeAnalysis.Operations.IAttributeOperation Microsoft.CodeAnalysis.Operations.IAttributeOperation.Operation.get -> Microsoft.CodeAnalysis.IOperation! -Microsoft.CodeAnalysis.Operations.ICollectionLiteralOperation -Microsoft.CodeAnalysis.Operations.ICollectionLiteralOperation.CollectionCreation.get -> Microsoft.CodeAnalysis.IOperation! -Microsoft.CodeAnalysis.Operations.ICollectionLiteralOperation.ElementValues.get -> System.Collections.Immutable.ImmutableArray Microsoft.CodeAnalysis.SymbolDisplayLocalOptions.IncludeModifiers = 4 -> Microsoft.CodeAnalysis.SymbolDisplayLocalOptions Microsoft.CodeAnalysis.SymbolDisplayParameterOptions.IncludeModifiers = 2 -> Microsoft.CodeAnalysis.SymbolDisplayParameterOptions Microsoft.CodeAnalysis.SyntaxNode.ContainsDirective(int rawKind) -> bool static Microsoft.CodeAnalysis.FlowAnalysis.ControlFlowGraph.Create(Microsoft.CodeAnalysis.Operations.IAttributeOperation! attribute, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) -> Microsoft.CodeAnalysis.FlowAnalysis.ControlFlowGraph! static Microsoft.CodeAnalysis.Location.Create(string! filePath, Microsoft.CodeAnalysis.Text.TextSpan textSpan, Microsoft.CodeAnalysis.Text.LinePositionSpan lineSpan, string! mappedFilePath, Microsoft.CodeAnalysis.Text.LinePositionSpan mappedLineSpan) -> Microsoft.CodeAnalysis.Location! virtual Microsoft.CodeAnalysis.Operations.OperationVisitor.VisitAttribute(Microsoft.CodeAnalysis.Operations.IAttributeOperation! operation) -> void -virtual Microsoft.CodeAnalysis.Operations.OperationVisitor.VisitCollectionLiteral(Microsoft.CodeAnalysis.Operations.ICollectionLiteralOperation! operation) -> void virtual Microsoft.CodeAnalysis.Operations.OperationVisitor.VisitAttribute(Microsoft.CodeAnalysis.Operations.IAttributeOperation! operation, TArgument argument) -> TResult? -virtual Microsoft.CodeAnalysis.Operations.OperationVisitor.VisitCollectionLiteral(Microsoft.CodeAnalysis.Operations.ICollectionLiteralOperation! operation, TArgument argument) -> TResult? From b5743080350ee8b47b1f50c7426ebe9b29876a00 Mon Sep 17 00:00:00 2001 From: Charles Stoner <10732005+cston@users.noreply.github.com> Date: Sun, 5 Mar 2023 21:21:43 -0800 Subject: [PATCH 08/18] Support BoundUnconvertedCollectionLiteralExpression --- .../Operations/CSharpOperationFactory.cs | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory.cs b/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory.cs index f8a12e288aeca..778c444ca43f0 100644 --- a/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory.cs +++ b/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory.cs @@ -104,6 +104,8 @@ public CSharpOperationFactory(SemanticModel semanticModel) return CreateBoundCollectionInitializerCollectionLiteralExpression((BoundCollectionInitializerCollectionLiteralExpression)boundNode); case BoundKind.ArrayOrSpanCollectionLiteralExpression: return CreateBoundArrayOrSpanCollectionLiteralExpression((BoundArrayOrSpanCollectionLiteralExpression)boundNode); + case BoundKind.UnconvertedCollectionLiteralExpression: + return CreateBoundUnconvertedCollectionLiteralExpression((BoundUnconvertedCollectionLiteralExpression)boundNode); case BoundKind.DefaultLiteral: return CreateBoundDefaultLiteralOperation((BoundDefaultLiteral)boundNode); case BoundKind.DefaultExpression: @@ -304,7 +306,6 @@ public CSharpOperationFactory(SemanticModel semanticModel) case BoundKind.StackAllocArrayCreation: case BoundKind.TypeExpression: case BoundKind.TypeOrValueExpression: - case BoundKind.UnconvertedCollectionLiteralExpression: // PROTOTYPE: Is this correct? ConstantValue? constantValue = (boundNode as BoundExpression)?.ConstantValueOpt; bool isImplicit = boundNode.WasCompilerGenerated; @@ -1293,11 +1294,10 @@ private IOperation CreateBoundCollectionInitializerCollectionLiteralExpression(B } else if (boundCollectionLiteralExpression.CollectionCreation is BoundObjectCreationExpression collectionCreation) { - Debug.Assert(collectionCreation.Arguments.IsEmpty); return new ObjectCreationOperation( collectionCreation.Constructor.GetPublicSymbol(), initializer, - arguments: ImmutableArray.Empty, + DeriveArguments(collectionCreation), _semanticModel, syntax, collectionType, @@ -1307,10 +1307,19 @@ private IOperation CreateBoundCollectionInitializerCollectionLiteralExpression(B else { // PROTOTYPE: Temporary until natural type is supported. - return new NoneOperation(ImmutableArray.Empty, _semanticModel, syntax, type: collectionType, constantValue: null, isImplicit: isImplicit); + return new InvalidOperation(ImmutableArray.Create(initializer), _semanticModel, syntax, collectionType, constantValue: null, isImplicit); } } + private IOperation CreateBoundUnconvertedCollectionLiteralExpression(BoundUnconvertedCollectionLiteralExpression boundCollectionLiteralExpression) + { + SyntaxNode syntax = boundCollectionLiteralExpression.Syntax; + ITypeSymbol? collectionType = boundCollectionLiteralExpression.GetPublicTypeSymbol(); + bool isImplicit = boundCollectionLiteralExpression.WasCompilerGenerated; + var children = CreateFromArray(boundCollectionLiteralExpression.Initializers); + return new InvalidOperation(children, _semanticModel, syntax, collectionType, constantValue: null, isImplicit); + } + private IDefaultValueOperation CreateBoundDefaultLiteralOperation(BoundDefaultLiteral boundDefaultLiteral) { SyntaxNode syntax = boundDefaultLiteral.Syntax; From eb41971d636476905ca239301324b5e907c863f2 Mon Sep 17 00:00:00 2001 From: Charles Stoner <10732005+cston@users.noreply.github.com> Date: Mon, 6 Mar 2023 09:49:07 -0800 Subject: [PATCH 09/18] Update ControlFlowGraphVerifier --- .../Semantics/CollectionLiteralTests.cs | 141 ++++++++++++++++++ .../Compilation/ControlFlowGraphVerifier.cs | 6 + 2 files changed, 147 insertions(+) diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/CollectionLiteralTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/CollectionLiteralTests.cs index 5d8785442233c..0c28d090d29e3 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/CollectionLiteralTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/CollectionLiteralTests.cs @@ -2087,6 +2087,147 @@ class Program "); } + [Fact] + public void IOperation_Nested() + { + string source = """ + using System.Collections.Generic; + class Program + { + static void Main() + { + List> x = /**/[[Get(1)]]/**/; + } + static int Get(int value) => value; + } + """; + + var comp = CreateCompilation(source); + comp.VerifyDiagnostics(); + + VerifyOperationTreeForTest(comp, +@"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.Collections.Generic.List 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)]') + 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) + 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 tree = comp.SyntaxTrees[0]; + var method = tree.GetRoot().DescendantNodes().OfType().Single(m => m.Identifier.Text == "Main"); + VerifyFlowGraph(comp, method, +@"Block[B0] - Entry + Statements (0) + Next (Regular) Block[B1] + Entering: {R1} +.locals {R1} +{ + Locals: [System.Collections.Generic.List> x] + CaptureIds: [0] + Block[B1] - Block + Predecessors: [B0] + Statements (1) + IFlowCaptureOperation: 0 (OperationKind.FlowCapture, Type: null, IsImplicit) (Syntax: '[[Get(1)]]') + Value: + IObjectCreationOperation (Constructor: System.Collections.Generic.List>..ctor()) (OperationKind.ObjectCreation, Type: System.Collections.Generic.List>) (Syntax: '[[Get(1)]]') + Arguments(0) + Initializer: + null + Next (Regular) Block[B2] + Entering: {R2} + .locals {R2} + { + CaptureIds: [1] + Block[B2] - Block + Predecessors: [B1] + Statements (3) + IFlowCaptureOperation: 1 (OperationKind.FlowCapture, Type: null, IsImplicit) (Syntax: '[Get(1)]') + Value: + IObjectCreationOperation (Constructor: System.Collections.Generic.List..ctor()) (OperationKind.ObjectCreation, Type: System.Collections.Generic.List) (Syntax: '[Get(1)]') + Arguments(0) + Initializer: + null + IInvocationOperation ( void System.Collections.Generic.List.Add(System.Int32 item)) (OperationKind.Invocation, Type: System.Void, IsImplicit) (Syntax: 'Get(1)') + Instance Receiver: + IFlowCaptureReferenceOperation: 1 (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)') + 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) + IInvocationOperation ( void System.Collections.Generic.List>.Add(System.Collections.Generic.List item)) (OperationKind.Invocation, Type: System.Void, IsImplicit) (Syntax: '[Get(1)]') + Instance Receiver: + 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)]') + 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] + Leaving: {R2} + } + Block[B3] - Block + Predecessors: [B2] + Statements (1) + ISimpleAssignmentOperation (OperationKind.SimpleAssignment, Type: System.Collections.Generic.List>, IsImplicit) (Syntax: 'x = /**/[[Get(1)]]') + 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)]]') + Next (Regular) Block[B4] + Leaving: {R1} +} +Block[B4] - Exit + Predecessors: [B3] + Statements (0) +"); + } + [Fact] public void Async() { diff --git a/src/Compilers/Test/Core/Compilation/ControlFlowGraphVerifier.cs b/src/Compilers/Test/Core/Compilation/ControlFlowGraphVerifier.cs index 334629d357d4f..e285004ada6e6 100644 --- a/src/Compilers/Test/Core/Compilation/ControlFlowGraphVerifier.cs +++ b/src/Compilers/Test/Core/Compilation/ControlFlowGraphVerifier.cs @@ -1143,6 +1143,12 @@ bool isLongLivedCaptureReferenceSyntax(SyntaxNode captureReferenceSyntax) return true; } break; + case CSharp.SyntaxKind.CollectionCreationExpression: + if (((CSharp.Syntax.CollectionCreationExpressionSyntax)syntax).Elements.Any()) + { + return true; + } + break; } if (syntax.Parent is CSharp.Syntax.WithExpressionSyntax withExpr From 85f1334070c1856e9cfcb82dbb63a9f159e0da5b Mon Sep 17 00:00:00 2001 From: Charles Stoner <10732005+cston@users.noreply.github.com> Date: Mon, 6 Mar 2023 13:49:10 -0800 Subject: [PATCH 10/18] Update test --- .../IOperationTests_IArrayElementReferenceExpression.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_IArrayElementReferenceExpression.cs b/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_IArrayElementReferenceExpression.cs index 8400d6a7bf11c..69f8647de670f 100644 --- a/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_IArrayElementReferenceExpression.cs +++ b/src/Compilers/CSharp/Test/IOperation/IOperation/IOperationTests_IArrayElementReferenceExpression.cs @@ -519,7 +519,9 @@ public void F() "; string expectedOperationTree = @" -IOperation: (OperationKind.None, Type: null, IsInvalid) (Syntax: '[0]') +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[] { From ced04c376c87b2a8a4dedadd58a46c415811c6cd Mon Sep 17 00:00:00 2001 From: Charles Stoner <10732005+cston@users.noreply.github.com> Date: Mon, 6 Mar 2023 15:16:57 -0800 Subject: [PATCH 11/18] More tests --- .../Portable/Binder/Binder_Conversions.cs | 2 +- .../Semantics/CollectionLiteralTests.cs | 203 +++++++++++++++++- 2 files changed, 203 insertions(+), 2 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Conversions.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Conversions.cs index 42ee9857be60c..675e419a64602 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Conversions.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Conversions.cs @@ -558,7 +558,7 @@ bool collectionTypeImplementsIEnumerable(TypeSymbol targetType, CSharpSyntaxNode // This implementation differs from CollectionInitializerTypeImplementsIEnumerable(). // That method checks for an implicit conversion from IEnumerable to the collection type, // but that would allow: Nullable s = []; - var ienumerableType = GetSpecialType(SpecialType.System_Collections_IEnumerable, diagnostics, syntax); // PROTOTYPE: Test missing type. + var ienumerableType = GetSpecialType(SpecialType.System_Collections_IEnumerable, diagnostics, syntax); var allInterfaces = targetType is TypeParameterSymbol typeParameter ? typeParameter.AllEffectiveInterfacesNoUseSiteDiagnostics : targetType.AllInterfacesNoUseSiteDiagnostics; diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/CollectionLiteralTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/CollectionLiteralTests.cs index 0c28d090d29e3..7dcb0e7a47d11 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/CollectionLiteralTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/CollectionLiteralTests.cs @@ -1227,6 +1227,175 @@ static void Main() 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 _list = new List(); + 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, GetCollectionExtensions() }, expectedOutput: "[], [i=1, o=2], "); + } + + [Fact] + public void CollectionInitializerType_13() + { + 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_14() + { + string source = """ + using System.Collections; + struct S : IEnumerable + { + public void Add(T x, T y) { } + IEnumerator IEnumerable.GetEnumerator() => throw null; + } + class Program + { + static void Main() + { + S s; + s = []; + s = [1, 2]; + } + } + """; + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics( + // (13,14): error CS7036: There is no argument given that corresponds to the required parameter 'y' of 'S.Add(int, int)' + // s = [1, 2]; + Diagnostic(ErrorCode.ERR_NoCorrespondingArgument, "1").WithArguments("y", "S.Add(int, int)").WithLocation(13, 14), + // (13,17): error CS7036: There is no argument given that corresponds to the required parameter 'y' of 'S.Add(int, int)' + // s = [1, 2]; + Diagnostic(ErrorCode.ERR_NoCorrespondingArgument, "2").WithArguments("y", "S.Add(int, int)").WithLocation(13, 17)); + } + + [Fact] + public void CollectionInitializerType_15() + { + string source = """ + using System.Collections; + using System.Collections.Generic; + class C : IEnumerable + { + List _list = new List(); + public void Add(T t, int index = -1) { _list.Add(t); } + IEnumerator IEnumerable.GetEnumerator() => _list.GetEnumerator(); + } + class Program + { + static void Main() + { + C c = [1, 2]; + c.Report(); + } + } + """; + CompileAndVerify(new[] { source, GetCollectionExtensions() }, expectedOutput: "[1, 2], "); + } + + [Fact] + public void CollectionInitializerType_16() + { + string source = """ + using System.Collections; + using System.Collections.Generic; + class C1 : IEnumerable + { + List _list = new List(); + public void Add(T t, params T[] args) { _list.Add(t); } + IEnumerator IEnumerable.GetEnumerator() => _list.GetEnumerator(); + } + class C2 : IEnumerable + { + List _list = new List(); + public void Add(params T[] args) { _list.Add(args[0]); } + IEnumerator IEnumerable.GetEnumerator() => _list.GetEnumerator(); + } + class Program + { + static void Main() + { + C1 x = [1, 2]; + C2 y = [3, 4]; + x.Report(); + y.Report(); + } + } + """; + CompileAndVerify(new[] { source, GetCollectionExtensions() }, expectedOutput: "[1, 2], [3, 4], "); + } + + [Fact] + public void CollectionInitializerType_18() + { + string source = """ + using System.Collections; + class S : IEnumerable + { + internal void Add(T t) { } + private void Add(U u) { } + IEnumerator IEnumerable.GetEnumerator() => throw null; + static S Create(T t, U u) => [t, u]; + } + class Program + { + static S Create(T x, U y) => [x, y]; + } + """; + var comp = CreateCompilation(source); + comp.VerifyEmitDiagnostics( + // (11,50): error CS1950: The best overloaded Add method 'S.Add(T)' for the collection initializer has some invalid arguments + // static S Create(T x, U y) => [x, y]; + Diagnostic(ErrorCode.ERR_BadArgTypesForCollectionAdd, "y").WithArguments("S.Add(T)").WithLocation(11, 50), + // (11,50): error CS1503: Argument 1: cannot convert from 'U' to 'T' + // static S Create(T x, U y) => [x, y]; + Diagnostic(ErrorCode.ERR_BadArgType, "y").WithArguments("1", "U", "T").WithLocation(11, 50)); + } + [Theory] [InlineData("class")] [InlineData("struct")] @@ -1329,7 +1498,39 @@ class Program Diagnostic(ErrorCode.ERR_CollectionLiteralTargetTypeNotConstructible, "[]").WithArguments("T?").WithLocation(16, 57)); } - // PROTOTYPE: Test variations of Add() methods - inaccessible, conversion errors, use-site errors, optional parameters, params array, etc. + [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 CS0518: Predefined type 'System.Collections.IEnumerable' is not defined or imported + // S s = []; + Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, "[]").WithArguments("System.Collections.IEnumerable").WithLocation(8, 15), + // (8,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(8, 15), + // (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. + // object o = (S)[1, 2]; + Diagnostic(ErrorCode.ERR_CollectionLiteralTargetTypeNotConstructible, "[1, 2]").WithArguments("S").WithLocation(9, 23)); + } [Fact] public void DictionaryElement_01() From c6ac59019e49d55441c33b3865bdb9970e7a9895 Mon Sep 17 00:00:00 2001 From: Charles Stoner <10732005+cston@users.noreply.github.com> Date: Mon, 6 Mar 2023 16:50:26 -0800 Subject: [PATCH 12/18] Add test --- .../Semantics/CollectionLiteralTests.cs | 69 ++++++++++++++++++- 1 file changed, 67 insertions(+), 2 deletions(-) diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/CollectionLiteralTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/CollectionLiteralTests.cs index 7dcb0e7a47d11..0f166e6afb792 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/CollectionLiteralTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/CollectionLiteralTests.cs @@ -996,8 +996,6 @@ static void Main() CompileAndVerify(new[] { source, GetCollectionExtensions() }, expectedOutput: "[], [], [1, 2], [3, 4], "); } - // PROTOTYPE: Test constructor use-site error. - [Fact] public void CollectionInitializerType_07() { @@ -1532,6 +1530,73 @@ static void Main() Diagnostic(ErrorCode.ERR_CollectionLiteralTargetTypeNotConstructible, "[1, 2]").WithArguments("S").WithLocation(9, 23)); } + [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 + { + List _list = new List(); + public B1(A1 a = null) { } + public void Add(int i) { _list.Add(i); } + IEnumerator IEnumerable.GetEnumerator() => _list.GetEnumerator(); + } + public class B2 : IEnumerable + { + List _list = new List(); + public void Add(int x, A2 y = null) { _list.Add(x); } + 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, GetCollectionExtensions() }, references: new[] { refA, refB }, expectedOutput: "[], [1, 2], [], [3, 4], "); + + comp = CreateCompilation(new[] { sourceC, GetCollectionExtensions() }, 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 = []; + Diagnostic(ErrorCode.ERR_NoTypeDef, "[]").WithArguments("A1", $"{assemblyA}, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null").WithLocation(6, 13), + // (8,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 = [1, 2]; + Diagnostic(ErrorCode.ERR_NoTypeDef, "[1, 2]").WithArguments("A1", $"{assemblyA}, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null").WithLocation(8, 13), + // (13,14): error CS0012: The type 'A2' 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'. + // y = [3, 4]; + Diagnostic(ErrorCode.ERR_NoTypeDef, "3").WithArguments("A2", $"{assemblyA}, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null").WithLocation(13, 14), + // (13,17): error CS0012: The type 'A2' 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'. + // y = [3, 4]; + Diagnostic(ErrorCode.ERR_NoTypeDef, "4").WithArguments("A2", $"{assemblyA}, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null").WithLocation(13, 17)); + } + [Fact] public void DictionaryElement_01() { From 489ff8d990534733798a98abef128b6c26c58dbd Mon Sep 17 00:00:00 2001 From: Charles Stoner <10732005+cston@users.noreply.github.com> Date: Mon, 6 Mar 2023 20:28:17 -0800 Subject: [PATCH 13/18] Update tests --- .../Compilation/CSharpSemanticModel.cs | 2 +- .../Semantics/CollectionLiteralTests.cs | 70 ++++++++++++++++--- 2 files changed, 62 insertions(+), 10 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Compilation/CSharpSemanticModel.cs b/src/Compilers/CSharp/Portable/Compilation/CSharpSemanticModel.cs index 29bf2a8e0e388..836737515fb68 100644 --- a/src/Compilers/CSharp/Portable/Compilation/CSharpSemanticModel.cs +++ b/src/Compilers/CSharp/Portable/Compilation/CSharpSemanticModel.cs @@ -2195,7 +2195,7 @@ internal CSharpTypeInfo GetTypeInfoForNode( } else { - Debug.Assert(false); // PROTOTYPE: Are we hitting this path? + Debug.Assert(false); // PROTOTYPE: Add test. // There was an explicit cast on top of this type = convertedCollection.NaturalTypeOpt; (convertedType, convertedNullability) = (type, nullability); diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/CollectionLiteralTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/CollectionLiteralTests.cs index 0f166e6afb792..0ff4586a62828 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/CollectionLiteralTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/CollectionLiteralTests.cs @@ -1341,30 +1341,82 @@ public void CollectionInitializerType_16() string source = """ using System.Collections; using System.Collections.Generic; - class C1 : IEnumerable + class C : IEnumerable { List _list = new List(); public void Add(T t, params T[] args) { _list.Add(t); } IEnumerator IEnumerable.GetEnumerator() => _list.GetEnumerator(); } - class C2 : IEnumerable + class Program + { + static void Main() + { + C c = [1, 2]; + c.Report(); + } + } + """; + CompileAndVerify(new[] { source, GetCollectionExtensions() }, expectedOutput: "[1, 2], "); + } + + [Fact] + public void CollectionInitializerType_17() + { + string source = """ + using System.Collections; + using System.Collections.Generic; + class C : IEnumerable { List _list = new List(); - public void Add(params T[] args) { _list.Add(args[0]); } + public void Add(params T[] args) { _list.AddRange(args); } IEnumerator IEnumerable.GetEnumerator() => _list.GetEnumerator(); } class Program { static void Main() { - C1 x = [1, 2]; - C2 y = [3, 4]; - x.Report(); - y.Report(); + C c = [[], [1, 2], 3]; + c.Report(); } } """; - CompileAndVerify(new[] { source, GetCollectionExtensions() }, expectedOutput: "[1, 2], [3, 4], "); + var verifier = CompileAndVerify(new[] { source, GetCollectionExtensions() }, expectedOutput: "[1, 2, 3], "); + verifier.VerifyIL("Program.Main", """ + { + // Code size 61 (0x3d) + .maxstack 5 + .locals init (C V_0) + IL_0000: newobj "C..ctor()" + IL_0005: stloc.0 + IL_0006: ldloc.0 + IL_0007: ldc.i4.0 + IL_0008: newarr "int" + IL_000d: callvirt "void C.Add(params int[])" + IL_0012: ldloc.0 + IL_0013: ldc.i4.2 + IL_0014: newarr "int" + IL_0019: dup + IL_001a: ldc.i4.0 + IL_001b: ldc.i4.1 + IL_001c: stelem.i4 + IL_001d: dup + IL_001e: ldc.i4.1 + IL_001f: ldc.i4.2 + IL_0020: stelem.i4 + IL_0021: callvirt "void C.Add(params int[])" + IL_0026: ldloc.0 + IL_0027: ldc.i4.1 + IL_0028: newarr "int" + IL_002d: dup + IL_002e: ldc.i4.0 + IL_002f: ldc.i4.3 + 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 + } + """); } [Fact] @@ -1670,7 +1722,7 @@ static void Main() } """; var comp = CreateCompilation(source); - // PROTOTYPE: Should // 2 be reported as an error? Do we report this for the array initializer case? + // PROTOTYPE: // 2 should be reported as an error (compare with array initializer: new object[] { null }). comp.VerifyEmitDiagnostics( // (7,9): warning CS8602: Dereference of a possibly null reference. // x[0].ToString(); // 1 From e29e59e876b8c2baf471c8794a4ad483b610c01d Mon Sep 17 00:00:00 2001 From: Charles Stoner <10732005+cston@users.noreply.github.com> Date: Wed, 8 Mar 2023 11:28:02 -0800 Subject: [PATCH 14/18] Add tests and comment --- .../Operations/CSharpOperationFactory.cs | 1 + .../Semantics/CollectionLiteralTests.cs | 39 ++++++++++++++++++- 2 files changed, 38 insertions(+), 2 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory.cs b/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory.cs index 778c444ca43f0..b9a19cb59257b 100644 --- a/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory.cs +++ b/src/Compilers/CSharp/Portable/Operations/CSharpOperationFactory.cs @@ -1220,6 +1220,7 @@ private IArrayInitializerOperation CreateBoundArrayInitializationOperation(Bound return new ArrayInitializerOperation(elementValues, _semanticModel, syntax, isImplicit); } + // PROTOTYPE: Decide public API shape. private IOperation CreateBoundArrayOrSpanCollectionLiteralExpression(BoundArrayOrSpanCollectionLiteralExpression boundCollectionLiteralExpression) { SyntaxNode syntax = boundCollectionLiteralExpression.Syntax; diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/CollectionLiteralTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/CollectionLiteralTests.cs index 0ff4586a62828..fcde312d921cf 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/CollectionLiteralTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/CollectionLiteralTests.cs @@ -1892,7 +1892,7 @@ Add C`1[System.Int32] // Ensure collection literal conversions are not standard implicit conversions // and, as a result, are ignored when determining user-defined conversions. [Fact] - public void UserDefinedConversion() + public void UserDefinedConversions_01() { string source = """ struct S @@ -1905,6 +1905,7 @@ static void Main() { S s = []; s = [1, 2]; + s = (S)[3, 4]; } } """; @@ -1915,7 +1916,41 @@ static void Main() 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. // s = [1, 2]; - Diagnostic(ErrorCode.ERR_CollectionLiteralTargetTypeNotConstructible, "[1, 2]").WithArguments("S").WithLocation(10, 13)); + 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. + // s = (S)[3, 4]; + Diagnostic(ErrorCode.ERR_CollectionLiteralTargetTypeNotConstructible, "[3, 4]").WithArguments("S").WithLocation(11, 16)); + } + + [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 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. + // 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. + // s = (S)[3, 4]; + Diagnostic(ErrorCode.ERR_CollectionLiteralTargetTypeNotConstructible, "[3, 4]").WithArguments("S").WithLocation(11, 16)); } [ConditionalFact(typeof(CoreClrOnly))] From 1ac5058d0e18494051840a6b8ab62399c784946b Mon Sep 17 00:00:00 2001 From: Charles Stoner <10732005+cston@users.noreply.github.com> Date: Sat, 11 Mar 2023 14:46:52 -0800 Subject: [PATCH 15/18] Respond to feedback --- .../Portable/Binder/Binder_Conversions.cs | 27 +- .../Portable/Binder/Binder_Expressions.cs | 10 +- .../Semantics/Conversions/Conversion.cs | 2 +- .../CSharp/Portable/BoundTree/BoundNodes.xml | 3 +- .../Portable/FlowAnalysis/AbstractFlowPass.cs | 1 + .../Generated/BoundNodes.xml.Generated.cs | 11 +- .../LocalRewriter_CollectionLiteral.cs | 33 +- .../Portable/Symbols/ErrorTypeSymbol.cs | 2 +- .../Portable/Symbols/NamedTypeSymbol.cs | 2 +- .../Semantics/CollectionLiteralTests.cs | 315 +++++++++++++++++- .../Portable/Symbols/MethodSymbol.vb | 2 +- .../Portable/Symbols/NamedTypeSymbol.vb | 2 +- 12 files changed, 354 insertions(+), 56 deletions(-) diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Conversions.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Conversions.cs index 675e419a64602..9f14c4403fc8e 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Conversions.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Conversions.cs @@ -465,10 +465,10 @@ private BoundExpression ConvertCollectionLiteralExpression( bool isSpanType(TypeSymbol targetType, WellKnownType spanType, [NotNullWhen(true)] out TypeSymbol? elementType) { - if (targetType is NamedTypeSymbol { TypeArgumentsWithAnnotationsNoUseSiteDiagnostics: [var typeArgument] } namedType + if (targetType is NamedTypeSymbol { Arity: 1 } namedType && TypeSymbol.Equals(namedType.OriginalDefinition, Compilation.GetWellKnownType(spanType), TypeCompareKind.AllIgnoreOptions)) { - elementType = typeArgument.Type; + elementType = namedType.TypeArgumentsWithAnnotationsNoUseSiteDiagnostics[0].Type; return true; } elementType = null; @@ -491,8 +491,8 @@ BoundArrayOrSpanCollectionLiteralExpression bindArrayOrSpan( return new BoundArrayOrSpanCollectionLiteralExpression( syntax, spanConstructor, - naturalTypeOpt: null, // PROTOTYPE: Support natural type. - wasTargetTyped: true, // PROTOTYPE: Support natural type. + naturalTypeOpt: null, + wasTargetTyped: true, implicitReceiver, builder.ToImmutableAndFree(), targetType) @@ -505,7 +505,7 @@ BoundCollectionInitializerCollectionLiteralExpression bindCollectionInitializer( ImmutableArray elements, BindingDiagnosticBag diagnostics) { - BoundExpression? collectionCreation = null; + BoundExpression collectionCreation; if (targetType is NamedTypeSymbol namedType) { var analyzedArguments = AnalyzedArguments.GetInstance(); @@ -513,12 +513,19 @@ BoundCollectionInitializerCollectionLiteralExpression bindCollectionInitializer( 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 - && !syntax.HasErrors - && !targetType.IsErrorType() - && collectionCreation?.HasErrors != true) + if (!hasEnumerableInitializerType && !targetType.IsErrorType()) { Error(diagnostics, ErrorCode.ERR_CollectionLiteralTargetTypeNotConstructible, syntax, targetType); } @@ -562,7 +569,7 @@ bool collectionTypeImplementsIEnumerable(TypeSymbol targetType, CSharpSyntaxNode var allInterfaces = targetType is TypeParameterSymbol typeParameter ? typeParameter.AllEffectiveInterfacesNoUseSiteDiagnostics : targetType.AllInterfacesNoUseSiteDiagnostics; - return allInterfaces.Any(t => ienumerableType.Equals(t, TypeCompareKind.AllIgnoreOptions)); + return allInterfaces.Any(static (a, b) => a.Equals(b, TypeCompareKind.AllIgnoreOptions), ienumerableType); } BoundExpression convertArrayElement(BoundExpression element, TypeSymbol elementType, BindingDiagnosticBag diagnostics) diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs index 174406dab0a87..300710625d2c0 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs @@ -755,7 +755,7 @@ private BoundExpression BindExpressionInternal(ExpressionSyntax node, BindingDia return BadExpression(node); case SyntaxKind.CollectionCreationExpression: - return BindCollectionCreationExpression((CollectionCreationExpressionSyntax)node, diagnostics); + return BindCollectionLiteralExpression((CollectionCreationExpressionSyntax)node, diagnostics); case SyntaxKind.NullableType: // Not reachable during method body binding, but @@ -4341,7 +4341,7 @@ BoundExpression bindObjectCreationExpression(ObjectCreationExpressionSyntax node } } - private BoundExpression BindCollectionCreationExpression(CollectionCreationExpressionSyntax syntax, BindingDiagnosticBag diagnostics) + private BoundExpression BindCollectionLiteralExpression(CollectionCreationExpressionSyntax syntax, BindingDiagnosticBag diagnostics) { MessageID.IDS_FeatureCollectionLiterals.CheckFeatureAvailability(diagnostics, syntax, syntax.OpenBracketToken.GetLocation()); @@ -4650,7 +4650,6 @@ private BoundExpression MakeBadExpressionForObjectCreation(SyntaxNode node, Type return new BoundBadExpression(node, LookupResultKind.NotCreatable, ImmutableArray.Create(type), children.ToImmutableAndFree(), type) { WasCompilerGenerated = wasCompilerGenerated }; } -#nullable disable private BoundObjectInitializerExpressionBase BindInitializerExpression( InitializerExpressionSyntax syntax, @@ -4682,6 +4681,7 @@ private BoundObjectInitializerExpressionBase BindInitializerExpression( throw ExceptionUtilities.Unreachable(); } } +#nullable disable private BoundExpression BindInitializerExpressionOrValue( ExpressionSyntax syntax, @@ -5865,8 +5865,9 @@ private BoundExpression BindTypeParameterCreationExpression(ObjectCreationExpres return result; } +#nullable enable private BoundExpression BindTypeParameterCreationExpression( - SyntaxNode node, TypeParameterSymbol typeParameter, AnalyzedArguments analyzedArguments, InitializerExpressionSyntax initializerOpt, + SyntaxNode node, TypeParameterSymbol typeParameter, AnalyzedArguments analyzedArguments, InitializerExpressionSyntax? initializerOpt, SyntaxNode typeSyntax, bool wasTargetTyped, BindingDiagnosticBag diagnostics) { if (!typeParameter.HasConstructorConstraint && !typeParameter.IsValueType) @@ -5892,6 +5893,7 @@ private BoundExpression BindTypeParameterCreationExpression( return MakeBadExpressionForObjectCreation(node, typeParameter, analyzedArguments, initializerOpt, typeSyntax, diagnostics); } +#nullable disable /// /// Given the type containing constructors, gets the list of candidate instance constructors and uses overload resolution to determine which one should be called. diff --git a/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/Conversion.cs b/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/Conversion.cs index f5061030c1c1e..9261886f7c650 100644 --- a/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/Conversion.cs +++ b/src/Compilers/CSharp/Portable/Binder/Semantics/Conversions/Conversion.cs @@ -633,7 +633,7 @@ public bool IsObjectCreation } /// - /// Returns true if the conversion is an implicit collection creation expression conversion. + /// Returns true if the conversion is an implicit collection literal expression conversion. /// public bool IsCollectionLiteral => Kind == ConversionKind.CollectionLiteral; diff --git a/src/Compilers/CSharp/Portable/BoundTree/BoundNodes.xml b/src/Compilers/CSharp/Portable/BoundTree/BoundNodes.xml index 6c9a8eaa13d47..beaa4c265abad 100644 --- a/src/Compilers/CSharp/Portable/BoundTree/BoundNodes.xml +++ b/src/Compilers/CSharp/Portable/BoundTree/BoundNodes.xml @@ -1860,8 +1860,7 @@ - - +