Author: Ronan Lamy <[email protected]>
Branch: 
Changeset: r80818:f174c2bebfa6
Date: 2015-11-21 16:39 +0000
http://bitbucket.org/pypy/pypy/changeset/f174c2bebfa6/

Log:    Merged in anntype (pull request #359)

        Refactor and improve exception analysis in the annotator.

diff --git a/rpython/annotator/annrpython.py b/rpython/annotator/annrpython.py
--- a/rpython/annotator/annrpython.py
+++ b/rpython/annotator/annrpython.py
@@ -1,15 +1,17 @@
 from __future__ import absolute_import
 
 import types
+from collections import defaultdict
 
 from rpython.tool.ansi_print import ansi_log
 from rpython.tool.pairtype import pair
 from rpython.tool.error import (format_blocked_annotation_error,
                              gather_error, source_lines)
-from rpython.flowspace.model import (
-    Variable, Constant, FunctionGraph, checkgraph)
+from rpython.flowspace.model import Variable, Constant, checkgraph
 from rpython.translator import simplify, transform
 from rpython.annotator import model as annmodel, signature
+from rpython.annotator.model import (
+        typeof, SomeExceptCase, s_ImpossibleValue)
 from rpython.annotator.bookkeeper import Bookkeeper
 from rpython.rtyper.normalizecalls import perform_normalizations
 
@@ -209,7 +211,7 @@
         for graph in newgraphs:
             v = graph.getreturnvar()
             if v.annotation is None:
-                self.setbinding(v, annmodel.s_ImpossibleValue)
+                self.setbinding(v, s_ImpossibleValue)
 
     def validate(self):
         """Check that the annotation results are valid"""
@@ -281,7 +283,7 @@
         except KeyError:
             # the function didn't reach any return statement so far.
             # (some functions actually never do, they always raise exceptions)
-            return annmodel.s_ImpossibleValue
+            return s_ImpossibleValue
 
     def reflowfromposition(self, position_key):
         graph, block, index = position_key
@@ -387,6 +389,34 @@
         if unions != oldcells:
             self.bindinputargs(graph, block, unions)
 
+    def apply_renaming(self, s_out, renaming):
+        if hasattr(s_out, 'is_type_of'):
+            renamed_is_type_of = []
+            for v in s_out.is_type_of:
+                renamed_is_type_of += renaming[v]
+            assert s_out.knowntype is type
+            newcell = typeof(renamed_is_type_of)
+            if s_out.is_constant():
+                newcell.const = s_out.const
+            s_out = newcell
+
+        if hasattr(s_out, 'knowntypedata'):
+            renamed_knowntypedata = {}
+            for value, constraints in s_out.knowntypedata.items():
+                renamed_knowntypedata[value] = {}
+                for v, s in constraints.items():
+                    new_vs = renaming.get(v, [])
+                    for new_v in new_vs:
+                        renamed_knowntypedata[value][new_v] = s
+            assert isinstance(s_out, annmodel.SomeBool)
+            newcell = annmodel.SomeBool()
+            if s_out.is_constant():
+                newcell.const = s_out.const
+            s_out = newcell
+            s_out.set_knowntypedata(renamed_knowntypedata)
+        return s_out
+
+
     def whereami(self, position_key):
         graph, block, i = position_key
         blk = ""
@@ -456,33 +486,43 @@
                     exits = [link for link in exits
                                   if link.exitcase == s_exitswitch.const]
 
-        # filter out those exceptions which cannot
-        # occour for this specific, typed operation.
         if block.canraise:
             op = block.raising_op
             can_only_throw = op.get_can_only_throw(self)
             if can_only_throw is not None:
-                candidates = can_only_throw
-                candidate_exits = exits
-                exits = []
-                for link in candidate_exits:
+                # filter out those exceptions which cannot
+                # occur for this specific, typed operation.
+                s_exception = self.bookkeeper.new_exception(can_only_throw)
+                for link in exits:
                     case = link.exitcase
                     if case is None:
-                        exits.append(link)
+                        self.follow_link(graph, link, {})
                         continue
-                    covered = [c for c in candidates if issubclass(c, case)]
-                    if covered:
-                        exits.append(link)
-                        candidates = [c for c in candidates if c not in 
covered]
+                    if s_exception == s_ImpossibleValue:
+                        break
+                    s_case = SomeExceptCase(
+                            self.bookkeeper.getuniqueclassdef(case))
+                    s_matching_exc = s_exception.intersection(s_case)
+                    if s_matching_exc != s_ImpossibleValue:
+                        self.follow_raise_link(graph, link, s_matching_exc)
+                    s_exception = s_exception.difference(s_case)
+            else:
+                for link in exits:
+                    if link.exitcase is None:
+                        self.follow_link(graph, link, {})
+                    else:
+                        s_exception = 
self.bookkeeper.valueoftype(link.exitcase)
+                        self.follow_raise_link(graph, link, s_exception)
+        else:
+            if isinstance(block.exitswitch, Variable):
+                knowntypedata = getattr(block.exitswitch.annotation,
+                                            "knowntypedata", {})
+            else:
+                knowntypedata = {}
+            for link in exits:
+                constraints = knowntypedata.get(link.exitcase, {})
+                self.follow_link(graph, link, constraints)
 
-        # mapping (exitcase, variable) -> s_annotation
-        # that can be attached to booleans, exitswitches
-        knowntypedata = {}
-        if isinstance(block.exitswitch, Variable):
-            knowntypedata = getattr(self.binding(block.exitswitch),
-                                    "knowntypedata", {})
-        for link in exits:
-            self.follow_link(graph, link, knowntypedata)
         if block in self.notify:
             # reflow from certain positions when this block is done
             for callback in self.notify[block]:
@@ -491,84 +531,66 @@
                 else:
                     callback()
 
-    def follow_link(self, graph, link, knowntypedata):
-        in_except_block = False
-        v_last_exc_type = link.last_exception  # may be None for non-exception 
link
-        v_last_exc_value = link.last_exc_value  # may be None for 
non-exception link
 
-        if (isinstance(link.exitcase, (types.ClassType, type)) and
-                issubclass(link.exitcase, BaseException)):
-            assert v_last_exc_type and v_last_exc_value
-            s_last_exc_value = self.bookkeeper.valueoftype(link.exitcase)
-            s_last_exc_type = annmodel.SomeType()
-            if isinstance(v_last_exc_type, Constant):
-                s_last_exc_type.const = v_last_exc_type.value
-            s_last_exc_type.is_type_of = [v_last_exc_value]
-
-            if isinstance(v_last_exc_type, Variable):
-                self.setbinding(v_last_exc_type, s_last_exc_type)
-            if isinstance(v_last_exc_value, Variable):
-                self.setbinding(v_last_exc_value, s_last_exc_value)
-
-            s_last_exc_type = annmodel.SomeType()
-            if isinstance(v_last_exc_type, Constant):
-                s_last_exc_type.const = v_last_exc_type.value
-            last_exc_value_vars = []
-            in_except_block = True
+    def follow_link(self, graph, link, constraints):
+        assert not (isinstance(link.exitcase, (types.ClassType, type)) and
+                issubclass(link.exitcase, BaseException))
 
         ignore_link = False
         inputs_s = []
-        renaming = {}
+        renaming = defaultdict(list)
         for v_out, v_input in zip(link.args, link.target.inputargs):
-            renaming.setdefault(v_out, []).append(v_input)
-        for v_out, v_input in zip(link.args, link.target.inputargs):
-            if v_out == v_last_exc_type:
-                assert in_except_block
-                inputs_s.append(s_last_exc_type)
-            elif v_out == v_last_exc_value:
-                assert in_except_block
-                inputs_s.append(s_last_exc_value)
-                last_exc_value_vars.append(v_input)
-            else:
-                s_out = self.annotation(v_out)
-                if (link.exitcase, v_out) in knowntypedata:
-                    knownvarvalue = knowntypedata[(link.exitcase, v_out)]
-                    s_out = pair(s_out, knownvarvalue).improve()
-                    # ignore links that try to pass impossible values
-                    if s_out == annmodel.s_ImpossibleValue:
-                        ignore_link = True
+            renaming[v_out].append(v_input)
 
-                if hasattr(s_out,'is_type_of'):
-                    renamed_is_type_of = []
-                    for v in s_out.is_type_of:
-                        new_vs = renaming.get(v, [])
-                        renamed_is_type_of += new_vs
-                    assert s_out.knowntype is type
-                    newcell = annmodel.SomeType()
-                    if s_out.is_constant():
-                        newcell.const = s_out.const
-                    s_out = newcell
-                    s_out.is_type_of = renamed_is_type_of
-
-                if hasattr(s_out, 'knowntypedata'):
-                    renamed_knowntypedata = {}
-                    for (value, v), s in s_out.knowntypedata.items():
-                        new_vs = renaming.get(v, [])
-                        for new_v in new_vs:
-                            renamed_knowntypedata[value, new_v] = s
-                    assert isinstance(s_out, annmodel.SomeBool)
-                    newcell = annmodel.SomeBool()
-                    if s_out.is_constant():
-                        newcell.const = s_out.const
-                    s_out = newcell
-                    s_out.set_knowntypedata(renamed_knowntypedata)
-
-                inputs_s.append(s_out)
+        for v_out in link.args:
+            s_out = self.annotation(v_out)
+            if v_out in constraints:
+                s_constraint = constraints[v_out]
+                s_out = pair(s_out, s_constraint).improve()
+                # ignore links that try to pass impossible values
+                if s_out == s_ImpossibleValue:
+                    ignore_link = True
+            s_out = self.apply_renaming(s_out, renaming)
+            inputs_s.append(s_out)
         if ignore_link:
             return
 
-        if in_except_block:
-            s_last_exc_type.is_type_of = last_exc_value_vars
+        self.links_followed[link] = True
+        self.addpendingblock(graph, link.target, inputs_s)
+
+    def follow_raise_link(self, graph, link, s_last_exc_value):
+        v_last_exc_type = link.last_exception
+        v_last_exc_value = link.last_exc_value
+
+        assert (isinstance(link.exitcase, (types.ClassType, type)) and
+                issubclass(link.exitcase, BaseException))
+
+        assert v_last_exc_type and v_last_exc_value
+
+        if isinstance(v_last_exc_value, Variable):
+            self.setbinding(v_last_exc_value, s_last_exc_value)
+
+        if isinstance(v_last_exc_type, Variable):
+            self.setbinding(v_last_exc_type, typeof([v_last_exc_value]))
+
+        inputs_s = []
+        renaming = defaultdict(list)
+        for v_out, v_input in zip(link.args, link.target.inputargs):
+            renaming[v_out].append(v_input)
+
+        for v_out, v_input in zip(link.args, link.target.inputargs):
+            if v_out == v_last_exc_type:
+                s_out = typeof(renaming[v_last_exc_value])
+                if isinstance(v_last_exc_type, Constant):
+                    s_out.const = v_last_exc_type.value
+                elif v_last_exc_type.annotation.is_constant():
+                    s_out.const = v_last_exc_type.annotation.const
+                inputs_s.append(s_out)
+            else:
+                s_out = self.annotation(v_out)
+                s_out = self.apply_renaming(s_out, renaming)
+                inputs_s.append(s_out)
+
         self.links_followed[link] = True
         self.addpendingblock(graph, link.target, inputs_s)
 
@@ -586,8 +608,8 @@
                 raise BlockedInference(self, op, -1)
         resultcell = op.consider(self)
         if resultcell is None:
-            resultcell = annmodel.s_ImpossibleValue
-        elif resultcell == annmodel.s_ImpossibleValue:
+            resultcell = s_ImpossibleValue
+        elif resultcell == s_ImpossibleValue:
             raise BlockedInference(self, op, -1) # the operation cannot succeed
         assert isinstance(resultcell, annmodel.SomeObject)
         assert isinstance(op.result, Variable)
diff --git a/rpython/annotator/binaryop.py b/rpython/annotator/binaryop.py
--- a/rpython/annotator/binaryop.py
+++ b/rpython/annotator/binaryop.py
@@ -1,18 +1,19 @@
 """
 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,
+    SomeDict, SomeUnicodeCodePoint, SomeUnicodeString, SomeException,
     SomeTuple, SomeImpossibleValue, s_ImpossibleValue, SomeInstance,
     SomeBuiltinMethod, SomeIterator, SomePBC, SomeNone, SomeFloat, s_None,
     SomeByteArray, SomeWeakRef, SomeSingleFloat,
-    SomeLongFloat, SomeType, SomeConstantType, unionof, UnionError,
+    SomeLongFloat, SomeType, SomeTypeOf, SomeConstantType, unionof, UnionError,
     read_can_only_throw, add_knowntypedata,
     merge_knowntypedata,)
-from rpython.annotator.bookkeeper import immutablevalue
+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
@@ -35,7 +36,7 @@
     elif s_obj1.is_constant():
         if s_obj1.const is None and not s_obj2.can_be_none():
             r.const = False
-    knowntypedata = {}
+    knowntypedata = defaultdict(dict)
     bk = annotator.bookkeeper
 
     def bind(src_obj, tgt_obj):
@@ -145,24 +146,18 @@
 
     def union((obj1, obj2)):
         result = SomeType()
-        is_type_of1 = getattr(obj1, 'is_type_of', None)
-        is_type_of2 = getattr(obj2, 'is_type_of', None)
         if obj1.is_immutable_constant() and obj2.is_immutable_constant() and 
obj1.const == obj2.const:
             result.const = obj1.const
-            is_type_of = {}
-            if is_type_of1:
-                for v in is_type_of1:
-                    is_type_of[v] = True
-            if is_type_of2:
-                for v in is_type_of2:
-                    is_type_of[v] = True
-            if is_type_of:
-                result.is_type_of = is_type_of.keys()
-        else:
-            if is_type_of1 and is_type_of1 == is_type_of2:
-                result.is_type_of = is_type_of1
         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):
@@ -263,7 +258,7 @@
         if not (rarithmetic.signedtype(s_int1.knowntype) and
                 rarithmetic.signedtype(s_int2.knowntype)):
             return r
-        knowntypedata = {}
+        knowntypedata = defaultdict(dict)
         def tointtype(s_int0):
             if s_int0.knowntype is bool:
                 return int
@@ -682,6 +677,22 @@
             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):
diff --git a/rpython/annotator/bookkeeper.py b/rpython/annotator/bookkeeper.py
--- a/rpython/annotator/bookkeeper.py
+++ b/rpython/annotator/bookkeeper.py
@@ -12,7 +12,7 @@
 from rpython.annotator.model import (
     SomeOrderedDict, SomeString, SomeChar, SomeFloat, unionof, SomeInstance,
     SomeDict, SomeBuiltin, SomePBC, SomeInteger, TLS, SomeUnicodeCodePoint,
-    s_None, s_ImpossibleValue, SomeBool, SomeTuple,
+    s_None, s_ImpossibleValue, SomeBool, SomeTuple, SomeException,
     SomeImpossibleValue, SomeUnicodeString, SomeList, HarmlesslyBlocked,
     SomeWeakRef, SomeByteArray, SomeConstantType, SomeProperty)
 from rpython.annotator.classdesc import ClassDef, ClassDesc
@@ -167,6 +167,10 @@
         desc = self.getdesc(cls)
         return desc.getuniqueclassdef()
 
+    def new_exception(self, exc_classes):
+        clsdefs = {self.getuniqueclassdef(cls) for cls in exc_classes}
+        return SomeException(clsdefs)
+
     def getlistdef(self, **flags_if_new):
         """Get the ListDef associated with the current position."""
         try:
diff --git a/rpython/annotator/builtin.py b/rpython/annotator/builtin.py
--- a/rpython/annotator/builtin.py
+++ b/rpython/annotator/builtin.py
@@ -2,7 +2,7 @@
 Built-in functions.
 """
 import sys
-from collections import OrderedDict
+from collections import OrderedDict, defaultdict
 
 from rpython.annotator.model import (
     SomeInteger, SomeChar, SomeBool, SomeString, SomeTuple,
@@ -188,7 +188,7 @@
             variables = [op.args[1]]
         for variable in variables:
             assert bk.annotator.binding(variable) == s_obj
-        knowntypedata = {}
+        knowntypedata = defaultdict(dict)
         if not hasattr(typ, '_freeze_') and isinstance(s_type, SomePBC):
             add_knowntypedata(knowntypedata, True, variables, 
bk.valueoftype(typ))
         r.set_knowntypedata(knowntypedata)
diff --git a/rpython/annotator/model.py b/rpython/annotator/model.py
--- a/rpython/annotator/model.py
+++ b/rpython/annotator/model.py
@@ -32,7 +32,7 @@
 import inspect
 import weakref
 from types import BuiltinFunctionType, MethodType
-from collections import OrderedDict
+from collections import OrderedDict, defaultdict
 
 import rpython
 from rpython.tool import descriptor
@@ -138,6 +138,23 @@
     def can_be_none(self):
         return False
 
+class SomeTypeOf(SomeType):
+    """The type of a variable"""
+    def __init__(self, args_v):
+        self.is_type_of = args_v
+
+def typeof(args_v):
+    if args_v:
+        result = SomeTypeOf(args_v)
+        if len(args_v) == 1:
+            s_arg = args_v[0].annotation
+            if isinstance(s_arg, SomeException) and len(s_arg.classdefs) == 1:
+                cdef, = s_arg.classdefs
+                result.const = cdef.classdesc.pyobj
+        return result
+    else:
+        return SomeType()
+
 
 class SomeFloat(SomeObject):
     "Stands for a float or an integer."
@@ -437,6 +454,39 @@
     def noneify(self):
         return SomeInstance(self.classdef, can_be_None=True)
 
+class SomeException(SomeObject):
+    """The set of exceptions obeying type(exc) in self.classes"""
+    def __init__(self, classdefs):
+        self.classdefs = classdefs
+
+    def intersection(self, other):
+        assert isinstance(other, SomeExceptCase)
+        classdefs = {c for c in self.classdefs if c.issubclass(other.case)}
+        if classdefs:
+            return SomeException(classdefs)
+        else:
+            return s_ImpossibleValue
+
+    def difference(self, other):
+        assert isinstance(other, SomeExceptCase)
+        classdefs = {c for c in self.classdefs if not c.issubclass(other.case)}
+        if classdefs:
+            return SomeException(classdefs)
+        else:
+            return s_ImpossibleValue
+
+    def as_SomeInstance(self):
+        return unionof(*[SomeInstance(cdef) for cdef in self.classdefs])
+
+
+class SomeExceptCase(SomeObject):
+    """The set of exceptions that match a given except clause.
+
+    IOW, the set of exceptions that verify isinstance(exc, self.case).
+    """
+    def __init__(self, case):
+        self.case = case
+
 
 class SomePBC(SomeObject):
     """Stands for a global user instance, built prior to the analysis,
@@ -682,14 +732,15 @@
 
 def add_knowntypedata(ktd, truth, vars, s_obj):
     for v in vars:
-        ktd[(truth, v)] = s_obj
+        ktd[truth][v] = s_obj
 
 
 def merge_knowntypedata(ktd1, ktd2):
-    r = {}
-    for truth_v in ktd1:
-        if truth_v in ktd2:
-            r[truth_v] = unionof(ktd1[truth_v], ktd2[truth_v])
+    r = defaultdict(dict)
+    for truth, constraints in ktd1.items():
+        for v in constraints:
+            if truth in ktd2 and v in ktd2[truth]:
+                r[truth][v] = unionof(ktd1[truth][v], ktd2[truth][v])
     return r
 
 
diff --git a/rpython/annotator/test/test_annrpython.py 
b/rpython/annotator/test/test_annrpython.py
--- a/rpython/annotator/test/test_annrpython.py
+++ b/rpython/annotator/test/test_annrpython.py
@@ -698,6 +698,56 @@
         s = a.build_types(snippet.exc_deduction_our_excs_plus_others, [])
         assert isinstance(s, annmodel.SomeInteger)
 
+    def test_complex_exception_deduction(self):
+        class InternalError(Exception):
+            def __init__(self, msg):
+                self.msg = msg
+
+        class AppError(Exception):
+            def __init__(self, msg):
+                self.msg = msg
+        def apperror(msg):
+            return AppError(msg)
+
+        def f(string):
+            if not string:
+                raise InternalError('Empty string')
+            return string, None
+        def cleanup():
+            pass
+
+        def g(string):
+            try:
+                try:
+                    string, _ = f(string)
+                except ZeroDivisionError:
+                    raise apperror('ZeroDivisionError')
+                try:
+                    result, _ = f(string)
+                finally:
+                    cleanup()
+            except InternalError as e:
+                raise apperror(e.msg)
+            return result
+
+        a = self.RPythonAnnotator()
+        s_result = a.build_types(g, [str])
+        assert isinstance(s_result, annmodel.SomeString)
+
+    def test_method_exception_specialization(self):
+        def f(l):
+            try:
+                return l.pop()
+            except Exception:
+                raise
+        a = self.RPythonAnnotator()
+        s = a.build_types(f, [[int]])
+        graph = graphof(a, f)
+        etype, evalue = graph.exceptblock.inputargs
+        assert evalue.annotation.classdefs == {
+                a.bookkeeper.getuniqueclassdef(IndexError)}
+        assert etype.annotation.const == IndexError
+
     def test_operation_always_raising(self):
         def operation_always_raising(n):
             lst = []
@@ -1376,11 +1426,11 @@
         a.build_types(f, [somedict(annmodel.s_Int, annmodel.s_Int)])
         fg = graphof(a, f)
         et, ev = fg.exceptblock.inputargs
-        t = annmodel.SomeType()
+        t = annmodel.SomeTypeOf([ev])
         t.const = KeyError
-        t.is_type_of = [ev]
-        assert a.binding(et) == t
-        assert isinstance(a.binding(ev), annmodel.SomeInstance) and 
a.binding(ev).classdef == a.bookkeeper.getuniqueclassdef(KeyError)
+        assert et.annotation == t
+        s_ev = ev.annotation
+        assert s_ev == a.bookkeeper.new_exception([KeyError])
 
     def test_reraiseAnything(self):
         def f(dic):
@@ -1392,11 +1442,11 @@
         a.build_types(f, [somedict(annmodel.s_Int, annmodel.s_Int)])
         fg = graphof(a, f)
         et, ev = fg.exceptblock.inputargs
-        t = annmodel.SomeType()
-        t.is_type_of = [ev]
-        t.const = KeyError    # IndexError ignored because 'dic' is a dict
-        assert a.binding(et) == t
-        assert isinstance(a.binding(ev), annmodel.SomeInstance) and 
a.binding(ev).classdef == a.bookkeeper.getuniqueclassdef(KeyError)
+        t = annmodel.SomeTypeOf([ev])
+        t.const = KeyError  # IndexError ignored because 'dic' is a dict
+        assert et.annotation == t
+        s_ev = ev.annotation
+        assert s_ev == a.bookkeeper.new_exception([KeyError])
 
     def test_exception_mixing(self):
         def h():
@@ -1427,10 +1477,11 @@
         a.build_types(f, [int, somelist(annmodel.s_Int)])
         fg = graphof(a, f)
         et, ev = fg.exceptblock.inputargs
-        t = annmodel.SomeType()
-        t.is_type_of = [ev]
-        assert a.binding(et) == t
-        assert isinstance(a.binding(ev), annmodel.SomeInstance) and 
a.binding(ev).classdef == a.bookkeeper.getuniqueclassdef(Exception)
+        t = annmodel.SomeTypeOf([ev])
+        assert et.annotation == t
+        s_ev = ev.annotation
+        assert (isinstance(s_ev, annmodel.SomeInstance) and
+                s_ev.classdef == a.bookkeeper.getuniqueclassdef(Exception))
 
     def test_try_except_raise_finally1(self):
         def h(): pass
@@ -1449,10 +1500,11 @@
         a.build_types(f, [])
         fg = graphof(a, f)
         et, ev = fg.exceptblock.inputargs
-        t = annmodel.SomeType()
-        t.is_type_of = [ev]
-        assert a.binding(et) == t
-        assert isinstance(a.binding(ev), annmodel.SomeInstance) and 
a.binding(ev).classdef == a.bookkeeper.getuniqueclassdef(Exception)
+        t = annmodel.SomeTypeOf([ev])
+        assert et.annotation == t
+        s_ev = ev.annotation
+        assert (isinstance(s_ev, annmodel.SomeInstance) and
+                s_ev.classdef == a.bookkeeper.getuniqueclassdef(Exception))
 
     def test_inplace_div(self):
         def f(n):
diff --git a/rpython/annotator/test/test_model.py 
b/rpython/annotator/test/test_model.py
--- a/rpython/annotator/test/test_model.py
+++ b/rpython/annotator/test/test_model.py
@@ -1,8 +1,14 @@
-import py
+import pytest
 
 from rpython.annotator.model import *
 from rpython.annotator.listdef import ListDef
 from rpython.translator.translator import TranslationContext
+from rpython.annotator import unaryop, binaryop  # for side-effects
+
[email protected]()
+def annotator():
+    t = TranslationContext()
+    return t.buildannotator()
 
 
 listdef1 = ListDef(None, SomeTuple([SomeInteger(nonneg=True), SomeString()]))
@@ -100,19 +106,21 @@
 class AAA(object):
     pass
 
-def test_blocked_inference1():
+def test_blocked_inference1(annotator):
     def blocked_inference():
         return AAA().m()
 
-    py.test.raises(AnnotatorError, compile_function, blocked_inference)
+    with pytest.raises(AnnotatorError):
+        annotator.build_types(blocked_inference, [])
 
-def test_blocked_inference2():
+def test_blocked_inference2(annotator):
     def blocked_inference():
         a = AAA()
         b = a.x
         return b
 
-    py.test.raises(AnnotatorError, compile_function, blocked_inference)
+    with pytest.raises(AnnotatorError):
+        annotator.build_types(blocked_inference, [])
 
 
 def test_not_const():
@@ -129,3 +137,17 @@
     assert s.no_nul is True
     s = SomeChar().nonnulify()
     assert s.no_nul is True
+
+def test_SomeException_union(annotator):
+    bk = annotator.bookkeeper
+    someinst = lambda cls, **kw: SomeInstance(bk.getuniqueclassdef(cls), **kw)
+    s_inst = someinst(Exception)
+    s_exc = bk.new_exception([ValueError, IndexError])
+    assert unionof(s_exc, s_inst) == s_inst
+    assert unionof(s_inst, s_exc) == s_inst
+    s_nullable = unionof(s_None, bk.new_exception([ValueError]))
+    assert isinstance(s_nullable, SomeInstance)
+    assert s_nullable.can_be_None
+    s_exc1 = bk.new_exception([ValueError])
+    s_exc2 = bk.new_exception([IndexError])
+    unionof(s_exc1, s_exc2) == unionof(s_exc2, s_exc1)
diff --git a/rpython/annotator/unaryop.py b/rpython/annotator/unaryop.py
--- a/rpython/annotator/unaryop.py
+++ b/rpython/annotator/unaryop.py
@@ -1,8 +1,9 @@
 """
 Unary operations on SomeValues.
 """
+from __future__ import absolute_import
 
-from __future__ import absolute_import
+from collections import defaultdict
 
 from rpython.tool.pairtype import pair
 from rpython.flowspace.operation import op
@@ -11,7 +12,7 @@
 from rpython.annotator.model import (SomeObject, SomeInteger, SomeBool,
     SomeString, SomeChar, SomeList, SomeDict, SomeTuple, SomeImpossibleValue,
     SomeUnicodeCodePoint, SomeInstance, SomeBuiltin, SomeBuiltinMethod,
-    SomeFloat, SomeIterator, SomePBC, SomeNone, SomeType, s_ImpossibleValue,
+    SomeFloat, SomeIterator, SomePBC, SomeNone, SomeTypeOf, s_ImpossibleValue,
     s_Bool, s_None, s_Int, unionof, add_knowntypedata,
     SomeWeakRef, SomeUnicodeString, SomeByteArray)
 from rpython.annotator.bookkeeper import getbookkeeper, immutablevalue
@@ -26,11 +27,11 @@
                         if oper.dispatch == 1])
 UNARY_OPERATIONS.remove('contains')
 
+
 @op.type.register(SomeObject)
-def type_SomeObject(annotator, arg):
-    r = SomeType()
-    r.is_type_of = [arg]
-    return r
+def type_SomeObject(annotator, v_arg):
+    return SomeTypeOf([v_arg])
+
 
 @op.bool.register(SomeObject)
 def bool_SomeObject(annotator, obj):
@@ -39,7 +40,7 @@
     s_nonnone_obj = annotator.annotation(obj)
     if s_nonnone_obj.can_be_none():
         s_nonnone_obj = s_nonnone_obj.nonnoneify()
-    knowntypedata = {}
+    knowntypedata = defaultdict(dict)
     add_knowntypedata(knowntypedata, True, [obj], s_nonnone_obj)
     r.set_knowntypedata(knowntypedata)
     return r
@@ -99,18 +100,17 @@
     callspec = complex_args([annotator.annotation(v_arg) for v_arg in args_v])
     return annotator.annotation(func).call(callspec)
 
[email protected](SomeObject)
+def issubtype(annotator, v_type, v_cls):
+    s_type = v_type.annotation
+    s_cls = annotator.annotation(v_cls)
+    if s_type.is_constant() and s_cls.is_constant():
+        return annotator.bookkeeper.immutablevalue(
+            issubclass(s_type.const, s_cls.const))
+    return s_Bool
+
 class __extend__(SomeObject):
 
-    def issubtype(self, s_cls):
-        if hasattr(self, 'is_type_of'):
-            vars = self.is_type_of
-            annotator = getbookkeeper().annotator
-            return builtin.builtin_isinstance(annotator.binding(vars[0]),
-                                              s_cls, vars)
-        if self.is_constant() and s_cls.is_constant():
-            return immutablevalue(issubclass(self.const, s_cls.const))
-        return s_Bool
-
     def len(self):
         return SomeInteger(nonneg=True)
 
@@ -520,7 +520,7 @@
 def contains_String(annotator, string, char):
     if annotator.annotation(char).is_constant() and 
annotator.annotation(char).const == "\0":
         r = SomeBool()
-        knowntypedata = {}
+        knowntypedata = defaultdict(dict)
         add_knowntypedata(knowntypedata, False, [string],
                           annotator.annotation(string).nonnulify())
         r.set_knowntypedata(knowntypedata)
@@ -913,6 +913,12 @@
         # really crash translated code).  It can be generalized later.
         return SomeImpossibleValue()
 
[email protected](SomeTypeOf)
+def issubtype(annotator, v_type, v_cls):
+    args_v = v_type.annotation.is_type_of
+    return builtin.builtin_isinstance(
+        args_v[0].annotation, annotator.annotation(v_cls), args_v)
+
 #_________________________________________
 # weakrefs
 
diff --git a/rpython/rtyper/rclass.py b/rpython/rtyper/rclass.py
--- a/rpython/rtyper/rclass.py
+++ b/rpython/rtyper/rclass.py
@@ -445,6 +445,13 @@
     def rtyper_makekey(self):
         return self.__class__, self.classdef
 
+class __extend__(annmodel.SomeException):
+    def rtyper_makerepr(self, rtyper):
+        return self.as_SomeInstance().rtyper_makerepr(rtyper)
+
+    def rtyper_makekey(self):
+        return self.__class__, frozenset(self.classdefs)
+
 class __extend__(annmodel.SomeType):
     def rtyper_makerepr(self, rtyper):
         return get_type_repr(rtyper)
diff --git a/rpython/translator/goal/query.py b/rpython/translator/goal/query.py
--- a/rpython/translator/goal/query.py
+++ b/rpython/translator/goal/query.py
@@ -48,7 +48,7 @@
         s_ev = annotator.annotation(ev)
         if s_et:
             if s_et.knowntype == type:
-                if s_et.__class__ == annmodel.SomeType:
+                if s_et.__class__ == annmodel.SomeTypeOf:
                     if hasattr(s_et, 'is_type_of') and  s_et.is_type_of == 
[ev]:
                         continue
                 else:
diff --git a/rpython/translator/transform.py b/rpython/translator/transform.py
--- a/rpython/translator/transform.py
+++ b/rpython/translator/transform.py
@@ -189,8 +189,7 @@
     self.links_followed[errlink] = True
     # fix the annotation of the exceptblock.inputargs
     etype, evalue = graph.exceptblock.inputargs
-    s_type = annmodel.SomeType()
-    s_type.is_type_of = [evalue]
+    s_type = annmodel.SomeTypeOf([evalue])
     s_value = 
annmodel.SomeInstance(self.bookkeeper.getuniqueclassdef(Exception))
     self.setbinding(etype, s_type)
     self.setbinding(evalue, s_value)
_______________________________________________
pypy-commit mailing list
[email protected]
https://mail.python.org/mailman/listinfo/pypy-commit

Reply via email to