[pypy-svn] r79688 - in pypy/trunk/pypy: annotation annotation/test interpreter jit/codewriter jit/codewriter/test jit/metainterp jit/metainterp/test jit/tl jit/tl/spli module/cpyext module/cpyext/include module/cpyext/test module/pypyjit/test objspace/std rlib rlib/rsre rlib/rsre/test rlib/test rpython rpython/test

arigo at codespeak.net arigo at codespeak.net
Tue Nov 30 17:02:30 CET 2010


Author: arigo
Date: Tue Nov 30 17:02:26 2010
New Revision: 79688

Modified:
   pypy/trunk/pypy/annotation/description.py
   pypy/trunk/pypy/annotation/listdef.py
   pypy/trunk/pypy/annotation/test/test_annrpython.py
   pypy/trunk/pypy/annotation/unaryop.py
   pypy/trunk/pypy/interpreter/pycode.py
   pypy/trunk/pypy/jit/codewriter/jtransform.py
   pypy/trunk/pypy/jit/codewriter/test/test_list.py
   pypy/trunk/pypy/jit/codewriter/test/test_void_list.py
   pypy/trunk/pypy/jit/metainterp/pyjitpl.py
   pypy/trunk/pypy/jit/metainterp/test/test_list.py
   pypy/trunk/pypy/jit/metainterp/test/test_recursive.py
   pypy/trunk/pypy/jit/metainterp/test/test_virtualizable.py
   pypy/trunk/pypy/jit/metainterp/warmspot.py
   pypy/trunk/pypy/jit/tl/spli/interpreter.py
   pypy/trunk/pypy/jit/tl/tl.py
   pypy/trunk/pypy/module/cpyext/api.py
   pypy/trunk/pypy/module/cpyext/include/tupleobject.h
   pypy/trunk/pypy/module/cpyext/slotdefs.py
   pypy/trunk/pypy/module/cpyext/test/test_cpyext.py
   pypy/trunk/pypy/module/cpyext/test/test_tupleobject.py
   pypy/trunk/pypy/module/cpyext/tupleobject.py
   pypy/trunk/pypy/module/pypyjit/test/test_pypy_c.py
   pypy/trunk/pypy/objspace/std/objspace.py
   pypy/trunk/pypy/rlib/debug.py
   pypy/trunk/pypy/rlib/rsre/rsre_core.py
   pypy/trunk/pypy/rlib/rsre/test/test_zjit.py
   pypy/trunk/pypy/rlib/test/test_debug.py
   pypy/trunk/pypy/rpython/rlist.py
   pypy/trunk/pypy/rpython/test/test_rlist.py
Log:
Merge branch/rlist-jit.

Improve in jtransform.py the handling of constructs like
'frame.locals[n]'.  This is now done with the help of the
annotator: from the hint _immutable_fields_=['locals[*]']
found on the class, the annotator flags the list returned
by 'frame.locals' as being an immutable list; then the
rtyper translates 'frame.locals[n]' with a call to
ll_getitem_foldable instead of ll_getitem.

The goal is to make an extra test in test_list.py pass:
handling IndexErrors out of RPython-level lists.  This is
done essentially by letting the JIT see the code
"if n>=len: raise IndexError" in rlist.py.

Other changes (related):

* fixes left and right because _immutable_fields_=['lst[*]']
  now implies that we only read from the list using a known-
  nonnegative index (checked in jtransform.py)

* cpyext refactoring about tuples



Modified: pypy/trunk/pypy/annotation/description.py
==============================================================================
--- pypy/trunk/pypy/annotation/description.py	(original)
+++ pypy/trunk/pypy/annotation/description.py	Tue Nov 30 17:02:26 2010
@@ -636,6 +636,24 @@
                     return self
         return None
 
+    def maybe_return_immutable_list(self, attr, s_result):
+        # hack: 'x.lst' where lst is listed in _immutable_fields_ as 'lst[*]'
+        # should really return an immutable list as a result.  Implemented
+        # by changing the result's annotation (but not, of course, doing an
+        # actual copy in the rtyper).  Tested in pypy.rpython.test.test_rlist,
+        # test_immutable_list_out_of_instance.
+        search = '%s[*]' % (attr,)
+        cdesc = self
+        while cdesc is not None:
+            if '_immutable_fields_' in cdesc.classdict:
+                if search in cdesc.classdict['_immutable_fields_'].value:
+                    s_result.listdef.never_resize()
+                    s_copy = s_result.listdef.offspring()
+                    s_copy.listdef.mark_as_immutable()
+                    return s_copy
+            cdesc = cdesc.basedesc
+        return s_result     # common case
+
     def consider_call_site(bookkeeper, family, descs, args, s_result):
         from pypy.annotation.model import SomeInstance, SomePBC, s_None
         if len(descs) == 1:

Modified: pypy/trunk/pypy/annotation/listdef.py
==============================================================================
--- pypy/trunk/pypy/annotation/listdef.py	(original)
+++ pypy/trunk/pypy/annotation/listdef.py	Tue Nov 30 17:02:26 2010
@@ -6,11 +6,16 @@
 class TooLateForChange(Exception):
     pass
 
+class ListChangeUnallowed(Exception):
+    pass
+
 class ListItem(object):
     mutated = False    # True for lists mutated after creation
     resized = False    # True for lists resized after creation
     range_step = None  # the step -- only for lists only created by a range()
     dont_change_any_more = False   # set to True when too late for changes
+    immutable = False  # for getattr out of _immutable_fields_ = ['attr[*]']
+    must_not_resize = False   # make_sure_not_resized()
 
     # what to do if range_step is different in merge.
     # - if one is a list (range_step is None), unify to a list.
@@ -26,7 +31,6 @@
         self.bookkeeper = bookkeeper
         self.itemof = {}  # set of all ListDefs using this ListItem
         self.read_locations = {}
-        self.dont_resize = False
         if bookkeeper is None:
             self.dont_change_any_more = True
 
@@ -34,12 +38,15 @@
         if not self.mutated:
             if self.dont_change_any_more:
                 raise TooLateForChange
+            self.immutable = False
             self.mutated = True
 
     def resize(self):
         if not self.resized:
-            if self.dont_change_any_more or self.dont_resize:
+            if self.dont_change_any_more:
                 raise TooLateForChange
+            if self.must_not_resize:
+                raise ListChangeUnallowed("resizing list")
             self.resized = True
 
     def setrangestep(self, step):
@@ -63,11 +70,13 @@
                     # things more general
                     self, other = other, self
 
-            if other.dont_resize:
-                if self.resized:                    
-                    raise TooLateForChange()
-                self.dont_resize = True
-            if other.mutated: self.mutate()
+            self.immutable &= other.immutable
+            if other.must_not_resize:
+                if self.resized:
+                    raise ListChangeUnallowed("list merge with a resized")
+                self.must_not_resize = True
+            if other.mutated:
+                self.mutate()
             if other.resized:
                 self.resize()
             if other.range_step != self.range_step:
@@ -176,9 +185,11 @@
         self.listitem.generalize(s_value)
 
     def __repr__(self):
-        return '<[%r]%s%s>' % (self.listitem.s_value,
+        return '<[%r]%s%s%s%s>' % (self.listitem.s_value,
                                self.listitem.mutated and 'm' or '',
-                               self.listitem.resized and 'r' or '')
+                               self.listitem.resized and 'r' or '',
+                               self.listitem.immutable and 'I' or '',
+                               self.listitem.must_not_resize and '!R' or '')
 
     def mutate(self):
         self.listitem.mutate()
@@ -189,13 +200,20 @@
 
     def never_resize(self):
         if self.listitem.resized:
-            raise TooLateForChange()
-        self.listitem.dont_resize = True
+            raise ListChangeUnallowed("list already resized")
+        self.listitem.must_not_resize = True
 
-    def never_mutate(self):
-        if self.listitem.resized or self.listitem.mutated:
-            raise TooLateForChange()
-        self.listitem.dont_change_any_more = True
+    def mark_as_immutable(self):
+        # Sets the 'immutable' flag.  Note that unlike "never resized",
+        # the immutable flag is only a hint.  It is cleared again e.g.
+        # when we merge with a "normal" list that doesn't have it.  It
+        # is thus expected to live only shortly, mostly for the case
+        # of writing 'x.list[n]'.
+        self.never_resize()
+        if not self.listitem.mutated:
+            self.listitem.immutable = True
+        #else: it's fine, don't set immutable=True at all (see
+        #      test_can_merge_immutable_list_with_regular_list)
 
 MOST_GENERAL_LISTDEF = ListDef(None, SomeObject())
 

Modified: pypy/trunk/pypy/annotation/test/test_annrpython.py
==============================================================================
--- pypy/trunk/pypy/annotation/test/test_annrpython.py	(original)
+++ pypy/trunk/pypy/annotation/test/test_annrpython.py	Tue Nov 30 17:02:26 2010
@@ -10,7 +10,7 @@
 from pypy.translator.translator import graphof as tgraphof
 from pypy.annotation import policy
 from pypy.annotation import specialize
-from pypy.annotation.listdef import ListDef, TooLateForChange
+from pypy.annotation.listdef import ListDef, ListChangeUnallowed
 from pypy.annotation.dictdef import DictDef
 from pypy.objspace.flow.model import *
 from pypy.rlib.rarithmetic import r_uint, base_int, r_longlong, r_ulonglong
@@ -3206,7 +3206,7 @@
             l.append(4)
 
         a = self.RPythonAnnotator()
-        py.test.raises(TooLateForChange, a.build_types, g, [])
+        py.test.raises(ListChangeUnallowed, a.build_types, g, [])
         assert called
 
     def test_listitem_no_mutating2(self):
