Skip to content

Commit

Permalink
make convert(Union{},x) directly ambiguous
Browse files Browse the repository at this point in the history
This should make it impossible to accidentally define or call this
method on foreign types.

Refs: #31602
Fixes: #45837
Closes: #45051
  • Loading branch information
vtjnash committed Jul 11, 2022
1 parent 43dac09 commit 353ae21
Show file tree
Hide file tree
Showing 6 changed files with 16 additions and 14 deletions.
1 change: 0 additions & 1 deletion base/array.jl
Original file line number Diff line number Diff line change
Expand Up @@ -612,7 +612,6 @@ oneunit(x::AbstractMatrix{T}) where {T} = _one(oneunit(T), x)
## Conversions ##

convert(::Type{T}, a::AbstractArray) where {T<:Array} = a isa T ? a : T(a)
convert(::Type{Union{}}, a::AbstractArray) = throw(MethodError(convert, (Union{}, a)))

promote_rule(a::Type{Array{T,n}}, b::Type{Array{S,n}}) where {T,n,S} = el_same(promote_type(T,S), a, b)

Expand Down
9 changes: 8 additions & 1 deletion base/essentials.jl
Original file line number Diff line number Diff line change
Expand Up @@ -279,7 +279,14 @@ See also: [`round`](@ref), [`trunc`](@ref), [`oftype`](@ref), [`reinterpret`](@r
"""
function convert end

convert(::Type{Union{}}, @nospecialize x) = throw(MethodError(convert, (Union{}, x)))
# make convert(::Type{<:Union{}}, x::T) intentionally ambiguous for all T
# so it will never get called or invalidated by loading packages
# with carefully chosen types that won't have any other convert methods defined
convert(T::Type{<:Core.IntrinsicFunction}, x) = throw(MethodError(convert, (T, x)))
convert(T::Type{<:Nothing}, x) = throw(MethodError(convert, (Nothing, x)))
convert(::Type{T}, x::T) where {T<:Core.IntrinsicFunction} = x
convert(::Type{T}, x::T) where {T<:Nothing} = x

convert(::Type{Type}, x::Type) = x # the ssair optimizer is strongly dependent on this method existing to avoid over-specialization
# in the absence of inlining-enabled
# (due to fields typed as `Type`, which is generally a bad idea)
Expand Down
1 change: 0 additions & 1 deletion base/filesystem.jl
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,6 @@ export File,
# open,
futime,
write,
JL_O_ACCMODE,
JL_O_WRONLY,
JL_O_RDONLY,
JL_O_RDWR,
Expand Down
2 changes: 0 additions & 2 deletions base/some.jl
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,6 @@ end

convert(::Type{T}, x::T) where {T>:Nothing} = x
convert(::Type{T}, x) where {T>:Nothing} = convert(nonnothingtype_checked(T), x)
convert(::Type{Nothing}, x) = throw(MethodError(convert, (Nothing, x)))
convert(::Type{Nothing}, ::Nothing) = nothing
convert(::Type{Some{T}}, x::Some{T}) where {T} = x
convert(::Type{Some{T}}, x::Some) where {T} = Some{T}(convert(T, x.value))

Expand Down
8 changes: 4 additions & 4 deletions stdlib/Test/src/Test.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1734,11 +1734,11 @@ function detect_ambiguities(mods::Module...;
ambs = Set{Tuple{Method,Method}}()
mods = collect(mods)::Vector{Module}
function sortdefs(m1::Method, m2::Method)
ord12 = m1.file < m2.file
if !ord12 && (m1.file == m2.file)
ord12 = m1.line < m2.line
ord12 = cmp(m1.file, m2.file)
if ord12 == 0
ord12 = cmp(m1.line, m2.line)
end
return ord12 ? (m1, m2) : (m2, m1)
return ord12 <= 0 ? (m1, m2) : (m2, m1)
end
function examine(mt::Core.MethodTable)
for m in Base.MethodList(mt)
Expand Down
9 changes: 4 additions & 5 deletions test/ambiguous.jl
Original file line number Diff line number Diff line change
Expand Up @@ -100,10 +100,6 @@ ambig(x::Union{Char, Int16}) = 's'
const allowed_undefineds = Set([
GlobalRef(Base, :active_repl),
GlobalRef(Base, :active_repl_backend),
GlobalRef(Base.Filesystem, :JL_O_TEMPORARY),
GlobalRef(Base.Filesystem, :JL_O_SHORT_LIVED),
GlobalRef(Base.Filesystem, :JL_O_SEQUENTIAL),
GlobalRef(Base.Filesystem, :JL_O_RANDOM),
])

let Distributed = get(Base.loaded_modules,
Expand Down Expand Up @@ -167,7 +163,7 @@ using LinearAlgebra, SparseArrays, SuiteSparse
# Test that Core and Base are free of ambiguities
# not using isempty so this prints more information when it fails
@testset "detect_ambiguities" begin
let ambig = Set{Any}(((m1.sig, m2.sig) for (m1, m2) in detect_ambiguities(Core, Base; recursive=true, ambiguous_bottom=false, allowed_undefineds)))
let ambig = Set(detect_ambiguities(Core, Base; recursive=true, ambiguous_bottom=false, allowed_undefineds))
good = true
for (sig1, sig2) in ambig
@test sig1 === sig2 # print this ambiguity
Expand All @@ -178,6 +174,9 @@ using LinearAlgebra, SparseArrays, SuiteSparse

# some ambiguities involving Union{} type parameters are expected, but not required
let ambig = Set(detect_ambiguities(Core; recursive=true, ambiguous_bottom=true))
m1 = which(Core.Compiler.convert, Tuple{Type{<:Core.IntrinsicFunction}, Any})
m2 = which(Core.Compiler.convert, Tuple{Type{<:Nothing}, Any})
pop!(ambig, (m1, m2))
@test !isempty(ambig)
end

Expand Down

0 comments on commit 353ae21

Please sign in to comment.