Skip to content

Commit 0615ea2

Browse files
authored
Merge pull request #20982 from JuliaLang/teh/ambiguities_bottom
Fix handling of `allow_bottom_tparams` in `isambiguous`
2 parents f95b010 + ec4444a commit 0615ea2

File tree

3 files changed

+54
-5
lines changed

3 files changed

+54
-5
lines changed

base/reflection.jl

Lines changed: 48 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -941,13 +941,41 @@ function method_exists(f::ANY, t::ANY)
941941
typemax(UInt)) != 0
942942
end
943943

944+
"""
945+
isambiguous(m1, m2, [allow_bottom_tparams=true]) -> Bool
946+
947+
Determine whether two methods `m1` and `m2` (typically of the same
948+
function) are ambiguous. This test is performed in the context of
949+
other methods of the same function; in isolation, `m1` and `m2` might
950+
be ambiguous, but if a third method resolving the ambiguity has been
951+
defined, this returns `false`.
952+
953+
For parametric types, `allow_bottom_tparams` controls whether
954+
`Union{}` is considered a valid intersection of type parameters. For example:
955+
```jldoctest
956+
julia> foo(x::Complex{<:Integer}) = 1
957+
foo (generic function with 1 method)
958+
959+
julia> foo(x::Complex{<:Rational}) = 2
960+
foo (generic function with 2 methods)
961+
962+
julia> m1, m2 = collect(methods(foo));
963+
964+
julia> typeintersect(m1.sig, m2.sig)
965+
Tuple{#foo,Complex{Union{}}}
966+
967+
julia> Base.isambiguous(m1, m2, true)
968+
true
969+
970+
julia> Base.isambiguous(m1, m2, false)
971+
false
972+
```
973+
"""
944974
function isambiguous(m1::Method, m2::Method, allow_bottom_tparams::Bool=true)
945975
ti = typeintersect(m1.sig, m2.sig)
946976
ti === Bottom && return false
947977
if !allow_bottom_tparams
948-
(_, env) = ccall(:jl_match_method, Ref{SimpleVector}, (Any, Any),
949-
ti, m1.sig)
950-
any(x->x === Bottom, env) && return false
978+
has_bottom_parameter(ti) && return false
951979
end
952980
ml = _methods_by_ftype(ti, -1, typemax(UInt))
953981
isempty(ml) && return true
@@ -959,6 +987,23 @@ function isambiguous(m1::Method, m2::Method, allow_bottom_tparams::Bool=true)
959987
return true
960988
end
961989

990+
"""
991+
has_bottom_parameter(t) -> Bool
992+
993+
Determine whether `t` is a Type for which one or more of its parameters is `Union{}`.
994+
"""
995+
function has_bottom_parameter(t::Type)
996+
ret = false
997+
for p in t.parameters
998+
ret |= (p == Bottom) || has_bottom_parameter(p)
999+
end
1000+
ret
1001+
end
1002+
has_bottom_parameter(t::UnionAll) = has_bottom_parameter(unwrap_unionall(t))
1003+
has_bottom_parameter(t::Union) = has_bottom_parameter(t.a) & has_bottom_parameter(t.b)
1004+
has_bottom_parameter(t::TypeVar) = has_bottom_parameter(t.ub)
1005+
has_bottom_parameter(::Any) = false
1006+
9621007
min_world(m::Method) = reinterpret(UInt, m.min_world)
9631008
max_world(m::Method) = reinterpret(UInt, m.max_world)
9641009
min_world(m::Core.MethodInstance) = reinterpret(UInt, m.min_world)

base/test.jl

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1117,12 +1117,16 @@ function test_approx_eq_modphase{S<:Real,T<:Real}(
11171117
end
11181118

11191119
"""
1120-
detect_ambiguities(mod1, mod2...; imported=false)
1120+
detect_ambiguities(mod1, mod2...; imported=false, allow_bottom=true)
11211121
11221122
Returns a vector of `(Method,Method)` pairs of ambiguous methods
11231123
defined in the specified modules. Use `imported=true` if you wish to
11241124
also test functions that were imported into these modules from
11251125
elsewhere.
1126+
1127+
`allow_bottom` controls whether ambiguities triggered only by
1128+
`Union{}` type parameters are included; in most cases you probably
1129+
want to set this to `false`. See [`Base.isambiguous`](@ref).
11261130
"""
11271131
function detect_ambiguities(mods...; imported::Bool=false, allow_bottom::Bool=true)
11281132
function sortdefs(m1, m2)

test/ambiguous.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,7 @@ ambs = detect_ambiguities(Ambig5)
134134

135135
# Test that Core and Base are free of ambiguities
136136
# TODO jb/subtype: we now detect a lot more
137-
@test_broken detect_ambiguities(Core, Base; imported=true) == []
137+
@test_broken detect_ambiguities(Core, Base; imported=true, allow_bottom=false) == []
138138
# not using isempty so this prints more information when it fails
139139

140140
amb_1(::Int8, ::Int) = 1

0 commit comments

Comments
 (0)