teal-language / tl

The compiler for Teal, a typed dialect of Lua
MIT License
2.03k stars 101 forks source link

annotation allowed in generic `for` statement #701

Closed fperrad closed 8 months ago

fperrad commented 9 months ago

The following code runs with tl:

for k <const>, v <const> in pairs(table as {string:any}) do
    print(k, v)
end

It looks like a bug (as not expected by docs/grammar.md).

Now, the reference manual for Lua 5.3 gives some equivalent code of for statement (in section https://www.lua.org/manual/5.3/manual.html#3.3.5)

  -- for v = e1, e2, e3 do block end

  do
    local var, limit, step = tonumber(e1), tonumber(e2), tonumber(e3)
    if not (var and limit and step) then error() end
    var = var - step
    while true do
      var = var + step
      if (step >= 0 and var > limit) or (step < 0 and var < limit) then
        break
      end
      local v = var
      block
    end
  end
  -- for var_1, ···, var_n in explist do block end

  do
    local f, s, var = explist
    while true do
      local var_1, ···, var_n = f(s, var)
      if var_1 == nil then break end
      var = var_1
      block
    end
  end

These pseudo codes were removed of the reference manual for Lua 5.4. The reference manual 5.4 (which introduces the <const> annotation) says:

You should not change the value of the control variable during the loop.

But, you could do it in any version of Lua:

$ cat fornum.lua 
for i = 1, 2 do
     i = 9
     print(i)
end
$ lua5.1 fornum.lua 
9
9
$ lua5.2 fornum.lua 
9
9
$ lua5.3 fornum.lua 
9
9
$ lua5.4 fornum.lua 
9
9
$ tl run fornum.lua 
9
9

As it is a bad/insane practice, Teal must prevent it by handling the control variable as a <const> one.

  -- for v = e1, e2, e3 do block end
  -- v is implicitly <const>

  do
    local var, limit, step = tonumber(e1), tonumber(e2), tonumber(e3)
    if not (var and limit and step) then error() end
    var = var - step
    while true do
      var = var + step
      if (step >= 0 and var > limit) or (step < 0 and var < limit) then
        break
      end
      local v <const> = var
      block
    end
  end
  -- for var_1, ···, var_n in explist do block end
  -- var_1 is implicitly <const>

  do
    local f, s, var = explist
    while true do
      local var_1 <const>, ···, var_n = f(s, var)
      if var_1 == nil then break end
      var = var_1
      block
    end
  end
hishamhm commented 8 months ago

I added the parser fix, but didn't force <const> on the variables.