Devel::Peek::Dumpで見るSVのボディの型

今までXSでSvTYPE(sv)をするとそのsvの型が返ってくると考えていたのだが,最近それは誤りであると気づいた。SvTYPE(sv)が返すのはそのsvのボディの型であり,それがsvの内容を表現していることもあれば,無関係のこともある。
具体的には,そのsvが配列(AV),ハッシュ(HV),サブルーチン(CV),型グロブ(GV)などであれば,SvTYPE(sv)が返すのは実際にそのsvの型と一致するが,それ以外のスカラー値の場合はsvのフラグを見なければ型を特定できない。
それを示すのが次のスクリプトである:

#!perl
use strict;
use Devel::Peek;
my $x;      # $xはundef
Dump $x;    # -> SvTYPEはNULL
$x = \10;   # 一旦リファレンスを代入
$x = undef; # $xはundef
Dump $x;    # -> SvTYPEはRV (内容とSvTYPEが不一致)
$x = *foo;  # SvTYPEはPVGV
$x = undef; # $xはundef
Dump $x;    # -> SvTYPEはPVMG (内容とSvTYPEが不一致)
__END__

結果はバージョンによって変わるが,5.10.0では以下のようになる

SV = NULL(0x0) at 0xa081b34
  REFCNT = 1
  FLAGS = (PADMY)
SV = RV(0xa081b40) at 0xa081b34
  REFCNT = 1
  FLAGS = (PADMY)
SV = PVMG(0xa07b46c) at 0xa081b34
  REFCNT = 1
  FLAGS = (PADMY)
  IV = 168471720
  NV = 0
  PV = 0xa091774 "*main::foo"\0
  CUR = 10
  LEN = 12

二番目のSV = RV(...)は何かのリファレンスのように見えるかもしれないが,undefである。もし有効なリファレンスであれば,FLAGSにROKがあるはずだ。三番目のSV = PVMG(...)は特殊な文字列のように見えるかもしれないが,これもundefである。これも同様に,もし文字列であればFLAGSにPOKがあるはずだ。
したがって,XSレベルでスカラーを期待するsvに対してswitch(SvTYPE(sv)){ ... }としたくなったときは注意が必要である。