[pypy-svn] r24836 - in pypy/branch/pypy-rdict-refactoring: . annotation rpython rpython/lltypesystem rpython/test

arigo at codespeak.net arigo at codespeak.net
Thu Mar 23 00:35:10 CET 2006


Author: arigo
Date: Thu Mar 23 00:35:06 2006
New Revision: 24836

Added:
   pypy/branch/pypy-rdict-refactoring/
      - copied from r24803, pypy/dist/pypy/
   pypy/branch/pypy-rdict-refactoring/rpython/annlowlevel.py
      - copied unchanged from r24831, pypy/dist/pypy/rpython/annlowlevel.py
Modified:
   pypy/branch/pypy-rdict-refactoring/annotation/model.py
   pypy/branch/pypy-rdict-refactoring/rpython/llinterp.py
   pypy/branch/pypy-rdict-refactoring/rpython/lltypesystem/lltype.py
   pypy/branch/pypy-rdict-refactoring/rpython/lltypesystem/rclass.py
   pypy/branch/pypy-rdict-refactoring/rpython/raddress.py
   pypy/branch/pypy-rdict-refactoring/rpython/rclass.py
   pypy/branch/pypy-rdict-refactoring/rpython/rdict.py
   pypy/branch/pypy-rdict-refactoring/rpython/rfloat.py
   pypy/branch/pypy-rdict-refactoring/rpython/rint.py
   pypy/branch/pypy-rdict-refactoring/rpython/rmodel.py
   pypy/branch/pypy-rdict-refactoring/rpython/rstr.py
   pypy/branch/pypy-rdict-refactoring/rpython/rtuple.py
   pypy/branch/pypy-rdict-refactoring/rpython/rtyper.py
   pypy/branch/pypy-rdict-refactoring/rpython/test/test_rdict.py
Log:
Branch check-in: this rdict refactoring is mostly done, but
I got a test_stress failure.  (I promize a more detailed log
message in the trunk merge.)


Modified: pypy/branch/pypy-rdict-refactoring/annotation/model.py
==============================================================================
--- pypy/dist/pypy/annotation/model.py	(original)
+++ pypy/branch/pypy-rdict-refactoring/annotation/model.py	Thu Mar 23 00:35:06 2006
@@ -28,7 +28,7 @@
 #
 
 
-from types import BuiltinFunctionType, MethodType
+from types import BuiltinFunctionType, MethodType, FunctionType
 import pypy
 from pypy.annotation.pairtype import pair, extendabletype
 from pypy.tool.tls import tlsobject
@@ -582,6 +582,11 @@
         ll_ptrtype = lltype.typeOf(v.im_self)
         assert isinstance(ll_ptrtype, lltype.Ptr)
         return SomeLLADTMeth(ll_ptrtype, v.im_func)
+    if isinstance(v, FunctionType):
+        # this case should only be for staticmethod instances used in
+        # adtmeths: the getattr() result is then a plain FunctionType object.
+        from pypy.annotation.bookkeeper import getbookkeeper
+        return getbookkeeper().immutablevalue(v)
     return lltype_to_annotation(lltype.typeOf(v))
     
 # ____________________________________________________________

Modified: pypy/branch/pypy-rdict-refactoring/rpython/llinterp.py
==============================================================================
--- pypy/dist/pypy/rpython/llinterp.py	(original)
+++ pypy/branch/pypy-rdict-refactoring/rpython/llinterp.py	Thu Mar 23 00:35:06 2006
@@ -449,20 +449,17 @@
 
     def op_getfield(self, obj, field):
         assert checkptr(obj)
-        result = getattr(obj, field)
         # check the difference between op_getfield and op_getsubstruct:
-        # the former returns the real field, the latter a pointer to it
-        assert lltype.typeOf(result) == getattr(lltype.typeOf(obj).TO, field)
-        return result
+        assert not isinstance(getattr(lltype.typeOf(obj).TO, field),
+                              lltype.ContainerType)
+        return getattr(obj, field)
 
     def op_getsubstruct(self, obj, field):
         assert checkptr(obj)
-        result = getattr(obj, field)
         # check the difference between op_getfield and op_getsubstruct:
-        # the former returns the real field, the latter a pointer to it
-        assert (lltype.typeOf(result) ==
-                lltype.Ptr(getattr(lltype.typeOf(obj).TO, field)))
-        return result
+        assert isinstance(getattr(lltype.typeOf(obj).TO, field),
+                          lltype.ContainerType)
+        return getattr(obj, field)
 
     def op_getarraysubstruct(self, array, index):
         assert checkptr(array)

Modified: pypy/branch/pypy-rdict-refactoring/rpython/lltypesystem/lltype.py
==============================================================================
--- pypy/dist/pypy/rpython/lltypesystem/lltype.py	(original)
+++ pypy/branch/pypy-rdict-refactoring/rpython/lltypesystem/lltype.py	Thu Mar 23 00:35:06 2006
@@ -686,9 +686,17 @@
                 o = getattr(self._obj, field_name)
                 return _expose(o, self._solid)
         if isinstance(self._T, ContainerType):
-            adtmeth = self._T._adtmeths.get(field_name)
-            if adtmeth is not None:
-                return adtmeth.__get__(self)
+            try:
+                adtmeth = self._T._adtmeths[field_name]
+            except KeyError:
+                pass
+            else:
+                try:
+                    getter = adtmeth.__get__
+                except AttributeError:
+                    return adtmeth
+                else:
+                    return getter(self)
         raise AttributeError("%r instance has no field %r" % (self._T,
                                                               field_name))
 

Modified: pypy/branch/pypy-rdict-refactoring/rpython/lltypesystem/rclass.py
==============================================================================
--- pypy/dist/pypy/rpython/lltypesystem/rclass.py	(original)
+++ pypy/branch/pypy-rdict-refactoring/rpython/lltypesystem/rclass.py	Thu Mar 23 00:35:06 2006
@@ -620,6 +620,8 @@
     return obj.typeptr.rtti
 
 def ll_inst_hash(ins):
+    if not ins:
+        return 0    # for None
     cached = ins.hash_cache
     if cached == 0:
        cached = ins.hash_cache = id(ins)

Modified: pypy/branch/pypy-rdict-refactoring/rpython/raddress.py
==============================================================================
--- pypy/dist/pypy/rpython/raddress.py	(original)
+++ pypy/branch/pypy-rdict-refactoring/rpython/raddress.py	Thu Mar 23 00:35:06 2006
@@ -44,6 +44,8 @@
     def get_ll_hash_function(self):
         return ll_addrhash
 
+    get_ll_fasthash_function = get_ll_hash_function
+
 def ll_addrhash(addr1):
     return cast_adr_to_int(addr1)
 

Modified: pypy/branch/pypy-rdict-refactoring/rpython/rclass.py
==============================================================================
--- pypy/dist/pypy/rpython/rclass.py	(original)
+++ pypy/branch/pypy-rdict-refactoring/rpython/rclass.py	Thu Mar 23 00:35:06 2006
@@ -182,6 +182,9 @@
     def get_ll_eq_function(self):
         return None    # defaults to compare by identity ('==' on pointers)
 
