Skip to content

Commit 1110f2b

Browse files
Kenoaviatesk
authored andcommitted
Merge pull request #45299 from JuliaLang/kf/rt_effect_free
Fix effects modeling for return_type
1 parent 8dc9f58 commit 1110f2b

File tree

6 files changed

+93
-74
lines changed

6 files changed

+93
-74
lines changed

base/compiler/abstractinterpretation.jl

Lines changed: 58 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -55,17 +55,16 @@ function abstract_call_gf_by_type(interp::AbstractInterpreter, @nospecialize(f),
5555
# At this point we are guaranteed to end up throwing on this path,
5656
# which is all that's required for :consistent-cy. Of course, we don't
5757
# know anything else about this statement.
58-
tristate_merge!(sv, Effects(; consistent=ALWAYS_TRUE, nonoverlayed))
59-
return CallMeta(Any, false)
58+
effects = Effects(; consistent=ALWAYS_TRUE, nonoverlayed)
59+
return CallMeta(Any, effects, false)
6060
end
6161

6262
argtypes = arginfo.argtypes
6363
matches = find_matching_methods(argtypes, atype, method_table(interp),
6464
InferenceParams(interp).MAX_UNION_SPLITTING, max_methods)
6565
if isa(matches, FailedMethodMatch)
6666
add_remark!(interp, sv, matches.reason)
67-
tristate_merge!(sv, Effects())
68-
return CallMeta(Any, false)
67+
return CallMeta(Any, Effects(), false)
6968
end
7069

7170
(; valid_worlds, applicable, info) = matches
@@ -89,7 +88,7 @@ function abstract_call_gf_by_type(interp::AbstractInterpreter, @nospecialize(f),
8988

9089
# try pure-evaluation
9190
val = pure_eval_call(interp, f, applicable, arginfo, sv)
92-
val !== nothing && return CallMeta(val, MethodResultPure(info)) # TODO: add some sort of edge(s)
91+
val !== nothing && return CallMeta(val, all_effects, MethodResultPure(info)) # TODO: add some sort of edge(s)
9392

9493
for i in 1:napplicable
9594
match = applicable[i]::MethodMatch
@@ -232,8 +231,7 @@ function abstract_call_gf_by_type(interp::AbstractInterpreter, @nospecialize(f),
232231
delete!(sv.pclimitations, caller)
233232
end
234233
end
235-
tristate_merge!(sv, all_effects)
236-
return CallMeta(rettype, info)
234+
return CallMeta(rettype, all_effects, info)
237235
end
238236

239237
struct FailedMethodMatch
@@ -1160,7 +1158,7 @@ function abstract_iteration(interp::AbstractInterpreter, @nospecialize(itft), @n
11601158
# WARNING: Changes to the iteration protocol must be reflected here,
11611159
# this is not just an optimization.
11621160
# TODO: this doesn't realize that Array, SimpleVector, Tuple, and NamedTuple do not use the iterate protocol
1163-
stateordonet === Bottom && return Any[Bottom], AbstractIterationInfo(CallMeta[CallMeta(Bottom, info)])
1161+
stateordonet === Bottom && return Any[Bottom], AbstractIterationInfo(CallMeta[CallMeta(Bottom, call.effects, info)])
11641162
valtype = statetype = Bottom
11651163
ret = Any[]
11661164
calls = CallMeta[call]
@@ -1236,23 +1234,23 @@ function abstract_apply(interp::AbstractInterpreter, argtypes::Vector{Any}, sv::
12361234
max_methods::Int = get_max_methods(sv.mod, interp))
12371235
itft = argtype_by_index(argtypes, 2)
12381236
aft = argtype_by_index(argtypes, 3)
1239-
(itft === Bottom || aft === Bottom) && return CallMeta(Bottom, false)
1237+
(itft === Bottom || aft === Bottom) && return CallMeta(Bottom, EFFECTS_THROWS, false)
12401238
aargtypes = argtype_tail(argtypes, 4)
12411239
aftw = widenconst(aft)
12421240
if !isa(aft, Const) && !isa(aft, PartialOpaque) && (!isType(aftw) || has_free_typevars(aftw))
12431241
if !isconcretetype(aftw) || (aftw <: Builtin)
12441242
add_remark!(interp, sv, "Core._apply_iterate called on a function of a non-concrete type")
1245-
tristate_merge!(sv, Effects())
12461243
# bail now, since it seems unlikely that abstract_call will be able to do any better after splitting
12471244
# this also ensures we don't call abstract_call_gf_by_type below on an IntrinsicFunction or Builtin
1248-
return CallMeta(Any, false)
1245+
return CallMeta(Any, Effects(), false)
12491246
end
12501247
end
12511248
res = Union{}
12521249
nargs = length(aargtypes)
12531250
splitunions = 1 < unionsplitcost(aargtypes) <= InferenceParams(interp).MAX_APPLY_UNION_ENUM
12541251
ctypes = [Any[aft]]
12551252
infos = Vector{MaybeAbstractIterationInfo}[MaybeAbstractIterationInfo[]]
1253+
effects = EFFECTS_TOTAL
12561254
for i = 1:nargs
12571255
ctypes´ = Vector{Any}[]
12581256
infos′ = Vector{MaybeAbstractIterationInfo}[]
@@ -1315,6 +1313,7 @@ function abstract_apply(interp::AbstractInterpreter, argtypes::Vector{Any}, sv::
13151313
call = abstract_call(interp, ArgInfo(nothing, ct), sv, max_methods)
13161314
push!(retinfos, ApplyCallInfo(call.info, arginfo))
13171315
res = tmerge(res, call.rt)
1316+
effects = tristate_merge(effects, call.effects)
13181317
if bail_out_apply(interp, res, sv)
13191318
if i != length(ctypes)
13201319
# No point carrying forward the info, we're not gonna inline it anyway
@@ -1325,7 +1324,7 @@ function abstract_apply(interp::AbstractInterpreter, argtypes::Vector{Any}, sv::
13251324
end
13261325
# TODO: Add a special info type to capture all the iteration info.
13271326
# For now, only propagate info if we don't also union-split the iteration
1328-
return CallMeta(res, retinfo)
1327+
return CallMeta(res, effects, retinfo)
13291328
end
13301329

13311330
function argtype_by_index(argtypes::Vector{Any}, i::Int)
@@ -1506,21 +1505,21 @@ end
15061505
function abstract_invoke(interp::AbstractInterpreter, (; fargs, argtypes)::ArgInfo, sv::InferenceState)
15071506
ft′ = argtype_by_index(argtypes, 2)
15081507
ft = widenconst(ft′)
1509-
ft === Bottom && return CallMeta(Bottom, false), EFFECTS_THROWS
1508+
ft === Bottom && return CallMeta(Bottom, EFFECTS_THROWS, false)
15101509
(types, isexact, isconcrete, istype) = instanceof_tfunc(argtype_by_index(argtypes, 3))
1511-
types === Bottom && return CallMeta(Bottom, false), EFFECTS_THROWS
1512-
isexact || return CallMeta(Any, false), Effects()
1510+
types === Bottom && return CallMeta(Bottom, EFFECTS_THROWS, false)
1511+
isexact || return CallMeta(Any, Effects(), false)
15131512
argtype = argtypes_to_type(argtype_tail(argtypes, 4))
15141513
nargtype = typeintersect(types, argtype)
1515-
nargtype === Bottom && return CallMeta(Bottom, false), EFFECTS_THROWS
1516-
nargtype isa DataType || return CallMeta(Any, false), Effects() # other cases are not implemented below
1517-
isdispatchelem(ft) || return CallMeta(Any, false), Effects() # check that we might not have a subtype of `ft` at runtime, before doing supertype lookup below
1514+
nargtype === Bottom && return CallMeta(Bottom, EFFECTS_THROWS, false)
1515+
nargtype isa DataType || return CallMeta(Any, Effects(), false) # other cases are not implemented below
1516+
isdispatchelem(ft) || return CallMeta(Any, Effects(), false) # check that we might not have a subtype of `ft` at runtime, before doing supertype lookup below
15181517
ft = ft::DataType
15191518
types = rewrap_unionall(Tuple{ft, unwrap_unionall(types).parameters...}, types)::Type
15201519
nargtype = Tuple{ft, nargtype.parameters...}
15211520
argtype = Tuple{ft, argtype.parameters...}
15221521
match, valid_worlds, overlayed = findsup(types, method_table(interp))
1523-
match === nothing && return CallMeta(Any, false), Effects()
1522+
match === nothing && return CallMeta(Any, Effects(), false)
15241523
update_valid_age!(sv, valid_worlds)
15251524
method = match.method
15261525
(ti, env::SimpleVector) = ccall(:jl_type_intersection_with_env, Any, (Any, Any), nargtype, method.sig)::SimpleVector
@@ -1547,7 +1546,7 @@ function abstract_invoke(interp::AbstractInterpreter, (; fargs, argtypes)::ArgIn
15471546
end
15481547
end
15491548
effects = Effects(effects; nonoverlayed=!overlayed)
1550-
return CallMeta(from_interprocedural!(rt, sv, arginfo, sig), InvokeCallInfo(match, const_result)), effects
1549+
return CallMeta(from_interprocedural!(rt, sv, arginfo, sig), effects, InvokeCallInfo(match, const_result))
15511550
end
15521551

15531552
function invoke_rewrite(xs::Vector{Any})
@@ -1568,37 +1567,30 @@ function abstract_call_known(interp::AbstractInterpreter, @nospecialize(f),
15681567
if f === _apply_iterate
15691568
return abstract_apply(interp, argtypes, sv, max_methods)
15701569
elseif f === invoke
1571-
call, effects = abstract_invoke(interp, arginfo, sv)
1572-
tristate_merge!(sv, effects)
1573-
return call
1570+
return abstract_invoke(interp, arginfo, sv)
15741571
elseif f === modifyfield!
1575-
tristate_merge!(sv, Effects()) # TODO
15761572
return abstract_modifyfield!(interp, argtypes, sv)
15771573
end
15781574
rt = abstract_call_builtin(interp, f, arginfo, sv, max_methods)
1579-
tristate_merge!(sv, builtin_effects(f, argtypes, rt))
1580-
return CallMeta(rt, false)
1575+
return CallMeta(rt, builtin_effects(f, argtypes, rt), false)
15811576
elseif isa(f, Core.OpaqueClosure)
15821577
# calling an OpaqueClosure about which we have no information returns no information
1583-
tristate_merge!(sv, Effects())
1584-
return CallMeta(Any, false)
1578+
return CallMeta(Any, Effects(), false)
15851579
elseif f === Core.kwfunc
15861580
if la == 2
15871581
aty = argtypes[2]
15881582
if !isvarargtype(aty)
15891583
ft = widenconst(aty)
15901584
if isa(ft, DataType) && isdefined(ft.name, :mt) && isdefined(ft.name.mt, :kwsorter)
1591-
return CallMeta(Const(ft.name.mt.kwsorter), MethodResultPure())
1585+
return CallMeta(Const(ft.name.mt.kwsorter), EFFECTS_TOTAL, MethodResultPure())
15921586
end
15931587
end
15941588
end
1595-
tristate_merge!(sv, EFFECTS_UNKNOWN) # TODO
1596-
return CallMeta(Any, false)
1589+
return CallMeta(Any, EFFECTS_UNKNOWN, false)
15971590
elseif f === TypeVar
15981591
# Manually look through the definition of TypeVar to
15991592
# make sure to be able to get `PartialTypeVar`s out.
1600-
tristate_merge!(sv, EFFECTS_UNKNOWN) # TODO
1601-
(la < 2 || la > 4) && return CallMeta(Union{}, false)
1593+
(la < 2 || la > 4) && return CallMeta(Union{}, EFFECTS_UNKNOWN, false)
16021594
n = argtypes[2]
16031595
ub_var = Const(Any)
16041596
lb_var = Const(Union{})
@@ -1608,36 +1600,33 @@ function abstract_call_known(interp::AbstractInterpreter, @nospecialize(f),
16081600
elseif la == 3
16091601
ub_var = argtypes[3]
16101602
end
1611-
return CallMeta(typevar_tfunc(n, lb_var, ub_var), false)
1603+
return CallMeta(typevar_tfunc(n, lb_var, ub_var), EFFECTS_UNKNOWN, false)
16121604
elseif f === UnionAll
1613-
tristate_merge!(sv, EFFECTS_UNKNOWN) # TODO
1614-
return CallMeta(abstract_call_unionall(argtypes), false)
1605+
return CallMeta(abstract_call_unionall(argtypes), EFFECTS_UNKNOWN, false)
16151606
elseif f === Tuple && la == 2
1616-
tristate_merge!(sv, EFFECTS_UNKNOWN) # TODO
16171607
aty = argtypes[2]
16181608
ty = isvarargtype(aty) ? unwrapva(aty) : widenconst(aty)
16191609
if !isconcretetype(ty)
1620-
return CallMeta(Tuple, false)
1610+
return CallMeta(Tuple, EFFECTS_UNKNOWN, false)
16211611
end
16221612
elseif is_return_type(f)
1623-
tristate_merge!(sv, EFFECTS_UNKNOWN) # TODO
16241613
return return_type_tfunc(interp, argtypes, sv)
16251614
elseif la == 2 && istopfunction(f, :!)
16261615
# handle Conditional propagation through !Bool
16271616
aty = argtypes[2]
16281617
if isa(aty, Conditional)
16291618
call = abstract_call_gf_by_type(interp, f, ArgInfo(fargs, Any[Const(f), Bool]), Tuple{typeof(f), Bool}, sv, max_methods) # make sure we've inferred `!(::Bool)`
1630-
return CallMeta(Conditional(aty.var, aty.elsetype, aty.vtype), call.info)
1619+
return CallMeta(Conditional(aty.var, aty.elsetype, aty.vtype), call.effects, call.info)
16311620
end
16321621
elseif la == 3 && istopfunction(f, :!==)
16331622
# mark !== as exactly a negated call to ===
16341623
rty = abstract_call_known(interp, (===), arginfo, sv, max_methods).rt
16351624
if isa(rty, Conditional)
1636-
return CallMeta(Conditional(rty.var, rty.elsetype, rty.vtype), false) # swap if-else
1625+
return CallMeta(Conditional(rty.var, rty.elsetype, rty.vtype), EFFECTS_TOTAL, false) # swap if-else
16371626
elseif isa(rty, Const)
1638-
return CallMeta(Const(rty.val === false), MethodResultPure())
1627+
return CallMeta(Const(rty.val === false), EFFECTS_TOTAL, MethodResultPure())
16391628
end
1640-
return CallMeta(rty, false)
1629+
return CallMeta(rty, EFFECTS_TOTAL, false)
16411630
elseif la == 3 && istopfunction(f, :(>:))
16421631
# mark issupertype as a exact alias for issubtype
16431632
# swap T1 and T2 arguments and call <:
@@ -1647,28 +1636,28 @@ function abstract_call_known(interp::AbstractInterpreter, @nospecialize(f),
16471636
fargs = nothing
16481637
end
16491638
argtypes = Any[typeof(<:), argtypes[3], argtypes[2]]
1650-
return CallMeta(abstract_call_known(interp, <:, ArgInfo(fargs, argtypes), sv, max_methods).rt, false)
1639+
return CallMeta(abstract_call_known(interp, <:, ArgInfo(fargs, argtypes), sv, max_methods).rt, EFFECTS_TOTAL, false)
16511640
elseif la == 2 &&
16521641
(a2 = argtypes[2]; isa(a2, Const)) && (svecval = a2.val; isa(svecval, SimpleVector)) &&
16531642
istopfunction(f, :length)
16541643
# mark length(::SimpleVector) as @pure
1655-
return CallMeta(Const(length(svecval)), MethodResultPure())
1644+
return CallMeta(Const(length(svecval)), EFFECTS_TOTAL, MethodResultPure())
16561645
elseif la == 3 &&
16571646
(a2 = argtypes[2]; isa(a2, Const)) && (svecval = a2.val; isa(svecval, SimpleVector)) &&
16581647
(a3 = argtypes[3]; isa(a3, Const)) && (idx = a3.val; isa(idx, Int)) &&
16591648
istopfunction(f, :getindex)
16601649
# mark getindex(::SimpleVector, i::Int) as @pure
16611650
if 1 <= idx <= length(svecval) && isassigned(svecval, idx)
1662-
return CallMeta(Const(getindex(svecval, idx)), MethodResultPure())
1651+
return CallMeta(Const(getindex(svecval, idx)), EFFECTS_TOTAL, MethodResultPure())
16631652
end
16641653
elseif la == 2 && istopfunction(f, :typename)
1665-
return CallMeta(typename_static(argtypes[2]), MethodResultPure())
1654+
return CallMeta(typename_static(argtypes[2]), EFFECTS_TOTAL, MethodResultPure())
16661655
elseif max_methods > 1 && istopfunction(f, :copyto!)
16671656
max_methods = 1
16681657
elseif la == 3 && istopfunction(f, :typejoin)
16691658
if is_all_const_arg(arginfo)
16701659
val = _pure_eval_call(f, arginfo)
1671-
return CallMeta(val === nothing ? Type : val, MethodResultPure())
1660+
return CallMeta(val === nothing ? Type : val, EFFECTS_TOTAL, MethodResultPure())
16721661
end
16731662
end
16741663
atype = argtypes_to_type(argtypes)
@@ -1677,7 +1666,7 @@ end
16771666

16781667
function abstract_call_opaque_closure(interp::AbstractInterpreter, closure::PartialOpaque, arginfo::ArgInfo, sv::InferenceState)
16791668
sig = argtypes_to_type(arginfo.argtypes)
1680-
(; rt, edge) = result = abstract_call_method(interp, closure.source, sig, Core.svec(), false, sv)
1669+
(; rt, edge, edge_effects) = result = abstract_call_method(interp, closure.source, sig, Core.svec(), false, sv)
16811670
edge !== nothing && add_backedge!(edge, sv)
16821671
tt = closure.typ
16831672
sigT = (unwrap_unionall(tt)::DataType).parameters[1]
@@ -1693,7 +1682,7 @@ function abstract_call_opaque_closure(interp::AbstractInterpreter, closure::Part
16931682
end
16941683
end
16951684
info = OpaqueClosureCallInfo(match, const_result)
1696-
return CallMeta(from_interprocedural!(rt, sv, arginfo, match.spec_types), info)
1685+
return CallMeta(from_interprocedural!(rt, sv, arginfo, match.spec_types), edge_effects, info)
16971686
end
16981687

16991688
function most_general_argtypes(closure::PartialOpaque)
@@ -1715,18 +1704,30 @@ function abstract_call(interp::AbstractInterpreter, arginfo::ArgInfo,
17151704
if isa(ft, PartialOpaque)
17161705
newargtypes = copy(argtypes)
17171706
newargtypes[1] = ft.env
1718-
tristate_merge!(sv, Effects()) # TODO
1719-
return abstract_call_opaque_closure(interp, ft, ArgInfo(arginfo.fargs, newargtypes), sv)
1707+
body_call = abstract_call_opaque_closure(interp, ft, ArgInfo(arginfo.fargs, newargtypes), sv)
1708+
# Analyze implicit type asserts on argument and return type
1709+
ftt = ft.typ
1710+
(at, rt) = unwrap_unionall(ftt).parameters
1711+
if isa(rt, TypeVar)
1712+
rt = rewrap_unionall(rt.lb, ftt)
1713+
else
1714+
rt = rewrap_unionall(rt, ftt)
1715+
end
1716+
nothrow = body_call.rt rt
1717+
if nothrow
1718+
nothrow = tuple_tfunc(newargtypes[2:end]) rewrap_unionall(at, ftt)
1719+
end
1720+
return CallMeta(body_call.rt, Effects(body_call.effects,
1721+
nothrow = nothrow ? TRISTATE_UNKNOWN : body_call.effects.nothrow),
1722+
body_call.info)
17201723
elseif (uft = unwrap_unionall(widenconst(ft)); isa(uft, DataType) && uft.name === typename(Core.OpaqueClosure))
1721-
tristate_merge!(sv, Effects()) # TODO
1722-
return CallMeta(rewrap_unionall((uft::DataType).parameters[2], widenconst(ft)), false)
1724+
return CallMeta(rewrap_unionall((uft::DataType).parameters[2], widenconst(ft)), Effects(), false)
17231725
elseif f === nothing
17241726
# non-constant function, but the number of arguments is known
17251727
# and the ft is not a Builtin or IntrinsicFunction
17261728
if hasintersect(widenconst(ft), Union{Builtin, Core.OpaqueClosure})
1727-
tristate_merge!(sv, Effects())
17281729
add_remark!(interp, sv, "Could not identify method table for call")
1729-
return CallMeta(Any, false)
1730+
return CallMeta(Any, Effects(), false)
17301731
end
17311732
return abstract_call_gf_by_type(interp, nothing, arginfo, argtypes_to_type(argtypes), sv, max_methods)
17321733
end
@@ -1852,6 +1853,7 @@ function abstract_eval_statement(interp::AbstractInterpreter, @nospecialize(e),
18521853
t = Bottom
18531854
else
18541855
callinfo = abstract_call(interp, ArgInfo(ea, argtypes), sv)
1856+
tristate_merge!(sv, callinfo.effects)
18551857
sv.stmt_info[sv.currpc] = callinfo.info
18561858
t = callinfo.rt
18571859
end

base/compiler/optimize.jl

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -202,7 +202,6 @@ function stmt_effect_free(@nospecialize(stmt), @nospecialize(rt), src::Union{IRC
202202
f = argextype(args[1], src)
203203
f = singleton_type(f)
204204
f === nothing && return false
205-
is_return_type(f) && return true
206205
if isa(f, IntrinsicFunction)
207206
intrinsic_effect_free_if_nothrow(f) || return false
208207
return intrinsic_nothrow(f,

base/compiler/ssair/inlining.jl

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1166,9 +1166,6 @@ function process_simple!(ir::IRCode, idx::Int, state::InliningState, todo::Vecto
11661166
ir[SSAValue(idx)][:inst] = lateres.val
11671167
check_effect_free!(ir, idx, lateres.val, rt)
11681168
return nothing
1169-
elseif is_return_type(sig.f)
1170-
check_effect_free!(ir, idx, stmt, rt)
1171-
return nothing
11721169
end
11731170

11741171
return stmt, sig

base/compiler/stmtinfo.jl

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ and any additional information (`call.info`) for a given generic call.
1010
"""
1111
struct CallMeta
1212
rt::Any
13+
effects::Effects
1314
info::Any
1415
end
1516

@@ -75,7 +76,7 @@ effect-free, including being no-throw (typically because the value was computed
7576
by calling an `@pure` function).
7677
"""
7778
struct MethodResultPure
78-
info::Union{MethodMatchInfo,UnionSplitInfo,Bool}
79+
info::Any
7980
end
8081
let instance = MethodResultPure(false)
8182
global MethodResultPure

0 commit comments

Comments
 (0)