Vimのパッチを作る(3)
(1),(2)は欠番
↑ちょっとこまるので直すことにしました
条件を探る
なんやかんやで再現コードを作りました
code:vim
function! s:func()
echo "s:func() was called"
endfunction
def! s:C1()
s:func()
enddef
def! s:C2()
var Ref2 = function('s:func')
Ref2()
enddef
def! s:C3()
var Ref3 = function(s:func)
Ref3()
enddef
def! s:C4()
var Ref1 = s:func
Ref1()
enddef
call s:C1()
call s:C2()
call s:C3()
call s:C4()
Vim scriptでは関数の名前として基本小文字から始まるものは利用できない
ただし、スクリプトローカル関数・オートロード関数は除く
Vim9 scriptではさらに厳しく、どちらも利用できない
例外的に、Vim script内で定義されたVim9 script関数ではそれらの関数を呼び出すことができるが
関数の参照を取ることはできない
これは大文字の場合と異なり、自然とは言えないしHelpにも記載がない
→バグじゃね?
エラーを確認するとこの問題は大きく二つの原因によるものだと推定できた(2つの道でエラーを吐くのに本当にバグなのかはわからない)
1) Vim9 scriptのコンパイル
まずVim9 scriptの:disassembleコマンドを使って、Vimが変数名をどのように解釈しているかを予想した
名前のスコープ(s:/g:/b:)を取っ払った時変数が大文字から始まるか小文字から始まるかをみる
→大文字の場合:名前が関数の名前空間に属しているか変数の名前空間に属しているか確かめる
→関数の名前空間:関数としてコンパイル(PUSHFUNC)する
→変数の名前空間:変数としてコンパイル(LOADS)する
→両方:関数としてコンパイルする(PUSHFUNC)
→小文字の場合:名前が変数の名前空間に属しているか確かめる
→属している:変数としてコンパイル(LOADS)する
→属していない:エラーを吐く
メモ
コンテキストがVim scriptで定義されたVim9 script関数なのかVim script関数なのか、Vim9 scriptで定義されたVim script関数なのかVim9 script関数はin_vim9script()関数とcurrent_script_is_vim9()関数で判断できるよ!
table: vim9
コンテキスト 関数 in_vim9script() current_script_is_vim9()
Vimscript Vimscript 0 0
Vimscript Vim9script 1 0
Vim9script Vimscript 0 1
Vim9script Vim9script 1 1
funcrefでE1267がでるやつ
Vim scriptで定義されたVIm9 script関数でのみs:hogeの形式を通したい
src/userfunc.c:3775
src/userfunc.c:3998: Vim9 scriptの中かつスコープの指定子がついているかつ先頭が大文字ではない → エラー
3984 ~ 4020
ちょっとこれはむずかしそうなので後回し
Vim9 scriptでs:hogeを関数として取得できないやつ
vim9compile.cを読もう
コンパイル時にスクリプト変数はvim9_compile.cのfind_script_var()関数で探されているよ!
ブレークポイントを貼って観察してみる
code:vim
function! s:Func()
echo "s:func() was called"
def! s:C2()
var Ref2 = s:Func
Ref2()
enddef
call s:C2()
まずこのコード 意図した通りに動作する
find_script_var()は発火しませんでした!あれ
どうもある名前が変数なのか関数なのかの判断はもっと前の段階でやっているようです
仕方ないのでvim9_compile.cを ^compile で検索してみることにします
ありました vim9compile.c:2689 compile_def_function.cとか
行は→の関数で処理されるcompile_assignment() vim9compile.c:3160あたり
vim9compile.c:2254行手前で変数の型が特定されているぽい
compile_assignment() (vim9compile.c:2012Lにbpを貼ってみる)
見逃した VAR_ANY として特定されている
潜って潜って
compile_load()で変数に対するLOAD命令が生成されている!!!!
vim9expr.c:400L
code:diff
diff --git a/src/vim9expr.c b/src/vim9expr.c
index b2bb405f6..5acc09c5a 100644
--- a/src/vim9expr.c
+++ b/src/vim9expr.c
@@ -451,8 +451,7 @@ compile_load(
vim_free(name);
return FAIL;
}
- if (is_expr && ASCII_ISUPPER(*name)
- && find_func(name, FALSE) != NULL)
+ if (is_expr && find_func(name, FALSE) != NULL)
res = generate_funcref(cctx, name, FALSE);
else
res = compile_load_scriptvar(cctx, name,
勝ち申した
わざわざチェックが入ってるの、仕様なのかしら
funcrefでE1267がでるやつ
戻ってきた
userfunc.c:3985あたりの条件を精査する必要がありそう
0:26 ... 寝る勇気
直した
Vim script内で定義されたVim9 script関数の時だけチェックを変えた
code:diff
diff --git a/src/userfunc.c b/src/userfunc.c
index ae3e20202..6c69c3ec7 100644
--- a/src/userfunc.c
+++ b/src/userfunc.c
@@ -3791,6 +3791,7 @@ trans_function_name(
int prefix_g = FALSE;
lval_T lv;
int vim9script = in_vim9script();
+ int script_is_vim9 = current_script_is_vim9();
int vim9_local;
if (fdp != NULL)
@@ -3995,7 +3996,8 @@ trans_function_name(
{
if (!vim9_local)
{
- if (vim9script && lead == 2 && !ASCII_ISUPPER(*lv.ll_name))
+ if (vim9script && lead == 2 && !ASCII_ISUPPER(*lv.ll_name)
+ && script_is_vim9)
{
semsg(_(e_function_name_must_start_with_capital_str), start);
goto theend;
diff --git a/src/vim9expr.c b/src/vim9expr.c
index b2bb405f6..5acc09c5a 100644
--- a/src/vim9expr.c
+++ b/src/vim9expr.c
@@ -451,8 +451,7 @@ compile_load(
vim_free(name);
return FAIL;
}
- if (is_expr && ASCII_ISUPPER(*name)
- && find_func(name, FALSE) != NULL)
+ if (is_expr && find_func(name, FALSE) != NULL)
res = generate_funcref(cctx, name, FALSE);
else
res = compile_load_scriptvar(cctx, name,