diff --git a/NEWS.md b/NEWS.md index ac218f591e380..751386438661e 100644 --- a/NEWS.md +++ b/NEWS.md @@ -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 --------------------------- diff --git a/base/broadcast.jl b/base/broadcast.jl index 2ec5d451512bd..09f2b2c605aad 100644 --- a/base/broadcast.jl +++ b/base/broadcast.jl @@ -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) @@ -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 diff --git a/base/complex.jl b/base/complex.jl index 07a7cbe317423..277ab796de933 100644 --- a/base/complex.jl +++ b/base/complex.jl @@ -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) diff --git a/base/deprecated.jl b/base/deprecated.jl index 9cd35d490acc8..a36db544d9c34 100644 --- a/base/deprecated.jl +++ b/base/deprecated.jl @@ -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) diff --git a/base/dict.jl b/base/dict.jl index 10c326b1358a1..24aaa18c9dbae 100644 --- a/base/dict.jl +++ b/base/dict.jl @@ -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)) @@ -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) diff --git a/base/exports.jl b/base/exports.jl index 0b359c200e64a..b15e1709383a0 100644 --- a/base/exports.jl +++ b/base/exports.jl @@ -931,7 +931,7 @@ export fieldname, fieldnames, fieldcount, - isleaftype, + isconcrete, oftype, promote, promote_rule, diff --git a/base/float.jl b/base/float.jl index 8be322ba4ea6f..ddd3d194353a2 100644 --- a/base/float.jl +++ b/base/float.jl @@ -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) diff --git a/base/inference.jl b/base/inference.jl index 198a5aaaca788..e35858709bbec 100644 --- a/base/inference.jl +++ b/base/inference.jl @@ -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 diff --git a/base/interactiveutil.jl b/base/interactiveutil.jl index 8a780d3304102..790906dea361a 100644 --- a/base/interactiveutil.jl +++ b/base/interactiveutil.jl @@ -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 diff --git a/base/iterators.jl b/base/iterators.jl index 9a401adfb2e00..5a29cc9d77db1 100644 --- a/base/iterators.jl +++ b/base/iterators.jl @@ -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) diff --git a/base/methodshow.jl b/base/methodshow.jl index 286980c197be0..109d22b8868e4 100644 --- a/base/methodshow.jl +++ b/base/methodshow.jl @@ -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) @@ -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) diff --git a/base/nullable.jl b/base/nullable.jl index 64e0069999c8c..953deb33e30de 100644 --- a/base/nullable.jl +++ b/base/nullable.jl @@ -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) @@ -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) diff --git a/base/promotion.jl b/base/promotion.jl index 57c18a2e00293..928f8a38808f4 100644 --- a/base/promotion.jl +++ b/base/promotion.jl @@ -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 diff --git a/base/reflection.jl b/base/reflection.jl index 41b67dff32964..c1ab5459de132 100644 --- a/base/reflection.jl +++ b/base/reflection.jl @@ -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) @@ -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 @@ -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 diff --git a/base/refpointer.jl b/base/refpointer.jl index 9ad3be1d89dbf..6c98b4b3aa1fd 100644 --- a/base/refpointer.jl +++ b/base/refpointer.jl @@ -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. diff --git a/base/replutil.jl b/base/replutil.jl index a59905d86af45..5fef42c044118 100644 --- a/base/replutil.jl +++ b/base/replutil.jl @@ -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 diff --git a/base/set.jl b/base/set.jl index 916d9746a1435..d9dea0a0ebc36 100644 --- a/base/set.jl +++ b/base/set.jl @@ -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 @@ -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 diff --git a/base/show.jl b/base/show.jl index 7d2ac38960156..4bd1e9204c944 100644 --- a/base/show.jl +++ b/base/show.jl @@ -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 @@ -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") @@ -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 @@ -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 diff --git a/doc/src/stdlib/base.md b/doc/src/stdlib/base.md index a6afa955fe6ea..2eaf02425e1e7 100644 --- a/doc/src/stdlib/base.md +++ b/doc/src/stdlib/base.md @@ -106,7 +106,7 @@ Base.fieldoffset Core.fieldtype Base.isimmutable Base.isbits -Base.isleaftype +Base.isconcrete Base.typejoin Base.typeintersect Base.instances diff --git a/test/arrayops.jl b/test/arrayops.jl index 1457684b6a0ec..2f1b4309bc9b2 100644 --- a/test/arrayops.jl +++ b/test/arrayops.jl @@ -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 diff --git a/test/core.jl b/test/core.jl index c7e40f706445d..65e456d176e9e 100644 --- a/test/core.jl +++ b/test/core.jl @@ -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 @@ -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 diff --git a/test/inference.jl b/test/inference.jl index a599922235824..218e8ebd6fe7f 100644 --- a/test/inference.jl +++ b/test/inference.jl @@ -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) diff --git a/test/reflection.jl b/test/reflection.jl index dc0af73e23987..752dda5fc51c7 100644 --- a/test/reflection.jl +++ b/test/reflection.jl @@ -139,11 +139,35 @@ end # module WarnType @test isbits(Tuple{Vararg{Any, 0}}) # issue #16670 -@test isleaftype(Tuple{Int, Vararg{Int, 2}}) -@test !isleaftype(Tuple{Integer, Vararg{Int, 2}}) -@test !isleaftype(Tuple{Int, Vararg{Int}}) -@test isleaftype(Type{Tuple{Integer, Vararg{Int}}}) -@test isleaftype(Type{Vector}) +@test Base._isleaftype(Tuple{Int, Vararg{Int, 2}}) +@test !Base._isleaftype(Tuple{Integer, Vararg{Int, 2}}) +@test !Base._isleaftype(Tuple{Int, Vararg{Int}}) +@test Base._isleaftype(Type{Tuple{Integer, Vararg{Int}}}) +@test Base._isleaftype(Type{Vector}) +@test isconcrete(Int) +@test isconcrete(Vector{Int}) +@test isconcrete(Tuple{Int, Vararg{Int, 2}}) +@test !isconcrete(Tuple{Any}) +@test !isconcrete(Tuple{Integer, Vararg{Int, 2}}) +@test !isconcrete(Tuple{Int, Vararg{Int}}) +@test !isconcrete(Type{Tuple{Integer, Vararg{Int}}}) +@test !isconcrete(Type{Vector}) +@test !isconcrete(Type{Int}) +@test !isconcrete(Tuple{Type{Int}}) +@test isconcrete(DataType) +@test isconcrete(Union) +@test !isconcrete(Union{}) +@test !isconcrete(Tuple{Union{}}) +@test !isconcrete(Complex) +@test !isconcrete(Complex.body) +@test !isconcrete(AbstractArray{Int,1}) +struct AlwaysHasLayout{T} + x +end +@test !isconcrete(AlwaysHasLayout) && !isconcrete(AlwaysHasLayout.body) +@test isconcrete(AlwaysHasLayout{Any}) +@test isconcrete(Ptr{Void}) +@test !isconcrete(Ptr) && !isconcrete(Ptr.body) # issue #10165 i10165(::Type) = 0