3434
3535import com .google .common .base .Objects ;
3636
37+ import java .lang .reflect .Method ;
38+ import java .lang .reflect .Modifier ;
3739import java .lang .reflect .ParameterizedType ;
3840import java .lang .reflect .Type ;
3941import java .lang .reflect .TypeVariable ;
@@ -56,9 +58,9 @@ private MatchingUtils() {
5658 }
5759
5860 /**
59- * Checks for raw assignability. TODO This method is not yet fully
60- * implemented. The correct behavior should be as follows. Suppose we have a
61- * generic typed method like:
61+ * Checks for raw assignability. TODO This method is not yet fully implemented.
62+ * The correct behavior should be as follows. Suppose we have a generic typed
63+ * method like:
6264 *
6365 * <pre>
6466 *public static <N> List<N> foo(N in) {
@@ -88,13 +90,13 @@ private MatchingUtils() {
8890 * checkGenericOutputsAssignability(nilN.getType(), nilWildcardNumber.getType, ...)
8991 * </pre>
9092 *
91- * Using a map where N was already bound to Integer (N -> Integer.class).
92- * This method is useful for the following scenario: During ops matching, we
93- * first check if the arguments (inputs) of the requested op are applicable
94- * to the arguments of an op candidate. During this process, possible type
95- * variables may be inferred. The can then be used with this method to find
96- * out if the outputs of the op candidate would be assignable to the output
97- * of the requested op.
93+ * Using a map where N was already bound to Integer (N -> Integer.class). This
94+ * method is useful for the following scenario: During ops matching, we first
95+ * check if the arguments (inputs) of the requested op are applicable to the
96+ * arguments of an op candidate. During this process, possible type variables
97+ * may be inferred. The can then be used with this method to find out if the
98+ * outputs of the op candidate would be assignable to the output of the
99+ * requested op.
98100 *
99101 * @param froms
100102 * @param tos
@@ -106,18 +108,23 @@ public static int checkGenericOutputsAssignability(Type[] froms, Type[] tos,
106108 for (int i = 0 ; i < froms .length ; i ++) {
107109 Type from = froms [i ];
108110 Type to = tos [i ];
109-
110- //HACK: we CAN assign, for example, a Function<Iterable<N>, O> to a Function<Iterable<Integer>, Double>,
111- //because in this situation O is not bounded to any other types. However isAssignable will fail,
112- //since we cannot just cast Double to O without that required knowledge that O can be fixed to Double.
113- //We get around this by recording in typeBounds that our previously unbounded TypeVariable (from) \
114- //is now fixed to (to), then simply assigning (from) to (to), since from only has one bound, being to.
115- if (from instanceof TypeVariable && typeBounds .get (from ) == null ) {
116- TypeVariable <?> fromTypeVar = (TypeVariable <?>) from ;
117- TypeVarFromParameterizedTypeInfo fromInfo = new TypeVarFromParameterizedTypeInfo (fromTypeVar );
118- fromInfo .fixBounds (to , true );
119- typeBounds .put (fromTypeVar , fromInfo );
120- from = to ;
111+
112+ // HACK: we CAN assign, for example, a Function<Iterable<N>, O> to a
113+ // Function<Iterable<Integer>, Double>,
114+ // because in this situation O is not bounded to any other types. However
115+ // isAssignable will fail,
116+ // since we cannot just cast Double to O without that required knowledge that O
117+ // can be fixed to Double.
118+ // We get around this by recording in typeBounds that our previously unbounded
119+ // TypeVariable (from) \
120+ // is now fixed to (to), then simply assigning (from) to (to), since from only
121+ // has one bound, being to.
122+ if (from instanceof TypeVariable && typeBounds .get (from ) == null ) {
123+ TypeVariable <?> fromTypeVar = (TypeVariable <?>) from ;
124+ TypeVarFromParameterizedTypeInfo fromInfo = new TypeVarFromParameterizedTypeInfo (fromTypeVar );
125+ fromInfo .fixBounds (to , true );
126+ typeBounds .put (fromTypeVar , fromInfo );
127+ from = to ;
121128 }
122129
123130 if (!Types .isAssignable (Types .raw (from ), Types .raw (to )))
@@ -128,8 +135,8 @@ public static int checkGenericOutputsAssignability(Type[] froms, Type[] tos,
128135
129136 /**
130137 * Checks whether it would be legal to assign the {@link Type} source to the
131- * specified {@link ParameterizedType} destination (which could possibly be
132- * a supertype of the source type). Thereby, possible {@link TypeVariable}s
138+ * specified {@link ParameterizedType} destination (which could possibly be a
139+ * supertype of the source type). Thereby, possible {@link TypeVariable}s
133140 * contained in the parameters of the source are tried to be inferred in the
134141 * sense of empty angle brackets when a new object is created:
135142 *
@@ -140,9 +147,9 @@ public static int checkGenericOutputsAssignability(Type[] froms, Type[] tos,
140147 * Hence, the types to put between the brackets are tried to be determined.
141148 * Inference will be done by simple matching of an encountered
142149 * {@link TypeVariable} in the source to the corresponding type in the
143- * parameters of the destination. If an {@link TypeVariable} is encountered
144- * more than once, the corresponding type in the destination needs to
145- * perfectly match. Else, false will be returned.</br>
150+ * parameters of the destination. If an {@link TypeVariable} is encountered more
151+ * than once, the corresponding type in the destination needs to perfectly
152+ * match. Else, false will be returned.</br>
146153 * </br>
147154 * Examples:
148155 * <ul>
@@ -171,8 +178,8 @@ public static int checkGenericOutputsAssignability(Type[] froms, Type[] tos,
171178 * </ul>
172179 * </ul>
173180 * <ul>
174- * Here, the parameter {@code <M extends Number>} can be inferred to be of
175- * type {@code Double} from the type {@code Supplier<Double>}
181+ * Here, the parameter {@code <M extends Number>} can be inferred to be of type
182+ * {@code Double} from the type {@code Supplier<Double>}
176183 * </ul>
177184 * <ul>
178185 * Consequently the following will return false:
@@ -183,8 +190,8 @@ public static int checkGenericOutputsAssignability(Type[] froms, Type[] tos,
183190 * Nil<Supplier<String>>() {}.getType())</li>
184191 * </ul>
185192 * <ul>
186- * {@code <M extends Number>} can't be inferred, as type {@code String} is
187- * not within the bounds of {@code M}.
193+ * {@code <M extends Number>} can't be inferred, as type {@code String} is not
194+ * within the bounds of {@code M}.
188195 * </ul>
189196 * <ul>
190197 * Furthermore, the following will return false for:
@@ -203,12 +210,20 @@ public static int checkGenericOutputsAssignability(Type[] froms, Type[] tos,
203210 * @param src
204211 * the type for which assignment should be checked from
205212 * @param dest
206- * the parameterized type for which assignment should be checked
207- * to
208- * @return whether and assignment of source to destination would be a legal
209- * java statement
213+ * the parameterized type for which assignment should be checked to
214+ * @param typeVarAssigns
215+ * the map of TypeVariables to Types that would occur in this
216+ * scenario
217+ * @param safeAssignability
218+ * used to determine if we want to check if the src->dest
219+ * assignment would be safely assignable even though it would cause a
220+ * compiler error if we explicitly tried to do this (useful pretty
221+ * much only for Op matching)
222+ * @return whether and assignment of source to destination would be a legal java
223+ * statement
210224 */
211- public static boolean checkGenericAssignability (Type src , ParameterizedType dest , Map <TypeVariable <?>, Type > typeVarAssigns ) {
225+ public static boolean checkGenericAssignability (Type src , ParameterizedType dest ,
226+ Map <TypeVariable <?>, Type > typeVarAssigns , boolean safeAssignability ) {
212227 // check raw assignability
213228 if (!Types .isAssignable (Types .raw (src ), Types .raw (dest )))
214229 return false ;
@@ -225,18 +240,68 @@ public static boolean checkGenericAssignability(Type src, ParameterizedType dest
225240 return Types .isAssignable (src , dest );
226241 }
227242
228- return checkGenericAssignability (srcTypes , destTypes , dest , typeVarAssigns );
243+ return checkGenericAssignability (srcTypes , destTypes , src , dest , typeVarAssigns , safeAssignability );
229244 }
230245
231- public static boolean checkGenericAssignability (Type src , ParameterizedType dest ) {
232- return checkGenericAssignability (src , dest , new HashMap <TypeVariable <?>, Type >());
246+ /**
247+ * @param src
248+ * the type for which assignment should be checked from
249+ * @param dest
250+ * the parameterized type for which assignment should be checked to
251+ * @param safeAssignability
252+ * used to determine if we want to check if the src->dest
253+ * assignment would be safely assignable even though it would cause a
254+ * compiler error if we explicitly tried to do this (useful pretty
255+ * much only for Op matching)
256+ * @return whether and assignment of source to destination would be a legal java
257+ * statement
258+ */
259+ public static boolean checkGenericAssignability (Type src , ParameterizedType dest , boolean safeAssignability ) {
260+ return checkGenericAssignability (src , dest , new HashMap <TypeVariable <?>, Type >(), safeAssignability );
233261 }
234262
235- private static boolean checkGenericAssignability (Type [] srcTypes , Type [] destTypes , Type dest ) {
236- return checkGenericAssignability (srcTypes , destTypes , dest , new HashMap <TypeVariable <?>, Type >());
263+ /**
264+ * @param srcTypes the Type arguments for the source Type
265+ * @param destTypes the Type arguments for the destination Type
266+ * @param src
267+ * the type for which assignment should be checked from
268+ * @param dest
269+ * the parameterized type for which assignment should be checked to
270+ * @param safeAssignability
271+ * used to determine if we want to check if the src->dest
272+ * assignment would be safely assignable even though it would cause a
273+ * compiler error if we explicitly tried to do this (useful pretty
274+ * much only for Op matching)
275+ * @return whether and assignment of source to destination would be a legal java
276+ * statement
277+ */
278+ private static boolean checkGenericAssignability (Type [] srcTypes , Type [] destTypes , Type src , Type dest ,
279+ boolean safeAssignability ) {
280+ return checkGenericAssignability (srcTypes , destTypes , src , dest , new HashMap <TypeVariable <?>, Type >(),
281+ safeAssignability );
237282 }
238283
239- private static boolean checkGenericAssignability (Type [] srcTypes , Type [] destTypes , Type dest , Map <TypeVariable <?>, Type > typeVarAssigns ) {
284+ /**
285+ *
286+ * @param srcTypes the Type arguments for the source Type
287+ * @param destTypes the Type arguments for the destination Type
288+ * @param src
289+ * the type for which assignment should be checked from
290+ * @param dest
291+ * the parameterized type for which assignment should be checked to
292+ * @param typeVarAssigns
293+ * the map of TypeVariables to Types that would occur in this
294+ * scenario
295+ * @param safeAssignability
296+ * used to determine if we want to check if the src->dest
297+ * assignment would be safely assignable even though it would cause a
298+ * compiler error if we explicitly tried to do this (useful pretty
299+ * much only for Op matching)
300+ * @return whether and assignment of source to destination would be a legal java
301+ * statement
302+ */
303+ private static boolean checkGenericAssignability (Type [] srcTypes , Type [] destTypes , Type src , Type dest ,
304+ Map <TypeVariable <?>, Type > typeVarAssigns , boolean safeAssignability ) {
240305 // if the number of type arguments does not match, the types can't be
241306 // assignable
242307 if (srcTypes .length != destTypes .length ) {
@@ -252,19 +317,62 @@ private static boolean checkGenericAssignability(Type[] srcTypes, Type[] destTyp
252317 mappedSrcTypes = mapVarToTypes (srcTypes , typeVarAssigns );
253318 } catch (TypeInferenceException e ) {
254319 // types can't be inferred
255- return false ;
320+ return safeAssignability && isSafeAssignable ( destTypes , typeVarAssigns , src , dest ) ;
256321 }
257322
258323 // Build a new parameterized type from inferred types and check
259324 // assignability
260325 Class <?> matchingRawType = Types .raw (dest );
261326 Type inferredSrcType = Types .parameterize (matchingRawType , mappedSrcTypes );
262327 if (!Types .isAssignable (inferredSrcType , dest )) {
263- return false ;
328+ if (!safeAssignability || !isSafeAssignable (destTypes , typeVarAssigns , src , dest ))
329+ return false ;
264330 }
265331 return true ;
266332 }
267333
334+ /**
335+ * We know that the special types for the Op candidate and what we asked for are
336+ * the same (i.e. that we are trying to determine if one Function can be
337+ * assigned to another Function). There are some situations (that are
338+ * particularly common when using ops.run()) where the Function SHOULD NOT
339+ * NORMALLY MATCH UP but WE KNOW IT WILL BE SAFE TO ASSIGN. This method attempts
340+ * to tease those situations out as a last resort.
341+ *
342+ * @param srcTypes
343+ * - the array of Parameterized types of the potential match
344+ * @param destTypes
345+ * - the array of Parameterized types of the OpInfo we called the
346+ * matcher on (in the case of ops.run(), it is a Type array of the
347+ * types of the args we passed through.)
348+ * @param typeVarAssigns
349+ * - a Map of all of the Type Variables already determined.
350+ * @param dest
351+ * - the speical type of the Op that we want to find a match for
352+ * (determined by the user / ops.run())
353+ * @return boolean - true if we can safely match this Op even though the types
354+ * do not directly match up. False otherwise.
355+ */
356+ public static boolean isSafeAssignable (Type [] destTypes , Map <TypeVariable <?>, Type > typeVarAssigns , Type src ,
357+ Type dest ) {
358+
359+ Method [] destMethods = Arrays .stream (Types .raw (dest ).getDeclaredMethods ())
360+ .filter (method -> Modifier .isAbstract (method .getModifiers ())).toArray (Method []::new );
361+ Type [] params = Types .methodParamTypes (destMethods [0 ], Types .raw (src ));
362+ Type returnType = Types .methodReturnType (destMethods [0 ], Types .raw (src ));
363+ for (int i = 0 ; i < params .length ; i ++) {
364+ if (!Types .isAssignable (destTypes [i ], params [i ], typeVarAssigns ))
365+ return false ;
366+ }
367+
368+ // Computers will have void as their return type, meaning that there is no
369+ // output to check.
370+ if (returnType == void .class )
371+ return true ;
372+
373+ return Types .isAssignable (returnType , destTypes [destTypes .length - 1 ], typeVarAssigns );
374+ }
375+
268376 /**
269377 * Exception indicating that type vars could not be inferred.
270378 */
@@ -277,8 +385,8 @@ private static class TypeInferenceException extends Exception {
277385
278386 /**
279387 * Map type vars in specified type list to types using the specified map. In
280- * doing so, type vars mapping to other type vars will not be followed but
281- * just repalced.
388+ * doing so, type vars mapping to other type vars will not be followed but just
389+ * repalced.
282390 *
283391 * @param typesToMap
284392 * @param typeAssigns
@@ -355,13 +463,17 @@ private static void inferTypeVariables(Type[] types, Type[] inferFrom, Map<TypeV
355463 } else if (types [i ] instanceof ParameterizedType ) {
356464 // Recursively follow parameterized types
357465 if (!(inferFrom [i ] instanceof ParameterizedType )) {
358- throw new TypeInferenceException ();
466+ Type [] fromType = { types [i ] };
467+ fromType = Types .mapVarToTypes (fromType , typeAssigns );
468+ if (!Types .isAssignable (inferFrom [i ], fromType [0 ])) {
469+ throw new TypeInferenceException ();
470+ }
471+ } else {
472+ ParameterizedType paramType = (ParameterizedType ) types [i ];
473+ ParameterizedType paramInferFrom = (ParameterizedType ) inferFrom [i ];
474+ inferTypeVariables (paramType .getActualTypeArguments (), paramInferFrom .getActualTypeArguments (),
475+ typeAssigns );
359476 }
360- ParameterizedType paramType = (ParameterizedType ) types [i ];
361- ParameterizedType paramInferFrom = (ParameterizedType ) inferFrom [i ];
362- inferTypeVariables (paramType .getActualTypeArguments (), paramInferFrom .getActualTypeArguments (),
363- typeAssigns );
364-
365477 } else if (types [i ] instanceof WildcardType ) {
366478 // TODO Do we need to specifically handle Wildcards? Or are they
367479 // sufficiently handled by Types.satisfies below?
@@ -374,20 +486,19 @@ private static void inferTypeVariables(Type[] types, Type[] inferFrom, Map<TypeV
374486 }
375487
376488 /**
377- * Finds the type parameters of the most specific super type of the
378- * specified subType whose erasure is the specified superErasure. Hence,
379- * will return the type parameters of superErasure possibly narrowed down by
380- * subType. If superErasure is not raw or not a super type of subType, an
381- * empty array will be returned.
489+ * Finds the type parameters of the most specific super type of the specified
490+ * subType whose erasure is the specified superErasure. Hence, will return the
491+ * type parameters of superErasure possibly narrowed down by subType. If
492+ * superErasure is not raw or not a super type of subType, an empty array will
493+ * be returned.
382494 *
383495 * @param subType
384496 * the type to narrow down type parameters
385497 * @param superErasure
386- * the erasure of an super type of subType to get the parameters
387- * from
388- * @return type parameters of superErasure possibly narrowed down by
389- * subType, or empty type array if no exists or superErasure is not
390- * a super type of subtype
498+ * the erasure of an super type of subType to get the parameters from
499+ * @return type parameters of superErasure possibly narrowed down by subType, or
500+ * empty type array if no exists or superErasure is not a super type of
501+ * subtype
391502 */
392503 public static Type [] getParams (Class <?> subType , Class <?> superErasure ) {
393504 Type pt = Types .parameterizeRaw (subType );
@@ -416,8 +527,8 @@ public static Class<?> getClass(final Object obj) {
416527
417528 /**
418529 * Finds the levels of casting between <code>origin</code> and
419- * <code>dest</code>. Returns 0 if dest and origin are the same. Returns -1
420- * if dest is not assignable from origin.
530+ * <code>dest</code>. Returns 0 if dest and origin are the same. Returns -1 if
531+ * dest is not assignable from origin.
421532 */
422533 public static int findCastLevels (final Class <?> dest , final Class <?> origin ) {
423534 if (dest .equals (origin ))
0 commit comments