Skip to content

Commit

Permalink
Collection expressions: require applicable constructor and Add method…
Browse files Browse the repository at this point in the history
… for target type implementing IEnumerable
  • Loading branch information
cston committed Jan 12, 2024
1 parent 1754846 commit ec6908b
Show file tree
Hide file tree
Showing 6 changed files with 989 additions and 521 deletions.
96 changes: 71 additions & 25 deletions src/Compilers/CSharp/Portable/Binder/Binder_Conversions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -654,35 +654,25 @@ private BoundCollectionExpression ConvertCollectionExpression(
if (collectionTypeKind is CollectionExpressionTypeKind.ImplementsIEnumerable)
{
implicitReceiver = new BoundObjectOrCollectionValuePlaceholder(syntax, isNewInstance: true, targetType) { WasCompilerGenerated = true };
if (targetType is NamedTypeSymbol namedType)
{
var analyzedArguments = AnalyzedArguments.GetInstance();
// https://github.com/dotnet/roslyn/issues/68785: Use ctor with `int capacity` when the size is known.
collectionCreation = BindClassCreationExpression(syntax, namedType.Name, syntax, namedType, analyzedArguments, diagnostics);
collectionCreation.WasCompilerGenerated = true;
analyzedArguments.Free();
}
else if (targetType is TypeParameterSymbol typeParameter)
{
var analyzedArguments = AnalyzedArguments.GetInstance();
collectionCreation = BindTypeParameterCreationExpression(syntax, typeParameter, analyzedArguments, initializerOpt: null, typeSyntax: syntax, wasTargetTyped: true, diagnostics);
collectionCreation.WasCompilerGenerated = true;
analyzedArguments.Free();
}
else
{
collectionCreation = new BoundBadExpression(syntax, LookupResultKind.NotCreatable, ImmutableArray<Symbol?>.Empty, ImmutableArray<BoundExpression>.Empty, targetType);
}
collectionCreation = BindCollectionExpressionConstructor(syntax, targetType, diagnostics);

var collectionInitializerAddMethodBinder = this.WithAdditionalFlags(BinderFlags.CollectionInitializerAddMethod);
foreach (var element in elements)
{
BoundNode convertedElement = BindCollectionExpressionElementAddMethod(
element,
collectionInitializerAddMethodBinder,
implicitReceiver,
diagnostics,
out _);
BoundNode convertedElement = element is BoundCollectionExpressionSpreadElement spreadElement ?
(BoundNode)BindCollectionExpressionSpreadElementAddMethod(
(SpreadElementSyntax)spreadElement.Syntax,
spreadElement,
collectionInitializerAddMethodBinder,
implicitReceiver,
diagnostics) :
BindCollectionInitializerElementAddMethod(
(ExpressionSyntax)element.Syntax,
ImmutableArray.Create((BoundExpression)element),
hasEnumerableInitializerType: true,
collectionInitializerAddMethodBinder,
diagnostics,
implicitReceiver);
builder.Add(convertedElement);
}
}
Expand Down Expand Up @@ -766,6 +756,49 @@ BoundNode bindSpreadElement(BoundCollectionExpressionSpreadElement element, Type
}
}

internal BoundExpression BindCollectionExpressionConstructor(SyntaxNode syntax, TypeSymbol targetType, BindingDiagnosticBag diagnostics)
{
BoundExpression collectionCreation;
var analyzedArguments = AnalyzedArguments.GetInstance();
if (targetType is NamedTypeSymbol namedType)
{
collectionCreation = BindClassCreationExpression(syntax, namedType.Name, syntax, namedType, analyzedArguments, diagnostics);
collectionCreation.WasCompilerGenerated = true;
}
else if (targetType is TypeParameterSymbol typeParameter)
{
collectionCreation = BindTypeParameterCreationExpression(syntax, typeParameter, analyzedArguments, initializerOpt: null, typeSyntax: syntax, wasTargetTyped: true, diagnostics);
collectionCreation.WasCompilerGenerated = true;
}
else
{
throw ExceptionUtilities.UnexpectedValue(targetType);
}
analyzedArguments.Free();
return collectionCreation;
}

internal bool HasCollectionExpressionApplicableConstructor(SyntaxNode syntax, TypeSymbol targetType, BindingDiagnosticBag diagnostics)
{
var collectionCreation = BindCollectionExpressionConstructor(syntax, targetType, diagnostics);
return !collectionCreation.HasErrors;
}

internal bool HasCollectionExpressionApplicableAddMethod(SyntaxNode syntax, TypeSymbol targetType, TypeSymbol elementType, BindingDiagnosticBag diagnostics)
{
var implicitReceiver = new BoundObjectOrCollectionValuePlaceholder(syntax, isNewInstance: true, targetType) { WasCompilerGenerated = true };
var elementPlaceholder = new BoundValuePlaceholder(syntax, elementType) { WasCompilerGenerated = true };
var addMethodBinder = WithAdditionalFlags(BinderFlags.CollectionInitializerAddMethod);
var result = BindCollectionInitializerElementAddMethod(
syntax,
ImmutableArray.Create<BoundExpression>(elementPlaceholder),
hasEnumerableInitializerType: true,
addMethodBinder,
diagnostics,
implicitReceiver);
return !result.HasErrors;
}

