1 //---------------------------------------------------------------------
2 // <copyright file="FunctionOverloadResolver.cs" company="Microsoft">
3 // Copyright (c) Microsoft Corporation. All rights reserved.
7 // @backupOwner Microsoft
8 //---------------------------------------------------------------------
10 namespace System
.Data
.Common
.EntitySql
13 using System
.Collections
.Generic
;
14 using System
.Data
.Entity
;
15 using System
.Data
.Metadata
.Edm
;
16 using System
.Diagnostics
;
20 /// Represents function overload resolution mechanism, used by L2E and eSQL frontends.
22 internal static class FunctionOverloadResolver
25 /// Resolves <paramref name="argTypes"/> against the list of function signatures.
27 /// <returns>Funciton metadata</returns>
28 internal static EdmFunction
ResolveFunctionOverloads(IList
<EdmFunction
> functionsMetadata
,
29 IList
<TypeUsage
> argTypes
,
30 bool isGroupAggregateFunction
,
33 return ResolveFunctionOverloads(
36 (edmFunction
) => edmFunction
.Parameters
,
37 (functionParameter
) => functionParameter
.TypeUsage
,
38 (functionParameter
) => functionParameter
.Mode
,
39 (argType
) => TypeSemantics
.FlattenType(argType
),
40 (paramType
, argType
) => TypeSemantics
.FlattenType(paramType
),
41 (fromType
, toType
) => TypeSemantics
.IsPromotableTo(fromType
, toType
),
42 (fromType
, toType
) => TypeSemantics
.IsStructurallyEqual(fromType
, toType
),
43 isGroupAggregateFunction
,
48 /// Resolves <paramref name="argTypes"/> against the list of function signatures.
50 /// <returns>Funciton metadata</returns>
51 internal static EdmFunction
ResolveFunctionOverloads(IList
<EdmFunction
> functionsMetadata
,
52 IList
<TypeUsage
> argTypes
,
53 Func
<TypeUsage
, IEnumerable
<TypeUsage
>> flattenArgumentType
,
54 Func
<TypeUsage
, TypeUsage
, IEnumerable
<TypeUsage
>> flattenParameterType
,
55 Func
<TypeUsage
, TypeUsage
, bool> isPromotableTo
,
56 Func
<TypeUsage
, TypeUsage
, bool> isStructurallyEqual
,
57 bool isGroupAggregateFunction
,
60 return ResolveFunctionOverloads(
63 (edmFunction
) => edmFunction
.Parameters
,
64 (functionParameter
) => functionParameter
.TypeUsage
,
65 (functionParameter
) => functionParameter
.Mode
,
70 isGroupAggregateFunction
,
75 /// Resolves <paramref name="argTypes"/> against the list of function signatures.
77 /// <param name="getSignatureParams">function formal signature getter</param>
78 /// <param name="getParameterTypeUsage">TypeUsage getter for a signature param</param>
79 /// <param name="getParameterMode">ParameterMode getter for a signature param</param>
80 /// <returns>Funciton metadata</returns>
81 internal static TFunctionMetadata ResolveFunctionOverloads
<TFunctionMetadata
, TFunctionParameterMetadata
>(
82 IList
<TFunctionMetadata
> functionsMetadata
,
83 IList
<TypeUsage
> argTypes
,
84 Func
<TFunctionMetadata
, IList
<TFunctionParameterMetadata
>> getSignatureParams
,
85 Func
<TFunctionParameterMetadata
, TypeUsage
> getParameterTypeUsage
,
86 Func
<TFunctionParameterMetadata
, ParameterMode
> getParameterMode
,
87 Func
<TypeUsage
, IEnumerable
<TypeUsage
>> flattenArgumentType
,
88 Func
<TypeUsage
, TypeUsage
, IEnumerable
<TypeUsage
>> flattenParameterType
,
89 Func
<TypeUsage
, TypeUsage
, bool> isPromotableTo
,
90 Func
<TypeUsage
, TypeUsage
, bool> isStructurallyEqual
,
91 bool isGroupAggregateFunction
,
92 out bool isAmbiguous
) where TFunctionMetadata
: class
95 // Flatten argument list
97 List
<TypeUsage
> argTypesFlat
= new List
<TypeUsage
>(argTypes
.Count
);
98 foreach (TypeUsage argType
in argTypes
)
100 argTypesFlat
.AddRange(flattenArgumentType(argType
));
104 // Find a candidate overload with the best total rank, remember the candidate and its composite rank.
106 TFunctionMetadata bestCandidate
= null;
108 List
<int[]> ranks
= new List
<int[]>(functionsMetadata
.Count
);
109 int[] bestCandidateRank
= null;
110 for (int i
= 0, maxTotalRank
= int.MinValue
; i
< functionsMetadata
.Count
; i
++)
114 if (TryRankFunctionParameters(argTypes
,
116 getSignatureParams(functionsMetadata
[i
]),
117 getParameterTypeUsage
,
119 flattenParameterType
,
122 isGroupAggregateFunction
,
123 out totalRank
, out rank
))
125 if (totalRank
== maxTotalRank
)
129 else if (totalRank
> maxTotalRank
)
132 maxTotalRank
= totalRank
;
133 bestCandidate
= functionsMetadata
[i
];
134 bestCandidateRank
= rank
;
137 Debug
.Assert(argTypesFlat
.Count
== rank
.Length
, "argTypesFlat.Count == rank.Length");
144 // If there is a best candidate, check it for ambiguity against composite ranks of other candidates
146 if (bestCandidate
!= null &&
148 argTypesFlat
.Count
> 1 && // best candidate may be ambiguous only in the case of 2 or more arguments
151 Debug
.Assert(bestCandidateRank
!= null);
154 // Search collection of composite ranks to see if there is an overload that would render the best candidate ambiguous
156 isAmbiguous
= ranks
.Any(rank
=>
158 Debug
.Assert(rank
.Length
== bestCandidateRank
.Length
, "composite ranks have different number of elements");
160 if (!Object
.ReferenceEquals(bestCandidateRank
, rank
)) // do not compare best cadnidate against itself
162 // All individual ranks of the best candidate must equal or better than the ranks of all other candidates,
163 // otherwise we consider it ambigous, even though it has an unambigously best total rank.
164 for (int i
= 0; i
< rank
.Length
; ++i
)
166 if (bestCandidateRank
[i
] < rank
[i
])
177 return isAmbiguous
? null : bestCandidate
;
181 /// Check promotability, returns true if argument list is promotable to the overload and overload was successfully ranked, otherwise false.
182 /// Ranks the overload parameter types against the argument list.
184 /// <param name="argumentList">list of argument types</param>
185 /// <param name="flatArgumentList">flattened list of argument types</param>
186 /// <param name="overloadParamList1">list of overload parameter types</param>
187 /// <param name="getParameterTypeUsage">TypeUsage getter for the overload parameters</param>
188 /// <param name="getParameterMode">ParameterMode getter for the overload parameters</param>
189 /// <param name="totalRank">returns total promotion rank of the overload, 0 if no arguments</param>
190 /// <param name="parameterRanks">returns individual promotion ranks of the overload parameters, empty array if no arguments</param>
191 private static bool TryRankFunctionParameters
<TFunctionParameterMetadata
>(IList
<TypeUsage
> argumentList
,
192 IList
<TypeUsage
> flatArgumentList
,
193 IList
<TFunctionParameterMetadata
> overloadParamList
,
194 Func
<TFunctionParameterMetadata
, TypeUsage
> getParameterTypeUsage
,
195 Func
<TFunctionParameterMetadata
, ParameterMode
> getParameterMode
,
196 Func
<TypeUsage
, TypeUsage
, IEnumerable
<TypeUsage
>> flattenParameterType
,
197 Func
<TypeUsage
, TypeUsage
, bool> isPromotableTo
,
198 Func
<TypeUsage
, TypeUsage
, bool> isStructurallyEqual
,
199 bool isGroupAggregateFunction
,
201 out int[] parameterRanks
)
204 parameterRanks
= null;
206 if (argumentList
.Count
!= overloadParamList
.Count
)
212 // Check promotability and flatten the parameter types
214 List
<TypeUsage
> flatOverloadParamList
= new List
<TypeUsage
>(flatArgumentList
.Count
);
215 for (int i
= 0; i
< overloadParamList
.Count
; ++i
)
217 TypeUsage argumentType
= argumentList
[i
];
218 TypeUsage parameterType
= getParameterTypeUsage(overloadParamList
[i
]);
221 // Parameter mode must match.
223 ParameterMode parameterMode
= getParameterMode(overloadParamList
[i
]);
224 if (parameterMode
!= ParameterMode
.In
&& parameterMode
!= ParameterMode
.InOut
)
230 // If function being ranked is a group aggregate, consider the element type.
232 if (isGroupAggregateFunction
)
234 if (!TypeSemantics
.IsCollectionType(parameterType
))
237 // Even though it is the job of metadata to ensure that the provider manifest is consistent.
238 // Ensure that if a function is marked as aggregate, then the argument type must be of collection{GivenType}.
240 throw EntityUtil
.EntitySqlError(Strings
.InvalidArgumentTypeForAggregateFunction
);
242 parameterType
= TypeHelpers
.GetElementTypeUsage(parameterType
);
246 // If argument is not promotable - reject the overload.
248 if (!isPromotableTo(argumentType
, parameterType
))
254 // Flatten the parameter type.
256 flatOverloadParamList
.AddRange(flattenParameterType(parameterType
, argumentType
));
259 Debug
.Assert(flatArgumentList
.Count
== flatOverloadParamList
.Count
, "flatArgumentList.Count == flatOverloadParamList.Count");
262 // Rank argument promotions
264 parameterRanks
= new int[flatOverloadParamList
.Count
];
265 for (int i
= 0; i
< parameterRanks
.Length
; ++i
)
267 int rank
= GetPromotionRank(flatArgumentList
[i
], flatOverloadParamList
[i
], isPromotableTo
, isStructurallyEqual
);
269 parameterRanks
[i
] = rank
;
276 /// Ranks the <paramref name="fromType"/> -> <paramref name="toType"/> promotion.
277 /// Range of values: 0 to negative infinity, with 0 as the best rank (promotion to self).
278 /// <paramref name="fromType"/> must be promotable to <paramref name="toType"/>, otherwise internal error is thrown.
280 private static int GetPromotionRank(TypeUsage fromType
,
282 Func
<TypeUsage
, TypeUsage
, bool> isPromotableTo
,
283 Func
<TypeUsage
, TypeUsage
, bool> isStructurallyEqual
)
286 // Only promotable types are allowed at this point.
288 Debug
.Assert(isPromotableTo(fromType
, toType
), "isPromotableTo(fromType, toType)");
291 // If both types are the same return rank 0 - the best match.
293 if (isStructurallyEqual(fromType
, toType
))
299 // In the case of eSQL untyped null will float up to the point of isStructurallyEqual(...) above.
300 // Below it eveything should be normal.
302 Debug
.Assert(fromType
!= null, "fromType != null");
303 Debug
.Assert(toType
!= null, "toType != null");
306 // Handle primitive types
308 PrimitiveType primitiveFromType
= fromType
.EdmType
as PrimitiveType
;
309 PrimitiveType primitiveToType
= toType
.EdmType
as PrimitiveType
;
310 if (primitiveFromType
!= null && primitiveToType
!= null)
312 if (Helper
.AreSameSpatialUnionType(primitiveFromType
, primitiveToType
))
317 IList
<PrimitiveType
> promotions
= EdmProviderManifest
.Instance
.GetPromotionTypes(primitiveFromType
);
319 int promotionIndex
= promotions
.IndexOf(primitiveToType
);
321 if (promotionIndex
< 0)
323 throw EntityUtil
.InternalError(EntityUtil
.InternalErrorCode
.FailedToGeneratePromotionRank
, 1);
326 return -promotionIndex
;
330 // Handle entity/relship types
332 EntityTypeBase entityBaseFromType
= fromType
.EdmType
as EntityTypeBase
;
333 EntityTypeBase entityBaseToType
= toType
.EdmType
as EntityTypeBase
;
334 if (entityBaseFromType
!= null && entityBaseToType
!= null)
336 int promotionIndex
= 0;
338 for (t
= entityBaseFromType
; t
!= entityBaseToType
&& t
!= null; t
= t
.BaseType
, ++promotionIndex
);
342 throw EntityUtil
.InternalError(EntityUtil
.InternalErrorCode
.FailedToGeneratePromotionRank
, 2);
345 return -promotionIndex
;
348 throw EntityUtil
.InternalError(EntityUtil
.InternalErrorCode
.FailedToGeneratePromotionRank
, 3);