mirror of https://github.com/icsharpcode/ILSpy.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
639 lines
18 KiB
639 lines
18 KiB
// Copyright (c) 2010-2013 AlphaSierraPapa for the SharpDevelop Team |
|
// |
|
// Permission is hereby granted, free of charge, to any person obtaining a copy of this |
|
// software and associated documentation files (the "Software"), to deal in the Software |
|
// without restriction, including without limitation the rights to use, copy, modify, merge, |
|
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons |
|
// to whom the Software is furnished to do so, subject to the following conditions: |
|
// |
|
// The above copyright notice and this permission notice shall be included in all copies or |
|
// substantial portions of the Software. |
|
// |
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, |
|
// INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR |
|
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE |
|
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR |
|
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER |
|
// DEALINGS IN THE SOFTWARE. |
|
|
|
using System; |
|
using System.Collections.Immutable; |
|
using System.Linq; |
|
|
|
using ICSharpCode.Decompiler.TypeSystem; |
|
|
|
namespace ICSharpCode.Decompiler.Semantics |
|
{ |
|
/// <summary> |
|
/// Holds information about a conversion between two types. |
|
/// </summary> |
|
public abstract class Conversion : IEquatable<Conversion> |
|
{ |
|
#region Conversion factory methods |
|
/// <summary> |
|
/// Not a valid conversion. |
|
/// </summary> |
|
public static readonly Conversion None = new InvalidConversion(); |
|
|
|
/// <summary> |
|
/// Identity conversion. |
|
/// </summary> |
|
public static readonly Conversion IdentityConversion = new BuiltinConversion(true, 0); |
|
|
|
public static readonly Conversion ImplicitNumericConversion = new NumericOrEnumerationConversion(true, false); |
|
public static readonly Conversion ExplicitNumericConversion = new NumericOrEnumerationConversion(false, false); |
|
public static readonly Conversion ImplicitLiftedNumericConversion = new NumericOrEnumerationConversion(true, true); |
|
public static readonly Conversion ExplicitLiftedNumericConversion = new NumericOrEnumerationConversion(false, true); |
|
|
|
public static Conversion EnumerationConversion(bool isImplicit, bool isLifted) |
|
{ |
|
return new NumericOrEnumerationConversion(isImplicit, isLifted, true); |
|
} |
|
|
|
public static readonly Conversion NullLiteralConversion = new BuiltinConversion(true, 1); |
|
|
|
/// <summary> |
|
/// The numeric conversion of a constant expression. |
|
/// </summary> |
|
public static readonly Conversion ImplicitConstantExpressionConversion = new BuiltinConversion(true, 2); |
|
|
|
public static readonly Conversion ImplicitReferenceConversion = new BuiltinConversion(true, 3); |
|
public static readonly Conversion ExplicitReferenceConversion = new BuiltinConversion(false, 3); |
|
|
|
public static readonly Conversion ImplicitDynamicConversion = new BuiltinConversion(true, 4); |
|
public static readonly Conversion ExplicitDynamicConversion = new BuiltinConversion(false, 4); |
|
|
|
public static readonly Conversion ImplicitNullableConversion = new BuiltinConversion(true, 5); |
|
public static readonly Conversion ExplicitNullableConversion = new BuiltinConversion(false, 5); |
|
|
|
public static readonly Conversion ImplicitPointerConversion = new BuiltinConversion(true, 6); |
|
public static readonly Conversion ExplicitPointerConversion = new BuiltinConversion(false, 6); |
|
|
|
public static readonly Conversion BoxingConversion = new BuiltinConversion(true, 7); |
|
public static readonly Conversion UnboxingConversion = new BuiltinConversion(false, 8); |
|
|
|
/// <summary> |
|
/// C# 'as' cast. |
|
/// </summary> |
|
public static readonly Conversion TryCast = new BuiltinConversion(false, 9); |
|
|
|
/// <summary> |
|
/// C# 6 string interpolation expression implicitly being converted to <see cref="System.IFormattable"/> or <see cref="System.FormattableString"/>. |
|
/// </summary> |
|
public static readonly Conversion ImplicitInterpolatedStringConversion = new BuiltinConversion(true, 10); |
|
|
|
/// <summary> |
|
/// C# 7 throw expression being converted to an arbitrary type. |
|
/// </summary> |
|
public static readonly Conversion ThrowExpressionConversion = new BuiltinConversion(true, 11); |
|
|
|
public static Conversion UserDefinedConversion(IMethod operatorMethod, bool isImplicit, Conversion conversionBeforeUserDefinedOperator, Conversion conversionAfterUserDefinedOperator, bool isLifted = false, bool isAmbiguous = false) |
|
{ |
|
if (operatorMethod == null) |
|
throw new ArgumentNullException(nameof(operatorMethod)); |
|
return new UserDefinedConv(isImplicit, operatorMethod, conversionBeforeUserDefinedOperator, conversionAfterUserDefinedOperator, isLifted, isAmbiguous); |
|
} |
|
|
|
public static Conversion MethodGroupConversion(IMethod chosenMethod, bool isVirtualMethodLookup, bool delegateCapturesFirstArgument) |
|
{ |
|
if (chosenMethod == null) |
|
throw new ArgumentNullException(nameof(chosenMethod)); |
|
return new MethodGroupConv(chosenMethod, isVirtualMethodLookup, delegateCapturesFirstArgument, isValid: true); |
|
} |
|
|
|
public static Conversion InvalidMethodGroupConversion(IMethod chosenMethod, bool isVirtualMethodLookup, bool delegateCapturesFirstArgument) |
|
{ |
|
if (chosenMethod == null) |
|
throw new ArgumentNullException(nameof(chosenMethod)); |
|
return new MethodGroupConv(chosenMethod, isVirtualMethodLookup, delegateCapturesFirstArgument, isValid: false); |
|
} |
|
|
|
public static Conversion TupleConversion(ImmutableArray<Conversion> conversions) |
|
{ |
|
return new TupleConv(conversions); |
|
} |
|
#endregion |
|
|
|
#region Inner classes |
|
sealed class InvalidConversion : Conversion |
|
{ |
|
public override bool IsValid { |
|
get { return false; } |
|
} |
|
|
|
public override string ToString() |
|
{ |
|
return "None"; |
|
} |
|
} |
|
|
|
sealed class NumericOrEnumerationConversion : Conversion |
|
{ |
|
readonly bool isImplicit; |
|
readonly bool isLifted; |
|
readonly bool isEnumeration; |
|
|
|
public NumericOrEnumerationConversion(bool isImplicit, bool isLifted, bool isEnumeration = false) |
|
{ |
|
this.isImplicit = isImplicit; |
|
this.isLifted = isLifted; |
|
this.isEnumeration = isEnumeration; |
|
} |
|
|
|
public override bool IsImplicit { |
|
get { return isImplicit; } |
|
} |
|
|
|
public override bool IsExplicit { |
|
get { return !isImplicit; } |
|
} |
|
|
|
public override bool IsNumericConversion { |
|
get { return !isEnumeration; } |
|
} |
|
|
|
public override bool IsEnumerationConversion { |
|
get { return isEnumeration; } |
|
} |
|
|
|
public override bool IsLifted { |
|
get { return isLifted; } |
|
} |
|
|
|
public override string ToString() |
|
{ |
|
return (isImplicit ? "implicit" : "explicit") |
|
+ (isLifted ? " lifted" : "") |
|
+ (isEnumeration ? " enumeration" : " numeric") |
|
+ " conversion"; |
|
} |
|
|
|
public override bool Equals(Conversion other) |
|
{ |
|
NumericOrEnumerationConversion o = other as NumericOrEnumerationConversion; |
|
return o != null && isImplicit == o.isImplicit && isLifted == o.isLifted && isEnumeration == o.isEnumeration; |
|
} |
|
|
|
public override int GetHashCode() |
|
{ |
|
return (isImplicit ? 1 : 0) + (isLifted ? 2 : 0) + (isEnumeration ? 4 : 0); |
|
} |
|
} |
|
|
|
sealed class BuiltinConversion : Conversion |
|
{ |
|
readonly bool isImplicit; |
|
readonly byte type; |
|
|
|
public BuiltinConversion(bool isImplicit, byte type) |
|
{ |
|
this.isImplicit = isImplicit; |
|
this.type = type; |
|
} |
|
|
|
public override bool IsImplicit { |
|
get { return isImplicit; } |
|
} |
|
|
|
public override bool IsExplicit { |
|
get { return !isImplicit; } |
|
} |
|
|
|
public override bool IsIdentityConversion { |
|
get { return type == 0; } |
|
} |
|
|
|
public override bool IsNullLiteralConversion { |
|
get { return type == 1; } |
|
} |
|
|
|
public override bool IsConstantExpressionConversion { |
|
get { return type == 2; } |
|
} |
|
|
|
public override bool IsReferenceConversion { |
|
get { return type == 3; } |
|
} |
|
|
|
public override bool IsDynamicConversion { |
|
get { return type == 4; } |
|
} |
|
|
|
public override bool IsNullableConversion { |
|
get { return type == 5; } |
|
} |
|
|
|
public override bool IsPointerConversion { |
|
get { return type == 6; } |
|
} |
|
|
|
public override bool IsBoxingConversion { |
|
get { return type == 7; } |
|
} |
|
|
|
public override bool IsUnboxingConversion { |
|
get { return type == 8; } |
|
} |
|
|
|
public override bool IsTryCast { |
|
get { return type == 9; } |
|
} |
|
|
|
public override bool IsInterpolatedStringConversion => type == 10; |
|
|
|
public override bool IsThrowExpressionConversion { |
|
get { return type == 11; } |
|
} |
|
|
|
public override string ToString() |
|
{ |
|
string name = null; |
|
switch (type) |
|
{ |
|
case 0: |
|
return "identity conversion"; |
|
case 1: |
|
return "null-literal conversion"; |
|
case 2: |
|
name = "constant-expression"; |
|
break; |
|
case 3: |
|
name = "reference"; |
|
break; |
|
case 4: |
|
name = "dynamic"; |
|
break; |
|
case 5: |
|
name = "nullable"; |
|
break; |
|
case 6: |
|
name = "pointer"; |
|
break; |
|
case 7: |
|
return "boxing conversion"; |
|
case 8: |
|
return "unboxing conversion"; |
|
case 9: |
|
return "try cast"; |
|
case 10: |
|
return "interpolated string"; |
|
case 11: |
|
return "throw-expression conversion"; |
|
} |
|
return (isImplicit ? "implicit " : "explicit ") + name + " conversion"; |
|
} |
|
} |
|
|
|
sealed class UserDefinedConv : Conversion |
|
{ |
|
readonly IMethod method; |
|
readonly bool isLifted; |
|
readonly Conversion conversionBeforeUserDefinedOperator; |
|
readonly Conversion conversionAfterUserDefinedOperator; |
|
readonly bool isImplicit; |
|
readonly bool isValid; |
|
|
|
public UserDefinedConv(bool isImplicit, IMethod method, Conversion conversionBeforeUserDefinedOperator, Conversion conversionAfterUserDefinedOperator, bool isLifted, bool isAmbiguous) |
|
{ |
|
this.method = method; |
|
this.isLifted = isLifted; |
|
this.conversionBeforeUserDefinedOperator = conversionBeforeUserDefinedOperator; |
|
this.conversionAfterUserDefinedOperator = conversionAfterUserDefinedOperator; |
|
this.isImplicit = isImplicit; |
|
this.isValid = !isAmbiguous; |
|
} |
|
|
|
public override bool IsValid { |
|
get { return isValid; } |
|
} |
|
|
|
public override bool IsImplicit { |
|
get { return isImplicit; } |
|
} |
|
|
|
public override bool IsExplicit { |
|
get { return !isImplicit; } |
|
} |
|
|
|
public override bool IsLifted { |
|
get { return isLifted; } |
|
} |
|
|
|
public override bool IsUserDefined { |
|
get { return true; } |
|
} |
|
|
|
public override Conversion ConversionBeforeUserDefinedOperator { |
|
get { return conversionBeforeUserDefinedOperator; } |
|
} |
|
|
|
public override Conversion ConversionAfterUserDefinedOperator { |
|
get { return conversionAfterUserDefinedOperator; } |
|
} |
|
|
|
public override IMethod Method { |
|
get { return method; } |
|
} |
|
|
|
public override bool Equals(Conversion other) |
|
{ |
|
UserDefinedConv o = other as UserDefinedConv; |
|
return o != null && isLifted == o.isLifted && isImplicit == o.isImplicit && isValid == o.isValid && method.Equals(o.method); |
|
} |
|
|
|
public override int GetHashCode() |
|
{ |
|
return unchecked(method.GetHashCode() + (isLifted ? 31 : 27) + (isImplicit ? 71 : 61) + (isValid ? 107 : 109)); |
|
} |
|
|
|
public override string ToString() |
|
{ |
|
return (isImplicit ? "implicit" : "explicit") |
|
+ (isLifted ? " lifted" : "") |
|
+ (isValid ? "" : " ambiguous") |
|
+ "user-defined conversion (" + method + ")"; |
|
} |
|
} |
|
|
|
sealed class MethodGroupConv : Conversion |
|
{ |
|
readonly IMethod method; |
|
readonly bool isVirtualMethodLookup; |
|
readonly bool delegateCapturesFirstArgument; |
|
readonly bool isValid; |
|
|
|
public MethodGroupConv(IMethod method, bool isVirtualMethodLookup, bool delegateCapturesFirstArgument, bool isValid) |
|
{ |
|
this.method = method; |
|
this.isVirtualMethodLookup = isVirtualMethodLookup; |
|
this.delegateCapturesFirstArgument = delegateCapturesFirstArgument; |
|
this.isValid = isValid; |
|
} |
|
|
|
public override bool IsValid { |
|
get { return isValid; } |
|
} |
|
|
|
public override bool IsImplicit { |
|
get { return true; } |
|
} |
|
|
|
public override bool IsMethodGroupConversion { |
|
get { return true; } |
|
} |
|
|
|
public override bool IsVirtualMethodLookup { |
|
get { return isVirtualMethodLookup; } |
|
} |
|
|
|
public override bool DelegateCapturesFirstArgument { |
|
get { return delegateCapturesFirstArgument; } |
|
} |
|
|
|
public override IMethod Method { |
|
get { return method; } |
|
} |
|
|
|
public override bool Equals(Conversion other) |
|
{ |
|
MethodGroupConv o = other as MethodGroupConv; |
|
return o != null && method.Equals(o.method); |
|
} |
|
|
|
public override int GetHashCode() |
|
{ |
|
return method.GetHashCode(); |
|
} |
|
} |
|
|
|
sealed class TupleConv : Conversion |
|
{ |
|
public override bool IsImplicit { get; } |
|
public override bool IsExplicit => !IsImplicit; |
|
public override ImmutableArray<Conversion> ElementConversions { get; } |
|
public override bool IsTupleConversion => true; |
|
|
|
public TupleConv(ImmutableArray<Conversion> elementConversions) |
|
{ |
|
this.ElementConversions = elementConversions; |
|
this.IsImplicit = elementConversions.All(c => c.IsImplicit); |
|
} |
|
|
|
public override bool Equals(Conversion other) |
|
{ |
|
return other is TupleConv o |
|
&& ElementConversions.SequenceEqual(o.ElementConversions); |
|
} |
|
|
|
public override int GetHashCode() |
|
{ |
|
unchecked |
|
{ |
|
int hash = 0; |
|
foreach (var conv in ElementConversions) |
|
{ |
|
hash *= 31; |
|
hash += conv.GetHashCode(); |
|
} |
|
return hash; |
|
} |
|
} |
|
|
|
public override string ToString() |
|
{ |
|
return (IsImplicit ? "implicit " : "explicit ") + " tuple conversion"; |
|
} |
|
} |
|
#endregion |
|
|
|
/// <summary> |
|
/// Gets whether the conversion is valid. |
|
/// </summary> |
|
public virtual bool IsValid { |
|
get { return true; } |
|
} |
|
|
|
public virtual bool IsImplicit { |
|
get { return false; } |
|
} |
|
|
|
public virtual bool IsExplicit { |
|
get { return false; } |
|
} |
|
|
|
/// <summary> |
|
/// Gets whether the conversion is an '<c>as</c>' cast. |
|
/// </summary> |
|
public virtual bool IsTryCast { |
|
get { return false; } |
|
} |
|
|
|
public virtual bool IsThrowExpressionConversion { |
|
get { return false; } |
|
} |
|
|
|
public virtual bool IsIdentityConversion { |
|
get { return false; } |
|
} |
|
|
|
public virtual bool IsNullLiteralConversion { |
|
get { return false; } |
|
} |
|
|
|
public virtual bool IsConstantExpressionConversion { |
|
get { return false; } |
|
} |
|
|
|
public virtual bool IsNumericConversion { |
|
get { return false; } |
|
} |
|
|
|
/// <summary> |
|
/// Gets whether this conversion is a lifted version of another conversion. |
|
/// </summary> |
|
public virtual bool IsLifted { |
|
get { return false; } |
|
} |
|
|
|
/// <summary> |
|
/// Gets whether the conversion is dynamic. |
|
/// </summary> |
|
public virtual bool IsDynamicConversion { |
|
get { return false; } |
|
} |
|
|
|
/// <summary> |
|
/// Gets whether the conversion is a reference conversion. |
|
/// </summary> |
|
public virtual bool IsReferenceConversion { |
|
get { return false; } |
|
} |
|
|
|
/// <summary> |
|
/// Gets whether the conversion is an enumeration conversion. |
|
/// </summary> |
|
public virtual bool IsEnumerationConversion { |
|
get { return false; } |
|
} |
|
|
|
/// <summary> |
|
/// Gets whether the conversion is a nullable conversion |
|
/// (conversion between a nullable type and the regular type). |
|
/// </summary> |
|
public virtual bool IsNullableConversion { |
|
get { return false; } |
|
} |
|
|
|
/// <summary> |
|
/// Gets whether this conversion is user-defined (op_Implicit or op_Explicit). |
|
/// </summary> |
|
public virtual bool IsUserDefined { |
|
get { return false; } |
|
} |
|
|
|
/// <summary> |
|
/// The conversion that is applied to the input before the user-defined conversion operator is invoked. |
|
/// </summary> |
|
public virtual Conversion ConversionBeforeUserDefinedOperator { |
|
get { return null; } |
|
} |
|
|
|
/// <summary> |
|
/// The conversion that is applied to the result of the user-defined conversion operator. |
|
/// </summary> |
|
public virtual Conversion ConversionAfterUserDefinedOperator { |
|
get { return null; } |
|
} |
|
|
|
/// <summary> |
|
/// Gets whether this conversion is a boxing conversion. |
|
/// </summary> |
|
public virtual bool IsBoxingConversion { |
|
get { return false; } |
|
} |
|
|
|
/// <summary> |
|
/// Gets whether this conversion is an unboxing conversion. |
|
/// </summary> |
|
public virtual bool IsUnboxingConversion { |
|
get { return false; } |
|
} |
|
|
|
/// <summary> |
|
/// Gets whether this conversion is a pointer conversion. |
|
/// </summary> |
|
public virtual bool IsPointerConversion { |
|
get { return false; } |
|
} |
|
|
|
/// <summary> |
|
/// Gets whether this conversion is a method group conversion. |
|
/// </summary> |
|
public virtual bool IsMethodGroupConversion { |
|
get { return false; } |
|
} |
|
|
|
/// <summary> |
|
/// For method-group conversions, gets whether to perform a virtual method lookup at runtime. |
|
/// </summary> |
|
public virtual bool IsVirtualMethodLookup { |
|
get { return false; } |
|
} |
|
|
|
/// <summary> |
|
/// For method-group conversions, gets whether the conversion captures the first argument. |
|
/// |
|
/// For instance methods, this property always returns true for C# method-group conversions. |
|
/// For static methods, this property returns true for method-group conversions of an extension method performed on an instance (eg. <c>Func<int> f = myEnumerable.Single</c>). |
|
/// </summary> |
|
public virtual bool DelegateCapturesFirstArgument { |
|
get { return false; } |
|
} |
|
|
|
/// <summary> |
|
/// Gets whether this conversion is an anonymous function conversion. |
|
/// </summary> |
|
public virtual bool IsAnonymousFunctionConversion { |
|
get { return false; } |
|
} |
|
|
|
/// <summary> |
|
/// Gets the method associated with this conversion. |
|
/// For user-defined conversions, this is the method being called. |
|
/// For method-group conversions, this is the method that was chosen from the group. |
|
/// </summary> |
|
public virtual IMethod Method { |
|
get { return null; } |
|
} |
|
|
|
/// <summary> |
|
/// Gets whether this conversion is a tuple conversion. |
|
/// </summary> |
|
public virtual bool IsTupleConversion => false; |
|
|
|
/// <summary> |
|
/// Gets whether this is an interpolated string conversion to <see cref="IFormattable" /> or <see cref="FormattableString"/>. |
|
/// </summary> |
|
public virtual bool IsInterpolatedStringConversion => false; |
|
|
|
/// <summary> |
|
/// For a tuple conversion, gets the individual tuple element conversions. |
|
/// </summary> |
|
public virtual ImmutableArray<Conversion> ElementConversions => default(ImmutableArray<Conversion>); |
|
|
|
public override sealed bool Equals(object obj) |
|
{ |
|
return Equals(obj as Conversion); |
|
} |
|
|
|
public override int GetHashCode() |
|
{ |
|
return base.GetHashCode(); |
|
} |
|
|
|
public virtual bool Equals(Conversion other) |
|
{ |
|
return this == other; |
|
} |
|
} |
|
}
|
|
|