@@ -3229,7 +3229,7 @@
 
         a = self.RPythonAnnotator()
         a.translator.config.translation.list_comprehension_operations = True
-        py.test.raises(TooLateForChange, a.build_types, fn, [int])
+        py.test.raises(ListChangeUnallowed, a.build_types, fn, [int])
 
     def test_listitem_never_resize(self):
         from pypy.rlib.debug import check_annotation
@@ -3243,7 +3243,7 @@
             check_annotation(l, checker)
 
         a = self.RPythonAnnotator()
-        py.test.raises(TooLateForChange, a.build_types, f, [])
+        py.test.raises(ListChangeUnallowed, a.build_types, f, [])
 
 
     def test_len_of_empty_list(self):
@@ -3357,6 +3357,87 @@
         # not a constant: both __enter__ and __exit__ have been annotated
         assert not s.is_constant()
 
+    def test_make_sure_not_resized(self):
+        from pypy.rlib.debug import make_sure_not_resized
+
+        def pycode(consts):
+            make_sure_not_resized(consts)
+        def build1():
+            return pycode(consts=[1])
+        def build2():
+            return pycode(consts=[0])
+        def fn():
+            build1()
+            build2()
+
+        a = self.RPythonAnnotator()
+        a.translator.config.translation.list_comprehension_operations = True
+        a.build_types(fn, [])
+        # assert did not raise ListChangeUnallowed
+
+    def test_return_immutable_list(self):
+        class A:
+            _immutable_fields_ = 'lst[*]'
+        def f(n):
+            a = A()
+            l1 = [n, 0]
+            l1[1] = n+1
+            a.lst = l1
+            return a.lst
+
+        a = self.RPythonAnnotator()
+        s = a.build_types(f, [int])
+        assert s.listdef.listitem.immutable
+
+    def test_immutable_list_is_actually_resized(self):
+        class A:
+            _immutable_fields_ = 'lst[*]'
+        def f(n):
+            a = A()
+            l1 = [n]
+            l1.append(n+1)
+            a.lst = l1
+            return a.lst
+
+        a = self.RPythonAnnotator()
+        py.test.raises(ListChangeUnallowed, a.build_types, f, [int])
+
+    def test_can_merge_immutable_list_with_regular_list(self):
+        class A:
+            _immutable_fields_ = 'lst[*]'
+        def foo(lst):
+            pass
+
+        def f(n):
+            a = A()
+            l1 = [n, 0]
+            l1[1] = n+1
+            a.lst = l1
+            if n > 0:
+                foo(a.lst)
+            else:
+                lst = [0]
+                lst[0] = n
+                foo(lst)
+
+        a = self.RPythonAnnotator()
+        a.build_types(f, [int])
+
+        def f(n):
+            a = A()
+            l1 = [n, 0]
+            l1[1] = n+1
+            a.lst = l1
+            if n > 0:
+                lst = [0]
+                lst[0] = n
+                foo(lst)
+            else:
+                foo(a.lst)
+
+        a = self.RPythonAnnotator()
+        a.build_types(f, [int])
+
 
 def g(n):
     return [0,1,2,n]

Modified: pypy/trunk/pypy/annotation/unaryop.py
==============================================================================
--- pypy/trunk/pypy/annotation/unaryop.py	(original)
+++ pypy/trunk/pypy/annotation/unaryop.py	Tue Nov 30 17:02:26 2010
@@ -615,6 +615,9 @@
                     if basedef.classdesc.all_enforced_attrs is not None:
                         if attr in basedef.classdesc.all_enforced_attrs:
                             raise HarmlesslyBlocked("get enforced attr")
+            elif isinstance(s_result, SomeList):
+                s_result = ins.classdef.classdesc.maybe_return_immutable_list(
+                    attr, s_result)
             return s_result
         return SomeObject()
     getattr.can_only_throw = []

Modified: pypy/trunk/pypy/interpreter/pycode.py
==============================================================================
--- pypy/trunk/pypy/interpreter/pycode.py	(original)
+++ pypy/trunk/pypy/interpreter/pycode.py	Tue Nov 30 17:02:26 2010
@@ -15,7 +15,7 @@
     CO_OPTIMIZED, CO_NEWLOCALS, CO_VARARGS, CO_VARKEYWORDS, CO_NESTED,
     CO_GENERATOR, CO_CONTAINSGLOBALS)
 from pypy.rlib.rarithmetic import intmask
-from pypy.rlib.debug import make_sure_not_resized, make_sure_not_modified
+from pypy.rlib.debug import make_sure_not_resized
 from pypy.rlib import jit
 from pypy.rlib.objectmodel import compute_hash
 from pypy.tool.stdlib_opcode import opcodedesc, HAVE_ARGUMENT
@@ -69,7 +69,7 @@
         self.co_stacksize = stacksize
         self.co_flags = flags
         self.co_code = code
-        self.co_consts_w = make_sure_not_modified(consts)
+        self.co_consts_w = consts
         self.co_names_w = [space.new_interned_str(aname) for aname in names]
         self.co_varnames = varnames
         self.co_freevars = freevars
@@ -269,7 +269,7 @@
         dis.dis(co)
 
     def fget_co_consts(space, self):
-        return space.newtuple(self.co_consts_w[:])
+        return space.newtuple(self.co_consts_w)
     
     def fget_co_names(space, self):
         return space.newtuple(self.co_names_w)
@@ -383,7 +383,7 @@
             w(self.co_stacksize), 
             w(self.co_flags),
             w(self.co_code), 
-            space.newtuple(self.co_consts_w[:]), 
+            space.newtuple(self.co_consts_w), 
             space.newtuple(self.co_names_w), 
             space.newtuple([w(v) for v in self.co_varnames]), 
             w(self.co_filename),

Modified: pypy/trunk/pypy/jit/codewriter/jtransform.py
==============================================================================
--- pypy/trunk/pypy/jit/codewriter/jtransform.py	(original)
+++ pypy/trunk/pypy/jit/codewriter/jtransform.py	Tue Nov 30 17:02:26 2010
@@ -24,6 +24,7 @@
 
 
 class Transformer(object):
+    vable_array_vars = None
 
     def __init__(self, cpu=None, callcontrol=None, portal_jd=None):
         self.cpu = cpu
@@ -38,7 +39,6 @@
     def optimize_block(self, block):
         if block.operations == ():
             return
-        self.immutable_arrays = {}
         self.vable_array_vars = {}
         self.vable_flags = {}
         renamings = {}
@@ -83,6 +83,7 @@
         self.follow_constant_exit(block)
         self.optimize_goto_if_not(block)
         for link in block.exits:
+            self._check_no_vable_array(link.args)
             self._do_renaming_on_link(renamings, link)
 
     def _do_renaming(self, rename, op):
@@ -100,6 +101,28 @@
                 op.args[i] = ListOfKind(v.kind, newlst)
         return op
 
+    def _check_no_vable_array(self, list):
+        if not self.vable_array_vars:
+            return
+        for v in list:
+            if v in self.vable_array_vars:
+                raise AssertionError(
+                    "A virtualizable array is passed around; it should\n"
+                    "only be used immediately after being read.  Note\n"
+                    "that a possible cause is indexing with an index not\n"
+                    "known non-negative, or catching IndexError, or\n"
+                    "not inlining at all (for tests: use listops=True).\n"
+                    "Occurred in: %r" % self.graph)
+            # extra expanation: with the way things are organized in
+            # rpython/rlist.py, the ll_getitem becomes a function call
+            # that is typically meant to be inlined by the JIT, but
+            # this does not work with vable arrays because
+            # jtransform.py expects the getfield and the getarrayitem
+            # to be in the same basic block.  It works a bit as a hack
+            # for simple cases where we performed the backendopt
+            # inlining before (even with a very low threshold, because
+            # there is _always_inline_ on the relevant functions).
+
     def _do_renaming_on_link(self, rename, link):
         for i, v in enumerate(link.args):
             if isinstance(v, Variable):
@@ -170,9 +193,12 @@
         else:
             return rewrite(self, op)
 
-    def rewrite_op_same_as(self, op): pass
-    def rewrite_op_cast_pointer(self, op): pass
-    def rewrite_op_cast_opaque_ptr(self, op): pass   # rlib.rerased
+    def rewrite_op_same_as(self, op):
+        if op.args[0] in self.vable_array_vars:
+            self.vable_array_vars[op.result]= self.vable_array_vars[op.args[0]]
+
+    rewrite_op_cast_pointer = rewrite_op_same_as
+    rewrite_op_cast_pointer = rewrite_op_same_as   # rlib.rerased
     def rewrite_op_cast_primitive(self, op): pass
     def rewrite_op_cast_bool_to_int(self, op): pass
     def rewrite_op_cast_bool_to_uint(self, op): pass
