/rpython/annotator/binaryop.py
Python | 836 lines | 645 code | 160 blank | 31 comment | 144 complexity | 6a4c7bf26fb8919958bfbc9a20922658 MD5 | raw file
Possible License(s): AGPL-3.0, BSD-3-Clause, Apache-2.0
- """
- Binary operations between SomeValues.
- """
- from collections import defaultdict
- from rpython.tool.pairtype import pair, pairtype
- from rpython.annotator.model import (
- SomeObject, SomeInteger, SomeBool, s_Bool, SomeString, SomeChar, SomeList,
- SomeDict, SomeUnicodeCodePoint, SomeUnicodeString, SomeException,
- SomeTuple, SomeImpossibleValue, s_ImpossibleValue, SomeInstance,
- SomeBuiltinMethod, SomeIterator, SomePBC, SomeNone, SomeFloat, s_None,
- SomeByteArray, SomeWeakRef, SomeSingleFloat,
- SomeLongFloat, SomeType, SomeTypeOf, SomeConstantType, unionof, UnionError,
- read_can_only_throw, add_knowntypedata,
- merge_knowntypedata,)
- from rpython.annotator.bookkeeper import immutablevalue, getbookkeeper
- from rpython.flowspace.model import Variable, Constant, const
- from rpython.flowspace.operation import op
- from rpython.rlib import rarithmetic
- from rpython.annotator.model import AnnotatorError
- BINARY_OPERATIONS = set([oper.opname for oper in op.__dict__.values()
- if oper.dispatch == 2])
- @op.is_.register(SomeObject, SomeObject)
- def is__default(annotator, obj1, obj2):
- r = SomeBool()
- s_obj1 = annotator.annotation(obj1)
- s_obj2 = annotator.annotation(obj2)
- if s_obj2.is_constant():
- if s_obj1.is_constant():
- r.const = s_obj1.const is s_obj2.const
- if s_obj2.const is None and not s_obj1.can_be_none():
- r.const = False
- elif s_obj1.is_constant():
- if s_obj1.const is None and not s_obj2.can_be_none():
- r.const = False
- knowntypedata = defaultdict(dict)
- bk = annotator.bookkeeper
- def bind(src_obj, tgt_obj):
- s_src = annotator.annotation(src_obj)
- s_tgt = annotator.annotation(tgt_obj)
- if hasattr(s_tgt, 'is_type_of') and s_src.is_constant():
- add_knowntypedata(
- knowntypedata, True,
- s_tgt.is_type_of,
- bk.valueoftype(s_src.const))
- add_knowntypedata(knowntypedata, True, [tgt_obj], s_src)
- s_nonnone = s_tgt
- if (s_src.is_constant() and s_src.const is None and
- s_tgt.can_be_none()):
- s_nonnone = s_tgt.nonnoneify()
- add_knowntypedata(knowntypedata, False, [tgt_obj], s_nonnone)
- bind(obj2, obj1)
- bind(obj1, obj2)
- r.set_knowntypedata(knowntypedata)
- return r
- def _make_cmp_annotator_default(cmp_op):
- @cmp_op.register(SomeObject, SomeObject)
- def default_annotate(annotator, obj1, obj2):
- s_1, s_2 = annotator.annotation(obj1), annotator.annotation(obj2)
- if s_1.is_immutable_constant() and s_2.is_immutable_constant():
- return immutablevalue(cmp_op.pyfunc(s_1.const, s_2.const))
- else:
- return s_Bool
- for cmp_op in [op.lt, op.le, op.eq, op.ne, op.gt, op.ge]:
- _make_cmp_annotator_default(cmp_op)
- @op.getitem.register(SomeObject, SomeObject)
- def getitem_default(ann, v_obj, v_index):
- return s_ImpossibleValue
- def _getitem_can_only_throw(s_c1, s_o2):
- impl = op.getitem.get_specialization(s_c1, s_o2)
- return read_can_only_throw(impl, s_c1, s_o2)
- @op.getitem_idx.register(SomeObject, SomeObject)
- def getitem_idx(ann, v_obj, v_index):
- s_obj = ann.annotation(v_obj)
- s_index = ann.annotation(v_index)
- impl = op.getitem.get_specialization(s_obj, s_index)
- return impl(ann, v_obj, v_index)
- getitem_idx.can_only_throw = _getitem_can_only_throw
- class __extend__(pairtype(SomeObject, SomeObject)):
- def union((obj1, obj2)):
- raise UnionError(obj1, obj2)
- # inplace_xxx ---> xxx by default
- def inplace_add((obj1, obj2)): return pair(obj1, obj2).add()
- def inplace_sub((obj1, obj2)): return pair(obj1, obj2).sub()
- def inplace_mul((obj1, obj2)): return pair(obj1, obj2).mul()
- def inplace_truediv((obj1, obj2)): return pair(obj1, obj2).truediv()
- def inplace_floordiv((obj1, obj2)): return pair(obj1, obj2).floordiv()
- def inplace_div((obj1, obj2)): return pair(obj1, obj2).div()
- def inplace_mod((obj1, obj2)): return pair(obj1, obj2).mod()
- def inplace_lshift((obj1, obj2)): return pair(obj1, obj2).lshift()
- def inplace_rshift((obj1, obj2)): return pair(obj1, obj2).rshift()
- def inplace_and((obj1, obj2)): return pair(obj1, obj2).and_()
- def inplace_or((obj1, obj2)): return pair(obj1, obj2).or_()
- def inplace_xor((obj1, obj2)): return pair(obj1, obj2).xor()
- for name, func in locals().items():
- if name.startswith('inplace_'):
- func.can_only_throw = []
- inplace_div.can_only_throw = [ZeroDivisionError]
- inplace_truediv.can_only_throw = [ZeroDivisionError]
- inplace_floordiv.can_only_throw = [ZeroDivisionError]
- inplace_mod.can_only_throw = [ZeroDivisionError]
- def cmp((obj1, obj2)):
- if obj1.is_immutable_constant() and obj2.is_immutable_constant():
- return immutablevalue(cmp(obj1.const, obj2.const))
- else:
- return SomeInteger()
- def divmod((obj1, obj2)):
- return SomeTuple([pair(obj1, obj2).div(), pair(obj1, obj2).mod()])
- def coerce((obj1, obj2)):
- return pair(obj1, obj2).union() # reasonable enough
- def add((obj1, obj2)):
- return s_ImpossibleValue
- sub = mul = truediv = floordiv = div = mod = add
- lshift = rshift = and_ = or_ = xor = delitem = add
- def setitem((obj1, obj2), _):
- return s_ImpossibleValue
- # approximation of an annotation intersection, the result should be the annotation obj or
- # the intersection of obj and improvement
- def improve((obj, improvement)):
- if not improvement.contains(obj) and obj.contains(improvement):
- return improvement
- else:
- return obj
- class __extend__(pairtype(SomeType, SomeType),
- pairtype(SomeType, SomeConstantType),
- pairtype(SomeConstantType, SomeType),):
- def union((obj1, obj2)):
- result = SomeType()
- if obj1.is_immutable_constant() and obj2.is_immutable_constant() and obj1.const == obj2.const:
- result.const = obj1.const
- return result
- class __extend__(pairtype(SomeTypeOf, SomeTypeOf)):
- def union((s_obj1, s_obj2)):
- vars = list(set(s_obj1.is_type_of) | set(s_obj2.is_type_of))
- result = SomeTypeOf(vars)
- if (s_obj1.is_immutable_constant() and s_obj2.is_immutable_constant()
- and s_obj1.const == s_obj2.const):
- result.const = obj1.const
- return result
- # cloning a function with identical code, for the can_only_throw attribute
- def _clone(f, can_only_throw = None):
- newfunc = type(f)(f.func_code, f.func_globals, f.func_name,
- f.func_defaults, f.func_closure)
- if can_only_throw is not None:
- newfunc.can_only_throw = can_only_throw
- return newfunc
- class __extend__(pairtype(SomeInteger, SomeInteger)):
- # unsignedness is considered a rare and contagious disease
- def union((int1, int2)):
- if int1.unsigned == int2.unsigned:
- knowntype = rarithmetic.compute_restype(int1.knowntype, int2.knowntype)
- else:
- t1 = int1.knowntype
- if t1 is bool:
- t1 = int
- t2 = int2.knowntype
- if t2 is bool:
- t2 = int
- if t2 is int:
- if int2.nonneg == False:
- raise UnionError(int1, int2, "RPython cannot prove that these " + \
- "integers are of the same signedness")
- knowntype = t1
- elif t1 is int:
- if int1.nonneg == False:
- raise UnionError(int1, int2, "RPython cannot prove that these " + \
- "integers are of the same signedness")
- knowntype = t2
- else:
- raise UnionError(int1, int2)
- return SomeInteger(nonneg=int1.nonneg and int2.nonneg,
- knowntype=knowntype)
- or_ = xor = add = mul = _clone(union, [])
- add_ovf = mul_ovf = _clone(union, [OverflowError])
- div = floordiv = mod = _clone(union, [ZeroDivisionError])
- div_ovf= floordiv_ovf = mod_ovf = _clone(union, [ZeroDivisionError, OverflowError])
- def truediv((int1, int2)):
- return SomeFloat()
- truediv.can_only_throw = [ZeroDivisionError]
- truediv_ovf = _clone(truediv, [ZeroDivisionError, OverflowError])
- inplace_div = div
- inplace_truediv = truediv
- def sub((int1, int2)):
- knowntype = rarithmetic.compute_restype(int1.knowntype, int2.knowntype)
- return SomeInteger(knowntype=knowntype)
- sub.can_only_throw = []
- sub_ovf = _clone(sub, [OverflowError])
- def and_((int1, int2)):
- knowntype = rarithmetic.compute_restype(int1.knowntype, int2.knowntype)
- return SomeInteger(nonneg=int1.nonneg or int2.nonneg,
- knowntype=knowntype)
- and_.can_only_throw = []
- def lshift((int1, int2)):
- if isinstance(int1, SomeBool):
- return SomeInteger()
- else:
- return SomeInteger(knowntype=int1.knowntype)
- lshift.can_only_throw = []
- lshift_ovf = _clone(lshift, [OverflowError])
- def rshift((int1, int2)):
- if isinstance(int1, SomeBool):
- return SomeInteger(nonneg=True)
- else:
- return SomeInteger(nonneg=int1.nonneg, knowntype=int1.knowntype)
- rshift.can_only_throw = []
- def _make_cmp_annotator_int(cmp_op):
- @cmp_op.register(SomeInteger, SomeInteger)
- def _compare_helper(annotator, int1, int2):
- r = SomeBool()
- s_int1, s_int2 = annotator.annotation(int1), annotator.annotation(int2)
- if s_int1.is_immutable_constant() and s_int2.is_immutable_constant():
- r.const = cmp_op.pyfunc(s_int1.const, s_int2.const)
- #
- # The rest of the code propagates nonneg information between
- # the two arguments.
- #
- # Doing the right thing when int1 or int2 change from signed
- # to unsigned (r_uint) is almost impossible. See test_intcmp_bug.
- # Instead, we only deduce constrains on the operands in the
- # case where they are both signed. In other words, if y is
- # nonneg then "assert x>=y" will let the annotator know that
- # x is nonneg too, but it will not work if y is unsigned.
- #
- if not (rarithmetic.signedtype(s_int1.knowntype) and
- rarithmetic.signedtype(s_int2.knowntype)):
- return r
- knowntypedata = defaultdict(dict)
- def tointtype(s_int0):
- if s_int0.knowntype is bool:
- return int
- return s_int0.knowntype
- if s_int1.nonneg and isinstance(int2, Variable):
- case = cmp_op.opname in ('lt', 'le', 'eq')
- add_knowntypedata(knowntypedata, case, [int2],
- SomeInteger(nonneg=True, knowntype=tointtype(s_int2)))
- if s_int2.nonneg and isinstance(int1, Variable):
- case = cmp_op.opname in ('gt', 'ge', 'eq')
- add_knowntypedata(knowntypedata, case, [int1],
- SomeInteger(nonneg=True, knowntype=tointtype(s_int1)))
- r.set_knowntypedata(knowntypedata)
- # a special case for 'x < 0' or 'x >= 0',
- # where 0 is a flow graph Constant
- # (in this case we are sure that it cannot become a r_uint later)
- if (isinstance(int2, Constant) and
- type(int2.value) is int and # filter out Symbolics
- int2.value == 0):
- if s_int1.nonneg:
- if cmp_op.opname == 'lt':
- r.const = False
- if cmp_op.opname == 'ge':
- r.const = True
- return r
- for cmp_op in [op.lt, op.le, op.eq, op.ne, op.gt, op.ge]:
- _make_cmp_annotator_int(cmp_op)
- class __extend__(pairtype(SomeBool, SomeBool)):
- def union((boo1, boo2)):
- s = SomeBool()
- if getattr(boo1, 'const', -1) == getattr(boo2, 'const', -2):
- s.const = boo1.const
- if hasattr(boo1, 'knowntypedata') and \
- hasattr(boo2, 'knowntypedata'):
- ktd = merge_knowntypedata(boo1.knowntypedata, boo2.knowntypedata)
- s.set_knowntypedata(ktd)
- return s
- def and_((boo1, boo2)):
- s = SomeBool()
- if boo1.is_constant():
- if not boo1.const:
- s.const = False
- else:
- return boo2
- if boo2.is_constant():
- if not boo2.const:
- s.const = False
- return s
- def or_((boo1, boo2)):
- s = SomeBool()
- if boo1.is_constant():
- if boo1.const:
- s.const = True
- else:
- return boo2
- if boo2.is_constant():
- if boo2.const:
- s.const = True
- return s
- def xor((boo1, boo2)):
- s = SomeBool()
- if boo1.is_constant() and boo2.is_constant():
- s.const = boo1.const ^ boo2.const
- return s
- class __extend__(pairtype(SomeString, SomeString)):
- def union((str1, str2)):
- can_be_None = str1.can_be_None or str2.can_be_None
- no_nul = str1.no_nul and str2.no_nul
- return SomeString(can_be_None=can_be_None, no_nul=no_nul)
- def add((str1, str2)):
- # propagate const-ness to help getattr(obj, 'prefix' + const_name)
- result = SomeString(no_nul=str1.no_nul and str2.no_nul)
- if str1.is_immutable_constant() and str2.is_immutable_constant():
- result.const = str1.const + str2.const
- return result
- class __extend__(pairtype(SomeByteArray, SomeByteArray)):
- def union((b1, b2)):
- can_be_None = b1.can_be_None or b2.can_be_None
- return SomeByteArray(can_be_None=can_be_None)
- def add((b1, b2)):
- return SomeByteArray()
- class __extend__(pairtype(SomeByteArray, SomeInteger)):
- def getitem((s_b, s_i)):
- return SomeInteger()
- def setitem((s_b, s_i), s_i2):
- assert isinstance(s_i2, SomeInteger)
- class __extend__(pairtype(SomeString, SomeByteArray),
- pairtype(SomeByteArray, SomeString),
- pairtype(SomeChar, SomeByteArray),
- pairtype(SomeByteArray, SomeChar)):
- def add((b1, b2)):
- return SomeByteArray()
- class __extend__(pairtype(SomeChar, SomeChar)):
- def union((chr1, chr2)):
- no_nul = chr1.no_nul and chr2.no_nul
- return SomeChar(no_nul=no_nul)
- class __extend__(pairtype(SomeChar, SomeUnicodeCodePoint),
- pairtype(SomeUnicodeCodePoint, SomeChar)):
- def union((uchr1, uchr2)):
- return SomeUnicodeCodePoint()
- class __extend__(pairtype(SomeUnicodeCodePoint, SomeUnicodeCodePoint)):
- def union((uchr1, uchr2)):
- no_nul = uchr1.no_nul and uchr2.no_nul
- return SomeUnicodeCodePoint(no_nul=no_nul)
- def add((chr1, chr2)):
- return SomeUnicodeString()
- class __extend__(pairtype(SomeString, SomeUnicodeString),
- pairtype(SomeUnicodeString, SomeString)):
- def mod((str, unistring)):
- raise AnnotatorError(
- "string formatting mixing strings and unicode not supported")
- class __extend__(pairtype(SomeString, SomeTuple),
- pairtype(SomeUnicodeString, SomeTuple)):
- def mod((s_string, s_tuple)):
- if not s_string.is_constant():
- raise AnnotatorError("string formatting requires a constant "
- "string/unicode on the left of '%'")
- is_string = isinstance(s_string, SomeString)
- is_unicode = isinstance(s_string, SomeUnicodeString)
- assert is_string or is_unicode
- for s_item in s_tuple.items:
- if (is_unicode and isinstance(s_item, (SomeChar, SomeString)) or
- is_string and isinstance(s_item, (SomeUnicodeCodePoint,
- SomeUnicodeString))):
- raise AnnotatorError(
- "string formatting mixing strings and unicode not supported")
- no_nul = s_string.no_nul
- for s_item in s_tuple.items:
- if isinstance(s_item, SomeFloat):
- pass # or s_item is a subclass, like SomeInteger
- elif (isinstance(s_item, SomeString) or
- isinstance(s_item, SomeUnicodeString)) and s_item.no_nul:
- pass
- else:
- no_nul = False
- break
- return s_string.__class__(no_nul=no_nul)
- class __extend__(pairtype(SomeString, SomeObject),
- pairtype(SomeUnicodeString, SomeObject)):
- def mod((s_string, s_arg)):
- assert not isinstance(s_arg, SomeTuple)
- return pair(s_string, SomeTuple([s_arg])).mod()
- class __extend__(pairtype(SomeFloat, SomeFloat)):
- def union((flt1, flt2)):
- return SomeFloat()
- add = sub = mul = union
- def div((flt1, flt2)):
- return SomeFloat()
- div.can_only_throw = []
- truediv = div
- # repeat these in order to copy the 'can_only_throw' attribute
- inplace_div = div
- inplace_truediv = truediv
- class __extend__(pairtype(SomeSingleFloat, SomeSingleFloat)):
- def union((flt1, flt2)):
- return SomeSingleFloat()
- class __extend__(pairtype(SomeLongFloat, SomeLongFloat)):
- def union((flt1, flt2)):
- return SomeLongFloat()
- class __extend__(pairtype(SomeList, SomeList)):
- def union((lst1, lst2)):
- return SomeList(lst1.listdef.union(lst2.listdef))
- def add((lst1, lst2)):
- bk = getbookkeeper()
- return lst1.listdef.offspring(bk, lst2.listdef)
- def eq((lst1, lst2)):
- lst1.listdef.agree(getbookkeeper(), lst2.listdef)
- return s_Bool
- ne = eq
- class __extend__(pairtype(SomeList, SomeObject)):
- def inplace_add((lst1, obj2)):
- lst1.method_extend(obj2)
- return lst1
- inplace_add.can_only_throw = []
- def inplace_mul((lst1, obj2)):
- lst1.listdef.resize()
- return lst1
- inplace_mul.can_only_throw = []
- class __extend__(pairtype(SomeTuple, SomeTuple)):
- def union((tup1, tup2)):
- if len(tup1.items) != len(tup2.items):
- raise UnionError(tup1, tup2, "RPython cannot unify tuples of "
- "different length: %d versus %d" % \
- (len(tup1.items), len(tup2.items)))
- else:
- unions = [unionof(x,y) for x,y in zip(tup1.items, tup2.items)]
- return SomeTuple(items = unions)
- def add((tup1, tup2)):
- return SomeTuple(items = tup1.items + tup2.items)
- def eq(tup1tup2):
- tup1tup2.union()
- return s_Bool
- ne = eq
- def lt((tup1, tup2)):
- raise Exception("unsupported: (...) < (...)")
- def le((tup1, tup2)):
- raise Exception("unsupported: (...) <= (...)")
- def gt((tup1, tup2)):
- raise Exception("unsupported: (...) > (...)")
- def ge((tup1, tup2)):
- raise Exception("unsupported: (...) >= (...)")
- class __extend__(pairtype(SomeDict, SomeDict)):
- def union((dic1, dic2)):
- assert dic1.__class__ == dic2.__class__
- return dic1.__class__(dic1.dictdef.union(dic2.dictdef))
- def _dict_can_only_throw_keyerror(s_dct, *ignore):
- if s_dct.dictdef.dictkey.custom_eq_hash:
- return None # r_dict: can throw anything
- return [KeyError]
- def _dict_can_only_throw_nothing(s_dct, *ignore):
- if s_dct.dictdef.dictkey.custom_eq_hash:
- return None # r_dict: can throw anything
- return [] # else: no possible exception
- @op.getitem.register(SomeDict, SomeObject)
- def getitem_SomeDict(annotator, v_dict, v_key):
- s_dict = annotator.annotation(v_dict)
- s_key = annotator.annotation(v_key)
- s_dict.dictdef.generalize_key(s_key)
- position = annotator.bookkeeper.position_key
- return s_dict.dictdef.read_value(position)
- getitem_SomeDict.can_only_throw = _dict_can_only_throw_keyerror
- class __extend__(pairtype(SomeDict, SomeObject)):
- def setitem((dic1, obj2), s_value):
- dic1.dictdef.generalize_key(obj2)
- dic1.dictdef.generalize_value(s_value)
- setitem.can_only_throw = _dict_can_only_throw_nothing
- def delitem((dic1, obj2)):
- dic1.dictdef.generalize_key(obj2)
- delitem.can_only_throw = _dict_can_only_throw_keyerror
- class __extend__(pairtype(SomeTuple, SomeInteger)):
- def getitem((tup1, int2)):
- if int2.is_immutable_constant():
- try:
- return tup1.items[int2.const]
- except IndexError:
- return s_ImpossibleValue
- else:
- return unionof(*tup1.items)
- getitem.can_only_throw = [IndexError]
- class __extend__(pairtype(SomeList, SomeInteger)):
- def mul((lst1, int2)):
- bk = getbookkeeper()
- return lst1.listdef.offspring(bk)
- def getitem((lst1, int2)):
- position = getbookkeeper().position_key
- return lst1.listdef.read_item(position)
- getitem.can_only_throw = []
- def getitem_idx((lst1, int2)):
- position = getbookkeeper().position_key
- return lst1.listdef.read_item(position)
- getitem_idx.can_only_throw = [IndexError]
- def setitem((lst1, int2), s_value):
- lst1.listdef.mutate()
- lst1.listdef.generalize(s_value)
- setitem.can_only_throw = [IndexError]
- def delitem((lst1, int2)):
- lst1.listdef.resize()
- delitem.can_only_throw = [IndexError]
- class __extend__(pairtype(SomeString, SomeInteger)):
- def getitem((str1, int2)):
- return SomeChar(no_nul=str1.no_nul)
- getitem.can_only_throw = []
- def getitem_idx((str1, int2)):
- return SomeChar(no_nul=str1.no_nul)
- getitem_idx.can_only_throw = [IndexError]
- def mul((str1, int2)): # xxx do we want to support this
- return SomeString(no_nul=str1.no_nul)
- class __extend__(pairtype(SomeUnicodeString, SomeInteger)):
- def getitem((str1, int2)):
- return SomeUnicodeCodePoint(no_nul=str1.no_nul)
- getitem.can_only_throw = []
- def getitem_idx((str1, int2)):
- return SomeUnicodeCodePoint(no_nul=str1.no_nul)
- getitem_idx.can_only_throw = [IndexError]
- def mul((str1, int2)): # xxx do we want to support this
- return SomeUnicodeString(no_nul=str1.no_nul)
- class __extend__(pairtype(SomeInteger, SomeString),
- pairtype(SomeInteger, SomeUnicodeString)):
- def mul((int1, str2)): # xxx do we want to support this
- return str2.basestringclass(no_nul=str2.no_nul)
- class __extend__(pairtype(SomeUnicodeCodePoint, SomeUnicodeString),
- pairtype(SomeUnicodeString, SomeUnicodeCodePoint),
- pairtype(SomeUnicodeString, SomeUnicodeString)):
- def union((str1, str2)):
- can_be_None = str1.can_be_None or str2.can_be_None
- no_nul = str1.no_nul and str2.no_nul
- return SomeUnicodeString(can_be_None=can_be_None, no_nul=no_nul)
- def add((str1, str2)):
- # propagate const-ness to help getattr(obj, 'prefix' + const_name)
- result = SomeUnicodeString(no_nul=str1.no_nul and str2.no_nul)
- if str1.is_immutable_constant() and str2.is_immutable_constant():
- result.const = str1.const + str2.const
- return result
- class __extend__(pairtype(SomeInteger, SomeList)):
- def mul((int1, lst2)):
- bk = getbookkeeper()
- return lst2.listdef.offspring(bk)
- class __extend__(pairtype(SomeInstance, SomeInstance)):
- def union((ins1, ins2)):
- if ins1.classdef is None or ins2.classdef is None:
- # special case only
- basedef = None
- else:
- basedef = ins1.classdef.commonbase(ins2.classdef)
- if basedef is None:
- raise UnionError(ins1, ins2, "RPython cannot unify instances "
- "with no common base class")
- flags = ins1.flags
- if flags:
- flags = flags.copy()
- for key, value in flags.items():
- if key not in ins2.flags or ins2.flags[key] != value:
- del flags[key]
- return SomeInstance(basedef,
- can_be_None=ins1.can_be_None or ins2.can_be_None,
- flags=flags)
- def improve((ins1, ins2)):
- if ins1.classdef is None:
- resdef = ins2.classdef
- elif ins2.classdef is None:
- resdef = ins1.classdef
- else:
- basedef = ins1.classdef.commonbase(ins2.classdef)
- if basedef is ins1.classdef:
- resdef = ins2.classdef
- elif basedef is ins2.classdef:
- resdef = ins1.classdef
- else:
- if ins1.can_be_None and ins2.can_be_None:
- return s_None
- else:
- return s_ImpossibleValue
- res = SomeInstance(resdef, can_be_None=ins1.can_be_None and ins2.can_be_None)
- if ins1.contains(res) and ins2.contains(res):
- return res # fine
- else:
- # this case can occur in the presence of 'const' attributes,
- # which we should try to preserve. Fall-back...
- thistype = pairtype(SomeInstance, SomeInstance)
- return super(thistype, pair(ins1, ins2)).improve()
- class __extend__(
- pairtype(SomeException, SomeInstance),
- pairtype(SomeException, SomeNone)):
- def union((s_exc, s_inst)):
- return unionof(s_exc.as_SomeInstance(), s_inst)
- class __extend__(
- pairtype(SomeInstance, SomeException),
- pairtype(SomeNone, SomeException)):
- def union((s_inst, s_exc)):
- return unionof(s_exc.as_SomeInstance(), s_inst)
- class __extend__(pairtype(SomeException, SomeException)):
- def union((s_exc1, s_exc2)):
- return SomeException(s_exc1.classdefs | s_exc2.classdefs)
- @op.getitem.register_transform(SomeInstance, SomeObject)
- def getitem_SomeInstance(annotator, v_ins, v_idx):
- get_getitem = op.getattr(v_ins, const('__getitem__'))
- return [get_getitem, op.simple_call(get_getitem.result, v_idx)]
- @op.setitem.register_transform(SomeInstance, SomeObject)
- def setitem_SomeInstance(annotator, v_ins, v_idx, v_value):
- get_setitem = op.getattr(v_ins, const('__setitem__'))
- return [get_setitem,
- op.simple_call(get_setitem.result, v_idx, v_value)]
- class __extend__(pairtype(SomeIterator, SomeIterator)):
- def union((iter1, iter2)):
- s_cont = unionof(iter1.s_container, iter2.s_container)
- if iter1.variant != iter2.variant:
- raise UnionError(iter1, iter2,
- "RPython cannot unify incompatible iterator variants")
- return SomeIterator(s_cont, *iter1.variant)
- class __extend__(pairtype(SomeBuiltinMethod, SomeBuiltinMethod)):
- def union((bltn1, bltn2)):
- if (bltn1.analyser != bltn2.analyser or
- bltn1.methodname != bltn2.methodname):
- raise UnionError(bltn1, bltn2)
- s_self = unionof(bltn1.s_self, bltn2.s_self)
- return SomeBuiltinMethod(bltn1.analyser, s_self,
- methodname=bltn1.methodname)
- @op.is_.register(SomePBC, SomePBC)
- def is__PBC_PBC(annotator, pbc1, pbc2):
- s = is__default(annotator, pbc1, pbc2)
- if not s.is_constant():
- s_pbc1 = annotator.annotation(pbc1)
- s_pbc2 = annotator.annotation(pbc2)
- if not s_pbc1.can_be_None or not s_pbc2.can_be_None:
- for desc in s_pbc1.descriptions:
- if desc in s_pbc2.descriptions:
- break
- else:
- s.const = False # no common desc in the two sets
- return s
- class __extend__(pairtype(SomePBC, SomePBC)):
- def union((pbc1, pbc2)):
- d = pbc1.descriptions.copy()
- d.update(pbc2.descriptions)
- return SomePBC(d, can_be_None = pbc1.can_be_None or pbc2.can_be_None)
- class __extend__(pairtype(SomeImpossibleValue, SomeObject)):
- def union((imp1, obj2)):
- return obj2
- class __extend__(pairtype(SomeObject, SomeImpossibleValue)):
- def union((obj1, imp2)):
- return obj1
- # mixing Nones with other objects
- class __extend__(pairtype(SomeObject, SomeNone)):
- def union((obj, none)):
- return obj.noneify()
- class __extend__(pairtype(SomeNone, SomeObject)):
- def union((none, obj)):
- return obj.noneify()
- class __extend__(pairtype(SomeImpossibleValue, SomeNone)):
- def union((imp1, none)):
- return s_None
- class __extend__(pairtype(SomeNone, SomeImpossibleValue)):
- def union((none, imp2)):
- return s_None
- class __extend__(pairtype(SomePBC, SomeObject)):
- def getitem((pbc, o)):
- raise AnnotatorError("getitem on %r" % pbc)
- def setitem((pbc, o), s_value):
- raise AnnotatorError("setitem on %r" % pbc)
- class __extend__(pairtype(SomeNone, SomeObject)):
- def getitem((none, o)):
- return s_ImpossibleValue
- getitem.can_only_throw = []
- def setitem((none, o), s_value):
- return None
- class __extend__(pairtype(SomePBC, SomeString)):
- def add((pbc, o)):
- raise AnnotatorError('add on %r' % pbc)
- class __extend__(pairtype(SomeNone, SomeString)):
- def add((none, o)):
- return s_ImpossibleValue
- class __extend__(pairtype(SomeString, SomePBC)):
- def add((o, pbc)):
- raise AnnotatorError('add on %r' % pbc)
- class __extend__(pairtype(SomeString, SomeNone)):
- def add((o, none)):
- return s_ImpossibleValue
- #_________________________________________
- # weakrefs
- class __extend__(pairtype(SomeWeakRef, SomeWeakRef)):
- def union((s_wrf1, s_wrf2)):
- if s_wrf1.classdef is None:
- basedef = s_wrf2.classdef # s_wrf1 is known to be dead
- elif s_wrf2.classdef is None:
- basedef = s_wrf1.classdef # s_wrf2 is known to be dead
- else:
- basedef = s_wrf1.classdef.commonbase(s_wrf2.classdef)
- if basedef is None: # no common base class! complain...
- raise UnionError(s_wrf1, s_wrf2)
- return SomeWeakRef(basedef)