/// <summary>
/// If the element is from a collection type where elements are added with collection initializers,
/// return the argument to the collection initializer Add method or null if the element is not a
Expand Down Expand Up @@ -868,6 +901,19 @@ private void GenerateImplicitConversionErrorForCollectionExpression(
var elementType = elementTypeWithAnnotations.Type;
Debug.Assert(elementType is { });

if (collectionTypeKind == CollectionExpressionTypeKind.ImplementsIEnumerable)
{
if (!HasCollectionExpressionApplicableConstructor(node.Syntax, targetType, diagnostics))
{
reportedErrors = true;
}

if (!HasCollectionExpressionApplicableAddMethod(node.Syntax, targetType, elementType, diagnostics))
{
reportedErrors = true;
}
}

var elements = node.Elements;
var useSiteInfo = GetNewCompoundUseSiteInfo(diagnostics);
foreach (var element in elements)
Expand Down
27 changes: 1 addition & 26 deletions src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5819,7 +5819,7 @@ private BoundExpression BindUnexpectedComplexElementInitializer(InitializerExpre
}

private BoundExpression BindCollectionInitializerElementAddMethod(
ExpressionSyntax elementInitializer,
SyntaxNode elementInitializer,
ImmutableArray<BoundExpression> boundElementInitializerExpressions,
bool hasEnumerableInitializerType,
Binder collectionInitializerAddMethodBinder,
Expand Down Expand Up @@ -5937,31 +5937,6 @@ static void copyRelevantAddMethodDiagnostics(BindingDiagnosticBag source, Bindin
}

#nullable enable
internal BoundNode BindCollectionExpressionElementAddMethod(
BoundNode element,
Binder collectionInitializerAddMethodBinder,
BoundObjectOrCollectionValuePlaceholder implicitReceiver,
BindingDiagnosticBag diagnostics,
out bool hasErrors)
{
var result = element is BoundCollectionExpressionSpreadElement spreadElement ?
(BoundNode)BindCollectionExpressionSpreadElementAddMethod(
(SpreadElementSyntax)spreadElement.Syntax,
spreadElement,
collectionInitializerAddMethodBinder,
implicitReceiver,
diagnostics) :
BindCollectionInitializerElementAddMethod(
(ExpressionSyntax)element.Syntax,
ImmutableArray.Create((BoundExpression)element),
hasEnumerableInitializerType: true,
collectionInitializerAddMethodBinder,
diagnostics,
implicitReceiver);
hasErrors = result.HasErrors;
return result;
}

private BoundCollectionExpressionSpreadElement BindCollectionExpressionSpreadElementAddMethod(
SpreadElementSyntax syntax,
BoundCollectionExpressionSpreadElement element,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
using System.Linq;
using Microsoft.CodeAnalysis.CSharp.Symbols;
using Microsoft.CodeAnalysis.PooledObjects;
using Roslyn.Utilities;

namespace Microsoft.CodeAnalysis.CSharp
{
Expand Down Expand Up @@ -183,6 +182,19 @@ protected override Conversion GetCollectionExpressionConversion(

Debug.Assert(elementType is { });

if (collectionTypeKind == CollectionExpressionTypeKind.ImplementsIEnumerable)
{
if (!_binder.HasCollectionExpressionApplicableConstructor(syntax, targetType, BindingDiagnosticBag.Discarded))
{
return Conversion.NoConversion;
}

if (!_binder.HasCollectionExpressionApplicableAddMethod(syntax, targetType, elementType, BindingDiagnosticBag.Discarded))
{
return Conversion.NoConversion;
}
}

var elements = node.Elements;
var builder = ArrayBuilder<Conversion>.GetInstance(elements.Length);
foreach (var element in elements)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2570,10 +2570,10 @@ kind2 is CollectionExpressionTypeKind.Span &&
return true;
}

// - T1 is System.ReadOnlySpan<E1> or System.Span<E1>, and T2 is an array_or_array_interface_or_string_type
// - T1 is System.ReadOnlySpan<E1> or System.Span<E1>, and T2 is an array_or_array_interface
// with iteration type E2, and an implicit conversion exists from E1 to E2
if (kind1 is CollectionExpressionTypeKind.ReadOnlySpan or CollectionExpressionTypeKind.Span &&
IsSZArrayOrArrayInterfaceOrString(t2, out elementType2) &&
IsSZArrayOrArrayInterface(t2, out elementType2) &&
hasImplicitConversion(elementType1, elementType2, ref useSiteInfo))
{
return true;
Expand All @@ -2593,14 +2593,8 @@ bool hasImplicitConversion(TypeSymbol source, TypeSymbol destination, ref Compou
Conversions.ClassifyImplicitConversionFromType(source, destination, ref useSiteInfo).IsImplicit;
}

private bool IsSZArrayOrArrayInterfaceOrString(TypeSymbol type, out TypeSymbol elementType)
private static bool IsSZArrayOrArrayInterface(TypeSymbol type, out TypeSymbol elementType)
{
if (type.SpecialType == SpecialType.System_String)
{
elementType = Compilation.GetSpecialType(SpecialType.System_Char);
return true;
}

if (type is ArrayTypeSymbol { IsSZArray: true } arrayType)
{
elementType = arrayType.ElementType;
Expand Down
Loading

0 comments on commit ec6908b

Please sign in to comment.