using System; using System.Collections.Immutable; using System.Diagnostics; using Microsoft.CodeAnalysis.CSharp.Symbols; using Microsoft.CodeAnalysis.Operations; using Microsoft.CodeAnalysis.PooledObjects; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.CSharp; public readonly struct Conversion : IEquatable, IConvertibleConversion { private abstract class UncommonData { } private sealed class MethodUncommonData : UncommonData { public static readonly MethodUncommonData NoApplicableOperators = new MethodUncommonData(isExtensionMethod: false, isArrayIndex: false, UserDefinedConversionResult.NoApplicableOperators(ImmutableArray.Empty), null); internal readonly MethodSymbol? _conversionMethod; internal readonly UserDefinedConversionResult _conversionResult; private const byte IsExtensionMethodMask = 1; private const byte IsArrayIndexMask = 2; private readonly byte _flags; internal bool IsExtensionMethod => (_flags & 1) != 0; internal bool IsArrayIndex => (_flags & 2) != 0; public MethodUncommonData(bool isExtensionMethod, bool isArrayIndex, UserDefinedConversionResult conversionResult, MethodSymbol? conversionMethod) { _conversionMethod = conversionMethod; _conversionResult = conversionResult; _flags = (isExtensionMethod ? ((byte)1) : ((byte)0)); if (isArrayIndex) { _flags |= 2; } } } private class NestedUncommonData : UncommonData { internal readonly ImmutableArray _nestedConversionsOpt; public NestedUncommonData(ImmutableArray nestedConversions) { _nestedConversionsOpt = nestedConversions; } } private sealed class DeconstructionUncommonData : UncommonData { internal readonly DeconstructMethodInfo DeconstructMethodInfo; internal readonly ImmutableArray<(BoundValuePlaceholder? placeholder, BoundExpression? conversion)> DeconstructConversionInfo; internal DeconstructionUncommonData(DeconstructMethodInfo deconstructMethodInfoOpt, ImmutableArray<(BoundValuePlaceholder? placeholder, BoundExpression? conversion)> deconstructConversionInfo) { DeconstructMethodInfo = deconstructMethodInfoOpt; DeconstructConversionInfo = deconstructConversionInfo; } } private sealed class CollectionExpressionUncommonData : NestedUncommonData { internal readonly CollectionExpressionTypeKind CollectionExpressionTypeKind; internal readonly TypeSymbol? ElementType; internal CollectionExpressionUncommonData(CollectionExpressionTypeKind collectionExpressionTypeKind, TypeSymbol? elementType, ImmutableArray elementConversions) : base(elementConversions) { CollectionExpressionTypeKind = collectionExpressionTypeKind; ElementType = elementType; } } private static class ConversionSingletons { internal static ImmutableArray IdentityUnderlying = ImmutableArray.Create(Identity); internal static ImmutableArray ImplicitConstantUnderlying = ImmutableArray.Create(ImplicitConstant); internal static ImmutableArray ImplicitNumericUnderlying = ImmutableArray.Create(ImplicitNumeric); internal static ImmutableArray ExplicitNumericUnderlying = ImmutableArray.Create(ExplicitNumeric); internal static ImmutableArray ExplicitEnumerationUnderlying = ImmutableArray.Create(ExplicitEnumeration); internal static ImmutableArray PointerToIntegerUnderlying = ImmutableArray.Create(PointerToInteger); } private readonly ConversionKind _kind; private readonly UncommonData? _uncommonData; internal static readonly Conversion ExplicitNullableWithExplicitEnumerationUnderlying; internal static readonly Conversion ExplicitNullableWithPointerToIntegerUnderlying; internal static readonly Conversion ExplicitNullableWithIdentityUnderlying; internal static readonly Conversion ExplicitNullableWithImplicitNumericUnderlying; internal static readonly Conversion ExplicitNullableWithExplicitNumericUnderlying; internal static readonly Conversion ExplicitNullableWithImplicitConstantUnderlying; internal static readonly Conversion ImplicitNullableWithExplicitEnumerationUnderlying; internal static readonly Conversion ImplicitNullableWithPointerToIntegerUnderlying; internal static readonly Conversion ImplicitNullableWithIdentityUnderlying; internal static readonly Conversion ImplicitNullableWithImplicitNumericUnderlying; internal static readonly Conversion ImplicitNullableWithExplicitNumericUnderlying; internal static readonly Conversion ImplicitNullableWithImplicitConstantUnderlying; internal static Conversion UnsetConversion => new Conversion(ConversionKind.UnsetConversionKind); internal static Conversion NoConversion => new Conversion(ConversionKind.NoConversion); internal static Conversion Identity => new Conversion(ConversionKind.Identity); internal static Conversion ImplicitConstant => new Conversion(ConversionKind.ImplicitConstant); internal static Conversion ImplicitNumeric => new Conversion(ConversionKind.ImplicitNumeric); internal static Conversion ImplicitReference => new Conversion(ConversionKind.ImplicitReference); internal static Conversion ImplicitEnumeration => new Conversion(ConversionKind.ImplicitEnumeration); internal static Conversion ImplicitThrow => new Conversion(ConversionKind.ImplicitThrow); internal static Conversion ObjectCreation => new Conversion(ConversionKind.ObjectCreation); internal static Conversion CollectionExpression => new Conversion(ConversionKind.CollectionExpression); internal static Conversion AnonymousFunction => new Conversion(ConversionKind.AnonymousFunction); internal static Conversion Boxing => new Conversion(ConversionKind.Boxing); internal static Conversion NullLiteral => new Conversion(ConversionKind.NullLiteral); internal static Conversion DefaultLiteral => new Conversion(ConversionKind.DefaultLiteral); internal static Conversion NullToPointer => new Conversion(ConversionKind.ImplicitNullToPointer); internal static Conversion PointerToVoid => new Conversion(ConversionKind.ImplicitPointerToVoid); internal static Conversion PointerToPointer => new Conversion(ConversionKind.ExplicitPointerToPointer); internal static Conversion PointerToInteger => new Conversion(ConversionKind.ExplicitPointerToInteger); internal static Conversion IntegerToPointer => new Conversion(ConversionKind.ExplicitIntegerToPointer); internal static Conversion Unboxing => new Conversion(ConversionKind.Unboxing); internal static Conversion ExplicitReference => new Conversion(ConversionKind.ExplicitReference); internal static Conversion IntPtr => new Conversion(ConversionKind.IntPtr); internal static Conversion ExplicitEnumeration => new Conversion(ConversionKind.ExplicitEnumeration); internal static Conversion ExplicitNumeric => new Conversion(ConversionKind.ExplicitNumeric); internal static Conversion ImplicitDynamic => new Conversion(ConversionKind.ImplicitDynamic); internal static Conversion ExplicitDynamic => new Conversion(ConversionKind.ExplicitDynamic); internal static Conversion InterpolatedString => new Conversion(ConversionKind.InterpolatedString); internal static Conversion InterpolatedStringHandler => new Conversion(ConversionKind.InterpolatedStringHandler); internal static Conversion Deconstruction => new Conversion(ConversionKind.Deconstruction); internal static Conversion PinnedObjectToPointer => new Conversion(ConversionKind.PinnedObjectToPointer); internal static Conversion ImplicitPointer => new Conversion(ConversionKind.ImplicitPointer); internal static Conversion FunctionType => new Conversion(ConversionKind.FunctionType); internal static Conversion InlineArray => new Conversion(ConversionKind.InlineArray); internal static ImmutableArray IdentityUnderlying => ConversionSingletons.IdentityUnderlying; internal static ImmutableArray ImplicitConstantUnderlying => ConversionSingletons.ImplicitConstantUnderlying; internal static ImmutableArray ImplicitNumericUnderlying => ConversionSingletons.ImplicitNumericUnderlying; internal static ImmutableArray ExplicitNumericUnderlying => ConversionSingletons.ExplicitNumericUnderlying; internal static ImmutableArray ExplicitEnumerationUnderlying => ConversionSingletons.ExplicitEnumerationUnderlying; internal static ImmutableArray PointerToIntegerUnderlying => ConversionSingletons.PointerToIntegerUnderlying; internal ConversionKind Kind => _kind; internal bool IsExtensionMethod { get { if (_uncommonData is MethodUncommonData methodUncommonData) { return methodUncommonData.IsExtensionMethod; } return false; } } internal bool IsArrayIndex { get { if (_uncommonData is MethodUncommonData methodUncommonData) { return methodUncommonData.IsArrayIndex; } return false; } } internal ImmutableArray UnderlyingConversions { get { if (_uncommonData is NestedUncommonData nestedUncommonData) { return nestedUncommonData._nestedConversionsOpt; } return default(ImmutableArray); } } internal MethodSymbol? Method { get { UncommonData uncommonData = _uncommonData; if (!(uncommonData is MethodUncommonData methodUncommonData)) { if (uncommonData is DeconstructionUncommonData deconstructionUncommonData && deconstructionUncommonData.DeconstructMethodInfo.Invocation is BoundCall boundCall) { return boundCall.Method; } } else { if ((object)methodUncommonData._conversionMethod != null) { return methodUncommonData._conversionMethod; } UserDefinedConversionResult conversionResult = methodUncommonData._conversionResult; if (conversionResult.Kind == UserDefinedConversionResultKind.Valid) { return conversionResult.Results[conversionResult.Best].Operator; } } return null; } } internal TypeParameterSymbol? ConstrainedToTypeOpt { get { if (_uncommonData is MethodUncommonData { _conversionMethod: null, _conversionResult: { Kind: UserDefinedConversionResultKind.Valid } conversionResult }) { return conversionResult.Results[conversionResult.Best].ConstrainedToTypeOpt; } return null; } } internal DeconstructMethodInfo DeconstructionInfo => ((DeconstructionUncommonData)_uncommonData)?.DeconstructMethodInfo ?? default(DeconstructMethodInfo); internal ImmutableArray<(BoundValuePlaceholder? placeholder, BoundExpression? conversion)> DeconstructConversionInfo => ((DeconstructionUncommonData)_uncommonData)?.DeconstructConversionInfo ?? default(ImmutableArray<(BoundValuePlaceholder, BoundExpression)>); internal bool IsValid { get { if (!Exists) { return false; } if (_uncommonData is NestedUncommonData { _nestedConversionsOpt: { IsDefault: false } nestedConversionsOpt }) { ImmutableArray.Enumerator enumerator = nestedConversionsOpt.GetEnumerator(); while (enumerator.MoveNext()) { if (!enumerator.Current.IsValid) { return false; } } return true; } if (IsUserDefined && (object)Method == null) { MethodUncommonData obj = _uncommonData as MethodUncommonData; if (obj == null) { return false; } return obj._conversionResult.Kind == UserDefinedConversionResultKind.Valid; } return true; } } public bool Exists => Kind != ConversionKind.NoConversion; public bool IsImplicit => Kind.IsImplicitConversion(); public bool IsExplicit { get { if (Exists) { return !IsImplicit; } return false; } } public bool IsIdentity => Kind == ConversionKind.Identity; public bool IsStackAlloc { get { if (Kind != ConversionKind.StackAllocToPointerType) { return Kind == ConversionKind.StackAllocToSpanType; } return true; } } public bool IsNumeric { get { if (Kind != ConversionKind.ImplicitNumeric) { return Kind == ConversionKind.ExplicitNumeric; } return true; } } public bool IsEnumeration { get { if (Kind != ConversionKind.ImplicitEnumeration) { return Kind == ConversionKind.ExplicitEnumeration; } return true; } } public bool IsThrow => Kind == ConversionKind.ImplicitThrow; public bool IsObjectCreation => Kind == ConversionKind.ObjectCreation; public bool IsCollectionExpression => Kind == ConversionKind.CollectionExpression; public bool IsSwitchExpression => Kind == ConversionKind.SwitchExpression; public bool IsConditionalExpression => Kind == ConversionKind.ConditionalExpression; public bool IsInterpolatedString => Kind == ConversionKind.InterpolatedString; public bool IsInterpolatedStringHandler => Kind == ConversionKind.InterpolatedStringHandler; public bool IsInlineArray => Kind == ConversionKind.InlineArray; public bool IsNullable { get { if (Kind != ConversionKind.ImplicitNullable) { return Kind == ConversionKind.ExplicitNullable; } return true; } } public bool IsTupleLiteralConversion { get { if (Kind != ConversionKind.ImplicitTupleLiteral) { return Kind == ConversionKind.ExplicitTupleLiteral; } return true; } } public bool IsTupleConversion { get { if (Kind != ConversionKind.ImplicitTuple) { return Kind == ConversionKind.ExplicitTuple; } return true; } } public bool IsReference { get { if (Kind != ConversionKind.ImplicitReference) { return Kind == ConversionKind.ExplicitReference; } return true; } } public bool IsUserDefined => Kind.IsUserDefinedConversion(); public bool IsBoxing => Kind == ConversionKind.Boxing; public bool IsUnboxing => Kind == ConversionKind.Unboxing; public bool IsNullLiteral => Kind == ConversionKind.NullLiteral; public bool IsDefaultLiteral => Kind == ConversionKind.DefaultLiteral; public bool IsDynamic => Kind.IsDynamic(); public bool IsConstantExpression => Kind == ConversionKind.ImplicitConstant; public bool IsAnonymousFunction => Kind == ConversionKind.AnonymousFunction; public bool IsMethodGroup => Kind == ConversionKind.MethodGroup; public bool IsPointer => Kind.IsPointerConversion(); public bool IsIntPtr => Kind == ConversionKind.IntPtr; public IMethodSymbol? MethodSymbol => Method.GetPublicSymbol(); public ITypeSymbol? ConstrainedToType => (ITypeSymbol?)(object)ConstrainedToTypeOpt.GetPublicSymbol(); internal LookupResultKind ResultKind { get { UserDefinedConversionResult userDefinedConversionResult = (_uncommonData as MethodUncommonData)?._conversionResult ?? default(UserDefinedConversionResult); switch (userDefinedConversionResult.Kind) { case UserDefinedConversionResultKind.Valid: return LookupResultKind.Viable; case UserDefinedConversionResultKind.NoBestSourceType: case UserDefinedConversionResultKind.NoBestTargetType: case UserDefinedConversionResultKind.Ambiguous: return LookupResultKind.OverloadResolutionFailure; case UserDefinedConversionResultKind.NoApplicableOperators: if (userDefinedConversionResult.Results.IsDefaultOrEmpty) { if (Kind != ConversionKind.NoConversion) { return LookupResultKind.Viable; } return LookupResultKind.Empty; } return LookupResultKind.OverloadResolutionFailure; default: throw ExceptionUtilities.UnexpectedValue((object)userDefinedConversionResult.Kind); } } } internal Conversion UserDefinedFromConversion => BestUserDefinedConversionAnalysis?.SourceConversion ?? NoConversion; internal Conversion UserDefinedToConversion => BestUserDefinedConversionAnalysis?.TargetConversion ?? NoConversion; internal ImmutableArray OriginalUserDefinedConversions { get { if (_uncommonData is MethodUncommonData { _conversionResult: { Kind: not UserDefinedConversionResultKind.NoApplicableOperators } conversionResult }) { ArrayBuilder instance = ArrayBuilder.GetInstance(); ImmutableArray.Enumerator enumerator = conversionResult.Results.GetEnumerator(); while (enumerator.MoveNext()) { UserDefinedConversionAnalysis current = enumerator.Current; instance.Add(current.Operator); } return instance.ToImmutableAndFree(); } return ImmutableArray.Empty; } } internal UserDefinedConversionAnalysis? BestUserDefinedConversionAnalysis { get { if (_uncommonData is MethodUncommonData { _conversionResult: { Kind: UserDefinedConversionResultKind.Valid } conversionResult }) { return conversionResult.Results[conversionResult.Best]; } return null; } } internal static Conversion CreateCollectionExpressionConversion(CollectionExpressionTypeKind collectionExpressionTypeKind, TypeSymbol? elementType, ImmutableArray elementConversions) { return new Conversion(ConversionKind.CollectionExpression, new CollectionExpressionUncommonData(collectionExpressionTypeKind, elementType, elementConversions)); } private Conversion(ConversionKind kind, UncommonData? uncommonData = null) { _kind = kind; _uncommonData = uncommonData; } internal Conversion(UserDefinedConversionResult conversionResult, bool isImplicit) { _kind = ((conversionResult.Kind == UserDefinedConversionResultKind.NoApplicableOperators) ? ConversionKind.NoConversion : (isImplicit ? ConversionKind.ImplicitUserDefined : ConversionKind.ExplicitUserDefined)); _uncommonData = ((conversionResult.Kind == UserDefinedConversionResultKind.NoApplicableOperators && conversionResult.Results.IsEmpty) ? MethodUncommonData.NoApplicableOperators : new MethodUncommonData(isExtensionMethod: false, isArrayIndex: false, conversionResult, null)); } internal Conversion(ConversionKind kind, MethodSymbol conversionMethod, bool isExtensionMethod) { _kind = kind; _uncommonData = new MethodUncommonData(isExtensionMethod, isArrayIndex: false, default(UserDefinedConversionResult), conversionMethod); } internal Conversion(ConversionKind kind, ImmutableArray nestedConversions) { _kind = kind; _uncommonData = new NestedUncommonData(nestedConversions); } internal Conversion(ConversionKind kind, DeconstructMethodInfo deconstructMethodInfo, ImmutableArray<(BoundValuePlaceholder? placeholder, BoundExpression? conversion)> deconstructConversionInfo) { _kind = kind; _uncommonData = new DeconstructionUncommonData(deconstructMethodInfo, deconstructConversionInfo); } internal Conversion SetConversionMethod(MethodSymbol conversionMethod) { return new Conversion(Kind, conversionMethod, IsExtensionMethod); } internal Conversion SetArrayIndexConversionForDynamic() { return new Conversion(_kind, new MethodUncommonData(isExtensionMethod: false, isArrayIndex: true, default(UserDefinedConversionResult), null)); } [Conditional("DEBUG")] private static void AssertTrivialConversion(ConversionKind kind) { switch (kind) { } } internal static Conversion GetTrivialConversion(ConversionKind kind) { return new Conversion(kind); } internal static Conversion MakeStackAllocToPointerType(Conversion underlyingConversion) { return new Conversion(ConversionKind.StackAllocToPointerType, ImmutableArray.Create(underlyingConversion)); } internal static Conversion MakeStackAllocToSpanType(Conversion underlyingConversion) { return new Conversion(ConversionKind.StackAllocToSpanType, ImmutableArray.Create(underlyingConversion)); } internal static Conversion MakeNullableConversion(ConversionKind kind, Conversion nestedConversion) { return nestedConversion.Kind switch { ConversionKind.Identity => (kind == ConversionKind.ImplicitNullable) ? ImplicitNullableWithIdentityUnderlying : ExplicitNullableWithIdentityUnderlying, ConversionKind.ImplicitConstant => (kind == ConversionKind.ImplicitNullable) ? ImplicitNullableWithImplicitConstantUnderlying : ExplicitNullableWithImplicitConstantUnderlying, ConversionKind.ImplicitNumeric => (kind == ConversionKind.ImplicitNullable) ? ImplicitNullableWithImplicitNumericUnderlying : ExplicitNullableWithImplicitNumericUnderlying, ConversionKind.ExplicitNumeric => (kind == ConversionKind.ImplicitNullable) ? ImplicitNullableWithExplicitNumericUnderlying : ExplicitNullableWithExplicitNumericUnderlying, ConversionKind.ExplicitEnumeration => (kind == ConversionKind.ImplicitNullable) ? ImplicitNullableWithExplicitEnumerationUnderlying : ExplicitNullableWithExplicitEnumerationUnderlying, ConversionKind.ExplicitPointerToInteger => (kind == ConversionKind.ImplicitNullable) ? ImplicitNullableWithPointerToIntegerUnderlying : ExplicitNullableWithPointerToIntegerUnderlying, _ => new Conversion(kind, ImmutableArray.Create(nestedConversion)), }; } internal static Conversion MakeSwitchExpression(ImmutableArray innerConversions) { return new Conversion(ConversionKind.SwitchExpression, innerConversions); } internal static Conversion MakeConditionalExpression(ImmutableArray innerConversions) { return new Conversion(ConversionKind.ConditionalExpression, innerConversions); } [Conditional("DEBUG")] internal void AssertUnderlyingConversionsChecked() { } [Conditional("DEBUG")] internal void AssertUnderlyingConversionsCheckedRecursive() { ImmutableArray underlyingConversions = UnderlyingConversions; if (!underlyingConversions.IsDefaultOrEmpty) { ImmutableArray.Enumerator enumerator = underlyingConversions.GetEnumerator(); while (enumerator.MoveNext()) { _ = enumerator.Current; } } _ = IsUserDefined; } [Conditional("DEBUG")] internal void MarkUnderlyingConversionsChecked() { } [Conditional("DEBUG")] internal void MarkUnderlyingConversionsCheckedRecursive() { } internal CollectionExpressionTypeKind GetCollectionExpressionTypeKind(out TypeSymbol? elementType) { if (_uncommonData is CollectionExpressionUncommonData collectionExpressionUncommonData) { elementType = collectionExpressionUncommonData.ElementType; return collectionExpressionUncommonData.CollectionExpressionTypeKind; } elementType = null; return CollectionExpressionTypeKind.None; } public CommonConversion ToCommonConversion() { //IL_0046: Unknown result type (might be due to invalid IL or missing references) IMethodSymbol val; ITypeSymbol val2; if (!IsUserDefined) { val = null; val2 = null; } else { IMethodSymbol? methodSymbol = MethodSymbol; ITypeSymbol constrainedToType = ConstrainedToType; val2 = constrainedToType; val = methodSymbol; } return new CommonConversion(Exists, IsIdentity, IsNumeric, IsReference, IsImplicit, IsNullable, val, val2); } public override string ToString() { return Kind.ToString(); } public override bool Equals(object? obj) { if (obj is Conversion) { return Equals((Conversion)obj); } return false; } public bool Equals(Conversion other) { if (Kind == other.Kind) { return Method == other.Method; } return false; } public override int GetHashCode() { return Hash.Combine(Method, (int)Kind); } public static bool operator ==(Conversion left, Conversion right) { return left.Equals(right); } public static bool operator !=(Conversion left, Conversion right) { return !(left == right); } static Conversion() { ExplicitNullableWithExplicitEnumerationUnderlying = new Conversion(ConversionKind.ExplicitNullable, ExplicitEnumerationUnderlying); ExplicitNullableWithPointerToIntegerUnderlying = new Conversion(ConversionKind.ExplicitNullable, PointerToIntegerUnderlying); ExplicitNullableWithIdentityUnderlying = new Conversion(ConversionKind.ExplicitNullable, IdentityUnderlying); ExplicitNullableWithImplicitNumericUnderlying = new Conversion(ConversionKind.ExplicitNullable, ImplicitNumericUnderlying); ExplicitNullableWithExplicitNumericUnderlying = new Conversion(ConversionKind.ExplicitNullable, ExplicitNumericUnderlying); ExplicitNullableWithImplicitConstantUnderlying = new Conversion(ConversionKind.ExplicitNullable, ImplicitConstantUnderlying); ImplicitNullableWithExplicitEnumerationUnderlying = new Conversion(ConversionKind.ImplicitNullable, ExplicitEnumerationUnderlying); ImplicitNullableWithPointerToIntegerUnderlying = new Conversion(ConversionKind.ImplicitNullable, PointerToIntegerUnderlying); ImplicitNullableWithIdentityUnderlying = new Conversion(ConversionKind.ImplicitNullable, IdentityUnderlying); ImplicitNullableWithImplicitNumericUnderlying = new Conversion(ConversionKind.ImplicitNullable, ImplicitNumericUnderlying); ImplicitNullableWithExplicitNumericUnderlying = new Conversion(ConversionKind.ImplicitNullable, ExplicitNumericUnderlying); ImplicitNullableWithImplicitConstantUnderlying = new Conversion(ConversionKind.ImplicitNullable, ImplicitConstantUnderlying); } }