// 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 ICSharpCode.NRefactory.TypeSystem;
namespace ICSharpCode.NRefactory.Semantics
{
///
/// Holds information about a conversion between two types.
///
public abstract class Conversion : IEquatable
{
#region Conversion factory methods
///
/// Not a valid conversion.
///
public static readonly Conversion None = new InvalidConversion();
///
/// Identity conversion.
///
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);
///
/// The numeric conversion of a constant expression.
///
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);
///
/// C# 'as' cast.
///
public static readonly Conversion TryCast = new BuiltinConversion(false, 9);
[Obsolete("Use UserDefinedConversion() instead")]
public static Conversion UserDefinedImplicitConversion(IMethod operatorMethod, Conversion conversionBeforeUserDefinedOperator, Conversion conversionAfterUserDefinedOperator, bool isLifted)
{
if (operatorMethod == null)
throw new ArgumentNullException("operatorMethod");
return new UserDefinedConv(true, operatorMethod, conversionBeforeUserDefinedOperator, conversionAfterUserDefinedOperator, isLifted, false);
}
[Obsolete("Use UserDefinedConversion() instead")]
public static Conversion UserDefinedExplicitConversion(IMethod operatorMethod, Conversion conversionBeforeUserDefinedOperator, Conversion conversionAfterUserDefinedOperator, bool isLifted)
{
if (operatorMethod == null)
throw new ArgumentNullException("operatorMethod");
return new UserDefinedConv(false, operatorMethod, conversionBeforeUserDefinedOperator, conversionAfterUserDefinedOperator, isLifted, false);
}
public static Conversion UserDefinedConversion(IMethod operatorMethod, bool isImplicit, Conversion conversionBeforeUserDefinedOperator, Conversion conversionAfterUserDefinedOperator, bool isLifted = false, bool isAmbiguous = false)
{
if (operatorMethod == null)
throw new ArgumentNullException("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("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("chosenMethod");
return new MethodGroupConv(chosenMethod, isVirtualMethodLookup, delegateCapturesFirstArgument, isValid: false);
}
#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 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";
}
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();
}
}
#endregion
///
/// Gets whether the conversion is valid.
///
public virtual bool IsValid {
get { return true; }
}
public virtual bool IsImplicit {
get { return false; }
}
public virtual bool IsExplicit {
get { return false; }
}
///
/// Gets whether the conversion is an 'as' cast.
///
public virtual bool IsTryCast {
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; }
}
///
/// Gets whether this conversion is a lifted version of another conversion.
///
public virtual bool IsLifted {
get { return false; }
}
///
/// Gets whether the conversion is dynamic.
///
public virtual bool IsDynamicConversion {
get { return false; }
}
///
/// Gets whether the conversion is a reference conversion.
///
public virtual bool IsReferenceConversion {
get { return false; }
}
///
/// Gets whether the conversion is an enumeration conversion.
///
public virtual bool IsEnumerationConversion {
get { return false; }
}
///
/// Gets whether the conversion is a nullable conversion
/// (conversion between a nullable type and the regular type).
///
public virtual bool IsNullableConversion {
get { return false; }
}
///
/// Gets whether this conversion is user-defined (op_Implicit or op_Explicit).
///
public virtual bool IsUserDefined {
get { return false; }
}
///
/// The conversion that is applied to the input before the user-defined conversion operator is invoked.
///
public virtual Conversion ConversionBeforeUserDefinedOperator {
get { return null; }
}
///
/// The conversion that is applied to the result of the user-defined conversion operator.
///
public virtual Conversion ConversionAfterUserDefinedOperator {
get { return null; }
}
///
/// Gets whether this conversion is a boxing conversion.
///
public virtual bool IsBoxingConversion {
get { return false; }
}
///
/// Gets whether this conversion is an unboxing conversion.
///
public virtual bool IsUnboxingConversion {
get { return false; }
}
///
/// Gets whether this conversion is a pointer conversion.
///
public virtual bool IsPointerConversion {
get { return false; }
}
///
/// Gets whether this conversion is a method group conversion.
///
public virtual bool IsMethodGroupConversion {
get { return false; }
}
///
/// For method-group conversions, gets whether to perform a virtual method lookup at runtime.
///
public virtual bool IsVirtualMethodLookup {
get { return false; }
}
///
/// 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. Func<int> f = myEnumerable.Single).
///
public virtual bool DelegateCapturesFirstArgument {
get { return false; }
}
///
/// Gets whether this conversion is an anonymous function conversion.
///
public virtual bool IsAnonymousFunctionConversion {
get { return false; }
}
///
/// 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.
///
public virtual IMethod Method {
get { return null; }
}
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;
}
}
}