// // User-defined conversions // static Expression UserDefinedConversion (ResolveContext ec, Expression source, TypeSpec target, bool implicitOnly, Location loc) { List<MethodSpec> candidates = null; // // If S or T are nullable types, source_type and target_type are their underlying types // otherwise source_type and target_type are equal to S and T respectively. // TypeSpec source_type = source.Type; TypeSpec target_type = target; Expression source_type_expr; if (source_type.IsNullableType) { // No implicit conversion S? -> T for non-reference types if (implicitOnly && !TypeSpec.IsReferenceType (target_type) && !target_type.IsNullableType) return null; source_type_expr = Nullable.Unwrap.Create (source); source_type = source_type_expr.Type; } else { source_type_expr = source; } if (target_type.IsNullableType) target_type = Nullable.NullableInfo.GetUnderlyingType (target_type); // Only these containers can contain a user defined implicit or explicit operators const MemberKind user_conversion_kinds = MemberKind.Class | MemberKind.Struct | MemberKind.TypeParameter; if ((source_type.Kind & user_conversion_kinds) != 0 && source_type.BuiltinType != BuiltinTypeSpec.Type.Decimal) { bool declared_only = source_type.IsStruct; var operators = MemberCache.GetUserOperator (source_type, Operator.OpType.Implicit, declared_only); if (operators != null) { FindApplicableUserDefinedConversionOperators (operators, source_type_expr, target_type, implicitOnly, ref candidates); } if (!implicitOnly) { operators = MemberCache.GetUserOperator (source_type, Operator.OpType.Explicit, declared_only); if (operators != null) { FindApplicableUserDefinedConversionOperators (operators, source_type_expr, target_type, false, ref candidates); } } } if ((target.Kind & user_conversion_kinds) != 0 && target_type.BuiltinType != BuiltinTypeSpec.Type.Decimal) { bool declared_only = target.IsStruct || implicitOnly; var operators = MemberCache.GetUserOperator (target_type, Operator.OpType.Implicit, declared_only); if (operators != null) { FindApplicableUserDefinedConversionOperators (operators, source_type_expr, target_type, implicitOnly, ref candidates); } if (!implicitOnly) { operators = MemberCache.GetUserOperator (target_type, Operator.OpType.Explicit, declared_only); if (operators != null) { FindApplicableUserDefinedConversionOperators (operators, source_type_expr, target_type, false, ref candidates); } } } if (candidates == null) return null; // // Find the most specific conversion operator // MethodSpec most_specific_operator; TypeSpec s_x, t_x; if (candidates.Count == 1) { most_specific_operator = candidates[0]; s_x = most_specific_operator.Parameters.Types[0]; t_x = most_specific_operator.ReturnType; } else { // // Pass original source type to find the best match against input type and // not the unwrapped expression // s_x = FindMostSpecificSource (candidates, source.Type, source_type_expr, !implicitOnly); if (s_x == null) return null; t_x = FindMostSpecificTarget (candidates, target, !implicitOnly); if (t_x == null) return null; most_specific_operator = null; for (int i = 0; i < candidates.Count; ++i) { if (candidates[i].ReturnType == t_x && candidates[i].Parameters.Types[0] == s_x) { most_specific_operator = candidates[i]; break; } } if (most_specific_operator == null) { MethodSpec ambig_arg = null; foreach (var candidate in candidates) { if (candidate.ReturnType == t_x) most_specific_operator = candidate; else if (candidate.Parameters.Types[0] == s_x) ambig_arg = candidate; } ec.Report.Error (457, loc, "Ambiguous user defined operators `{0}' and `{1}' when converting from `{2}' to `{3}'", ambig_arg.GetSignatureForError (), most_specific_operator.GetSignatureForError (), source.Type.GetSignatureForError (), target.GetSignatureForError ()); return ErrorExpression.Instance; } } // // Convert input type when it's different to best operator argument // if (s_x != source_type) { var c = source as Constant; if (c != null) { source = c.TryReduce (ec, s_x, loc); } else { source = implicitOnly ? ImplicitConversionStandard (ec, source_type_expr, s_x, loc) : ExplicitConversionStandard (ec, source_type_expr, s_x, loc); } } else { source = source_type_expr; } source = new UserCast (most_specific_operator, source, loc).Resolve (ec); // // Convert result type when it's different to best operator return type // if (t_x != target_type) { // // User operator is of T?, no need to lift it // if (t_x == target && t_x.IsNullableType) return source; source = implicitOnly ? ImplicitConversionStandard (ec, source, target_type, loc) : ExplicitConversionStandard (ec, source, target_type, loc); if (source == null) return null; } // // Source expression is of nullable type, lift the result in the case it's null and // not nullable/lifted user operator is used // if (source_type_expr is Nullable.Unwrap && !s_x.IsNullableType && (TypeSpec.IsReferenceType (target) || target_type != target)) source = new Nullable.Lifted (source, source_type_expr, target).Resolve (ec); else if (target_type != target) source = Nullable.Wrap.Create (source, target); return source; }
// // User-defined conversions // public static Expression UserDefinedConversion (ResolveContext rc, Expression source, TypeSpec target, UserConversionRestriction restr, Location loc) { List<MethodSpec> candidates = null; // // If S or T are nullable types, source_type and target_type are their underlying types // otherwise source_type and target_type are equal to S and T respectively. // TypeSpec source_type = source.Type; TypeSpec target_type = target; Expression source_type_expr; bool nullable_source = false; var implicitOnly = (restr & UserConversionRestriction.ImplicitOnly) != 0; if (source_type.IsNullableType) { // No unwrapping conversion S? -> T for non-reference types if (implicitOnly && !TypeSpec.IsReferenceType (target_type) && !target_type.IsNullableType) { source_type_expr = source; } else { source_type_expr = Nullable.Unwrap.CreateUnwrapped (source); source_type = source_type_expr.Type; nullable_source = true; } } else { source_type_expr = source; } if (target_type.IsNullableType) target_type = Nullable.NullableInfo.GetUnderlyingType (target_type); // Only these containers can contain a user defined implicit or explicit operators const MemberKind user_conversion_kinds = MemberKind.Class | MemberKind.Struct | MemberKind.TypeParameter; if ((source_type.Kind & user_conversion_kinds) != 0 && source_type.BuiltinType != BuiltinTypeSpec.Type.Decimal) { bool declared_only = source_type.IsStruct; var operators = MemberCache.GetUserOperator (source_type, Operator.OpType.Implicit, declared_only); if (operators != null) { FindApplicableUserDefinedConversionOperators (rc, operators, source_type_expr, target_type, restr, ref candidates); } if (!implicitOnly) { operators = MemberCache.GetUserOperator (source_type, Operator.OpType.Explicit, declared_only); if (operators != null) { FindApplicableUserDefinedConversionOperators (rc, operators, source_type_expr, target_type, restr, ref candidates); } } } if ((target.Kind & user_conversion_kinds) != 0 && target_type.BuiltinType != BuiltinTypeSpec.Type.Decimal) { bool declared_only = target.IsStruct || implicitOnly; var operators = MemberCache.GetUserOperator (target_type, Operator.OpType.Implicit, declared_only); if (operators != null) { FindApplicableUserDefinedConversionOperators (rc, operators, source_type_expr, target_type, restr, ref candidates); } if (!implicitOnly) { operators = MemberCache.GetUserOperator (target_type, Operator.OpType.Explicit, declared_only); if (operators != null) { FindApplicableUserDefinedConversionOperators (rc, operators, source_type_expr, target_type, restr, ref candidates); } } } if (candidates == null) return null; // // Find the most specific conversion operator // MethodSpec most_specific_operator; TypeSpec s_x, t_x; if (candidates.Count == 1) { most_specific_operator = candidates[0]; s_x = most_specific_operator.Parameters.Types[0]; t_x = most_specific_operator.ReturnType; } else { // // Pass original source type to find the best match against input type and // not the unwrapped expression // s_x = FindMostSpecificSource (rc, candidates, source.Type, source_type_expr, !implicitOnly); if (s_x == null) return null; t_x = FindMostSpecificTarget (candidates, target, !implicitOnly); if (t_x == null) return null; most_specific_operator = null; for (int i = 0; i < candidates.Count; ++i) { if (candidates[i].ReturnType == t_x && candidates[i].Parameters.Types[0] == s_x) { most_specific_operator = candidates[i]; break; } } if (most_specific_operator == null) { // // Unless running in probing more // if ((restr & UserConversionRestriction.ProbingOnly) == 0) { MethodSpec ambig_arg = candidates [0]; most_specific_operator = candidates [1]; /* foreach (var candidate in candidates) { if (candidate.ReturnType == t_x) most_specific_operator = candidate; else if (candidate.Parameters.Types[0] == s_x) ambig_arg = candidate; } */ rc.Report.Error (457, loc, "Ambiguous user defined operators `{0}' and `{1}' when converting from `{2}' to `{3}'", ambig_arg.GetSignatureForError (), most_specific_operator.GetSignatureForError (), source.Type.GetSignatureForError (), target.GetSignatureForError ()); } return ErrorExpression.Instance; } } // // Convert input type when it's different to best operator argument // if (s_x != source_type) { var c = source as Constant; if (c != null) { source = c.Reduce (rc, s_x); if (source == null) c = null; } if (c == null) { source = implicitOnly ? ImplicitConversionStandard (rc, source_type_expr, s_x, loc) : ExplicitConversionStandard (rc, source_type_expr, s_x, loc); } } else { source = source_type_expr; } source = new UserCast (most_specific_operator, source, loc).Resolve (rc); // // Convert result type when it's different to best operator return type // if (t_x != target_type) { // // User operator is of T? // if (t_x.IsNullableType && (target.IsNullableType || !implicitOnly)) { // // User operator return type does not match target type we need // yet another conversion. This should happen for promoted numeric // types only // if (t_x != target) { var unwrap = Nullable.Unwrap.CreateUnwrapped (source); source = implicitOnly ? ImplicitConversionStandard (rc, unwrap, target_type, loc) : ExplicitConversionStandard (rc, unwrap, target_type, loc); if (source == null) return null; if (target.IsNullableType) source = new Nullable.LiftedConversion (source, unwrap, target).Resolve (rc); } } else { source = implicitOnly ? ImplicitConversionStandard (rc, source, target_type, loc) : ExplicitConversionStandard (rc, source, target_type, loc); if (source == null) return null; } } // // Source expression is of nullable type and underlying conversion returns // only non-nullable type we need to lift it manually // if (nullable_source && !s_x.IsNullableType) return new Nullable.LiftedConversion (source, source_type_expr, target).Resolve (rc); // // Target is of nullable type but source type is not, wrap the result expression // if (target.IsNullableType && !t_x.IsNullableType) source = Nullable.Wrap.Create (source, target); return source; }
/// <summary> /// User-defined conversions /// </summary> public static Expression UserDefinedConversion(ResolveContext ec, Expression source, TypeSpec target, Location loc, bool look_for_explicit, bool return_convert) { TypeSpec source_type = source.Type; MethodSpec method = null; Expression expr = null; object o; DoubleHash hash; if (look_for_explicit) { hash = explicit_conv; } else { // Implicit user operators cannot convert to interfaces if (target.IsInterface) return null; hash = implicit_conv; } if (!(source is Constant) && hash.Lookup (source_type, target, out o)) { method = (MethodSpec) o; } else { if (source_type == InternalType.Dynamic) return null; method = GetConversionOperator (ec.Compiler, null, source, target, look_for_explicit); } if (method != null) { TypeSpec most_specific_source = method.Parameters.Types[0]; // // This will do the conversion to the best match that we // found. Now we need to perform an implict standard conversion // if the best match was not the type that we were requested // by target. // if (look_for_explicit) { ReportPrinter temp = new SessionReportPrinter (); ReportPrinter prev = ec.Report.SetPrinter (temp); expr = ExplicitConversionStandard (ec, source, most_specific_source, loc); ec.Report.SetPrinter (prev); if (temp.ErrorsCount != 0) expr = null; } else { if (ImplicitStandardConversionExists (source, most_specific_source)) expr = ImplicitConversionStandard (ec, source, most_specific_source, loc); else expr = null; } } if (expr == null) { bool nullable = false; if (TypeManager.IsNullableType (source_type)) { source = Nullable.Unwrap.Create (source); nullable = true; } TypeSpec target_underlying; if (TypeManager.IsNullableType (target)) { target_underlying = TypeManager.GetTypeArguments (target)[0]; nullable = true; } else { // No implicit conversion S? -> T for non-reference type T if (!look_for_explicit && !TypeManager.IsReferenceType (target)) nullable = false; target_underlying = target; } if (nullable) { expr = UserDefinedConversion (ec, source, target_underlying, loc, look_for_explicit, return_convert); // Do result expression lifting only when it's needed if (expr != null && (!look_for_explicit || TypeManager.IsReferenceType (target))) expr = new Nullable.Lifted (expr, source, target).Resolve (ec); return expr; } } else { expr = new UserCast (method, expr, loc).Resolve (ec); if (return_convert && !TypeManager.IsEqual (expr.Type, target)) { if (look_for_explicit) { expr = ExplicitConversionStandard (ec, expr, target, loc); } else { expr = ImplicitConversionStandard (ec, expr, target, loc); } } } if (!(source is Constant)) hash.Insert (source_type, target, method); return expr; }
// // User-defined conversions // static Expression UserDefinedConversion (ResolveContext ec, Expression source, TypeSpec target, bool implicitOnly, Location loc) { List<MethodSpec> candidates = null; // // If S or T are nullable types, S0 and T0 are their underlying types // otherwise S0 and T0 are equal to S and T respectively. // TypeSpec source_type = source.Type; TypeSpec target_type = target; Expression unwrap; if (TypeManager.IsNullableType (source_type)) { // No implicit conversion S? -> T for non-reference types if (implicitOnly && !TypeManager.IsReferenceType (target_type) && !TypeManager.IsNullableType (target_type)) return null; unwrap = source = Nullable.Unwrap.Create (source); source_type = source.Type; } else { unwrap = null; } if (TypeManager.IsNullableType (target_type)) target_type = Nullable.NullableInfo.GetUnderlyingType (target_type); // Only these containers can contain a user defined implicit or explicit operators const MemberKind user_conversion_kinds = MemberKind.Class | MemberKind.Struct | MemberKind.TypeParameter; if ((source_type.Kind & user_conversion_kinds) != 0 && source_type != TypeManager.decimal_type) { bool declared_only = source_type.IsStruct; var operators = MemberCache.GetUserOperator (source_type, Operator.OpType.Implicit, declared_only); if (operators != null) { FindApplicableUserDefinedConversionOperators (operators, source, target_type, implicitOnly, ref candidates); } if (!implicitOnly) { operators = MemberCache.GetUserOperator (source_type, Operator.OpType.Explicit, declared_only); if (operators != null) { FindApplicableUserDefinedConversionOperators (operators, source, target_type, false, ref candidates); } } } if ((target.Kind & user_conversion_kinds) != 0 && target != TypeManager.decimal_type) { bool declared_only = target.IsStruct || implicitOnly; var operators = MemberCache.GetUserOperator (target_type, Operator.OpType.Implicit, declared_only); if (operators != null) { FindApplicableUserDefinedConversionOperators (operators, source, target_type, implicitOnly, ref candidates); } if (!implicitOnly) { operators = MemberCache.GetUserOperator (target_type, Operator.OpType.Explicit, declared_only); if (operators != null) { FindApplicableUserDefinedConversionOperators (operators, source, target_type, false, ref candidates); } } } if (candidates == null) return null; // // Find the most specific conversion operator // MethodSpec most_specific_operator; TypeSpec s_x, t_x; if (candidates.Count == 1) { most_specific_operator = candidates[0]; s_x = most_specific_operator.Parameters.Types[0]; t_x = most_specific_operator.ReturnType; } else { s_x = FindMostSpecificSource (candidates, source, !implicitOnly); if (s_x == null) return null; t_x = FindMostSpecificTarget (candidates, target, !implicitOnly); if (t_x == null) return null; most_specific_operator = candidates[0]; for (int i = 1; i < candidates.Count; ++i) { if (candidates[i].ReturnType == t_x && candidates[i].Parameters.Types[0] == s_x) { most_specific_operator = candidates[i]; break; } } } // // Convert input type when it's different to best operator argument // if (s_x != source.Type) source = implicitOnly ? ImplicitConversionStandard (ec, source, s_x, loc) : ExplicitConversionStandard (ec, source, s_x, loc); source = new UserCast (most_specific_operator, source, loc).Resolve (ec); // // Convert result type when it's different to best operator return type // if (t_x != target_type) { // // User operator is of T?, no need to lift it // if (TypeManager.IsNullableType (t_x) && t_x == target) return source; source = implicitOnly ? ImplicitConversionStandard (ec, source, target_type, loc) : ExplicitConversionStandard (ec, source, target_type, loc); } // // Source expression is of nullable type, lift the result in case of it's null // if (unwrap != null && (TypeManager.IsReferenceType (target) || target_type != target)) source = new Nullable.Lifted (source, unwrap, target).Resolve (ec); else if (target_type != target) source = Nullable.Wrap.Create (source, target); return source; }