:method attributeの有効活用

サブルーチンに付けられる:methodアトリビュートは,メソッドを定義するときに付加することで,同名の組み込み関数を呼び出すときの"Ambiguous call"警告を避けることができる。

#!perl -w
sub length :method{ 1 }
# 以下の文は:methodがないと"Ambiguous call"警告を発生させる
print length("foo"); # => 3

しかし,現在のPerlで:methodが有用なのは今のところこのようなケースだけである。

ところで,CPANにはUNIVERSAL::canとUNIVERSAL::isaというモジュールが存在する。これらは,&UNIVERSAL::canや&UNIVERSAL::isaがメソッドではなく関数として呼ばれたときに警告を発生させるためのモジュールだ。can()やisa()は継承されている可能性があるので,これらを関数として呼び出してはいけない。
しかし,この両モジュールは安全性を保障する代わりに実行速度を犠牲にする。isa()もcan()もよく使われるメソッドなので,できれば実行速度を落としたくない。

そのようなことを考えていたときにふと思い出した。Perlコードからはあるサブルーチンがメソッドとして呼ばれたか関数として呼ばれたかを識別する術はないが,OPコードレベルではこの呼び出しを区別することができるのだ。
また,更に先に述べた:methodだが,内部的にはCVのフラグとして実装されている。したがって,XSレベルからの参照コストは非常に小さい。
ここで一つ閃いた。:methodとPL_checkを結びつけると,実行時のコストをまったく掛けずに,メソッドをサブルーチンとして呼ばれるケースで警告を出せるようにすることができるのではないか。

use warnings::method; # or use warnings::method 'FATAL'
sub Foo::bar :method{ print "Hello, world!\n" }
Foo->bar(); # OK
Foo::bar(); # ! Method "bar" in Foo called as a function

ためしに実装してみたが,無事に動くことがわかった。PL_check[OP_ENTERSUB]を書き換えるだけなので,実行速度にはまったく影響しない。
コードとテストは書いたので,ドキュメントが書きあがり次第CPANに公開する。
http://search.cpan.org/dist/warnings-method/ (予定)