Author: fijal
Branch: rpython-20
Changeset: r93046:5f87d65c7f82
Date: 2017-11-15 19:26 +0100
http://bitbucket.org/pypy/pypy/changeset/5f87d65c7f82/

Log:    start a branch to play with stronger type guarantees

diff --git a/rpython/annotator/model.py b/rpython/annotator/model.py
--- a/rpython/annotator/model.py
+++ b/rpython/annotator/model.py
@@ -47,19 +47,33 @@
     allow_int_to_float = True
 TLS = State()
 
+def compare_dict(d1, d2, ommit):
+    for k, v in d1.iteritems():
+        if k in ommit:
+            continue
+        if k not in d2 or v != d2[k]:
+            return False
+    for k, v in d2.iteritems():
+        if k in ommit:
+            continue
+        if k not in d1: # don't need to compare again
+            return False
+    return True
+
 class SomeObject(object):
     """The set of all objects.  Each instance stands
     for an arbitrary object about which nothing is known."""
     __metaclass__ = extendabletype
     immutable = False
     knowntype = object
+    can_union = True
 
     def __init__(self):
         assert type(self) is not SomeObject
 
     def __eq__(self, other):
         return (self.__class__ is other.__class__ and
-                self.__dict__  == other.__dict__)
+                compare_dict(self.__dict__, other.__dict__, ('can_union',)))
 
     def __ne__(self, other):
         return not (self == other)
@@ -74,7 +88,7 @@
         else:
             reprdict[self] = True
             try:
-                items = self.__dict__.items()
+                items = [x for x in self.__dict__.items() if x[0] != 
'can_union']
                 items.sort()
                 args = []
                 for k, v in items:
@@ -269,11 +283,10 @@
         d1 = self.__dict__
         d2 = other.__dict__
         if not TLS.check_str_without_nul:
-            d1 = d1.copy()
-            d1['no_nul'] = 0
-            d2 = d2.copy()
-            d2['no_nul'] = 0
-        return d1 == d2
+            ommit = ('no_nul', 'can_union')
+        else:
+            ommit = ()
+        return compare_dict(d1, d2, ommit)
 
     def nonnoneify(self):
         return self.__class__(can_be_None=False, no_nul=self.no_nul)
@@ -341,11 +354,8 @@
             return False
         if not self.listdef.same_as(other.listdef):
             return False
-        selfdic = self.__dict__.copy()
-        otherdic = other.__dict__.copy()
-        del selfdic['listdef']
-        del otherdic['listdef']
-        return selfdic == otherdic
+        return compare_dict(self.__dict__, other.__dict__,
+                            ('listdef', 'can_union'))
 
     def can_be_none(self):
         return True
@@ -383,11 +393,8 @@
             return False
         if not self.dictdef.same_as(other.dictdef):
             return False
-        selfdic = self.__dict__.copy()
-        otherdic = other.__dict__.copy()
-        del selfdic['dictdef']
-        del otherdic['dictdef']
-        return selfdic == otherdic
+        return compare_dict(self.__dict__, other.__dict__,
+                            ('dictdef', 'can_union'))
 
     def can_be_none(self):
         return True
@@ -755,8 +762,15 @@
         if s1 == s2:
             # Most pair(...).union() methods deal incorrectly with that case
             # when constants are involved.
-            return s1
-        return pair(s1, s2).union()
+            r = s1
+        else:
+            r = pair(s1, s2).union()
+        if not s1.can_union and not s1 == r:
+            raise AnnotatorError("Merging %s and %s forbidden" % (s2, s1))
+        if not s2.can_union and not s2 == r:
+            raise AnnotatorError("Merging %s and %s forbidden" % (s1, s2))
+        return r
+
     finally:
         TLS.no_side_effects_in_union -= 1
 
@@ -773,6 +787,17 @@
         # See comment in union() above
         if s1 != s2:
             s1 = pair(s1, s2).union()
+    for i, s in enumerate(somevalues):
+        if not s.can_union and not s == s1:
+            l = []
+            for j, _s in enumerate(somevalues):
+                if i == j:
+                    l.append("* " + repr(_s))
+                else:
+                    l.append("  " + repr(_s))
+            allargs = "\n".join(l)
+            raise AnnotatorError("Merging:\n%s\nwill produce %s, * marks 
strict"
+                " which cannot be generalized" % (allargs, s1))
     return s1
 
 
diff --git a/rpython/annotator/test/test_strongly_typed.py 
b/rpython/annotator/test/test_strongly_typed.py
new file mode 100644
--- /dev/null
+++ b/rpython/annotator/test/test_strongly_typed.py
@@ -0,0 +1,40 @@
+
+import py
+
+from rpython.conftest import option
+
+from rpython.annotator import model
+from rpython.annotator.annrpython import RPythonAnnotator as _RPythonAnnotator
+
+
+class TestAnnotateTestCase:
+    class RPythonAnnotator(_RPythonAnnotator):
+        def build_types(self, *args):
+            s = _RPythonAnnotator.build_types(self, *args)
+            self.validate()
+            if option.view:
+                self.translator.view()
+            return s
+
+    def build_types(self, func, types):
+        a = self.RPythonAnnotator()
+        return a.build_types(func, types)
+
+    def test_simple(self):
+        def f(a):
+            return a
+
+        s = model.SomeInteger()
+        s.can_union = False
+        self.build_types(f, [s])
+        assert s == model.SomeInteger()
+
+    def test_generalize_boom(self):
+        def f(i):
+            if i % 15 == 0:
+                return f(1.5)
+            return i
+
+        s = model.SomeInteger()
+        s.can_union = False
+        py.test.raises(model.AnnotatorError, self.build_types, f, [s])
diff --git a/rpython/doc/signatures.rst b/rpython/doc/signatures.rst
new file mode 100644
--- /dev/null
+++ b/rpython/doc/signatures.rst
@@ -0,0 +1,54 @@
+
+Basic types::
+
+  int    - signed machine size integer
+  r_uint - unsigned machine size integer
+  r_long/r_ulong/r_longlong/r_ulonglong - various integers
+  char   - single character (byte)
+  bytes  - immutable array of chars
+  bytes? - nullable bytes
+  float  - double-sized IEEE floating point
+
+Low level types:
+
+  ll.UCHAR
+  ll.INT
+  ...
+  ll.Array(xxx)
+  ll.Struct(xxx)
+  ll.GcStruct(xxx)
+  ll.GcArray(xxx)
+
+Container types::
+
+  list(X)        - resizable list of X
+  array(X)       - non-resizable list of X
+  dict(X, Y)     - dict of X keys and Y values
+  tuple(A, B, C) - tuple of 3 items, A, B, C
+  list?(X)       - nullable list, array or dict
+
+Classes::
+
+  class A(object):
+      _rpython_ = """
+      class foobar.A # <- namespace declaration for type name
+
+      a: int
+      b: list(int)
+      c: array(int)
+      """
+
+PBCs::
+
+  space = rpython_pbc("space.ObjSpace", space) - registers PBC under the name 
"space.ObjSpace",
+                                                 to be used in signatures
+
+Examples of a signature::
+
+  @rpython("int -> int")
+  def f(a):
+      return a
+
+  @rpython("space.ObjSpace, int, float -> bytes")
+  def f(space, i, f):
+      return space.str_w(space.newbytes(str(i)))
_______________________________________________
pypy-commit mailing list
[email protected]
https://mail.python.org/mailman/listinfo/pypy-commit

Reply via email to