Example #1
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 #2
0
		static void FindApplicableUserDefinedConversionOperators (ResolveContext rc, IList<MemberSpec> operators, Expression source, TypeSpec target, UserConversionRestriction restr, ref List<MethodSpec> candidates)
		{
			if (source.Type.IsInterface) {
				// Neither A nor B are interface-types
				return;
			}

			// For a conversion operator to be applicable, it must be possible
			// to perform a standard conversion from the source type to
			// the operand type of the operator, and it must be possible
			// to perform a standard conversion from the result type of
			// the operator to the target type.

			Expression texpr = null;

			foreach (MethodSpec op in operators) {
				
				// Can be null because MemberCache.GetUserOperator does not resize the array
				if (op == null)
					continue;

				var t = op.Parameters.Types[0];
				if (source.Type != t && !ImplicitStandardConversionExists (rc, source, t)) {
					if ((restr & UserConversionRestriction.ImplicitOnly) != 0)
						continue;

					if (!ImplicitStandardConversionExists (new EmptyExpression (t), source.Type))
							continue;
				}

				if ((restr & UserConversionRestriction.NullableSourceOnly) != 0 && !t.IsNullableType)
					continue;

				t = op.ReturnType;

				if (t.IsInterface)
					continue;

				if (target != t) {
					if (t.IsNullableType)
						t = Nullable.NullableInfo.GetUnderlyingType (t);

					if (!ImplicitStandardConversionExists (new EmptyExpression (t), target)) {
						if ((restr & UserConversionRestriction.ImplicitOnly) != 0)
							continue;

						if (texpr == null)
							texpr = new EmptyExpression (target);

						if (!ImplicitStandardConversionExists (texpr, t))
							continue;
					}
				}

				if (candidates == null)
					candidates = new List<MethodSpec> ();

				candidates.Add (op);
			}
		}