vim-jp / issues

有志で既知のバグや要望を検討・管理し、オフィシャルへの還元をしていきます。
https://vim-jp.org/
341 stars 11 forks source link

Vim9 scriptではs:hogeなどの小文字から始まる関数の参照を取れないように見える #1391

Closed kat0h closed 2 years ago

kat0h commented 2 years ago

不具合の内容

例1

関数名が小文字の場合

let s:hoge = "HOGE"
def! s:hoge()
  echo "s:hoge"
enddef

def! Func()
  hoge = "HOGEEEEEEEEE"
  echo s:hoge
enddef
call Func() "=> "HOGEEEEEEEEEE"

関数名が大文字の場合

let s:Hoge = "HOGE"
def! s:Hoge()
  echo "s:Hoge"
enddef

def! Func()
  Hoge = "HOGEEEEEEEEE"
  echo s:Hoge
enddef
call Func() "=> <SNR>412_HOGE

スクリプトローカルの関数名が小文字から始まる場合と大文字から始まる場合で挙動が変わる。

例2

関数名が小文字の場合

function! s:hoge()
  echo "s:Hoge() called"
endfunction
let s:hoge = "Hoge"

def! Func()
  var Ref = s:hoge
  Ref()
enddef

call Func() " Error (Unknown function)

関数名が小文字の場合

function! s:Hoge()
  echo "s:Hoge() called"
endfunction
let s:Hoge = "Hoge"

def! Func()
  var Ref = s:Hoge
  Ref()
enddef
call Func() "=> s:Hoge() called

期待動作

上記コードの関数名が大文字の場合と同じ動作

Vimのバージョン

9.0 patch 1-176

OSの種類/ディストリ/バージョン

その他

おそらく、Vim9 scriptのコンパイラが変数名を解決するロジックに誤りがあるのではないかと考えています。 例2のs:Hogeの場合は関数としてRefに関数として代入している一方、s:hogeの場合は変数として代入しています。

コンパイル時にs:Hogeなどの名前が関数の名前空間に属しているか変数の名前空間に属しているかは次の手順で判定される(たぶん(挙動を見る限り))
名前のスコープ(s:/g:/b:)を取っ払った時変数が大文字から始まるか小文字から始まるかをみる
 →大文字の場合:名前が関数の名前空間に属しているか変数の名前空間に属しているか確かめる
  →関数の名前空間:関数としてコンパイル(PUSHFUNC)する
  →変数の名前空間:変数としてコンパイル(LOADS)する
  →両方:関数としてコンパイルする(PUSHFUNC)
 →小文字の場合:名前が変数の名前空間に属しているか確かめる
  →属している:変数としてコンパイル(LOADS)する
  →属していない:エラーを吐く

基本的にVim scriptでは関数名が小文字から始まることは許されないのですが、s:に関しては例外で小文字から始まることが許可されます。 (autoload関数でも小文字から始めることができる 2022-08-11) VIm9 scriptのコンパイラではこのことが考慮されておらず、うまくうごかない(と考えています)のではないでしょうか。

kat0h commented 2 years ago

funcref/functionで参照をとった場合についても同様のようです。

tsuyoshicho commented 2 years ago

基本的にVim scriptでは関数名が小文字から始まることは許されないのですが

についてですが、autoload関数であれば小文字もできますね。

参照が取れるか、はまた別かもですが。

kat0h commented 2 years ago

autload関数 確かにそうでした。ご指摘ありがとうございます。 autoload関数についても確認してみます。

kat0h commented 2 years ago

autoload関数は問題なく読み込めるようでした

vim9script
var ref  = funcref(test9#func)
var ref2 = funcref('test9#func')
var ref3 = test9#func
ref()
ref2()
ref3()

echo rand()
kat0h commented 2 years ago
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()

こちらのコードが通るようにしてみようかなと考えています

kat0h commented 2 years ago
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,

これでs:funcが読めない問題は解決されるはず? (ASCII_ISUPPERがわざわざ入っているのでもしかしたら仕様かもしれないし、仕様でないかもしれない)

kat0h commented 2 years ago
kat0h commented 2 years ago
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,

多分両方の問題が解決しました

kat0h commented 2 years ago

https://github.com/vim-jp/issues/issues/1391#issuecomment-1214299081 ひとまずこちらのコードが通るようになりました

kat0h commented 2 years ago

https://github.com/vim/vim/pull/10926

kat0h commented 2 years ago

パッチが取り込まれましたのでこのissueはcloseします ご協力ありがとうございました! https://github.com/vim/vim/commit/948a3894d98f5e2a6e7fc57189fe9c2a5919eebf