abstract type
(ジュリアv0.6で)のフィールドにアクセスするときの型の不安定性について質問があります
。
型階層があり、そのすべてが同じインスタンス変数を共有しているとします。期待される変数を欠く新しいサブタイプを誰かがいつでも定義できるため、フィールドに正しくアクセスすることが正しいことは型が不安定であり、保証されていないことを私は知っています。しかし、メンバーアクセスを関数でラップしても、アクセスは依然として型が不安定で、その理由がわかりません。
単純な型階層があるとします。
julia> begin
abstract type AT end
mutable struct T1 <: AT
x::Int
end
mutable struct T2 <: AT
x::Int
end
end
直接アクセスする代わりにa.x
、関数バリアでラップします。
julia> getX(a::AT)::Int = a.x
>> getX (generic function with 1 method)
julia> @code_warntype getX(T1(1))
Variables:
#self# <optimized out>
a::T1
Body:
begin
return (Core.getfield)(a::T1, :x)::Int64
end::Int64
このメソッドによるアクセスは、 の型が であると推測できるため、型が安定していることに注意してa
くださいT1
。
ただし、getX
コンパイラが事前に変数の型を認識できないコンテキストで使用すると、型が不安定になります。
julia> foo() = getX(rand([T1(1),T2(2)]))
>> foo (generic function with 1 method)
julia> @code_warntype foo()
Variables:
#self# <optimized out>
T <optimized out>
Body:
begin
SSAValue(0) = (Core.tuple)($(Expr(:new, :(Main.T1), 1)), $(Expr(:new, :(Main.T2), 2)))::Tuple{T1,T2}
SSAValue(2) = $(Expr(:invoke, MethodInstance for rand(::Array{AT,1}), :(Main.rand), :($(Expr(:invoke, MethodInstance for copy!(::Array{AT,1}, ::Tuple{T1,T2}), :(Base.copy!), :($(Expr(:foreigncall, :(:jl_alloc_array_1d), Array{AT,1}, svec(Any, Int64), Array{AT,1}, 0, 2, 0))), SSAValue(0))))))
return (Core.typeassert)((Base.convert)(Main.Int, (Core.getfield)(SSAValue(2), :x)::Any)::Any, Main.Int)::Int64
end::Int64
の本体をインライン化し、getX
それを本質的に に置き換えたことに
注意してくださいtmp.x::Int64
。getX
これには驚きました。先ほど見た同じ定義の 2 つのインスタンス化のうちの 1 つにディスパッチすることを期待していたので、型がわかっているのでアサートは必要ありません。
getX
が実際に抽象基本型に対してのみ定義されている場合、これはある程度意味があると思いましたAT
-私が想像している方法でディスパッチするメソッドはありません。そこでgetX
、次のようにサブタイプごとに特定のメソッドを生成するように再定義してみました。
julia> getX(a::T where T<:AT)::Int = a.x
>> getX (generic function with 1 method)
しかし、それは実際には同一の定義であり、何も変わっていません:
julia> methods(getX)
>> # 1 method for generic function "getX":
getX(a::AT) in Main at none:1
どうすればこれを機能させることができますか?