@@ -257,6 +283,7 @@
            The name is one of '{residual,direct}_call_{r,ir,irf}_{i,r,f,v}'."""
         if args is None:
             args = op.args[1:]
+        self._check_no_vable_array(args)
         lst_i, lst_r, lst_f = self.make_three_lists(args)
         reskind = getkind(op.result.concretetype)[0]
         if lst_f or reskind == 'f': kinds = 'irf'
@@ -525,17 +552,14 @@
                                                 arrayfielddescr,
                                                 arraydescr)
             return []
-        # check for deepfrozen structures that force constant-folding
-        immut = v_inst.concretetype.TO._immutable_field(c_fieldname.value)
-        if immut:
+        # check for _immutable_fields_ hints
+        if v_inst.concretetype.TO._immutable_field(c_fieldname.value):
             if (self.callcontrol is not None and
                 self.callcontrol.could_be_green_field(v_inst.concretetype.TO,
                                                       c_fieldname.value)):
                 pure = '_greenfield'
             else:
                 pure = '_pure'
-            if immut == "[*]":
-                self.immutable_arrays[op.result] = True
         else:
             pure = ''
         argname = getattr(v_inst.concretetype.TO, '_gckind', 'gc')
@@ -932,21 +956,19 @@
         # base hints on the name of the ll function, which is a bit xxx-ish
         # but which is safe for now
         assert func.func_name.startswith('ll_')
+        # check that we have carefully placed the oopspec in
+        # pypy/rpython/rlist.py.  There should not be an oopspec on
+        # a ll_getitem or ll_setitem that expects a 'func' argument.
+        # The idea is that a ll_getitem/ll_setitem with dum_checkidx
+        # should get inlined by the JIT, so that we see the potential
+        # 'raise IndexError'.
+        assert 'func' not in func.func_code.co_varnames
         non_negative = '_nonneg' in func.func_name
         fast = '_fast' in func.func_name
-        if fast:
-            can_raise = False
-            non_negative = True
-        else:
-            tag = op.args[1].value
-            assert tag in (rlist.dum_nocheck, rlist.dum_checkidx)
-            can_raise = tag != rlist.dum_nocheck
-        return non_negative, can_raise
+        return non_negative or fast
 
     def _prepare_list_getset(self, op, descr, args, checkname):
-        non_negative, can_raise = self._get_list_nonneg_canraise_flags(op)
-        if can_raise:
-            raise NotSupported("list operation can raise")
+        non_negative = self._get_list_nonneg_canraise_flags(op)
         if non_negative:
             return args[1], []
         else:
@@ -958,9 +980,8 @@
             return v_posindex, [op0, op1]
 
     def _prepare_void_list_getset(self, op):
-        non_negative, can_raise = self._get_list_nonneg_canraise_flags(op)
-        if can_raise:
-            raise NotSupported("list operation can raise")
+        # sanity check:
+        self._get_list_nonneg_canraise_flags(op)
 
     def _get_initial_newlist_length(self, op, args):
         # normalize number of arguments to the 'newlist' function
@@ -1005,7 +1026,7 @@
         v_index, extraop = self._prepare_list_getset(op, arraydescr, args,
                                                      'check_neg_index')
         extra = getkind(op.result.concretetype)[0]
-        if pure or args[0] in self.immutable_arrays:
+        if pure:
             extra = 'pure_' + extra
         op = SpaceOperation('getarrayitem_gc_%s' % extra,
                             [args[0], arraydescr, v_index], op.result)

Modified: pypy/trunk/pypy/jit/codewriter/test/test_list.py
==============================================================================
--- pypy/trunk/pypy/jit/codewriter/test/test_list.py	(original)
+++ pypy/trunk/pypy/jit/codewriter/test/test_list.py	Tue Nov 30 17:02:26 2010
@@ -56,9 +56,8 @@
     if '/' in oopspec_name:
         oopspec_name, property = oopspec_name.split('/')
         def force_flags(op):
-            if property == 'NONNEG':   return True, False
-            if property == 'NEG':      return False, False
-            if property == 'CANRAISE': return False, True
+            if property == 'NONNEG':   return True
+            if property == 'NEG':      return False
             raise ValueError(property)
         tr._get_list_nonneg_canraise_flags = force_flags
     op = SpaceOperation('direct_call',
@@ -122,9 +121,6 @@
                      check_neg_index %r0, <ArrayDescr>, %i0 -> %i1
                      getarrayitem_gc_i %r0, <ArrayDescr>, %i1 -> %i2
                  """)
-    builtin_test('list.getitem/CANRAISE',
-                 [varoftype(FIXEDLIST), varoftype(lltype.Signed)],
-                 lltype.Signed, NotSupported)
 
 def test_fixed_getitem_foldable():
     builtin_test('list.getitem_foldable/NONNEG',
@@ -139,9 +135,6 @@
                      check_neg_index %r0, <ArrayDescr>, %i0 -> %i1
                      getarrayitem_gc_pure_i %r0, <ArrayDescr>, %i1 -> %i2
                  """)
-    builtin_test('list.getitem_foldable/CANRAISE',
-                 [varoftype(FIXEDLIST), varoftype(lltype.Signed)],
-                 lltype.Signed, NotSupported)
 
 def test_fixed_setitem():
     builtin_test('list.setitem/NONNEG', [varoftype(FIXEDLIST),
@@ -158,10 +151,6 @@
                      check_neg_index %r0, <ArrayDescr>, %i0 -> %i1
                      setarrayitem_gc_i %r0, <ArrayDescr>, %i1, %i2
                  """)
-    builtin_test('list.setitem/CANRAISE', [varoftype(FIXEDLIST),
-                                           varoftype(lltype.Signed),
-                                           varoftype(lltype.Signed)],
-                 lltype.Void, NotSupported)
 
 def test_fixed_len():
     builtin_test('list.len', [varoftype(FIXEDLIST)], lltype.Signed,
@@ -206,9 +195,6 @@
         check_resizable_neg_index %r0, <FieldDescr length>, %i0 -> %i1
         getlistitem_gc_i %r0, <FieldDescr items>, <ArrayDescr>, %i1 -> %i2
                  """)
-    builtin_test('list.getitem/CANRAISE',
-                 [varoftype(VARLIST), varoftype(lltype.Signed)],
-                 lltype.Signed, NotSupported)
 
 def test_resizable_setitem():
     builtin_test('list.setitem/NONNEG', [varoftype(VARLIST),
@@ -225,10 +211,6 @@
         check_resizable_neg_index %r0, <FieldDescr length>, %i0 -> %i1
         setlistitem_gc_i %r0, <FieldDescr items>, <ArrayDescr>, %i1, %i2
                  """)
