Expression TryCompileCast(Source src, DataType targetType, Expression operand) { if (targetType.IsInvalid || operand.IsInvalid) { return(Expression.Invalid); } // Equal types == NOP if (targetType.Equals(operand.ReturnType)) { return(operand); } var cast = TryResolveCastOverload(src, NameResolver.GetTypeCasts(targetType, operand.ReturnType), ref operand); if (cast != null) { return(new CallCast(src, cast, operand)); } // Box // Generic -> object // Method -> delegate // etc var impl = TryCompileImplicitCast(src, targetType, operand); if (impl != null) { return(impl); } // Down cast if (targetType.IsReferenceType && operand.ReturnType.IsReferenceType || targetType.IsInterface || operand.ReturnType.IsInterface) { return(operand.ReturnType.IsInterface || operand.ReturnType.IsSubclassOf(targetType) || operand.ReturnType.IsImplementingInterface(targetType) || targetType.IsInterface || targetType.IsSubclassOf(operand.ReturnType) || targetType.IsImplementingInterface(operand.ReturnType) ? new CastOp(src, targetType, operand) : Error(src, ErrorCode.E2028, targetType.Quote() + " and " + operand.ReturnType.Quote() + " are not compatible types")); } // Unbox // Ref -> generic if (targetType.IsValueType && operand.ReturnType.Equals(Essentials.Object) || targetType.IsGenericParameter && operand.ReturnType.IsReferenceType) { return(new CastOp(src, targetType, operand)); } if (operand.ReturnType.IsEnum) { operand.ReturnType.AssignBaseType(); return(TryCompileCast(src, targetType, new CastOp(src, operand.ReturnType.Base ?? Essentials.Int, operand))); } if (targetType.IsEnum) { targetType.AssignBaseType(); var intValue = TryCompileCast(src, targetType.Base ?? Essentials.Int, operand); if (intValue != null) { return(new CastOp(src, targetType, intValue)); } } return(null); }
Expression TryCompileImplicitCast(Source src, DataType expectedType, Expression value, bool reportErrorIfNotFound) { if (ImplicitCastStack.Contains(expectedType)) { ImplicitCastStack.Add(expectedType); } else { ImplicitCastStack.Add(expectedType); if (Equals(expectedType, value.ReturnType)) { return(value); } if (value.ReturnType is InvalidType || expectedType is InvalidType) { return(Expression.Invalid); } // (EnumType)0 if (expectedType is EnumType && value is Constant && value.ConstantValue is int && (int)value.ConstantValue == 0) { return(new Constant(value.Source, (EnumType)expectedType, (int)value.ConstantValue)); } // Null type (must be done before delegate) if (value.ReturnType.IsNull) { return(expectedType.IsReferenceType ? new Constant(src, expectedType, null) : !reportErrorIfNotFound ? null : Error(src, ErrorCode.E2043, "'<null>' has no implicit cast to " + expectedType.Quote() + " because that is not a reference type")); } // Delegate if (expectedType is DelegateType && value is MethodGroup) { var dt = expectedType as DelegateType; dt.AssignBaseType(); var mg = value as MethodGroup; var candidates = mg.Candidates; var compatibleMethods = new List <Method>(); foreach (var m in candidates) { if (!m.IsGenericDefinition && m.ReturnType.Equals(dt.ReturnType) && dt.CompareParameters(m)) { compatibleMethods.Add(m); } } if (compatibleMethods.Count == 0) { foreach (var c in candidates) { if (c.IsGenericDefinition) { var dummyArgs = new Expression[dt.Parameters.Length]; for (int i = 0; i < dummyArgs.Length; i++) { var p = dt.Parameters[i]; dummyArgs[i] = new Default(p.Source, p.Type); switch (p.Modifier) { case ParameterModifier.Const: dummyArgs[i] = new AddressOf(dummyArgs[i], AddressType.Const); break; case ParameterModifier.Ref: dummyArgs[i] = new AddressOf(dummyArgs[i], AddressType.Ref); break; case ParameterModifier.Out: dummyArgs[i] = new AddressOf(dummyArgs[i], AddressType.Out); break; } } candidates = CopyAndParameterizeGenericMethods(src, candidates, dummyArgs); foreach (var m in candidates) { if (!m.IsGenericDefinition && m.ReturnType.Equals(dt.ReturnType) && dt.CompareParameters(m)) { compatibleMethods.Add(m); } } break; } } if (compatibleMethods.Count == 0) { foreach (var m in candidates) { if (!m.IsGenericDefinition && (m.ReturnType.Equals(dt.ReturnType) || m.ReturnType.IsReferenceType && m.ReturnType.IsSubclassOfOrEqual(dt.ReturnType)) && dt.CompareParametersEqualOrSubclassOf(m)) { compatibleMethods.Add(m); } } } } if (compatibleMethods.Count == 1) { var m = compatibleMethods[0]; if (m.IsStatic) { return(new NewDelegate(src, dt, null, m)); } if (mg.Object != null && !mg.Object.ReturnType.IsReferenceType) { mg.Object = new CastOp(src, Essentials.Object, mg.Object.ActualValue); } return(new NewDelegate(src, dt, mg.Object, m)); } return(!reportErrorIfNotFound ? null : compatibleMethods.Count == 0 ? Error(src, ErrorCode.E2045, "No methods matches the parameter list and return type of delegate type " + dt.Quote()) : ReportAmbiguousMatchError(src, compatibleMethods)); } // Lambda if (expectedType is DelegateType && value is UncompiledLambda) { expectedType.AssignBaseType(); return(TryCompileImplicitLambdaCast((UncompiledLambda)value, (DelegateType)expectedType)); } // Constant if (value is Constant && expectedType.IsIntrinsic) { var constant = value as Constant; if (constant.Value is int) { int intValue = (int)constant.Value; switch (expectedType.BuiltinType) { case BuiltinType.SByte: if (intValue >= -0x80 && intValue <= 0x7f) { return(new Constant(constant.Source, expectedType, (sbyte)intValue)); } break; case BuiltinType.Byte: if (intValue >= 0 && intValue <= 0xff) { return(new Constant(constant.Source, expectedType, (byte)intValue)); } break; case BuiltinType.Short: if (intValue >= -0x8000 && intValue <= 0x7fff) { return(new Constant(constant.Source, expectedType, (short)intValue)); } break; case BuiltinType.UShort: if (intValue >= 0 && intValue <= 0xffff) { return(new Constant(constant.Source, expectedType, (ushort)intValue)); } break; case BuiltinType.UInt: if (intValue >= 0) { return(new Constant(constant.Source, expectedType, (uint)intValue)); } break; case BuiltinType.ULong: if (intValue >= 0) { return(new Constant(constant.Source, expectedType, (ulong)intValue)); } break; } } if (constant.Value is long) { long longValue = (long)constant.Value; switch (expectedType.BuiltinType) { case BuiltinType.ULong: if (longValue >= 0) { return(new Constant(constant.Source, expectedType, (ulong)longValue)); } break; } } } // Implict cast bool ambiguous; var cast = TryResolveCastOverload(src, NameResolver.GetTypeCasts(expectedType, value.ReturnType), ref value, reportErrorIfNotFound, out ambiguous); if (ambiguous) { goto ERROR; } if (cast != null && cast.IsImplicitCast) { return(new CallCast(src, cast, value)); } // Up cast if (expectedType == Essentials.Object || value.ReturnType.IsSubclassOf(expectedType) || expectedType.IsInterface && value.ReturnType.IsImplementingInterface(expectedType)) { return(new CastOp(src, expectedType, value)); } // Fixed Array if (expectedType.IsFixedArray && value.ReturnType.IsFixedArray) { var t1 = expectedType as FixedArrayType; var t2 = value.ReturnType as FixedArrayType; if (t1.ElementType.Equals(t2.ElementType) && ( t1.OptionalSize == null || t2.OptionalSize == null || t1.OptionalSize is Constant && t2.OptionalSize is Constant && Equals(t1.OptionalSize.ConstantValue, t2.OptionalSize.ConstantValue) )) { return(value); } } // T[] -> IEnumerable<T> if (value.ReturnType.IsRefArray && expectedType.MasterDefinition == Essentials.IEnumerable_T) { ImplicitCastStack.RemoveLast(); var arrayEnumerable = TypeBuilder.Parameterize(src, Essentials.ArrayEnumerable_T, value.ReturnType.ElementType); return(TryCompileImplicitCast(src, expectedType, ILFactory.NewObject(src, arrayEnumerable, value), reportErrorIfNotFound)); } } ERROR: return(!reportErrorIfNotFound ? null : Error(src, ErrorCode.E2047, "No implicit cast from " + value.ReturnType.Quote() + " to " + expectedType.Quote())); }