Bläddra i källkod

New way to control preambular declaration

Validity of the preambular global declaration in controled together
with all declarations, when checking variable names.
Roberto Ierusalimschy 2 månader sedan
förälder
incheckning
be05c44481
4 ändrade filer med 45 tillägg och 15 borttagningar
  1. 20 13
      lparser.c
  2. 3 0
      lparser.h
  3. 5 1
      testes/db.lua
  4. 17 1
      testes/goto.lua

+ 20 - 13
lparser.c

@@ -54,7 +54,6 @@ typedef struct BlockCnt {
   lu_byte upval;  /* true if some variable in the block is an upvalue */
   lu_byte isloop;  /* 1 if 'block' is a loop; 2 if it has pending breaks */
   lu_byte insidetbc;  /* true if inside the scope of a to-be-closed var. */
-  lu_byte globdec;  /* true if inside the scope of any global declaration */
 } BlockCnt;
 
 
@@ -399,22 +398,35 @@ static int newupvalue (FuncState *fs, TString *name, expdesc *v) {
 /*
 ** Look for an active variable with the name 'n' in the
 ** function 'fs'. If found, initialize 'var' with it and return
-** its expression kind; otherwise return -1.
+** its expression kind; otherwise return -1. While searching,
+** var->u.info==-1 means that the preambular global declaration is
+** active (the default while there is no other global declaration);
+** var->u.info==-2 means there is no active collective declaration
+** (some previous global declaration but no collective declaration);
+** and var->u.info>=0 points to the inner-most (the first one found)
+** collective declaration, if there is one.
 */
 static int searchvar (FuncState *fs, TString *n, expdesc *var) {
   int i;
   for (i = cast_int(fs->nactvar) - 1; i >= 0; i--) {
     Vardesc *vd = getlocalvardesc(fs, i);
-    if (vd->vd.name == NULL) {  /* 'global *'? */
-      if (var->u.info == -1) {  /* no previous collective declaration? */
-        var->u.info = fs->firstlocal + i;  /* will use this one as default */
+    if (varglobal(vd)) {  /* global declaration? */
+      if (vd->vd.name == NULL) {  /* collective declaration? */
+        if (var->u.info < 0)  /* no previous collective declaration? */
+          var->u.info = fs->firstlocal + i;  /* this is the first one */
+      }
+      else {  /* global name */
+        if (eqstr(n, vd->vd.name)) {  /* found? */
+          init_exp(var, VGLOBAL, fs->firstlocal + i);
+          return VGLOBAL;
+        }
+        else if (var->u.info == -1)  /* active preambular declaration? */
+          var->u.info = -2;  /* invalidate preambular declaration */
       }
     }
     else if (eqstr(n, vd->vd.name)) {  /* found? */
       if (vd->vd.kind == RDKCTC)  /* compile-time constant? */
         init_exp(var, VCONST, fs->firstlocal + i);
-      else if (vd->vd.kind == GDKREG || vd->vd.kind == GDKCONST)
-        init_exp(var, VGLOBAL, fs->firstlocal + i);
       else  /* local variable */
         init_var(fs, var, i);
       return cast_int(var->k);
@@ -486,7 +498,7 @@ static void buildvar (LexState *ls, TString *varname, expdesc *var) {
     expdesc key;
     int info = var->u.info;
     /* global by default in the scope of a global declaration? */
-    if (info == -1 && fs->bl->globdec)
+    if (info == -2)
       luaK_semerror(ls, "variable '%s' not declared", getstr(varname));
     singlevaraux(fs, ls->envn, var, 1);  /* get environment variable */
     if (var->k == VGLOBAL)
@@ -692,10 +704,6 @@ static void enterblock (FuncState *fs, BlockCnt *bl, lu_byte isloop) {
   bl->upval = 0;
   /* inherit 'insidetbc' from enclosing block */
   bl->insidetbc = (fs->bl != NULL && fs->bl->insidetbc);
-  /* inherit 'globdec' from enclosing block or enclosing function */
-  bl->globdec = fs->bl != NULL ? fs->bl->globdec
-              : fs->prev != NULL ? fs->prev->bl->globdec
-              : 0;  /* chunk's first block */
   bl->previous = fs->bl;  /* link block in function's block list */
   fs->bl = bl;
   lua_assert(fs->freereg == luaY_nvarstack(fs));
@@ -1855,7 +1863,6 @@ static void globalfunc (LexState *ls, int line) {
 static void globalstatfunc (LexState *ls, int line) {
   /* stat -> GLOBAL globalfunc | GLOBAL globalstat */
   luaX_next(ls);  /* skip 'global' */
-  ls->fs->bl->globdec = 1;  /* in the scope of a global declaration */
   if (testnext(ls, TK_FUNCTION))
     globalfunc(ls, line);
   else

+ 3 - 0
lparser.h

@@ -105,6 +105,9 @@ typedef struct expdesc {
 /* variables that live in registers */
 #define varinreg(v)	((v)->vd.kind <= RDKTOCLOSE)
 
+/* test for global variables */
+#define varglobal(v)	((v)->vd.kind >= GDKREG)
+
 
 /* description of an active variable */
 typedef union Vardesc {

+ 5 - 1
testes/db.lua

@@ -349,9 +349,11 @@ end, "crl")
 
 
 function f(a,b)
-  global collectgarbage, assert, g, string
+ -- declare some globals to check that they don't interfere with 'getlocal'
+  global collectgarbage
   collectgarbage()
   local _, x = debug.getlocal(1, 1)
+  global assert, g, string
   local _, y = debug.getlocal(1, 2)
   assert(x == a and y == b)
   assert(debug.setlocal(2, 3, "pera") == "AA".."AA")
@@ -387,7 +389,9 @@ function g (...)
   f(AAAA,B)
   assert(AAAA == "pera" and B == "manga")
   do
+     global *
      local B = 13
+     global<const> assert
      local x,y = debug.getlocal(1,5)
      assert(x == 'B' and y == 13)
   end

+ 17 - 1
testes/goto.lua

@@ -364,7 +364,23 @@ do
     print(X)    -- Ok to use
     Y = 1   -- ERROR
   ]], "assign to const variable 'Y'")
-  
+
+  checkerr([[
+    global *;
+    Y = X    -- Ok to use
+    global<const> *;
+    Y = 1   -- ERROR
+  ]], "assign to const variable 'Y'")
+
+  global *
+  Y = 10
+  assert(_ENV.Y == 10)
+  global<const> *
+  local x = Y
+  global *
+  Y = x + Y
+  assert(_ENV.Y == 20)
+
 end
 
 print'OK'