Browse Source

first version of empty entries in tables
(so that, in the future, tables can contain regular nil entries)

Roberto Ierusalimschy 7 years ago
parent
commit
9243c414d9
11 changed files with 146 additions and 99 deletions
  1. 18 12
      lapi.c
  2. 30 28
      lgc.c
  3. 2 2
      llex.c
  4. 27 3
      lobject.h
  5. 33 30
      ltable.c
  6. 6 1
      ltable.h
  7. 3 3
      ltests.c
  8. 4 4
      ltm.c
  9. 7 1
      ltm.h
  10. 11 10
      lvm.c
  11. 5 5
      lvm.h

+ 18 - 12
lapi.c

@@ -1,5 +1,5 @@
 /*
-** $Id: lapi.c,v 2.283 2018/02/05 17:10:52 roberto Exp roberto $
+** $Id: lapi.c,v 2.285 2018/02/20 16:52:50 roberto Exp roberto $
 ** Lua API
 ** See Copyright Notice in lua.h
 */
@@ -657,14 +657,26 @@ LUA_API int lua_geti (lua_State *L, int idx, lua_Integer n) {
 }
 
 
+static int finishrawget (lua_State *L, const TValue *val) {
+  if (isempty(val))  /* avoid copying empty items to the stack */
+    setnilvalue(s2v(L->top));
+  else
+    setobj2s(L, L->top, val);
+  api_incr_top(L);
+  lua_unlock(L);
+  return ttnov(s2v(L->top - 1));
+}
+
+
 LUA_API int lua_rawget (lua_State *L, int idx) {
   TValue *t;
+  const TValue *val;
   lua_lock(L);
   t = index2value(L, idx);
   api_check(L, ttistable(t), "table expected");
-  setobj2s(L, L->top - 1, luaH_get(hvalue(t), s2v(L->top - 1)));
-  lua_unlock(L);
-  return ttnov(s2v(L->top - 1));
+  val = luaH_get(hvalue(t), s2v(L->top - 1));
+  L->top--;  /* remove key */
+  return finishrawget(L, val);
 }
 
 
@@ -673,10 +685,7 @@ LUA_API int lua_rawgeti (lua_State *L, int idx, lua_Integer n) {
   lua_lock(L);
   t = index2value(L, idx);
   api_check(L, ttistable(t), "table expected");
-  setobj2s(L, L->top, luaH_getint(hvalue(t), n));
-  api_incr_top(L);
-  lua_unlock(L);
-  return ttnov(s2v(L->top - 1));
+  return finishrawget(L, luaH_getint(hvalue(t), n));
 }
 
 
@@ -687,10 +696,7 @@ LUA_API int lua_rawgetp (lua_State *L, int idx, const void *p) {
   t = index2value(L, idx);
   api_check(L, ttistable(t), "table expected");
   setpvalue(&k, cast_voidp(p));
-  setobj2s(L, L->top, luaH_get(hvalue(t), &k));
-  api_incr_top(L);
-  lua_unlock(L);
-  return ttnov(s2v(L->top - 1));
+  return finishrawget(L, luaH_get(hvalue(t), &k));
 }
 
 

+ 30 - 28
lgc.c

@@ -145,12 +145,13 @@ static GCObject **getgclist (GCObject *o) {
 ** Clear keys for empty entries in tables. If entry is empty
 ** and its key is not marked, mark its entry as dead. This allows the
 ** collection of the key, but keeps its entry in the table (its removal
-** could break a chain). Other places never manipulate dead keys,
-** because its associated nil value is enough to signal that the entry
-** is logically empty.
+** could break a chain). The main feature of a dead key is that it must
+** be different from any other value, to do not disturb searches. 
+** Other places never manipulate dead keys, because its associated empty
+** value is enough to signal that the entry is logically empty.
 */
