@@ -143,38 +143,38 @@ def process_tp(tp: type | RuntimeClass) -> JustTypeRef | type:
143143 return tp
144144
145145
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 ]
178178
179179
180180def identity (x : object ) -> object :
0 commit comments