diff --git a/base/compiler/abstractinterpretation.jl b/base/compiler/abstractinterpretation.jl index 52a9eb1a02ccd..296a30cdb2e0f 100644 --- a/base/compiler/abstractinterpretation.jl +++ b/base/compiler/abstractinterpretation.jl @@ -85,9 +85,12 @@ function abstract_call_gf_by_type(interp::AbstractInterpreter, @nospecialize(f), push!(edges, edge) end this_argtypes = isa(matches, MethodMatches) ? argtypes : matches.applicable_argtypes[i] - const_rt, const_result = abstract_call_method_with_const_args(interp, result, f, this_argtypes, match, sv, false) - if const_rt !== rt && const_rt ⊑ rt - rt = const_rt + const_result = abstract_call_method_with_const_args(interp, result, f, this_argtypes, match, sv, false) + if const_result !== nothing + const_rt, const_result = const_result + if const_rt !== rt && const_rt ⊑ rt + rt = const_rt + end end push!(const_results, const_result) if const_result !== nothing @@ -107,9 +110,12 @@ function abstract_call_gf_by_type(interp::AbstractInterpreter, @nospecialize(f), # try constant propagation with argtypes for this match # this is in preparation for inlining, or improving the return result this_argtypes = isa(matches, MethodMatches) ? argtypes : matches.applicable_argtypes[i] - const_this_rt, const_result = abstract_call_method_with_const_args(interp, result, f, this_argtypes, match, sv, false) - if const_this_rt !== this_rt && const_this_rt ⊑ this_rt - this_rt = const_this_rt + const_result = abstract_call_method_with_const_args(interp, result, f, this_argtypes, match, sv, false) + if const_result !== nothing + const_this_rt, const_result = const_result + if const_this_rt !== this_rt && const_this_rt ⊑ this_rt + this_rt = const_this_rt + end end push!(const_results, const_result) if const_result !== nothing @@ -523,33 +529,35 @@ function abstract_call_method_with_const_args(interp::AbstractInterpreter, resul @nospecialize(f), argtypes::Vector{Any}, match::MethodMatch, sv::InferenceState, va_override::Bool) mi = maybe_get_const_prop_profitable(interp, result, f, argtypes, match, sv) - mi === nothing && return Any, nothing + mi === nothing && return nothing # try constant prop' inf_cache = get_inference_cache(interp) inf_result = cache_lookup(mi, argtypes, inf_cache) if inf_result === nothing # if there might be a cycle, check to make sure we don't end up # calling ourselves here. - if result.edgecycle && _any(InfStackUnwind(sv)) do infstate - # if the type complexity limiting didn't decide to limit the call signature (`result.edgelimited = false`) - # we can relax the cycle detection by comparing `MethodInstance`s and allow inference to - # propagate different constant elements if the recursion is finite over the lattice - return (result.edgelimited ? match.method === infstate.linfo.def : mi === infstate.linfo) && - any(infstate.result.overridden_by_const) + let result = result # prevent capturing + if result.edgecycle && _any(InfStackUnwind(sv)) do infstate + # if the type complexity limiting didn't decide to limit the call signature (`result.edgelimited = false`) + # we can relax the cycle detection by comparing `MethodInstance`s and allow inference to + # propagate different constant elements if the recursion is finite over the lattice + return (result.edgelimited ? match.method === infstate.linfo.def : mi === infstate.linfo) && + any(infstate.result.overridden_by_const) + end + add_remark!(interp, sv, "[constprop] Edge cycle encountered") + return nothing end - add_remark!(interp, sv, "[constprop] Edge cycle encountered") - return Any, nothing end inf_result = InferenceResult(mi, argtypes, va_override) frame = InferenceState(inf_result, #=cache=#false, interp) - frame === nothing && return Any, nothing # this is probably a bad generated function (unsound), but just ignore it + frame === nothing && return nothing # this is probably a bad generated function (unsound), but just ignore it frame.parent = sv push!(inf_cache, inf_result) - typeinf(interp, frame) || return Any, nothing + typeinf(interp, frame) || return nothing end result = inf_result.result # if constant inference hits a cycle, just bail out - isa(result, InferenceState) && return Any, nothing + isa(result, InferenceState) && return nothing add_backedge!(mi, sv) return result, inf_result end @@ -1174,7 +1182,8 @@ function abstract_invoke(interp::AbstractInterpreter, argtypes::Vector{Any}, sv: nargtype === Bottom && return CallMeta(Bottom, false) nargtype isa DataType || return CallMeta(Any, false) # other cases are not implemented below isdispatchelem(ft) || return CallMeta(Any, false) # check that we might not have a subtype of `ft` at runtime, before doing supertype lookup below - types = rewrap_unionall(Tuple{ft, unwrap_unionall(types).parameters...}, types) + ft = ft::DataType + types = rewrap_unionall(Tuple{ft, unwrap_unionall(types).parameters...}, types)::Type nargtype = Tuple{ft, nargtype.parameters...} argtype = Tuple{ft, argtype.parameters...} result = findsup(types, method_table(interp)) @@ -1196,12 +1205,14 @@ function abstract_invoke(interp::AbstractInterpreter, argtypes::Vector{Any}, sv: # t, a = ti.parameters[i], argtypes′[i] # argtypes′[i] = t ⊑ a ? t : a # end - const_rt, const_result = abstract_call_method_with_const_args(interp, result, argtype_to_function(ft′), argtypes′, match, sv, false) - if const_rt !== rt && const_rt ⊑ rt - return CallMeta(const_rt, InvokeCallInfo(match, const_result)) - else - return CallMeta(rt, InvokeCallInfo(match, nothing)) + const_result = abstract_call_method_with_const_args(interp, result, argtype_to_function(ft′), argtypes′, match, sv, false) + if const_result !== nothing + const_rt, const_result = const_result + if const_rt !== rt && const_rt ⊑ rt + return CallMeta(const_rt, InvokeCallInfo(match, const_result)) + end end + return CallMeta(rt, InvokeCallInfo(match, nothing)) end # call where the function is known exactly @@ -1301,19 +1312,20 @@ end function abstract_call_opaque_closure(interp::AbstractInterpreter, closure::PartialOpaque, argtypes::Vector{Any}, sv::InferenceState) pushfirst!(argtypes, closure.env) sig = argtypes_to_type(argtypes) - (; rt, edge) = result = abstract_call_method(interp, closure.source::Method, sig, Core.svec(), false, sv) + (; rt, edge) = result = abstract_call_method(interp, closure.source, sig, Core.svec(), false, sv) edge !== nothing && add_backedge!(edge, sv) tt = closure.typ - sigT = unwrap_unionall(tt).parameters[1] - match = MethodMatch(sig, Core.svec(), closure.source::Method, sig <: rewrap_unionall(sigT, tt)) + sigT = (unwrap_unionall(tt)::DataType).parameters[1] + match = MethodMatch(sig, Core.svec(), closure.source, sig <: rewrap_unionall(sigT, tt)) info = OpaqueClosureCallInfo(match) if !result.edgecycle - const_rettype, const_result = abstract_call_method_with_const_args(interp, result, closure, argtypes, + const_result = abstract_call_method_with_const_args(interp, result, closure, argtypes, match, sv, closure.isva) - if const_rettype ⊑ rt - rt = const_rettype - end if const_result !== nothing + const_rettype, const_result = const_result + if const_rettype ⊑ rt + rt = const_rettype + end info = ConstCallInfo(info, Union{Nothing,InferenceResult}[const_result]) end end @@ -1323,7 +1335,7 @@ end function most_general_argtypes(closure::PartialOpaque) ret = Any[] cc = widenconst(closure) - argt = unwrap_unionall(cc).parameters[1] + argt = (unwrap_unionall(cc)::DataType).parameters[1] if !isa(argt, DataType) || argt.name !== typename(Tuple) argt = Tuple end @@ -1338,8 +1350,8 @@ function abstract_call(interp::AbstractInterpreter, fargs::Union{Nothing,Vector{ f = argtype_to_function(ft) if isa(ft, PartialOpaque) return abstract_call_opaque_closure(interp, ft, argtypes[2:end], sv) - elseif isa(unwrap_unionall(ft), DataType) && unwrap_unionall(ft).name === typename(Core.OpaqueClosure) - return CallMeta(rewrap_unionall(unwrap_unionall(ft).parameters[2], ft), false) + elseif (uft = unwrap_unionall(ft); isa(uft, DataType) && uft.name === typename(Core.OpaqueClosure)) + return CallMeta(rewrap_unionall((uft::DataType).parameters[2], ft), false) elseif f === nothing # non-constant function, but the number of arguments is known # and the ft is not a Builtin or IntrinsicFunction @@ -1534,12 +1546,12 @@ function abstract_eval_statement(interp::AbstractInterpreter, @nospecialize(e), if length(e.args) == 2 && isconcretetype(t) && !ismutabletype(t) at = abstract_eval_value(interp, e.args[2], vtypes, sv) n = fieldcount(t) - if isa(at, Const) && isa(at.val, Tuple) && n == length(at.val) && - let t = t; _all(i->getfield(at.val, i) isa fieldtype(t, i), 1:n); end + if isa(at, Const) && isa(at.val, Tuple) && n == length(at.val::Tuple) && + let t = t; _all(i->getfield(at.val::Tuple, i) isa fieldtype(t, i), 1:n); end t = Const(ccall(:jl_new_structt, Any, (Any, Any), t, at.val)) - elseif isa(at, PartialStruct) && at ⊑ Tuple && n == length(at.fields) && - let t = t, at = at; _all(i->at.fields[i] ⊑ fieldtype(t, i), 1:n); end - t = PartialStruct(t, at.fields) + elseif isa(at, PartialStruct) && at ⊑ Tuple && n == length(at.fields::Vector{Any}) && + let t = t, at = at; _all(i->(at.fields::Vector{Any})[i] ⊑ fieldtype(t, i), 1:n); end + t = PartialStruct(t, at.fields::Vector{Any}) end end elseif e.head === :new_opaque_closure @@ -1587,7 +1599,7 @@ function abstract_eval_statement(interp::AbstractInterpreter, @nospecialize(e), sym = e.args[1] t = Bool if isa(sym, SlotNumber) - vtyp = vtypes[slot_id(sym)] + vtyp = vtypes[slot_id(sym)]::VarState if vtyp.typ === Bottom t = Const(false) # never assigned previously elseif !vtyp.undef @@ -1602,7 +1614,7 @@ function abstract_eval_statement(interp::AbstractInterpreter, @nospecialize(e), t = Const(true) end elseif isa(sym, Expr) && sym.head === :static_parameter - n = sym.args[1] + n = sym.args[1]::Int if 1 <= n <= length(sv.sptypes) spty = sv.sptypes[n] if isa(spty, Const) @@ -1637,7 +1649,7 @@ function abstract_eval_global(M::Module, s::Symbol) end function abstract_eval_ssavalue(s::SSAValue, src::CodeInfo) - typ = src.ssavaluetypes[s.id] + typ = (src.ssavaluetypes::Vector{Any})[s.id] if typ === NOT_FOUND return Bottom end @@ -1725,6 +1737,7 @@ function typeinf_local(interp::AbstractInterpreter, frame::InferenceState) isva = isa(def, Method) && def.isva nslots = nargs - isva slottypes = frame.slottypes + ssavaluetypes = frame.src.ssavaluetypes::Vector{Any} while frame.pc´´ <= n # make progress on the active ip set local pc::Int = frame.pc´´ # current program-counter @@ -1828,7 +1841,7 @@ function typeinf_local(interp::AbstractInterpreter, frame::InferenceState) for (caller, caller_pc) in frame.cycle_backedges # notify backedges of updated type information typeassert(caller.stmt_types[caller_pc], VarTable) # we must have visited this statement before - if !(caller.src.ssavaluetypes[caller_pc] === Any) + if !((caller.src.ssavaluetypes::Vector{Any})[caller_pc] === Any) # no reason to revisit if that call-site doesn't affect the final result if caller_pc < caller.pc´´ caller.pc´´ = caller_pc @@ -1838,6 +1851,7 @@ function typeinf_local(interp::AbstractInterpreter, frame::InferenceState) end end elseif hd === :enter + stmt = stmt::Expr l = stmt.args[1]::Int frame.cur_hand = Pair{Any,Any}(l, frame.cur_hand) # propagate type info to exception handler @@ -1853,21 +1867,24 @@ function typeinf_local(interp::AbstractInterpreter, frame::InferenceState) typeassert(states[l], VarTable) frame.handler_at[l] = frame.cur_hand elseif hd === :leave + stmt = stmt::Expr for i = 1:((stmt.args[1])::Int) frame.cur_hand = (frame.cur_hand::Pair{Any,Any}).second end else if hd === :(=) + stmt = stmt::Expr t = abstract_eval_statement(interp, stmt.args[2], changes, frame) if t === Bottom break end - frame.src.ssavaluetypes[pc] = t + ssavaluetypes[pc] = t lhs = stmt.args[1] if isa(lhs, SlotNumber) changes = StateUpdate(lhs, VarState(t, false), changes, false) end elseif hd === :method + stmt = stmt::Expr fname = stmt.args[1] if isa(fname, SlotNumber) changes = StateUpdate(fname, VarState(Any, false), changes, false) @@ -1882,7 +1899,7 @@ function typeinf_local(interp::AbstractInterpreter, frame::InferenceState) if !isempty(frame.ssavalue_uses[pc]) record_ssa_assign(pc, t, frame) else - frame.src.ssavaluetypes[pc] = t + ssavaluetypes[pc] = t end end if frame.cur_hand !== nothing && isa(changes, StateUpdate) @@ -1903,7 +1920,7 @@ function typeinf_local(interp::AbstractInterpreter, frame::InferenceState) if t === nothing # mark other reached expressions as `Any` to indicate they don't throw - frame.src.ssavaluetypes[pc] = Any + ssavaluetypes[pc] = Any end pc´ > n && break # can't proceed with the fast-path fall-through diff --git a/base/compiler/inferencestate.jl b/base/compiler/inferencestate.jl index a3f2b1ea95d27..d76a4ebd18f6e 100644 --- a/base/compiler/inferencestate.jl +++ b/base/compiler/inferencestate.jl @@ -179,7 +179,7 @@ function sptypes_from_meth_instance(linfo::MethodInstance) while temp isa UnionAll temp = temp.body end - sigtypes = temp.parameters + sigtypes = (temp::DataType).parameters for j = 1:length(sigtypes) tj = sigtypes[j] if isType(tj) && tj.parameters[1] === Pi diff --git a/base/compiler/ssair/legacy.jl b/base/compiler/ssair/legacy.jl index 49d9aef973e29..e9fddd1d12a02 100644 --- a/base/compiler/ssair/legacy.jl +++ b/base/compiler/ssair/legacy.jl @@ -47,7 +47,7 @@ function replace_code_newstyle!(ci::CodeInfo, ir::IRCode, nargs::Int) for metanode in ir.meta push!(ci.code, metanode) push!(ci.codelocs, 1) - push!(ci.ssavaluetypes, Any) + push!(ci.ssavaluetypes::Vector{Any}, Any) push!(ci.ssaflags, 0x00) end # Translate BB Edges to statement edges diff --git a/base/compiler/ssair/passes.jl b/base/compiler/ssair/passes.jl index 37f5d38940e05..838ab5bd21755 100644 --- a/base/compiler/ssair/passes.jl +++ b/base/compiler/ssair/passes.jl @@ -1066,7 +1066,7 @@ function type_lift_pass!(ir::IRCode) if haskey(processed, id) val = processed[id] else - push!(worklist, (id, up_id, new_phi, i)) + push!(worklist, (id, up_id, new_phi::SSAValue, i)) continue end else diff --git a/base/compiler/ssair/slot2ssa.jl b/base/compiler/ssair/slot2ssa.jl index a89b03d04ff59..aad2d288151d4 100644 --- a/base/compiler/ssair/slot2ssa.jl +++ b/base/compiler/ssair/slot2ssa.jl @@ -873,7 +873,7 @@ function construct_ssa!(ci::CodeInfo, ir::IRCode, domtree::DomTree, defuse, narg changed = false for new_idx in type_refine_phi node = new_nodes.stmts[new_idx] - new_typ = recompute_type(node[:inst], ci, ir, ir.sptypes, slottypes) + new_typ = recompute_type(node[:inst]::Union{PhiNode,PhiCNode}, ci, ir, ir.sptypes, slottypes) if !(node[:type] ⊑ new_typ) || !(new_typ ⊑ node[:type]) node[:type] = new_typ changed = true diff --git a/base/compiler/tfuncs.jl b/base/compiler/tfuncs.jl index 09aa05f35b9f0..772db09417393 100644 --- a/base/compiler/tfuncs.jl +++ b/base/compiler/tfuncs.jl @@ -1564,7 +1564,7 @@ function builtin_tfunction(interp::AbstractInterpreter, @nospecialize(f), argtyp if length(argtypes) - 1 == tf[2] argtypes = argtypes[1:end-1] else - vatype = argtypes[end] + vatype = argtypes[end]::Core.TypeofVararg argtypes = argtypes[1:end-1] while length(argtypes) < tf[1] push!(argtypes, unwrapva(vatype)) @@ -1670,7 +1670,7 @@ function return_type_tfunc(interp::AbstractInterpreter, argtypes::Vector{Any}, s aft = argtypes[2] if isa(aft, Const) || (isType(aft) && !has_free_typevars(aft)) || (isconcretetype(aft) && !(aft <: Builtin)) - af_argtype = isa(tt, Const) ? tt.val : tt.parameters[1] + af_argtype = isa(tt, Const) ? tt.val : (tt::DataType).parameters[1] if isa(af_argtype, DataType) && af_argtype <: Tuple argtypes_vec = Any[aft, af_argtype.parameters...] if contains_is(argtypes_vec, Union{}) diff --git a/base/compiler/typeinfer.jl b/base/compiler/typeinfer.jl index 246f66fc64ed8..1d2559cf10c3a 100644 --- a/base/compiler/typeinfer.jl +++ b/base/compiler/typeinfer.jl @@ -243,7 +243,7 @@ function _typeinf(interp::AbstractInterpreter, frame::InferenceState) # collect results for the new expanded frame results = Tuple{InferenceResult, Vector{Any}, Bool}[ ( frames[i].result, - frames[i].stmt_edges[1], + frames[i].stmt_edges[1]::Vector{Any}, frames[i].cached ) for i in 1:length(frames) ] empty!(frames) @@ -341,7 +341,7 @@ function maybe_compress_codeinfo(interp::AbstractInterpreter, linfo::MethodInsta if cache_the_tree if may_compress(interp) nslots = length(ci.slotflags) - resize!(ci.slottypes, nslots) + resize!(ci.slottypes::Vector{Any}, nslots) resize!(ci.slotnames, nslots) return ccall(:jl_compress_ir, Any, (Any, Any), def, ci) else @@ -438,7 +438,7 @@ function finish(me::InferenceState, interp::AbstractInterpreter) empty!(edges) end if me.src.edges !== nothing - append!(s_edges, me.src.edges) + append!(s_edges, me.src.edges::Vector) me.src.edges = nothing end # inspect whether our inference had a limited result accuracy, @@ -447,7 +447,7 @@ function finish(me::InferenceState, interp::AbstractInterpreter) limited_ret = me.bestguess isa LimitedAccuracy limited_src = false if !limited_ret - gt = me.src.ssavaluetypes + gt = me.src.ssavaluetypes::Vector{Any} for j = 1:length(gt) gt[j] = gtj = cycle_fix_limited(gt[j], me) if gtj isa LimitedAccuracy && me.parent !== nothing @@ -511,8 +511,9 @@ end # widen all Const elements in type annotations function widen_all_consts!(src::CodeInfo) - for i = 1:length(src.ssavaluetypes) - src.ssavaluetypes[i] = widenconst(src.ssavaluetypes[i]) + ssavaluetypes = src.ssavaluetypes::Vector{Any} + for i = 1:length(ssavaluetypes) + ssavaluetypes[i] = widenconst(ssavaluetypes[i]) end for i = 1:length(src.code) @@ -577,6 +578,7 @@ function record_slot_assign!(sv::InferenceState) states = sv.stmt_types body = sv.src.code::Vector{Any} slottypes = sv.slottypes::Vector{Any} + ssavaluetypes = sv.src.ssavaluetypes::Vector{Any} for i = 1:length(body) expr = body[i] st_i = states[i] @@ -585,7 +587,7 @@ function record_slot_assign!(sv::InferenceState) lhs = expr.args[1] rhs = expr.args[2] if isa(lhs, SlotNumber) - vt = widenconst(sv.src.ssavaluetypes[i]) + vt = widenconst(ssavaluetypes[i]) if vt !== Bottom id = slot_id(lhs) otherTy = slottypes[id] @@ -608,12 +610,11 @@ function type_annotate!(sv::InferenceState, run_optimizer::Bool) # (otherwise, we'll perhaps run the optimization passes later, outside of inference) # remove all unused ssa values - gt = sv.src.ssavaluetypes - for j = 1:length(gt) - if gt[j] === NOT_FOUND - gt[j] = Union{} - end - gt[j] = widenconditional(gt[j]) + src = sv.src + ssavaluetypes = src.ssavaluetypes::Vector{Any} + for j = 1:length(ssavaluetypes) + t = ssavaluetypes[j] + ssavaluetypes[j] = t === NOT_FOUND ? Union{} : widenconditional(t) end # compute the required type for each slot @@ -626,7 +627,6 @@ function type_annotate!(sv::InferenceState, run_optimizer::Bool) # annotate variables load types # remove dead code optimization # and compute which variables may be used undef - src = sv.src states = sv.stmt_types nargs = sv.nargs nslots = length(states[1]::VarTable) @@ -669,7 +669,7 @@ function type_annotate!(sv::InferenceState, run_optimizer::Bool) elseif run_optimizer deleteat!(body, i) deleteat!(states, i) - deleteat!(src.ssavaluetypes, i) + deleteat!(ssavaluetypes, i) deleteat!(src.codelocs, i) deleteat!(sv.stmt_info, i) nexpr -= 1 diff --git a/base/compiler/typelattice.jl b/base/compiler/typelattice.jl index 6391d4029b58e..9712e023c7004 100644 --- a/base/compiler/typelattice.jl +++ b/base/compiler/typelattice.jl @@ -299,7 +299,7 @@ function smerge(sa::Union{NotFound,VarState}, sb::Union{NotFound,VarState}) end @inline tchanged(@nospecialize(n), @nospecialize(o)) = o === NOT_FOUND || (n !== NOT_FOUND && !(n ⊑ o)) -@inline schanged(@nospecialize(n), @nospecialize(o)) = (n !== o) && (o === NOT_FOUND || (n !== NOT_FOUND && !issubstate(n, o))) +@inline schanged(@nospecialize(n), @nospecialize(o)) = (n !== o) && (o === NOT_FOUND || (n !== NOT_FOUND && !issubstate(n::VarState, o::VarState))) widenconditional(@nospecialize typ) = typ function widenconditional(typ::AnyConditional) @@ -396,7 +396,7 @@ function stupdate1!(state::VarTable, change::StateUpdate) if isa(oldtypetyp, Conditional) && slot_id(oldtypetyp.var) == changeid oldtypetyp = widenconditional(oldtypetyp) if oldtype.typ isa LimitedAccuracy - oldtypetyp = LimitedAccuracy(oldtypetyp, oldtype.typ.causes) + oldtypetyp = LimitedAccuracy(oldtypetyp, (oldtype.typ::LimitedAccuracy).causes) end state[i] = VarState(oldtypetyp, oldtype.undef) end diff --git a/base/compiler/validation.jl b/base/compiler/validation.jl index f6b89f8f5cd04..c152dfb9fa6a5 100644 --- a/base/compiler/validation.jl +++ b/base/compiler/validation.jl @@ -1,7 +1,7 @@ # This file is a part of Julia. License is MIT: https://julialang.org/license # Expr head => argument count bounds -const VALID_EXPR_HEADS = IdDict{Symbol,UnitRange}( +const VALID_EXPR_HEADS = IdDict{Symbol,UnitRange{Int}}( :call => 1:typemax(Int), :invoke => 2:typemax(Int), :static_parameter => 1:1, @@ -180,10 +180,11 @@ function validate_code!(errors::Vector{>:InvalidCodeError}, c::CodeInfo, is_top_ !is_top_level && nslotnames == 0 && push!(errors, InvalidCodeError(EMPTY_SLOTNAMES)) nslotnames < nslotflags && push!(errors, InvalidCodeError(SLOTFLAGS_MISMATCH, (nslotnames, nslotflags))) if c.inferred - nssavaluetypes = length(c.ssavaluetypes) + nssavaluetypes = length(c.ssavaluetypes::Vector{Any}) nssavaluetypes < nssavals && push!(errors, InvalidCodeError(SSAVALUETYPES_MISMATCH, (nssavals, nssavaluetypes))) else - c.ssavaluetypes != nssavals && push!(errors, InvalidCodeError(SSAVALUETYPES_MISMATCH_UNINFERRED, (nssavals, c.ssavaluetypes))) + ssavaluetypes = c.ssavaluetypes::Int + ssavaluetypes != nssavals && push!(errors, InvalidCodeError(SSAVALUETYPES_MISMATCH_UNINFERRED, (nssavals, ssavaluetypes))) end return errors end @@ -205,7 +206,7 @@ function validate_code!(errors::Vector{>:InvalidCodeError}, mi::Core.MethodInsta else m = mi.def::Method mnargs = m.nargs - n_sig_params = length(Core.Compiler.unwrap_unionall(m.sig).parameters) + n_sig_params = length((unwrap_unionall(m.sig)::DataType).parameters) if (m.isva ? (n_sig_params < (mnargs - 1)) : (n_sig_params != mnargs)) push!(errors, InvalidCodeError(SIGNATURE_NARGS_MISMATCH, (m.isva, n_sig_params, mnargs))) end