Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -1754,12 +1754,11 @@ public bool HasAnyNullabilityImplicitConversion(TypeWithAnnotations source, Type
ClassifyImplicitConversionFromType(source.Type, destination.Type, ref discardedUseSiteInfo).Kind != ConversionKind.NoConversion;
}

public static bool HasIdentityConversionToAny<T>(T type, ArrayBuilder<T> targetTypes)
where T : TypeSymbol
private static bool HasIdentityConversionToAny(NamedTypeSymbol type, ArrayBuilder<(NamedTypeSymbol ParticipatingType, TypeParameterSymbol ConstrainedToTypeOpt)> targetTypes)
{
foreach (var targetType in targetTypes)
{
if (HasIdentityConversionInternal(type, targetType, includeNullability: false))
if (HasIdentityConversionInternal(type, targetType.ParticipatingType, includeNullability: false))
{
return true;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,19 @@
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

#nullable disable

using System.Diagnostics;
using Microsoft.CodeAnalysis.CSharp.Symbols;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.PooledObjects;
using Microsoft.CodeAnalysis.Text;
using System.Collections.Generic;
using System.Collections.Immutable;

namespace Microsoft.CodeAnalysis.CSharp
{
internal partial class ConversionsBase
{
public static void AddTypesParticipatingInUserDefinedConversion(ArrayBuilder<TypeSymbol> result, TypeSymbol type, bool includeBaseTypes, ref CompoundUseSiteInfo<AssemblySymbol> useSiteInfo)
public static void AddTypesParticipatingInUserDefinedConversion(ArrayBuilder<(NamedTypeSymbol ParticipatingType, TypeParameterSymbol? ConstrainedToTypeOpt)> result, TypeSymbol type, bool includeBaseTypes, ref CompoundUseSiteInfo<AssemblySymbol> useSiteInfo)
{
// CONSIDER: These sets are usually small; if they are large then this is an O(n^2)
// CONSIDER: algorithm. We could use a hash table instead to build up the set.
Expand Down Expand Up @@ -52,37 +51,33 @@ public static void AddTypesParticipatingInUserDefinedConversion(ArrayBuilder<Typ
if (type is TypeParameterSymbol typeParameter)
{
NamedTypeSymbol effectiveBaseClass = typeParameter.EffectiveBaseClass(ref useSiteInfo);

addFromClassOrStruct(result, excludeExisting, effectiveBaseClass, includeBaseTypes, ref useSiteInfo);

switch (effectiveBaseClass.SpecialType)
ImmutableArray<NamedTypeSymbol> interfaces = includeBaseTypes ?
typeParameter.AllEffectiveInterfacesWithDefinitionUseSiteDiagnostics(ref useSiteInfo) :
typeParameter.EffectiveInterfacesWithDefinitionUseSiteDiagnostics(ref useSiteInfo);

foreach (NamedTypeSymbol iface in interfaces)
{
case SpecialType.System_ValueType:
case SpecialType.System_Enum:
case SpecialType.System_Array:
case SpecialType.System_Object:
if (!excludeExisting || !HasIdentityConversionToAny(typeParameter, result))
{
// Add the type parameter to the set as well. This will be treated equivalent to adding its
// effective interfaces to the set. We are not doing that here because we still need to know
// the originating type parameter as "constrained to" type.
result.Add(typeParameter);
}
break;
if (!excludeExisting || !HasIdentityConversionToAny(iface, result))
{
result.Add((iface, typeParameter));
}
}
}
else
{
addFromClassOrStruct(result, excludeExisting, type, includeBaseTypes, ref useSiteInfo);
}

static void addFromClassOrStruct(ArrayBuilder<TypeSymbol> result, bool excludeExisting, TypeSymbol type, bool includeBaseTypes, ref CompoundUseSiteInfo<AssemblySymbol> useSiteInfo)
static void addFromClassOrStruct(ArrayBuilder<(NamedTypeSymbol ParticipatingType, TypeParameterSymbol? ConstrainedToTypeOpt)> result, bool excludeExisting, TypeSymbol type, bool includeBaseTypes, ref CompoundUseSiteInfo<AssemblySymbol> useSiteInfo)
{
if (type.IsClassType() || type.IsStructType())
{
if (!excludeExisting || !HasIdentityConversionToAny(type, result))
var namedType = (NamedTypeSymbol)type;
if (!excludeExisting || !HasIdentityConversionToAny(namedType, result))
{
result.Add(type);
result.Add((namedType, null));
}
}

Expand All @@ -96,7 +91,7 @@ static void addFromClassOrStruct(ArrayBuilder<TypeSymbol> result, bool excludeEx
{
if (!excludeExisting || !HasIdentityConversionToAny(t, result))
{
result.Add(t);
result.Add((t, null));
}

t = t.BaseTypeWithDefinitionUseSiteDiagnostics(ref useSiteInfo);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ private UserDefinedConversionResult AnalyzeExplicitUserDefinedConversions(

// SPEC: Find the set of types D from which user-defined conversion operators
// SPEC: will be considered...
var d = ArrayBuilder<TypeSymbol>.GetInstance();
var d = ArrayBuilder<(NamedTypeSymbol ParticipatingType, TypeParameterSymbol ConstrainedToTypeOpt)>.GetInstance();
ComputeUserDefinedExplicitConversionTypeSet(source, target, d, ref useSiteInfo);

// SPEC: Find the set of applicable user-defined and lifted conversion operators, U...
Expand Down Expand Up @@ -71,7 +71,7 @@ private UserDefinedConversionResult AnalyzeExplicitUserDefinedConversions(
return UserDefinedConversionResult.Valid(u, best.Value);
}

private static void ComputeUserDefinedExplicitConversionTypeSet(TypeSymbol source, TypeSymbol target, ArrayBuilder<TypeSymbol> d, ref CompoundUseSiteInfo<AssemblySymbol> useSiteInfo)
private static void ComputeUserDefinedExplicitConversionTypeSet(TypeSymbol source, TypeSymbol target, ArrayBuilder<(NamedTypeSymbol ParticipatingType, TypeParameterSymbol ConstrainedToTypeOpt)> d, ref CompoundUseSiteInfo<AssemblySymbol> useSiteInfo)
{
// Spec 6.4.5: User-defined explicit conversions
// Find the set of types, D, from which user-defined conversion operators will be considered.
Expand All @@ -87,7 +87,7 @@ private void ComputeApplicableUserDefinedExplicitConversionSet(
TypeSymbol source,
TypeSymbol target,
bool isChecked,
ArrayBuilder<TypeSymbol> d,
ArrayBuilder<(NamedTypeSymbol ParticipatingType, TypeParameterSymbol ConstrainedToTypeOpt)> d,
ArrayBuilder<UserDefinedConversionAnalysis> u,
ref CompoundUseSiteInfo<AssemblySymbol> useSiteInfo)
{
Expand All @@ -97,30 +97,29 @@ private void ComputeApplicableUserDefinedExplicitConversionSet(
Debug.Assert(d != null);
Debug.Assert(u != null);

HashSet<NamedTypeSymbol> lookedInInterfaces = null;
bool haveInterfaces = false;

foreach (TypeSymbol declaringType in d)
foreach ((NamedTypeSymbol declaringType, TypeParameterSymbol constrainedToTypeOpt) in d)
{
if (declaringType is TypeParameterSymbol typeParameter)
if (declaringType.IsInterface)
{
ImmutableArray<NamedTypeSymbol> interfaceTypes = typeParameter.AllEffectiveInterfacesWithDefinitionUseSiteDiagnostics(ref useSiteInfo);

if (!interfaceTypes.IsEmpty)
{
lookedInInterfaces ??= new HashSet<NamedTypeSymbol>(Symbols.SymbolEqualityComparer.AllIgnoreOptions); // Equivalent to has identity conversion check

foreach (var interfaceType in interfaceTypes)
{
if (lookedInInterfaces.Add(interfaceType))
{
addCandidatesFromType(constrainedToTypeOpt: typeParameter, interfaceType, sourceExpression, source, target, isChecked: isChecked, u, ref useSiteInfo);
}
}
}
Debug.Assert(constrainedToTypeOpt is not null);
haveInterfaces = true;
}
else
{
addCandidatesFromType(constrainedToTypeOpt: null, (NamedTypeSymbol)declaringType, sourceExpression, source, target, isChecked: isChecked, u, ref useSiteInfo);
addCandidatesFromType(constrainedToTypeOpt: null, declaringType, sourceExpression, source, target, isChecked: isChecked, u, ref useSiteInfo);
}
}

if (u.Count == 0 && haveInterfaces)
{
foreach ((NamedTypeSymbol declaringType, TypeParameterSymbol constrainedToTypeOpt) in d)
{
if (declaringType.IsInterface)
{
addCandidatesFromType(constrainedToTypeOpt: constrainedToTypeOpt, declaringType, sourceExpression, source, target, isChecked: isChecked, u, ref useSiteInfo);
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ private UserDefinedConversionResult AnalyzeImplicitUserDefinedConversions(
// A user-defined implicit conversion from an expression E to type T is processed as follows:

// SPEC: Find the set of types D from which user-defined conversion operators...
var d = ArrayBuilder<TypeSymbol>.GetInstance();
var d = ArrayBuilder<(NamedTypeSymbol ParticipatingType, TypeParameterSymbol ConstrainedToTypeOpt)>.GetInstance();
ComputeUserDefinedImplicitConversionTypeSet(source, target, d, ref useSiteInfo);

// SPEC: Find the set of applicable user-defined and lifted conversion operators, U...
Expand Down Expand Up @@ -115,7 +115,7 @@ private UserDefinedConversionResult AnalyzeImplicitUserDefinedConversions(
return UserDefinedConversionResult.Valid(u, best.Value);
}

private static void ComputeUserDefinedImplicitConversionTypeSet(TypeSymbol s, TypeSymbol t, ArrayBuilder<TypeSymbol> d, ref CompoundUseSiteInfo<AssemblySymbol> useSiteInfo)
private static void ComputeUserDefinedImplicitConversionTypeSet(TypeSymbol s, TypeSymbol t, ArrayBuilder<(NamedTypeSymbol ParticipatingType, TypeParameterSymbol ConstrainedToTypeOpt)> d, ref CompoundUseSiteInfo<AssemblySymbol> useSiteInfo)
{
// Spec 6.4.4: User-defined implicit conversions
// Find the set of types D from which user-defined conversion operators
Expand Down Expand Up @@ -143,7 +143,7 @@ private void ComputeApplicableUserDefinedImplicitConversionSet(
BoundExpression sourceExpression,
TypeSymbol source,
TypeSymbol target,
ArrayBuilder<TypeSymbol> d,
ArrayBuilder<(NamedTypeSymbol ParticipatingType, TypeParameterSymbol ConstrainedToTypeOpt)> d,
ArrayBuilder<UserDefinedConversionAnalysis> u,
ref CompoundUseSiteInfo<AssemblySymbol> useSiteInfo,
bool allowAnyTarget = false)
Expand Down Expand Up @@ -245,30 +245,29 @@ private void ComputeApplicableUserDefinedImplicitConversionSet(
return;
}

HashSet<NamedTypeSymbol> lookedInInterfaces = null;
bool haveInterfaces = false;

foreach (TypeSymbol declaringType in d)
foreach ((NamedTypeSymbol declaringType, TypeParameterSymbol constrainedToTypeOpt) in d)
{
if (declaringType is TypeParameterSymbol typeParameter)
if (declaringType.IsInterface)
{
ImmutableArray<NamedTypeSymbol> interfaceTypes = typeParameter.AllEffectiveInterfacesWithDefinitionUseSiteDiagnostics(ref useSiteInfo);

if (!interfaceTypes.IsEmpty)
{
lookedInInterfaces ??= new HashSet<NamedTypeSymbol>(Symbols.SymbolEqualityComparer.AllIgnoreOptions); // Equivalent to has identity conversion check

foreach (var interfaceType in interfaceTypes)
{
if (lookedInInterfaces.Add(interfaceType))
{
addCandidatesFromType(constrainedToTypeOpt: typeParameter, interfaceType, sourceExpression, source, target, u, ref useSiteInfo, allowAnyTarget);
}
}
}
Debug.Assert(constrainedToTypeOpt is not null);
haveInterfaces = true;
}
else
{
addCandidatesFromType(constrainedToTypeOpt: null, (NamedTypeSymbol)declaringType, sourceExpression, source, target, u, ref useSiteInfo, allowAnyTarget);
addCandidatesFromType(constrainedToTypeOpt: null, declaringType, sourceExpression, source, target, u, ref useSiteInfo, allowAnyTarget);
}
}

if (u.Count == 0 && haveInterfaces)
{
foreach ((NamedTypeSymbol declaringType, TypeParameterSymbol constrainedToTypeOpt) in d)
{
if (declaringType.IsInterface)
{
addCandidatesFromType(constrainedToTypeOpt: constrainedToTypeOpt, declaringType, sourceExpression, source, target, u, ref useSiteInfo, allowAnyTarget);
}
}
}

Expand Down Expand Up @@ -944,7 +943,7 @@ protected UserDefinedConversionResult AnalyzeImplicitUserDefinedConversionForV6S
// SPEC VIOLATION: We do the same to maintain compatibility with the native compiler.

// (a) Compute the set of types D from which user-defined conversion operators should be considered by considering only the source type.
var d = ArrayBuilder<TypeSymbol>.GetInstance();
var d = ArrayBuilder<(NamedTypeSymbol ParticipatingType, TypeParameterSymbol ConstrainedToTypeOpt)>.GetInstance();
ComputeUserDefinedImplicitConversionTypeSet(source, t: null, d: d, useSiteInfo: ref useSiteInfo);

// (b) Instead of computing applicable user defined implicit conversions U from the source type to a specific target type,
Expand Down
12 changes: 12 additions & 0 deletions src/Compilers/CSharp/Portable/Symbols/TypeParameterSymbol.cs
Original file line number Diff line number Diff line change
Expand Up @@ -290,6 +290,18 @@ internal ImmutableArray<NamedTypeSymbol> EffectiveInterfacesNoUseSiteDiagnostics
}
}

internal ImmutableArray<NamedTypeSymbol> EffectiveInterfacesWithDefinitionUseSiteDiagnostics(ref CompoundUseSiteInfo<AssemblySymbol> useSiteInfo)
{
var result = EffectiveInterfacesNoUseSiteDiagnostics;

foreach (var iface in result)
{
iface.OriginalDefinition.AddUseSiteInfo(ref useSiteInfo);
}

return result;
}

/// <summary>
/// The most encompassed type (spec 6.4.2) from the constraints.
/// </summary>
Expand Down
Loading