From be05c444818989463dc307eed283503d391f93eb Mon Sep 17 00:00:00 2001 From: Roberto Ierusalimschy Date: Tue, 20 May 2025 17:36:05 -0300 Subject: New way to control preambular declaration Validity of the preambular global declaration in controled together with all declarations, when checking variable names. --- lparser.c | 33 ++++++++++++++++++++------------- lparser.h | 3 +++ testes/db.lua | 6 +++++- testes/goto.lua | 18 +++++++++++++++++- 4 files changed, 45 insertions(+), 15 deletions(-) diff --git a/lparser.c b/lparser.c index c134b7a8..e868e887 100644 --- a/lparser.c +++ b/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 diff --git a/lparser.h b/lparser.h index 524df6ea..b08008ce 100644 --- a/lparser.h +++ b/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 { diff --git a/testes/db.lua b/testes/db.lua index ae204c41..0f174f17 100644 --- a/testes/db.lua +++ b/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 assert local x,y = debug.getlocal(1,5) assert(x == 'B' and y == 13) end diff --git a/testes/goto.lua b/testes/goto.lua index d7730061..7e40fc4f 100644 --- a/testes/goto.lua +++ b/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 *; + Y = 1 -- ERROR + ]], "assign to const variable 'Y'") + + global * + Y = 10 + assert(_ENV.Y == 10) + global * + local x = Y + global * + Y = x + Y + assert(_ENV.Y == 20) + end print'OK' -- cgit v1.2.3-55-g6feb