Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Ternary expressions containing lambdas result in error instead of evaluating #24

Open
joom opened this issue Jan 13, 2023 · 1 comment

Comments

@joom
Copy link

joom commented Jan 13, 2023

Consider the following vim9script code:

vim9script
var Foo = true ? (x: number): number => x : (x: number): number => x
echo Foo(10)

(this returns 10 in Vim 9)

This is currently compiled into this Lua code:

local vim9 = require('_vim9script')
local M = {}
local Foo = nil
-- vim9script
Foo = vim9.ternary(true, function(x)
  return x
end, function(x)
  return x
end)
print(Foo(10))
return M

... which throws an error for me:

Error detected while processing /Users/joomy/work/vim9jit/ex/bug.vim:
line    3:
E5108: Error executing lua ./bug.lua:19: attempt to call local 'Foo' (a nil value)
stack traceback:
        ./bug.lua:19: in function 'autoload'
        [string "luaeval()"]:1: in main chunk

(line 19 is the print(Foo(10)) line)

Given how ternary is implemented in _vim9script.lua, as long as the inputs to ternary are functions, they are called with no arguments:

M.ternary = function(cond, if_true, if_false)
if cond then
if type(if_true) == 'function' then
return if_true()
else
return if_true
end
else
if type(if_false) == 'function' then
return if_false()
else
return if_false
end
end
end

And the "suspender" functions are only used if the branches of the ternary expressions have side effects:

impl Generate for Ternary {
fn write_default(&self, state: &mut State, output: &mut Output) {
let if_true = if expr_utils::has_possible_sideffects(&self.if_true) {
format!("function() return {} end", self.if_true.gen(state))
} else {
self.if_true.gen(state)
};
let if_false = if expr_utils::has_possible_sideffects(&self.if_false) {
format!("function() return {} end", self.if_false.gen(state))
} else {
self.if_false.gen(state)
};
output.write_lua(&format!(
"vim9.ternary({}, {if_true}, {if_false})",
self.cond.gen(state),
))
}
}

and lambda expressions are considered not to have side effects:
Expression::Lambda(_) => false,

But even without that, even an identifier can evaluate to a function, which will make type(if_true) == 'function' true in the ternary implementation in Lua. This identifier can come from outside, so you cannot determine whether a term has side effects syntactically.

The solution seems to be making ternary expression generation wrap the branches in function() return .. end all the time. (Lua doesn't seem to have lazy and/or either so that also doesn't work).

@tjdevries
Copy link
Owner

Great bug report! :)

I'll think about the best way to solve. Thanks

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants