[pypy-svn] r20256 - in pypy/branch/somepbc-refactoring/pypy: annotation rpython translator/test

arigo at codespeak.net arigo at codespeak.net
Fri Nov 25 20:52:59 CET 2005


Author: arigo
Date: Fri Nov 25 20:52:58 2005
New Revision: 20256

Modified:
   pypy/branch/somepbc-refactoring/pypy/annotation/bookkeeper.py
   pypy/branch/somepbc-refactoring/pypy/annotation/classdef.py
   pypy/branch/somepbc-refactoring/pypy/annotation/description.py
   pypy/branch/somepbc-refactoring/pypy/annotation/model.py
   pypy/branch/somepbc-refactoring/pypy/rpython/rpbc.py
   pypy/branch/somepbc-refactoring/pypy/translator/test/test_annrpython.py
Log:
(pedronis, arigo)

As shown by an rtyper test, we need to make MethodDesc a bit more complicated.
Now they have an 'originclassdef' for the class they come from, and (when a
getattr occurs) they have a 'selfclassdef' for the class of the instance that
they were read from.

SomePBC.__init__() now takes care of the corner case that makes the annotator
'assert contains()' unhappy.

Incidentally, this allows us to re-enable a disabled test from test_annrpython.



Modified: pypy/branch/somepbc-refactoring/pypy/annotation/bookkeeper.py
==============================================================================
--- pypy/branch/somepbc-refactoring/pypy/annotation/bookkeeper.py	(original)
+++ pypy/branch/somepbc-refactoring/pypy/annotation/bookkeeper.py	Fri Nov 25 20:52:58 2005
@@ -407,10 +407,12 @@
                         self.getdesc(pyobj.im_func),            # funcdesc
                         self.getdesc(pyobj.im_self))            # frozendesc
                 else: # regular method
+                    origincls, name = origin_of_meth(pyobj)
                     result = self.getmethoddesc(
                         self.getdesc(pyobj.im_func),            # funcdesc
-                        self.getuniqueclassdef(pyobj.im_class), # classdef
-                        name_of_meth(pyobj))
+                        self.getuniqueclassdef(origincls),      # originclassdef
+                        self.getuniqueclassdef(pyobj.im_class), # selfclassdef
+                        name)
             else:
                 # must be a frozen pre-built constant, but let's check
                 assert pyobj._freeze_()
@@ -425,12 +427,14 @@
             self.descs[pyobj] = result
             return result
 
-    def getmethoddesc(self, funcdesc, classdef, name):
+    def getmethoddesc(self, funcdesc, originclassdef, selfclassdef, name):
+        key = funcdesc, originclassdef, selfclassdef, name
         try:
-            return self.methoddescs[funcdesc, classdef, name]
+            return self.methoddescs[key]
         except KeyError:
-            result = description.MethodDesc(self, funcdesc, classdef, name)
-            self.methoddescs[funcdesc, classdef, name] = result
+            result = description.MethodDesc(self, funcdesc, originclassdef,
+                                            selfclassdef, name)
+            self.methoddescs[key] = result
             return result
 
     def valueoftype(self, t):
@@ -561,16 +565,16 @@
     def warning(self, msg):
         return self.annotator.warning(msg)
 
-def name_of_meth(boundmeth):
+def origin_of_meth(boundmeth):
     func = boundmeth.im_func
     candname = func.func_name
     for cls in inspect.getmro(boundmeth.im_class):
         dict = cls.__dict__
         if dict.get(candname) is func:
-            return candname
+            return cls, candname
         for name, value in dict.iteritems():
             if value is func:
-                return name
+                return cls, name
     raise Exception, "could not match bound-method to attribute name: %r" % (boundmeth,)
 
 def ishashable(x):

Modified: pypy/branch/somepbc-refactoring/pypy/annotation/classdef.py
==============================================================================
--- pypy/branch/somepbc-refactoring/pypy/annotation/classdef.py	(original)
+++ pypy/branch/somepbc-refactoring/pypy/annotation/classdef.py	Fri Nov 25 20:52:58 2005
@@ -309,7 +309,7 @@
         for desc in pbc.descriptions:
             if isinstance(desc, description.MethodDesc):
                 meth = True
-                methclassdef = desc.classdef
+                methclassdef = desc.originclassdef
                 if methclassdef is not self and methclassdef.issubclass(self):
                     pass # subclasses methods are always candidates
                 elif self.issubclass(methclassdef):
@@ -323,6 +323,9 @@
                     # clsdef2.lookup_filter(pbc) (see formal proof...)
                 else:
                     continue # not matching
+                # bind the method by giving it a selfclassdef.  Use the
+                # more precise subclass that it's coming from.
+                desc = desc.bind_self(methclassdef)
             d.append(desc)
         if uplookup is not None:            
             # hack^2, in this case the classdef for uplookup could be the result
