Skip to content

Commit

Permalink
Collection expression arguments: initial binding and lowering (#76903)
Browse files Browse the repository at this point in the history
  • Loading branch information
cston authored Feb 4, 2025
1 parent f806fd4 commit f5224d8
Show file tree
Hide file tree
Showing 28 changed files with 2,126 additions and 82 deletions.
120 changes: 87 additions & 33 deletions src/Compilers/CSharp/Portable/Binder/Binder_Conversions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -895,13 +895,35 @@ private BoundCollectionExpression ConvertCollectionExpression(
}

implicitReceiver = new BoundObjectOrCollectionValuePlaceholder(syntax, isNewInstance: true, targetType) { WasCompilerGenerated = true };
collectionCreation = BindCollectionExpressionConstructor(syntax, targetType, constructor, diagnostics);
Debug.Assert((collectionCreation is BoundNewT && !isExpanded && constructor is null) ||
(collectionCreation is BoundObjectCreationExpression creation && creation.Expanded == isExpanded && creation.Constructor == constructor));

if (collectionCreation.HasErrors)
// Bind collection creation with arguments.
foreach (var element in elements)
{
return BindCollectionExpressionForErrorRecovery(node, targetType, inConversion: true, diagnostics);
if (element is BoundCollectionExpressionWithElement withElement)
{
var analyzedArguments = AnalyzedArguments.GetInstance();
withElement.GetArguments(analyzedArguments);
// PROTOTYPE: If there are multiple with() elements, should with() elements after
// the first be bound for error recovery only rather than as a constructor call?
var collectionWithArguments = BindCollectionExpressionConstructor(syntax, targetType, constructor, analyzedArguments, diagnostics);
analyzedArguments.Free();
collectionCreation ??= collectionWithArguments;
}
}

// Bind collection creation with no arguments if necessary.
if (collectionCreation is null)
{
var analyzedArguments = AnalyzedArguments.GetInstance();
collectionCreation = BindCollectionExpressionConstructor(syntax, targetType, constructor, analyzedArguments, diagnostics);
analyzedArguments.Free();
Debug.Assert((collectionCreation is BoundNewT && !isExpanded && constructor is null) ||
(collectionCreation is BoundObjectCreationExpression creation && creation.Expanded == isExpanded && creation.Constructor == constructor));

if (collectionCreation.HasErrors)
{
return BindCollectionExpressionForErrorRecovery(node, targetType, inConversion: true, diagnostics);
}
}

if (!elements.IsDefaultOrEmpty && HasCollectionInitializerTypeInProgress(syntax, targetType))
Expand All @@ -913,20 +935,32 @@ private BoundCollectionExpression ConvertCollectionExpression(
var collectionInitializerAddMethodBinder = new CollectionInitializerAddMethodBinder(syntax, targetType, this);
foreach (var element in elements)
{
BoundNode convertedElement = element is BoundCollectionExpressionSpreadElement spreadElement ?
(BoundNode)BindCollectionExpressionSpreadElementAddMethod(
(SpreadElementSyntax)spreadElement.Syntax,
spreadElement,
collectionInitializerAddMethodBinder,
implicitReceiver,
diagnostics) :
BindCollectionInitializerElementAddMethod(
element.Syntax,
ImmutableArray.Create((BoundExpression)element),
hasEnumerableInitializerType: true,
collectionInitializerAddMethodBinder,
diagnostics,
implicitReceiver);
BoundNode convertedElement;
switch (element)
{
case BoundCollectionExpressionWithElement:
// Handled above.
continue;
case BoundCollectionExpressionSpreadElement spreadElement:
convertedElement = BindCollectionExpressionSpreadElementAddMethod(
(SpreadElementSyntax)spreadElement.Syntax,
spreadElement,
collectionInitializerAddMethodBinder,
implicitReceiver,
diagnostics);
break;
case BoundKeyValuePairElement:
throw ExceptionUtilities.UnexpectedValue(element);
default:
convertedElement = BindCollectionInitializerElementAddMethod(
element.Syntax,
ImmutableArray.Create((BoundExpression)element),
hasEnumerableInitializerType: true,
collectionInitializerAddMethodBinder,
diagnostics,
implicitReceiver);
break;
}
builder.Add(convertedElement);
}
}
Expand Down Expand Up @@ -964,7 +998,6 @@ private BoundCollectionExpression ConvertCollectionExpression(
var elementConversions = conversion.UnderlyingConversions;

Debug.Assert(elementType is { });
Debug.Assert(elements.Length <= elementConversions.Length);
Debug.Assert(elementConversions.All(c => c.Exists));

int conversionIndex = 0;
Expand All @@ -973,6 +1006,12 @@ private BoundCollectionExpression ConvertCollectionExpression(
BoundNode convertedElement;
switch (element)
{
case BoundCollectionExpressionWithElement withElement:
if (withElement.Arguments.Length > 0)
{
diagnostics.Add(ErrorCode.ERR_CollectionArgumentsNotSupportedForType, ((WithElementSyntax)withElement.Syntax).WithKeyword, targetType);
}
continue;
case BoundCollectionExpressionSpreadElement spreadElement:
convertedElement = bindSpreadElement(
spreadElement,
Expand Down Expand Up @@ -1105,8 +1144,7 @@ private bool HasCollectionInitializerTypeInProgress(SyntaxNode syntax, TypeSymbo
Debug.Assert(result);

var useSiteInfo = GetNewCompoundUseSiteInfo(diagnostics);
Conversion collectionBuilderReturnTypeConversion;
collectionBuilderMethod = GetCollectionBuilderMethod(namedType, elementTypeOriginalDefinition.Type, builderType, methodName, ref useSiteInfo, out collectionBuilderReturnTypeConversion);
collectionBuilderMethod = GetCollectionBuilderMethod(namedType, elementTypeOriginalDefinition.Type, builderType, methodName, ref useSiteInfo);
diagnostics.Add(syntax, useSiteInfo);
if (collectionBuilderMethod is null)
{
Expand All @@ -1115,8 +1153,6 @@ private bool HasCollectionInitializerTypeInProgress(SyntaxNode syntax, TypeSymbo
return null;
}

Debug.Assert(collectionBuilderReturnTypeConversion.Exists);

ReportUseSite(collectionBuilderMethod, diagnostics, syntax.Location);

var parameterType = (NamedTypeSymbol)collectionBuilderMethod.Parameters[0].Type;
Expand All @@ -1134,7 +1170,12 @@ private bool HasCollectionInitializerTypeInProgress(SyntaxNode syntax, TypeSymbo
return collectionBuilderMethod;
}

internal BoundExpression BindCollectionExpressionConstructor(SyntaxNode syntax, TypeSymbol targetType, MethodSymbol? constructor, BindingDiagnosticBag diagnostics)
internal BoundExpression BindCollectionExpressionConstructor(
SyntaxNode syntax,
TypeSymbol targetType,
MethodSymbol? constructorNoArguments,
AnalyzedArguments analyzedArguments,
BindingDiagnosticBag diagnostics)
{
//
// !!! ATTENTION !!!
Expand All @@ -1144,10 +1185,9 @@ internal BoundExpression BindCollectionExpressionConstructor(SyntaxNode syntax,
//

BoundExpression collectionCreation;
var analyzedArguments = AnalyzedArguments.GetInstance();
if (targetType is NamedTypeSymbol namedType)
{
var binder = new ParamsCollectionTypeInProgressBinder(namedType, this, constructor);
var binder = new ParamsCollectionTypeInProgressBinder(namedType, this, constructorNoArguments);
collectionCreation = binder.BindClassCreationExpression(syntax, namedType.Name, syntax, namedType, analyzedArguments, diagnostics);
collectionCreation.WasCompilerGenerated = true;
}
Expand All @@ -1160,7 +1200,6 @@ internal BoundExpression BindCollectionExpressionConstructor(SyntaxNode syntax,
{
throw ExceptionUtilities.UnexpectedValue(targetType);
}
analyzedArguments.Free();
return collectionCreation;
}

Expand Down Expand Up @@ -1787,6 +1826,7 @@ private BoundCollectionExpression BindCollectionExpressionForErrorRecovery(
keyValuePairElement.Update(
BindToNaturalType(keyValuePairElement.Key, diagnostics, reportNoTargetType),
BindToNaturalType(keyValuePairElement.Value, diagnostics, reportNoTargetType)),
BoundCollectionExpressionWithElement withElement => bindArgumentsToNaturalType(withElement, diagnostics, reportNoTargetType),
_ => BindToNaturalType((BoundExpression)element, diagnostics, reportNoTargetType)
};
builder.Add(result);
Expand All @@ -1805,6 +1845,21 @@ private BoundCollectionExpression BindCollectionExpressionForErrorRecovery(
targetType,
hasErrors: true)
{ WasCompilerGenerated = node.IsParamsArrayOrCollection, IsParamsArrayOrCollection = node.IsParamsArrayOrCollection };

BoundCollectionExpressionWithElement bindArgumentsToNaturalType(BoundCollectionExpressionWithElement withElement, BindingDiagnosticBag diagnostics, bool reportNoTargetType)
{
var arguments = withElement.Arguments;
var builder = ArrayBuilder<BoundExpression>.GetInstance(arguments.Length);
foreach (var argument in arguments)
{
builder.Add(BindToNaturalType(argument, diagnostics, reportNoTargetType));
}
return withElement.Update(
builder.ToImmutableAndFree(),
withElement.ArgumentNamesOpt,
withElement.ArgumentRefKindsOpt,
withElement.Binder);
}
}

internal void GenerateImplicitConversionErrorForCollectionExpression(
Expand Down Expand Up @@ -1861,6 +1916,9 @@ internal void GenerateImplicitConversionErrorForCollectionExpression(
{
switch (element)
{
case BoundCollectionExpressionWithElement:
// Collection arguments do not affect convertibility.
break;
case BoundCollectionExpressionSpreadElement spreadElement:
{
var enumeratorInfo = spreadElement.EnumeratorInfoOpt;
Expand Down Expand Up @@ -1928,11 +1986,8 @@ void generateImplicitConversionError(
TypeSymbol elementTypeOriginalDefinition,
TypeSymbol? builderType,
string? methodName,
ref CompoundUseSiteInfo<AssemblySymbol> useSiteInfo,
out Conversion returnTypeConversion)
ref CompoundUseSiteInfo<AssemblySymbol> useSiteInfo)
{
returnTypeConversion = default;

if (!SourceNamedTypeSymbol.IsValidCollectionBuilderType(builderType))
{
return null;
Expand Down Expand Up @@ -2004,7 +2059,6 @@ void generateImplicitConversionError(
}

useSiteInfo.AddDiagnostics(candidateUseSiteInfo.Diagnostics);
returnTypeConversion = conversion;
return method;
}

Expand Down
Loading

0 comments on commit f5224d8

Please sign in to comment.