-    builtin_test('list.setitem/CANRAISE', [varoftype(VARLIST),
-                                           varoftype(lltype.Signed),
-                                           varoftype(lltype.Signed)],
-                 lltype.Void, NotSupported)
 
 def test_resizable_len():
     builtin_test('list.len', [varoftype(VARLIST)], lltype.Signed,

Modified: pypy/trunk/pypy/jit/codewriter/test/test_void_list.py
==============================================================================
--- pypy/trunk/pypy/jit/codewriter/test/test_void_list.py	(original)
+++ pypy/trunk/pypy/jit/codewriter/test/test_void_list.py	Tue Nov 30 17:02:26 2010
@@ -51,9 +51,6 @@
     builtin_test('list.getitem/NEG',
                  [varoftype(FIXEDLIST), varoftype(lltype.Signed)],
                  lltype.Void, "")
-    builtin_test('list.getitem/CANRAISE',
-                 [varoftype(FIXEDLIST), varoftype(lltype.Signed)],
-                 lltype.Void, NotSupported)
 
 def test_fixed_getitem_foldable():
     builtin_test('list.getitem_foldable/NONNEG',
@@ -62,9 +59,6 @@
     builtin_test('list.getitem_foldable/NEG',
                  [varoftype(FIXEDLIST), varoftype(lltype.Signed)],
                  lltype.Void, "")
-    builtin_test('list.getitem_foldable/CANRAISE',
-                 [varoftype(FIXEDLIST), varoftype(lltype.Signed)],
-                 lltype.Void, NotSupported)
 
 def test_fixed_setitem():
     builtin_test('list.setitem/NONNEG', [varoftype(FIXEDLIST),
@@ -75,10 +69,6 @@
                                       varoftype(lltype.Signed),
                                       varoftype(lltype.Void)],
                  lltype.Void, "")
-    builtin_test('list.setitem/CANRAISE', [varoftype(FIXEDLIST),
-                                           varoftype(lltype.Signed),
-                                           varoftype(lltype.Void)],
-                 lltype.Void, NotSupported)
 
 def test_fixed_len():
     builtin_test('list.len', [varoftype(FIXEDLIST)], lltype.Signed,
@@ -115,9 +105,6 @@
     builtin_test('list.getitem/NEG',
                  [varoftype(VARLIST), varoftype(lltype.Signed)],
                  lltype.Void, "")
-    builtin_test('list.getitem/CANRAISE',
-                 [varoftype(VARLIST), varoftype(lltype.Signed)],
-                 lltype.Void, NotSupported)
 
 def test_resizable_setitem():
     builtin_test('list.setitem/NONNEG', [varoftype(VARLIST),
@@ -128,10 +115,6 @@
                                       varoftype(lltype.Signed),
                                       varoftype(lltype.Void)],
                  lltype.Void, "")
-    builtin_test('list.setitem/CANRAISE', [varoftype(VARLIST),
-                                           varoftype(lltype.Signed),
-                                           varoftype(lltype.Void)],
-                 lltype.Void, NotSupported)
 
 def test_resizable_len():
     builtin_test('list.len', [varoftype(VARLIST)], lltype.Signed,

Modified: pypy/trunk/pypy/jit/metainterp/pyjitpl.py
==============================================================================
--- pypy/trunk/pypy/jit/metainterp/pyjitpl.py	(original)
+++ pypy/trunk/pypy/jit/metainterp/pyjitpl.py	Tue Nov 30 17:02:26 2010
@@ -612,8 +612,10 @@
         virtualizable = vinfo.unwrap_virtualizable_box(virtualizable_box)
         arrayindex = vinfo.array_field_by_descrs[arrayfielddescr]
         index = indexbox.getint()
-        if index < 0:
-            index += vinfo.get_array_length(virtualizable, arrayindex)
+        # Support for negative index: disabled
+        # (see codewriter/jtransform.py, _check_no_vable_array).
+        #if index < 0:
+        #    index += vinfo.get_array_length(virtualizable, arrayindex)
         assert 0 <= index < vinfo.get_array_length(virtualizable, arrayindex)
         return vinfo.get_index_in_array(virtualizable, arrayindex, index)
 

Modified: pypy/trunk/pypy/jit/metainterp/test/test_list.py
==============================================================================
--- pypy/trunk/pypy/jit/metainterp/test/test_list.py	(original)
+++ pypy/trunk/pypy/jit/metainterp/test/test_list.py	Tue Nov 30 17:02:26 2010
@@ -188,6 +188,26 @@
         assert res == f(4)
         self.check_loops(call=0, getfield_gc=0)
 
+    def test_fold_indexerror(self):
+        jitdriver = JitDriver(greens = [], reds = ['total', 'n', 'lst'])
+        def f(n):
+            lst = []
+            total = 0
+            while n > 0:
+                jitdriver.can_enter_jit(lst=lst, n=n, total=total)
+                jitdriver.jit_merge_point(lst=lst, n=n, total=total)
+                lst.append(n)
+                try:
+                    total += lst[n]
+                except IndexError:
+                    total += 1000
+                n -= 1
+            return total
+
+        res = self.meta_interp(f, [15], listops=True)
+        assert res == f(15)
+        self.check_loops(guard_exception=0)
+
 class TestOOtype(ListTests, OOJitMixin):
     pass
 

Modified: pypy/trunk/pypy/jit/metainterp/test/test_recursive.py
==============================================================================
--- pypy/trunk/pypy/jit/metainterp/test/test_recursive.py	(original)
+++ pypy/trunk/pypy/jit/metainterp/test/test_recursive.py	Tue Nov 30 17:02:26 2010
@@ -927,12 +927,16 @@
                                             x=x)
                 frame.s = hint(frame.s, promote=True)
                 n -= 1
-                x += frame.l[frame.s]
+                s = frame.s
+                assert s >= 0
+                x += frame.l[s]
                 frame.s += 1
                 if codeno == 0:
                     subframe = Frame([n, n+1, n+2, n+3], 0)
                     x += f(1, 10, 1, subframe)
-                x += frame.l[frame.s]
+                s = frame.s
+                assert s >= 0
+                x += frame.l[s]
                 x += len(frame.l)
                 frame.s -= 1
             return x

Modified: pypy/trunk/pypy/jit/metainterp/test/test_virtualizable.py
==============================================================================
--- pypy/trunk/pypy/jit/metainterp/test/test_virtualizable.py	(original)
+++ pypy/trunk/pypy/jit/metainterp/test/test_virtualizable.py	Tue Nov 30 17:02:26 2010
@@ -480,9 +480,13 @@
                 myjitdriver.jit_merge_point(frame=frame, n=n, x=x)
                 frame.s = hint(frame.s, promote=True)
                 n -= 1
-                x += frame.l[frame.s]
+                s = frame.s
+                assert s >= 0
+                x += frame.l[s]
                 frame.s += 1
-                x += frame.l[frame.s]
+                s = frame.s
+                assert s >= 0
+                x += frame.l[s]
                 x += len(frame.l)
                 frame.s -= 1
             return x
@@ -994,7 +998,9 @@
                 jitdriver.can_enter_jit(frame=frame, n=n)
                 jitdriver.jit_merge_point(frame=frame, n=n)
                 popped = frame.stack[frame.stackpos]
-                frame.stackpos -= 1
+                sp = frame.stackpos - 1
+                assert sp >= 0
+                frame.stackpos = sp
                 to_push = intmask(popped * 3)
                 frame.stack[frame.stackpos] = to_push
                 frame.stackpos += 1

Modified: pypy/trunk/pypy/jit/metainterp/warmspot.py
==============================================================================
--- pypy/trunk/pypy/jit/metainterp/warmspot.py	(original)
+++ pypy/trunk/pypy/jit/metainterp/warmspot.py	Tue Nov 30 17:02:26 2010
@@ -159,6 +159,8 @@
         self.check_access_directly_sanity(graphs)
         if backendopt:
             self.prejit_optimizations(policy, graphs)
+        elif self.opt.listops:
+            self.prejit_optimizations_minimal_inline(policy, graphs)
 
         self.build_meta_interp(ProfilerClass)
         self.make_args_specifications()
@@ -256,6 +258,10 @@
                               remove_asserts=True,
                               really_remove_asserts=True)
 
+    def prejit_optimizations_minimal_inline(self, policy, graphs):
+        from pypy.translator.backendopt.inline import auto_inline_graphs
+        auto_inline_graphs(self.translator, graphs, 0.01)
+
     def build_cpu(self, CPUClass, translate_support_code=False,
                   no_stats=False, **kwds):
         assert CPUClass is not None

Modified: pypy/trunk/pypy/jit/tl/spli/interpreter.py
==============================================================================
--- pypy/trunk/pypy/jit/tl/spli/interpreter.py	(original)
+++ pypy/trunk/pypy/jit/tl/spli/interpreter.py	Tue Nov 30 17:02:26 2010
@@ -105,26 +105,32 @@
         self.stack_depth += 1
 
     def pop(self):
-        self.stack_depth -= 1
-        val = self.value_stack[self.stack_depth]
-        self.value_stack[self.stack_depth] = None
+        sd = self.stack_depth - 1
+        assert sd >= 0
+        self.stack_depth = sd
+        val = self.value_stack[sd]
+        self.value_stack[sd] = None
         return val
 
     def pop_many(self, n):
         return [self.pop() for i in range(n)]
 
     def peek(self):
-        return self.value_stack[self.stack_depth - 1]
+        sd = self.stack_depth - 1
+        assert sd >= 0
+        return self.value_stack[sd]
 
     def POP_TOP(self, _, next_instr, code):
         self.pop()
         return next_instr
 
     def LOAD_FAST(self, name_index, next_instr, code):
+        assert name_index >= 0
         self.push(self.locals[name_index])
         return next_instr
 
     def STORE_FAST(self, name_index, next_instr, code):
+        assert name_index >= 0
         self.locals[name_index] = self.pop()
         return next_instr
 

Modified: pypy/trunk/pypy/jit/tl/tl.py
==============================================================================
--- pypy/trunk/pypy/jit/tl/tl.py	(original)
+++ pypy/trunk/pypy/jit/tl/tl.py	Tue Nov 30 17:02:26 2010
@@ -16,32 +16,40 @@
     def __init__(self, size):
         self = hint(self, access_directly=True, fresh_virtualizable=True)
         self.stack = [0] * size
-        self.stackpos = 0
+        self.stackpos = 0        # always store a known-nonneg integer here
 
     def append(self, elem):
         self.stack[self.stackpos] = elem
         self.stackpos += 1
 
     def pop(self):
-        self.stackpos -= 1
-        if self.stackpos < 0:
+        stackpos = self.stackpos - 1
+        if stackpos < 0:
             raise IndexError
-        return self.stack[self.stackpos]
+        self.stackpos = stackpos     # always store a known-nonneg integer here
+        return self.stack[stackpos]
 
     def pick(self, i):
-        self.append(self.stack[self.stackpos - i - 1])
+        n = self.stackpos - i - 1
+        assert n >= 0
+        self.append(self.stack[n])
 
     def put(self, i):
         elem = self.pop()
-        self.stack[self.stackpos - i - 1] = elem
+        n = self.stackpos - i - 1
+        assert n >= 0
+        self.stack[n] = elem
 
     def roll(self, r):
         if r < -1:
             i = self.stackpos + r
             if i < 0:
                 raise IndexError
-            elem = self.stack[self.stackpos - 1]
+            n = self.stackpos - 1
+            assert n >= 0
+            elem = self.stack[n]
             for j in range(self.stackpos - 2, i - 1, -1):
+                assert j >= 0
                 self.stack[j + 1] = self.stack[j]
             self.stack[i] = elem
         elif r > 1:
@@ -51,7 +59,9 @@
             elem = self.stack[i]
             for j in range(i, self.stackpos - 1):
                 self.stack[j] = self.stack[j + 1]
-            self.stack[self.stackpos - 1] = elem
+            n = self.stackpos - 1
+            assert n >= 0
+            self.stack[n] = elem
 
 
 def make_interp(supports_call):

Modified: pypy/trunk/pypy/module/cpyext/api.py
==============================================================================
--- pypy/trunk/pypy/module/cpyext/api.py	(original)
+++ pypy/trunk/pypy/module/cpyext/api.py	Tue Nov 30 17:02:26 2010
@@ -369,7 +369,7 @@
         }.items():
         GLOBALS['Py%s_Type#' % (cpyname, )] = ('PyTypeObject*', pypyexpr)
 
-    for cpyname in 'Method List Int Long Dict Tuple Class'.split():
+    for cpyname in 'Method List Int Long Dict Class'.split():
         FORWARD_DECLS.append('typedef struct { PyObject_HEAD } '
                              'Py%sObject' % (cpyname, ))
 build_exported_objects()

Modified: pypy/trunk/pypy/module/cpyext/include/tupleobject.h
==============================================================================
--- pypy/trunk/pypy/module/cpyext/include/tupleobject.h	(original)
+++ pypy/trunk/pypy/module/cpyext/include/tupleobject.h	Tue Nov 30 17:02:26 2010
@@ -10,9 +10,19 @@
 /* defined in varargswrapper.c */
 PyObject * PyTuple_Pack(Py_ssize_t, ...);
 
-#define PyTuple_SET_ITEM PyTuple_SetItem
-#define PyTuple_GET_ITEM PyTuple_GetItem
+typedef struct {
+    PyObject_HEAD
+    PyObject **items;
+    Py_ssize_t size;
+} PyTupleObject;
 
+#define PyTuple_GET_ITEM        PyTuple_GetItem
+
+/* Macro, trading safety for speed */
+#define PyTuple_GET_SIZE(op)    (((PyTupleObject *)(op))->size)
+
+/* Macro, *only* to be used to fill in brand new tuples */
+#define PyTuple_SET_ITEM(op, i, v) (((PyTupleObject *)(op))->items[i] = v)
 
 #ifdef __cplusplus
 }

