Skip to content

Commit

Permalink
Only ignore UDCs exactly matching compiler-defined conversions
Browse files Browse the repository at this point in the history
  • Loading branch information
jjonescz committed Jul 24, 2024
1 parent e6c5386 commit d9e4b71
Show file tree
Hide file tree
Showing 3 changed files with 81 additions and 165 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4037,37 +4037,12 @@ private bool HasExplicitSpanConversion(TypeSymbol? source, TypeSymbol destinatio

private bool IgnoreUserDefinedSpanConversions(TypeSymbol? source, TypeSymbol? target)
{
// SPEC: User-defined conversions are not considered when converting between types
// for which an implicit or an explicit span conversion exists.
var discarded = CompoundUseSiteInfo<AssemblySymbol>.Discarded;
return source is not null && target is not null &&
IsFeatureFirstClassSpanEnabled &&
(ignoreUserDefinedSpanConversionsInAnyDirection(source, target) ||
ignoreUserDefinedSpanConversionsInOneDirection(source, target) ||
ignoreUserDefinedSpanConversionsInOneDirection(target, source));

static bool ignoreUserDefinedSpanConversionsInOneDirection(TypeSymbol a, TypeSymbol b)
{
// SPEC: User-defined conversions are not considered when converting between
// SPEC: - any single-dimensional `array_type` and `System.Span<T>`/`System.ReadOnlySpan<T>`
if (a is ArrayTypeSymbol { IsSZArray: true } &&
(b.OriginalDefinition.IsSpan() || b.OriginalDefinition.IsReadOnlySpan()))
{
return true;
}

// SPEC: - `string` and `System.ReadOnlySpan<char>`
if (a.IsStringType() && b.IsReadOnlySpanChar())
{
return true;
}

return false;
}

static bool ignoreUserDefinedSpanConversionsInAnyDirection(TypeSymbol a, TypeSymbol b)
{
// SPEC: - any combination of `System.Span<T>`/`System.ReadOnlySpan<T>`
return (a.OriginalDefinition.IsSpan() || a.OriginalDefinition.IsReadOnlySpan()) &&
(b.OriginalDefinition.IsSpan() || b.OriginalDefinition.IsReadOnlySpan());
}
(HasImplicitSpanConversion(source, target, ref discarded) ||
HasExplicitSpanConversion(source, target, ref discarded));
}
}
}
4 changes: 2 additions & 2 deletions src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8904,8 +8904,8 @@ private TypeWithState VisitConversion(
{
var previousKind = conversion.Kind;
conversion = GenerateConversion(_conversions, conversionOperand, operandType.Type, targetType, fromExplicitCast, extensionMethodThisArgument, isChecked: conversionOpt?.Checked ?? false);
Debug.Assert(!conversion.Exists || conversion.Kind == previousKind);
canConvertNestedNullability = conversion.Exists;
// We do not want user-defined conversions to relax nullability, so we consider only span conversions.
canConvertNestedNullability = conversion.Exists && conversion.IsSpan;
}
break;

Expand Down
Loading

0 comments on commit d9e4b71

Please sign in to comment.