Skip to content

Commit e51da3b

Browse files
committed
inference: fix backedge correctness for applicable calls
Even if the return value of `applicable(f, args...)` is initially inferred as `Const(true)`, when a new method is added to `f` then it may cause new method match ambiguities, requiring the call to return return `false` at the next invocation. To handle such cases, it is necessary to always add a method table backedge in the inference of `applicable`. Also just as a minor backedge reduction optimization, this commit avoids adding backedges when `applicable` is inferred to return `::Bool`.
1 parent 2a06376 commit e51da3b

File tree

3 files changed

+36
-17
lines changed

3 files changed

+36
-17
lines changed

base/compiler/abstractinterpretation.jl

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -267,12 +267,14 @@ any_ambig(info::MethodMatchInfo) = any_ambig(info.results)
267267
any_ambig(m::MethodMatches) = any_ambig(m.info)
268268
fully_covering(info::MethodMatchInfo) = info.fullmatch
269269
fully_covering(m::MethodMatches) = fully_covering(m.info)
270-
function add_uncovered_edges!(sv::AbsIntState, info::MethodMatchInfo, @nospecialize(atype))
271-
fully_covering(info) || add_mt_backedge!(sv, info.mt, atype)
270+
function add_uncovered_edges!(sv::AbsIntState, info::MethodMatchInfo, @nospecialize(atype), force::Bool=false)
271+
if !fully_covering(info) || force
272+
add_mt_backedge!(sv, info.mt, atype)
273+
end
272274
nothing
273275
end
274-
add_uncovered_edges!(sv::AbsIntState, matches::MethodMatches, @nospecialize(atype)) =
275-
add_uncovered_edges!(sv, matches.info, atype)
276+
add_uncovered_edges!(sv::AbsIntState, matches::MethodMatches, @nospecialize(atype), force::Bool=false) =
277+
add_uncovered_edges!(sv, matches.info, atype, force)
276278

277279
struct UnionSplitMethodMatches
278280
applicable::Vector{Any}
@@ -284,15 +286,15 @@ any_ambig(info::UnionSplitInfo) = any(any_ambig, info.split)
284286
any_ambig(m::UnionSplitMethodMatches) = any_ambig(m.info)
285287
fully_covering(info::UnionSplitInfo) = all(fully_covering, info.split)
286288
fully_covering(m::UnionSplitMethodMatches) = fully_covering(m.info)
287-
function add_uncovered_edges!(sv::AbsIntState, info::UnionSplitInfo, @nospecialize(atype))
288-
all(fully_covering, info.split) && return nothing
289+
function add_uncovered_edges!(sv::AbsIntState, info::UnionSplitInfo, @nospecialize(atype), force::Bool=false)
290+
all(fully_covering, info.split) && !force && return nothing
289291
# add mt backedges with removing duplications
290292
for mt in uncovered_method_tables(info)
291-
add_mt_backedge!(sv, mt, atype)
293+
add_mt_backedge!(sv, mt, atype, force)
292294
end
293295
end
294-
add_uncovered_edges!(sv::AbsIntState, matches::UnionSplitMethodMatches, @nospecialize(atype)) =
295-
add_uncovered_edges!(sv, matches.info, atype)
296+
add_uncovered_edges!(sv::AbsIntState, matches::UnionSplitMethodMatches, @nospecialize(atype), force::Bool=false) =
297+
add_uncovered_edges!(sv, matches.info, atype, force)
296298
function uncovered_method_tables(info::UnionSplitInfo)
297299
mts = MethodTable[]
298300
for mminfo in info.split

base/compiler/tfuncs.jl

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3006,14 +3006,16 @@ function abstract_applicable(interp::AbstractInterpreter, argtypes::Vector{Any},
30063006
else
30073007
rt = Const(true) # has applicable matches
30083008
end
3009-
for i in 1:napplicable
3010-
match = applicable[i]::MethodMatch
3011-
edge = specialize_method(match)::MethodInstance
3012-
add_backedge!(sv, edge)
3013-
end
3014-
# also need an edge to the method table in case something gets
3015-
# added that did not intersect with any existing method
3016-
add_uncovered_edges!(sv, matches, atype)
3009+
if rt !== Bool
3010+
for i in 1:napplicable
3011+
match = applicable[i]::MethodMatch
3012+
edge = specialize_method(match)
3013+
add_backedge!(sv, edge)
3014+
end
3015+
# also need an edge to the method table in case something gets
3016+
# added that did not intersect with any existing method
3017+
add_uncovered_edges!(sv, matches, atype, #=force=#true)
3018+
end
30173019
end
30183020
return Future(CallMeta(rt, Union{}, EFFECTS_TOTAL, NoCallInfo()))
30193021
end

test/compiler/inference.jl

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5104,6 +5104,21 @@ fapplicable(::Integer, ::Int32) = 3
51045104
@test only(Base.return_types((Tuple{Vararg{Int}},)) do x; Val(applicable(sin, 1, x...)); end) == Val
51055105
@test only(Base.return_types((Tuple{Vararg{Int}},)) do x; Val(applicable(sin, 1, 2, x...)); end) === Val{false}
51065106

5107+
function fapplicable2 end
5108+
gapplicable2(args...) = Val(applicable(fapplicable2, args...))
5109+
fapplicable2(x::Int, y) = 1
5110+
@test Base.infer_return_type((Int,Int)) do x, y
5111+
gapplicable2(x, y)
5112+
end === Val{true}
5113+
fapplicable2(x, y::Int) = 2 # now `fapplicable2(::Int, ::Int)` is ambiguous
5114+
@test Base.infer_return_type((Int,Int)) do x, y
5115+
gapplicable2(x, y)
5116+
end === Val{false}
5117+
fapplicable2(x::Int, y::Int) = 3 # now `fapplicable2(::Int, ::Int)` is applicable
5118+
@test Base.infer_return_type((Int,Int)) do x, y
5119+
gapplicable2(x, y)
5120+
end === Val{true}
5121+
51075122
function fhasmethod end
51085123
ghasmethod() = Val(hasmethod(fhasmethod, Tuple{}))
51095124
@test only(Base.return_types(ghasmethod, ())) === Val{false}

0 commit comments

Comments
 (0)