short circuiting bug with (do)

inserting a do block into a three-or-more clause and statement causes the do block to be evaluated earlier:

(and x (do x.y) x)`

local _1_
  _1_ = x.y
return (x and _1_ and x)
Assigned to
3 months ago
3 months ago
No labels applied.

~andreyorst 3 months ago*

Looks like we're hitting the Lua's lack of proper expressions. I think the only way to make this work is to transform do into an anonymous function that is immediately called. Kinda like Scheme's let does.

return (x and (function () return x.y end)() and x)

But then it looses the ability to access vararg in a situation like this:

(fn foo [x ...]
  (if x
    (do (print (select "#" ...))
    (print :welp)))

But if we could detect that the outer scope has the vararg and compile do to a vararg function instead and call it properly (function (...) (print "#" ...) return {...} end)(...), then it would probably work seamessly.

~andreyorst referenced this from #99 3 months ago

~andreyorst 3 months ago

But then it loses the ability to access vararg in a situation like this:

Apparently, the compiler already does this. So perhaps it tries to optimize the function away in this case for some reason.

Here are some examples:


The last example means that perhaps we already need the thing requested in #99, and it is only needed to be exposed.

~andreyorst 3 months ago

the opts.nval value is equal to 1 in cases like (and (do 42) false), and the branching is done depending if nval is set https://git.sr.ht/~technomancy/fennel/tree/main/item/src/fennel/specials.fnl#L121-131

~technomancy REPORTED FIXED 3 months ago

Yeah, I think I have a fix for this. We should never use nval=1 for things that need short-circuiting; this is caused by the fix for https://todo.sr.ht/~technomancy/fennel/83 but using nval was not the right solution for that problem. We can detect the last element manually and discard everything but the first element in all other cases.

Fixed in bc71de6.

Register here or Log in to comment, or comment via email.