@@ -334,7 +337,9 @@
                 uplookup.attr_sources.setdefault(name, [])
                 check_for_missing_attrs = True
 
-            d.append(updesc)
+            # add the updesc method, bounding it to the more precise
+            # classdef 'self' instead of its originclassdef
+            d.append(updesc.bind_self(self))
         elif meth and name is not None:
             check_for_missing_attrs = True
 

Modified: pypy/branch/somepbc-refactoring/pypy/annotation/description.py
==============================================================================
--- pypy/branch/somepbc-refactoring/pypy/annotation/description.py	(original)
+++ pypy/branch/somepbc-refactoring/pypy/annotation/description.py	Fri Nov 25 20:52:58 2005
@@ -126,6 +126,10 @@
     def bind_under(self, classdef, name):
         return self
 
+    def simplify_desc_set(descs):
+        pass
+    simplify_desc_set = staticmethod(simplify_desc_set)
+
 
 class FunctionDesc(Desc):
     knowntype = types.FunctionType
@@ -209,7 +213,10 @@
 
     def bind_under(self, classdef, name):
         # XXX static methods
-        return self.bookkeeper.getmethoddesc(self, classdef, name)
+        return self.bookkeeper.getmethoddesc(self, 
+                                             classdef,   # originclassdef,
+                                             None,       # selfclassdef
+                                             name)
 
     def consider_call_site(bookkeeper, family, descs, args, s_result):
         row = FunctionDesc.row_to_consider(descs, args)
@@ -456,6 +463,7 @@
                 if isinstance(initfuncdesc, FunctionDesc):
                     initmethdesc = bookkeeper.getmethoddesc(initfuncdesc,
                                                             classdef,
+                                                            classdef,
                                                             '__init__')
                     initdescs.append(initmethdesc)
         # register a call to exactly these __init__ methods
@@ -473,25 +481,40 @@
 class MethodDesc(Desc):
     knowntype = types.MethodType
 
-    def __init__(self, bookkeeper, funcdesc, classdef, name):
+    def __init__(self, bookkeeper, funcdesc, originclassdef, 
+                 selfclassdef, name):
         super(MethodDesc, self).__init__(bookkeeper)
         self.funcdesc = funcdesc
-        self.classdef = classdef
+        self.originclassdef = originclassdef
+        self.selfclassdef = selfclassdef
         self.name = name
 
     def __repr__(self):
-        return '<MethodDesc %r of %r>' % (self.funcdesc,
-                                          self.classdef)
+        if self.selfclassdef is None:
+            return '<unbound MethodDesc %r of %r>' % (self.name,
+                                                      self.originclassdef)
+        else:
+            return '<MethodDesc %r of %r bound to %r>' % (self.name,
+                                                          self.originclassdef,
+                                                          self.selfclassdef)
 
     def pycall(self, schedule, args, s_previous_result):
         from pypy.annotation.model import SomeInstance
-        s_instance = SomeInstance(self.classdef)
+        if self.selfclassdef is None:
+            raise Exception("calling %r" % (self,))
+        s_instance = SomeInstance(self.selfclassdef)
         args = args.prepend(s_instance)
         return self.funcdesc.pycall(schedule, args, s_previous_result)
 
     def bind_under(self, classdef, name):
         self.bookkeeper.warning("rebinding an already bound %r" % (self,))
         return self.funcdesc.bind_under(classdef, name)
+
+    def bind_self(self, newselfclassdef):
+        return self.bookkeeper.getmethoddesc(self.funcdesc,
+                                             self.originclassdef,
+                                             newselfclassdef,
+                                             self.name)
     
     def consider_call_site(bookkeeper, family, descs, args, s_result):
         funcdescs = [methoddesc.funcdesc for methoddesc in descs]
@@ -506,6 +529,27 @@
         # FunctionDescs, not MethodDescs.  The present method returns the
         # FunctionDesc to use as a key in that family.
         return self.funcdesc
+    
+    def simplify_desc_set(descs):
+        # if two MethodDescs in the set differ only in their selfclassdefs,
+        # and if one of the selfclassdefs is a subclass of the other, then
+        # we can remove the former.  This is not just an optimization but
+        # needed to make contains() happy on SomePBC.
+        groups = {}
+        for desc in descs:
+            if desc.selfclassdef is not None:
+                key = desc.funcdesc, desc.originclassdef, desc.name
+                groups.setdefault(key, []).append(desc)
+        for group in groups.values():
+            if len(group) > 1:
+                for desc1 in group:
+                    cdef1 = desc1.selfclassdef
+                    for desc2 in group:
+                        cdef2 = desc2.selfclassdef
+                        if cdef1 is not cdef2 and cdef1.issubclass(cdef2):
+                            del descs[desc1]
+                            break
+    simplify_desc_set = staticmethod(simplify_desc_set)
 
 
 def new_or_old_class(c):