Modified: pypy/trunk/pypy/module/cpyext/slotdefs.py
==============================================================================
--- pypy/trunk/pypy/module/cpyext/slotdefs.py	(original)
+++ pypy/trunk/pypy/module/cpyext/slotdefs.py	Tue Nov 30 17:02:26 2010
@@ -28,14 +28,14 @@
 
 def check_num_args(space, ob, n):
     from pypy.module.cpyext.tupleobject import PyTuple_CheckExact, \
-            PyTuple_GET_SIZE
+            _PyTuple_Size_Fast
     if not PyTuple_CheckExact(space, ob):
         raise OperationError(space.w_SystemError,
             space.wrap("PyArg_UnpackTuple() argument list is not a tuple"))
-    if n == PyTuple_GET_SIZE(space, ob):
+    if n == _PyTuple_Size_Fast(space, ob):
         return
     raise operationerrfmt(space.w_TypeError,
-        "expected %d arguments, got %d", n, PyTuple_GET_SIZE(space, ob))
+        "expected %d arguments, got %d", n, _PyTuple_Size_Fast(space, ob))
 
 def wrap_init(space, w_self, w_args, func, w_kwargs):
     func_init = rffi.cast(initproc, func)

Modified: pypy/trunk/pypy/module/cpyext/test/test_cpyext.py
==============================================================================
--- pypy/trunk/pypy/module/cpyext/test/test_cpyext.py	(original)
+++ pypy/trunk/pypy/module/cpyext/test/test_cpyext.py	Tue Nov 30 17:02:26 2010
@@ -545,16 +545,17 @@
             PyObject *true = Py_True;
             PyObject *tup = NULL;
             int refcnt = true->ob_refcnt;
-            int refcnt_after;
+            int refcnt_middle, refcnt_after;
 
             tup = PyTuple_New(1);
             Py_INCREF(true);
             if (PyTuple_SetItem(tup, 0, true) < 0)
                 return NULL;
-            refcnt_after = true->ob_refcnt;
+            refcnt_middle = true->ob_refcnt;
             Py_DECREF(tup);
-            fprintf(stderr, "REFCNT2 %i %i\\n", refcnt, refcnt_after);
-            return PyBool_FromLong(refcnt_after == refcnt);
+            refcnt_after = true->ob_refcnt;
+            fprintf(stderr, "REFCNT2 %i %i %i\\n", refcnt, refcnt_middle, refcnt_after);
+            return PyBool_FromLong(refcnt_after == refcnt && refcnt_middle == refcnt+1);
         }
 
         static PyMethodDef methods[] = {

Modified: pypy/trunk/pypy/module/cpyext/test/test_tupleobject.py
==============================================================================
--- pypy/trunk/pypy/module/cpyext/test/test_tupleobject.py	(original)
+++ pypy/trunk/pypy/module/cpyext/test/test_tupleobject.py	Tue Nov 30 17:02:26 2010
@@ -7,24 +7,34 @@
 class TestTupleObject(BaseApiTest):
     def test_tupleobject(self, space, api):
         assert not api.PyTuple_Check(space.w_None)
-        assert api.PyTuple_SetItem(space.w_None, 0, space.w_None) == -1
+        #assert api.PyTuple_SetItem(space.w_None, 0, space.w_None) == -1     XXX
         atuple = space.newtuple([0, 1, 'yay'])
         assert api.PyTuple_Size(atuple) == 3
-        assert api.PyTuple_GET_SIZE(atuple) == 3
-        raises(TypeError, api.PyTuple_Size(space.newlist([])))
+        #raises(TypeError, api.PyTuple_Size(space.newlist([])))     XXX
         api.PyErr_Clear()
     
     def test_tuple_resize(self, space, api):
-        py_tuple = api.PyTuple_New(3)
+        ref_tup = api.PyTuple_New(3)
         ar = lltype.malloc(PyObjectP.TO, 1, flavor='raw')
-        ar[0] = rffi.cast(PyObject, make_ref(space, py_tuple))
+        ar[0] = rffi.cast(PyObject, ref_tup)
         api._PyTuple_Resize(ar, 2)
-        py_tuple = from_ref(space, ar[0])
-        assert len(py_tuple.wrappeditems) == 2
+        assert ar[0] == rffi.cast(PyObject, ref_tup)
+        # ^^^ our _PyTuple_Resize does not actually need to change the ptr so far
+        assert api.PyTuple_Size(ar[0]) == 2
         
         api._PyTuple_Resize(ar, 10)
-        py_tuple = from_ref(space, ar[0])
-        assert len(py_tuple.wrappeditems) == 10
+        assert api.PyTuple_Size(ar[0]) == 10
         
         api.Py_DecRef(ar[0])
         lltype.free(ar, flavor='raw')
+
+    def test_tuple_setup(self, space, api):
+        ref_tup = api.PyTuple_New(2)
+        ref0 = make_ref(space, space.wrap(123))
+        api.PyTuple_SetItem(ref_tup, 0, ref0)
+        ref1 = make_ref(space, space.wrap(456))
+        api.PyTuple_SetItem(ref_tup, 1, ref1)
+
+        w_tup = from_ref(space, ref_tup)
+        assert space.is_true(space.eq(w_tup, space.wrap((123, 456))))
+        api.Py_DecRef(ref_tup)

Modified: pypy/trunk/pypy/module/cpyext/tupleobject.py
==============================================================================
--- pypy/trunk/pypy/module/cpyext/tupleobject.py	(original)
+++ pypy/trunk/pypy/module/cpyext/tupleobject.py	Tue Nov 30 17:02:26 2010
@@ -1,55 +1,144 @@
 from pypy.interpreter.error import OperationError
 from pypy.rpython.lltypesystem import rffi, lltype
 from pypy.module.cpyext.api import (cpython_api, Py_ssize_t, CANNOT_FAIL,
-                                    build_type_checkers)
+                                    build_type_checkers, PyObjectFields,
+                                    cpython_struct, bootstrap_function)
 from pypy.module.cpyext.pyobject import (PyObject, PyObjectP, Py_DecRef,
-    borrow_from, make_ref, from_ref)
+    borrow_from, make_ref, from_ref, make_typedescr, get_typedescr, Reference,
+    track_reference)
 from pypy.module.cpyext.pyerrors import PyErr_BadInternalCall
-from pypy.objspace.std.tupleobject import W_TupleObject
 
+##
+## Implementation of PyTupleObject
+## ===============================
+##
+## We have the same problem as PyStringObject: a PyTupleObject can be
+## initially used in a read-write way with PyTuple_New(), PyTuple_SetItem()
+## and _PyTuple_Resize().
+##
+## The 'size' and 'items' fields of a PyTupleObject are always valid.
+## Apart from that detail, see the big comment in stringobject.py for
+## more information.
+##
+
+ARRAY_OF_PYOBJ = rffi.CArrayPtr(PyObject)
+PyTupleObjectStruct = lltype.ForwardReference()
+PyTupleObject = lltype.Ptr(PyTupleObjectStruct)
+PyTupleObjectFields = PyObjectFields + \
+    (("items", ARRAY_OF_PYOBJ), ("size", Py_ssize_t))
+cpython_struct("PyTupleObject", PyTupleObjectFields, PyTupleObjectStruct)
+
+ at bootstrap_function
+def init_tupleobject(space):
+    "Type description of PyTupleObject"
+    make_typedescr(space.w_tuple.instancetypedef,
+                   basestruct=PyTupleObject.TO,
+                   attach=tuple_attach,
+                   dealloc=tuple_dealloc,
+                   realize=tuple_realize)
 
 PyTuple_Check, PyTuple_CheckExact = build_type_checkers("Tuple")
 
+def new_empty_tuple(space, length):
+    """
+    Allocate a PyTupleObject and its array, but without a corresponding
+    interpreter object.  The array items may be mutated, until
+    tuple_realize() is called.
+    """
+    typedescr = get_typedescr(space.w_tuple.instancetypedef)
+    py_obj = typedescr.allocate(space, space.w_tuple)
+    py_tup = rffi.cast(PyTupleObject, py_obj)
+
+    py_tup.c_items = lltype.malloc(ARRAY_OF_PYOBJ.TO, length,
+                                   flavor='raw', zero=True)
+    py_tup.c_size = length
+    return py_tup
+
+def tuple_attach(space, py_obj, w_obj):
+    """
+    Fills a newly allocated PyTupleObject with the given tuple object.
+    """
+    items_w = space.fixedview(w_obj)
+    py_tup = rffi.cast(PyTupleObject, py_obj)
+    py_tup.c_items = lltype.nullptr(ARRAY_OF_PYOBJ.TO)
+    py_tup.c_size = len(items_w)
+
+def tuple_realize(space, py_obj):
+    """
+    Creates the tuple in the interpreter. The PyTupleObject items array
+    must not be modified after this call.
+    """
+    py_tup = rffi.cast(PyTupleObject, py_obj)
+    # If your CPython extension creates a self-referential tuple
+    # with PyTuple_SetItem(), you loose.
+    c_items = py_tup.c_items
+    items_w = [from_ref(space, c_items[i]) for i in range(py_tup.c_size)]
+    w_obj = space.newtuple(items_w)
+    track_reference(space, py_obj, w_obj)
+    return w_obj
+
+ at cpython_api([PyObject], lltype.Void, external=False)
+def tuple_dealloc(space, py_obj):
+    """Frees allocated PyTupleObject resources.
+    """
+    py_tup = rffi.cast(PyTupleObject, py_obj)
+    if py_tup.c_items:
+        for i in range(py_tup.c_size):
+            Py_DecRef(space, py_tup.c_items[i])
+        lltype.free(py_tup.c_items, flavor="raw")
+    from pypy.module.cpyext.object import PyObject_dealloc
+    PyObject_dealloc(space, py_obj)
+
+#_______________________________________________________________________
+
 @cpython_api([Py_ssize_t], PyObject)
 def PyTuple_New(space, size):
