Example #1
0
		//
		// 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;
		}
Example #2
0
		//
		// 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;
		}
Example #3
0
        /// <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;
        }
Example #4
0
		//
		// 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;
		}