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