Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Collection expressions: prefer ReadOnlySpan<T> over Span<T> in overload resolution #70328

Merged
merged 7 commits into from
Oct 13, 2023
Original file line number Diff line number Diff line change
Expand Up @@ -2531,57 +2531,87 @@ private BetterResult BetterConversionFromExpression(BoundExpression node, TypeSy
if (!conv2.IsConditionalExpression && conv1.IsConditionalExpression)
return BetterResult.Right;

// C1 and C2 are collection expression conversions and
// T1 is a span_type with iteration type E1, and
// T2 is an array_or_array_interface_or_string_type with iteration type E2, and
// - E1 is implicitly convertible to E2
// - E is a collection expression and one of the following holds:
333fred marked this conversation as resolved.
Show resolved Hide resolved
cston marked this conversation as resolved.
Show resolved Hide resolved
if (conv1.Kind == ConversionKind.CollectionExpression &&
conv2.Kind == ConversionKind.CollectionExpression)
{
TypeSymbol elementType;
TypeWithAnnotations typeArg;
if ((conv1.GetCollectionExpressionTypeKind(out elementType) is CollectionExpressionTypeKind.Span or CollectionExpressionTypeKind.ReadOnlySpan) &&
IsSZArrayOrArrayInterfaceOrString(t2, out typeArg))
if (IsBetterCollectionExpressionConversion(t1, conv1, t2, conv2, ref useSiteInfo))
{
return Conversions.ClassifyImplicitConversionFromType(elementType, typeArg.Type, ref useSiteInfo).IsImplicit ?
BetterResult.Left :
BetterResult.Neither;
return BetterResult.Left;
}
if ((conv2.GetCollectionExpressionTypeKind(out elementType) is CollectionExpressionTypeKind.Span or CollectionExpressionTypeKind.ReadOnlySpan) &&
IsSZArrayOrArrayInterfaceOrString(t1, out typeArg))
if (IsBetterCollectionExpressionConversion(t2, conv2, t1, conv1, ref useSiteInfo))
{
return Conversions.ClassifyImplicitConversionFromType(elementType, typeArg.Type, ref useSiteInfo).IsImplicit ?
BetterResult.Right :
BetterResult.Neither;
return BetterResult.Right;
}
return BetterResult.Neither;
}

// - T1 is a better conversion target than T2 and either C1 and C2 are both conditional expression
// conversions or neither is a conditional expression conversion.
return BetterConversionTarget(node, t1, conv1, t2, conv2, ref useSiteInfo, out okToDowngradeToNeither);
}

private bool IsSZArrayOrArrayInterfaceOrString(TypeSymbol type, out TypeWithAnnotations elementType)
// Implements the rules for
// - E is a collection expression and one of the following holds:
cston marked this conversation as resolved.
Show resolved Hide resolved
private bool IsBetterCollectionExpressionConversion(TypeSymbol t1, Conversion conv1, TypeSymbol t2, Conversion conv2, ref CompoundUseSiteInfo<AssemblySymbol> useSiteInfo)
{
TypeSymbol elementType1;
var kind1 = conv1.GetCollectionExpressionTypeKind(out elementType1);
TypeSymbol elementType2;
var kind2 = conv2.GetCollectionExpressionTypeKind(out elementType2);

// - T1 is System.ReadOnlySpan<E1>, and T2 is System.Span<E2>, and an implicit conversion exists from E1 to E2
if (kind1 is CollectionExpressionTypeKind.ReadOnlySpan &&
kind2 is CollectionExpressionTypeKind.Span &&
hasImplicitConversion(elementType1, elementType2, ref useSiteInfo))
{
return true;
}

// - T1 is System.ReadOnlySpan<E1> or System.Span<E1>, and T2 is an array_or_array_interface_or_string_type
// 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) &&
hasImplicitConversion(elementType1, elementType2, ref useSiteInfo))
{
return true;
}

// - T1 is not a span_type, and T2 is not a span_type, and an implicit conversion exists from T1 to T2
if (kind1 is not (CollectionExpressionTypeKind.ReadOnlySpan or CollectionExpressionTypeKind.Span) &&
kind2 is not (CollectionExpressionTypeKind.ReadOnlySpan or CollectionExpressionTypeKind.Span) &&
hasImplicitConversion(t1, t2, ref useSiteInfo))
{
return true;
}

return false;

bool hasImplicitConversion(TypeSymbol source, TypeSymbol destination, ref CompoundUseSiteInfo<AssemblySymbol> useSiteInfo) =>
Conversions.ClassifyImplicitConversionFromType(source, destination, ref useSiteInfo).IsImplicit;
}

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

if (type is ArrayTypeSymbol { IsSZArray: true } arrayType)
{
elementType = arrayType.ElementTypeWithAnnotations;
elementType = arrayType.ElementType;
return true;
}

if (type.IsArrayInterface(out TypeWithAnnotations typeArg))
{
elementType = typeArg;
elementType = typeArg.Type;
return true;
}

elementType = default;
elementType = null;
return false;
}

Expand Down
Loading
Loading