Skip to content

Commit

Permalink
Merge pull request #68831 from cston/collections-merge-feature
Browse files Browse the repository at this point in the history
Merge features/CollectionLiterals into main
  • Loading branch information
cston authored Jun 30, 2023
2 parents fd01b7e + b865d5c commit 8817655
Show file tree
Hide file tree
Showing 94 changed files with 26,888 additions and 4,536 deletions.
13 changes: 13 additions & 0 deletions src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs
Original file line number Diff line number Diff line change
Expand Up @@ -416,6 +416,13 @@ private BoundExpression CheckValue(BoundExpression expr, BindValueKind valueKind
}
break;

case BoundKind.UnconvertedCollectionLiteralExpression:
if (valueKind == BindValueKind.RValue)
{
return expr;
}
break;

case BoundKind.PointerIndirectionOperator:
if ((valueKind & BindValueKind.RefersToLocation) == BindValueKind.RefersToLocation)
{
Expand Down Expand Up @@ -3952,6 +3959,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.
Expand Down Expand Up @@ -4478,6 +4488,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.
Expand Down
217 changes: 217 additions & 0 deletions src/Compilers/CSharp/Portable/Binder/Binder_Conversions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ static bool filterConversion(Conversion conversion)
return !conversion.IsInterpolatedString &&
!conversion.IsInterpolatedStringHandler &&
!conversion.IsSwitchExpression &&
!conversion.IsCollectionLiteral &&
!(conversion.IsTupleLiteralConversion || (conversion.IsNullable && conversion.UnderlyingConversions[0].IsTupleLiteralConversion)) &&
(!conversion.IsUserDefined || filterConversion(conversion.UserDefinedFromConversion));
}
Expand Down Expand Up @@ -230,6 +231,16 @@ BoundExpression createConversion(
return ConvertObjectCreationExpression(syntax, (BoundUnconvertedObjectCreationExpression)source, conversion, isCast, destination, conversionGroupOpt, wasCompilerGenerated, diagnostics);
}

if (source.Kind == BoundKind.UnconvertedCollectionLiteralExpression)
{
Debug.Assert(conversion.IsCollectionLiteral || !conversion.Exists);
source = ConvertCollectionLiteralExpression(
(BoundUnconvertedCollectionLiteralExpression)source,
destination,
wasCompilerGenerated,
diagnostics);
}

if (source.Kind == BoundKind.UnconvertedConditionalOperator)
{
Debug.Assert(source.Type is null);
Expand Down Expand Up @@ -515,6 +526,212 @@ static BoundExpression bindObjectCreationExpression(
}
}

private BoundExpression ConvertCollectionLiteralExpression(
BoundUnconvertedCollectionLiteralExpression node,
TypeSymbol targetType,
bool wasCompilerGenerated,
BindingDiagnosticBag diagnostics)
{
TypeSymbol? elementType;
var collectionTypeKind = ConversionsBase.GetCollectionLiteralTypeKind(Compilation, targetType, out elementType);
switch (collectionTypeKind)
{
case CollectionLiteralTypeKind.CollectionInitializer:
return BindCollectionInitializerCollectionLiteral(node, collectionTypeKind, targetType, wasCompilerGenerated: wasCompilerGenerated, diagnostics);
case CollectionLiteralTypeKind.Array:
case CollectionLiteralTypeKind.Span:
case CollectionLiteralTypeKind.ReadOnlySpan:
return BindArrayOrSpanCollectionLiteral(node, targetType, wasCompilerGenerated: wasCompilerGenerated, collectionTypeKind, elementType!, diagnostics);
case CollectionLiteralTypeKind.ListInterface:
return BindListInterfaceCollectionLiteral(node, targetType, wasCompilerGenerated: wasCompilerGenerated, elementType!, diagnostics);
case CollectionLiteralTypeKind.None:
return BindCollectionLiteralForErrorRecovery(node, targetType, diagnostics);
default:
throw ExceptionUtilities.UnexpectedValue(collectionTypeKind);
}
}

