[pypy-svn] r76857 - in pypy/trunk/pypy: jit/codewriter objspace/std rpython rpython/lltypesystem rpython/lltypesystem/test rpython/memory/test rpython/ootypesystem rpython/test translator/backendopt/test
arigo at codespeak.net
arigo at codespeak.net
Fri Sep 3 17:39:50 CEST 2010
Author: arigo
Date: Fri Sep 3 17:39:47 2010
New Revision: 76857
Modified:
pypy/trunk/pypy/jit/codewriter/jtransform.py
pypy/trunk/pypy/objspace/std/tupleobject.py
pypy/trunk/pypy/rpython/lltypesystem/lloperation.py
pypy/trunk/pypy/rpython/lltypesystem/lltype.py
pypy/trunk/pypy/rpython/lltypesystem/opimpl.py
pypy/trunk/pypy/rpython/lltypesystem/test/test_lloperation.py
pypy/trunk/pypy/rpython/lltypesystem/test/test_lltype.py
pypy/trunk/pypy/rpython/memory/test/test_gctypelayout.py
pypy/trunk/pypy/rpython/ootypesystem/ootype.py
pypy/trunk/pypy/rpython/ootypesystem/rclass.py
pypy/trunk/pypy/rpython/rclass.py
pypy/trunk/pypy/rpython/test/test_rclass.py
pypy/trunk/pypy/translator/backendopt/test/test_constfold.py
Log:
Merge branch/no-_immutable_.
Unlike hinted by the name of the branch, this does not kill the
_immutable_ hint; it just makes it saner (and _immutable_fields_ too).
Now, the definition in pure Python terms is precisely that an instance x
is fully immutable if and only if hasattr(x, '_immutable_').
During translation, it is now an error (reported by rpython/rclass.py)
if we find the _immutable_ hint on a class but, on some parent class, we
have non-trivial non-read-only attributes.
Similarly, it is an error to list a field name in _immutable_fields_ if
that field ends up on some parent class.
However, it is ok to give a field name that is not actually present in
this class. If/when it shows up on some subclass, it will be flagged as
read-only there. The idea is again that if we say
_immutable_fields_=['x'] on some class A, then any attribute 'x' on any
instance of (a subclass of) A will be read-only.
Modified: pypy/trunk/pypy/jit/codewriter/jtransform.py
==============================================================================
--- pypy/trunk/pypy/jit/codewriter/jtransform.py (original)
+++ pypy/trunk/pypy/jit/codewriter/jtransform.py Fri Sep 3 17:39:47 2010
@@ -511,14 +511,11 @@
arraydescr)
return []
# check for deepfrozen structures that force constant-folding
- hints = v_inst.concretetype.TO._hints
- accessor = hints.get("immutable_fields")
- if accessor and c_fieldname.value in accessor.fields:
+ immut = v_inst.concretetype.TO._immutable_field(c_fieldname.value)
+ if immut:
pure = '_pure'
- if accessor.fields[c_fieldname.value] == "[*]":
+ if immut == "[*]":
self.immutable_arrays[op.result] = True
- elif hints.get('immutable'):
- pure = '_pure'
else:
pure = ''
argname = getattr(v_inst.concretetype.TO, '_gckind', 'gc')
Modified: pypy/trunk/pypy/objspace/std/tupleobject.py
==============================================================================
--- pypy/trunk/pypy/objspace/std/tupleobject.py (original)
+++ pypy/trunk/pypy/objspace/std/tupleobject.py Fri Sep 3 17:39:47 2010
@@ -10,7 +10,7 @@
class W_TupleObject(W_Object):
from pypy.objspace.std.tupletype import tuple_typedef as typedef
- _immutable_ = True
+ _immutable_fields_ = ['wrappeditems[*]']
def __init__(w_self, wrappeditems):
make_sure_not_resized(wrappeditems)
Modified: pypy/trunk/pypy/rpython/lltypesystem/lloperation.py
==============================================================================
--- pypy/trunk/pypy/rpython/lltypesystem/lloperation.py (original)
+++ pypy/trunk/pypy/rpython/lltypesystem/lloperation.py Fri Sep 3 17:39:47 2010
@@ -85,16 +85,20 @@
fold = roproperty(get_fold_impl)
def is_pure(self, args_v):
- return (self.canfold or # canfold => pure operation
- self is llop.debug_assert or # debug_assert is pure enough
- # reading from immutable
- (self in (llop.getfield, llop.getarrayitem) and
- args_v[0].concretetype.TO._hints.get('immutable')) or
- (self is llop.getfield and # reading from immutable_field
- 'immutable_fields' in args_v[0].concretetype.TO._hints and
- args_v[1].value in args_v[0].concretetype.TO
- ._hints['immutable_fields'].fields))
- # XXX: what about ootype immutable arrays?
+ if self.canfold: # canfold => pure operation
+ return True
+ if self is llop.debug_assert: # debug_assert is pure enough
+ return True
+ # reading from immutable (lltype)
+ if self is llop.getfield or self is llop.getarrayitem:
+ field = getattr(args_v[1], 'value', None)
+ return args_v[0].concretetype.TO._immutable_field(field)
+ # reading from immutable (ootype) (xxx what about arrays?)
+ if self is llop.oogetfield:
+ field = getattr(args_v[1], 'value', None)
+ return args_v[0].concretetype._immutable_field(field)
+ # default
+ return False
def __repr__(self):
return '<LLOp %s>' % (getattr(self, 'opname', '?'),)
Modified: pypy/trunk/pypy/rpython/lltypesystem/lltype.py
==============================================================================
--- pypy/trunk/pypy/rpython/lltypesystem/lltype.py (original)
+++ pypy/trunk/pypy/rpython/lltypesystem/lltype.py Fri Sep 3 17:39:47 2010
@@ -297,6 +297,15 @@
n = 1
return _struct(self, n, initialization='example')
+ def _immutable_field(self, field):
+ if 'immutable_fields' in self._hints:
+ try:
+ s = self._hints['immutable_fields'].fields[field]
+ return s or True
+ except KeyError:
+ pass
+ return self._hints.get('immutable', False)
+
class RttiStruct(Struct):
_runtime_type_info = None
@@ -391,6 +400,9 @@
def _container_example(self):
return _array(self, 1, initialization='example')
+ def _immutable_field(self, index=None):
+ return self._hints.get('immutable', False)
+
class GcArray(Array):
_gckind = 'gc'
def _inline_is_varsize(self, last):
Modified: pypy/trunk/pypy/rpython/lltypesystem/opimpl.py
==============================================================================
--- pypy/trunk/pypy/rpython/lltypesystem/opimpl.py (original)
+++ pypy/trunk/pypy/rpython/lltypesystem/opimpl.py Fri Sep 3 17:39:47 2010
@@ -150,12 +150,7 @@
# we can constant-fold this if the innermost structure from which we
# read the final field is immutable.
T = lltype.typeOf(innermostcontainer).TO
- if T._hints.get('immutable'):
- pass
- elif ('immutable_fields' in T._hints and
- offsets[-1] in T._hints['immutable_fields'].fields):
- pass
- else:
+ if not T._immutable_field(offsets[-1]):
raise TypeError("cannot fold getinteriorfield on mutable struct")
assert not isinstance(ob, lltype._interior_ptr)
return ob
@@ -437,19 +432,15 @@
def op_getfield(p, name):
checkptr(p)
TYPE = lltype.typeOf(p).TO
- if TYPE._hints.get('immutable'):
- pass
- elif ('immutable_fields' in TYPE._hints and
- name in TYPE._hints['immutable_fields'].fields):
- pass
- else:
+ if not TYPE._immutable_field(name):
raise TypeError("cannot fold getfield on mutable struct")
return getattr(p, name)
def op_getarrayitem(p, index):
checkptr(p)
- if not lltype.typeOf(p).TO._hints.get('immutable'):
- raise TypeError("cannot fold getfield on mutable array")
+ ARRAY = lltype.typeOf(p).TO
+ if not ARRAY._immutable_field(index):
+ raise TypeError("cannot fold getarrayitem on mutable array")
return p[index]
def _normalize(x):
Modified: pypy/trunk/pypy/rpython/lltypesystem/test/test_lloperation.py
==============================================================================
--- pypy/trunk/pypy/rpython/lltypesystem/test/test_lloperation.py (original)
+++ pypy/trunk/pypy/rpython/lltypesystem/test/test_lloperation.py Fri Sep 3 17:39:47 2010
@@ -88,7 +88,7 @@
accessor = rclass.FieldListAccessor()
S3 = lltype.GcStruct('S', ('x', lltype.Signed), ('y', lltype.Signed),
hints={'immutable_fields': accessor})
- accessor.initialize(S3, ['x'])
+ accessor.initialize(S3, {'x': ''})
v_s3 = Variable()
v_s3.concretetype = lltype.Ptr(S3)
assert not llop.setfield.is_pure([v_s3, Constant('x'), Variable()])
@@ -103,7 +103,7 @@
accessor = rclass.FieldListAccessor()
S3 = lltype.GcStruct('S', ('x', lltype.Signed), ('y', lltype.Signed),
hints={'immutable_fields': accessor})
- accessor.initialize(S3, ['x'])
+ accessor.initialize(S3, {'x': ''})
#
s1 = lltype.malloc(S1); s1.x = 45
py.test.raises(TypeError, llop.getfield, lltype.Signed, s1, 'x')
Modified: pypy/trunk/pypy/rpython/lltypesystem/test/test_lltype.py
==============================================================================
--- pypy/trunk/pypy/rpython/lltypesystem/test/test_lltype.py (original)
+++ pypy/trunk/pypy/rpython/lltypesystem/test/test_lltype.py Fri Sep 3 17:39:47 2010
@@ -781,6 +781,28 @@
p = cast_opaque_ptr(llmemory.GCREF, a)
assert hash1 == identityhash(p)
+def test_immutable_hint():
+ S = GcStruct('S', ('x', lltype.Signed))
+ assert S._immutable_field('x') == False
+ #
+ S = GcStruct('S', ('x', lltype.Signed), hints={'immutable': True})
+ assert S._immutable_field('x') == True
+ #
+ class FieldListAccessor(object):
+ def __init__(self, fields):
+ self.fields = fields
+ S = GcStruct('S', ('x', lltype.Signed),
+ hints={'immutable_fields': FieldListAccessor({'x':''})})
+ assert S._immutable_field('x') == True
+ #
+ class FieldListAccessor(object):
+ def __init__(self, fields):
+ self.fields = fields
+ S = GcStruct('S', ('x', lltype.Signed),
+ hints={'immutable_fields': FieldListAccessor({'x':'[*]'})})
+ assert S._immutable_field('x') == '[*]'
+
+
class TestTrackAllocation:
def setup_method(self, func):
start_tracking_allocations()
Modified: pypy/trunk/pypy/rpython/memory/test/test_gctypelayout.py
==============================================================================
--- pypy/trunk/pypy/rpython/memory/test/test_gctypelayout.py (original)
+++ pypy/trunk/pypy/rpython/memory/test/test_gctypelayout.py Fri Sep 3 17:39:47 2010
@@ -101,7 +101,7 @@
accessor = rclass.FieldListAccessor()
S3 = lltype.GcStruct('S', ('x', PT), ('y', PT),
hints={'immutable_fields': accessor})
- accessor.initialize(S3, ['x'])
+ accessor.initialize(S3, {'x': ''})
#
s1 = lltype.malloc(S1)
adr = llmemory.cast_ptr_to_adr(s1)
Modified: pypy/trunk/pypy/rpython/ootypesystem/ootype.py
==============================================================================
--- pypy/trunk/pypy/rpython/ootypesystem/ootype.py (original)
+++ pypy/trunk/pypy/rpython/ootypesystem/ootype.py Fri Sep 3 17:39:47 2010
@@ -267,6 +267,14 @@
return self._fields_with_default[:]
return self._superclass._get_fields_with_default() + self._fields_with_default
+ def _immutable_field(self, field):
+ if 'immutable_fields' in self._hints:
+ try:
+ s = self._hints['immutable_fields'].fields[field]
+ return s or True
+ except KeyError:
+ pass
+ return self._hints.get('immutable', False)
class SpecializableType(OOType):
Modified: pypy/trunk/pypy/rpython/ootypesystem/rclass.py
==============================================================================
--- pypy/trunk/pypy/rpython/ootypesystem/rclass.py (original)
+++ pypy/trunk/pypy/rpython/ootypesystem/rclass.py Fri Sep 3 17:39:47 2010
@@ -194,6 +194,7 @@
self.lowleveltype._hints.update(hints)
if self.classdef is None:
+ self.fields = {}
self.allfields = {}
self.allmethods = {}
self.allclassattributes = {}
@@ -210,6 +211,7 @@
allclassattributes = {}
fields = {}
+ nonmangledfields = []
fielddefaults = {}
if llfields:
@@ -224,6 +226,7 @@
allfields[mangled] = repr
oot = repr.lowleveltype
fields[mangled] = oot
+ nonmangledfields.append(name)
try:
value = self.classdef.classdesc.read_attribute(name)
fielddefaults[mangled] = repr.convert_desc_or_const(value)
@@ -294,6 +297,7 @@
if not attrdef.s_value.is_constant():
classattributes[mangled] = attrdef.s_value, value
+ self.fields = nonmangledfields
self.allfields = allfields
self.allmethods = allmethods
self.allclassattributes = allclassattributes
Modified: pypy/trunk/pypy/rpython/rclass.py
==============================================================================
--- pypy/trunk/pypy/rpython/rclass.py (original)
+++ pypy/trunk/pypy/rpython/rclass.py Fri Sep 3 17:39:47 2010
@@ -9,6 +9,7 @@
class FieldListAccessor(object):
def initialize(self, TYPE, fields):
+ assert type(fields) is dict
self.TYPE = TYPE
self.fields = fields
@@ -18,6 +19,10 @@
def _freeze_(self):
return True
+class ImmutableConflictError(Exception):
+ """Raised when the _immutable_ or _immutable_fields_ hints are
+ not consistent across a class hierarchy."""
+
def getclassrepr(rtyper, classdef):
try:
@@ -153,7 +158,7 @@
pass
def _check_for_immutable_hints(self, hints):
- if '_immutable_' in self.classdef.classdesc.classdict:
+ if self.classdef.classdesc.lookup('_immutable_') is not None:
hints = hints.copy()
hints['immutable'] = True
self.immutable_field_list = [] # unless overwritten below
@@ -182,16 +187,20 @@
return 'InstanceR %s' % (clsname,)
def _setup_repr_final(self):
+ self._setup_immutable_field_list()
+ self._check_for_immutable_conflicts()
+
+ def _setup_immutable_field_list(self):
hints = self.object_type._hints
if "immutable_fields" in hints:
accessor = hints["immutable_fields"]
- immutable_fields = {}
- rbase = self
- while rbase.classdef is not None:
- immutable_fields.update(
- dict.fromkeys(rbase.immutable_field_list))
- rbase = rbase.rbase
- self._parse_field_list(immutable_fields, accessor)
+ if not hasattr(accessor, 'fields'):
+ immutable_fields = []
+ rbase = self
+ while rbase.classdef is not None:
+ immutable_fields += rbase.immutable_field_list
+ rbase = rbase.rbase
+ self._parse_field_list(immutable_fields, accessor)
def _parse_field_list(self, fields, accessor):
with_suffix = {}
@@ -209,6 +218,36 @@
accessor.initialize(self.object_type, with_suffix)
return with_suffix
+ def _check_for_immutable_conflicts(self):
+ # check for conflicts, i.e. a field that is defined normally as
+ # mutable in some parent class but that is now declared immutable
+ from pypy.rpython.lltypesystem.lltype import Void
+ is_self_immutable = "immutable" in self.object_type._hints
+ base = self
+ while base.classdef is not None:
+ base = base.rbase
+ for fieldname in base.fields:
+ try:
+ mangled, r = base._get_field(fieldname)
+ except KeyError:
+ continue
+ if r.lowleveltype == Void:
+ continue
+ base._setup_immutable_field_list()
+ if base.object_type._immutable_field(mangled):
+ continue
+ # 'fieldname' is a mutable, non-Void field in the parent
+ if is_self_immutable:
+ raise ImmutableConflictError(
+ "class %r has _immutable_=True, but parent class %r "
+ "defines (at least) the mutable field %r" % (
+ self, base, fieldname))
+ if fieldname in self.immutable_field_list:
+ raise ImmutableConflictError(
+ "field %r is defined mutable in class %r, but "
+ "listed in _immutable_fields_ in subclass %r" % (
+ fieldname, base, self))
+
def new_instance(self, llops, classcallhop=None):
raise NotImplementedError
Modified: pypy/trunk/pypy/rpython/test/test_rclass.py
==============================================================================
--- pypy/trunk/pypy/rpython/test/test_rclass.py (original)
+++ pypy/trunk/pypy/rpython/test/test_rclass.py Fri Sep 3 17:39:47 2010
@@ -796,27 +796,92 @@
assert accessor.fields == {"inst_y" : ""} or \
accessor.fields == {"oy" : ""} # for ootype
- def test_immutable_inheritance(self):
- class I(object):
- def __init__(self, v):
- self.v = v
-
- class J(I):
+ def test_immutable_forbidden_inheritance_1(self):
+ from pypy.rpython.rclass import ImmutableConflictError
+ class A(object):
+ pass
+ class B(A):
+ _immutable_fields_ = ['v']
+ def f():
+ A().v = 123
+ B() # crash: class B says 'v' is immutable,
+ # but it is defined on parent class A
+ py.test.raises(ImmutableConflictError, self.gengraph, f, [])
+
+ def test_immutable_forbidden_inheritance_2(self):
+ from pypy.rpython.rclass import ImmutableConflictError
+ class A(object):
+ pass
+ class B(A):
+ _immutable_ = True
+ def f():
+ A().v = 123
+ B() # crash: class B has _immutable_ = True
+ # but class A defines 'v' to be mutable
+ py.test.raises(ImmutableConflictError, self.gengraph, f, [])
+
+ def test_immutable_ok_inheritance_2(self):
+ from pypy.jit.metainterp.typesystem import deref
+ class A(object):
+ _immutable_fields_ = ['v']
+ class B(A):
+ _immutable_ = True
+ def f():
+ A().v = 123
+ B().w = 456
+ return B()
+ t, typer, graph = self.gengraph(f, [])
+ B_TYPE = deref(graph.getreturnvar().concretetype)
+ assert B_TYPE._hints["immutable"]
+ try:
+ A_TYPE = B_TYPE.super
+ except AttributeError:
+ A_TYPE = B_TYPE._superclass # for ootype
+ accessor = A_TYPE._hints["immutable_fields"]
+ assert accessor.fields == {"inst_v" : ""} or \
+ accessor.fields == {"ov" : ""} # for ootype
+
+ def test_immutable_subclass_1(self):
+ from pypy.jit.metainterp.typesystem import deref
+ class A(object):
+ _immutable_ = True
+ class B(A):
+ pass
+ def f():
+ B().v = 123
+ return B()
+ t, typer, graph = self.gengraph(f, [])
+ B_TYPE = deref(graph.getreturnvar().concretetype)
+ assert B_TYPE._hints["immutable"] # inherited from A
+
+ def test_immutable_subclass_2(self):
+ from pypy.jit.metainterp.typesystem import deref
+ class A(object):
+ pass
+ class B(A):
_immutable_ = True
- def __init__(self, v, w):
- self.w = w
- I.__init__(self, v)
-
- j = J(3, 4)
- def f():
- j.v = j.v * 1 # make the annotator think it is mutated
- j.w = j.w * 1 # make the annotator think it is mutated
- return j.v + j.w
-
- t, typer, graph = self.gengraph(f, [], backendopt=True)
- f_summary = summary(graph)
- assert f_summary == {"setfield": 2} or \
- f_summary == {"oosetfield": 2} # for ootype
+ def f():
+ B().v = 123
+ return B()
+ t, typer, graph = self.gengraph(f, [])
+ B_TYPE = deref(graph.getreturnvar().concretetype)
+ assert B_TYPE._hints["immutable"]
+
+ def test_immutable_subclass_void(self):
+ from pypy.jit.metainterp.typesystem import deref
+ class A(object):
+ pass
+ class B(A):
+ _immutable_ = True
+ def myfunc():
+ pass
+ def f():
+ A().f = myfunc # it's ok to add Void attributes to A
+ B().v = 123 # even though only B is declared _immutable_
+ return B()
+ t, typer, graph = self.gengraph(f, [])
+ B_TYPE = deref(graph.getreturnvar().concretetype)
+ assert B_TYPE._hints["immutable"]
class TestLLtype(BaseTestRclass, LLRtypeMixin):
Modified: pypy/trunk/pypy/translator/backendopt/test/test_constfold.py
==============================================================================
--- pypy/trunk/pypy/translator/backendopt/test/test_constfold.py (original)
+++ pypy/trunk/pypy/translator/backendopt/test/test_constfold.py Fri Sep 3 17:39:47 2010
@@ -49,7 +49,7 @@
accessor = rclass.FieldListAccessor()
S2 = lltype.GcStruct('S2', ('x', lltype.Signed),
hints={'immutable_fields': accessor})
- accessor.initialize(S2, ['x'])
+ accessor.initialize(S2, {'x': ''})
test_simple(S2)
More information about the Pypy-commit
mailing list