-    return space.newtuple([space.w_None] * size)
+    return rffi.cast(PyObject, new_empty_tuple(space, size))
 
 @cpython_api([PyObject, Py_ssize_t, PyObject], rffi.INT_real, error=-1)
-def PyTuple_SetItem(space, w_t, pos, w_obj):
-    if not PyTuple_Check(space, w_t):
-        # XXX this should also steal a reference, test it!!!
-        PyErr_BadInternalCall(space)
-    assert isinstance(w_t, W_TupleObject)
-    w_t.wrappeditems[pos] = w_obj
-    Py_DecRef(space, w_obj) # SetItem steals a reference!
+def PyTuple_SetItem(space, ref, pos, ref_item):
+    # XXX steals a reference at the level of PyObjects.  Don't try to
+    # XXX call this function with an interpreter object as ref_item!
+
+    # XXX do PyTuple_Check, without forcing ref as an interpreter object
+    # XXX -- then if it fails it should also steal a reference, test it!!!
+    ref_tup = rffi.cast(PyTupleObject, ref)
+    if not ref_tup.c_items:
+        msg = "PyTuple_SetItem() called on an already-escaped tuple object"
+        raise OperationError(space.w_SystemError, space.wrap(msg))
+    ref_old = ref_tup.c_items[pos]
+    ref_tup.c_items[pos] = ref_item      # SetItem steals a reference!
+    Py_DecRef(space, ref_old)
     return 0
 
 @cpython_api([PyObject, Py_ssize_t], PyObject)
-def PyTuple_GetItem(space, w_t, pos):
-    if not PyTuple_Check(space, w_t):
-        PyErr_BadInternalCall(space)
-    assert isinstance(w_t, W_TupleObject)
-    w_obj = w_t.wrappeditems[pos]
-    return borrow_from(w_t, w_obj)
-
- at cpython_api([PyObject], Py_ssize_t, error=CANNOT_FAIL)
-def PyTuple_GET_SIZE(space, w_t):
-    """Return the size of the tuple p, which must be non-NULL and point to a tuple;
-    no error checking is performed. """
-    assert isinstance(w_t, W_TupleObject)
-    return len(w_t.wrappeditems)
+def PyTuple_GetItem(space, ref, pos):
+    # XXX do PyTuple_Check, without forcing ref as an interpreter object
+    ref_tup = rffi.cast(PyTupleObject, ref)
+    if ref_tup.c_items:
+        return Reference(ref_tup.c_items[pos])     # borrowed reference
+    else:
+        w_t = from_ref(space, ref)
+        w_obj = space.getitem(w_t, space.wrap(pos))
+        return borrow_from(w_t, w_obj)
+
+ at cpython_api([PyObject], Py_ssize_t, error=-1)
+def _PyTuple_Size_Fast(space, ref):
+    # custom version: it's not a macro, so it can be called from other .py
+    # files; but it doesn't include PyTuple_Check()
+    ref_tup = rffi.cast(PyTupleObject, ref)
+    return ref_tup.c_size
 
 @cpython_api([PyObject], Py_ssize_t, error=-1)
 def PyTuple_Size(space, ref):
     """Take a pointer to a tuple object, and return the size of that tuple."""
-    if not PyTuple_Check(space, ref):
-        raise OperationError(space.w_TypeError,
-                             space.wrap("expected tuple object"))
-    return PyTuple_GET_SIZE(space, ref)
+    # XXX do PyTuple_Check, without forcing ref as an interpreter object
+    ref_tup = rffi.cast(PyTupleObject, ref)
+    return ref_tup.c_size
 
 
 @cpython_api([PyObjectP, Py_ssize_t], rffi.INT_real, error=-1)
-def _PyTuple_Resize(space, ref, newsize):
+def _PyTuple_Resize(space, refp, newsize):
     """Can be used to resize a tuple.  newsize will be the new length of the tuple.
     Because tuples are supposed to be immutable, this should only be used if there
     is only one reference to the object.  Do not use this if the tuple may already
@@ -60,18 +149,22 @@
     this function. If the object referenced by *p is replaced, the original
     *p is destroyed.  On failure, returns -1 and sets *p to NULL, and
     raises MemoryError or SystemError."""
-    py_tuple = from_ref(space, ref[0])
-    if not PyTuple_Check(space, py_tuple):
-        PyErr_BadInternalCall(space)
-    assert isinstance(py_tuple, W_TupleObject)
-    py_newtuple = PyTuple_New(space, newsize)
-    
-    to_cp = newsize
-    oldsize = len(py_tuple.wrappeditems)
-    if oldsize < newsize:
-        to_cp = oldsize
-    for i in range(to_cp):
-        py_newtuple.wrappeditems[i] = py_tuple.wrappeditems[i]
-    Py_DecRef(space, ref[0])
-    ref[0] = make_ref(space, py_newtuple)
+    # XXX do PyTuple_Check, without forcing ref as an interpreter object
+    # XXX -- then if it fails it should reset refp[0] to null
+    ref_tup = rffi.cast(PyTupleObject, refp[0])
+    c_newitems = lltype.malloc(ARRAY_OF_PYOBJ.TO, newsize,
+                               flavor='raw', zero=True)
+    c_olditems = ref_tup.c_items
+    if not c_olditems:
+        msg = "_PyTuple_Resize() called on an already-escaped tuple object"
+        raise OperationError(space.w_SystemError, space.wrap(msg))
+    oldsize = ref_tup.c_size
+    for i in range(min(oldsize, newsize)):
+        c_newitems[i] = c_olditems[i]
+    # decref items deleted by shrinkage
+    for i in range(newsize, oldsize):
+        Py_DecRef(space, c_olditems[i])
+    ref_tup.c_items = c_newitems
+    ref_tup.c_size = newsize
+    lltype.free(c_olditems, flavor='raw')
     return 0

Modified: pypy/trunk/pypy/module/pypyjit/test/test_pypy_c.py
==============================================================================
--- pypy/trunk/pypy/module/pypyjit/test/test_pypy_c.py	(original)
+++ pypy/trunk/pypy/module/pypyjit/test/test_pypy_c.py	Tue Nov 30 17:02:26 2010
@@ -695,11 +695,9 @@
                 i = t2[3]
                 del t2
             return i
-        ''', 100, ([], 100))
+        ''', 40, ([], 100))
         bytecode, = self.get_by_bytecode('BINARY_SUBSCR')
-        assert len(bytecode.get_opnames('new_array')) == 1
-        # XXX I would like here to say that it's 0, but unfortunately
-        #     call that can raise is not exchanged into getarrayitem_gc
+        assert len(bytecode.get_opnames('new_array')) == 0
 
     def test_overflow_checking(self):
         startvalue = sys.maxint - 2147483647

Modified: pypy/trunk/pypy/objspace/std/objspace.py
==============================================================================
--- pypy/trunk/pypy/objspace/std/objspace.py	(original)
+++ pypy/trunk/pypy/objspace/std/objspace.py	Tue Nov 30 17:02:26 2010
@@ -376,7 +376,7 @@
                     self, w_obj, expected_length)[:])
         if expected_length != -1 and len(t) != expected_length:
             raise self._wrap_expected_length(expected_length, len(t))
-        return t
+        return make_sure_not_resized(t)
 
     def fixedview_unroll(self, w_obj, expected_length=-1):
         return self.fixedview(w_obj, expected_length, unroll=True)

Modified: pypy/trunk/pypy/rlib/debug.py
==============================================================================
--- pypy/trunk/pypy/rlib/debug.py	(original)
+++ pypy/trunk/pypy/rlib/debug.py	Tue Nov 30 17:02:26 2010
@@ -226,31 +226,6 @@
         hop.exception_cannot_occur()
         return hop.inputarg(hop.args_r[0], arg=0)
 
-def make_sure_not_modified(arg):
-    """ Function checking whether annotation of SomeList is never resized
-    and never modified, useful for debugging. Does nothing when run directly
-    """
-    return arg
-
-class Entry(ExtRegistryEntry):
-    _about_ = make_sure_not_modified
-
-    def compute_result_annotation(self, s_arg):
-        from pypy.annotation.model import SomeList
-        assert isinstance(s_arg, SomeList)
-        # the logic behind it is that we try not to propagate
-        # make_sure_not_resized, when list comprehension is not on
-        if self.bookkeeper.annotator.translator.config.translation.list_comprehension_operations:
-            s_arg.listdef.never_mutate()
-        else:
-            from pypy.annotation.annrpython import log
-            log.WARNING('make_sure_not_modified called, but has no effect since list_comprehension is off')
-        return s_arg
-    
-    def specialize_call(self, hop):
-        hop.exception_cannot_occur()
-        return hop.inputarg(hop.args_r[0], arg=0)
-
 
 class IntegerCanBeNegative(Exception):
     pass

