//////////////////////////////////////////////////////////////////////////////// // // Helper methods // //////////////////////////////////////////////////////////////////////////////// // // In error recovery and reporting scenarios we sometimes end up in a situation // like this: // // x.Foo( y=> // // and the question is, "is Foo a valid extension method of x?" If Foo is // generic, then Foo will be something like: // // static Blah Foo<T>(this Bar<T> bar, Func<T, T> f){ ... } // // What we would like to know is: given _only_ the expression x, can we infer // what T is in Bar<T> ? If we can, then for error recovery and reporting // we can provisionally consider Foo to be an extension method of x. If we // cannot deduce this just from x then we should consider Foo to not be an // extension method of x, at least until we have more information. // // Clearly it is pointless to run multiple phases public static ImmutableArray<TypeSymbol> InferTypeArgumentsFromFirstArgument( ConversionsBase conversions, MethodSymbol method, ImmutableArray<BoundExpression> arguments, ref HashSet<DiagnosticInfo> useSiteDiagnostics) { Debug.Assert((object)method != null); Debug.Assert(method.Arity > 0); Debug.Assert(!arguments.IsDefault); // We need at least one formal parameter type and at least one argument. if ((method.ParameterCount < 1) || (arguments.Length < 1)) { return default(ImmutableArray<TypeSymbol>); } Debug.Assert(!method.ParameterTypes[0].IsDynamic()); var constructedFromMethod = method.ConstructedFrom; var inferrer = new MethodTypeInferrer( conversions, constructedFromMethod.TypeParameters, constructedFromMethod.ContainingType, constructedFromMethod.GetParameterTypes(), constructedFromMethod.ParameterRefKinds, arguments); if (!inferrer.InferTypeArgumentsFromFirstArgument(ref useSiteDiagnostics)) { return default(ImmutableArray<TypeSymbol>); } return inferrer.GetInferredTypeArguments(); }
public static MethodTypeInferenceResult Infer( Binder binder, ImmutableArray<TypeParameterSymbol> methodTypeParameters, // We are attempting to build a map from method type parameters // to inferred type arguments. NamedTypeSymbol constructedContainingTypeOfMethod, ImmutableArray<TypeSymbol> formalParameterTypes, // We have some unusual requirements for the types that flow into the inference engine. // Consider the following inference problems: // // Scenario one: // // class C<T> // { // delegate Y FT<X, Y>(T t, X x); // static void M<U, V>(U u, FT<U, V> f); // ... // C<double>.M(123, (t,x)=>t+x); // // From the first argument we infer that U is int. How now must we make an inference on // the second argument? The *declared* type of the formal is C<T>.FT<U,V>. The // actual type at the time of inference is known to be C<double>.FT<int, something> // where "something" is to be determined by inferring the return type of the // lambda by determine the type of "double + int". // // Therefore when we do type inference, if a formal parameter type is a generic delegate // then *its* formal parameter types must be the formal parameter types of the // *constructed* generic delegate C<double>.FT<...>, not C<T>.FT<...>. // // One would therefore suppose that we'd expect the formal parameter types to here // be passed in with the types constructed with the information known from the // call site, not the declared types. // // Contrast that with this scenario: // // Scenario Two: // // interface I<T> // { // void M<U>(T t, U u); // } // ... // static void Foo<V>(V v, I<V> iv) // { // iv.M(v, ""); // } // // Obviously inference should succeed here; it should infer that U is string. // // But consider what happens during the inference process on the first argument. // The first thing we will do is say "what's the type of the argument? V. What's // the type of the corresponding formal parameter? The first formal parameter of // I<V>.M<whatever> is of type V. The inference engine will then say "V is a // method type parameter, and therefore we have an inference from V to V". // But *V* is not one of the method type parameters being inferred; the only // method type parameter being inferred here is *U*. // // This is perhaps some evidence that the formal parameters passed in should be // the formal parameters of the *declared* method; in this case, (T, U), not // the formal parameters of the *constructed* method, (V, U). // // However, one might make the argument that no, we could just add a check // to ensure that if we see a method type parameter as a formal parameter type, // then we only perform the inference if the method type parameter is a type // parameter of the method for which inference is being performed. // // Unfortunately, that does not work either: // // Scenario three: // // class C<T> // { // static void M<U>(T t, U u) // { // ... // C<U>.M(u, 123); // ... // } // } // // The *original* formal parameter types are (T, U); the *constructed* formal parameter types // are (U, U), but *those are logically two different U's*. The first U is from the outer caller; // the second U is the U of the recursive call. // // That is, suppose someone called C<string>.M<double>(string, double). The recursive call should be to // C<double>.M<int>(double, int). We should absolutely not make an inference on the first argument // from U to U just because C<U>.M<something>'s first formal parameter is of type U. If we did then // inference would fail, because we'd end up with two bounds on 'U' -- 'U' and 'int'. We only want // the latter bound. // // What these three scenarios show is that for a "normal" inference we need to have the // formal parameters of the *original* method definition, but when making an inference from a lambda // to a delegate, we need to have the *constructed* method signature in order that the formal // parameters *of the delegate* be correct. // // How to solve this problem? // // We solve it by passing in the formal parameters in their *original* form, but also getting // the *fully constructed* type that the method call is on. When constructing the fixed // delegate type for inference from a lambda, we do the appropriate type substitution on // the delegate. ImmutableArray<RefKind> formalParameterRefKinds, // Optional; assume all value if missing. ImmutableArray<BoundExpression> arguments,// Required; in scenarios like method group conversions where there are // no arguments per se we cons up some fake arguments. ref HashSet<DiagnosticInfo> useSiteDiagnostics) { Debug.Assert(!methodTypeParameters.IsDefault); Debug.Assert(methodTypeParameters.Length > 0); Debug.Assert(!formalParameterTypes.IsDefault); Debug.Assert(formalParameterRefKinds.IsDefault || formalParameterRefKinds.Length == formalParameterTypes.Length); Debug.Assert(!arguments.IsDefault); // Early out: if the method has no formal parameters then we know that inference will fail. if (formalParameterTypes.Length == 0) { return new MethodTypeInferenceResult(false, default(ImmutableArray<TypeSymbol>)); // UNDONE: OPTIMIZATION: We could check to see whether there is a type // UNDONE: parameter which is never used in any formal parameter; if // UNDONE: so then we know ahead of time that inference will fail. } var inferrer = new MethodTypeInferrer( binder.Conversions, methodTypeParameters, constructedContainingTypeOfMethod, formalParameterTypes, formalParameterRefKinds, arguments); return inferrer.InferTypeArgs(binder, ref useSiteDiagnostics); }