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

Reply via email to