Describe the bug
In Vim9 script, exists() might fail to test the existence of a list item in compiled code.
To Reproduce
Run this shell command:
vim -Nu NONE -S <(cat <<'EOF'
vim9script
def Func()
var l = ['a', 'b', 'c']
echomsg exists('l[1]')
enddef
Func()
EOF
)
0 is echo'ed.
Expected behavior
1 is echo'ed (or true, not sure). Because the list l contains 3 items; one of which has the index 1 (i.e. 'b'). Therefore, l[1] does exist.
Environment
Additional context
No issue at the script level:
vim9script var l = ['a', 'b', 'c'] echomsg exists('l[1]')
1
If this is working as intended, maybe it should be documented at :help exists():
varname internal variable (see
|internal-variables|). Also works
for |curly-braces-names|, |Dictionary|
entries, |List| items, etc. Beware
that evaluating an index may cause an
error message for an invalid
expression. E.g.: >
:let l = [1, 2, 3]
:echo exists("l[5]")
0 >
:echo exists("l[xx]")
E121: Undefined variable: xx
0
—
You are receiving this because you are subscribed to this thread.
Reply to this email directly, view it on GitHub, or unsubscribe.![]()
Closing because I think it's working as intended: at runtime, exists() cannot find a function-local variable, because – I guess – it's on the stack, and it cannot access the latter.
Still, the example given in the help is a bit misleading:
:let l = [1, 2, 3]
:echo exists("l[5]")
Because it can be copied and adapted to successfully detect the existence of a list item inside a legacy function, but not inside a Vim9 one. I'll try to come up with a patch later.
Closed #8534.
I'll try to come up with a patch later.
As a suggestion, here is a patch:
diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt index e5f9b4651..c95de2a02 100644 --- a/runtime/doc/eval.txt +++ b/runtime/doc/eval.txt @@ -4364,6 +4364,20 @@ exists({expr}) The result is a Number, which is |TRUE| if {expr} is defined, :echo exists("l[xx]") < E121: Undefined variable: xx 0 + + NOTE: In a `:def` function, exists() + cannot test the existence of a + function-local variable. The latter is + on the stack, and cannot be accessed + outside the compiled function. + exists() always returns 0 then: > + def Func() + var name = 0 + echo exists('name') + enddef + Func() + 0 +< :cmdname Ex command: built-in command, user command or command modifier |:command|. Returns:
Using 0 for the variable value could be somehow confusing to the reader, because it's also the output of exists() in this case. Using another arbitrary number like 123 would be less confusing then. New patch:
diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt
index e5f9b4651..3c8df99ce 100644
--- a/runtime/doc/eval.txt +++ b/runtime/doc/eval.txt @@ -4364,6 +4364,20 @@
exists({expr}) The result is a Number, which is |TRUE| if {expr} is defined,
:echo exists("l[xx]")
< E121: Undefined variable: xx
0
+ + NOTE: In a `:def` function, exists() + cannot test the existence of a + function-local variable. The latter is + on the stack, and cannot be accessed + outside the compiled function. + exists() always returns 0 then: > + def Func()
+ var name = 123+ echo exists('name') + enddef + Func() + 0 +< :cmdname Ex command: built-in command, user command or command modifier |:command|. Returns:
—
I'll add a note in the help, but keep it short, since it's already quite long.