aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRoberto I <roberto@inf.puc-rio.br>2025-09-24 18:33:08 -0300
committerRoberto I <roberto@inf.puc-rio.br>2025-09-24 18:33:08 -0300
commit25c54fe60e22d05cdfaa48c64372d354efa59547 (patch)
tree3ccaeded5e4363db358f73b7c8fc6b9f414a2f2a
parent0cc3c9447cca9abae9738ee77c24d88801c3916c (diff)
downloadlua-25c54fe60e22d05cdfaa48c64372d354efa59547.tar.gz
lua-25c54fe60e22d05cdfaa48c64372d354efa59547.tar.bz2
lua-25c54fe60e22d05cdfaa48c64372d354efa59547.zip
Optimization for vararg tables
A vararg table can be virtual. If the vararg table is used only as a base in indexing expressions, the code does not need to create an actual table for it. Instead, it compiles the indexing expressions into direct accesses to the internal vararg data.
Diffstat (limited to '')
-rw-r--r--lcode.c57
-rw-r--r--ljumptab.h3
-rw-r--r--lopcodes.c1
-rw-r--r--lopcodes.h2
-rw-r--r--lopnames.h1
-rw-r--r--lparser.c10
-rw-r--r--lparser.h2
-rw-r--r--ltm.c22
-rw-r--r--ltm.h1
-rw-r--r--lvm.c6
-rw-r--r--manual/manual.of91
-rw-r--r--testes/locals.lua6
-rw-r--r--testes/vararg.lua47
13 files changed, 186 insertions, 63 deletions
diff --git a/lcode.c b/lcode.c
index f74223eb..f7c2334c 100644
--- a/lcode.c
+++ b/lcode.c
@@ -842,6 +842,12 @@ void luaK_dischargevars (FuncState *fs, expdesc *e) {
842 e->k = VRELOC; 842 e->k = VRELOC;
843 break; 843 break;
844 } 844 }
845 case VVARGIND: {
846 freeregs(fs, e->u.ind.t, e->u.ind.idx);
847 e->u.info = luaK_codeABC(fs, OP_GETVARG, 0, e->u.ind.t, e->u.ind.idx);
848 e->k = VRELOC;
849 break;
850 }
845 case VVARARG: case VCALL: { 851 case VVARARG: case VCALL: {
846 luaK_setoneret(fs, e); 852 luaK_setoneret(fs, e);
847 break; 853 break;
@@ -1004,11 +1010,11 @@ int luaK_exp2anyreg (FuncState *fs, expdesc *e) {
1004 1010
1005 1011
1006/* 1012/*
1007** Ensures final expression result is either in a register 1013** Ensures final expression result is either in a register,
1008** or in an upvalue. 1014** in an upvalue, or it is the vararg parameter.
1009*/ 1015*/
1010void luaK_exp2anyregup (FuncState *fs, expdesc *e) { 1016void luaK_exp2anyregup (FuncState *fs, expdesc *e) {
1011 if (e->k != VUPVAL || hasjumps(e)) 1017 if ((e->k != VUPVAL && e->k != VVARGVAR) || hasjumps(e))
1012 luaK_exp2anyreg(fs, e); 1018 luaK_exp2anyreg(fs, e);
1013} 1019}
1014 1020
@@ -1314,6 +1320,13 @@ void luaK_self (FuncState *fs, expdesc *e, expdesc *key) {
1314} 1320}
1315 1321
1316 1322
1323/* auxiliary function to define indexing expressions */
1324static void fillidxk (expdesc *t, int idx, expkind k) {
1325 t->u.ind.idx = cast_byte(idx);
1326 t->k = k;
1327}
1328
1329
1317/* 1330/*
1318** Create expression 't[k]'. 't' must have its final result already in a 1331** Create expression 't[k]'. 't' must have its final result already in a
1319** register or upvalue. Upvalues can only be indexed by literal strings. 1332** register or upvalue. Upvalues can only be indexed by literal strings.
@@ -1325,31 +1338,30 @@ void luaK_indexed (FuncState *fs, expdesc *t, expdesc *k) {
1325 if (k->k == VKSTR) 1338 if (k->k == VKSTR)
1326 keystr = str2K(fs, k); 1339 keystr = str2K(fs, k);
1327 lua_assert(!hasjumps(t) && 1340 lua_assert(!hasjumps(t) &&
1328 (t->k == VLOCAL || t->k == VNONRELOC || t->k == VUPVAL)); 1341 (t->k == VLOCAL || t->k == VVARGVAR ||
1342 t->k == VNONRELOC || t->k == VUPVAL));
1329 if (t->k == VUPVAL && !isKstr(fs, k)) /* upvalue indexed by non 'Kstr'? */ 1343 if (t->k == VUPVAL && !isKstr(fs, k)) /* upvalue indexed by non 'Kstr'? */
1330 luaK_exp2anyreg(fs, t); /* put it in a register */ 1344 luaK_exp2anyreg(fs, t); /* put it in a register */
1331 if (t->k == VUPVAL) { 1345 if (t->k == VUPVAL) {
1332 lu_byte temp = cast_byte(t->u.info); /* upvalue index */ 1346 lu_byte temp = cast_byte(t->u.info); /* upvalue index */
1333 t->u.ind.t = temp; /* (can't do a direct assignment; values overlap) */ 1347 t->u.ind.t = temp; /* (can't do a direct assignment; values overlap) */
1334 lua_assert(isKstr(fs, k)); 1348 lua_assert(isKstr(fs, k));
1335 t->u.ind.idx = cast_short(k->u.info); /* literal short string */ 1349 fillidxk(t, k->u.info, VINDEXUP); /* literal short string */
1336 t->k = VINDEXUP; 1350 }
1351 else if (t->k == VVARGVAR) { /* indexing the vararg parameter? */
1352 lua_assert(t->u.ind.t == fs->f->numparams);
1353 t->u.ind.t = cast_byte(t->u.var.ridx);
1354 fillidxk(t, luaK_exp2anyreg(fs, k), VVARGIND); /* register */
1337 } 1355 }
1338 else { 1356 else {
1339 /* register index of the table */ 1357 /* register index of the table */
1340 t->u.ind.t = cast_byte((t->k == VLOCAL) ? t->u.var.ridx: t->u.info); 1358 t->u.ind.t = cast_byte((t->k == VLOCAL) ? t->u.var.ridx: t->u.info);
1341 if (isKstr(fs, k)) { 1359 if (isKstr(fs, k))
1342 t->u.ind.idx = cast_short(k->u.info); /* literal short string */ 1360 fillidxk(t, k->u.info, VINDEXSTR); /* literal short string */
1343 t->k = VINDEXSTR; 1361 else if (isCint(k)) /* int. constant in proper range? */
1344 } 1362 fillidxk(t, cast_int(k->u.ival), VINDEXI);
1345 else if (isCint(k)) { /* int. constant in proper range? */ 1363 else
1346 t->u.ind.idx = cast_short(k->u.ival); 1364 fillidxk(t, luaK_exp2anyreg(fs, k), VINDEXED); /* register */
1347 t->k = VINDEXI;
1348 }
1349 else {
1350 t->u.ind.idx = cast_short(luaK_exp2anyreg(fs, k)); /* register */
1351 t->k = VINDEXED;
1352 }
1353 } 1365 }
1354 t->u.ind.keystr = keystr; /* string index in 'k' */ 1366 t->u.ind.keystr = keystr; /* string index in 'k' */
1355 t->u.ind.ro = 0; /* by default, not read-only */ 1367 t->u.ind.ro = 0; /* by default, not read-only */
@@ -1913,9 +1925,14 @@ void luaK_finish (FuncState *fs) {
1913 SETARG_C(*pc, p->numparams + 1); /* signal that it is vararg */ 1925 SETARG_C(*pc, p->numparams + 1); /* signal that it is vararg */
1914 break; 1926 break;
1915 } 1927 }
1916 case OP_JMP: { 1928 case OP_GETVARG: {
1929 if (p->flag & PF_VATAB) /* function has a vararg table? */
1930 SET_OPCODE(*pc, OP_GETTABLE); /* must get vararg there */
1931 break;
1932 }
1933 case OP_JMP: { /* to optimize jumps to jumps */
1917 int target = finaltarget(p->code, i); 1934 int target = finaltarget(p->code, i);
1918 fixjump(fs, i, target); 1935 fixjump(fs, i, target); /* jump directly to final target */
1919 break; 1936 break;
1920 } 1937 }
1921 default: break; 1938 default: break;
diff --git a/ljumptab.h b/ljumptab.h
index a24828bb..f896b658 100644
--- a/ljumptab.h
+++ b/ljumptab.h
@@ -21,7 +21,7 @@ static const void *const disptab[NUM_OPCODES] = {
21#if 0 21#if 0
22** you can update the following list with this command: 22** you can update the following list with this command:
23** 23**
24** sed -n '/^OP_/\!d; s/OP_/\&\&L_OP_/ ; s/,.*/,/ ; s/\/.*// ; p' lopcodes.h 24** sed -n '/^OP_/!d; s/OP_/\&\&L_OP_/ ; s/,.*/,/ ; s/\/.*// ; p' lopcodes.h
25** 25**
26#endif 26#endif
27 27
@@ -106,6 +106,7 @@ static const void *const disptab[NUM_OPCODES] = {
106&&L_OP_SETLIST, 106&&L_OP_SETLIST,
107&&L_OP_CLOSURE, 107&&L_OP_CLOSURE,
108&&L_OP_VARARG, 108&&L_OP_VARARG,
109&&L_OP_GETVARG,
109&&L_OP_VARARGPREP, 110&&L_OP_VARARGPREP,
110&&L_OP_EXTRAARG 111&&L_OP_EXTRAARG
111 112
diff --git a/lopcodes.c b/lopcodes.c
index 79ffbe25..47458e40 100644
--- a/lopcodes.c
+++ b/lopcodes.c
@@ -102,6 +102,7 @@ LUAI_DDEF const lu_byte luaP_opmodes[NUM_OPCODES] = {
102 ,opmode(0, 0, 1, 0, 0, ivABC) /* OP_SETLIST */ 102 ,opmode(0, 0, 1, 0, 0, ivABC) /* OP_SETLIST */
103 ,opmode(0, 0, 0, 0, 1, iABx) /* OP_CLOSURE */ 103 ,opmode(0, 0, 0, 0, 1, iABx) /* OP_CLOSURE */
104 ,opmode(0, 1, 0, 0, 1, iABC) /* OP_VARARG */ 104 ,opmode(0, 1, 0, 0, 1, iABC) /* OP_VARARG */
105 ,opmode(0, 0, 0, 0, 1, iABC) /* OP_GETVARG */
105 ,opmode(0, 0, 1, 0, 1, iABC) /* OP_VARARGPREP */ 106 ,opmode(0, 0, 1, 0, 1, iABC) /* OP_VARARGPREP */
106 ,opmode(0, 0, 0, 0, 0, iAx) /* OP_EXTRAARG */ 107 ,opmode(0, 0, 0, 0, 0, iAx) /* OP_EXTRAARG */
107}; 108};
diff --git a/lopcodes.h b/lopcodes.h
index c3f7f64d..82bba721 100644
--- a/lopcodes.h
+++ b/lopcodes.h
@@ -338,6 +338,8 @@ OP_CLOSURE,/* A Bx R[A] := closure(KPROTO[Bx]) */
338 338
339OP_VARARG,/* A C R[A], R[A+1], ..., R[A+C-2] = vararg */ 339OP_VARARG,/* A C R[A], R[A+1], ..., R[A+C-2] = vararg */
340 340
341OP_GETVARG, /* A B C R[A] := R[B][R[C]], R[B] is vararg parameter */
342
341OP_VARARGPREP,/* (adjust vararg parameters) */ 343OP_VARARGPREP,/* (adjust vararg parameters) */
342 344
343OP_EXTRAARG/* Ax extra (larger) argument for previous opcode */ 345OP_EXTRAARG/* Ax extra (larger) argument for previous opcode */
diff --git a/lopnames.h b/lopnames.h
index 39df332f..aa7bea77 100644
--- a/lopnames.h
+++ b/lopnames.h
@@ -94,6 +94,7 @@ static const char *const opnames[] = {
94 "SETLIST", 94 "SETLIST",
95 "CLOSURE", 95 "CLOSURE",
96 "VARARG", 96 "VARARG",
97 "GETVARG",
97 "VARARGPREP", 98 "VARARGPREP",
98 "EXTRAARG", 99 "EXTRAARG",
99 NULL 100 NULL
diff --git a/lparser.c b/lparser.c
index 8b909f3d..408b8e21 100644
--- a/lparser.c
+++ b/lparser.c
@@ -279,7 +279,9 @@ static void init_var (FuncState *fs, expdesc *e, int vidx) {
279 279
280 280
281/* 281/*
282** Raises an error if variable described by 'e' is read only 282** Raises an error if variable described by 'e' is read only; moreover,
283** if 'e' is t[exp] where t is the vararg parameter, change it to index
284** a real table. (Virtual vararg tables cannot be changed.)
283*/ 285*/
284static void check_readonly (LexState *ls, expdesc *e) { 286static void check_readonly (LexState *ls, expdesc *e) {
285 FuncState *fs = ls->fs; 287 FuncState *fs = ls->fs;
@@ -301,6 +303,10 @@ static void check_readonly (LexState *ls, expdesc *e) {
301 varname = up->name; 303 varname = up->name;
302 break; 304 break;
303 } 305 }
306 case VVARGIND: {
307 fs->f->flag |= PF_VATAB; /* function will need a vararg table */
308 e->k = VINDEXED;
309 } /* FALLTHROUGH */
304 case VINDEXUP: case VINDEXSTR: case VINDEXED: { /* global variable */ 310 case VINDEXUP: case VINDEXSTR: case VINDEXED: { /* global variable */
305 if (e->u.ind.ro) /* read-only? */ 311 if (e->u.ind.ro) /* read-only? */
306 varname = tsvalue(&fs->f->k[e->u.ind.keystr]); 312 varname = tsvalue(&fs->f->k[e->u.ind.keystr]);
@@ -1073,7 +1079,7 @@ static void parlist (LexState *ls) {
1073 case TK_DOTS: { 1079 case TK_DOTS: {
1074 varargk |= PF_ISVARARG; 1080 varargk |= PF_ISVARARG;
1075 luaX_next(ls); 1081 luaX_next(ls);
1076 if (testnext(ls, '=')) { 1082 if (testnext(ls, '|')) {
1077 new_varkind(ls, str_checkname(ls), RDKVAVAR); 1083 new_varkind(ls, str_checkname(ls), RDKVAVAR);
1078 varargk |= PF_VAVAR; 1084 varargk |= PF_VAVAR;
1079 } 1085 }
diff --git a/lparser.h b/lparser.h
index 327170e3..a30df04f 100644
--- a/lparser.h
+++ b/lparser.h
@@ -51,6 +51,8 @@ typedef enum {
51 ind.ro = true if it represents a read-only global; 51 ind.ro = true if it represents a read-only global;
52 ind.keystr = if key is a string, index in 'k' of that string; 52 ind.keystr = if key is a string, index in 'k' of that string;
53 -1 if key is not a string */ 53 -1 if key is not a string */
54 VVARGIND, /* indexed vararg parameter;
55 ind.* as in VINDEXED */
54 VINDEXUP, /* indexed upvalue; 56 VINDEXUP, /* indexed upvalue;
55 ind.idx = key's K index; 57 ind.idx = key's K index;
56 ind.* as in VINDEXED */ 58 ind.* as in VINDEXED */
diff --git a/ltm.c b/ltm.c
index cc812e62..92a03e71 100644
--- a/ltm.c
+++ b/ltm.c
@@ -277,6 +277,28 @@ void luaT_adjustvarargs (lua_State *L, CallInfo *ci, const Proto *p) {
277} 277}
278 278
279 279
280void luaT_getvararg (CallInfo *ci, StkId ra, TValue *rc) {
281 int nextra = ci->u.l.nextraargs;
282 lua_Integer n;
283 if (tointegerns(rc, &n)) { /* integral value? */
284 if (l_castS2U(n) - 1 < cast_uint(nextra)) {
285 StkId slot = ci->func.p - nextra + cast_int(n) - 1;
286 setobjs2s(((lua_State*)NULL), ra, slot);
287 return;
288 }
289 }
290 else if (ttisshrstring(rc)) { /* short-string value? */
291 size_t len;
292 const char *s = getlstr(tsvalue(rc), len);
293 if (len == 1 && s[0] == 'n') { /* key is "n"? */
294 setivalue(s2v(ra), nextra);
295 return;
296 }
297 }
298 setnilvalue(s2v(ra)); /* else produce nil */
299}
300
301
280void luaT_getvarargs (lua_State *L, CallInfo *ci, StkId where, int wanted) { 302void luaT_getvarargs (lua_State *L, CallInfo *ci, StkId where, int wanted) {
281 int i; 303 int i;
282 int nextra = ci->u.l.nextraargs; 304 int nextra = ci->u.l.nextraargs;
diff --git a/ltm.h b/ltm.h
index ed479bb4..86f457eb 100644
--- a/ltm.h
+++ b/ltm.h
@@ -97,6 +97,7 @@ LUAI_FUNC int luaT_callorderiTM (lua_State *L, const TValue *p1, int v2,
97 97
98LUAI_FUNC void luaT_adjustvarargs (lua_State *L, struct CallInfo *ci, 98LUAI_FUNC void luaT_adjustvarargs (lua_State *L, struct CallInfo *ci,
99 const Proto *p); 99 const Proto *p);
100LUAI_FUNC void luaT_getvararg (CallInfo *ci, StkId ra, TValue *rc);
100LUAI_FUNC void luaT_getvarargs (lua_State *L, struct CallInfo *ci, 101LUAI_FUNC void luaT_getvarargs (lua_State *L, struct CallInfo *ci,
101 StkId where, int wanted); 102 StkId where, int wanted);
102 103
diff --git a/lvm.c b/lvm.c
index d88a80d1..3ce7e87f 100644
--- a/lvm.c
+++ b/lvm.c
@@ -1926,6 +1926,12 @@ void luaV_execute (lua_State *L, CallInfo *ci) {
1926 Protect(luaT_getvarargs(L, ci, ra, n)); 1926 Protect(luaT_getvarargs(L, ci, ra, n));
1927 vmbreak; 1927 vmbreak;
1928 } 1928 }
1929 vmcase(OP_GETVARG) {
1930 StkId ra = RA(i);
1931 TValue *rc = vRC(i);
1932 luaT_getvararg(ci, ra, rc);
1933 vmbreak;
1934 }
1929 vmcase(OP_VARARGPREP) { 1935 vmcase(OP_VARARGPREP) {
1930 ProtectNT(luaT_adjustvarargs(L, ci, cl->p)); 1936 ProtectNT(luaT_adjustvarargs(L, ci, cl->p));
1931 if (l_unlikely(trap)) { /* previous "Protect" updated trap */ 1937 if (l_unlikely(trap)) { /* previous "Protect" updated trap */
diff --git a/manual/manual.of b/manual/manual.of
index 3c704118..beea41f9 100644
--- a/manual/manual.of
+++ b/manual/manual.of
@@ -2262,7 +2262,7 @@ return x or f(x) -- results adjusted to 1
2262 2262
2263@sect3{func-def| @title{Function Definitions} 2263@sect3{func-def| @title{Function Definitions}
2264 2264
2265The syntax for function definition is 2265The syntax for a function definition is
2266@Produc{ 2266@Produc{
2267@producname{functiondef}@producbody{@Rw{function} funcbody} 2267@producname{functiondef}@producbody{@Rw{function} funcbody}
2268@producname{funcbody}@producbody{@bnfter{(} @bnfopt{parlist} @bnfter{)} block @Rw{end}} 2268@producname{funcbody}@producbody{@bnfter{(} @bnfopt{parlist} @bnfter{)} block @Rw{end}}
@@ -2315,6 +2315,18 @@ translates to
2315global f; f = function () @rep{body} end 2315global f; f = function () @rep{body} end
2316} 2316}
2317 2317
2318The @emphx{colon} syntax
2319is used to emulate @def{methods},
2320adding an implicit extra parameter @idx{self} to the function.
2321Thus, the statement
2322@verbatim{
2323function t.a.b.c:f (@rep{params}) @rep{body} end
2324}
2325is syntactic sugar for
2326@verbatim{
2327t.a.b.c.f = function (self, @rep{params}) @rep{body} end
2328}
2329
2318A function definition is an executable expression, 2330A function definition is an executable expression,
2319whose value has type @emph{function}. 2331whose value has type @emph{function}.
2320When Lua precompiles a chunk, 2332When Lua precompiles a chunk,
@@ -2325,11 +2337,25 @@ the function is @emph{instantiated} (or @emph{closed}).
2325This function instance, or @emphx{closure}, 2337This function instance, or @emphx{closure},
2326is the final value of the expression. 2338is the final value of the expression.
2327 2339
2340Results are returned using the @Rw{return} statement @see{control}.
2341If control reaches the end of a function
2342without encountering a @Rw{return} statement,
2343then the function returns with no results.
2344
2345@index{multiple return}
2346There is a system-dependent limit on the number of values
2347that a function may return.
2348This limit is guaranteed to be at least 1000.
2349
2350@sect4{@title{Parameters}
2351
2328Parameters act as local variables that are 2352Parameters act as local variables that are
2329initialized with the argument values: 2353initialized with the argument values:
2330@Produc{ 2354@Produc{
2331@producname{parlist}@producbody{namelist @bnfopt{@bnfter{,} @bnfter{...}} @Or 2355@producname{parlist}@producbody{namelist @bnfopt{@bnfter{,} varargparam} @Or
2332 @bnfter{...}} 2356 varargparam}
2357@producname{varargparam}@producbody{@bnfter{...}
2358 @bnfopt{@bnfter{|} @bnfNter{Name}}}
2333} 2359}
2334When a Lua function is called, 2360When a Lua function is called,
2335it adjusts its list of @x{arguments} to 2361it adjusts its list of @x{arguments} to
@@ -2339,11 +2365,12 @@ which is indicated by three dots (@Char{...})
2339at the end of its parameter list. 2365at the end of its parameter list.
2340A variadic function does not adjust its argument list; 2366A variadic function does not adjust its argument list;
2341instead, it collects all extra arguments and supplies them 2367instead, it collects all extra arguments and supplies them
2342to the function through a @def{vararg expression}, 2368to the function through a @def{vararg expression} and,
2343which is also written as three dots. 2369if present, a @def{vararg table}.
2344The value of this expression is a list of all actual extra arguments,
2345similar to a function with multiple results @see{multires}.
2346 2370
2371A vararg expression is also written as three dots,
2372and its value is a list of all actual extra arguments,
2373similar to a function with multiple results @see{multires}.
2347 2374
2348As an example, consider the following definitions: 2375As an example, consider the following definitions:
2349@verbatim{ 2376@verbatim{
@@ -2368,26 +2395,27 @@ g(3, 4, 5, 8) a=3, b=4, ... -> 5 8
2368g(5, r()) a=5, b=1, ... -> 2 3 2395g(5, r()) a=5, b=1, ... -> 2 3
2369} 2396}
2370 2397
2371Results are returned using the @Rw{return} statement @see{control}. 2398The presence of a varag table in a variadic function is indicated
2372If control reaches the end of a function 2399by the @T{|name} syntax after the three dots.
2373without encountering a @Rw{return} statement, 2400When present,
2374then the function returns with no results. 2401a vararg table behaves like a read-only local variable
2375 2402with the given name that is initialized with a table.
2376@index{multiple return} 2403In that table,
2377There is a system-dependent limit on the number of values 2404the values at indices 1, 2, etc. are the extra arguments,
2378that a function may return. 2405and the value at index @St{n} is the number of extra arguments.
2379This limit is guaranteed to be at least 1000. 2406In other words, the code behaves as if the function started with
2380 2407the following statement,
2381The @emphx{colon} syntax 2408assuming the standard behavior of @Lid{table.pack}:
2382is used to emulate @def{methods},
2383adding an implicit extra parameter @idx{self} to the function.
2384Thus, the statement
2385@verbatim{ 2409@verbatim{
2386function t.a.b.c:f (@rep{params}) @rep{body} end 2410local <const> name = table.pack(...)
2387} 2411}
2388is syntactic sugar for 2412
2389@verbatim{ 2413As an optimization,
2390t.a.b.c.f = function (self, @rep{params}) @rep{body} end 2414if the vararg table is used only as a base in indexing expressions
2415(the @T{t} in @T{t[exp]} or @T{t.id}) and it is not an upvalue,
2416the code does not create an actual table and instead translates
2417the indexing expressions into accesses to the internal vararg data.
2418
2391} 2419}
2392 2420
2393} 2421}
@@ -2422,7 +2450,7 @@ for instance @T{foo(e1, e2, e3)} @see{functioncall}.}
2422for instance @T{a , b, c = e1, e2, e3} @see{assignment}.} 2450for instance @T{a , b, c = e1, e2, e3} @see{assignment}.}
2423 2451
2424@item{A local or global declaration, 2452@item{A local or global declaration,
2425which is a special case of multiple assignment.} 2453which is similar to a multiple assignment.}
2426 2454
2427@item{The initial values in a generic @rw{for} loop, 2455@item{The initial values in a generic @rw{for} loop,
2428for instance @T{for k in e1, e2, e3 do ... end} @see{for}.} 2456for instance @T{for k in e1, e2, e3 do ... end} @see{for}.}
@@ -3016,7 +3044,7 @@ typedef void * (*lua_Alloc) (void *ud,
3016 size_t osize, 3044 size_t osize,
3017 size_t nsize);| 3045 size_t nsize);|
3018 3046
3019The type of the @x{memory-allocation function} used by Lua states. 3047The type of the @x{memory-allocator function} used by Lua states.
3020The allocator function must provide a 3048The allocator function must provide a
3021functionality similar to @id{realloc}, 3049functionality similar to @id{realloc},
3022but not exactly the same. 3050but not exactly the same.
@@ -3482,7 +3510,7 @@ This function should not be called by a finalizer.
3482@APIEntry{lua_Alloc lua_getallocf (lua_State *L, void **ud);| 3510@APIEntry{lua_Alloc lua_getallocf (lua_State *L, void **ud);|
3483@apii{0,0,-} 3511@apii{0,0,-}
3484 3512
3485Returns the @x{memory-allocation function} of a given state. 3513Returns the @x{memory-allocator function} of a given state.
3486If @id{ud} is not @id{NULL}, Lua stores in @T{*ud} the 3514If @id{ud} is not @id{NULL}, Lua stores in @T{*ud} the
3487opaque pointer given when the memory-allocator function was set. 3515opaque pointer given when the memory-allocator function was set.
3488 3516
@@ -9732,8 +9760,11 @@ and @bnfNter{LiteralString}, see @See{lexical}.)
9732 9760
9733@producname{funcbody}@producbody{@bnfter{(} @bnfopt{parlist} @bnfter{)} block @Rw{end}} 9761@producname{funcbody}@producbody{@bnfter{(} @bnfopt{parlist} @bnfter{)} block @Rw{end}}
9734 9762
9735@producname{parlist}@producbody{namelist @bnfopt{@bnfter{,} @bnfter{...}} 9763@producname{parlist}@producbody{namelist @bnfopt{@bnfter{,} varargparam} @Or
9736 @Or @bnfter{...}} 9764 varargparam}
9765
9766@producname{varargparam}@producbody{@bnfter{...}
9767 @bnfopt{@bnfter{|} @bnfNter{Name}}}
9737 9768
9738@producname{tableconstructor}@producbody{@bnfter{@Open} @bnfopt{fieldlist} @bnfter{@Close}} 9769@producname{tableconstructor}@producbody{@bnfter{@Open} @bnfopt{fieldlist} @bnfter{@Close}}
9739 9770
diff --git a/testes/locals.lua b/testes/locals.lua
index 02f41980..5222802f 100644
--- a/testes/locals.lua
+++ b/testes/locals.lua
@@ -310,8 +310,7 @@ do -- testing presence of second argument
310 local function foo (howtoclose, obj, n) 310 local function foo (howtoclose, obj, n)
311 local ca -- copy of 'a' visible inside its close metamethod 311 local ca -- copy of 'a' visible inside its close metamethod
312 do 312 do
313 local a <close> = func2close(function (...) 313 local a <close> = func2close(function (... | t)
314 local t = table.pack(...)
315 assert(select("#", ...) == n) 314 assert(select("#", ...) == n)
316 assert(t.n == n and t[1] == ca and (t.n < 2 or t[2] == obj)) 315 assert(t.n == n and t[1] == ca and (t.n < 2 or t[2] == obj))
317 ca = 15 -- final value to be returned if howtoclose=="scope" 316 ca = 15 -- final value to be returned if howtoclose=="scope"
@@ -911,8 +910,7 @@ do
911 910
912 local extrares -- result from extra yield (if any) 911 local extrares -- result from extra yield (if any)
913 912
914 local function check (body, extra, ...) 913 local function check (body, extra, ...|t)
915 local t = table.pack(...) -- expected returns
916 local co = coroutine.wrap(body) 914 local co = coroutine.wrap(body)
917 if extra then 915 if extra then
918 extrares = co() -- runs until first (extra) yield 916 extrares = co() -- runs until first (extra) yield
diff --git a/testes/vararg.lua b/testes/vararg.lua
index 92f720cb..5711f78b 100644
--- a/testes/vararg.lua
+++ b/testes/vararg.lua
@@ -3,7 +3,7 @@
3 3
4print('testing vararg') 4print('testing vararg')
5 5
6local function f (a, ...=t) 6local function f (a, ...|t)
7 local x = {n = select('#', ...), ...} 7 local x = {n = select('#', ...), ...}
8 assert(x.n == t.n) 8 assert(x.n == t.n)
9 for i = 1, x.n do 9 for i = 1, x.n do
@@ -20,7 +20,7 @@ local function c12 (...)
20 return res, 2 20 return res, 2
21end 21end
22 22
23local function vararg (...=t) return t end 23local function vararg (... | t) return t end
24 24
25local call = function (f, args) return f(table.unpack(args, 1, args.n)) end 25local call = function (f, args) return f(table.unpack(args, 1, args.n)) end
26 26
@@ -153,8 +153,8 @@ end
153 153
154 154
155do -- vararg parameter used in nested functions 155do -- vararg parameter used in nested functions
156 local function foo (... = tab1) 156 local function foo (... | tab1)
157 return function (... = tab2) 157 return function (... | tab2)
158 return {tab1, tab2} 158 return {tab1, tab2}
159 end 159 end
160 end 160 end
@@ -165,16 +165,51 @@ do -- vararg parameter used in nested functions
165end 165end
166 166
167do -- vararg parameter is read-only 167do -- vararg parameter is read-only
168 local st, msg = load("return function (... = t) t = 10 end") 168 local st, msg = load("return function (... | t) t = 10 end")
169 assert(string.find(msg, "const variable 't'")) 169 assert(string.find(msg, "const variable 't'"))
170 170
171 local st, msg = load[[ 171 local st, msg = load[[
172 local function foo (... = extra) 172 local function foo (... | extra)
173 return function (...) extra = nil end 173 return function (...) extra = nil end
174 end 174 end
175 ]] 175 ]]
176 assert(string.find(msg, "const variable 'extra'")) 176 assert(string.find(msg, "const variable 'extra'"))
177end 177end
178 178
179
180do -- _ENV as vararg parameter
181 local st, msg = load[[
182 local function aux (... | _ENV)
183 global <const> a
184 a = 10
185 end ]]
186 assert(string.find(msg, "const variable 'a'"))
187end
188
189
190do -- access to vararg parameter
191 local function notab (keys, t, ... | v)
192 for _, k in pairs(keys) do
193 assert(t[k] == v[k])
194 end
195 assert(t.n == v.n)
196 end
197
198 local t = table.pack(10, 20, 30)
199 local keys = {-1, 0, 1, t.n, t.n + 1, 1.0, 1.1, "n", print, "k", "1"}
200 notab(keys, t, 10, 20, 30) -- ensure stack space
201 local m = collectgarbage"count"
202 notab(keys, t, 10, 20, 30)
203 -- 'notab' does not create any table/object
204 assert(m == collectgarbage"count")
205
206 -- writing to the vararg table
207 local function foo (... | t)
208 t[1] = t[1] + 10
209 return t[1]
210 end
211 assert(foo(10, 30) == 20)
212end
213
179print('OK') 214print('OK')
180 215