lua-users home
lua-l archive

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

A local <const> variable is not necessary initialized with a simple constant value, it may be initialized by the value of a complex _expression_ that needs to be evaluated only once, and its value stored somewhere.
Imagine you declare thousands of such local <const> variables, all these initializations must still be evaluated, independently of if the resulting value is used or not in the rest of the function.

The compiler may then try to optimize the code, by not storing the resulting values in a local variable, if the value can be evaluated as a real simple constant whose value is known at compiling time. In that case, it can use that known value inside the code and will not need to generate a local variable for the storage of this value

Only in that case then, the variable should not be counted in the limit of 200 local variables (including local variables added for declared function parameters; and including also the "self" local variable implicitly declared for functions declared with a colon-prefix to the function name when using the magic sugar of method-like function declarations, which are in fact assignment statements into the key of a table external to the function itself).

Now imagine the compiler accepts thousands of such "local <const>" variable declarations in a function body. It will not know when parsing them if these variables are used or not: it will know that only after parsing the whole function body. So it could only emit the error about the quota being exhausted and the final "end" keyword of the function body, or when anywhere during the parsing of the body the compiler detects the 200th local variable being actually used.

A "use" of the local variable must be counted for a function named parameters or "self" along with the implicit "use" of all named parameters. For example:
    function (a,b) return b end
Only the second parameter "b" is explicitly used, but parameter "a" is still used implicitly by the fact that it uses storage in the stack. But note that you can call a function with of to 242 arguments (even if they are not named, by using varargs, so that you can write:
    print(1,2,3,4, --[[...], 241, 242)
but not:
    print(1,2,3,4, --[[...], 241, 242, 243)
which generates a parsing error for a too complex _expression_: there's also a limit on the number of values you can push on the call stack before calling the function.
However you cannot declare an argument name in the function body for all these 242 allowed arguments, only for the 200 first arguments at most; arguments number 201 to 242 can only be used by the function in vararg expressions; if you have declared 200 arguments for a function, you're not allowed to declare any additional local variable in the function body, but you can use arguments 201 to 242 !

Le sam. 11 sept. 2021 à 03:19, Egor Skriptunoff <> a écrit :

After the <const> attribute was introduced in Lua I was wondering:
What mistakes the <const> attribute may protect us from?
I don't remember such a situation when <const> (if it existed) would help me detect a mistake in my code.
IMO, when <const> attribute is used as intended (to mark variables which never change their values after initialization), it just makes code harder to write, harder to read, and does not bring any benefits.

But it seems that in some scenarios local <const> variables might be useful.
This is due to Lua bytecode optimization that <const> is involved in.
Simple example:

   local A <const> = "aaaaa"
   local B <const> = "bbbbb"

Lua optimizes this script by removing unused constant B.
And the string "bbbbb" is also removed from bytecode (debug info should be stripped).

My idea is to define a lot of constants to let the user select only a few constants he needs.
Due to optimization, unused constants will be removed, and the compiled chunk will be tiny.


   local VK_LBUTTON <const> = 0x01  -- Left Mouse Button
   local VK_RBUTTON <const> = 0x02  -- Right Mouse Button
   local VK_BACK    <const> = 0x08  -- Backspace
   local VK_TAB     <const> = 0x09  -- Tab
   local VK_RETURN  <const> = 0x0D  -- Enter
   local VK_SHIFT   <const> = 0x10  -- Shift
   local VK_CONTROL <const> = 0x11  -- Ctrl
   local VK_MENU    <const> = 0x12  -- Alt
   local VK_ESCAPE  <const> = 0x1B  -- Esc
   local VK_SPACE   <const> = 0x20  -- Space
   local VK_A       <const> = 0x41  -- A key
   local VK_B       <const> = 0x42  -- B key
   (there are more than 200 virtual-key codes in the full list)
   -- nothing should be changed above this line
   actions.jump.keys = {VK_W, VK_CONTROL} = {VK_SPACE, VK_LBUTTON}
   actions.use.keys = {VK_RETURN, VK_RBUTTON}

But this solution does not compile: the number of constants exceeds the quota for local variables (200).

A local <const> variable which holds a simple constant (number or string) does not occupy a VM register.
It would be good if such variables do not count in local variables quota.
This way we would be able to define thousands of constants.