Skip to content

Commit

Permalink
replace export of isleaftype with a more simply-defined isconcrete (
Browse files Browse the repository at this point in the history
#23666)

fixes #17086
  • Loading branch information
JeffBezanson authored Sep 13, 2017
1 parent 957848b commit 55ec9fe
Show file tree
Hide file tree
Showing 23 changed files with 94 additions and 49 deletions.
5 changes: 5 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -438,6 +438,11 @@ Deprecated or removed
been deprecated due to inconsistency with linear algebra. Use `.+` and `.-` for these operations
instead.

* `isleaftype` is deprecated in favor of a simpler predicate `isconcrete`. Concrete types are
those that might equal `typeof(x)` for some `x`; `isleaftype` includes some types for which
this is not true. If you are certain you need the old behavior, it is temporarily available
as `Base._isleaftype` ([#17086]).

Command-line option changes
---------------------------

Expand Down
6 changes: 3 additions & 3 deletions base/broadcast.jl
Original file line number Diff line number Diff line change
Expand Up @@ -309,7 +309,7 @@ _nullable_eltype(f, A, As...) =
T = _broadcast_eltype(f, A, Bs...)
shape = broadcast_indices(A, Bs...)
iter = CartesianRange(shape)
if isleaftype(T)
if Base._isleaftype(T)
return broadcast_t(f, T, shape, iter, A, Bs...)
end
if isempty(iter)
Expand All @@ -320,8 +320,8 @@ end
@inline function broadcast_c(f, ::Type{Nullable}, a...)
nonnull = all(hasvalue, a)
S = _nullable_eltype(f, a...)
if isleaftype(S) && null_safe_op(f, maptoTuple(_unsafe_get_eltype,
a...).types...)
if Base._isleaftype(S) && null_safe_op(f, maptoTuple(_unsafe_get_eltype,
a...).types...)
Nullable{S}(f(map(unsafe_get, a)...), nonnull)
else
if nonnull
Expand Down
2 changes: 1 addition & 1 deletion base/complex.jl
Original file line number Diff line number Diff line change
Expand Up @@ -958,7 +958,7 @@ big(z::Complex{T}) where {T<:Real} = Complex{big(T)}(z)
complex(A::AbstractArray{<:Complex}) = A

function complex(A::AbstractArray{T}) where T
if !isleaftype(T)
if !isconcrete(T)
error("`complex` not defined on abstractly-typed arrays; please convert to a more specific type")
end
convert(AbstractArray{typeof(complex(zero(T)))}, A)
Expand Down
3 changes: 3 additions & 0 deletions base/deprecated.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1766,6 +1766,9 @@ import .Iterators.enumerate
# issue #5794
@deprecate map(f, d::T) where {T<:Associative} T( f(p) for p in pairs(d) )

# issue #17086
@deprecate isleaftype isconcrete

# PR #22932
@deprecate +(a::Number, b::AbstractArray) broadcast(+, a, b)
@deprecate +(a::AbstractArray, b::Number) broadcast(+, a, b)
Expand Down
4 changes: 2 additions & 2 deletions base/dict.jl
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ function show(io::IO, t::Associative{K,V}) where V where K
if isempty(t)
print(io, typeof(t), "()")
else
if isleaftype(K) && isleaftype(V)
if _isleaftype(K) && _isleaftype(V)
print(io, typeof(t).name)
else
print(io, typeof(t))
Expand Down Expand Up @@ -161,7 +161,7 @@ associative_with_eltype(DT_apply, ::Type) = DT_apply(Any, Any)()
associative_with_eltype(DT_apply::F, kv, t) where {F} = grow_to!(associative_with_eltype(DT_apply, _default_eltype(typeof(kv))), kv)
function associative_with_eltype(DT_apply::F, kv::Generator, t) where F
T = _default_eltype(typeof(kv))
if T <: Union{Pair, Tuple{Any, Any}} && isleaftype(T)
if T <: Union{Pair, Tuple{Any, Any}} && _isleaftype(T)
return associative_with_eltype(DT_apply, kv, T)
end
return grow_to!(associative_with_eltype(DT_apply, T), kv)
Expand Down
2 changes: 1 addition & 1 deletion base/exports.jl
Original file line number Diff line number Diff line change
Expand Up @@ -931,7 +931,7 @@ export
fieldname,
fieldnames,
fieldcount,
isleaftype,
isconcrete,
oftype,
promote,
promote_rule,
Expand Down
2 changes: 1 addition & 1 deletion base/float.jl
Original file line number Diff line number Diff line change
Expand Up @@ -877,7 +877,7 @@ Base.iszero(x::Float16) = reinterpret(UInt16, x) & ~sign_mask(Float16) == 0x0000
float(A::AbstractArray{<:AbstractFloat}) = A

function float(A::AbstractArray{T}) where T
if !isleaftype(T)
if !isconcrete(T)
error("`float` not defined on abstractly-typed arrays; please convert to a more specific type")
end
convert(AbstractArray{typeof(float(zero(T)))}, A)
Expand Down
2 changes: 2 additions & 0 deletions base/inference.jl
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,8 @@ const NF = NotFound()
const LineNum = Int
const VarTable = Array{Any,1}

const isleaftype = _isleaftype

# The type of a variable load is either a value or an UndefVarError
mutable struct VarState
typ
Expand Down
2 changes: 1 addition & 1 deletion base/interactiveutil.jl
Original file line number Diff line number Diff line change
Expand Up @@ -555,7 +555,7 @@ Evaluates the arguments to the function or macro call, determines their types, a
function type_close_enough(@nospecialize(x), @nospecialize(t))
x == t && return true
return (isa(x,DataType) && isa(t,DataType) && x.name === t.name &&
!isleaftype(t) && x <: t) ||
!_isleaftype(t) && x <: t) ||
(isa(x,Union) && isa(t,DataType) && (type_close_enough(x.a, t) || type_close_enough(x.b, t)))
end

Expand Down
11 changes: 4 additions & 7 deletions base/iterators.jl
Original file line number Diff line number Diff line change
Expand Up @@ -733,17 +733,14 @@ iteratoreltype(::Type{Flatten{I}}) where {I} = _flatteneltype(I, iteratoreltype(
_flatteneltype(I, ::HasEltype) = iteratoreltype(eltype(I))
_flatteneltype(I, et) = EltypeUnknown()

flatten_iteratorsize(::Union{HasShape, HasLength}, b::Type{<:Tuple}) = isleaftype(b) ? HasLength() : SizeUnknown()
flatten_iteratorsize(::Union{HasShape, HasLength}, b::Type{<:Number}) = HasLength()
flatten_iteratorsize(::Union{HasShape, HasLength}, ::Type{<:NTuple{N,Any}}) where {N} = HasLength()
flatten_iteratorsize(::Union{HasShape, HasLength}, ::Type{<:Tuple}) = SizeUnknown()
flatten_iteratorsize(::Union{HasShape, HasLength}, ::Type{<:Number}) = HasLength()
flatten_iteratorsize(a, b) = SizeUnknown()

iteratorsize(::Type{Flatten{I}}) where {I} = flatten_iteratorsize(iteratorsize(I), eltype(I))

function flatten_length(f, ::Type{T}) where {T<:Tuple}
if !isleaftype(T)
throw(ArgumentError(
"Cannot compute length of a tuple-type which is not a leaf-type"))
end
function flatten_length(f, T::Type{<:NTuple{N,Any}}) where {N}
fieldcount(T)*length(f.it)
end
flatten_length(f, ::Type{<:Number}) = length(f.it)
Expand Down
4 changes: 2 additions & 2 deletions base/methodshow.jl
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ function show(io::IO, m::Method; kwtype::Nullable{DataType}=Nullable{DataType}()
# TODO: more accurate test? (tn.name === "#" name)
ft0 === typeof(getfield(ft.name.module, ft.name.mt.name))
print(io, ft.name.mt.name)
elseif isa(ft, DataType) && ft.name === Type.body.name && isleaftype(ft)
elseif isa(ft, DataType) && ft.name === Type.body.name
f = ft.parameters[1]
if isa(f, DataType) && isempty(f.parameters)
print(io, f)
Expand Down Expand Up @@ -235,7 +235,7 @@ function show(io::IO, ::MIME"text/html", m::Method; kwtype::Nullable{DataType}=N
isdefined(ft.name.module, ft.name.mt.name) &&
ft0 === typeof(getfield(ft.name.module, ft.name.mt.name))
print(io, ft.name.mt.name)
elseif isa(ft, DataType) && ft.name === Type.body.name && isleaftype(ft)
elseif isa(ft, DataType) && ft.name === Type.body.name
f = ft.parameters[1]
if isa(f, DataType) && isempty(f.parameters)
print(io, f)
Expand Down
4 changes: 2 additions & 2 deletions base/nullable.jl
Original file line number Diff line number Diff line change
Expand Up @@ -339,7 +339,7 @@ end
"""
Return the given type if it is concrete, and `Union{}` otherwise.
"""
nullable_returntype(::Type{T}) where {T} = isleaftype(T) ? T : Union{}
nullable_returntype(::Type{T}) where {T} = _isleaftype(T) ? T : Union{}

"""
map(f, x::Nullable)
Expand All @@ -365,7 +365,7 @@ Nullable{Bool}()
"""
function map(f, x::Nullable{T}) where T
S = promote_op(f, T)
if isleaftype(S) && null_safe_op(f, T)
if _isleaftype(S) && null_safe_op(f, T)
Nullable(f(unsafe_get(x)), !isnull(x))
else
if isnull(x)
Expand Down
4 changes: 2 additions & 2 deletions base/promotion.jl
Original file line number Diff line number Diff line change
Expand Up @@ -346,13 +346,13 @@ promote_op(::Any...) = (@_inline_meta; Any)
function promote_op(f, ::Type{S}) where S
@_inline_meta
T = _return_type(f, Tuple{_default_type(S)})
isleaftype(S) && return isleaftype(T) ? T : Any
_isleaftype(S) && return _isleaftype(T) ? T : Any
return typejoin(S, T)
end
function promote_op(f, ::Type{R}, ::Type{S}) where {R,S}
@_inline_meta
T = _return_type(f, Tuple{_default_type(R), _default_type(S)})
isleaftype(R) && isleaftype(S) && return isleaftype(T) ? T : Any
_isleaftype(R) && _isleaftype(S) && return _isleaftype(T) ? T : Any
return typejoin(R, S, T)
end

Expand Down
33 changes: 23 additions & 10 deletions base/reflection.jl
Original file line number Diff line number Diff line change
Expand Up @@ -287,28 +287,41 @@ isbits(t::DataType) = (@_pure_meta; !t.mutable & (t.layout != C_NULL) && datatyp
isbits(t::Type) = (@_pure_meta; false)
isbits(x) = (@_pure_meta; isbits(typeof(x)))

_isleaftype(@nospecialize(t)) = (@_pure_meta; isa(t, DataType) && t.isleaftype)

"""
isleaftype(T)
isconcrete(T)
Determine whether `T`'s only subtypes are itself and `Union{}`. This means `T` is
a concrete type that can have instances.
Determine whether `T` is a concrete type, meaning it can have direct instances
(values `x` such that `typeof(x) === T`).
# Examples
```jldoctest
julia> isleaftype(Complex)
julia> isconcrete(Complex)
false
julia> isleaftype(Complex{Float32})
julia> isconcrete(Complex{Float32})
true
julia> isleaftype(Vector{Complex})
julia> isconcrete(Vector{Complex})
true
julia> isleaftype(Vector{Complex{Float32}})
julia> isconcrete(Vector{Complex{Float32}})
true
julia> isconcrete(Union{})
false
julia> isconcrete(Union{Int,String})
false
```
"""
isleaftype(@nospecialize(t)) = (@_pure_meta; isa(t, DataType) && t.isleaftype)
function isconcrete(@nospecialize(t))
@_pure_meta
return (isa(t, DataType) && !t.abstract &&
!t.hasfreetypevars &&
(t.name !== Tuple.name || all(isconcrete, t.parameters)))
end

"""
Base.isabstract(T)
Expand Down Expand Up @@ -791,7 +804,7 @@ function _dump_function_linfo(linfo::Core.MethodInstance, world::UInt, native::B
end

# TODO: use jl_is_cacheable_sig instead of isleaftype
isleaftype(linfo.specTypes) || (str = "; WARNING: This code may not match what actually runs.\n" * str)
_isleaftype(linfo.specTypes) || (str = "; WARNING: This code may not match what actually runs.\n" * str)
return str
end

Expand Down Expand Up @@ -822,7 +835,7 @@ code_native(::IO, ::Any, ::Symbol) = error("illegal code_native call") # resolve

# give a decent error message if we try to instantiate a staged function on non-leaf types
function func_for_method_checked(m::Method, @nospecialize types)
if isdefined(m,:generator) && !isdefined(m,:source) && !isleaftype(types)
if isdefined(m,:generator) && !isdefined(m,:source) && !_isleaftype(types)
error("cannot call @generated function `", m, "` ",
"with abstract argument types: ", types)
end
Expand Down
2 changes: 1 addition & 1 deletion base/refpointer.jl
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ convert(::Type{Ref{T}}, x) where {T} = RefValue{T}(x)
function unsafe_convert(P::Type{Ptr{T}}, b::RefValue{T}) where T
if isbits(T) || isbitsunion(T)
return convert(P, pointer_from_objref(b))
elseif isleaftype(T)
elseif _isleaftype(T)
return convert(P, pointer_from_objref(b.x))
else
# If the slot is not leaf type, it could be either isbits or not.
Expand Down
2 changes: 1 addition & 1 deletion base/replutil.jl
Original file line number Diff line number Diff line change
Expand Up @@ -468,7 +468,7 @@ function show_method_candidates(io::IO, ex::MethodError, kwargs::Vector=Any[])
# pool MethodErrors for these two functions.
if f === convert && !isempty(arg_types_param)
at1 = arg_types_param[1]
if isa(at1,DataType) && (at1::DataType).name === Type.body.name && isleaftype(at1)
if isa(at1,DataType) && (at1::DataType).name === Type.body.name && !Core.Inference.has_free_typevars(at1)
push!(funcs, (at1.parameters[1], arg_types_param[2:end]))
end
end
Expand Down
4 changes: 2 additions & 2 deletions base/set.jl
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ for sets of arbitrary objects.
Set(itr) = Set{eltype(itr)}(itr)
function Set(g::Generator)
T = _default_eltype(typeof(g))
(isleaftype(T) || T === Union{}) || return grow_to!(Set{T}(), g)
(_isleaftype(T) || T === Union{}) || return grow_to!(Set{T}(), g)
return Set{T}(g)
end

Expand Down Expand Up @@ -266,7 +266,7 @@ function unique(itr)
return out
end
x, i = next(itr, i)
if !isleaftype(T) && iteratoreltype(itr) == EltypeUnknown()
if !_isleaftype(T) && iteratoreltype(itr) == EltypeUnknown()
S = typeof(x)
return _unique_from(itr, S[x], Set{S}((x,)), i)
end
Expand Down
8 changes: 4 additions & 4 deletions base/show.jl
Original file line number Diff line number Diff line change
Expand Up @@ -253,7 +253,7 @@ function show_type_name(io::IO, tn::TypeName)
globname_str = string(globname)
if ('#' globname_str && '@' globname_str && isdefined(tn, :module) &&
isbindingresolved(tn.module, globname) && isdefined(tn.module, globname) &&
isa(getfield(tn.module, globname), tn.wrapper) && isleaftype(tn.wrapper))
isa(getfield(tn.module, globname), tn.wrapper) && _isleaftype(tn.wrapper))
globfunc = true
end
end
Expand Down Expand Up @@ -617,7 +617,7 @@ function show_expr_type(io::IO, @nospecialize(ty), emph::Bool)
elseif ty === Core.IntrinsicFunction
print(io, "::I")
else
if emph && (!isleaftype(ty) || ty == Core.Box)
if emph && (!_isleaftype(ty) || ty == Core.Box)
emphasize(io, "::$ty")
else
print(io, "::$ty")
Expand Down Expand Up @@ -1171,7 +1171,7 @@ function show_tuple_as_call(io::IO, name::Symbol, sig::Type)
isdefined(uw.name.module, uw.name.mt.name) &&
ft == typeof(getfield(uw.name.module, uw.name.mt.name))
print(io, uw.name.mt.name)
elseif isa(ft, DataType) && ft.name === Type.body.name && isleaftype(ft)
elseif isa(ft, DataType) && ft.name === Type.body.name && !Core.Inference.has_free_typevars(ft)
f = ft.parameters[1]
print(io, f)
else
Expand Down Expand Up @@ -1913,7 +1913,7 @@ function array_eltype_show_how(X)
str = string(e)
end
# Types hard-coded here are those which are created by default for a given syntax
(isleaftype(e),
(_isleaftype(e),
(!isempty(X) && (e===Float64 || e===Int || e===Char || e===String) ? "" : str))
end

Expand Down
2 changes: 1 addition & 1 deletion doc/src/stdlib/base.md
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ Base.fieldoffset
Core.fieldtype
Base.isimmutable
Base.isbits
Base.isleaftype
Base.isconcrete
Base.typejoin
Base.typeintersect
Base.instances
Expand Down
2 changes: 1 addition & 1 deletion test/arrayops.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1916,7 +1916,7 @@ let A = zeros(Int, 2, 2), B = zeros(Float64, 2, 2)
for f in [f1, f2, f3, f4, f5, f6, f7, f8, f9, f10, f11, f12, f13, f14, f15, f16,
f17, f18, f19, f20, f21, f22, f23, f24, f25, f26, f27, f28, f29, f30,
f31, f32, f33, f34, f35, f36, f37, f38, f39, f40, f41, f42]
@test isleaftype(Base.return_types(f, ())[1])
@test Base._isleaftype(Base.return_types(f, ())[1])
end
end

Expand Down
4 changes: 2 additions & 2 deletions test/core.jl
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,7 @@ let elT = T22624.body.body.body.types[1].parameters[1]
elT2 = elT.body.types[1].parameters[1]
@test elT2 == T22624{Int64, Int64, C} where C
@test elT2.body.types[1].parameters[1] === elT2
@test isleaftype(elT2.body.types[1])
@test Base._isleaftype(elT2.body.types[1])
end

# issue #3890
Expand Down Expand Up @@ -4284,7 +4284,7 @@ let a = Val{Val{TypeVar(:_, Int)}},

@test !isdefined(a, :instance)
@test isdefined(b, :instance)
@test isleaftype(b)
@test Base._isleaftype(b)
end

# A return type widened to Type{Union{T,Void}} should not confuse
Expand Down
1 change: 1 addition & 0 deletions test/inference.jl
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,7 @@ function find_tvar10930(arg)
end
@test find_tvar10930(Vararg{Int}) === 1

const isleaftype = Base._isleaftype

# issue #12474
@generated function f12474(::Any)
Expand Down
Loading

2 comments on commit 55ec9fe

@nanosoldier
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Executing the daily benchmark build, I will reply here when finished:

@nanosoldier runbenchmarks(ALL, isdaily = true)

@nanosoldier
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Your benchmark job has completed - possible performance regressions were detected. A full report can be found here. cc @ararslan

Please sign in to comment.