Author: Ronan Lamy <[email protected]>
Branch:
Changeset: r88360:c14a4a09d0fe
Date: 2016-11-13 15:35 +0000
http://bitbucket.org/pypy/pypy/changeset/c14a4a09d0fe/
Log: Merged in union-side-effects-2 (pull request #495)
Creates and tests union().
diff --git a/rpython/annotator/annrpython.py b/rpython/annotator/annrpython.py
--- a/rpython/annotator/annrpython.py
+++ b/rpython/annotator/annrpython.py
@@ -246,7 +246,7 @@
if s_old is not None:
if not s_value.contains(s_old):
log.WARNING("%s does not contain %s" % (s_value, s_old))
- log.WARNING("%s" % annmodel.unionof(s_value, s_old))
+ log.WARNING("%s" % annmodel.union(s_value, s_old))
assert False
arg.annotation = s_value
diff --git a/rpython/annotator/binaryop.py b/rpython/annotator/binaryop.py
--- a/rpython/annotator/binaryop.py
+++ b/rpython/annotator/binaryop.py
@@ -11,7 +11,7 @@
SomeBuiltinMethod, SomeIterator, SomePBC, SomeNone, SomeFloat, s_None,
SomeByteArray, SomeWeakRef, SomeSingleFloat,
SomeLongFloat, SomeType, SomeTypeOf, SomeConstantType, unionof, UnionError,
- read_can_only_throw, add_knowntypedata,
+ union, read_can_only_throw, add_knowntypedata,
merge_knowntypedata,)
from rpython.annotator.bookkeeper import immutablevalue, getbookkeeper
from rpython.flowspace.model import Variable, Constant, const
@@ -703,13 +703,13 @@
pairtype(SomeException, SomeInstance),
pairtype(SomeException, SomeNone)):
def union((s_exc, s_inst)):
- return unionof(s_exc.as_SomeInstance(), s_inst)
+ return union(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)
+ return union(s_exc.as_SomeInstance(), s_inst)
class __extend__(pairtype(SomeException, SomeException)):
def union((s_exc1, s_exc2)):
diff --git a/rpython/annotator/builtin.py b/rpython/annotator/builtin.py
--- a/rpython/annotator/builtin.py
+++ b/rpython/annotator/builtin.py
@@ -6,7 +6,7 @@
from rpython.annotator.model import (
SomeInteger, SomeChar, SomeBool, SomeString, SomeTuple,
- SomeUnicodeCodePoint, SomeFloat, unionof, SomeUnicodeString,
+ SomeUnicodeCodePoint, SomeFloat, union, SomeUnicodeString,
SomePBC, SomeInstance, SomeDict, SomeList, SomeWeakRef, SomeIterator,
SomeOrderedDict, SomeByteArray, add_knowntypedata, s_ImpossibleValue,)
from rpython.annotator.bookkeeper import (
@@ -166,14 +166,14 @@
s_iter = s_values[0].iter()
return s_iter.next()
else:
- return unionof(*s_values)
+ return union(*s_values)
def builtin_max(*s_values):
if len(s_values) == 1: # xxx do we support this?
s_iter = s_values[0].iter()
return s_iter.next()
else:
- s = unionof(*s_values)
+ s = union(*s_values)
if type(s) is SomeInteger and not s.nonneg:
nonneg = False
for s1 in s_values:
diff --git a/rpython/annotator/dictdef.py b/rpython/annotator/dictdef.py
--- a/rpython/annotator/dictdef.py
+++ b/rpython/annotator/dictdef.py
@@ -1,5 +1,5 @@
-from rpython.annotator.model import s_ImpossibleValue
-from rpython.annotator.model import SomeInteger, s_Bool, unionof
+from rpython.annotator.model import (
+ s_ImpossibleValue, SomeInteger, s_Bool, union)
from rpython.annotator.listdef import ListItem
from rpython.rlib.objectmodel import compute_hash
@@ -34,8 +34,8 @@
def update_rdict_annotations(self, s_eqfn, s_hashfn, other=None):
assert self.custom_eq_hash
- s_eqfn = unionof(s_eqfn, self.s_rdict_eqfn)
- s_hashfn = unionof(s_hashfn, self.s_rdict_hashfn)
+ s_eqfn = union(s_eqfn, self.s_rdict_eqfn)
+ s_hashfn = union(s_hashfn, self.s_rdict_hashfn)
self.s_rdict_eqfn = s_eqfn
self.s_rdict_hashfn = s_hashfn
self.emulate_rdict_calls(other=other)
diff --git a/rpython/annotator/model.py b/rpython/annotator/model.py
--- a/rpython/annotator/model.py
+++ b/rpython/annotator/model.py
@@ -94,16 +94,9 @@
if self == other:
return True
try:
- TLS.no_side_effects_in_union += 1
- except AttributeError:
- TLS.no_side_effects_in_union = 1
- try:
- try:
- return pair(self, other).union() == self
- except UnionError:
- return False
- finally:
- TLS.no_side_effects_in_union -= 1
+ return union(self, other) == self
+ except UnionError:
+ return False
def is_constant(self):
d = self.__dict__
@@ -680,6 +673,10 @@
s_None = SomeNone()
s_Bool = SomeBool()
+s_True = SomeBool()
+s_True.const = True
+s_False = SomeBool()
+s_False.const = False
s_Int = SomeInteger()
s_ImpossibleValue = SomeImpossibleValue()
s_Str0 = SomeString(no_nul=True)
@@ -739,6 +736,27 @@
def __repr__(self):
return str(self)
+def union(s1, s2):
+ """The join operation in the lattice of annotations.
+
+ It is the most precise SomeObject instance that contains both arguments.
+
+ union() is (supposed to be) idempotent, commutative, associative and has
+ no side-effects.
+ """
+ try:
+ TLS.no_side_effects_in_union += 1
+ except AttributeError:
+ TLS.no_side_effects_in_union = 1
+ try:
+ if s1 == s2:
+ # Most pair(...).union() methods deal incorrectly with that case
+ # when constants are involved.
+ return s1
+ return pair(s1, s2).union()
+ finally:
+ TLS.no_side_effects_in_union -= 1
+
def unionof(*somevalues):
"The most precise SomeValue instance that contains all the values."
try:
@@ -749,8 +767,7 @@
if s1 != s2:
s1 = pair(s1, s2).union()
else:
- # this is just a performance shortcut
- # XXX: This is a lie! Grep for no_side_effects_in_union and weep.
+ # See comment in union() above
if s1 != s2:
s1 = pair(s1, s2).union()
return s1
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,5 +1,8 @@
import pytest
+from hypothesis import given, assume, settings
+from hypothesis import strategies as st
+
from rpython.flowspace.model import Variable
from rpython.flowspace.operation import op
from rpython.translator.translator import TranslationContext
@@ -67,11 +70,11 @@
except TypeError: # if A0 is also a new-style class, e.g. in PyPy
class B3(A0, object):
pass
- assert commonbase(A1,A2) is A0
- assert commonbase(A1,A0) is A0
- assert commonbase(A1,A1) is A1
- assert commonbase(A2,B2) is object
- assert commonbase(A2,B3) is A0
+ assert commonbase(A1, A2) is A0
+ assert commonbase(A1, A0) is A0
+ assert commonbase(A1, A1) is A1
+ assert commonbase(A2, B2) is object
+ assert commonbase(A2, B3) is A0
def test_list_union():
listdef1 = ListDef('dummy', SomeInteger(nonneg=True))
@@ -102,6 +105,93 @@
assert f2.contains(f1)
assert f1.contains(f2)
+def const_float(x):
+ s = SomeFloat()
+ s.const = x
+ return s
+
+def const_int(n):
+ s = SomeInteger(nonneg=(n >= 0))
+ s.const = n
+ return s
+
+def const_str(x):
+ no_nul = not '\x00' in x
+ if len(x) == 1:
+ result = SomeChar(no_nul=no_nul)
+ else:
+ result = SomeString(no_nul=no_nul)
+ result.const = x
+ return result
+
+def const_unicode(x):
+ no_nul = not u'\x00' in x
+ if len(x) == 1:
+ result = SomeUnicodeCodePoint(no_nul=no_nul)
+ else:
+ result = SomeUnicodeString(no_nul=no_nul)
+ result.const = x
+ return result
+
+def compatible(s1, s2):
+ try:
+ union(s1, s2)
+ except UnionError:
+ return False
+ return True
+
+def compatible_pair(pair_s):
+ return compatible(*pair_s)
+
+st_float = st.just(SomeFloat()) | st.builds(const_float, st.floats())
+st_int = st.one_of(st.builds(SomeInteger, st.booleans(), st.booleans()),
+ st.builds(const_int, st.integers()))
+st_bool = st.sampled_from([s_Bool, s_True, s_False])
+st_numeric = st.one_of(st_float, st_int, st_bool)
+st_str = (st.builds(SomeString, st.booleans(), st.booleans())
+ | st.builds(const_str, st.binary()))
+st_unicode = (st.builds(SomeUnicodeString, st.booleans(), st.booleans())
+ | st.builds(const_unicode, st.text()))
+st_simple = st.one_of(st_numeric, st_str, st_unicode,
st.just(s_ImpossibleValue), st.just(s_None))
+
+def valid_unions(st_ann):
+ """From a strategy generating annotations, create a strategy returning
+ unions of these annotations."""
+ pairs = st.tuples(st_ann, st_ann)
+ return pairs.filter(compatible_pair).map(lambda t: union(*t))
+
+
+st_annotation = st.recursive(st_simple,
+ lambda st_ann: valid_unions(st_ann) | st.builds(SomeTuple,
st.lists(st_ann)),
+ max_leaves=3)
+
+@given(s=st_annotation)
+def test_union_unary(s):
+ assert union(s, s) == s
+ assert union(s_ImpossibleValue, s) == s
+
+@given(s1=st_annotation, s2=st_annotation)
+def test_commutativity_of_union_compatibility(s1, s2):
+ assert compatible(s1, s2) == compatible(s2, s1)
+
+@given(st_annotation, st_annotation)
+def test_union_commutative(s1, s2):
+ try:
+ s_union = union(s1, s2)
+ except UnionError:
+ assume(False)
+ assert union(s2, s1) == s_union
+ assert s_union.contains(s1)
+ assert s_union.contains(s2)
+
[email protected]
+@settings(max_examples=500)
+@given(st_annotation, st_annotation, st_annotation)
+def test_union_associative(s1, s2, s3):
+ assume(compatible(s1, s2) and compatible(union(s1, s2), s3))
+ assert union(union(s1, s2), s3) == union(s1, union(s2, s3))
+
+
def compile_function(function, annotation=[]):
t = TranslationContext()
t.buildannotator().build_types(function, annotation)
@@ -146,14 +236,14 @@
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 union(s_exc, s_inst) == s_inst
+ assert union(s_inst, s_exc) == s_inst
+ s_nullable = union(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)
+ union(s_exc1, s_exc2) == union(s_exc2, s_exc1)
def contains_s(s_a, s_b):
if s_b is None:
diff --git a/rpython/rtyper/test/test_llannotation.py
b/rpython/rtyper/test/test_llannotation.py
--- a/rpython/rtyper/test/test_llannotation.py
+++ b/rpython/rtyper/test/test_llannotation.py
@@ -1,6 +1,6 @@
import py.test
from rpython.annotator.model import (
- SomeInteger, SomeBool, SomeChar, unionof, SomeImpossibleValue,
+ SomeInteger, SomeBool, SomeChar, union, SomeImpossibleValue,
UnionError, SomeInstance, SomeSingleFloat)
from rpython.rlib.rarithmetic import r_uint, r_singlefloat
from rpython.rtyper.llannotation import (
@@ -69,22 +69,22 @@
PA1 = lltype.Ptr(lltype.GcArray())
PA2 = lltype.Ptr(lltype.GcArray())
- assert unionof(SomePtr(PS1), SomePtr(PS1)) == SomePtr(PS1)
- assert unionof(SomePtr(PS1), SomePtr(PS2)) == SomePtr(PS2)
- assert unionof(SomePtr(PS1), SomePtr(PS2)) == SomePtr(PS1)
+ assert union(SomePtr(PS1), SomePtr(PS1)) == SomePtr(PS1)
+ assert union(SomePtr(PS1), SomePtr(PS2)) == SomePtr(PS2)
+ assert union(SomePtr(PS1), SomePtr(PS2)) == SomePtr(PS1)
- assert unionof(SomePtr(PA1), SomePtr(PA1)) == SomePtr(PA1)
- assert unionof(SomePtr(PA1), SomePtr(PA2)) == SomePtr(PA2)
- assert unionof(SomePtr(PA1), SomePtr(PA2)) == SomePtr(PA1)
+ assert union(SomePtr(PA1), SomePtr(PA1)) == SomePtr(PA1)
+ assert union(SomePtr(PA1), SomePtr(PA2)) == SomePtr(PA2)
+ assert union(SomePtr(PA1), SomePtr(PA2)) == SomePtr(PA1)
- assert unionof(SomePtr(PS1), SomeImpossibleValue()) == SomePtr(PS1)
- assert unionof(SomeImpossibleValue(), SomePtr(PS1)) == SomePtr(PS1)
+ assert union(SomePtr(PS1), SomeImpossibleValue()) == SomePtr(PS1)
+ assert union(SomeImpossibleValue(), SomePtr(PS1)) == SomePtr(PS1)
with py.test.raises(UnionError):
- unionof(SomePtr(PA1), SomePtr(PS1))
+ union(SomePtr(PA1), SomePtr(PS1))
with py.test.raises(UnionError):
- unionof(SomePtr(PS1), SomePtr(PS3))
+ union(SomePtr(PS1), SomePtr(PS3))
with py.test.raises(UnionError):
- unionof(SomePtr(PS1), SomeInteger())
+ union(SomePtr(PS1), SomeInteger())
with py.test.raises(UnionError):
- unionof(SomeInteger(), SomePtr(PS1))
+ union(SomeInteger(), SomePtr(PS1))
_______________________________________________
pypy-commit mailing list
[email protected]
https://mail.python.org/mailman/listinfo/pypy-commit