From 9b37a4695ebf50b37b5b4fb279ae948f23b5b6a0 Mon Sep 17 00:00:00 2001 From: Roberto Ierusalimschy Date: Tue, 19 Mar 2019 10:53:18 -0300 Subject: [PATCH] New semantics for the integer 'for' loop The numerical 'for' loop over integers now uses a precomputed counter to control its number of iteractions. This change eliminates several weird cases caused by overflows (wrap-around) in the control variable. (It also ensures that every integer loop halts.) Also, the special opcodes for the usual case of step==1 were removed. (The new code is already somewhat complex for the usual case, but efficient.) --- ljumptab.h | 2 - lopcodes.c | 2 - lopcodes.h | 4 -- lopnames.h | 2 - lparser.c | 42 +++++-------- lvm.c | 147 +++++++++++++++++++++++---------------------- manual/manual.of | 120 ++++++++++++++++-------------------- testes/code.lua | 4 +- testes/db.lua | 2 +- testes/nextvar.lua | 73 ++++++++++++++++++++-- 10 files changed, 213 insertions(+), 185 deletions(-) diff --git a/ljumptab.h b/ljumptab.h index 9fa72a73..fa4277cc 100644 --- a/ljumptab.h +++ b/ljumptab.h @@ -99,8 +99,6 @@ static void *disptab[NUM_OPCODES] = { &&L_OP_RETURN, &&L_OP_RETURN0, &&L_OP_RETURN1, -&&L_OP_FORLOOP1, -&&L_OP_FORPREP1, &&L_OP_FORLOOP, &&L_OP_FORPREP, &&L_OP_TFORPREP, diff --git a/lopcodes.c b/lopcodes.c index 3f0d551a..c35a0aaf 100644 --- a/lopcodes.c +++ b/lopcodes.c @@ -93,8 +93,6 @@ LUAI_DDEF const lu_byte luaP_opmodes[NUM_OPCODES] = { ,opmode(0, 1, 0, 0, iABC) /* OP_RETURN */ ,opmode(0, 0, 0, 0, iABC) /* OP_RETURN0 */ ,opmode(0, 0, 0, 0, iABC) /* OP_RETURN1 */ - ,opmode(0, 0, 0, 1, iABx) /* OP_FORLOOP1 */ - ,opmode(0, 0, 0, 1, iABx) /* OP_FORPREP1 */ ,opmode(0, 0, 0, 1, iABx) /* OP_FORLOOP */ ,opmode(0, 0, 0, 1, iABx) /* OP_FORPREP */ ,opmode(0, 0, 0, 0, iABx) /* OP_TFORPREP */ diff --git a/lopcodes.h b/lopcodes.h index 3e100259..f867a01b 100644 --- a/lopcodes.h +++ b/lopcodes.h @@ -280,10 +280,6 @@ OP_RETURN,/* A B C return R(A), ... ,R(A+B-2) (see note) */ OP_RETURN0,/* return */ OP_RETURN1,/* A return R(A) */ -OP_FORLOOP1,/* A Bx R(A)++; - if R(A) <= R(A+1) then { pc-=Bx; R(A+3)=R(A) } */ -OP_FORPREP1,/* A Bx R(A)--; pc+=Bx */ - OP_FORLOOP,/* A Bx R(A)+=R(A+2); if R(A) fs, &e); lua_assert(e.k == VNONRELOC); - return res; } @@ -1403,31 +1399,29 @@ static void fixforjump (FuncState *fs, int pc, int dest, int back) { /* -** Generate code for a 'for' loop. 'kind' can be zero (a common for -** loop), one (a basic for loop, with integer values and increment of -** 1), or two (a generic for loop). +** Generate code for a 'for' loop. */ -static void forbody (LexState *ls, int base, int line, int nvars, int kind) { +static void forbody (LexState *ls, int base, int line, int nvars, int isgen) { /* forbody -> DO block */ - static OpCode forprep[3] = {OP_FORPREP, OP_FORPREP1, OP_TFORPREP}; - static OpCode forloop[3] = {OP_FORLOOP, OP_FORLOOP1, OP_TFORLOOP}; + static OpCode forprep[2] = {OP_FORPREP, OP_TFORPREP}; + static OpCode forloop[2] = {OP_FORLOOP, OP_TFORLOOP}; BlockCnt bl; FuncState *fs = ls->fs; int prep, endfor; checknext(ls, TK_DO); - prep = luaK_codeABx(fs, forprep[kind], base, 0); + prep = luaK_codeABx(fs, forprep[isgen], base, 0); enterblock(fs, &bl, 0); /* scope for declared variables */ adjustlocalvars(ls, nvars); luaK_reserveregs(fs, nvars); block(ls); leaveblock(fs); /* end of scope for declared variables */ fixforjump(fs, prep, luaK_getlabel(fs), 0); - if (kind == 2) { /* generic for? */ + if (isgen) { /* generic for? */ luaK_codeABC(fs, OP_TFORCALL, base, 0, nvars); luaK_fixline(fs, line); base += 2; /* base for 'OP_TFORLOOP' (skips function and state) */ } - endfor = luaK_codeABx(fs, forloop[kind], base, 0); + endfor = luaK_codeABx(fs, forloop[isgen], base, 0); fixforjump(fs, endfor, prep + 1, 1); luaK_fixline(fs, line); } @@ -1437,26 +1431,22 @@ static void fornum (LexState *ls, TString *varname, int line) { /* fornum -> NAME = exp,exp[,exp] forbody */ FuncState *fs = ls->fs; int base = fs->freereg; - int basicfor = 1; /* true if it is a "basic" 'for' (integer + 1) */ new_localvarliteral(ls, "(for index)"); new_localvarliteral(ls, "(for limit)"); new_localvarliteral(ls, "(for step)"); new_localvar(ls, varname); checknext(ls, '='); - if (!exp1(ls, 0)) /* initial value not an integer? */ - basicfor = 0; /* not a basic 'for' */ + exp1(ls); /* initial value */ checknext(ls, ','); - exp1(ls, 0); /* limit */ - if (testnext(ls, ',')) { - if (!exp1(ls, 1)) /* optional step not 1? */ - basicfor = 0; /* not a basic 'for' */ - } + exp1(ls); /* limit */ + if (testnext(ls, ',')) + exp1(ls); /* optional step */ else { /* default step = 1 */ luaK_int(fs, fs->freereg, 1); luaK_reserveregs(fs, 1); } adjustlocalvars(ls, 3); /* control variables */ - forbody(ls, base, line, 1, basicfor); + forbody(ls, base, line, 1, 0); } @@ -1484,7 +1474,7 @@ static void forlist (LexState *ls, TString *indexname) { adjust_assign(ls, 4, explist(ls, &e), &e); adjustlocalvars(ls, 4); /* control variables */ luaK_checkstack(fs, 3); /* extra space to call generator */ - forbody(ls, base, line, nvars - 4, 2); + forbody(ls, base, line, nvars - 4, 1); } @@ -1633,7 +1623,7 @@ static void tocloselocalstat (LexState *ls) { luaO_pushfstring(ls->L, "unknown attribute '%s'", getstr(attr))); new_localvar(ls, str_checkname(ls)); checknext(ls, '='); - exp1(ls, 0); + exp1(ls); markupval(fs, fs->nactvar); fs->bl->insidetbc = 1; /* in the scope of a to-be-closed variable */ adjustlocalvars(ls, 1); diff --git a/lvm.c b/lvm.c index 23e7ff70..75b05f00 100644 --- a/lvm.c +++ b/lvm.c @@ -148,35 +148,34 @@ int luaV_tointeger (const TValue *obj, lua_Integer *p, int mode) { /* ** Try to convert a 'for' limit to an integer, preserving the semantics -** of the loop. (The following explanation assumes a non-negative step; +** of the loop. (The following explanation assumes a positive step; ** it is valid for negative steps mutatis mutandis.) ** If the limit is an integer or can be converted to an integer, ** rounding down, that is it. -** Otherwise, check whether the limit can be converted to a float. If -** the number is too large, it is OK to set the limit as LUA_MAXINTEGER, -** which means no limit. If the number is too negative, the loop -** should not run, because any initial integer value is larger than the -** limit. So, it sets the limit to LUA_MININTEGER. 'stopnow' corrects -** the extreme case when the initial value is LUA_MININTEGER, in which -** case the LUA_MININTEGER limit would still run the loop once. +** Otherwise, check whether the limit can be converted to a float. If +** the float is too large, clip it to LUA_MAXINTEGER. If the float +** is too negative, the loop should not run, because any initial +** integer value is greater than such limit; so, it sets 'stopnow'. +** (For this latter case, no integer limit would be correct; even a +** limit of LUA_MININTEGER would run the loop once for an initial +** value equal to LUA_MININTEGER.) */ -static int forlimit (const TValue *obj, lua_Integer *p, lua_Integer step, +static int forlimit (const TValue *lim, lua_Integer *p, lua_Integer step, int *stopnow) { *stopnow = 0; /* usually, let loops run */ - if (ttisinteger(obj)) - *p = ivalue(obj); - else if (!luaV_tointeger(obj, p, (step < 0 ? 2 : 1))) { + if (!luaV_tointeger(lim, p, (step < 0 ? 2 : 1))) { /* not coercible to in integer */ - lua_Number n; /* try to convert to float */ - if (!tonumber(obj, &n)) /* cannot convert to float? */ + lua_Number flim; /* try to convert to float */ + if (!tonumber(lim, &flim)) /* cannot convert to float? */ return 0; /* not a number */ - if (luai_numlt(0, n)) { /* if true, float is larger than max integer */ - *p = LUA_MAXINTEGER; - if (step < 0) *stopnow = 1; + /* 'flim' is a float out of integer bounds */ + if (luai_numlt(0, flim)) { /* if it is positive, it is too large */ + *p = LUA_MAXINTEGER; /* truncate */ + if (step < 0) *stopnow = 1; /* initial value must be less than it */ } - else { /* float is less than min integer */ - *p = LUA_MININTEGER; - if (step >= 0) *stopnow = 1; + else { /* it is less than min integer */ + *p = LUA_MININTEGER; /* truncate */ + if (step > 0) *stopnow = 1; /* initial value must be greater than it */ } } return 1; @@ -1636,85 +1635,87 @@ void luaV_execute (lua_State *L, CallInfo *ci) { } return; } - vmcase(OP_FORLOOP1) { - lua_Integer idx = intop(+, ivalue(s2v(ra)), 1); /* increment index */ - lua_Integer limit = ivalue(s2v(ra + 1)); - if (idx <= limit) { - pc -= GETARG_Bx(i); /* jump back */ - chgivalue(s2v(ra), idx); /* update internal index... */ - setivalue(s2v(ra + 3), idx); /* ...and external index */ - } - updatetrap(ci); - vmbreak; - } - vmcase(OP_FORPREP1) { - TValue *init = s2v(ra); - TValue *plimit = s2v(ra + 1); - lua_Integer ilimit, initv; - int stopnow; - if (unlikely(!forlimit(plimit, &ilimit, 1, &stopnow))) { - savestate(L, ci); /* for the error message */ - luaG_forerror(L, plimit, "limit"); - } - initv = (stopnow ? 0 : ivalue(init)); - setivalue(plimit, ilimit); - setivalue(init, intop(-, initv, 1)); - pc += GETARG_Bx(i); - vmbreak; - } vmcase(OP_FORLOOP) { - if (ttisinteger(s2v(ra))) { /* integer loop? */ - lua_Integer step = ivalue(s2v(ra + 2)); - lua_Integer idx = intop(+, ivalue(s2v(ra)), step); /* new index */ - lua_Integer limit = ivalue(s2v(ra + 1)); - if ((0 < step) ? (idx <= limit) : (limit <= idx)) { + if (ttisinteger(s2v(ra + 2))) { /* integer loop? */ + lua_Unsigned count = l_castS2U(ivalue(s2v(ra))); + if (count > 0) { /* still more iterations? */ + lua_Integer step = ivalue(s2v(ra + 2)); + lua_Integer idx = ivalue(s2v(ra + 3)); + idx = intop(+, idx, step); /* add step to index */ + chgivalue(s2v(ra), count - 1); /* update counter... */ + setivalue(s2v(ra + 3), idx); /* ...and index */ pc -= GETARG_Bx(i); /* jump back */ - chgivalue(s2v(ra), idx); /* update internal index... */ - setivalue(s2v(ra + 3), idx); /* ...and external index */ } } else { /* floating loop */ lua_Number step = fltvalue(s2v(ra + 2)); lua_Number limit = fltvalue(s2v(ra + 1)); - lua_Number idx = fltvalue(s2v(ra)); + lua_Number idx = fltvalue(s2v(ra + 3)); idx = luai_numadd(L, idx, step); /* inc. index */ if (luai_numlt(0, step) ? luai_numle(idx, limit) : luai_numle(limit, idx)) { + setfltvalue(s2v(ra + 3), idx); /* update index */ pc -= GETARG_Bx(i); /* jump back */ - chgfltvalue(s2v(ra), idx); /* update internal index... */ - setfltvalue(s2v(ra + 3), idx); /* ...and external index */ } } - updatetrap(ci); + updatetrap(ci); /* allows a signal to break the loop */ vmbreak; } vmcase(OP_FORPREP) { - TValue *init = s2v(ra); + TValue *pinit = s2v(ra); TValue *plimit = s2v(ra + 1); TValue *pstep = s2v(ra + 2); lua_Integer ilimit; int stopnow; - if (ttisinteger(init) && ttisinteger(pstep) && + savestate(L, ci); /* in case of errors */ + if (ttisinteger(pinit) && ttisinteger(pstep) && forlimit(plimit, &ilimit, ivalue(pstep), &stopnow)) { - /* all values are integer */ - lua_Integer initv = (stopnow ? 0 : ivalue(init)); - setivalue(plimit, ilimit); - setivalue(init, intop(-, initv, ivalue(pstep))); + /* integer loop */ + lua_Integer init = ivalue(pinit); + lua_Integer step = ivalue(pstep); + setivalue(s2v(ra + 3), init); /* control variable */ + if (step == 0) + luaG_runerror(L, "'for' step is zero"); + else if (stopnow) + pc += GETARG_Bx(i) + 1; /* skip the loop */ + else if (step > 0) { /* ascending loop? */ + if (init > ilimit) + pc += GETARG_Bx(i) + 1; /* skip the loop */ + else { + lua_Unsigned count = l_castS2U(ilimit) - l_castS2U(init); + if (step != 1) /* avoid division in the too common case */ + count /= l_castS2U(step); + setivalue(s2v(ra), count); + } + } + else { /* descending loop */ + if (init < ilimit) + pc += GETARG_Bx(i) + 1; /* skip the loop */ + else { + lua_Unsigned count = l_castS2U(init) - l_castS2U(ilimit); + count /= -l_castS2U(step); + setivalue(s2v(ra), count); + } + } } else { /* try making all values floats */ - lua_Number ninit; lua_Number nlimit; lua_Number nstep; - savestate(L, ci); /* in case of errors */ - if (unlikely(!tonumber(plimit, &nlimit))) + lua_Number init; lua_Number flimit; lua_Number step; + if (unlikely(!tonumber(plimit, &flimit))) luaG_forerror(L, plimit, "limit"); - setfltvalue(plimit, nlimit); - if (unlikely(!tonumber(pstep, &nstep))) + setfltvalue(plimit, flimit); + if (unlikely(!tonumber(pstep, &step))) luaG_forerror(L, pstep, "step"); - setfltvalue(pstep, nstep); - if (unlikely(!tonumber(init, &ninit))) - luaG_forerror(L, init, "initial value"); - setfltvalue(init, luai_numsub(L, ninit, nstep)); + setfltvalue(pstep, step); + if (unlikely(!tonumber(pinit, &init))) + luaG_forerror(L, pinit, "initial value"); + if (step == 0) + luaG_runerror(L, "'for' step is zero"); + if (luai_numlt(0, step) ? luai_numlt(flimit, init) + : luai_numlt(init, flimit)) + pc += GETARG_Bx(i) + 1; /* skip the loop */ + else + setfltvalue(s2v(ra + 3), init); /* control variable */ } - pc += GETARG_Bx(i); vmbreak; } vmcase(OP_TFORPREP) { diff --git a/manual/manual.of b/manual/manual.of index 8a8ebad5..b7ced443 100644 --- a/manual/manual.of +++ b/manual/manual.of @@ -594,7 +594,7 @@ controls how long the collector waits before starting a new cycle. The collector starts a new cycle when the use of memory hits @M{n%} of the use after the previous collection. Larger values make the collector less aggressive. -Values smaller than 100 mean the collector will not wait to +Values less than 100 mean the collector will not wait to start a new cycle. A value of 200 means that the collector waits for the total memory in use to double before starting a new cycle. @@ -608,7 +608,7 @@ how many elements it marks or sweeps for each kilobyte of memory allocated. Larger values make the collector more aggressive but also increase the size of each incremental step. -You should not use values smaller than 100, +You should not use values less than 100, because they make the collector too slow and can result in the collector never finishing a cycle. The default value is 100; the maximum value is 1000. @@ -1004,7 +1004,7 @@ the escape sequence @T{\u{@rep{XXX}}} (note the mandatory enclosing brackets), where @rep{XXX} is a sequence of one or more hexadecimal digits representing the character code point. -This code point can be any value smaller than @M{2@sp{31}}. +This code point can be any value less than @M{2@sp{31}}. (Lua uses the original UTF-8 specification here.) Literal strings can also be defined using a long format @@ -1370,74 +1370,50 @@ because now @Rw{return} is the last statement in its (inner) block. The @Rw{for} statement has two forms: one numerical and one generic. +@sect4{@title{The numerical @Rw{for} loop} + The numerical @Rw{for} loop repeats a block of code while a -control variable runs through an arithmetic progression. +control variable goes through an arithmetic progression. It has the following syntax: @Produc{ @producname{stat}@producbody{@Rw{for} @bnfNter{Name} @bnfter{=} exp @bnfter{,} exp @bnfopt{@bnfter{,} exp} @Rw{do} block @Rw{end}} } -The @emph{block} is repeated for @emph{name} starting at the value of -the first @emph{exp}, until it passes the second @emph{exp} by steps of the -third @emph{exp}. -More precisely, a @Rw{for} statement like -@verbatim{ -for v = @rep{e1}, @rep{e2}, @rep{e3} do @rep{block} end -} -is equivalent to the code: -@verbatim{ -do - local @rep{var}, @rep{limit}, @rep{step} = tonumber(@rep{e1}), tonumber(@rep{e2}), tonumber(@rep{e3}) - if not (@rep{var} and @rep{limit} and @rep{step}) then error() end - @rep{var} = @rep{var} - @rep{step} - while true do - @rep{var} = @rep{var} + @rep{step} - if (@rep{step} >= 0 and @rep{var} > @rep{limit}) or (@rep{step} < 0 and @rep{var} < @rep{limit}) then - break - end - local v = @rep{var} - @rep{block} - end -end -} +The given identifier (@bnfNter{Name}) defines the control variable, +which is local to the loop body (@emph{block}). -Note the following: -@itemize{ +The loop starts by evaluating once the three control expressions; +they must all result in numbers. +Their values are called respectively +the @emph{initial value}, the @emph{limit}, and the @emph{step}. +If the step is absent, it defaults @N{to 1}. +Then the loop body is repeated with the value of the control variable +going through an arithmetic progression, +starting at the initial value, +with a common difference given by the step, +until that value passes the limit. +A negative step makes a decreasing sequence; +a step equal to zero raises an error. +If the initial value is already greater than the limit +(or less than, if the step is negative), the body is not executed. -@item{ -All three control expressions are evaluated only once, -before the loop starts. -They must all result in numbers. -} +If both the initial value and the step are integers, +the loop is done with integers; +in this case, the range of the control variable is limited +by the range of integers. +Otherwise, the loop is done with floats. +(Beware of floating-point accuracy in this case.) -@item{ -@T{@rep{var}}, @T{@rep{limit}}, and @T{@rep{step}} are invisible variables. -The names shown here are for explanatory purposes only. -} - -@item{ -If the third expression (the step) is absent, -then a step @N{of 1} is used. -} - -@item{ -You can use @Rw{break} and @Rw{goto} to exit a @Rw{for} loop. -} - -@item{ -The loop variable @T{v} is local to the loop body. +You should not change the value of the control variable +during the loop. If you need its value after the loop, assign it to another variable before exiting the loop. -} - -@item{ -The values in @rep{var}, @rep{limit}, and @rep{step} -can be integers or floats. -All operations on them respect the usual rules in Lua. -} } +@sect4{@title{The generic @Rw{for} loop} + + The generic @Rw{for} statement works over functions, called @def{iterators}. On each iteration, the iterator function is called to produce a new value, @@ -1499,6 +1475,8 @@ then assign them to other variables before breaking or exiting the loop. } +} + @sect3{funcstat| @title{Function Calls as Statements} To allow possible side-effects, function calls can be executed as statements: @@ -1819,7 +1797,7 @@ A comparison @T{a > b} is translated to @T{b < a} and @T{a >= b} is translated to @T{b <= a}. Following the @x{IEEE 754} standard, -@x{NaN} is considered neither smaller than, +@x{NaN} is considered neither less than, nor equal to, nor greater than any value (including itself). } @@ -2171,7 +2149,7 @@ then the function returns with no results. @index{multiple return} There is a system-dependent limit on the number of values that a function may return. -This limit is guaranteed to be larger than 1000. +This limit is guaranteed to be greater than 1000. The @emphx{colon} syntax is used for defining @def{methods}, @@ -2367,7 +2345,7 @@ but it also can be any positive index after the stack top within the space allocated for the stack, that is, indices up to the stack size. (Note that 0 is never an acceptable index.) -Indices to upvalues @see{c-closure} larger than the real number +Indices to upvalues @see{c-closure} greater than the real number of upvalues in the current @N{C function} are also acceptable (but invalid). Except when noted otherwise, functions in the API work with acceptable indices. @@ -2879,7 +2857,7 @@ Ensures that the stack has space for at least @id{n} extra slots (that is, that you can safely push up to @id{n} values into it). It returns false if it cannot fulfill the request, either because it would cause the stack -to be larger than a fixed maximum size +to be greater than a fixed maximum size (typically at least several thousand elements) or because it cannot allocate memory for the extra space. This function never shrinks the stack; @@ -4053,7 +4031,7 @@ for the @Q{newindex} event @see{metatable}. Accepts any index, @N{or 0}, and sets the stack top to this index. -If the new top is larger than the old one, +If the new top is greater than the old one, then the new elements are filled with @nil. If @id{index} @N{is 0}, then all stack elements are removed. @@ -5056,7 +5034,7 @@ size @id{sz} with a call @T{luaL_buffinitsize(L, &b, sz)}.} @item{ Finish by calling @T{luaL_pushresultsize(&b, sz)}, where @id{sz} is the total size of the resulting string -copied into that space (which may be smaller than or +copied into that space (which may be less than or equal to the preallocated size). } @@ -7336,7 +7314,7 @@ Functions that interpret byte sequences only accept valid sequences (well formed and not overlong). By default, they only accept byte sequences that result in valid Unicode code points, -rejecting values larger than @T{10FFFF} and surrogates. +rejecting values greater than @T{10FFFF} and surrogates. A boolean argument @id{nonstrict}, when available, lifts these checks, so that all values up to @T{0x7FFFFFFF} are accepted. @@ -7572,7 +7550,7 @@ returns the arc tangent of @id{y}. @LibEntry{math.ceil (x)| -Returns the smallest integral value larger than or equal to @id{x}. +Returns the smallest integral value greater than or equal to @id{x}. } @@ -7597,7 +7575,7 @@ Returns the value @M{e@sp{x}} @LibEntry{math.floor (x)| -Returns the largest integral value smaller than or equal to @id{x}. +Returns the largest integral value less than or equal to @id{x}. } @@ -7611,7 +7589,7 @@ that rounds the quotient towards zero. (integer/float) @LibEntry{math.huge| The float value @idx{HUGE_VAL}, -a value larger than any other numeric value. +a value greater than any other numeric value. } @@ -8352,7 +8330,7 @@ of the given thread: @N{level 1} is the function that called @id{getinfo} (except for tail calls, which do not count on the stack); and so on. -If @id{f} is a number larger than the number of active functions, +If @id{f} is a number greater than the number of active functions, then @id{getinfo} returns @nil. The returned table can contain all the fields returned by @Lid{lua_getinfo}, @@ -8745,6 +8723,12 @@ has been removed. When needed, this metamethod must be explicitly defined. } +@item{ +The semantics of the numerical @Rw{for} loop +over integers changed in some details. +In particular, the control variable never wraps around. +} + @item{ When a coroutine finishes with an error, its stack is unwound (to run any pending closing methods). diff --git a/testes/code.lua b/testes/code.lua index 834ff5e2..128ca2cb 100644 --- a/testes/code.lua +++ b/testes/code.lua @@ -303,9 +303,9 @@ check(function (x) return x & 2.0 end, 'LOADF', 'BAND', 'RETURN1') -- basic 'for' loops check(function () for i = -10, 10.5 do end end, -'LOADI', 'LOADK', 'LOADI', 'FORPREP1', 'FORLOOP1', 'RETURN0') +'LOADI', 'LOADK', 'LOADI', 'FORPREP', 'FORLOOP', 'RETURN0') check(function () for i = 0xfffffff, 10.0, 1 do end end, -'LOADK', 'LOADF', 'LOADI', 'FORPREP1', 'FORLOOP1', 'RETURN0') +'LOADK', 'LOADF', 'LOADI', 'FORPREP', 'FORLOOP', 'RETURN0') -- bug in constant folding for 5.1 check(function () return -nil end, 'LOADNIL', 'UNM', 'RETURN1') diff --git a/testes/db.lua b/testes/db.lua index 976962b0..0858dd20 100644 --- a/testes/db.lua +++ b/testes/db.lua @@ -162,7 +162,7 @@ test([[for i,v in pairs{'a','b'} do end ]], {1,2,1,2,1,3}) -test([[for i=1,4 do a=1 end]], {1,1,1,1,1}) +test([[for i=1,4 do a=1 end]], {1,1,1,1}, true) do -- testing line info/trace with large gaps in source diff --git a/testes/nextvar.lua b/testes/nextvar.lua index d2306ed1..e769ccdd 100644 --- a/testes/nextvar.lua +++ b/testes/nextvar.lua @@ -76,7 +76,7 @@ while a < lim do a = math.ceil(a*1.3) end - + local function check (t, na, nh) local a, h = T.querytab(t) if a ~= na or h ~= nh then @@ -100,7 +100,7 @@ local s = 'return {' for i=1,lim do s = s..i..',' local s = s - for k=0,lim do + for k=0,lim do local t = load(s..'}', '')() assert(#t == i) check(t, fb(i), mp2(k)) @@ -279,7 +279,7 @@ do -- clear global table end --- +-- local function checknext (a) local b = {} @@ -500,7 +500,7 @@ else --[ mt.__newindex = nil mt.__len = nil local tab2 = {} - local u2 = T.newuserdata(0) + local u2 = T.newuserdata(0) debug.setmetatable(u2, {__newindex = function (_, k, v) tab2[k] = v end}) table.move(u, 1, 4, 1, u2) assert(#tab2 == 4 and tab2[1] == tab[1] and tab2[4] == tab[4]) @@ -601,6 +601,69 @@ do -- checking types end + +do -- testing other strange cases for numeric 'for' + + local function checkfor (from, to, step, t) + local c = 0 + for i = from, to, step do + c = c + 1 + assert(i == t[c]) + end + assert(c == #t) + end + + local maxi = math.maxinteger + local mini = math.mininteger + + checkfor(mini, maxi, maxi, {mini, -1, maxi - 1}) + + checkfor(mini, math.huge, maxi, {mini, -1, maxi - 1}) + + checkfor(maxi, mini, mini, {maxi, -1}) + + checkfor(maxi, mini, -maxi, {maxi, 0, -maxi}) + + checkfor(maxi, -math.huge, mini, {maxi, -1}) + + checkfor(maxi, mini, 1, {}) + checkfor(mini, maxi, -1, {}) + + checkfor(maxi - 6, maxi, 3, {maxi - 6, maxi - 3, maxi}) + checkfor(mini + 4, mini, -2, {mini + 4, mini + 2, mini}) + + local step = maxi // 10 + local c = mini + for i = mini, maxi, step do + assert(i == c) + c = c + step + end + + c = maxi + for i = maxi, mini, -step do + assert(i == c) + c = c - step + end + + checkfor(maxi, maxi, maxi, {maxi}) + checkfor(maxi, maxi, mini, {maxi}) + checkfor(mini, mini, maxi, {mini}) + checkfor(mini, mini, mini, {mini}) +end + + +checkerror("'for' step is zero", function () + for i = 1, 10, 0 do end +end) + +checkerror("'for' step is zero", function () + for i = 1, -10, 0 do end +end) + +checkerror("'for' step is zero", function () + for i = 1.0, -10, 0.0 do end +end) + collectgarbage() @@ -657,7 +720,7 @@ a[3] = 30 -- testing ipairs with metamethods a = {n=10} setmetatable(a, { __index = function (t,k) - if k <= t.n then return k * 10 end + if k <= t.n then return k * 10 end end}) i = 0 for k,v in ipairs(a) do