+    def can_ll_be_null(self, s_value):
+        return s_value.can_be_none()
+
 # ____________________________________________________________
 
 def rtype_new_instance(rtyper, classdef, llops):

Modified: pypy/branch/pypy-rdict-refactoring/rpython/rdict.py
==============================================================================
--- pypy/dist/pypy/rpython/rdict.py	(original)
+++ pypy/branch/pypy-rdict-refactoring/rpython/rdict.py	Thu Mar 23 00:35:06 2006
@@ -13,17 +13,15 @@
 #  generic implementation of RPython dictionary, with parametric DICTKEY and
 #  DICTVALUE types.
 #
-#  XXX this should be re-optimized for specific types of keys; e.g.
-#      for string keys we don't need the two boolean flags but can use
-#      a NULL and a special 'dummy' keys.  Similarily, for immutable dicts,
-#      the array should be inlined and num_pristine_entries is not needed.
+#  XXX for immutable dicts, the array should be inlined and
+#      num_pristine_entries and everused are not needed.
 #
 #    struct dictentry {
 #        DICTKEY key;
-#        bool valid;      # to mark if the entry is filled
-#        bool everused;   # to mark if the entry is or has ever been filled
+#        bool f_valid;      # (optional) the entry is filled
+#        bool f_everused;   # (optional) the entry is or has ever been filled
 #        DICTVALUE value;
-#        int hash;
+#        int f_hash;        # (optional) key hash, if hard to recompute
 #    }
 #    
 #    struct dicttable {
@@ -61,39 +59,10 @@
     def rtyper_makekey(self):
         return (self.__class__, self.dictdef.dictkey, self.dictdef.dictvalue)
 
-class DictTrait:
-    # avoid explosion of one helper function per repr
-    # vs. layout and functions
-
-    def __init__(self, dictrepr):
-        self.DICT = dictrepr.DICT
-        self.DICTENTRYARRAY = dictrepr.DICTENTRYARRAY
-        self.custom_eq_hash = False
-        self.ll_keyhash = dictrepr.ll_keyhash
-        self.ll_keyeq = dictrepr.ll_keyeq
-
-    def _freeze_(self):
-        return True
-
-    def __repr__(self):
-        return "DictT %s" % self.DICT._short_name()
-
-def dict_trait(rtyper, dictrepr):
-    if dictrepr.custom_eq_hash: # in this case use there is not much point in not just using the repr
-        return dictrepr
-
-    key = (dictrepr.DICT, dictrepr.ll_keyhash, dictrepr.ll_keyeq)
-    try:
-        return rtyper._dict_traits[key]
-    except KeyError:
-        trait = DictTrait(dictrepr)
-        rtyper._dict_traits[key] = trait
-        return trait
-    
 
 class DictRepr(rmodel.Repr):
 