-static void removeentry (Node *n) {
-  lua_assert(ttisnil(gval(n)));
+static void clearkey (Node *n) {
+  lua_assert(isempty(gval(n)));
   if (keyiswhite(n))
     setdeadkey(n);  /* unused and unmarked key; remove it */
 }
@@ -386,8 +387,8 @@ static void traverseweakvalue (global_State *g, Table *h) {
      worth traversing it now just to check) */
   int hasclears = (h->sizearray > 0);
   for (n = gnode(h, 0); n < limit; n++) {  /* traverse hash part */
-    if (ttisnil(gval(n)))  /* entry is empty? */
-      removeentry(n);  /* remove it */
+    if (isempty(gval(n)))  /* entry is empty? */
+      clearkey(n);  /* clear its key */
     else {
       lua_assert(!keyisnil(n));
       markkey(g, n);
@@ -428,8 +429,8 @@ static int traverseephemeron (global_State *g, Table *h) {
   }
   /* traverse hash part */
   for (n = gnode(h, 0); n < limit; n++) {
-    if (ttisnil(gval(n)))  /* entry is empty? */
-      removeentry(n);  /* remove it */
+    if (isempty(gval(n)))  /* entry is empty? */
+      clearkey(n);  /* clear its key */
     else if (iscleared(g, gckeyN(n))) {  /* key is not marked (yet)? */
       hasclears = 1;  /* table must be cleared */
       if (valiswhite(gval(n)))  /* value not marked yet? */
@@ -461,8 +462,8 @@ static void traversestrongtable (global_State *g, Table *h) {
   for (i = 0; i < h->sizearray; i++)  /* traverse array part */
     markvalue(g, &h->array[i]);
   for (n = gnode(h, 0); n < limit; n++) {  /* traverse hash part */
-    if (ttisnil(gval(n)))  /* entry is empty? */
-      removeentry(n);  /* remove it */
+    if (isempty(gval(n)))  /* entry is empty? */
+      clearkey(n);  /* clear its key */
     else {
       lua_assert(!keyisnil(n));
       markkey(g, n);
@@ -681,15 +682,16 @@ static void clearprotolist (global_State *g) {
 /*
 ** clear entries with unmarked keys from all weaktables in list 'l'
 */
-static void clearkeys (global_State *g, GCObject *l) {
+static void clearbykeys (global_State *g, GCObject *l) {
   for (; l; l = gco2t(l)->gclist) {
     Table *h = gco2t(l);
-    Node *n, *limit = gnodelast(h);
+    Node *limit = gnodelast(h);
+    Node *n;
     for (n = gnode(h, 0); n < limit; n++) {
-      if (!ttisnil(gval(n)) && (iscleared(g, gckeyN(n))))  /* unmarked key? */
-        setnilvalue(gval(n));  /* clear value */
-      if (ttisnil(gval(n)))  /* is entry empty? */
-        removeentry(n);  /* remove it from table */
+      if (isempty(gval(n)))  /* is entry empty? */
+        clearkey(n);  /* clear its key */
+      else if (iscleared(g, gckeyN(n)))  /* unmarked key? */
+        setempty(gval(n));  /* remove entry */
     }
   }
 }
@@ -699,7 +701,7 @@ static void clearkeys (global_State *g, GCObject *l) {
 ** clear entries with unmarked values from all weaktables in list 'l' up
 ** to element 'f'
 */
-static void clearvalues (global_State *g, GCObject *l, GCObject *f) {
+static void clearbyvalues (global_State *g, GCObject *l, GCObject *f) {
   for (; l != f; l = gco2t(l)->gclist) {
     Table *h = gco2t(l);
     Node *n, *limit = gnodelast(h);
@@ -707,13 +709,13 @@ static void clearvalues (global_State *g, GCObject *l, GCObject *f) {
     for (i = 0; i < h->sizearray; i++) {
       TValue *o = &h->array[i];
       if (iscleared(g, gcvalueN(o)))  /* value was collected? */
-        setnilvalue(o);  /* remove value */
+        setempty(o);  /* remove entry */
     }
     for (n = gnode(h, 0); n < limit; n++) {
       if (iscleared(g, gcvalueN(gval(n))))  /* unmarked value? */
-        setnilvalue(gval(n));  /* clear value */
-      if (ttisnil(gval(n)))  /* is entry empty? */
-        removeentry(n);  /* remove it from table */
+        setempty(gval(n));  /* remove entry */
+      if (isempty(gval(n)))  /* is entry empty? */
+        clearkey(n);  /* clear its key */
     }
   }
 }
@@ -1372,8 +1374,8 @@ static lu_mem atomic (lua_State *L) {
   convergeephemerons(g);
   /* at this point, all strongly accessible objects are marked. */
   /* Clear values from weak tables, before checking finalizers */
-  clearvalues(g, g->weak, NULL);
-  clearvalues(g, g->allweak, NULL);
+  clearbyvalues(g, g->weak, NULL);
+  clearbyvalues(g, g->allweak, NULL);
   origweak = g->weak; origall = g->allweak;
   separatetobefnz(g, 0);  /* separate objects to be finalized */
   work += markbeingfnz(g);  /* mark objects that will be finalized */
@@ -1381,11 +1383,11 @@ static lu_mem atomic (lua_State *L) {
   convergeephemerons(g);
   /* at this point, all resurrected objects are marked. */
   /* remove dead objects from weak tables */
-  clearkeys(g, g->ephemeron);  /* clear keys from all ephemeron tables */
-  clearkeys(g, g->allweak);  /* clear keys from all 'allweak' tables */
+  clearbykeys(g, g->ephemeron);  /* clear keys from all ephemeron tables */
+  clearbykeys(g, g->allweak);  /* clear keys from all 'allweak' tables */
   /* clear values from resurrected weak tables */
-  clearvalues(g, g->weak, origweak);
-  clearvalues(g, g->allweak, origall);
+  clearbyvalues(g, g->weak, origweak);
+  clearbyvalues(g, g->allweak, origall);
   luaS_clearcache(g);
   clearprotolist(g);
   g->currentwhite = cast_byte(otherwhite(g));  /* flip current white */

+ 2 - 2
llex.c

@@ -1,5 +1,5 @@
 /*
-** $Id: llex.c,v 2.98 2017/06/29 15:06:44 roberto Exp roberto $
+** $Id: llex.c,v 2.99 2018/01/28 15:13:26 roberto Exp roberto $
 ** Lexical Analyzer
 ** See Copyright Notice in lua.h
 */
@@ -130,7 +130,7 @@ TString *luaX_newstring (LexState *ls, const char *str, size_t l) {
   TString *ts = luaS_newlstr(L, str, l);  /* create new string */
   setsvalue2s(L, L->top++, ts);  /* temporarily anchor it in stack */
   o = luaH_set(L, ls->h, s2v(L->top - 1));
-  if (ttisnil(o)) {  /* not in use yet? */
+  if (isempty(o)) {  /* not in use yet? */
     /* boolean value does not need GC barrier;
        table is not a metatable, so it does not need to invalidate cache */
     setbvalue(o, 1);  /* t[string] = true */

+ 27 - 3
lobject.h

@@ -1,5 +1,5 @@
 /*
-** $Id: lobject.h,v 2.135 2018/02/21 16:28:12 roberto Exp roberto $
+** $Id: lobject.h,v 2.136 2018/02/22 17:28:10 roberto Exp roberto $
 ** Type definitions for Lua objects
 ** See Copyright Notice in lua.h
 */
@@ -100,7 +100,7 @@ typedef struct TValue {
 #define setobj(L,obj1,obj2) \
 	{ TValue *io1=(obj1); const TValue *io2=(obj2); \
           io1->value_ = io2->value_; io1->tt_ = io2->tt_; \
-	  (void)L; checkliveness(L,io1); }
+	  (void)L; checkliveness(L,io1); lua_assert(!isreallyempty(io1)); }
 
 /*
 ** different types of assignments, according to destination
@@ -148,6 +148,30 @@ typedef StackValue *StkId;  /* index to stack elements */
 /* (address of) a fixed nil value */
 #define luaO_nilobject		(&luaO_nilobject_)
 
+
+/*
+** Variant tag, used only in tables to signal an empty slot
+** (which might be different from a slot containing nil)
+*/
+#define LUA_TEMPTY	(LUA_TNIL | (1 << 4))
+
+#define ttisnilorempty(v)	checktype((v), LUA_TNIL)
+/*
+** By default, entries with any kind of nil are considered empty
+*/
+#define isempty(v)		ttisnilorempty(v)
+
+#define isreallyempty(v)	checktag((v), LUA_TEMPTY)
+
+/* macro defining an empty value */
+#define EMPTYCONSTANT	{NULL}, LUA_TEMPTY
+
+
+/* mark an entry as empty */
+#define setempty(v)		settt_(v, LUA_TEMPTY)
+
+
+
 /* }================================================================== */
 
 
@@ -644,7 +668,7 @@ typedef struct Table {
 
 /*
 ** Use a "nil table" to mark dead keys in a table. Those keys serve
-** only to keep space for removed entries, which may still be part of
+** to keep space for removed entries, which may still be part of
 ** chains. Note that the 'keytt' does not have the BIT_ISCOLLECTABLE
 ** set, so these values are considered not collectable and are different
 ** from any valid value.

+ 33 - 30
ltable.c

@@ -1,5 +1,5 @@
 /*
-** $Id: ltable.c,v 2.132 2018/02/19 20:06:56 roberto Exp roberto $
+** $Id: ltable.c,v 2.133 2018/02/21 12:54:26 roberto Exp roberto $
 ** Lua tables (hash)
 ** See Copyright Notice in lua.h
 */
@@ -88,11 +88,14 @@
 #define dummynode		(&dummynode_)
 
 static const Node dummynode_ = {
-  {{NULL}, LUA_TNIL,  /* value's value and type */
+  {{NULL}, LUA_TEMPTY,  /* value's value and type */
    LUA_TNIL, 0, {NULL}}  /* key type, next, and key value */
 };
 
 
+LUAI_DDEF const TValue luaH_emptyobject_ = {EMPTYCONSTANT};
+
+
 /*
 ** Hash for floating-point numbers.
 ** The main computation should be just
@@ -200,7 +203,7 @@ static const TValue *getgeneric (Table *t, const TValue *key) {
     else {
       int nx = gnext(n);
       if (nx == 0)
-        return luaO_nilobject;  /* not found */
+        return luaH_emptyobject;  /* not found */
       n += nx;
     }
   }
@@ -232,7 +235,7 @@ static unsigned int findindex (lua_State *L, Table *t, TValue *key) {
     return i;  /* yes; that's the index */
   else {
     const TValue *n = getgeneric(t, key);
-    if (n == luaO_nilobject)
+    if (n == luaH_emptyobject)
       luaG_runerror(L, "invalid key to 'next'");  /* key not found */
     i = cast_int(nodefromval(n) - gnode(t, 0));  /* key index in hash table */
     /* hash elements are numbered after array ones */
@@ -244,14 +247,14 @@ static unsigned int findindex (lua_State *L, Table *t, TValue *key) {
 int luaH_next (lua_State *L, Table *t, StkId key) {
   unsigned int i = findindex(L, t, s2v(key));  /* find original element */
   for (; i < t->sizearray; i++) {  /* try first array part */
-    if (!ttisnil(&t->array[i])) {  /* a non-nil value? */
+    if (!isempty(&t->array[i])) {  /* a non-empty entry? */
       setivalue(s2v(key), i + 1);
       setobj2s(L, key + 1, &t->array[i]);
       return 1;
     }
   }
   for (i -= t->sizearray; cast_int(i) < sizenode(t); i++) {  /* hash part */
-    if (!ttisnil(gval(gnode(t, i)))) {  /* a non-nil value? */
+    if (!isempty(gval(gnode(t, i)))) {  /* a non-empty entry? */
       Node *n = gnode(t, i);
       getnodekey(L, s2v(key), n);
       setobj2s(L, key + 1, gval(n));
@@ -336,7 +339,7 @@ static unsigned int numusearray (const Table *t, unsigned int *nums) {
     }
     /* count elements in range (2^(lg - 1), 2^lg] */
     for (; i <= lim; i++) {
-      if (!ttisnil(&t->array[i-1]))
+      if (!isempty(&t->array[i-1]))
         lc++;
     }
     nums[lg] += lc;
@@ -352,7 +355,7 @@ static int numusehash (const Table *t, unsigned int *nums, unsigned int *pna) {
   int i = sizenode(t);
   while (i--) {
     Node *n = &t->node[i];
-    if (!ttisnil(gval(n))) {
+    if (!isempty(gval(n))) {
       if (keyisinteger(n))
         ause += countint(keyival(n), nums);
       totaluse++;
@@ -387,7 +390,7 @@ static void setnodevector (lua_State *L, Table *t, unsigned int size) {
       Node *n = gnode(t, i);
       gnext(n) = 0;
       setnilkey(n);
-      setnilvalue(gval(n));
+      setempty(gval(n));
     }
     t->lsizenode = cast_byte(lsize);
     t->lastfree = gnode(t, size);  /* all positions are free */
@@ -403,7 +406,7 @@ static void reinsert (lua_State *L, Table *ot, Table *t) {
   int size = sizenode(ot);
   for (j = 0; j < size; j++) {
     Node *old = gnode(ot, j);
-    if (!ttisnil(gval(old))) {
+    if (!isempty(gval(old))) {
       /* doesn't need barrier/invalidate cache, as entry was
          already present in the table */
       TValue k;
@@ -456,7 +459,7 @@ void luaH_resize (lua_State *L, Table *t, unsigned int newasize,
     exchangehashpart(t, &newt);  /* and new hash */
     /* re-insert into the new hash the elements from vanishing slice */
     for (i = newasize; i < oldasize; i++) {
-      if (!ttisnil(&t->array[i]))
+      if (!isempty(&t->array[i]))
         luaH_setint(L, t, i + 1, &t->array[i]);
     }
     t->sizearray = oldasize;  /* restore current size... */
@@ -473,7 +476,7 @@ void luaH_resize (lua_State *L, Table *t, unsigned int newasize,
   t->array = newarray;  /* set new array part */
   t->sizearray = newasize;
   for (i = oldasize; i < newasize; i++)  /* clear new slice of the array */
-     setnilvalue(&t->array[i]);
+     setempty(&t->array[i]);
   /* re-insert elements from old hash part into new parts */
   reinsert(L, &newt, t);  /* 'newt' now has the old hash */
   freehash(L, &newt);  /* free old hash part */
@@ -569,7 +572,7 @@ TValue *luaH_newkey (lua_State *L, Table *t, const TValue *key) {
       luaG_runerror(L, "table index is NaN");
   }
   mp = mainpositionTV(t, key);
-  if (!ttisnil(gval(mp)) || isdummy(t)) {  /* main position is taken? */
+  if (!isempty(gval(mp)) || isdummy(t)) {  /* main position is taken? */
     Node *othern;
     Node *f = getfreepos(t);  /* get a free place */
     if (f == NULL) {  /* cannot find a free place? */
@@ -589,7 +592,7 @@ TValue *luaH_newkey (lua_State *L, Table *t, const TValue *key) {
         gnext(f) += cast_int(mp - f);  /* correct 'next' */
         gnext(mp) = 0;  /* now 'mp' is free */
       }
-      setnilvalue(gval(mp));
+      setempty(gval(mp));
     }
     else {  /* colliding node is in its own main position */
       /* new node will go into free position */
@@ -602,7 +605,7 @@ TValue *luaH_newkey (lua_State *L, Table *t, const TValue *key) {
   }
   setnodekey(L, mp, key);
   luaC_barrierback(L, obj2gco(t), key);
-  lua_assert(ttisnil(gval(mp)));
+  lua_assert(isempty(gval(mp)));
   return gval(mp);
 }
 
@@ -625,7 +628,7 @@ const TValue *luaH_getint (Table *t, lua_Integer key) {
         n += nx;
       }
     }
-    return luaO_nilobject;
+    return luaH_emptyobject;
   }
 }
 
@@ -642,7 +645,7 @@ const TValue *luaH_getshortstr (Table *t, TString *key) {
     else {
       int nx = gnext(n);
       if (nx == 0)
-        return luaO_nilobject;  /* not found */
+        return luaH_emptyobject;  /* not found */
       n += nx;
     }
   }
@@ -667,7 +670,7 @@ const TValue *luaH_get (Table *t, const TValue *key) {
   switch (ttype(key)) {
     case LUA_TSHRSTR: return luaH_getshortstr(t, tsvalue(key));
     case LUA_TNUMINT: return luaH_getint(t, ivalue(key));
-    case LUA_TNIL: return luaO_nilobject;
+    case LUA_TNIL: return luaH_emptyobject;
     case LUA_TNUMFLT: {
       lua_Integer k;
       if (luaV_flttointeger(fltvalue(key), &k, 0)) /* index is an integral? */
@@ -686,7 +689,7 @@ const TValue *luaH_get (Table *t, const TValue *key) {
 */
 TValue *luaH_set (lua_State *L, Table *t, const TValue *key) {
   const TValue *p = luaH_get(t, key);
-  if (p != luaO_nilobject)
+  if (p != luaH_emptyobject)
     return cast(TValue *, p);
   else return luaH_newkey(L, t, key);
 }
@@ -695,7 +698,7 @@ TValue *luaH_set (lua_State *L, Table *t, const TValue *key) {
 void luaH_setint (lua_State *L, Table *t, lua_Integer key, TValue *value) {
   const TValue *p = luaH_getint(t, key);
   TValue *cell;
-  if (p != luaO_nilobject)
+  if (p != luaH_emptyobject)
     cell = cast(TValue *, p);
   else {
     TValue k;
@@ -728,16 +731,16 @@ static lua_Unsigned hash_search (Table *t, lua_Unsigned j) {
       j *= 2;
     else {
       j = LUA_MAXINTEGER;
-      if (ttisnil(luaH_getint(t, j)))  /* t[j] == nil? */
+      if (isempty(luaH_getint(t, j)))  /* t[j] not present? */
         break;  /* 'j' now is an absent index */
       else  /* weird case */
         return j;  /* well, max integer is a boundary... */
     }
-  } while (!ttisnil(luaH_getint(t, j)));  /* repeat until t[j] == nil */
-  /* i < j  &&  t[i] !≃ nil  &&  t[j] == nil */
+  } while (!isempty(luaH_getint(t, j)));  /* repeat until an absent t[j] */
+  /* i < j  &&  t[i] present  &&  t[j] absent */
   while (j - i > 1u) {  /* do a binary search between them */
     lua_Unsigned m = (i + j) / 2;
-    if (ttisnil(luaH_getint(t, m))) j = m;
+    if (isempty(luaH_getint(t, m))) j = m;
     else i = m;
   }
   return i;
@@ -746,27 +749,27 @@ static lua_Unsigned hash_search (Table *t, lua_Unsigned j) {
 
 /*
 ** Try to find a boundary in table 't'. (A 'boundary' is an integer index
-** such that t[i] is non-nil and t[i+1] is nil, plus 0 if t[1] is nil
-** and 'maxinteger' if t[maxinteger] is not nil.)
+** such that t[i] is present and t[i+1] is absent, or 0 if t[1] is absent
+** and 'maxinteger' if t[maxinteger] is present.)
 ** First, try the array part: if there is an array part and its last
-** element is nil, there must be a boundary there; a binary search
+** element is absent, there must be a boundary there; a binary search
 ** finds that boundary. Otherwise, if the hash part is empty or does not
 ** contain 'j + 1', 'j' is a boundary. Otherwize, call 'hash_search'
 ** to find a boundary in the hash part.
 */
 lua_Unsigned luaH_getn (Table *t) {
   unsigned int j = t->sizearray;
-  if (j > 0 && ttisnil(&t->array[j - 1])) {
+  if (j > 0 && isempty(&t->array[j - 1])) {
     unsigned int i = 0;
     while (j - i > 1u) {  /* binary search */
       unsigned int m = (i + j) / 2;
-      if (ttisnil(&t->array[m - 1])) j = m;
+      if (isempty(&t->array[m - 1])) j = m;
       else i = m;
     }
     return i;
   }
   else {  /* 'j' is zero or present in table */
-    if (isdummy(t) || ttisnil(luaH_getint(t, l_castU2S(j + 1))))
+    if (isdummy(t) || isempty(luaH_getint(t, l_castU2S(j + 1))))
       return j;  /* 'j + 1' is absent... */
     else  /* 'j + 1' is also present */
       return hash_search(t, j);

+ 6 - 1
ltable.h

@@ -1,5 +1,5 @@
 /*
-** $Id: ltable.h,v 2.24 2017/05/19 12:48:15 roberto Exp roberto $
+** $Id: ltable.h,v 2.25 2017/06/09 16:48:44 roberto Exp roberto $
 ** Lua tables (hash)
 ** See Copyright Notice in lua.h
 */
@@ -21,6 +21,8 @@
 /* true when 't' is using 'dummynode' as its hash part */
 #define isdummy(t)		((t)->lastfree == NULL)
 
+#define luaH_emptyobject	(&luaH_emptyobject_)
+
 
 /* allocated size for hash nodes */
 #define allocsizenode(t)	(isdummy(t) ? 0 : sizenode(t))
@@ -30,6 +32,9 @@
 #define nodefromval(v) 	cast(Node *, (v))
 
 
+LUAI_DDEC const TValue luaH_emptyobject_;
+
+
 LUAI_FUNC const TValue *luaH_getint (Table *t, lua_Integer key);
 LUAI_FUNC void luaH_setint (lua_State *L, Table *t, lua_Integer key,
                                                     TValue *value);

+ 3 - 3
ltests.c

@@ -1,5 +1,5 @@
 /*
-** $Id: ltests.c,v 2.240 2018/01/28 15:13:26 roberto Exp roberto $
+** $Id: ltests.c,v 2.241 2018/02/20 16:52:50 roberto Exp roberto $
 ** Internal Module for Debugging of the Lua Implementation
 ** See Copyright Notice in lua.h
 */
@@ -254,7 +254,7 @@ static void checktable (global_State *g, Table *h) {
   for (i = 0; i < h->sizearray; i++)
     checkvalref(g, hgc, &h->array[i]);
   for (n = gnode(h, 0); n < limit; n++) {
-    if (!ttisnil(gval(n))) {
+    if (!isempty(gval(n))) {
       TValue k;
       getnodekey(g->mainthread, &k, n);
       lua_assert(!keyisnil(n));
@@ -842,7 +842,7 @@ static int table_query (lua_State *L) {
   else if ((i -= t->sizearray) < sizenode(t)) {
     TValue k;
     getnodekey(L, &k, gnode(t, i));
-    if (!ttisnil(gval(gnode(t, i))) ||
+    if (!isempty(gval(gnode(t, i))) ||
         ttisnil(&k) ||
         ttisnumber(&k)) {
       pushobject(L, &k);

+ 4 - 4
ltm.c

@@ -1,5 +1,5 @@
 /*
-** $Id: ltm.c,v 2.62 2018/02/17 19:20:00 roberto Exp roberto $
+** $Id: ltm.c,v 2.63 2018/02/21 15:49:32 roberto Exp roberto $
 ** Tag methods
 ** See Copyright Notice in lua.h
 */
@@ -60,7 +60,7 @@ void luaT_init (lua_State *L) {
 const TValue *luaT_gettm (Table *events, TMS event, TString *ename) {
   const TValue *tm = luaH_getshortstr(events, ename);
   lua_assert(event <= TM_EQ);
-  if (ttisnil(tm)) {  /* no tag method? */
+  if (notm(tm)) {  /* no tag method? */
     events->flags |= cast_byte(1u<<event);  /* cache this fact */
     return NULL;
   }
@@ -137,9 +137,9 @@ void luaT_callTMres (lua_State *L, const TValue *f, const TValue *p1,
 static int callbinTM (lua_State *L, const TValue *p1, const TValue *p2,
                       StkId res, TMS event) {
   const TValue *tm = luaT_gettmbyobj(L, p1, event);  /* try first operand */
-  if (ttisnil(tm))
+  if (notm(tm))
     tm = luaT_gettmbyobj(L, p2, event);  /* try second operand */
-  if (ttisnil(tm)) return 0;
+  if (notm(tm)) return 0;
   luaT_callTMres(L, tm, p1, p2, res);
   return 1;
 }

+ 7 - 1
ltm.h

@@ -1,5 +1,5 @@
 /*
-** $Id: ltm.h,v 2.31 2018/02/09 15:16:06 roberto Exp roberto $
+** $Id: ltm.h,v 2.32 2018/02/17 19:20:00 roberto Exp roberto $
 ** Tag methods
 ** See Copyright Notice in lua.h
 */
@@ -45,6 +45,12 @@ typedef enum {
 } TMS;
 
 
+/*
+** Test whether there is no tagmethod.
+** (Because tagmethods use raw accesses, the result may be an "empty" nil.)
+*/
+#define notm(tm)	ttisnilorempty(tm)
+
 
 #define gfasttm(g,et,e) ((et) == NULL ? NULL : \
   ((et)->flags & (1u<<(e))) ? NULL : luaT_gettm(et, e, (g)->tmname[e]))

+ 11 - 10
lvm.c

@@ -1,5 +1,5 @@
 /*
-** $Id: lvm.c,v 2.345 2018/02/21 15:49:32 roberto Exp roberto $
+** $Id: lvm.c,v 2.346 2018/02/21 19:43:44 roberto Exp roberto $
 ** Lua virtual machine
 ** See Copyright Notice in lua.h
 */
@@ -168,7 +168,7 @@ static int forlimit (const TValue *obj, lua_Integer *p, lua_Integer step,
 /*
 ** Finish the table access 'val = t[key]'.
 ** if 'slot' is NULL, 't' is not a table; otherwise, 'slot' points to
-** t[k] entry (which must be nil).
+** t[k] entry (which must be empty).
 */
 void luaV_finishget (lua_State *L, const TValue *t, TValue *key, StkId val,
                       const TValue *slot) {
@@ -178,12 +178,12 @@ void luaV_finishget (lua_State *L, const TValue *t, TValue *key, StkId val,
     if (slot == NULL) {  /* 't' is not a table? */
       lua_assert(!ttistable(t));
       tm = luaT_gettmbyobj(L, t, TM_INDEX);
-      if (ttisnil(tm))
+      if (notm(tm))
         luaG_typeerror(L, t, "index");  /* no metamethod */
       /* else will try the metamethod */
     }
     else {  /* 't' is a table */
-      lua_assert(ttisnil(slot));
+      lua_assert(isempty(slot));
       tm = fasttm(L, hvalue(t)->metatable, TM_INDEX);  /* table's metamethod */
       if (tm == NULL) {  /* no metamethod? */
         setnilvalue(s2v(val));  /* result is nil */
@@ -209,8 +209,8 @@ void luaV_finishget (lua_State *L, const TValue *t, TValue *key, StkId val,
 /*
 ** Finish a table assignment 't[key] = val'.
 ** If 'slot' is NULL, 't' is not a table.  Otherwise, 'slot' points
-** to the entry 't[key]', or to 'luaO_nilobject' if there is no such
-** entry.  (The value at 'slot' must be nil, otherwise 'luaV_fastget'
+** to the entry 't[key]', or to 'luaH_emptyobject' if there is no such
+** entry.  (The value at 'slot' must be empty, otherwise 'luaV_fastget'
 ** would have done the job.)
 */
 void luaV_finishset (lua_State *L, const TValue *t, TValue *key,
@@ -220,10 +220,10 @@ void luaV_finishset (lua_State *L, const TValue *t, TValue *key,
     const TValue *tm;  /* '__newindex' metamethod */
     if (slot != NULL) {  /* is 't' a table? */
       Table *h = hvalue(t);  /* save 't' table */
-      lua_assert(ttisnil(slot));  /* old value must be nil */
+      lua_assert(isempty(slot));  /* slot must be empty */
       tm = fasttm(L, h->metatable, TM_NEWINDEX);  /* get metamethod */
       if (tm == NULL) {  /* no metamethod? */
-        if (slot == luaO_nilobject)  /* no previous entry? */
+        if (slot == luaH_emptyobject)  /* no previous entry? */
           slot = luaH_newkey(L, h, key);  /* create one */
         /* no metamethod and (now) there is an entry with given key */
         setobj2t(L, cast(TValue *, slot), val);  /* set its new value */
@@ -234,7 +234,8 @@ void luaV_finishset (lua_State *L, const TValue *t, TValue *key,
       /* else will try the metamethod */
     }
     else {  /* not a table; check metamethod */
-      if (ttisnil(tm = luaT_gettmbyobj(L, t, TM_NEWINDEX)))
+      tm = luaT_gettmbyobj(L, t, TM_NEWINDEX);
+      if (notm(tm))
         luaG_typeerror(L, t, "index");
     }
     /* try the metamethod */
@@ -586,7 +587,7 @@ void luaV_objlen (lua_State *L, StkId ra, const TValue *rb) {
     }
     default: {  /* try metamethod */
       tm = luaT_gettmbyobj(L, rb, TM_LEN);
-      if (ttisnil(tm))  /* no metamethod? */
+      if (notm(tm))  /* no metamethod? */
         luaG_typeerror(L, rb, "get length of");
       break;
     }

+ 5 - 5
lvm.h

@@ -1,5 +1,5 @@
 /*
-** $Id: lvm.h,v 2.49 2018/02/19 20:06:56 roberto Exp roberto $
+** $Id: lvm.h,v 2.50 2018/02/21 12:54:26 roberto Exp roberto $
 ** Lua virtual machine
 ** See Copyright Notice in lua.h
 */
@@ -64,17 +64,17 @@
 
 
 /*
-** fast track for 'gettable': if 't' is a table and 't[k]' is not nil,
+** fast track for 'gettable': if 't' is a table and 't[k]' is present,
 ** return 1 with 'slot' pointing to 't[k]' (position of final result).
 ** Otherwise, return 0 (meaning it will have to check metamethod)
-** with 'slot' pointing to a nil 't[k]' (if 't' is a table) or NULL
+** with 'slot' pointing to an empty 't[k]' (if 't' is a table) or NULL
 ** (otherwise). 'f' is the raw get function to use.
 */
 #define luaV_fastget(L,t,k,slot,f) \
   (!ttistable(t)  \
    ? (slot = NULL, 0)  /* not a table; 'slot' is NULL and result is 0 */  \
    : (slot = f(hvalue(t), k),  /* else, do raw access */  \
-      !ttisnil(slot)))  /* result not nil? */
+      !isempty(slot)))  /* result not empty? */
 
 
 /*
@@ -86,7 +86,7 @@
    ? (slot = NULL, 0)  /* not a table; 'slot' is NULL and result is 0 */  \
    : (slot = (l_castS2U(k) - 1u < hvalue(t)->sizearray) \
               ? &hvalue(t)->array[k - 1] : luaH_getint(hvalue(t), k), \
-      !ttisnil(slot)))  /* result not nil? */
+      !isempty(slot)))  /* result not empty? */
 
 
 /*