1
0
mirror of https://github.com/lua/lua.git synced 2025-01-14 05:43:00 +08:00

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.)
This commit is contained in:
Roberto Ierusalimschy 2019-03-19 10:53:18 -03:00
parent 1e0c73d5b6
commit 9b37a4695e
10 changed files with 213 additions and 185 deletions

View File

@ -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,

View File

@ -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 */

View File

@ -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) <?= R(A+1) then { pc-=Bx; R(A+3)=R(A) } */
OP_FORPREP,/* A Bx R(A)-=R(A+2); pc+=Bx */

View File

@ -84,8 +84,6 @@ static const char *const opnames[] = {
"RETURN",
"RETURN0",
"RETURN1",
"FORLOOP1",
"FORPREP1",
"FORLOOP",
"FORPREP",
"TFORPREP",

View File

@ -1371,18 +1371,14 @@ static void repeatstat (LexState *ls, int line) {
/*
** Read an expression and generate code to put its results in next
** stack slot. Return true if expression is a constant integer and,
** if 'i' is not-zero, its value is equal to 'i'.
** stack slot.
**
*/
static int exp1 (LexState *ls, int i) {
static void exp1 (LexState *ls) {
expdesc e;
int res;
expr(ls, &e);
res = luaK_isKint(&e) && (i == 0 || i == e.u.ival);
luaK_exp2nextreg(ls->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);

143
lvm.c
View File

@ -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.
** 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? */
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 = intop(+, ivalue(s2v(ra)), step); /* new index */
lua_Integer limit = ivalue(s2v(ra + 1));
if ((0 < step) ? (idx <= limit) : (limit <= idx)) {
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) {

View File

@ -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).

View File

@ -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')

View File

@ -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

View File

@ -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()