Skip to content

Commit

Permalink
Add span type inference
Browse files Browse the repository at this point in the history
  • Loading branch information
jjonescz committed Aug 5, 2024
1 parent bccbfbd commit f408180
Show file tree
Hide file tree
Showing 2 changed files with 417 additions and 23 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -1634,6 +1634,13 @@ private void ExactInference(TypeWithAnnotations source, TypeWithAnnotations targ
return;
}

// SPEC: * V is a Span<V1> and U is an array type U1[] or a Span<U1>
// SPEC: * V is a ReadOnlySpan<V1> and U is an array type U1[] or a Span<U1> or ReadOnlySpan<U1>
if (ExactSpanInference(source.Type, target.Type, ref useSiteInfo))
{
return;
}

// SPEC: * Otherwise, if V is a constructed type C<V1...Vk> and U is a constructed
// SPEC: type C<U1...Uk> then an exact inference is made
// SPEC: from each Ui to the corresponding Vi.
Expand Down Expand Up @@ -1690,6 +1697,54 @@ private bool ExactArrayInference(TypeWithAnnotations source, TypeWithAnnotations
return true;
}

private readonly bool IsFeatureFirstClassSpanEnabled
{
get
{
// Note: when Compilation is null, we assume latest LangVersion.
return _compilation?.IsFeatureEnabled(MessageID.IDS_FeatureFirstClassSpan) != false;
}
}

private bool ExactSpanInference(TypeSymbol source, TypeSymbol target, ref CompoundUseSiteInfo<AssemblySymbol> useSiteInfo)
{
Debug.Assert(source is not null);
Debug.Assert(target is not null);

if (IsFeatureFirstClassSpanEnabled && (
// SPEC: * V is a Span<V1> and U is an array type U1[] or a Span<U1>
(
target.IsSpan() &&
(source.IsSZArray() || source.IsSpan())
) ||
// SPEC: * V is a ReadOnlySpan<V1> and U is an array type U1[] or a Span<U1> or ReadOnlySpan<U1>
(
target.IsReadOnlySpan() &&
(source.IsSZArray() || source.IsSpan() || source.IsReadOnlySpan())
)
))
{
var sourceElementType = GetSpanOrSZArrayElementType(source);
var targetElementType = GetSpanElementType(target);
ExactInference(sourceElementType, targetElementType, ref useSiteInfo);
return true;
}

return false;
}

private static TypeWithAnnotations GetSpanElementType(TypeSymbol type)
{
return ((NamedTypeSymbol)type).TypeArgumentsWithAnnotationsNoUseSiteDiagnostics[0];
}

private static TypeWithAnnotations GetSpanOrSZArrayElementType(TypeSymbol type)
{
return type is ArrayTypeSymbol arraySource
? arraySource.ElementTypeWithAnnotations
: GetSpanElementType(type);
}

private enum ExactOrBoundsKind
{
Exact,
Expand Down Expand Up @@ -1922,6 +1977,13 @@ private void LowerBoundInference(TypeWithAnnotations source, TypeWithAnnotations
return;
}

// SPEC: * V is a Span<V1> and U is an array type U1[] or a Span<U1>
// SPEC: * V is a ReadOnlySpan<V1> and U is an array type U1[] or a Span<U1> or ReadOnlySpan<U1>
if (LowerBoundSpanInference(source.Type, target.Type, ref useSiteInfo))
{
return;
}

// UNDONE: At this point we could also do an inference from non-nullable U
// UNDONE: to nullable V.
// UNDONE:
Expand Down Expand Up @@ -2055,6 +2117,44 @@ private bool LowerBoundArrayInference(TypeSymbol source, TypeSymbol target, ref
return true;
}

private bool LowerBoundSpanInference(TypeSymbol source, TypeSymbol target, ref CompoundUseSiteInfo<AssemblySymbol> useSiteInfo)
{
Debug.Assert(source is not null);
Debug.Assert(target is not null);

if (IsFeatureFirstClassSpanEnabled && (
// SPEC: * V is a Span<V1> and U is an array type U1[] or a Span<U1>
(
target.IsSpan() &&
(source.IsSZArray() || source.IsSpan())
) ||
// SPEC: * V is a ReadOnlySpan<V1> and U is an array type U1[] or a Span<U1> or ReadOnlySpan<U1>
(
target.IsReadOnlySpan() &&
(source.IsSZArray() || source.IsSpan() || source.IsReadOnlySpan())
)
))
{
var sourceElementType = GetSpanOrSZArrayElementType(source);
var targetElementType = GetSpanElementType(target);

// SPEC: * If U1 is not known to be a reference type then an exact inference is made
// SPEC: * If V is a Span<V1>, then an exact inference is made
if (!sourceElementType.Type.IsReferenceType || target.IsSpan())
{
ExactInference(sourceElementType, targetElementType, ref useSiteInfo);
}
else
{
LowerBoundInference(sourceElementType, targetElementType, ref useSiteInfo);
}

return true;
}

return false;
}

private bool LowerBoundNullableInference(TypeWithAnnotations source, TypeWithAnnotations target, ref CompoundUseSiteInfo<AssemblySymbol> useSiteInfo)
{
return ExactOrBoundsNullableInference(ExactOrBoundsKind.LowerBound, source, target, ref useSiteInfo);
Expand Down
Loading

0 comments on commit f408180

Please sign in to comment.