private BoundCollectionLiteralExpression BindArrayOrSpanCollectionLiteral(
BoundUnconvertedCollectionLiteralExpression node,
TypeSymbol targetType,
bool wasCompilerGenerated,
CollectionLiteralTypeKind collectionTypeKind,
TypeSymbol elementType,
BindingDiagnosticBag diagnostics)
{
var syntax = (CSharpSyntaxNode)node.Syntax;

switch (collectionTypeKind)
{
case CollectionLiteralTypeKind.Span:
_ = GetWellKnownTypeMember(WellKnownMember.System_Span_T__ctor_Array, diagnostics, syntax: syntax);
break;
case CollectionLiteralTypeKind.ReadOnlySpan:
_ = GetWellKnownTypeMember(WellKnownMember.System_ReadOnlySpan_T__ctor_Array, diagnostics, syntax: syntax);
break;
}

var elements = node.Elements;
if (elements.Any(e => e is BoundCollectionLiteralSpreadElement))
{
// The array initializer includes at least one spread element, so we'll create an intermediate List<T> instance.
// https://github.com/dotnet/roslyn/issues/68785: Avoid intermediate List<T> if all spread elements have Length property.
_ = GetWellKnownTypeMember(WellKnownMember.System_Collections_Generic_List_T__ToArray, diagnostics, syntax: syntax);
var result = BindCollectionInitializerCollectionLiteral(
node,
collectionTypeKind,
GetWellKnownType(WellKnownType.System_Collections_Generic_List_T, diagnostics, syntax).Construct(elementType),
wasCompilerGenerated: wasCompilerGenerated,
diagnostics);
return result.Update(result.CollectionTypeKind, result.Placeholder, result.CollectionCreation, result.Elements, targetType);
}

var implicitReceiver = new BoundObjectOrCollectionValuePlaceholder(syntax, isNewInstance: true, targetType) { WasCompilerGenerated = true };
var builder = ArrayBuilder<BoundExpression>.GetInstance(elements.Length);
foreach (var element in elements)
{
builder.Add(convertArrayElement(element, elementType, diagnostics));
}
return new BoundCollectionLiteralExpression(
syntax,
collectionTypeKind,
implicitReceiver,
collectionCreation: null,
builder.ToImmutableAndFree(),
targetType)
{ WasCompilerGenerated = wasCompilerGenerated };

BoundExpression convertArrayElement(BoundExpression element, TypeSymbol elementType, BindingDiagnosticBag diagnostics)
{
var 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);
// Suppress any additional diagnostics
diagnostics = BindingDiagnosticBag.Discarded;
}
var result = CreateConversion(
element.Syntax,
element,
conversion,
isCast: false,
conversionGroupOpt: null,
wasCompilerGenerated: true,
destination: elementType,
diagnostics,
hasErrors: hasErrors);
result.WasCompilerGenerated = true;
return result;
}
}

private BoundCollectionLiteralExpression BindCollectionInitializerCollectionLiteral(
BoundUnconvertedCollectionLiteralExpression node,
CollectionLiteralTypeKind collectionTypeKind,
TypeSymbol targetType,
bool wasCompilerGenerated,
BindingDiagnosticBag diagnostics,
bool hasErrors = false)
{
var syntax = node.Syntax;

BoundExpression collectionCreation;
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 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<Symbol?>.Empty, ImmutableArray<BoundExpression>.Empty, targetType);
}

var implicitReceiver = new BoundObjectOrCollectionValuePlaceholder(syntax, isNewInstance: true, targetType) { WasCompilerGenerated = true };
var collectionInitializerAddMethodBinder = this.WithAdditionalFlags(BinderFlags.CollectionInitializerAddMethod);
var builder = ArrayBuilder<BoundExpression>.GetInstance(node.Elements.Length);
foreach (var element in node.Elements)
{
var result = element switch
{
BoundBadExpression => element,
BoundCollectionLiteralSpreadElement spreadElement => BindCollectionInitializerSpreadElementAddMethod(
(SpreadElementSyntax)spreadElement.Syntax,
spreadElement,
collectionInitializerAddMethodBinder,
implicitReceiver,
diagnostics),
_ => BindCollectionInitializerElementAddMethod(
(ExpressionSyntax)element.Syntax,
ImmutableArray.Create(element),
hasEnumerableInitializerType: true,
collectionInitializerAddMethodBinder,
diagnostics,
implicitReceiver),
};
result.WasCompilerGenerated = true;
builder.Add(result);
}
return new BoundCollectionLiteralExpression(
syntax,
collectionTypeKind,
implicitReceiver,
collectionCreation,
builder.ToImmutableAndFree(),
targetType,
hasErrors)
{ WasCompilerGenerated = wasCompilerGenerated };
}

private BoundCollectionLiteralExpression BindListInterfaceCollectionLiteral(
BoundUnconvertedCollectionLiteralExpression node,
TypeSymbol targetType,
bool wasCompilerGenerated,
TypeSymbol elementType,
BindingDiagnosticBag diagnostics)
{
// https://github.com/dotnet/roslyn/issues/68785: Emit [] as Array.Empty<T>() rather than a List<T>.
var result = BindCollectionInitializerCollectionLiteral(
node,
CollectionLiteralTypeKind.ListInterface,
GetWellKnownType(WellKnownType.System_Collections_Generic_List_T, diagnostics, node.Syntax).Construct(elementType),
wasCompilerGenerated: wasCompilerGenerated,
diagnostics);
return result.Update(result.CollectionTypeKind, result.Placeholder, result.CollectionCreation, result.Elements, targetType);
}

private BoundCollectionLiteralExpression BindCollectionLiteralForErrorRecovery(
BoundUnconvertedCollectionLiteralExpression node,
TypeSymbol targetType,
BindingDiagnosticBag diagnostics)
{
var syntax = node.Syntax;
var builder = ArrayBuilder<BoundExpression>.GetInstance(node.Elements.Length);
foreach (var element in node.Elements)
{
builder.Add(BindToNaturalType(element, diagnostics, reportNoTargetType: !targetType.IsErrorType()));
}
return new BoundCollectionLiteralExpression(
syntax,
collectionTypeKind: CollectionLiteralTypeKind.None,
placeholder: null,
collectionCreation: null,
elements: builder.ToImmutableAndFree(),
targetType,
hasErrors: true);
}

/// <summary>
/// Rewrite the subexpressions in a conditional expression to convert the whole thing to the destination type.
/// </summary>
Expand Down
Loading

0 comments on commit 8817655

Please sign in to comment.