Why set the control variable of this for loop to read-only in Lua5.5?

456 views
Skip to first unread message

Cheese

unread,
Dec 23, 2025, 4:18:01 AM12/23/25
to lua-l
Dear maintainers of the Lua project
I cannot understand why you made the variable looped by the for loop read-only in Lua 5.5.

In other language like java:
I can do

```java
for (int i=0; i<10; i++) {
   // some logic
   if (xxx) {
        i += 2; // skip 3 loop
    }
}
```

But in lua5.5

```lua
j=0
for i=1,10 do
  if j>0 then
    goto continue
  end
  -- some logic
  if xxx then
    j=2
  end
  ::continue::
end
```

I need to introduce another local variable to skip 2 loops.

But in lua5.4 or older

for i=1,10 do
  if xxx then
    i=i+2
  end
  ::continue::
end

I think this restriction is completely unnecessary.
In many programming languages, we can modify the control variables in the loop.

Xmilia Hermit

unread,
Dec 23, 2025, 4:23:21 AM12/23/25
to lu...@googlegroups.com
Cheese:
> But in lua5.4 or older
>
> for i=1,10 do
>   if xxx then
>     i=i+2
>   end
>   ::continue::
> end

Because this code does not what you want. It does not change the
iteration variable. This is just syntactic sugar for

local private_i = 1
while private_i <= 10 do
local i = private_i
if xxx then
i=i+2
end
private_i = private_i + 1
end

so your update on i is only in the current loop. And to make this clear
it was changed.

Regards,
Xmilia

Luiz Henrique de Figueiredo

unread,
Dec 23, 2025, 4:46:26 AM12/23/25
to lu...@googlegroups.com
> I cannot understand why you made the variable looped by the for loop read-only in Lua 5.5.

It's been like that since Lua 5.1.
Take this program:

for i=1,10 do
print(i)
if i==4 then i=7 end
end

Here is its output in Lua 4 and Lua 5:

-- Lua 4.0.1
-- Lua 5.0.3
1
2
3
4
8
9
10
-- Lua 5.1.5
-- Lua 5.2.4
-- Lua 5.3.6
-- Lua 5.4.8
1
2
3
4
5
6
7
8
9
10
-- Lua 5.5.0
i:3: attempt to assign to const variable 'i'

Spar

unread,
Dec 23, 2025, 4:55:03 AM12/23/25
to lu...@googlegroups.com
For the historical background, why was it shadowed in 5.1?
--
You received this message because you are subscribed to the Google Groups "lua-l" group.
To unsubscribe from this group and stop receiving emails from it, send an email to lua-l+un...@googlegroups.com.
To view this discussion visit https://groups.google.com/d/msgid/lua-l/CABt16q%3DOeM5FDZcQJtD4bkcvAbDFs-ReD1EySzt6S3vA7%3D1Cfg%40mail.gmail.com.

Philippe Verdy

unread,
Dec 23, 2025, 6:13:22 AM12/23/25
to lu...@googlegroups.com
That's a stupid decision: it still creates an additional (hidden) local variable, but in the wrong (outer) lexical scope, instead of the inner scope of the loop statement itself. So this does not save anything.

The for loop:
```for i=1,10 do
  if xxx then
    i=i+2
  end
  ::continue::
end```

should not be at all (using hidden variable j with the incorrect scope):

```j=0
for i=1,10 do
  if j>0 then
    goto continue
  end
  -- some logic
  if xxx then
    j=2
  end
  ::continue::
end````

but should have been equivalent to this syntaxic sugar equivalent:

```do
  local i=1 // loop-control variable initializer (first parameter of the 'for' statement)
  while i < 10 do // 
      -- some logic
      if xxx then
        i = i + 2 // skip 3 loops
      end
  end
  ::continue::
  i = i + 1 // implicit increment (third parameter of the 'for' statement)
end```

(and no need in fact to introduce a label pseudo statement '::continue::' to make it work)

For the compiler this just means creating a lexical scope (like the outer do... end above) to embed the for statement. And it matches exactly what other OO languages like Java or C++ are doing, without needing to complexify the source (and in fact the first equivalent using an extra variable j is just wrong, it is not equivalent at all (it does NOT skip 3 loops, once the variable j is set to 2, it remains equal to 2 and still performs the loops using goto continue each time till the end of the 10 loops, so it can skip up to 10 loops, not just 3)

--
You received this message because you are subscribed to the Google Groups "lua-l" group.
To unsubscribe from this group and stop receiving emails from it, send an email to lua-l+un...@googlegroups.com.

Sewbacca

unread,
Dec 23, 2025, 7:41:59 AM12/23/25
to lu...@googlegroups.com
I wouldn't call this decision stupid, but I agree with the core of the sentiment.

for loops had this IMO usefull quirk that you could change the iteration variable without changing the loop structure.

You could argue that it was misleading and making it constant now will show unintentional bugs in your code, but it will also break intentional uses of this quirk. And every implementation detail that stays around for long enough will eventually become part of the API.

If we break scripts anyways, why not leverage this change and allow us to modify the iteration variable instead? Change it to what it should have been since 5.1: a way to control the for loop during execution.

There's however another reason I want to mention here:
for loops are guaranteed to break, except if one limit is math.huge (not sure about NaN). That is even if you get an arithmetic over or underflow, the loop will still break preventing an infinite loop. Granted this happens irregularly, but if it happens it could be a devastating bug.

~ Sewbacca

ImagicTheCat

unread,
Dec 23, 2025, 8:32:25 AM12/23/25
to lu...@googlegroups.com
Hi,

> for loops had this IMO usefull quirk that you could change the iteration variable without changing the loop structure.

Take my opinion with a grain of salt as I almost exclusively use
LuaJIT and I am not a professional developer, but I don't see it as a
quirk. I have been bitten in the past by the scoping rules of
JavaScript when creating closures in for-loops and I was glad to
discover that Lua did it right (IMO): all loop variables are just new
locals for the scope of the current iteration. So, I personally see
this read-only constraint as a regression from an elegant design,
maybe to handle a (common?) misunderstanding that I never really
experienced (maybe once). (And when there is a need for more control,
I will just use a while-loop.)

-- Imagic

Augusto Goulart

unread,
Dec 23, 2025, 9:40:02 AM12/23/25
to lu...@googlegroups.com
I’d just like to say this discussion started with a misunderstanding of the purpose of for/while loops. If you know start, end, steps: for; if you must check a condition: while. And just because you CAN do it in this or that language doesn’t mean you SHOULD do it. Just because you can do for(;;ch=getchar()) in C does not mean you should do it.

Augusto

--
You received this message because you are subscribed to the Google Groups "lua-l" group.
To unsubscribe from this group and stop receiving emails from it, send an email to lua-l+un...@googlegroups.com.

Roberto Ierusalimschy

unread,
Dec 23, 2025, 10:30:49 AM12/23/25
to lu...@googlegroups.com
> For the historical background, why was it shadowed in 5.1?

I am not sure that was the reason in 5.1, but at least for more recent
versions that semantics allows a slightly more efficient implementation.

A for loop does not know, in general, whether the step is positive
or negative at compile time. So it would have to check that at each
step. What Lua does, instead, is that it computes once, when it
enters the loop, how many steps the loop will run. After that, each
iteration only decrements that count and checks for zero, which is
quite efficient. However, that implies that the value of the so-called
"control variable" does not control anything after the loop starts.

-- Roberto

Roberto Ierusalimschy

unread,
Dec 23, 2025, 10:36:17 AM12/23/25
to lu...@googlegroups.com
BTW, making the variable read-only also allows a small optimization, as
the code does not need to keep an extra copy of the control variable.

-- Roberto

Francisco Olarte

unread,
Dec 23, 2025, 12:32:25 PM12/23/25
to lu...@googlegroups.com
On Tue, 23 Dec 2025 at 10:18, Cheese <justlike...@gmail.com> wrote:
Dear maintainers of the Lua project
I cannot understand why you made the variable looped by the for loop read-only in Lua 5.5.
....
I think this restriction is completely unnecessary.
In many programming languages, we can modify the control variables in the loop.

And in many others you can not. The restriction makes the special properties ( since 5.1 ) of the control variable explicit and helps avoid mistakes. Letting you modify it and then forgetting modifications can lead to puzzling behaviour. It is unnecessary, but it will probably be a lurking mousetrap even for me. 

I like it better this way. As said, just local i=i gets you the 5.4 behaviour. So if I typo-modify by mistake I get an error, if I see the telling local i=i I know I have to be extra careful.

Not having a generic loop makes the language simpler. Lua goes for that in many places. And emulating it with a while, and a surrounding block if you want c++/java declaration, is simple for people who need it. And the resulting code is many times easier, I have lost count of the times I have started with a generic pointer chasing or similar generic for loop, had to modify control variables inside, leaned back and refactored it to a while for my own sake on future maintenance work.

Francisco Olarte.

Cheese

unread,
Dec 24, 2025, 7:21:47 AM12/24/25
to lua-l
I am sorry that I forget it. Thank you all for your replies. I hope lua will get better and better in the future. Thank you so much for taking the time to reply despite your busy schedules. I now understand the change😉.
QQ20251224-201437.png

Marc Balmer

unread,
Dec 24, 2025, 8:00:34 AM12/24/25
to lu...@googlegroups.com, lua-l


> I hope lua will get better and better in the future.

While everything gets better, Lua remains good.

Philippe Verdy

unread,
Dec 24, 2025, 10:52:00 AM12/24/25
to lu...@googlegroups.com
Le mer. 24 déc. 2025 à 13:21, Cheese <justlike...@gmail.com> a écrit :
I am sorry that I forget it. Thank you all for your replies. I hope lua will get better and better in the future. Thank you so much for taking the time to reply despite your busy schedules. I now understand the change😉.
QQ20251224-201437.png
在2025年12月24日星期三 UTC+8 01:32:25<Francisco Olarte> 写道:
On Tue, 23 Dec 2025 at 10:18, Cheese <justlike...@gmail.com> wrote:
Dear maintainers of the Lua project
I cannot understand why you made the variable looped by the for loop read-only in Lua 5.5.
....
I think this restriction is completely unnecessary.
In many programming languages, we can modify the control variables in the loop.

And in many others you can not. The restriction makes the special properties ( since 5.1 ) of the control variable explicit and helps avoid mistakes. Letting you modify it and then forgetting modifications can lead to puzzling behaviour. It is unnecessary, but it will probably be a lurking mousetrap even for me. 

I like it better this way. As said, just local i=i gets you the 5.4 behaviour.

It does not allow changing the number of loops, notably to skip some steps in the given example, Many data parsers (notably with variable-length structrures, or many cases were you need to support backtracking) require this.

And even with the "justification" give nthat the loop is "warrantied" to break, this is false, this has never been defined like this and not even been tested (e.g. with overflows/underflows), so this argument just assumes a specific undefined behavior. If you want to warranty that the loop will end, this still needs to be checked explicitly before starting any loops (even if you don't ever modify the control variable inside the loop).

My opinion is that this new requirement was made only to allow some hardware-specific optimizations in some Lua implementations (e.g. allowing them to unroll some loops to process multiple items at the same time or in parallel), but these optimizations are unsafe (and it is tricky to make it safe, e.g. to respect things like memory address alignment and use vector extensions, and also ensure that this does not create other risks such as side-channel time attacks which may occur when using some optimistic branch prediction, and is impossible to protest from is there's support in the Language for memory fences and for strict isolation of resources usage, between distinct security realms: Lua has no mechanism for that, it is running inside a single system thread and offers no control on its own process face to other concurrent processes, or concurrent threads that may be used and not necessarily running a Lua engine, e.g. a webserver worker thread, or some I/O worker service).

If such optimimisation were intended, we don't know for which platform this was done, and for which intent (which means that this is also undefined behavior), but it seemed to work for that unkown intent and there's no way to check it. The internal compiler may already be very unsafe and actually are too poorly developed to support that, or it would have requried extensive work to obtain that goal and the initial implementation would not have met the goal before a long time and costly development of this uunknown specific Lua VM implementation.

Pan

unread,
Dec 24, 2025, 12:19:37 PM12/24/25
to lu...@googlegroups.com
I think a better example exists given that, at least
when it comes to the numeric for, you're usually much better off using a
while/repeat or a good step value if you will change it after. BUT
there is a common pattern that exists and takes the control vars
locality/mutability into account and uses it for its advantage.


for m in file:gmatch(general_pattern) do
m = m:match(actual_pattern)
if m then
...
end
end


This is extremely common given the limitations of lua patterns, the fix
is simple, but annoying, especially if you're just golfing.


I suppose building a tool that inserts a "local <control_var>" after
all for loops is necessary, it being hopefully smart enough to examine
the for loop block and inserting local <control_var> only if the
control_var is reassigned in the block on all lua files, actually, why
wasn't that an option?

Martin Eden

unread,
Dec 25, 2025, 6:41:06 AM12/25/25
to lu...@googlegroups.com
On 2025-12-24 17:51, Philippe Verdy wrote:
> It does not allow changing the number of loops, notably to skip some steps
> in the given example, Many data parsers (notably with variable-length
> structrures, or many cases were you need to support backtracking) require
> this.

Hello Philippe (and merry Christmas). I disagree,

"for" is just a specialized "while" (which is specialized "go to")

Main virtue of "for" is predictability

When I see "for" in Lua's code I can rely that margins are calculated
just once (unlike C's "for" macro). I can count that there will be no
hanky-panky stuff with index variable inside. That's good.

I feel I wrote enough parsers (Lua, PkZip, JSON, Csv) and nowhere used
"for" with index changes. It's always "while", maybe with some "goto's"
in old code.

Still one time I was lazy in adding support for custom case and tried to
increment index in "for" processing loop to skip next iteration. Stupid
mistake.


Side thought: most cases are "for 1, f()"

What if we have "repeat <N> do ... end" construction. Will it make code
better?

On 2025-12-24 17:51, Philippe Verdy wrote:
> My opinion is that this new requirement was made only to allow some
> hardware-specific optimizations in some Lua implementations (e.g. allowing
> them to unroll some loops to process multiple items at the same time or in
> parallel), but these optimizations are unsafe [...]

Speaking of security, control variable mangling in "for" is much more
practical and thus more dangerous thing than side-channel attacks from
godlike hackers. I mean damage from pride/ignorance is much more common
than premeditated attack.


On 2025-12-24 17:51, Philippe Verdy wrote:
> so this argument just assumes a specific undefined behavior

Then it's "defined behavior" ;)


-- Martin

Sean Conner

unread,
Dec 25, 2025, 4:39:06 PM12/25/25
to 'Martin Eden' via lua-l
It was thus said that the Great 'Martin Eden' via lua-l once stated:
> Side thought: most cases are "for 1, f()"
>
> What if we have "repeat <N> do ... end" construction. Will it make code
> better?

Some musings while dinner is cooking.

I came across a language decades ago (in a magazine article, so no direct
experience) that used '!' as a looping mechanism, and was used to replace
for, while, do while and several other variations. I'll use a slight
variation to make the examples cleaner, with the following conventions:

i - integer scalar
x - integer scalar
f - boolean scalar
a - array of values

A loop repeated 10 times:

{!10 <code> !}

There is no control varible to mangle. If you wanted a variable number of
iterations:

{!x <code> !}

as long as x >= 1, it would loop x times. Again, no control variable to
mangle---x could change in the code block, but its use as a limit was done.
If you wanted a control variable:

{!i=x <code> !}

Each time <code> was executed, i would be successive values. The language I
read about didn't mention if i was read-only (I did read about this in the
early 80s, times were different then) but I suspect today it would be
read-only. Then there is:

{!x=a <code> !}

where x is successive values from the a array---no index value though. If
you wanted an index (and the original language didn't include this), maybe:

{!i,x=a <code> !}

But so far, all the examples have been replacing a for-loop. A while-loop
would look like:

{!x<50 <code> !}

One can do a repeat-until loop as:

{! <code> x>50!}

and of course, both can be used:

{!x<50 <code> f!}

And even a for-loop start with a until clause at the end:

{!100 <code> f!}

for a loop that executes up to 100 times, or until f is true, whatever
happens first.

I've always liked this idea for looping [1], but never got around to
implementing a language with it, nor have I ever seen an implemetation of
the language that inspired this idea. I know what I described will probably
never show up in Lua, but it was fun to muse about it.

-spc

[1] The language itself had no key words in it. All syntatical
constructs are done via symbols; "!" for loops or "?" for ifs, that
type of thing. Fun to think about.

David Given

unread,
Dec 25, 2025, 5:23:14 PM12/25/25
to lu...@googlegroups.com
Algol-68 has a single loop structure:

[ FOR index ] [ FROM first ] [ BY increment ] [ TO last ] [ WHILE condition ] [ UNTIL condition ] DO statements OD

...where all clauses can be used together, and all of them are optional (except DO...OD). So an infinite loop is:

DO ... OD

A traditional for loop is:

FOR i FROM 1 TO 10 DO ... OD

A traditional while loop is:

WHILE i != 10 DO ... OD

But you can combine them if you want a FOR loop with an exit clause:

FOR i FROM 1 TO 10 WHILE running == TRUE DO ... OD

Or omit bits if you don't want an iterator variable:

FROM 1 TO 10 DO ... OD

I believe that BY 2 WHILE ... and FOR i WHILE ... don't do anything useful but are valid, but it's been a while.



--
You received this message because you are subscribed to the Google Groups "lua-l" group.
To unsubscribe from this group and stop receiving emails from it, send an email to lua-l+un...@googlegroups.com.

Gé Weijers

unread,
Dec 26, 2025, 5:04:21 AM12/26/25
to lu...@googlegroups.com
On Thu, Dec 25, 2025 at 2:23 PM David Given <d...@cowlark.com> wrote:
Algol-68 has a single loop structure:

[ FOR index ] [ FROM first ] [ BY increment ] [ TO last ] [ WHILE condition ] [ UNTIL condition ] DO statements OD

There is no 'UNTIL' in the Algol 68 loop clause. See section 17.5.1 in the Revised Report.
The "Algol68 Genie" compiler has one, but it's an extension that's not found in the language definition. You can write a post-checked loop as:

WHILE
  statement;
  statement;
  condition
DO SKIP OD

In the early '80s I spent about 4-5 semesters as a teacher's assistant on programming courses using Algol 68. One of the authors of the Revised Report (Prof. C. H. A. Koster) was my master's thesis advisor. I still have my print copy of the "REVISED REPORT ON THE ALGORITHMIC LANGUAGE ALGOL 68".

--


Matěj Cepl

unread,
Dec 31, 2025, 5:29:15 AM12/31/25
to lu...@googlegroups.com
>> But in lua5.4 or older
>>
>> for i=1,10 do
>> if xxx then
>> i=i+2
>> end
>> ::continue::
>> end
>>
>> I think this restriction is completely unnecessary.
>> In many programming languages, we can modify the control variables in
>> the loop.

And yes, in many programming languages it is as tight as in Lua 5.5.

My experience is that the whole `for i=1,10` loop is a code smell [1],
carried around by C programmers, who don’t know any better. Yes, you may
be in one of those very rare situations, when the use of such construct
is necessary, but more likely you haven’t thought about your algorithms
deep enough, and you would be much better using some for-in construct.
These days, whenever I start to write `for i=1,10` (or more likely these
days `for i in range(n)` in Python), I stop and think whether
I shouldn’t use something else entirely. Nine of ten cases I found out
that I was just lazy and that for-in cycle (or even while) leads to much
cleaner and easier to understand code.

Best,

Matěj

[1] https://wiki.c2.com/?CodeSmell
--
http://matej.ceplovi.cz/blog/, @mc...@en.osm.town
GPG Finger: 3C76 A027 CA45 AD70 98B5 BC1D 7920 5802 880B C9D8

We are told that [St. Anthony] once fell into dejection, finding
uninterrupted contemplation above his strength; but was taught to
apply himself at intervals to manual labour by a vision of an
angel who appeared platting mats of palm-tree leaves, then rising
to pray, and after some time sitting down again to work; and who
at length said to him, “Do thus, and thou shalt be saved.”
-- Life of St. Anthony

E09FEF25D96484AC.asc
signature.asc

Zdeněk Pavlas

unread,
Dec 31, 2025, 5:47:39 AM12/31/25
to lu...@googlegroups.com
> `for i=1,10` loop is a code smell

No, it isn't. It's the most natural way to initialize e.g. sets or permutations. Ofc you can wrap it in some fancy library and avoid using it directly, but that does not make it "wrong" by any means.

Dne st 31. 12. 2025 11:29 uživatel Matěj Cepl <mc...@cepl.eu> napsal:
--
You received this message because you are subscribed to the Google Groups "lua-l" group.
To unsubscribe from this group and stop receiving emails from it, send an email to lua-l+un...@googlegroups.com.

Sewbacca

unread,
Dec 31, 2025, 5:52:45 AM12/31/25
to lu...@googlegroups.com
I disagree, there are quite a few legitimate examples, i.e. structured arrays or removing elements from an array during a reversed search and the most important one is speed. Except for the last one you could write an iterator wrapping that behavior, but a numeric for loop is more often than not the less bug prone variant, unless you use the idiom a lot.

dev1 dev2

unread,
Jan 14, 2026, 5:12:01 AM (7 days ago) Jan 14
to lua-l
There is plenty of code which is using _ (underscore) as a loop key and then as a placeholder for a return value of some function. Now such code is broken. Of course, one could rename some of the variables, but it will also mean calming a linter to not complain about an unused variable. How would you suggest to deal with such cases, is it possible to prevent code editing?

for _, v in pairs({}) do
    local err
    _, err = some_func() -- @:3: attempt to assign to const variable '_'
end

Thanks for the great work.

Luiz Henrique de Figueiredo

unread,
Jan 14, 2026, 8:04:33 AM (7 days ago) Jan 14
to lu...@googlegroups.com
The usual idiom is
    local _, err = some_func()


--
You received this message because you are subscribed to the Google Groups "lua-l" group.
To unsubscribe from this group and stop receiving emails from it, send an email to lua-l+un...@googlegroups.com.

dev1 dev2

unread,
Jan 15, 2026, 7:00:52 AM (6 days ago) Jan 15
to lua-l
> The usual idiom is
>    local _, err = some_func()
On practice it is handy to not redefine variables like err every time, and in general I rather speak about already written and tested code in hundreds of files.

In addition, the way of adding a duplicated local variable, proposed in https://groups.google.com/g/lua-l/c/SlAG5QfpTac, triggers a linter
for s in ... do
    local s = s -- variable 's' was previously defined as a loop variable on line 122
    ...
end

So far, I came up with a couple of simple mechanical changes, which on practice don't change the logic and don't trigger the linter
- adding `local _` basically the beginning of the the loop body. Luckily it does not trigger the linter, because it's fine to shadow _, since it's very common.
- changing the first variable to something rarely used and using the old real name as a local variable. For example,
for cflcv, value in ... do
    local key = cflcv -- where cflcv refers to const_for_loop_control_variable, cflcv is not exactly what I use, but similar
end


I also think about code written by clients of our software. Such breaking change does not look valuable enough and at the same time is going to be quite frequent... Could somebody perhaps suggest a backward compatibility patch for Lua?
I have not looked into details yet. Just found the commit https://github.com/lua/lua/commit/b2f7b3b79f3117885b265575f6c5dbf934757797 and wonder whether it would be sufficient to simply change the type from RDKCONST to VDKREG. Has it stopped using the copy of the control variable, or not?

Martin Eden

unread,
Jan 15, 2026, 9:18:33 AM (6 days ago) Jan 15
to lu...@googlegroups.com

On 2026-01-15 14:00, dev1 dev2 wrote:
> I also think about code written by clients of our software. Such breaking
> change does not look valuable enough and at the same time is going to be
> quite frequent...

Hello, dev1

That's not argument.

Even if all users of Lua occur to use control loop var modification as
their style idiom it's still bad practice. I may be wrong and we can
discuss it. But do you have more reasonable arguments versus this change?
Not that some semi-sapient "clients" were taught to use it and now they
can't and crying?


This reminds me "Workflow xkcd": https://xkcd.com/1172/

Also reminds me buzz when macro name changes "broke" version detection
in LuaRocks. (Glorious use of "grep" on header files!)

Also reminds me general b-hrt when changes in gcc "broke" Flash player.
They relied on specific implementation behavior of some C function.

Personally I absolutely don't care about this lock on loop control var.
This does not require any changes in my code. So for me it's fine.


I deeply respect Lua project for it's independence. From my point of
view it never tried to get favors from crowd. It never was sold to
corporations to promote it. It does not try to lure you and lock by
package code dependencies. Or by overblown specification like C#.
It does not want your soul. It's worthy.

I may have my own view on some of language details but I'm really glad
for lack of creeping featurism here: all that "+=", classes, book just
about "standard" libraries, package manager, acolytes with PRs and
so on.

-- Martin

Lars Müller

unread,
Jan 15, 2026, 9:53:43 AM (6 days ago) Jan 15
to lu...@googlegroups.com
You're probably using a linter that was designed back when <const> did not exist, and not updated properly since.

Given constants and mutable variables, a reasonable linter should allow you to redeclare a const variable as mutable and vice versa. The same should go for other attributes like <close>.

Shadowing warnings in general are something I'm not the biggest fan of.
There are reasons for why Lua allows shadowing at all in the first place, and did so already before the introduction of constants.
Rust, a language famous for its general strictness, lets you shadow variables.

And as you say, if for some silly reason forced to avoid shadowing entirely, it's no big deal to just rename the loop variable.
But why would it have to be something weird like cflcv?
Why not, say, key -> key_const, and then have local key = key_const?

As for backwards compatibility, it is simply not a hard goal of new Lua versions to be backwards compatible. If you seek backwards compatibility, I would suggest picking a Lua version you're happy with and sticking with it. You really don't need to update. This is part of the reason Lua 5.1 is still incredibly popular.

In any case, I think it's good that there's now a definitive statement on whether loop variables can be mutated or not - and what happens if you do try to mutate them - that is enforced by the reference implementation.

It's certainly better than being in an indeterminate state of "the reference manual says don't do it, but it works reliably, so inevitably people do it anyways".

Regards,
Lars

dev1 dev2

unread,
Jan 15, 2026, 12:08:44 PM (6 days ago) Jan 15
to lua-l
I think it's worth clarifying. I am not arguing against this change. Please let me know if I wrote it somewhere. I am asking for a help from people who made this decision and who are very experienced in Lua.

In principle, IMO this change makes sense. The biggest value of it IMO is to prevent the misunderstanding, when one expects it to change the loop logic, but de-facto changing of the variable has no effect on the loop.
I am also super glad, that the issues are found during the parsing stage, not in runtime. Although, it's still kind of runtime for the code loaded by `load`.

Lars Müller:
> In any case, I think it's good that there's now a definitive statement on whether loop variables can be mutated or not - and what happens if you do try to mutate them - that is enforced by the reference implementation.
> It's certainly better than being in an indeterminate state of "the reference manual says don't do it, but it works reliably, so inevitably people do it anyways".
Fully agree with it.

In the past Lua was more friendly to the backward compatibility, e.g., there were macros like LUA_COMPAT_Whatever. For such fundamental breaking changes of the language like for-loop, something like this would also be beneficial, IMO.

> Lars Müller: You're probably using a linter that was designed back when <const> did not exist, and not updated properly since...
You are right. The order in which things are executed does not always seem optimal. Maybe updating or changing the linter shall be a prerequisite now.

> Lars Müller: Why not, say, key -> key_const, and then have local key = key_const?
That's a good idea, but subjectively it can lead to mistakes, when one uses key_const instead of a newer value referenced by key. It's better to avoid that dualism. Anyway, thanks for the suggestion.

> Just found the commit https://github.com/lua/lua/commit/b2f7b3b79f3117885b265575f6c5dbf934757797 and wonder whether it would be sufficient to simply change the type from RDKCONST to VDKREG. Has it stopped using the copy of the control variable, or not?
Found the follow-up commits. I think it deserves a little more thinking, but now it seems doable internally.



Roberto Ierusalimschy

unread,
Jan 16, 2026, 2:24:43 PM (5 days ago) Jan 16
to lu...@googlegroups.com
> In the past Lua was more friendly to the backward compatibility, e.g.,
> there were macros like LUA_COMPAT_Whatever. For such fundamental breaking
> changes of the language like for-loop, something like this would also
> be beneficial, IMO.

We will add a LUA_COMPAT_Whatever to control the "read-onlyness" of

dev1 dev2

unread,
Jan 20, 2026, 4:44:13 AM (yesterday) Jan 20
to lua-l
Thank you, looking forward to it.
Reply all
Reply to author
Forward
0 new messages