@@ -143,38 +143,38 @@ def process_tp(tp: type | RuntimeClass) -> JustTypeRef | type:
143
143
return tp
144
144
145
145
146
- # def min_convertable_tp(a: object, b: object, name: str) -> JustTypeRef:
147
- # """
148
- # Returns the minimum convertable type between a and b, that has a method `name`, raising a ConvertError if no such type exists.
149
- # """
150
- # decls = _retrieve_conversion_decls().copy()
151
- # if isinstance(a, RuntimeExpr):
152
- # decls |= a
153
- # if isinstance(b, RuntimeExpr):
154
- # decls |= b
155
-
156
- # a_tp = _get_tp(a )
157
- # b_tp = _get_tp(b )
158
- # # Make sure at least one of the types has the method, to avoid issue with == upcasting improperly
159
- # if not (
160
- # (isinstance(a_tp, JustTypeRef) and decls.has_method(a_tp.name, name))
161
- # or ( isinstance(b_tp , JustTypeRef) and decls.has_method(b_tp.name, name) )
162
- # ):
163
- # raise ConvertError(f"Neither {a_tp} nor {b_tp} has method {name}" )
164
- # a_converts_to = {
165
- # to: c for ((from_, to), (c, _)) in CONVERSIONS.items() if from_ == a_tp and decls.has_method(to.name, name )
166
- # }
167
- # b_converts_to = {
168
- # to: c for ((from_, to), (c, _)) in CONVERSIONS.items() if from_ == b_tp and decls.has_method(to.name, name)
169
- # }
170
- # if isinstance(a_tp, JustTypeRef) and decls.has_method(a_tp.name, name ):
171
- # a_converts_to[a_tp] = 0
172
- # if isinstance(b_tp, JustTypeRef) and decls.has_method(b_tp.name, name):
173
- # b_converts_to[b_tp] = 0
174
- # common = set(a_converts_to) & set(b_converts_to )
175
- # if not common :
176
- # raise ConvertError(f"Cannot convert {a_tp} and {b_tp} to a common type")
177
- # return min(common, key=lambda tp: a_converts_to[tp] + b_converts_to[tp])
146
+ def min_binary_conversion (
147
+ method_name : str , lhs : type | JustTypeRef , rhs : type | JustTypeRef
148
+ ) -> tuple [ Callable [[ Any ], RuntimeExpr ], Callable [[ Any ], RuntimeExpr ]] | None :
149
+ """
150
+ Given a binary method and two starting types for the LHS and RHS, return a pair of callable which will convert
151
+ the LHS and RHS to appropriate types which support this method. If no such conversion is possible, return None.
152
+
153
+ It should return the types which minimize the total conversion cost. If one of the types is a Python type, then
154
+ both of them can be converted. However, if both are egglog types, then only one of them can be converted.
155
+ """
156
+ decls = retrieve_conversion_decls ( )
157
+ # tuple of (cost, convert_self )
158
+ best_method : tuple [ int , Callable [[ Any ], RuntimeExpr ]] | None = None
159
+ # Start by checking if we have a LHS that matches exactly and a RHS which can be converted
160
+ if (
161
+ isinstance (lhs , JustTypeRef )
162
+ and ( desired_other_type := decls . check_binary_method_with_self_type ( method_name , lhs ))
163
+ and ( converter := CONVERSIONS . get (( rhs , desired_other_type )) )
164
+ ):
165
+ best_method = ( converter [ 0 ], lambda x : x )
166
+
167
+ # Next see if it's possible to convert the LHS and keep the RHS as is
168
+ if isinstance ( rhs , JustTypeRef ):
169
+ decls = retrieve_conversion_decls ()
170
+ for desired_self_type in decls .check_binary_method_with_other_type ( method_name , rhs ):
171
+ if converter := CONVERSIONS . get (( lhs , desired_self_type )):
172
+ cost , convert_self = converter
173
+ if best_method is None or best_method [ 0 ] > cost :
174
+ best_method = ( cost , convert_self )
175
+ if best_method is None :
176
+ return None
177
+ return best_method [ 1 ], best_method [ 1 ]
178
178
179
179
180
180
def identity (x : object ) -> object :
0 commit comments