#21378·vim

Vim9: :def function in legacy script can create a script variable by assignment

Author: h-eastCreated Sep 27, 2026Updated Sep 27, 2026

In a legacy script (no vim9script), a :def function can create a new script-local variable just by assigning to it:

vim
def s:F()
  s:ccc = 1
enddef
call s:F()
echo s:ccc

This echoes 1. Declaring the variable in the same place is rejected:

vim
def s:F()
  var s:ccc = 1  # E1101: Cannot declare a script variable in a function
enddef

and let s:ccc = 1 gives E1126, since :let cannot be used in a :def function.

Patch 8.2.1373 (#6630) added an error for assigning to a script variable that does not exist, but only for a function defined in a Vim9 script ("Check that in Vim9 script the variable was defined"). The check in compile_lhs() still tests uf_script_ctx_version == SCRIPT_VERSION_VIM9, so a :def function in a legacy script was left out. (In a Vim9 script the s: prefix itself has since become an error, E1268.) The help does not cover this case: :help vim9-s:var only states that "s:" is optional in a :def function in a legacy script.

This looks inconsistent to me. The body of a :def function follows Vim9 rules, where a variable is declared before it is used, and :help E1269 states that script-local variables must be declared at the script level. Yet var s:ccc = 1 is an error while a plain s:ccc = 1 creates the variable.

Making this an error (E1089, "Unknown variable") would break a legacy script that relies on it. The error would only be given when the variable does not exist yet at the time the function is compiled; assigning to an existing script variable keeps working.

I went through the legacy scripts under runtime/ that contain a :def function, and none of them creates a script variable this way. So I would still like to change it. What do others think? If you know of a plugin that depends on this, please let me know.