[BUG] VMify "Unresolved Upvalue" error during special repeat until case
VMify will fail if there's a repeat until statement that references a local in the until condition that was first defined in the repeat's body
Code to reproduce:
repeat
local x = 5
until x == 5 -- The condition here should see the `local x = 5` above, as they are actually contained in the same scope
The actual code that parses the repeat until (src/prometheus/parser.lua#L225-L233) is parsing the condition with the return's scope, which should be good, but for some reason something later is breaking because of this, and I have no idea what
VMIFY Scoping problem.
that
There is a possible fix for this.
Possible fix #1:
Add a boolean reference in Repeat Statement AST named "hasUpvalue", set it to false if one of the condition variables only refers inside the Repeat loop or true by default; all of this can be done during parsing, and then add a check like this in the compiler.
if statement.hasUpvalue then
-- Handle the condition first, which means no local variable refers from body to condition, which is safe.
else
-- Handle the loop body first (this is where the fix should be applied).
end
before the code:
local conditionReg = self:compileExpression(statement.condition, funcDepth, 1)[1];
Possible fix #2:
Add a function in the compiler that safely traverses expressions, so that you can check if there is a local variable inside the Repeat loop that is being used in the condition.
If anyone is willing to fix this issue, I am willing to accept a PR, but I currently do not have time, to fix this myself due to personal reasons.
Just redirect the variables in the repeat scope to the scope outside of repeat.
(MAIN SCOPE) REPEAT (REPEAT_SCOPE) UNTIL (REPEAT_SCOPE) EXTRACT(REPEAT_SCOPE, MAIN_SCOPE)
This is how I visualize it.
Just redirect the variables in the repeat scope to the scope outside of repeat.
(MAIN SCOPE) REPEAT (REPEAT_SCOPE) UNTIL (REPEAT_SCOPE) EXTRACT(REPEAT_SCOPE, MAIN_SCOPE)
This is how I visualize it.
That would probably cause other issues, because if the variables are defined in the outer scope, upvalues will not work properly. Consider that the following codes are not equivalent:
local a = 0
local arr = {}
repeat
a = a + 1
local b = a
arr[b] = function() print(b) end
until a == 2
arr[1]() -- 1
arr[2]() -- 2
local a = 0
local arr = {}
local b
repeat
a = a + 1
b = a
arr[b] = function() print(b) end
until a == 2
arr[1]() -- 2
arr[2]() -- 2
Rather the repeat condition should always be handled within the repeat scope. I think this is not properly implemented in the compiler.
Just redirect the variables in the repeat scope to the scope outside of repeat. (MAIN SCOPE) REPEAT (REPEAT_SCOPE) UNTIL (REPEAT_SCOPE) EXTRACT(REPEAT_SCOPE, MAIN_SCOPE) This is how I visualize it.
That would probably cause other issues, because if the variables are defined in the outer scope, upvalues will not work properly. Consider that the following codes are not equivalent:
local a = 0 local arr = {} repeat a = a + 1 local b = a arr[b] = function() print(b) end until a == 2 arr1 -- 1 arr2 -- 2 local a = 0 local arr = {} local b repeat a = a + 1 b = a arr[b] = function() print(b) end until a == 2 arr1 -- 2 arr2 -- 2 Rather the repeat condition should always be handled within the repeat scope. I think this is not properly implemented in the compiler.
Maybe putting the repeat scope vars in the parent scope? Instead of extracting, just copy it. Idk, but this seems like some kind of alt option for me, also maybe adding a name change or "originating_scope_id" property in a scope variable when extracting can help differentiate.