-    def __init__(self, rtyper, key_repr, value_repr, dictkey=None, dictvalue=None,
+    def __init__(self, rtyper, key_repr, value_repr, dictkey, dictvalue,
                  custom_eq_hash=None):
         self.rtyper = rtyper
         self.DICT = lltype.GcForwardReference()
@@ -113,7 +82,6 @@
         self.dictvalue = dictvalue
         self.dict_cache = {}
         self._custom_eq_hash_repr = custom_eq_hash
-        self._trait = None # cache for get_trait
         # setup() needs to be called to finish this initialization
         
     def pickrepr(self, item_repr):
@@ -142,12 +110,77 @@
         if isinstance(self.DICT, lltype.GcForwardReference):
             self.DICTKEY = self.key_repr.lowleveltype
             self.DICTVALUE = self.value_repr.lowleveltype
-            self.DICTENTRY = lltype.Struct("dictentry", 
-                                ("key", self.DICTKEY),
-                                ("hash", lltype.Signed),
-                                ("valid", lltype.Bool),
-                                ("everused", lltype.Bool),
-                                ("value", self.DICTVALUE))
+
+            # compute the shape of the DICTENTRY structure
+            entryfields = []
+            entrymeths = {
+                'must_clear_key':   (isinstance(self.DICTKEY, lltype.Ptr)
+                                     and self.DICTKEY._needsgc()),
+                'must_clear_value': (isinstance(self.DICTVALUE, lltype.Ptr)
+                                     and self.DICTVALUE._needsgc()),
+                }
+
+            # * the key
+            entryfields.append(("key", self.DICTKEY))
+
+            # * if NULL is not a valid ll value for the key or the value
+            #   field of the entry, it can be used as a marker for
+            #   never-used entries.  Otherwise, we need an explicit flag.
+            s_key   = self.dictkey.s_value
+            s_value = self.dictvalue.s_value
+            nullkeymarker = not self.key_repr.can_ll_be_null(s_key)
+            nullvaluemarker = not self.value_repr.can_ll_be_null(s_value)
+
+            if nullkeymarker:
+                entrymeths['everused'] = ll_everused_from_key
+            elif nullvaluemarker:
+                entrymeths['everused'] = ll_everused_from_value
+            else:
+                entryfields.append(("f_everused", lltype.Bool))
+                entrymeths['everused'] = ll_everused_from_flag
+
+            # * if the key or the value can also contain a "dummy" non-null
+            #   marker, we use it for deleted entries.
+            rtyper = self.rtyper
+            dummy_obj = self.key_repr.get_ll_dummyval_obj(rtyper, s_key)
+            if dummy_obj:
+                entrymeths['dummy_obj'] = dummy_obj
+                entrymeths['valid'] = ll_valid_from_key
+                entrymeths['mark_deleted'] = ll_mark_deleted_in_key
+                # the key is overwritten by 'dummy' when the entry is deleted
+                entrymeths['must_clear_key'] = False
+            else:
+                dummy_obj = self.value_repr.get_ll_dummyval_obj(rtyper,
+                                                                s_value)
+                if dummy_obj:
+                    entrymeths['dummy_obj'] = dummy_obj
+                    entrymeths['valid'] = ll_valid_from_value
+                    entrymeths['mark_deleted'] = ll_mark_deleted_in_value
+                    # value is overwritten by 'dummy' when entry is deleted
+                    entrymeths['must_clear_value'] = False
+                else:
+                    entryfields.append(("f_valid", lltype.Bool))
+                    entrymeths['valid'] = ll_valid_from_flag
+                    entrymeths['mark_deleted'] = ll_mark_deleted_in_flag
+
+            # * the value
+            entryfields.append(("value", self.DICTVALUE))
+
+            # * the hash, if needed
+            if self.custom_eq_hash:
+                fasthashfn = None
+            else:
+                fasthashfn = self.key_repr.get_ll_fasthash_function()
+            if fasthashfn is None:
+                entryfields.append(("f_hash", lltype.Signed))
+                entrymeths['hash'] = ll_hash_from_cache
+            else:
+                entrymeths['hash'] = ll_hash_recomputed
+                entrymeths['fasthashfn'] = fasthashfn
+
+            # Build the lltype data structures
+            self.DICTENTRY = lltype.Struct("dictentry", adtmeths=entrymeths,
+                                           *entryfields)
             self.DICTENTRYARRAY = lltype.GcArray(self.DICTENTRY)
             fields =          [ ("num_items", lltype.Signed),
                                 ("num_pristine_entries", lltype.Signed), 
@@ -156,11 +189,25 @@
                 self.r_rdict_eqfn, self.r_rdict_hashfn = self._custom_eq_hash_repr()
                 fields.extend([ ("fnkeyeq", self.r_rdict_eqfn.lowleveltype),
                                 ("fnkeyhash", self.r_rdict_hashfn.lowleveltype) ])
-            self.DICT.become(lltype.GcStruct("dicttable", *fields))
-        if 'll_keyhash' not in self.__dict__ and not self.custom_eq_hash:
-            # figure out which functions must be used to hash and compare keys
-            self.ll_keyeq   = self.key_repr.get_ll_eq_function()   # can be None
-            self.ll_keyhash = self.key_repr.get_ll_hash_function()
+                adtmeths = {
+                    'keyhash':        ll_keyhash_custom,
+                    'keyeq':          ll_keyeq_custom,
+                    'r_rdict_eqfn':   self.r_rdict_eqfn,
+                    'r_rdict_hashfn': self.r_rdict_hashfn,
+                    }
+            else:
+                # figure out which functions must be used to hash and compare
+                ll_keyhash = self.key_repr.get_ll_hash_function()
+                ll_keyeq = self.key_repr.get_ll_eq_function()  # can be None
+                ll_keyhash = staticmethod(ll_keyhash)
+                if ll_keyeq is not None:
+                    ll_keyeq = staticmethod(ll_keyeq)
+                adtmeths = {
+                    'keyhash': ll_keyhash,
+                    'keyeq':   ll_keyeq,
+                    }
+            self.DICT.become(lltype.GcStruct("dicttable", adtmeths=adtmeths,
+                                             *fields))
 
     def recast_value(self, llops, v):
         return llops.convertvar(v, self.value_repr, self.external_value_repr)
@@ -168,11 +215,6 @@
     def recast_key(self, llops, v):
         return llops.convertvar(v, self.key_repr, self.external_key_repr)
 
-    def get_trait(self):
-        if self._trait is None:
-            self._trait = dict_trait(self.rtyper, self)
-        return self._trait   
-
     def convert_const(self, dictobj):
         # get object from bound dict methods
         #dictobj = getattr(dictobj, '__self__', dictobj) 
@@ -185,7 +227,7 @@
             return self.dict_cache[key]
         except KeyError:
             self.setup()
-            l_dict = ll_newdict(self)
+            l_dict = ll_newdict_size(self.DICT, len(dictobj))
             self.dict_cache[key] = l_dict 
             r_key = self.key_repr
             r_value = self.value_repr
@@ -196,38 +238,20 @@
                 if self.r_rdict_hashfn.lowleveltype != lltype.Void:
                     l_fn = self.r_rdict_hashfn.convert_const(dictobj.key_hash)
                     l_dict.fnkeyhash = l_fn
-                # a dummy object with ll_keyeq and ll_keyhash methods to
-                # pass to ll_dict_setitem()
-                class Dummy:
-                    custom_eq_hash = False
-                    def ll_keyeq(self, key1, key2):
-                        # theory: all low-level values we consider as keys
-                        # can be compared by equality (i.e. identity for
-                        # pointers) because the r_dict itself should have
-                        # ensured that it does not store duplicate equal keys.
-                        return key1 == key2
-                    def ll_keyhash(self, key):
-                        # theoretically slow, but well (see theory above)
-                        for llkey, hash in self.cache:
-                            if key == llkey:
-                                return hash
-                        raise TyperError("hash missing in convert_const(%r)" %
-                                         (dictobj,))
-                dummy = Dummy()
-                dummy.cache = []
 
                 for dictkeycontainer, dictvalue in dictobj._dict.items():
                     llkey = r_key.convert_const(dictkeycontainer.key)
                     llvalue = r_value.convert_const(dictvalue)
-                    dummy.cache.insert(0, (llkey, dictkeycontainer.hash))
-                    ll_dict_setitem(l_dict, llkey, llvalue, dummy)
+                    ll_dict_insertclean(l_dict, llkey, llvalue,
+                                        dictkeycontainer.hash)
                 return l_dict
 
             else:
                 for dictkey, dictvalue in dictobj.items():
                     llkey = r_key.convert_const(dictkey)
                     llvalue = r_value.convert_const(dictvalue)
-                    ll_dict_setitem(l_dict, llkey, llvalue, self)
+                    ll_dict_insertclean(l_dict, llkey, llvalue,
+                                        l_dict.keyhash(llkey))
                 return l_dict
 
     def rtype_len(self, hop):
@@ -244,22 +268,19 @@
     def rtype_method_get(self, hop):
         v_dict, v_key, v_default = hop.inputargs(self, self.key_repr,
                                                  self.value_repr)
-        ctrait = hop.inputconst(lltype.Void, self.get_trait())
         hop.exception_cannot_occur()
-        v_res = hop.gendirectcall(ll_get, v_dict, v_key, v_default, ctrait)
+        v_res = hop.gendirectcall(ll_get, v_dict, v_key, v_default)
         return self.recast_value(hop.llops, v_res)
 
     def rtype_method_copy(self, hop):
         v_dict, = hop.inputargs(self)
-        ctrait = hop.inputconst(lltype.Void, self.get_trait())
         hop.exception_cannot_occur()
-        return hop.gendirectcall(ll_copy, v_dict, ctrait)
+        return hop.gendirectcall(ll_copy, v_dict)
 
     def rtype_method_update(self, hop):
         v_dic1, v_dic2 = hop.inputargs(self, self)
-        ctrait = hop.inputconst(lltype.Void, self.get_trait())
         hop.exception_cannot_occur()
-        return hop.gendirectcall(ll_update, v_dic1, v_dic2, ctrait)
+        return hop.gendirectcall(ll_update, v_dic1, v_dic2)
 
     def _rtype_method_kvi(self, hop, spec):
         v_dic, = hop.inputargs(self)
@@ -299,28 +320,24 @@
 
     def rtype_getitem((r_dict, r_key), hop):
         v_dict, v_key = hop.inputargs(r_dict, r_dict.key_repr)
-        ctrait = hop.inputconst(lltype.Void, r_dict.get_trait())
         hop.has_implicit_exception(KeyError)   # record that we know about it
         hop.exception_is_here()
-        v_res = hop.gendirectcall(ll_dict_getitem, v_dict, v_key, ctrait)
+        v_res = hop.gendirectcall(ll_dict_getitem, v_dict, v_key)
         return r_dict.recast_value(hop.llops, v_res)
 
     def rtype_delitem((r_dict, r_key), hop):
         v_dict, v_key = hop.inputargs(r_dict, r_dict.key_repr)
-        ctrait = hop.inputconst(lltype.Void, r_dict.get_trait())
         hop.has_implicit_exception(KeyError)   # record that we know about it
         hop.exception_is_here()
-        return hop.gendirectcall(ll_dict_delitem, v_dict, v_key, ctrait)
+        return hop.gendirectcall(ll_dict_delitem, v_dict, v_key)
 
     def rtype_setitem((r_dict, r_key), hop):
         v_dict, v_key, v_value = hop.inputargs(r_dict, r_dict.key_repr, r_dict.value_repr)
-        ctrait = hop.inputconst(lltype.Void, r_dict.get_trait())
-        hop.gendirectcall(ll_dict_setitem, v_dict, v_key, v_value, ctrait)
+        hop.gendirectcall(ll_dict_setitem, v_dict, v_key, v_value)
 
     def rtype_contains((r_dict, r_key), hop):
         v_dict, v_key = hop.inputargs(r_dict, r_dict.key_repr)
-        ctrait = hop.inputconst(lltype.Void, r_dict.get_trait())
-        return hop.gendirectcall(ll_contains, v_dict, v_key, ctrait)
+        return hop.gendirectcall(ll_contains, v_dict, v_key)
         
 class __extend__(pairtype(DictRepr, DictRepr)):
     def convert_from_to((r_dict1, r_dict2), v, llops):
@@ -342,6 +359,56 @@
 #  be direct_call'ed from rtyped flow graphs, which means that they will
 #  get flowed and annotated, mostly with SomePtr.
 
+def ll_everused_from_flag(entry):
+    return entry.f_everused
+
+def ll_everused_from_key(entry):
+    return bool(entry.key)
+
+def ll_everused_from_value(entry):
+    return bool(entry.value)
+
+def ll_valid_from_flag(entry):
+    return entry.f_valid
+
+def ll_mark_deleted_in_flag(entry):
+    entry.f_valid = False
+
+def ll_valid_from_key(entry):
+    ENTRY = lltype.typeOf(entry).TO
+    dummy = ENTRY.dummy_obj.ll_dummy_value
+    return entry.everused() and entry.key != dummy
+
+def ll_mark_deleted_in_key(entry):
+    ENTRY = lltype.typeOf(entry).TO
+    dummy = ENTRY.dummy_obj.ll_dummy_value
+    entry.key = dummy
+
+def ll_valid_from_value(entry):
+    ENTRY = lltype.typeOf(entry).TO
+    dummy = ENTRY.dummy_obj.ll_dummy_value
+    return entry.everused() and entry.value != dummy
+
+def ll_mark_deleted_in_value(entry):
+    ENTRY = lltype.typeOf(entry).TO
+    dummy = ENTRY.dummy_obj.ll_dummy_value
+    entry.value = dummy
+
+def ll_hash_from_cache(entry):
+    return entry.f_hash
+
+def ll_hash_recomputed(entry):
+    ENTRY = lltype.typeOf(entry).TO
+    return ENTRY.fasthashfn(entry.key)
+
+def ll_keyhash_custom(d, key):
+    DICT = lltype.typeOf(d).TO
+    return hlinvoke(DICT.r_rdict_hashfn, d.fnkeyhash, key)
+
+def ll_keyeq_custom(d, key1, key2):
+    DICT = lltype.typeOf(d).TO
+    return hlinvoke(DICT.r_rdict_eqfn, d.fnkeyeq, key1, key2)
+
 def dum_keys(): pass
 def dum_values(): pass
 def dum_items():pass
@@ -356,51 +423,69 @@
     # check if a dict is True, allowing for None
     return bool(d) and d.num_items != 0
 
-def ll_dict_getitem(d, key, dictrait):
-    entry = ll_dict_lookup(d, key, dictrait)
-    if entry.valid:
+def ll_dict_getitem(d, key):
+    entry = ll_dict_lookup(d, key, d.keyhash(key))
+    if entry.valid():
         return entry.value 
     else: 
         raise KeyError 
 
-def ll_dict_setitem(d, key, value, dictrait):
-    entry = ll_dict_lookup(d, key, dictrait)
+def ll_dict_setitem(d, key, value):
+    hash = d.keyhash(key)
+    entry = ll_dict_lookup(d, key, hash)
+    everused = entry.everused()
+    valid    = entry.valid()
+    # set up the new entry
+    ENTRY = lltype.typeOf(entry).TO
     entry.value = value
-    if entry.valid:
+    if valid:
         return
-    if dictrait.custom_eq_hash:
-        hash = hlinvoke(dictrait.r_rdict_hashfn, d.fnkeyhash, key)
-    else:
-        hash = dictrait.ll_keyhash(key)
-    entry.key = key 
-    entry.hash = hash
-    entry.valid = True
+    entry.key = key
+    if hasattr(ENTRY, 'f_hash'):  entry.f_hash = hash
+    if hasattr(ENTRY, 'f_valid'): entry.f_valid = True
     d.num_items += 1
-    if not entry.everused:
-        entry.everused = True
+    if not everused:
+        if hasattr(ENTRY, 'f_everused'): entry.f_everused = True
         d.num_pristine_entries -= 1
         if d.num_pristine_entries <= len(d.entries) / 3:
-            ll_dict_resize(d, dictrait)
+            ll_dict_resize(d)
 
-def ll_dict_delitem(d, key, dictrait):
-    entry = ll_dict_lookup(d, key, dictrait)
-    if not entry.valid:
+def ll_dict_insertclean(d, key, value, hash):
+    # Internal routine used by ll_dict_resize() to insert an item which is
+    # known to be absent from the dict.  This routine also assumes that
+    # the dict contains no deleted entries.  This routine has the advantage
+    # of never calling d.keyhash() and d.keyeq(), so it cannot call back
+    # to user code.  ll_dict_insertclean() doesn't resize the dict, either.
+    entry = ll_dict_lookup_clean(d, hash)
+    ENTRY = lltype.typeOf(entry).TO
+    entry.value = value
+    entry.key = key
+    if hasattr(ENTRY, 'f_hash'):     entry.f_hash = hash
+    if hasattr(ENTRY, 'f_valid'):    entry.f_valid = True
+    if hasattr(ENTRY, 'f_everused'): entry.f_everused = True
+    d.num_items += 1
+    d.num_pristine_entries -= 1
+
+def ll_dict_delitem(d, key):
+    entry = ll_dict_lookup(d, key, d.keyhash(key))
+    if not entry.valid():
         raise KeyError
-    entry.valid = False
+    entry.mark_deleted()
     d.num_items -= 1
-    # clear the key and the value if they are pointers
-    keytype = lltype.typeOf(entry).TO.key
-    if isinstance(keytype, lltype.Ptr):
-        key = entry.key   # careful about destructor side effects
-        entry.key = lltype.nullptr(keytype.TO)
-    valuetype = lltype.typeOf(entry).TO.value
-    if isinstance(valuetype, lltype.Ptr):
-        entry.value = lltype.nullptr(valuetype.TO)
+    # clear the key and the value if they are GC pointers
+    ENTRY = lltype.typeOf(entry).TO
+    if ENTRY.must_clear_key:
+        key = entry.key   # careful about destructor side effects:
+                          # keep key alive until entry.value has also
+                          # been zeroed (if it must be)
+        entry.key = lltype.nullptr(ENTRY.key.TO)
+    if ENTRY.must_clear_value:
+        entry.value = lltype.nullptr(ENTRY.value.TO)
     num_entries = len(d.entries)
     if num_entries > DICT_INITSIZE and d.num_items < num_entries / 4:
-        ll_dict_resize(d, dictrait)
+        ll_dict_resize(d)
 
-def ll_dict_resize(d, dictrait):
+def ll_dict_resize(d):
     old_entries = d.entries
     old_size = len(old_entries) 
     # make a 'new_size' estimate and shrink it if there are many
@@ -409,49 +494,41 @@
     while new_size > DICT_INITSIZE and d.num_items < new_size / 4:
         new_size /= 2
     d.entries = lltype.malloc(lltype.typeOf(old_entries).TO, new_size)
-    d.num_pristine_entries = new_size - d.num_items
+    d.num_items = 0
+    d.num_pristine_entries = new_size
     i = 0
     while i < old_size:
         entry = old_entries[i]
-        if entry.valid:
-           new_entry = ll_dict_lookup(d, entry.key, dictrait)
-           new_entry.key = entry.key
-           new_entry.hash = entry.hash
-           new_entry.value = entry.value
-           new_entry.valid = True
-           new_entry.everused = True
+        if entry.valid():
+            ll_dict_insertclean(d, entry.key, entry.value, entry.hash())
         i += 1
 
 # ------- a port of CPython's dictobject.c's lookdict implementation -------
 PERTURB_SHIFT = 5
 
-def ll_dict_lookup(d, key, dictrait):
-    if dictrait.custom_eq_hash:
-        hash = hlinvoke(dictrait.r_rdict_hashfn, d.fnkeyhash, key)
-    else:
-        hash = dictrait.ll_keyhash(key)
+def ll_dict_lookup(d, key, hash):
     entries = d.entries
     mask = len(entries) - 1
     i = r_uint(hash & mask) 
     # do the first try before any looping 
     entry = entries[i]
-    if entry.valid:
+    if entry.valid():
         checkingkey = entry.key
         if checkingkey == key:
             return entry   # found the entry
-        if entry.hash == hash:
-            if dictrait.custom_eq_hash:
-                res = hlinvoke(dictrait.r_rdict_eqfn, d.fnkeyeq, checkingkey, key)
+        if d.keyeq is not None and entry.hash() == hash:
+            # correct hash, maybe the key is e.g. a different pointer to
+            # an equal object
+            found = d.keyeq(checkingkey, key)
+            if 1:      # XXX not always needed
                 if (entries != d.entries or
-                    not entry.valid or entry.key != checkingkey):
+                    not entry.valid() or entry.key != checkingkey):
                     # the compare did major nasty stuff to the dict: start over
-                    return ll_dict_lookup(d, key, dictrait)
-            else:
-                res = dictrait.ll_keyeq is not None and dictrait.ll_keyeq(checkingkey, key)
-            if res:
+                    return ll_dict_lookup(d, key, hash)
+            if found:
                 return entry   # found the entry
         freeslot = lltype.nullptr(lltype.typeOf(entry).TO)
-    elif entry.everused:
+    elif entry.everused():
         freeslot = entry
     else:
         return entry    # pristine entry -- lookup failed
@@ -462,44 +539,65 @@
     while 1: 
         i = ((i << 2) + i + perturb + 1) & mask
         entry = entries[i]
-        if not entry.everused:
+        if not entry.everused():
             return freeslot or entry 
-        elif entry.valid:
+        elif entry.valid():
             checkingkey = entry.key
             if checkingkey == key:
                 return entry
-            if entry.hash == hash:
-                if dictrait.custom_eq_hash:
-                    res = hlinvoke(dictrait.r_rdict_eqfn, d.fnkeyeq, checkingkey, key)
+            if d.keyeq is not None and entry.hash() == hash:
+                # correct hash, maybe the key is e.g. a different pointer to
+                # an equal object
+                found = d.keyeq(checkingkey, key)
+                if 1:      # XXX not always needed
                     if (entries != d.entries or
-                        not entry.valid or entry.key != checkingkey):
-                        # the compare did major nasty stuff to the dict: start over
-                        return ll_dict_lookup(d, key, dictrait)
-                else:
-                    res = dictrait.ll_keyeq is not None and dictrait.ll_keyeq(checkingkey, key)
-                if res:
-                    return entry
+                        not entry.valid() or entry.key != checkingkey):
+                        # the compare did major nasty stuff to the dict:
+                        # start over
+                        return ll_dict_lookup(d, key, hash)
+                if found:
+                    return entry   # found the entry
         elif not freeslot:
             freeslot = entry 
         perturb >>= PERTURB_SHIFT
 