Modified: pypy/trunk/pypy/rlib/rsre/rsre_core.py
==============================================================================
--- pypy/trunk/pypy/rlib/rsre/rsre_core.py	(original)
+++ pypy/trunk/pypy/rlib/rsre/rsre_core.py	Tue Nov 30 17:02:26 2010
@@ -1,5 +1,5 @@
 import sys
-from pypy.rlib.debug import check_nonneg, make_sure_not_modified
+from pypy.rlib.debug import check_nonneg
 from pypy.rlib.unroll import unrolling_iterable
 from pypy.rlib.rsre import rsre_char
 from pypy.tool.sourcetools import func_with_new_name
@@ -471,7 +471,6 @@
     while True:
         op = ctx.pat(ppos)
         ppos += 1
-        make_sure_not_modified(ctx.pattern)
 
         #jit.jit_debug("sre_match", op, ppos, ptr)
         #

Modified: pypy/trunk/pypy/rlib/rsre/test/test_zjit.py
==============================================================================
--- pypy/trunk/pypy/rlib/rsre/test/test_zjit.py	(original)
+++ pypy/trunk/pypy/rlib/rsre/test/test_zjit.py	Tue Nov 30 17:02:26 2010
@@ -1,6 +1,5 @@
 from pypy.jit.metainterp.test import test_basic
 from pypy.rlib.nonconst import NonConstant
-from pypy.rlib.debug import make_sure_not_modified
 from pypy.rlib.rsre.test.test_match import get_code
 from pypy.rlib.rsre import rsre_core
 from pypy.rpython.lltypesystem import lltype

Modified: pypy/trunk/pypy/rlib/test/test_debug.py
==============================================================================
--- pypy/trunk/pypy/rlib/test/test_debug.py	(original)
+++ pypy/trunk/pypy/rlib/test/test_debug.py	Tue Nov 30 17:02:26 2010
@@ -42,14 +42,14 @@
     py.test.raises(IntegerCanBeNegative, interpret, g, [9])
 
 def test_make_sure_not_resized():
-    from pypy.annotation.listdef import TooLateForChange
+    from pypy.annotation.listdef import ListChangeUnallowed
     def f():
         result = [1,2,3]
         make_sure_not_resized(result)
         result.append(4)
         return len(result)
 