Modified: pypy/branch/somepbc-refactoring/pypy/annotation/model.py
==============================================================================
--- pypy/branch/somepbc-refactoring/pypy/annotation/model.py	(original)
+++ pypy/branch/somepbc-refactoring/pypy/annotation/model.py	Fri Nov 25 20:52:58 2005
@@ -303,7 +303,7 @@
         descriptions = dict.fromkeys(descriptions)
         self.descriptions = descriptions
         self.can_be_None = can_be_None
-        self.check()
+        self.simplify()
         if self.isNone():
             self.knowntype = type(None)
             self.const = None
@@ -333,10 +333,14 @@
             raise ValueError("no 'kind' on the 'None' PBC")
         return kinds.keys()[0]
 
-    def check(self):
+    def simplify(self):
         if self.descriptions:
             # We check that the set only contains a single kind of Desc instance
-            self.getKind()
+            kind = self.getKind()
+            # then we remove unnecessary entries in self.descriptions:
+            # some MethodDescs can be 'shadowed' by others
+            if len(self.descriptions) > 1:
+                kind.simplify_desc_set(self.descriptions)
         else:
             assert self.can_be_None, "use s_ImpossibleValue"
 

Modified: pypy/branch/somepbc-refactoring/pypy/rpython/rpbc.py
==============================================================================
--- pypy/branch/somepbc-refactoring/pypy/rpython/rpbc.py	(original)
+++ pypy/branch/somepbc-refactoring/pypy/rpython/rpbc.py	Fri Nov 25 20:52:58 2005
@@ -417,9 +417,9 @@
         return NotImplemented
 
 class AbstractMethodsPBCRepr(Repr):
-    """Representation selected for a PBC of the form {func: classdef...}.
-    It assumes that all the methods come from the same name in a base
-    classdef."""
+    """Representation selected for a PBC of MethodDescs.
+    It assumes that all the methods come from the same name and have
+    been read from instances with a common base."""
 
     def __init__(self, rtyper, s_pbc):
         self.rtyper = rtyper
@@ -429,14 +429,19 @@
                              "bound-method-object or None")
         mdescs = s_pbc.descriptions.keys()
         methodname = mdescs[0].name
+        classdef = mdescs[0].selfclassdef
         for mdesc in mdescs[1:]:
             if mdesc.name != methodname:
                 raise TyperError("cannot find a unique name under which the "
                                  "methods can be found: %r" % (
                         mdescs,))
+            classdef = classdef.commonbase(mdesc.selfclassdef)
+            if classdef is None:
+                raise TyperError("mixing methods coming from instances of "
+                                 "classes with no common base: %r" % (mdescs,))
 
         self.methodname = methodname
-        self.classdef = mdescs[0].classdef.locate_attribute(methodname)
+        self.classdef = classdef.locate_attribute(methodname)
         # the low-level representation is just the bound 'self' argument.
         self.s_im_self = annmodel.SomeInstance(self.classdef)
         self.r_im_self = rclass.getinstancerepr(rtyper, self.classdef)

Modified: pypy/branch/somepbc-refactoring/pypy/translator/test/test_annrpython.py
==============================================================================
--- pypy/branch/somepbc-refactoring/pypy/translator/test/test_annrpython.py	(original)
+++ pypy/branch/somepbc-refactoring/pypy/translator/test/test_annrpython.py	Fri Nov 25 20:52:58 2005
@@ -905,7 +905,7 @@
         bookkeeper = a.bookkeeper
 
         def fam(meth):
-            mdesc = bookkeeper.getmethoddesc(bookkeeper.getdesc(meth.im_func), clsdef(meth.im_class), meth.im_func.func_name)
+            mdesc = bookkeeper.getmethoddesc(bookkeeper.getdesc(meth.im_func), clsdef(meth.im_class), clsdef(meth.im_class), meth.im_func.func_name)
             mdesc2 = bookkeeper.immutablevalue(meth.im_func.__get__(meth.im_class(), meth.im_class)).descriptions.keys()[0]
             assert mdesc == mdesc2 # sanity check
             return mdesc.getcallfamily()
@@ -1468,7 +1468,7 @@
         s = a.build_types(f, [float])
         assert s.const == "dontknow"        
         
-    def MAYBE_test_hidden_method(self):
+    def test_hidden_method(self):
         class Base:
             def method(self):
                 return ["should be hidden"]
@@ -1477,7 +1477,7 @@
         class A(Base):
             def method(self):
                 return "visible"
-        class B(A):
+        class B(A):       # note: it's a chain of subclasses
             def method(self):
                 return None
         def f(flag):



More information about the Pypy-commit mailing list