+def ll_dict_lookup_clean(d, hash):
+    # a simplified version of ll_dict_lookup() which assumes that the
+    # key is new, and the dictionary doesn't contain deleted entries.
+    # It only find the next free slot for the given hash.
+    entries = d.entries
+    mask = len(entries) - 1
+    i = r_uint(hash & mask) 
+    entry = entries[i]
+    perturb = r_uint(hash) 
+    while entry.everused():
+        i = ((i << 2) + i + perturb + 1) & mask
+        entry = entries[i]
+    return entry
+
 # ____________________________________________________________
 #
 #  Irregular operations.
 
 DICT_INITSIZE = 8
 
-def ll_newdict(dictrait):
-    d = lltype.malloc(dictrait.DICT)
-    d.entries = lltype.malloc(dictrait.DICTENTRYARRAY, DICT_INITSIZE)
-    d.num_items = 0  # but still be explicit
+def ll_newdict(DICT):
+    d = lltype.malloc(DICT)
+    d.entries = lltype.malloc(DICT.entries.TO, DICT_INITSIZE)
+    #d.num_items = 0    -- defaults
     d.num_pristine_entries = DICT_INITSIZE
     return d
 
-def ll_copy_extra_data(targetdict, sourcedict, dictrait):
-    if dictrait.custom_eq_hash:
-        targetdict.fnkeyeq   = sourcedict.fnkeyeq
-        targetdict.fnkeyhash = sourcedict.fnkeyhash
+def ll_newdict_size(DICT, length_estimate):
+    length_estimate = (length_estimate // 2) * 3
+    n = DICT_INITSIZE
+    while n < length_estimate:
+        n *= 2
+    d = lltype.malloc(DICT)
+    d.entries = lltype.malloc(DICT.entries.TO, n)
+    #d.num_items = 0    -- defaults
+    d.num_pristine_entries = DICT_INITSIZE
+    return d
 
 def rtype_newdict(hop):
     hop.inputargs()    # no arguments expected
@@ -507,8 +605,8 @@
     if r_dict == robject.pyobj_repr: # special case: SomeObject: SomeObject dicts!
         cdict = hop.inputconst(robject.pyobj_repr, dict)
         return hop.genop('simple_call', [cdict], resulttype = robject.pyobj_repr)
-    ctrait = hop.inputconst(lltype.Void, r_dict.get_trait())
-    v_result = hop.gendirectcall(ll_newdict, ctrait)
+    cDICT = hop.inputconst(lltype.Void, r_dict.DICT)
+    v_result = hop.gendirectcall(ll_newdict, cDICT)
     return v_result
 
 def rtype_r_dict(hop):
@@ -517,9 +615,9 @@
         raise TyperError("r_dict() call does not return an r_dict instance")
     v_eqfn, v_hashfn = hop.inputargs(r_dict.r_rdict_eqfn,
                                      r_dict.r_rdict_hashfn)
-    ctrait = hop.inputconst(lltype.Void, r_dict)
+    cDICT = hop.inputconst(lltype.Void, r_dict.DICT)
     hop.exception_cannot_occur()
-    v_result = hop.gendirectcall(ll_newdict, ctrait)
+    v_result = hop.gendirectcall(ll_newdict, cDICT)
     if r_dict.r_rdict_eqfn.lowleveltype != lltype.Void:
         cname = hop.inputconst(lltype.Void, 'fnkeyeq')
         hop.genop('setfield', [v_result, cname, v_eqfn])
@@ -579,7 +677,7 @@
         while index < entries_len:
             entry = entries[index]
             index = index + 1
-            if entry.valid:
+            if entry.valid():
                 iter.index = index
                 if func is dum_items:
                     r = lltype.malloc(RETURNTYPE.TO)
@@ -597,49 +695,51 @@
 # _____________________________________________________________
 # methods
 
-def ll_get(dict, key, default, dictrait):
-    entry = ll_dict_lookup(dict, key, dictrait) 
-    if entry.valid:
+def ll_get(dict, key, default):
+    entry = ll_dict_lookup(dict, key, dict.keyhash(key))
+    if entry.valid():
         return entry.value
     else: 
         return default
 
-def ll_copy(dict, dictrait):
+def ll_copy(dict):
+    DICT = lltype.typeOf(dict).TO
     dictsize = len(dict.entries)
-    d = lltype.malloc(dictrait.DICT)
-    d.entries = lltype.malloc(dictrait.DICTENTRYARRAY, dictsize)
+    d = lltype.malloc(DICT)
+    d.entries = lltype.malloc(DICT.entries.TO, dictsize)
     d.num_items = dict.num_items
     d.num_pristine_entries = dict.num_pristine_entries
-    ll_copy_extra_data(d, dict, dictrait)
+    if hasattr(DICT, 'fnkeyeq'):   d.fnkeyeq   = dict.fnkeyeq
+    if hasattr(DICT, 'fnkeyhash'): d.fnkeyhash = dict.fnkeyhash
     i = 0
     while i < dictsize:
         d_entry = d.entries[i]
         entry = dict.entries[i]
+        ENTRY = lltype.typeOf(entry).TO
         d_entry.key = entry.key
-        d_entry.hash = entry.hash
+        if hasattr(ENTRY, 'f_valid'):    d_entry.f_valid    = entry.f_valid
+        if hasattr(ENTRY, 'f_everused'): d_entry.f_everused = entry.f_everused
         d_entry.value = entry.value
-        d_entry.valid = entry.valid
-        d_entry.everused = entry.everused
+        if hasattr(ENTRY, 'f_hash'):     d_entry.f_hash     = entry.f_hash
         i += 1
     return d
 
 def ll_clear(d):
     if len(d.entries) == d.num_pristine_entries == DICT_INITSIZE:
         return
-    DICTPTR = lltype.typeOf(d)
-    d.entries = lltype.malloc(DICTPTR.TO.entries.TO, DICT_INITSIZE)
+    DICT = lltype.typeOf(d).TO
+    d.entries = lltype.malloc(DICT.entries.TO, DICT_INITSIZE)
     d.num_items = 0
     d.num_pristine_entries = DICT_INITSIZE
 
-def ll_update(dic1, dic2, dictrait):
-    # XXX warning, no protection against ll_dict_setitem mutating dic2
-    d2len = len(dic2.entries)
+def ll_update(dic1, dic2):
     entries = dic2.entries
+    d2len = len(entries)
     i = 0
     while i < d2len:
         entry = entries[i]
-        if entry.valid:
-            ll_dict_setitem(dic1, entry.key, entry.value, dictrait)
+        if entry.valid():
+            ll_dict_setitem(dic1, entry.key, entry.value)
         i += 1
 
 # this is an implementation of keys(), values() and items()
@@ -655,14 +755,14 @@
 
 def ll_kvi(dic, LIST, func):
     res = LIST.ll_newlist(dic.num_items)
-    dlen = len(dic.entries)
     entries = dic.entries
+    dlen = len(entries)
     items = res.ll_items()
     i = 0
     p = 0
     while i < dlen:
         entry = entries[i]
-        if entry.valid:
+        if entry.valid():
             ELEM = lltype.typeOf(items).TO.OF
             if func is dum_items:
                 r = lltype.malloc(ELEM.TO)
@@ -672,11 +772,11 @@
             elif func is dum_keys:
                 items[p] = recast(ELEM, entry.key)
             elif func is dum_values:
-                 items[p] = recast(ELEM, entry.value)
+                items[p] = recast(ELEM, entry.value)
             p += 1
         i += 1
     return res
 
-def ll_contains(d, key, dictrait):
-    entry = ll_dict_lookup(d, key, dictrait)
-    return entry.valid
+def ll_contains(d, key):
+    entry = ll_dict_lookup(d, key, d.keyhash(key))
+    return entry.valid()

Modified: pypy/branch/pypy-rdict-refactoring/rpython/rfloat.py
==============================================================================
--- pypy/dist/pypy/rpython/rfloat.py	(original)
+++ pypy/branch/pypy-rdict-refactoring/rpython/rfloat.py	Thu Mar 23 00:35:06 2006
@@ -154,8 +154,6 @@
     v = (v - float(hipart)) * TAKE_NEXT
     x = hipart + int(v) + (expo << 15)
     return x
-ll_hash_float.cache_in_dict = True
-
 #
 # _________________________ Conversions _________________________
 

Modified: pypy/branch/pypy-rdict-refactoring/rpython/rint.py
==============================================================================
--- pypy/dist/pypy/rpython/rint.py	(original)
+++ pypy/branch/pypy-rdict-refactoring/rpython/rint.py	Thu Mar 23 00:35:06 2006
@@ -225,6 +225,17 @@
     def get_ll_hash_function(self):
         return ll_hash_int
 
+    get_ll_fasthash_function = get_ll_hash_function
+
+    def get_ll_dummyval_obj(self, rtyper, s_value):
+        # if >= 0, then all negative values are special
+        if s_value.nonneg and not s_value.unsigned:
+            return signed_repr    # whose ll_dummy_value is -1
+        else:
+            return None
+
+    ll_dummy_value = -1
+
     def rtype_chr(_, hop):
         vlist =  hop.inputargs(Signed)
         if hop.has_implicit_exception(ValueError):
@@ -416,9 +427,11 @@
         j += 1
     return result
 
-def ll_hash_int(n):
+def ll_identity(n):
     return n
 
+ll_hash_int = ll_identity
+
 def ll_check_chr(n):
     if 0 <= n <= 255:
         return

Modified: pypy/branch/pypy-rdict-refactoring/rpython/rmodel.py
==============================================================================
--- pypy/dist/pypy/rpython/rmodel.py	(original)
+++ pypy/branch/pypy-rdict-refactoring/rpython/rmodel.py	Thu Mar 23 00:35:06 2006
@@ -5,6 +5,7 @@
 from pypy.rpython.lltypesystem.lltype import \
      Void, Bool, Float, Signed, Char, UniChar, \
      typeOf, LowLevelType, Ptr, PyObject, isCompatibleType
+from pypy.rpython.lltypesystem import lltype
 from pypy.rpython.ootypesystem import ootype
 from pypy.rpython.error import TyperError, MissingRTypeOperation 
 
@@ -135,12 +136,41 @@
 
     def get_ll_hash_function(self):
         """Return a hash(x) function for low-level values of this Repr.
-        As a hint, the function can have a flag 'cache_in_dict=True' if it
-        makes non-trivial computations and its result should be cached in
-        dictionary entries.
         """
         raise TyperError, 'no hashing function for %r' % self
 
+    def get_ll_fasthash_function(self):
+        """Return a 'fast' hash(x) function for low-level values of this
+        Repr.  The function can assume that 'x' is already stored as a
+        key in a dict.  get_ll_fasthash_function() should return None if
+        the hash should rather be cached in the dict entry.
+        """
+        return None
+
+    def can_ll_be_null(self, s_value):
+        """Check if the low-level repr can take the value 0/NULL.
+        The annotation s_value is provided as a hint because it may
+        contain more information than the Repr.
+        """
+        return True   # conservative
+
+    def get_ll_dummyval_obj(self, rtyper, s_value):
+        """A dummy value is a special low-level value, not otherwise
+        used.  It should not be the NULL value even if it is special.
+        This returns either None, or a hashable object that has a
+        (possibly lazy) attribute 'll_dummy_value'.
+        The annotation s_value is provided as a hint because it may
+        contain more information than the Repr.
+        """
+        T = self.lowleveltype
+        if (isinstance(T, lltype.Ptr) and
+            isinstance(T.TO, (lltype.Struct,
+                              lltype.Array,
+                              lltype.ForwardReference))):
+            return DummyValueBuilder(rtyper, T.TO)
+        else:
+            return None
+
     def rtype_bltn_list(self, hop):
         raise TyperError, 'no list() support for %r' % self
 
@@ -310,6 +340,7 @@
     lowleveltype = Void
     def get_ll_eq_function(self): return None
     def get_ll_hash_function(self): return ll_hash_void
+    get_ll_fasthash_function = get_ll_hash_function
 impossible_repr = VoidRepr()
 
 # ____________________________________________________________
@@ -390,7 +421,43 @@
         return item_repr, rclass.getinstancerepr(rtyper, None)
     else:
         return item_repr, item_repr
-        
+
+
+class DummyValueBuilder(object):
+
+    def __init__(self, rtyper, TYPE):
+        self.rtyper = rtyper
+        self.TYPE = TYPE
+
+    def _freeze_(self):
+        return True
+
+    def __hash__(self):
+        return hash(self.TYPE)
+
+    def __eq__(self, other):
+        return (isinstance(other, DummyValueBuilder) and
+                self.rtyper is other.rtyper and
+                self.TYPE == other.TYPE)
+
+    def __ne__(self, other):
+        return not (self == other)
+
+    def build_ll_dummy_value(self):
+        TYPE = self.TYPE
+        try:
+            return self.rtyper.cache_dummy_values[TYPE]
+        except KeyError:
+            # generate a dummy ptr to an immortal placeholder struct/array
+            if TYPE._is_varsize():
+                p = lltype.malloc(TYPE, 0, immortal=True)
+            else:
+                p = lltype.malloc(TYPE, immortal=True)
+            self.rtyper.cache_dummy_values[TYPE] = p
+            return p
+
+    ll_dummy_value = property(build_ll_dummy_value)
+
 
 # logging/warning
 

Modified: pypy/branch/pypy-rdict-refactoring/rpython/rstr.py
==============================================================================
--- pypy/dist/pypy/rpython/rstr.py	(original)
+++ pypy/branch/pypy-rdict-refactoring/rpython/rstr.py	Thu Mar 23 00:35:06 2006
@@ -80,6 +80,16 @@
     def get_ll_hash_function(self):
         return ll_strhash
 
+    def get_ll_fasthash_function(self):
+        return ll_strfasthash
+
+    def can_ll_be_null(self, s_value):
+        if self is string_repr:
+            return s_value.can_be_none()
+        else:
+            return True     # for CharRepr/UniCharRepr subclasses,
+                            # where NULL is always valid: it is chr(0)
+
     def rtype_len(_, hop):
         v_str, = hop.inputargs(string_repr)
         return hop.gendirectcall(ll_strlen, v_str)
@@ -404,6 +414,8 @@
     def get_ll_hash_function(self):
         return ll_char_hash
 
+    get_ll_fasthash_function = get_ll_hash_function
+
     def ll_str(self, ch):
         return ll_chr2str(ch)
 
@@ -476,6 +488,8 @@
     def get_ll_hash_function(self):
         return ll_unichar_hash
 
+    get_ll_fasthash_function = get_ll_hash_function
+
 ##    def rtype_len(_, hop):
 ##        return hop.inputconst(Signed, 1)
 ##
@@ -641,6 +655,9 @@
         s.hash = x
     return x
 
+def ll_strfasthash(s):
+    return s.hash     # assumes that the hash is already computed
+
 def ll_strconcat(s1, s2):
     len1 = len(s1.chars)
     len2 = len(s2.chars)

Modified: pypy/branch/pypy-rdict-refactoring/rpython/rtuple.py
==============================================================================
--- pypy/dist/pypy/rpython/rtuple.py	(original)
+++ pypy/branch/pypy-rdict-refactoring/rpython/rtuple.py	Thu Mar 23 00:35:06 2006
@@ -81,7 +81,6 @@
         source = source % body
         exec source in miniglobals
         ll_hash = miniglobals['ll_hash']
-        ll_hash.cache_in_dict = True
         _gen_hash_function_cache[key] = ll_hash
         return ll_hash
 
@@ -127,6 +126,9 @@
     def get_ll_hash_function(self):
         return gen_hash_function(self.items_r)    
 
+    def can_ll_be_null(self, s_value):
+        return False
+
     def rtype_len(self, hop):
         return hop.inputconst(Signed, len(self.items_r))
 

Modified: pypy/branch/pypy-rdict-refactoring/rpython/rtyper.py
==============================================================================
--- pypy/dist/pypy/rpython/rtyper.py	(original)
+++ pypy/branch/pypy-rdict-refactoring/rpython/rtyper.py	Thu Mar 23 00:35:06 2006
@@ -59,6 +59,7 @@
         self.concrete_calltables = {}
         self.class_pbc_attributes = {}
         self.oo_meth_impls = {}
+        self.cache_dummy_values = {}
         self.typererrors = []
         self.typererror_count = 0
         # make the primitive_to_repr constant mapping

Modified: pypy/branch/pypy-rdict-refactoring/rpython/test/test_rdict.py
==============================================================================
--- pypy/dist/pypy/rpython/test/test_rdict.py	(original)
+++ pypy/branch/pypy-rdict-refactoring/rpython/test/test_rdict.py	Thu Mar 23 00:35:06 2006
@@ -123,7 +123,7 @@
 
     res = interpret(func2, [ord(x), ord(y)])
     for i in range(len(res.entries)): 
-        assert not (res.entries[i].everused and not res.entries[i].valid)
+        assert not (res.entries[i].everused() and not res.entries[i].valid())
 
     def func3(c0, c1, c2, c3, c4, c5, c6, c7):
         d = {}
@@ -143,7 +143,7 @@
                                for i in range(rdict.DICT_INITSIZE)])
     count_frees = 0
     for i in range(len(res.entries)):