-    py.test.raises(TooLateForChange, interpret, f, [], 
+    py.test.raises(ListChangeUnallowed, interpret, f, [], 
                    list_comprehension_operations=True)
 
 

Modified: pypy/trunk/pypy/rpython/rlist.py
==============================================================================
--- pypy/trunk/pypy/rpython/rlist.py	(original)
+++ pypy/trunk/pypy/rpython/rlist.py	Tue Nov 30 17:02:26 2010
@@ -9,7 +9,7 @@
 from pypy.rpython import robject
 from pypy.rlib.objectmodel import malloc_zero_filled
 from pypy.rlib.debug import ll_assert
-from pypy.rlib.rarithmetic import ovfcheck, widen
+from pypy.rlib.rarithmetic import ovfcheck, widen, r_uint, intmask
 from pypy.rpython.annlowlevel import ADTInterface
 from pypy.rlib import rgc
 
@@ -241,17 +241,22 @@
 class __extend__(pairtype(AbstractBaseListRepr, IntegerRepr)):
 
     def rtype_getitem((r_lst, r_int), hop, checkidx=False):
+        v_lst, v_index = hop.inputargs(r_lst, Signed)
         if checkidx:
-            spec = dum_checkidx
+            hop.exception_is_here()
         else:
-            spec = dum_nocheck
-        v_func = hop.inputconst(Void, spec)
-        v_lst, v_index = hop.inputargs(r_lst, Signed)
+            hop.exception_cannot_occur()
         if hop.args_s[0].listdef.listitem.mutated or checkidx:
             if hop.args_s[1].nonneg:
                 llfn = ll_getitem_nonneg
             else:
                 llfn = ll_getitem
+            if checkidx:
+                spec = dum_checkidx
+            else:
+                spec = dum_nocheck
+            c_func_marker = hop.inputconst(Void, spec)
+            v_res = hop.gendirectcall(llfn, c_func_marker, v_lst, v_index)
         else:
             # this is the 'foldable' version, which is not used when
             # we check for IndexError
@@ -259,11 +264,7 @@
                 llfn = ll_getitem_foldable_nonneg
             else:
                 llfn = ll_getitem_foldable
-        if checkidx:
-            hop.exception_is_here()
-        else:
-            hop.exception_cannot_occur()
-        v_res = hop.gendirectcall(llfn, v_func, v_lst, v_index)
+            v_res = hop.gendirectcall(llfn, v_lst, v_index)
         return r_lst.recast(hop.llops, v_res)
 
     rtype_getitem_key = rtype_getitem
@@ -538,12 +539,14 @@
             dest.ll_setitem_fast(dest_start + i, item)
             i += 1
 ll_arraycopy._annenforceargs_ = [None, None, int, int, int]
+# no oopspec -- the function is inlined by the JIT
 
 def ll_copy(RESLIST, l):
     length = l.ll_length()
     new_lst = RESLIST.ll_newlist(length)
     ll_arraycopy(l, new_lst, 0, 0, length)
     return new_lst
+# no oopspec -- the function is inlined by the JIT
 
 def ll_len(l):
     return l.ll_length()
@@ -551,6 +554,7 @@
 def ll_list_is_true(l):
     # check if a list is True, allowing for None
     return bool(l) and l.ll_length() != 0
+# no oopspec -- the function is inlined by the JIT
 
 def ll_len_foldable(l):
     return l.ll_length()
@@ -558,6 +562,7 @@
 
 def ll_list_is_true_foldable(l):
     return bool(l) and ll_len_foldable(l) != 0
+# no oopspec -- the function is inlined by the JIT
 
 def ll_append(l, newitem):
     length = l.ll_length()
@@ -588,6 +593,7 @@
     ll_arraycopy(l1, l, 0, 0, len1)
     ll_arraycopy(l2, l, 0, len1, len2)
     return l
+# no oopspec -- the function is inlined by the JIT
 
 def ll_insert_nonneg(l, index, newitem):
     length = l.ll_length()
@@ -674,60 +680,72 @@
         l.ll_setitem_fast(length_1_i, tmp)
         i += 1
         length_1_i -= 1
+ll_reverse.oopspec = 'list.reverse(l)'
 
 def ll_getitem_nonneg(func, l, index):
     ll_assert(index >= 0, "unexpectedly negative list getitem index")
     if func is dum_checkidx:
         if index >= l.ll_length():
             raise IndexError
-    else:
-        ll_assert(index < l.ll_length(), "list getitem index out of bound")
     return l.ll_getitem_fast(index)
-ll_getitem_nonneg.oopspec = 'list.getitem(l, index)'
+ll_getitem_nonneg._always_inline_ = True
+# no oopspec -- the function is inlined by the JIT
 
 def ll_getitem(func, l, index):
-    length = l.ll_length()
-    if index < 0:
-        index += length
     if func is dum_checkidx:
-        if index < 0 or index >= length:
-            raise IndexError
+        length = l.ll_length()    # common case: 0 <= index < length
+        if r_uint(index) >= r_uint(length):
+            # Failed, so either (-length <= index < 0), or we have to raise
+            # IndexError.  First add 'length' to get the final index, then
+            # check that we now have (0 <= index < length).
+            index = r_uint(index) + r_uint(length)
+            if index >= r_uint(length):
+                raise IndexError
+            index = intmask(index)
     else:
-        ll_assert(index >= 0, "negative list getitem index out of bound")
-        ll_assert(index < length, "list getitem index out of bound")
+        # We don't want checking, but still want to support index < 0.
+        # Only call ll_length() if needed.
+        if index < 0:
+            index += l.ll_length()
+            ll_assert(index >= 0, "negative list getitem index out of bound")
     return l.ll_getitem_fast(index)
-ll_getitem.oopspec = 'list.getitem(l, index)'
+# no oopspec -- the function is inlined by the JIT
 
-def ll_getitem_foldable_nonneg(func, l, index):
-    return ll_getitem_nonneg(func, l, index)
+def ll_getitem_foldable_nonneg(l, index):
+    ll_assert(index >= 0, "unexpectedly negative list getitem index")
+    return l.ll_getitem_fast(index)
 ll_getitem_foldable_nonneg.oopspec = 'list.getitem_foldable(l, index)'
 
-def ll_getitem_foldable(func, l, index):
-    return ll_getitem(func, l, index)
-ll_getitem_foldable.oopspec = 'list.getitem_foldable(l, index)'
+def ll_getitem_foldable(l, index):
+    if index < 0:
+        index += l.ll_length()
+    return ll_getitem_foldable_nonneg(l, index)
+ll_getitem_foldable._always_inline_ = True
+# no oopspec -- the function is inlined by the JIT
 
 def ll_setitem_nonneg(func, l, index, newitem):
     ll_assert(index >= 0, "unexpectedly negative list setitem index")
     if func is dum_checkidx:
         if index >= l.ll_length():
             raise IndexError
-    else:
-        ll_assert(index < l.ll_length(), "list setitem index out of bound")
     l.ll_setitem_fast(index, newitem)
-ll_setitem_nonneg.oopspec = 'list.setitem(l, index, newitem)'
+ll_setitem_nonneg._always_inline_ = True
+# no oopspec -- the function is inlined by the JIT
 
 def ll_setitem(func, l, index, newitem):
-    length = l.ll_length()
-    if index < 0:
-        index += length
     if func is dum_checkidx:
-        if index < 0 or index >= length:
-            raise IndexError
+        length = l.ll_length()
+        if r_uint(index) >= r_uint(length):   # see comments in ll_getitem().
+            index = r_uint(index) + r_uint(length)
+            if index >= r_uint(length):
+                raise IndexError
+            index = intmask(index)
     else:
-        ll_assert(index >= 0, "negative list setitem index out of bound")
-        ll_assert(index < length, "list setitem index out of bound")
+        if index < 0:
+            index += l.ll_length()
+            ll_assert(index >= 0, "negative list setitem index out of bound")
     l.ll_setitem_fast(index, newitem)
-ll_setitem.oopspec = 'list.setitem(l, index, newitem)'
+# no oopspec -- the function is inlined by the JIT
 
 def ll_delitem_nonneg(func, l, index):
     ll_assert(index >= 0, "unexpectedly negative list delitem index")
@@ -751,19 +769,20 @@
     l._ll_resize_le(newlength)
 ll_delitem_nonneg.oopspec = 'list.delitem(l, index)'
 
-def ll_delitem(func, l, i):
-    length = l.ll_length()
-    if i < 0:
-        i += length
+def ll_delitem(func, l, index):
     if func is dum_checkidx:
-        if i < 0 or i >= length:
-            raise IndexError
+        length = l.ll_length()
+        if r_uint(index) >= r_uint(length):   # see comments in ll_getitem().
+            index = r_uint(index) + r_uint(length)
+            if index >= r_uint(length):
+                raise IndexError
+            index = intmask(index)
     else:
-        ll_assert(i >= 0, "negative list delitem index out of bound")
-        ll_assert(i < length, "list delitem index out of bound")
-    ll_delitem_nonneg(dum_nocheck, l, i)
-ll_delitem.oopspec = 'list.delitem(l, i)'
-
+        if index < 0:
+            index += l.ll_length()
+            ll_assert(index >= 0, "negative list delitem index out of bound")
+    ll_delitem_nonneg(dum_nocheck, l, index)
+# no oopspec -- the function is inlined by the JIT
 
 def ll_extend(l1, l2):
     len1 = l1.ll_length()
@@ -799,6 +818,7 @@
         lst.ll_setitem_fast(j, c)
         i += 1
         j += 1
+# not inlined by the JIT -- contains a loop
 
 def ll_extend_with_str_slice_startstop(lst, s, getstrlen, getstritem,
                                        start, stop):
@@ -824,6 +844,7 @@
         lst.ll_setitem_fast(j, c)
         i += 1
         j += 1
+# not inlined by the JIT -- contains a loop
 
 def ll_extend_with_str_slice_minusone(lst, s, getstrlen, getstritem):
     len1 = lst.ll_length()
@@ -843,6 +864,7 @@
         lst.ll_setitem_fast(j, c)
         i += 1
         j += 1
+# not inlined by the JIT -- contains a loop
 
 def ll_extend_with_char_count(lst, char, count):
     if count <= 0:
@@ -859,6 +881,7 @@
     while j < newlength:
         lst.ll_setitem_fast(j, char)
         j += 1
+# not inlined by the JIT -- contains a loop
 
 def ll_listslice_startonly(RESLIST, l1, start):
     len1 = l1.ll_length()
@@ -869,6 +892,7 @@
     ll_arraycopy(l1, l, start, 0, newlength)
     return l
 ll_listslice_startonly._annenforceargs_ = (None, None, int)
+# no oopspec -- the function is inlined by the JIT
 
 def ll_listslice_startstop(RESLIST, l1, start, stop):
     length = l1.ll_length()
@@ -881,6 +905,7 @@
     l = RESLIST.ll_newlist(newlength)
     ll_arraycopy(l1, l, start, 0, newlength)
     return l
+# no oopspec -- the function is inlined by the JIT
 
 def ll_listslice_minusone(RESLIST, l1):
     newlength = l1.ll_length() - 1
@@ -888,6 +913,7 @@
     l = RESLIST.ll_newlist(newlength)
     ll_arraycopy(l1, l, 0, 0, newlength)
     return l
+# no oopspec -- the function is inlined by the JIT
 
 def ll_listdelslice_startonly(l, start):
     ll_assert(start >= 0, "del l[start:] with unexpectedly negative start")
@@ -958,6 +984,7 @@
                 return False
         j += 1
     return True
+# not inlined by the JIT -- contains a loop
 
 def ll_listcontains(lst, obj, eqfn):
     lng = lst.ll_length()
@@ -971,6 +998,7 @@
                 return True
         j += 1
     return False
+# not inlined by the JIT -- contains a loop
 
 def ll_listindex(lst, obj, eqfn):
     lng = lst.ll_length()
@@ -984,6 +1012,7 @@
                 return j
         j += 1
     raise ValueError # can't say 'list.index(x): x not in list'
+# not inlined by the JIT -- contains a loop
 
 def ll_listremove(lst, obj, eqfn):
     index = ll_listindex(lst, obj, eqfn) # raises ValueError if obj not in lst
@@ -1030,3 +1059,4 @@
             i += 1
         j += length
     return res
+# not inlined by the JIT -- contains a loop

Modified: pypy/trunk/pypy/rpython/test/test_rlist.py
==============================================================================
--- pypy/trunk/pypy/rpython/test/test_rlist.py	(original)
+++ pypy/trunk/pypy/rpython/test/test_rlist.py	Tue Nov 30 17:02:26 2010
@@ -12,6 +12,7 @@
 from pypy.rpython.rint import signed_repr
 from pypy.objspace.flow.model import Constant, Variable
 from pypy.rpython.test.tool import BaseRtypingTest, LLRtypeMixin, OORtypeMixin
+from pypy.rlib.debug import ll_assert
 
 # undo the specialization parameter
 for n1 in 'get set del'.split():
@@ -1076,7 +1077,13 @@
 
         res = self.interpret(f, [0])
         assert res == 1
-        py.test.raises(AssertionError, self.interpret, f, [1])
+        if self.type_system == 'lltype':
+            # on lltype we always get an AssertionError
+            py.test.raises(AssertionError, self.interpret, f, [1])
+        else:
+            # on ootype we happen to get through the ll_asserts and to
+            # hit the IndexError from ootype.py
+            self.interpret_raises(IndexError, f, [1])
 
         def f(x):
             l = [1]
@@ -1121,12 +1128,13 @@
 
         res = self.interpret(f, [0])
         assert res == 1
-        try:
-            self.interpret_raises(IndexError, f, [1])
-        except (AssertionError,), e:
-            pass
+        if self.type_system == 'lltype':
+            # on lltype we always get an AssertionError
+            py.test.raises(AssertionError, self.interpret, f, [1])
         else:
-            assert False
+            # on ootype we happen to get through the ll_asserts and to
+            # hit the IndexError from ootype.py
+            self.interpret_raises(IndexError, f, [1])
 
         def f(x):
             l = [1]
@@ -1163,12 +1171,13 @@
 
         res = self.interpret(f, [0])
         assert res == 1
-        try:
-            self.interpret_raises(IndexError, f, [1])
-        except (AssertionError,), e:
-            pass
+        if self.type_system == 'lltype':
+            # on lltype we always get an AssertionError
+            py.test.raises(AssertionError, self.interpret, f, [1])
         else:
-            assert False
+            # on ootype we happen to get through the ll_asserts and to
+            # hit the IndexError from ootype.py
+            self.interpret_raises(IndexError, f, [1])
 
     def test_charlist_extension_1(self):
         def f(n):
@@ -1327,8 +1336,32 @@
         res = self.interpret(f, [2])
         assert res == True
 
+    def test_immutable_list_out_of_instance(self):
+        from pypy.translator.simplify import get_funcobj
+        for immutable_fields in (["a", "b"], ["a", "b", "y[*]"]):
+            class A(object):
+                _immutable_fields_ = immutable_fields
+            class B(A):
+                pass
+            def f(i):
+                b = B()
+                lst = [i]
+                lst[0] += 1
+                b.y = lst
+                ll_assert(b.y is lst, "copying when reading out the attr?")
+                return b.y[0]
+            res = self.interpret(f, [10])
+            assert res == 11
+            t, rtyper, graph = self.gengraph(f, [int])
+            block = graph.startblock
+            op = block.operations[-1]
+            assert op.opname == 'direct_call'
+            func = get_funcobj(op.args[0].value)._callable
+            assert ('foldable' in func.func_name) == \
+                   ("y[*]" in immutable_fields)
 
 class TestLLtype(BaseTestRlist, LLRtypeMixin):
+    type_system = 'lltype'
     rlist = ll_rlist
 
     def test_memoryerror(self):
@@ -1420,14 +1453,16 @@
             lst2 = [i]
             lst2.append(42)    # mutated list
             return lst1[i] + lst2[i]
-        _, _, graph = self.gengraph(f, [int])
+        from pypy.annotation import model as annmodel
+        _, _, graph = self.gengraph(f, [annmodel.SomeInteger(nonneg=True)])
         block = graph.startblock
         lst1_getitem_op = block.operations[-3]     # XXX graph fishing
         lst2_getitem_op = block.operations[-2]
         func1 = lst1_getitem_op.args[0].value._obj._callable
         func2 = lst2_getitem_op.args[0].value._obj._callable
         assert func1.oopspec == 'list.getitem_foldable(l, index)'
-        assert func2.oopspec == 'list.getitem(l, index)'
+        assert not hasattr(func2, 'oopspec')
 
 class TestOOtype(BaseTestRlist, OORtypeMixin):
     rlist = oo_rlist
+    type_system = 'ootype'



More information about the Pypy-commit mailing list