lua-users home
lua-l archive

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


it should also be noted that the Lua specification states that "you should never change the value of the control variable: The effect of such changes is unpredictable. If you want to break a forloop before its normal termination, use break."

What this means is that changing this value "may or may not" have a side effect on the number of loops executed: a compiler may validly create an iterator from (a) to (b) inclusively, and then the enumerator will return values into the local variable "x", ignoring and overwriting any previously assigned value inside the loop.

So the code "for x=1,3 do x=x+.5; print(x, ' ') end" may print either:
- "1.5 2.5 3.5" (using a prebuilt iterator and ignoring changes to the control variable, i.e. using a stateless loop) or
- "1.5 3" (taking the current value of the control variable into account, i.e. using a stateful loop).

As Lua is elusive about the expected behavior, the compiler should emit a strong warning for such assignments to local control variables (I think that compilers should behave the best using the first behavior using a stateless loop).

My opinion is that Lua should have been defined so that only the first (stateless) behavior is valid, and so "for x=(a),(b) do (...) end" should always behave like "for x in foriter(a, b) do (...) end"


Le jeu. 15 nov. 2018 à 13:57, Philippe Verdy <verdy_p@wanadoo.fr> a écrit :
Le jeu. 15 nov. 2018 à 12:54, Philippe Verdy <verdy_p@wanadoo.fr> a écrit :
Writing  "for x = (a),(b) do (...) end" is only syntaxic sugar for: "do local x = (a); while x < (b) do (...); x = x + 1 end end"
More exactly it is the equivalent of:  "do local x, __end__ = (a), (b); while x <=   __end__   do (...); x = x + 1 end end"
because (b) is evaluated only once before the first checking of the condition and then entering the first loop (there was also a missing "=" in the condition)

A compiler may check the type of the value (a) assigned to the control variable (x), if it is known (constant), but then it must:
* coerce the value (b) assigned to "__end__" to the same datatype using ceiling (not floor);
* check that the coercion of (b) in "local x,__end__ = (a),coerce(b)" does not cause an overflow (if this occurs, the loop cannot be optimized using integers, it has to use a double floatting point for the control variable...);
* coerce the incrementation "x = x + 1", as "x =  successor(coerce(x))", if (a) was an integer type, so that the condition "x <= __end__" remains correct;
* check the possible integer overflow of this incrementation (if overflow occurs, this must break the loop immediately: an overflow can occur here only if "successor(__end__)" cannot be warrantied to be inside the valid range of the chosen datatype, or if the current value of "x" which was changed in the middle of the loop is outside the inclusive integer range from (a) to (b));
* compile the code inside the loop so that any reference to "x" will "uncoerce" its chosen integer type to the full range of "number" when needed (notably when using the value of "x" in function calls, but as well in simple expressions like "x*x"...)

A compiler may also want to unwind such loops if the code inside the loop is small, and if the difference between (a) and (b) is known (both (a) and (b) are constants) and small (generally not more than 4, but this may depend on the code size inside the loop; this is complex to do if that Lua code inside the loop contains other execution controls, requiring the generation of labels for generated jumps into the generated opcodes, or if the compiler makes special optimizations for exception handling, i.e. to optimize invokations in Lua code to "pcall(function,...)").