为何Julia 1.8代码中@code_warntype出现红色警告?
Julia 1.8中@code_warntype红色警告的原因分析
问题代码
mutable struct S X::Int32 const Children::Vector{Ref{S}} end function f2(s::Ref{S}) x = Int32(0) chs = s[].Children for ch in chs add = ch[].X x += add end println("f2: $(x)") end function test() s = S(3, Vector{Ref{S}}()) @code_warntype f2(Ref{S}(s)) end test()
@code_warntype输出结果
MethodInstance for f2(::Base.RefValue{S}) from f2(s::Ref{S}) in Main at example.jl:7 Arguments #self#::Core.Const(f2) s::Base.RefValue{S} Locals @_3::Union{Nothing, Tuple{Ref{S}, Int64}} (green) chs::Vector{Ref{S}} x::Any (red) ch::Ref{S} (red) add::Any (red) Body::Nothing 1 ─ (x = Main.Int32(0)) │ %2 = Base.getindex(s)::S │ (chs = Base.getproperty(%2, :Children)) │ %4 = chs::Vector{Ref{S}} │ (@_3 = Base.iterate(%4)) │ %6 = (@_3 === nothing)::Bool │ %7 = Base.not_int(%6)::Bool └── goto #4 if not %7 2 ┄ %9 = @_3::Tuple{Ref{S}, Int64} (red) │ (ch = Core.getfield(%9, 1)) │ %11 = Core.getfield(%9, 2)::Int64 │ %12 = Base.getindex(ch)::Any (red) │ (add = Base.getproperty(%12, :X)) │ (x = x + add) │ (@_3 = Base.iterate(%4, %11)) │ %16 = (@_3 === nothing)::Bool │ %17 = Base.not_int(%16)::Bool └── goto #4 if not %17 3 ─ goto #2 4 ┄ %20 = Base.string("f2: ", x)::String │ %21 = Main.println(%20)::Core.Const(nothing) └── return %21
疑问
我认为ch的类型在编译时可以确定,不应存在歧义,想了解出现红色警告的原因。
原因分析
核心问题:Ref{S}的可变性打破了静态类型推导
Ref的本质特性:Ref{S}是一个可变容器——虽然你标注了它存储的是S类型,但在运行时,这个引用指向的内容可以被外部代码(比如其他线程)修改,编译器无法静态保证ch[]返回的一定是S实例。类型推导的连锁失效:
- 编译器对
ch[]的返回值只能推导为Any(因为无法确保运行时类型不被篡改),对应输出里的%12 = Base.getindex(ch)::Any; - 依赖
ch[]的add = ch[].X自然也会被推导为Any; - 初始为
Int32的x在执行x += add时,由于add是Any类型,x的类型会被提升为Any; - 循环变量
ch的红色标记是因为迭代过程中关联了不确定类型的操作,并非ch本身类型不确定。
- 编译器对
解决方法:添加类型断言消除歧义
通过类型断言明确告诉编译器ch[]的返回类型是S,就能让类型推导恢复正常:
function f2(s::Ref{S}) x = Int32(0) chs = s[].Children for ch in chs s_instance = ch[]::S # 强制指定返回类型为S add = s_instance.X x += add end println("f2: $(x)") end
添加断言后,@code_warntype会显示所有变量都是确定的静态类型,红色警告全部消失。
内容的提问来源于stack exchange,提问作者mafu
相关产品推荐
相关产品推荐

