lua-users home
lua-l archive

[Date Prev][Date Next][Thread Prev][Thread Next] [Date Index] [Thread Index]


The discussion about explicit mode made me realise that I have
overlooked a use case with my explicit upvalues patch.  Specifically
the case where you may want to disable the global environment but
still allow upvalues to function normally.  To that end, I have
changed the behaviour so that if `_ENV` is not explicitly declared
then it will never be used as an upvalue, even if the wildcard upvalue
behaviour is used.  I had no idea for what the wildcard behaviour
would actually represent when I first implemented it, I really just
included it for completeness (equivalence to argument declarations).

So, this patch behaves the same as the previous except that there is
no longer any way to explicitly declare exactly the same behaviour as
is implicit without the annotation.

```lua
function a() <_ENV,...>

end
Index: lparser.c
@@ -28,6 +28,12 @@
 #include "ltable.h"
 
 
+/* vararg and upvalue control flags */
+#define LUA_VAUSED  (1<<0)
+#define LUA_VADECL  (1<<1)
+#define LUA_VUNENV  (1<<2)
+#define LUA_VULOCK  (1<<3)
+
 
 /* maximum number of local variables per function (must be smaller
    than 250, due to the bytecode format) */
@@ -278,6 +284,11 @@ static int singlevaraux (FuncState *fs, TString *n, expdesc *var, int base) {
     else {  /* not found as local at current level; try upvalues */
       int idx = searchupvalue(fs, n);  /* try existing upvalues */
       if (idx < 0) {  /* not found? */
+        Proto *f = fs->f;
+        if (f->is_vararg & LUA_VULOCK)  /* no more upvalues */
+          return VVOID;  /* look in globals */
+        if ((f->is_vararg & LUA_VUNENV) && eqstr(fs->ls->envn, n))  /* cannot pass _ENV */
+          return VVOID;  /* fail when unwound */
         if (singlevaraux(fs->prev, n, var, 0) == VVOID) /* try upper levels */
           return VVOID;  /* not found; is a global */
         /* else was LOCAL or UPVAL */
@@ -295,7 +306,10 @@ static void singlevar (LexState *ls, expdesc *var) {
   FuncState *fs = ls->fs;
   if (singlevaraux(fs, varname, var, 1) == VVOID) {  /* global name? */
     expdesc key;
-    singlevaraux(fs, ls->envn, var, 1);  /* get environment variable */
+    if (singlevaraux(fs, ls->envn, var, 1) == VVOID) {  /* get environment variable */
+      luaX_syntaxerror(ls, luaO_pushfstring(ls->L,
+        "variable '%s' not found (_ENV disabled)", getstr(varname)));
+    }
     lua_assert(var->k == VLOCAL || var->k == VUPVAL);
     codestring(ls, &key, varname);  /* key is variable name */
     luaK_indexed(fs, var, &key);  /* env[varname] */
@@ -537,6 +551,7 @@ static void open_func (LexState *ls, FuncState *fs, BlockCnt *bl) {
   fs->firstlocal = ls->dyd->actvar.n;
   fs->bl = NULL;
   f = fs->f;
+  f->is_vararg = 0;
   f->source = ls->source;
   f->maxstacksize = 2;  /* registers 0/1 are always valid */
   enterblock(fs, bl, 0);
@@ -561,6 +576,7 @@ static void close_func (LexState *ls) {
   f->sizelocvars = fs->nlocvars;
   luaM_reallocvector(L, f->upvalues, f->sizeupvalues, fs->nups, Upvaldesc);
   f->sizeupvalues = fs->nups;
+  f->is_vararg &= LUA_VAUSED;  /* keep only vararg usage as boolean */
   lua_assert(fs->bl == NULL);
   ls->fs = fs->prev;
   luaC_checkGC(L);
@@ -744,12 +760,59 @@ static void constructor (LexState *ls, expdesc *t) {
 
 
 
+static void force_upval (FuncState *fs, TString *name) {
+  expdesc var;
+  const char *msg = NULL;
+  if (searchupvalue(fs, name) >= 0) {
+    msg = "upval '%s' already declared";
+  } else if (singlevaraux(fs->prev, name, &var, 0) == VVOID) {
+    msg = "upval '%s' not found in closure context";
+  } else {
+    newupvalue(fs, name, &var);
+  }
+  if (msg) luaX_syntaxerror(fs->ls, luaO_pushfstring(fs->ls->L, msg, getstr(name)));
+}
+
+
+static void upvlist (LexState *ls) {
+  FuncState *fs = ls->fs;
+  Proto *f = fs->f;
+  int var = 0;
+  f->is_vararg |= LUA_VUNENV;  /* Assume _ENV is not available */
+  luaX_next(ls);
+  if (ls->t.token != '>') {
+    do {
+      switch (ls->t.token) {
+        case TK_NAME: {  /* upval -> NAME */
+          TString *name = str_checkname(ls);
+          force_upval(fs, name);
+          if (eqstr(ls->envn, name)) {
+            f->is_vararg &= ~LUA_VUNENV;
+          }
+          break;
+        }
+        case TK_DOTS: {  /* unlocked, but disable _ENV */
+          luaX_next(ls);
+          var = 1;
+          break;
+        }
+        default: luaX_syntaxerror(ls, "<name> or '...' expected");
+      }
+    } while (!var && testnext(ls, ','));
+  }
+  checknext(ls, '>');
+  if (!var) {
+    f->is_vararg |= LUA_VULOCK;  /* Not varupv */
+  }
+}
+
+
 static void parlist (LexState *ls) {
   /* parlist -> [ param { ',' param } ] */
   FuncState *fs = ls->fs;
   Proto *f = fs->f;
+  int var = 0;
   int nparams = 0;
-  f->is_vararg = 0;
   if (ls->t.token != ')') {  /* is 'parlist' not empty? */
     do {
       switch (ls->t.token) {
@@ -760,12 +823,15 @@ static void parlist (LexState *ls) {
         }
         case TK_DOTS: {  /* param -> '...' */
           luaX_next(ls);
-          f->is_vararg = 2;  /* declared vararg */
+          var = 1;
           break;
         }
         default: luaX_syntaxerror(ls, "<name> or '...' expected");
       }
-    } while (!f->is_vararg && testnext(ls, ','));
+    } while (!var && testnext(ls, ','));
+  }
+  if (var) {
+    f->is_vararg |= LUA_VADECL;  /* declared vararg */
   }
   adjustlocalvars(ls, nparams);
   f->numparams = cast_byte(fs->nactvar);
@@ -787,6 +853,9 @@ static void body (LexState *ls, expdesc *e, int ismethod, int line) {
   }
   parlist(ls);
   checknext(ls, ')');
+  if (ls->t.token == '<') {
+    upvlist(ls);
+  }
   statlist(ls);
   new_fs.f->lastlinedefined = ls->linenumber;
   check_match(ls, TK_END, TK_FUNCTION, line);
@@ -954,9 +1023,9 @@ static void simpleexp (LexState *ls, expdesc *v) {
     }
     case TK_DOTS: {  /* vararg */
       FuncState *fs = ls->fs;
-      check_condition(ls, fs->f->is_vararg,
+      check_condition(ls, fs->f->is_vararg & LUA_VADECL,
                       "cannot use '...' outside a vararg function");
-      fs->f->is_vararg = 1;  /* function actually uses vararg */
+      fs->f->is_vararg |= LUA_VAUSED;  /* function actually uses vararg */
       init_exp(v, VVARARG, luaK_codeABC(fs, OP_VARARG, 0, 1, 0));
       break;
     }
@@ -1611,7 +1680,7 @@ static void mainfunc (LexState *ls, FuncState *fs) {
   BlockCnt bl;
   expdesc v;
   open_func(ls, fs, &bl);
-  fs->f->is_vararg = 2;  /* main function is always declared vararg */
+  fs->f->is_vararg |= LUA_VADECL;  /* main function is always declared vararg */
   init_exp(&v, VLOCAL, 0);  /* create and... */
   newupvalue(fs, ls->envn, &v);  /* ...set environment upvalue */
   luaX_next(ls);  /* read first token */