-        if not res.entries[i].everused:
+        if not res.entries[i].everused():
             count_frees += 1
     assert count_frees >= 3
 
@@ -444,9 +444,13 @@
         yield x
 
 def test_stress():
-    dictrepr = rdict.DictRepr(None, rint.signed_repr, rint.signed_repr)
+    from pypy.annotation.dictdef import DictKey, DictValue
+    from pypy.annotation import model as annmodel
+    dictrepr = rdict.DictRepr(None, rint.signed_repr, rint.signed_repr,
+                              DictKey(None, annmodel.SomeInteger()),
+                              DictValue(None, annmodel.SomeInteger()))
     dictrepr.setup()
-    l_dict = rdict.ll_newdict(dictrepr)
+    l_dict = rdict.ll_newdict(dictrepr.DICT)
     referencetable = [None] * 400
     referencelength = 0
     value = 0
@@ -454,7 +458,7 @@
     def complete_check():
         for n, refvalue in zip(range(len(referencetable)), referencetable):
             try:
-                gotvalue = rdict.ll_dict_getitem(l_dict, n, dictrepr)
+                gotvalue = rdict.ll_dict_getitem(l_dict, n)
             except KeyError:
                 assert refvalue is None
             else:
@@ -464,18 +468,18 @@
         n = int(x*100.0)    # 0 <= x < 400
         op = repr(x)[-1]
         if op <= '2' and referencetable[n] is not None:
-            rdict.ll_dict_delitem(l_dict, n, dictrepr)
+            rdict.ll_dict_delitem(l_dict, n)
             referencetable[n] = None
             referencelength -= 1
         elif op <= '6':
-            rdict.ll_dict_setitem(l_dict, n, value, dictrepr)
+            rdict.ll_dict_setitem(l_dict, n, value)
             if referencetable[n] is None:
                 referencelength += 1
             referencetable[n] = value
             value += 1
         else:
             try:
-                gotvalue = rdict.ll_dict_getitem(l_dict, n, dictrepr)
+                gotvalue = rdict.ll_dict_getitem(l_dict, n)
             except KeyError:
                 assert referencetable[n] is None
             else:
@@ -486,6 +490,78 @@
         assert l_dict.num_items == referencelength
     complete_check()
 
+# ____________________________________________________________
+
+def test_opt_nullkeymarker():
+    def f():
+        d = {"hello": None}
+        d["world"] = None
+        return "hello" in d, d
+    res = interpret(f, [])
+    assert res.item0 == True
+    DICT = lltype.typeOf(res.item1).TO
+    assert not hasattr(DICT.entries.TO.OF, 'f_everused')# non-None string keys
+    assert not hasattr(DICT.entries.TO.OF, 'f_valid')   # strings have a dummy
+
+def test_opt_nullvaluemarker():
+    def f(n):
+        d = {-5: "abcd"}
+        d[123] = "def"
+        return len(d[n]), d
+    res = interpret(f, [-5])
+    assert res.item0 == 4
+    DICT = lltype.typeOf(res.item1).TO
+    assert not hasattr(DICT.entries.TO.OF, 'f_everused')# non-None str values
+    assert not hasattr(DICT.entries.TO.OF, 'f_valid')   # strs have a dummy
+
+def test_opt_nonullmarker():
+    class A:
+        pass
+    def f(n):
+        if n > 5:
+            a = A()
+        else:
+            a = None
+        d = {a: -5441}
+        d[A()] = n+9872
+        return d[a], d
+    res = interpret(f, [-5])
+    assert res.item0 == -5441
+    DICT = lltype.typeOf(res.item1).TO
+    assert hasattr(DICT.entries.TO.OF, 'f_everused') # can-be-None A instances
+    assert not hasattr(DICT.entries.TO.OF, 'f_valid')# with a dummy A instance
+
+    res = interpret(f, [6])
+    assert res.item0 == -5441
+
+def test_opt_nonnegint_dummy():
+    def f(n):
+        d = {n: 12}
+        d[-87] = 24
+        del d[n]
+        return len(d.copy()), d[-87], d
+    res = interpret(f, [5])
+    assert res.item0 == 1
+    assert res.item1 == 24
+    DICT = lltype.typeOf(res.item2).TO
+    assert hasattr(DICT.entries.TO.OF, 'f_everused') # all ints can be zero
+    assert not hasattr(DICT.entries.TO.OF, 'f_valid')# nonneg int: dummy -1
+
+def test_opt_no_dummy():
+    def f(n):
+        d = {n: 12}
+        d[-87] = -24
+        del d[n]
+        return len(d.copy()), d[-87], d
+    res = interpret(f, [5])
+    assert res.item0 == 1
+    assert res.item1 == -24
+    DICT = lltype.typeOf(res.item2).TO
+    assert hasattr(DICT.entries.TO.OF, 'f_everused') # all ints can be zero
+    assert hasattr(DICT.entries.TO.OF, 'f_valid')    # no dummy available
+
+# ____________________________________________________________
+
 def test_id_instances_keys():
     class A:
         pass
@@ -560,4 +636,14 @@
     res = interpret(f, [2])
     assert res == f(2)
         
-        
+def test_dict_of_dict():
+    def f(n):
+        d = {}
+        d[5] = d
+        d[6] = {}
+        return len(d[n])
+
+    res = interpret(f, [5])
+    assert res == 2
+    res = interpret(f, [6])
+    assert res == 0



More information about the Pypy-commit mailing list