diff --git a/base/compiler/abstractinterpretation.jl b/base/compiler/abstractinterpretation.jl index a8325a917e0fb..64a2cb363526a 100644 --- a/base/compiler/abstractinterpretation.jl +++ b/base/compiler/abstractinterpretation.jl @@ -125,19 +125,16 @@ function abstract_call_gf_by_type(interp::AbstractInterpreter, @nospecialize(f), splitsigs = switchtupleunion(sig) for sig_n in splitsigs result = abstract_call_method(interp, method, sig_n, svec(), multiple_matches, sv) - rt = result.rt - edge = result.edge + (; rt, edge, effects) = result edge !== nothing && push!(edges, edge) this_argtypes = isa(matches, MethodMatches) ? argtypes : matches.applicable_argtypes[i] this_arginfo = ArgInfo(fargs, this_argtypes) const_call_result = abstract_call_method_with_const_args(interp, result, f, this_arginfo, match, sv) - effects = result.edge_effects const_result = nothing if const_call_result !== nothing - const_rt = const_call_result.rt - if const_rt ⊑ rt - rt = const_rt + if const_call_result.rt ⊑ rt + rt = const_call_result.rt (; effects, const_result) = const_call_result end end @@ -167,9 +164,9 @@ function abstract_call_gf_by_type(interp::AbstractInterpreter, @nospecialize(f), end result = abstract_call_method(interp, method, sig, match.sparams, multiple_matches, sv) - this_conditional = ignorelimited(result.rt) - this_rt = widenwrappedconditional(result.rt) - edge = result.edge + (; rt, edge, effects) = result + this_conditional = ignorelimited(rt) + this_rt = widenwrappedconditional(rt) edge !== nothing && push!(edges, edge) # try constant propagation with argtypes for this match # this is in preparation for inlining, or improving the return result @@ -177,7 +174,6 @@ function abstract_call_gf_by_type(interp::AbstractInterpreter, @nospecialize(f), this_arginfo = ArgInfo(fargs, this_argtypes) const_call_result = abstract_call_method_with_const_args(interp, result, f, this_arginfo, match, sv) - effects = result.edge_effects const_result = nothing if const_call_result !== nothing this_const_conditional = ignorelimited(const_call_result.rt) @@ -606,7 +602,8 @@ function abstract_call_method(interp::AbstractInterpreter, method::Method, @nosp sparams = recomputed[2]::SimpleVector end - (; rt, edge, edge_effects) = typeinf_edge(interp, method, sig, sparams, sv) + (; rt, edge, effects) = typeinf_edge(interp, method, sig, sparams, sv) + if edge === nothing edgecycle = edgelimited = true end @@ -615,16 +612,17 @@ function abstract_call_method(interp::AbstractInterpreter, method::Method, @nosp # may have been tainted due to recursion at this point even if it's overridden if is_effect_overridden(sv, :terminates_globally) # this frame is known to terminate - edge_effects = Effects(edge_effects, terminates=ALWAYS_TRUE) + effects = Effects(effects, terminates=ALWAYS_TRUE) elseif is_effect_overridden(method, :terminates_globally) # this edge is known to terminate - edge_effects = Effects(edge_effects; terminates=ALWAYS_TRUE) + effects = Effects(effects; terminates=ALWAYS_TRUE) elseif edgecycle # Some sort of recursion was detected. Even if we did not limit types, # we cannot guarantee that the call will terminate - edge_effects = Effects(edge_effects; terminates=TRISTATE_UNKNOWN) + effects = Effects(effects; terminates=TRISTATE_UNKNOWN) end - return MethodCallResult(rt, edgecycle, edgelimited, edge, edge_effects) + + return MethodCallResult(rt, edgecycle, edgelimited, edge, effects) end function edge_matches_sv(frame::InferenceState, method::Method, @nospecialize(sig), sparams::SimpleVector, hardlimit::Bool, sv::InferenceState) @@ -700,13 +698,13 @@ struct MethodCallResult edgecycle::Bool edgelimited::Bool edge::Union{Nothing,MethodInstance} - edge_effects::Effects + effects::Effects function MethodCallResult(@nospecialize(rt), edgecycle::Bool, edgelimited::Bool, edge::Union{Nothing,MethodInstance}, - edge_effects::Effects) - return new(rt, edgecycle, edgelimited, edge, edge_effects) + effects::Effects) + return new(rt, edgecycle, edgelimited, edge, effects) end end @@ -751,10 +749,10 @@ function concrete_eval_eligible(interp::AbstractInterpreter, @nospecialize(f), result::MethodCallResult, arginfo::ArgInfo, sv::InferenceState) # disable concrete-evaluation if this function call is tainted by some overlayed # method since currently there is no direct way to execute overlayed methods - isoverlayed(method_table(interp)) && !is_nonoverlayed(result.edge_effects) && return false + isoverlayed(method_table(interp)) && !is_nonoverlayed(result.effects) && return false return f !== nothing && result.edge !== nothing && - is_foldable(result.edge_effects) && + is_foldable(result.effects) && is_all_const_arg(arginfo) end @@ -785,7 +783,7 @@ function concrete_eval_call(interp::AbstractInterpreter, Core._call_in_world_total(world, f, args...) catch # The evaulation threw. By :consistent-cy, we're guaranteed this would have happened at runtime - return ConstCallResults(Union{}, ConcreteResult(result.edge::MethodInstance, result.edge_effects), result.edge_effects) + return ConstCallResults(Union{}, ConcreteResult(result.edge::MethodInstance, result.effects), result.effects) end if is_inlineable_constant(value) || call_result_unused(sv) # If the constant is not inlineable, still do the const-prop, since the @@ -929,7 +927,7 @@ function const_prop_entry_heuristic(interp::AbstractInterpreter, result::MethodC return false else if isa(rt, Const) - if result.edge_effects.nothrow !== ALWAYS_TRUE + if result.effects.nothrow !== ALWAYS_TRUE # Could still be improved to Bottom (or at least could see the effects improved) return true end @@ -1596,8 +1594,8 @@ function abstract_invoke(interp::AbstractInterpreter, (; fargs, argtypes)::ArgIn method = match.method tienv = ccall(:jl_type_intersection_with_env, Any, (Any, Any), nargtype, method.sig)::SimpleVector ti = tienv[1]; env = tienv[2]::SimpleVector - (; rt, edge) = result = abstract_call_method(interp, method, ti, env, false, sv) - effects = result.edge_effects + result = abstract_call_method(interp, method, ti, env, false, sv) + (; rt, edge, effects) = result edge !== nothing && add_backedge!(edge::MethodInstance, sv) match = MethodMatch(ti, env, method, argtype <: method.sig) res = nothing @@ -1746,9 +1744,11 @@ function abstract_call_known(interp::AbstractInterpreter, @nospecialize(f), return abstract_call_gf_by_type(interp, f, arginfo, atype, sv, max_methods) end -function abstract_call_opaque_closure(interp::AbstractInterpreter, closure::PartialOpaque, arginfo::ArgInfo, sv::InferenceState) +function abstract_call_opaque_closure(interp::AbstractInterpreter, + closure::PartialOpaque, arginfo::ArgInfo, sv::InferenceState, check::Bool=true) sig = argtypes_to_type(arginfo.argtypes) - (; rt, edge, edge_effects) = result = abstract_call_method(interp, closure.source, sig, Core.svec(), false, sv) + result = abstract_call_method(interp, closure.source, sig, Core.svec(), false, sv) + (; rt, edge, effects) = result edge !== nothing && add_backedge!(edge, sv) tt = closure.typ sigT = (unwrap_unionall(tt)::DataType).parameters[1] @@ -1759,12 +1759,21 @@ function abstract_call_opaque_closure(interp::AbstractInterpreter, closure::Part nothing, arginfo, match, sv) if const_call_result !== nothing if const_call_result.rt ⊑ rt - (; rt, const_result) = const_call_result + (; rt, effects, const_result) = const_call_result end end end info = OpaqueClosureCallInfo(match, const_result) - return CallMeta(from_interprocedural!(rt, sv, arginfo, match.spec_types), edge_effects, info) + if check # analyze implicit type asserts on argument and return type + ftt = closure.typ + (aty, rty) = (unwrap_unionall(ftt)::DataType).parameters + rty = rewrap_unionall(rty isa TypeVar ? rty.lb : rty, ftt) + if !(rt ⊑ rty && tuple_tfunc(arginfo.argtypes[2:end]) ⊑ rewrap_unionall(aty, ftt)) + effects = Effects(effects; nothrow=TRISTATE_UNKNOWN) + end + end + rt = from_interprocedural!(rt, sv, arginfo, match.spec_types) + return CallMeta(rt, effects, info) end function most_general_argtypes(closure::PartialOpaque) @@ -1786,22 +1795,8 @@ function abstract_call(interp::AbstractInterpreter, arginfo::ArgInfo, if isa(ft, PartialOpaque) newargtypes = copy(argtypes) newargtypes[1] = ft.env - body_call = abstract_call_opaque_closure(interp, ft, ArgInfo(arginfo.fargs, newargtypes), sv) - # Analyze implicit type asserts on argument and return type - ftt = ft.typ - (at, rt) = (unwrap_unionall(ftt)::DataType).parameters - if isa(rt, TypeVar) - rt = rewrap_unionall(rt.lb, ftt) - else - rt = rewrap_unionall(rt, ftt) - end - nothrow = body_call.rt ⊑ rt - if nothrow - nothrow = tuple_tfunc(newargtypes[2:end]) ⊑ rewrap_unionall(at, ftt) - end - return CallMeta(body_call.rt, Effects(body_call.effects, - nothrow = nothrow ? TRISTATE_UNKNOWN : body_call.effects.nothrow), - body_call.info) + return abstract_call_opaque_closure(interp, + ft, ArgInfo(arginfo.fargs, newargtypes), sv, #=check=#true) elseif (uft = unwrap_unionall(widenconst(ft)); isa(uft, DataType) && uft.name === typename(Core.OpaqueClosure)) return CallMeta(rewrap_unionall((uft::DataType).parameters[2], widenconst(ft)), Effects(), false) elseif f === nothing @@ -2027,7 +2022,7 @@ function abstract_eval_statement(interp::AbstractInterpreter, @nospecialize(e), argtypes = most_general_argtypes(t) pushfirst!(argtypes, t.env) callinfo = abstract_call_opaque_closure(interp, t, - ArgInfo(nothing, argtypes), sv) + ArgInfo(nothing, argtypes), sv, #=check=#false) sv.stmt_info[sv.currpc] = OpaqueClosureCreateInfo(callinfo) end end diff --git a/base/compiler/typeinfer.jl b/base/compiler/typeinfer.jl index 97e8a0cfa1d29..a8bff1eba28fb 100644 --- a/base/compiler/typeinfer.jl +++ b/base/compiler/typeinfer.jl @@ -840,11 +840,11 @@ ipo_effects(code::CodeInstance) = decode_effects(code.ipo_purity_bits) struct EdgeCallResult rt #::Type edge::Union{Nothing,MethodInstance} - edge_effects::Effects + effects::Effects function EdgeCallResult(@nospecialize(rt), edge::Union{Nothing,MethodInstance}, - edge_effects::Effects) - return new(rt, edge, edge_effects) + effects::Effects) + return new(rt, edge, effects) end end diff --git a/base/compiler/utilities.jl b/base/compiler/utilities.jl index 3a2dc6e00f7a3..7ef006f244aa6 100644 --- a/base/compiler/utilities.jl +++ b/base/compiler/utilities.jl @@ -184,7 +184,7 @@ function normalize_typevars(method::Method, @nospecialize(atype), sparams::Simpl sp_ = ccall(:jl_type_intersection_with_env, Any, (Any, Any), at2, method.sig)::SimpleVector sparams = sp_[2]::SimpleVector end - return atype, sparams + return Pair{Any,SimpleVector}(atype, sparams) end # get a handle to the unique specialization object representing a particular instantiation of a call diff --git a/base/reflection.jl b/base/reflection.jl index c0bb28190cd62..55589399fdb35 100644 --- a/base/reflection.jl +++ b/base/reflection.jl @@ -1108,10 +1108,10 @@ const SLOT_USED = 0x8 ast_slotflag(@nospecialize(code), i) = ccall(:jl_ir_slotflag, UInt8, (Any, Csize_t), code, i - 1) """ - may_invoke_generator(method, atype, sparams) + may_invoke_generator(method, atype, sparams) -> Bool Computes whether or not we may invoke the generator for the given `method` on -the given atype and sparams. For correctness, all generated function are +the given `atype` and `sparams`. For correctness, all generated function are required to return monotonic answers. However, since we don't expect users to be able to successfully implement this criterion, we only call generated functions on concrete types. The one exception to this is that we allow calling @@ -1122,8 +1122,8 @@ computes whether we are in either of these cases. Unlike normal functions, the compilation heuristics still can't generate good dispatch in some cases, but this may still allow inference not to fall over in some limited cases. """ -function may_invoke_generator(method::MethodInstance) - return may_invoke_generator(method.def::Method, method.specTypes, method.sparam_vals) +function may_invoke_generator(mi::MethodInstance) + return may_invoke_generator(mi.def::Method, mi.specTypes, mi.sparam_vals) end function may_invoke_generator(method::Method, @nospecialize(atype), sparams::SimpleVector) # If we have complete information, we may always call the generator