Skip to content

Commit

Permalink
Collection literals natural type
Browse files Browse the repository at this point in the history
  • Loading branch information
cston committed Mar 21, 2023
1 parent 02f55f6 commit bb8bc82
Show file tree
Hide file tree
Showing 24 changed files with 993 additions and 301 deletions.
9 changes: 8 additions & 1 deletion src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs
Original file line number Diff line number Diff line change
Expand Up @@ -409,7 +409,14 @@ private BoundExpression CheckValue(BoundExpression expr, BindValueKind valueKind
expr = BindIndexerDefaultArguments((BoundIndexerAccess)expr, valueKind, diagnostics);
break;

case BoundKind.UnconvertedObjectCreationExpression:
case BoundKind.UnconvertedObjectCreationExpression: // PROTOTYPE: What is the effect of commenting out this case? Add a corresponding test for collection literals (next case).
if (valueKind == BindValueKind.RValue)
{
return expr;
}
break;

case BoundKind.UnconvertedCollectionLiteralExpression:
if (valueKind == BindValueKind.RValue)
{
return expr;
Expand Down
176 changes: 112 additions & 64 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 @@ -426,7 +427,6 @@ private BoundExpression ConvertCollectionLiteralExpression(
BindingDiagnosticBag diagnostics)
{
var syntax = (CSharpSyntaxNode)node.Syntax;
var implicitReceiver = new BoundObjectOrCollectionValuePlaceholder(syntax, isNewInstance: true, targetType) { WasCompilerGenerated = true };
TypeSymbol? elementType;
BoundCollectionLiteralExpression collectionLiteral;

Expand All @@ -436,21 +436,62 @@ private BoundExpression ConvertCollectionLiteralExpression(
{
Error(diagnostics, ErrorCode.ERR_CollectionLiteralTargetTypeNotConstructible, syntax, targetType);
}
collectionLiteral = bindArrayOrSpan(syntax, spanConstructor: null, implicitReceiver, node.Initializers, arrayType.ElementType, diagnostics);
collectionLiteral = bindArrayOrSpan(syntax, targetType, spanConstructor: null, node.Initializers, arrayType.ElementType, diagnostics);
}
else if (isSpanType(targetType, WellKnownType.System_Span_T, out elementType))
{
var spanConstructor = getSpanConstructor(syntax, targetType, WellKnownMember.System_Span_T__ctor_Array, diagnostics);
collectionLiteral = bindArrayOrSpan(syntax, spanConstructor, implicitReceiver, node.Initializers, elementType, diagnostics);
collectionLiteral = bindArrayOrSpan(syntax, targetType, spanConstructor, node.Initializers, elementType, diagnostics);
}
else if (isSpanType(targetType, WellKnownType.System_ReadOnlySpan_T, out elementType))
{
var spanConstructor = getSpanConstructor(syntax, targetType, WellKnownMember.System_ReadOnlySpan_T__ctor_Array, diagnostics);
collectionLiteral = bindArrayOrSpan(syntax, spanConstructor, implicitReceiver, node.Initializers, elementType, diagnostics);
collectionLiteral = bindArrayOrSpan(syntax, targetType, spanConstructor, node.Initializers, elementType, diagnostics);
}
else if (targetType.IsErrorType())
{
collectionLiteral = BindCollectionInitializerCollectionLiteral(node, targetType, wasTargetTyped: true, hasEnumerableInitializerType: false, wasCompilerGenerated: wasCompilerGenerated, diagnostics);
}
else
{
collectionLiteral = bindCollectionInitializer(syntax, implicitReceiver, node.Initializers, diagnostics);
bool hasEnumerableInitializerType = collectionTypeImplementsIEnumerable(targetType, syntax, diagnostics);
if (!hasEnumerableInitializerType)
{
// Target type is not constructible. Use collection literal natural type if available, instead.
if (node.Type is { } collectionType)
{
var expr = BindCollectionInitializerCollectionLiteral(
node,
collectionType,
wasTargetTyped: false,
hasEnumerableInitializerType: true,
wasCompilerGenerated: node.WasCompilerGenerated,
diagnostics);
Debug.Assert(expr.Type is { });
// PROTOTYPE: Test various conversions, including conversions expected to fail.
var useSiteInfo = GetNewCompoundUseSiteInfo(diagnostics);
conversion = Conversions.ClassifyImplicitConversionFromType(expr.Type, targetType, ref useSiteInfo);
// PROTOTYPE: Test.
diagnostics.Add(syntax, useSiteInfo);
bool hasErrors = !conversion.IsValid;
if (hasErrors)
{
GenerateImplicitConversionError(diagnostics, syntax, conversion, expr, targetType);
}
return CreateConversion(
syntax,
expr,
conversion,
isCast: false,
conversionGroupOpt: null,
wasCompilerGenerated: wasCompilerGenerated,
targetType,
diagnostics,
hasErrors: hasErrors);
}
Error(diagnostics, ErrorCode.ERR_CollectionLiteralTargetTypeNotConstructible, syntax, targetType);
}
collectionLiteral = BindCollectionInitializerCollectionLiteral(node, targetType, wasTargetTyped: true, hasEnumerableInitializerType: hasEnumerableInitializerType, wasCompilerGenerated: wasCompilerGenerated, diagnostics);
}

return new BoundConversion(
Expand All @@ -477,12 +518,13 @@ bool isSpanType(TypeSymbol targetType, WellKnownType spanType, [NotNullWhen(true

BoundArrayOrSpanCollectionLiteralExpression bindArrayOrSpan(
CSharpSyntaxNode syntax,
TypeSymbol targetType,
MethodSymbol? spanConstructor,
BoundObjectOrCollectionValuePlaceholder implicitReceiver,
ImmutableArray<BoundExpression> elements,
TypeSymbol elementType,
BindingDiagnosticBag diagnostics)
{
var implicitReceiver = new BoundObjectOrCollectionValuePlaceholder(syntax, isNewInstance: true, targetType) { WasCompilerGenerated = true };
var builder = ArrayBuilder<BoundExpression>.GetInstance(elements.Length);
foreach (var element in elements)
{
Expand All @@ -499,62 +541,6 @@ BoundArrayOrSpanCollectionLiteralExpression bindArrayOrSpan(
{ WasCompilerGenerated = wasCompilerGenerated };
}

BoundCollectionInitializerCollectionLiteralExpression bindCollectionInitializer(
CSharpSyntaxNode syntax,
BoundObjectOrCollectionValuePlaceholder implicitReceiver,
ImmutableArray<BoundExpression> elements,
BindingDiagnosticBag diagnostics)
{
BoundExpression collectionCreation;
if (targetType is NamedTypeSymbol namedType)
{
var analyzedArguments = AnalyzedArguments.GetInstance();
collectionCreation = BindClassCreationExpression(syntax, namedType.Name, syntax, namedType, analyzedArguments, diagnostics);
collectionCreation.WasCompilerGenerated = true;
analyzedArguments.Free();
}
else if (targetType is TypeParameterSymbol typeParameter)
{
var arguments = AnalyzedArguments.GetInstance();
collectionCreation = BindTypeParameterCreationExpression(syntax, typeParameter, arguments, initializerOpt: null, typeSyntax: syntax, wasTargetTyped: true, diagnostics);
arguments.Free();
}
else
{
collectionCreation = new BoundBadExpression(syntax, LookupResultKind.NotCreatable, ImmutableArray<Symbol?>.Empty, ImmutableArray<BoundExpression>.Empty, targetType);
}

bool hasEnumerableInitializerType = collectionTypeImplementsIEnumerable(targetType, syntax, diagnostics);
if (!hasEnumerableInitializerType && !targetType.IsErrorType())
{
Error(diagnostics, ErrorCode.ERR_CollectionLiteralTargetTypeNotConstructible, syntax, targetType);
}

var collectionInitializerAddMethodBinder = this.WithAdditionalFlags(BinderFlags.CollectionInitializerAddMethod);
var builder = ArrayBuilder<BoundExpression>.GetInstance(node.Initializers.Length);
foreach (var element in node.Initializers)
{
var result = BindCollectionInitializerElementAddMethod(
(ExpressionSyntax)element.Syntax,
ImmutableArray.Create(element),
hasEnumerableInitializerType,
collectionInitializerAddMethodBinder,
diagnostics,
implicitReceiver);
result.WasCompilerGenerated = true;
builder.Add(result);
}
return new BoundCollectionInitializerCollectionLiteralExpression(
syntax,
collectionCreation,
naturalTypeOpt: null, // PROTOTYPE: Support natural type.
wasTargetTyped: true, // PROTOTYPE: Support natural type.
implicitReceiver,
builder.ToImmutableAndFree(),
targetType)
{ WasCompilerGenerated = wasCompilerGenerated };
}

MethodSymbol? getSpanConstructor(CSharpSyntaxNode syntax, TypeSymbol spanType, WellKnownMember spanMember, BindingDiagnosticBag diagnostics)
{
return (MethodSymbol?)GetWellKnownTypeMember(spanMember, diagnostics, syntax: syntax)?.SymbolAsMember((NamedTypeSymbol)spanType);
Expand All @@ -574,7 +560,7 @@ bool collectionTypeImplementsIEnumerable(TypeSymbol targetType, CSharpSyntaxNode

BoundExpression convertArrayElement(BoundExpression element, TypeSymbol elementType, BindingDiagnosticBag diagnostics)
{
CompoundUseSiteInfo<AssemblySymbol> useSiteInfo = GetNewCompoundUseSiteInfo(diagnostics);
var useSiteInfo = GetNewCompoundUseSiteInfo(diagnostics);
var conversion = Conversions.ClassifyImplicitConversionFromExpression(element, elementType, ref useSiteInfo);
diagnostics.Add(element.Syntax, useSiteInfo);
bool hasErrors = !conversion.IsValid;
Expand All @@ -590,13 +576,75 @@ BoundExpression convertArrayElement(BoundExpression element, TypeSymbol elementT
conversionGroupOpt: null,
wasCompilerGenerated: true,
destination: elementType,
diagnostics: diagnostics,
diagnostics,
hasErrors: hasErrors);
result.WasCompilerGenerated = true;
return result;
}
}

private BoundCollectionInitializerCollectionLiteralExpression BindCollectionInitializerCollectionLiteral(
BoundUnconvertedCollectionLiteralExpression node,
TypeSymbol targetType,
bool wasTargetTyped,
bool hasEnumerableInitializerType,
bool wasCompilerGenerated,
BindingDiagnosticBag diagnostics)
{
Debug.Assert(wasTargetTyped ||
targetType.IsErrorType() ||
targetType.Equals(node.Type, TypeCompareKind.ConsiderEverything));

var syntax = node.Syntax;

BoundExpression collectionCreation;
if (targetType is NamedTypeSymbol namedType)
{
var analyzedArguments = AnalyzedArguments.GetInstance();
// PROTOTYPE: Use List<T>(int capacity) constructor when size is known, and when using natural type.
// What about when target-typed to List<T>? We should still use the int capacity constructor in that case.
// Review with LDM.
collectionCreation = BindClassCreationExpression(syntax, namedType.Name, syntax, namedType, analyzedArguments, diagnostics);
collectionCreation.WasCompilerGenerated = true;
analyzedArguments.Free();
}
else if (targetType is TypeParameterSymbol typeParameter)
{
var arguments = AnalyzedArguments.GetInstance();
collectionCreation = BindTypeParameterCreationExpression(syntax, typeParameter, arguments, initializerOpt: null, typeSyntax: syntax, wasTargetTyped: true, diagnostics);
arguments.Free();
}
else
{
collectionCreation = new BoundBadExpression(syntax, LookupResultKind.NotCreatable, ImmutableArray<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.Initializers.Length);
foreach (var element in node.Initializers)
{
var result = BindCollectionInitializerElementAddMethod(
element.Syntax,
ImmutableArray.Create(element),
hasEnumerableInitializerType,
collectionInitializerAddMethodBinder,
diagnostics,
implicitReceiver);
result.WasCompilerGenerated = true;
builder.Add(result);
}
return new BoundCollectionInitializerCollectionLiteralExpression(
syntax,
collectionCreation,
naturalTypeOpt: node.Type,
wasTargetTyped: wasTargetTyped,
implicitReceiver,
builder.ToImmutableAndFree(),
targetType)
{ WasCompilerGenerated = wasCompilerGenerated };
}

/// <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 bb8bc82

Please sign in to comment.