diff --git a/base/essentials.jl b/base/essentials.jl index 153f382d6a583..30d5fea5e3692 100644 --- a/base/essentials.jl +++ b/base/essentials.jl @@ -111,6 +111,15 @@ function unwrapva(t::ANY) isvarargtype(t2) ? t2.parameters[1] : t end +typename(a) = error("typename does not apply to this type") +typename(a::DataType) = a.name +function typename(a::Union) + ta = typename(a.a) + tb = typename(a.b) + ta === tb ? tb : error("typename does not apply to unions whose components have different typenames") +end +typename(union::UnionAll) = typename(union.body) + convert{T<:Tuple{Any,Vararg{Any}}}(::Type{T}, x::Tuple{Any, Vararg{Any}}) = tuple(convert(tuple_type_head(T),x[1]), convert(tuple_type_tail(T), tail(x))...) convert{T<:Tuple{Any,Vararg{Any}}}(::Type{T}, x::T) = x diff --git a/base/inference.jl b/base/inference.jl index 76e793ba5b4dc..c38693ab28a80 100644 --- a/base/inference.jl +++ b/base/inference.jl @@ -82,6 +82,15 @@ type Conditional end end +immutable PartialTypeVar + tv::TypeVar + # N.B.: Currently unused, but would allow turning something back + # into Const, if the bounds are pulled out of this TypeVar + lb_certain::Bool + ub_certain::Bool + PartialTypeVar(tv::TypeVar, lb_certain::Bool, ub_certain::Bool) = new(tv, lb_certain, ub_certain) +end + function rewrap(t::ANY, u::ANY) isa(t, Const) && return t isa(t, Conditional) && return t @@ -688,10 +697,25 @@ function limit_type_depth(t::ANY, d::Int, cov::Bool=true, var::Union{Void,TypeVa return (cov && !stillcov) ? UnionAll(var, R) : R end +const DataType_name_fieldindex = fieldindex(DataType, :name) const DataType_parameters_fieldindex = fieldindex(DataType, :parameters) const DataType_types_fieldindex = fieldindex(DataType, :types) const DataType_super_fieldindex = fieldindex(DataType, :super) +const TypeName_name_fieldindex = fieldindex(TypeName, :name) +const TypeName_module_fieldindex = fieldindex(TypeName, :module) +const TypeName_wrapper_fieldindex = fieldindex(TypeName, :wrapper) + +function const_datatype_getfield_tfunc(sv, fld) + if (fld == DataType_name_fieldindex || + fld == DataType_parameters_fieldindex || + fld == DataType_types_fieldindex || + fld == DataType_super_fieldindex) + return abstract_eval_constant(getfield(sv, fld)) + end + return nothing +end + # returns (type, isexact) function getfield_tfunc(s00::ANY, name) if isa(s00, TypeVar) @@ -719,11 +743,22 @@ function getfield_tfunc(s00::ANY, name) elseif nv === :body || nv === 2 return Const(sv.body) end + elseif isa(sv, DataType) + t = const_datatype_getfield_tfunc(sv, isa(nv, Symbol) ? + fieldindex(DataType, nv, false) : nv) + t !== nothing && return t + elseif isa(sv, TypeName) + fld = isa(nv, Symbol) ? fieldindex(TypeName, nv, false) : nv + if (fld == TypeName_name_fieldindex || + fld == TypeName_module_fieldindex || + fld == TypeName_wrapper_fieldindex) + return abstract_eval_constant(getfield(sv, fld)) + end end if isa(sv, Module) && isa(nv, Symbol) return abstract_eval_global(sv, nv) end - if (isa(sv, DataType) || isimmutable(sv)) && isdefined(sv, nv) + if (isa(sv, SimpleVector) || isimmutable(sv)) && isdefined(sv, nv) return abstract_eval_constant(getfield(sv, nv)) end end @@ -783,11 +818,9 @@ function getfield_tfunc(s00::ANY, name) else sp = nothing end - if (sp !== nothing && - (fld == DataType_parameters_fieldindex || - fld == DataType_types_fieldindex || - fld == DataType_super_fieldindex)) - return Const(getfield(sp, fld)) + if sp !== nothing + t = const_datatype_getfield_tfunc(sp, fld) + t !== nothing && return t end R = s.types[fld] if isempty(s.parameters) @@ -915,15 +948,20 @@ function apply_type_tfunc(headtypetype::ANY, args::ANY...) return Any end uncertain = false + canconst = true tparams = Any[] outervars = Any[] for i = 1:largs ai = args[i] if isType(ai) aip1 = ai.parameters[1] + canconst &= isleaftype(aip1) push!(tparams, aip1) - elseif isa(ai, Const) && (isa(ai.val, Type) || valid_tparam(ai.val)) + elseif isa(ai, Const) && (isa(ai.val, Type) || isa(ai.val, TypeVar) || valid_tparam(ai.val)) push!(tparams, ai.val) + elseif isa(ai, PartialTypeVar) + canconst = false + push!(tparams, ai.tv) else # TODO: return `Bottom` for trying to apply a non-UnionAll uncertain = true @@ -966,11 +1004,11 @@ function apply_type_tfunc(headtypetype::ANY, args::ANY...) # doesn't match, which could happen if a type estimate is too coarse return Type{_} where _<:headtype end - !uncertain && return Const(appl) + !uncertain && canconst && return Const(appl) if isvarargtype(headtype) return Type end - if type_too_complex(appl,0) + if uncertain && type_too_complex(appl,0) return Type{_} where _<:headtype end if istuple @@ -1486,6 +1524,25 @@ function Pair_name() return _Pair_name end +_typename(a) = Union{} +_typename(a::Vararg) = Any +_typename(a::TypeVar) = Any +_typename(a::DataType) = Const(a.name) +function _typename(a::Union) + ta = _typename(a.a) + tb = _typename(a.b) + ta === tb ? tb : (ta === Any || tb === Any) ? Any : Union{} +end +_typename(union::UnionAll) = _typename(union.body) +function typename_static(t) + # N.B.: typename maps type equivalence classes to a single value + if isa(t, Const) || isType(t) + return _typename(isa(t, Const) ? t.val : t.parameters[1]) + else + return Any + end +end + function abstract_call(f::ANY, fargs::Union{Tuple{},Vector{Any}}, argtypes::Vector{Any}, vtypes::VarTable, sv::InferenceState) if f === _apply length(fargs) > 1 || return Any @@ -1567,19 +1624,65 @@ function abstract_call(f::ANY, fargs::Union{Tuple{},Vector{Any}}, argtypes::Vect end end return Any - elseif f === UnionAll - if length(fargs) == 3 && isa(argtypes[2], Const) - tv = argtypes[2].val - if isa(tv, TypeVar) + elseif f === TypeVar + lb = Union{} + ub = Any + ub_certain = lb_certain = true + if length(fargs) >= 2 && isa(argtypes[2], Const) + nv = argtypes[2].val + ubidx = 3 + if length(fargs) >= 4 + ubidx = 4 if isa(argtypes[3], Const) - body = argtypes[3].val + lb = argtypes[3].val elseif isType(argtypes[3]) - body = argtypes[3].parameters[1] + lb = argtypes[3].parameters[1] + lb_certain = false else - return Any + return TypeVar + end + end + if length(fargs) >= ubidx + if isa(argtypes[ubidx], Const) + ub = argtypes[ubidx].val + elseif isType(argtypes[ubidx]) + ub = argtypes[ubidx].parameters[1] + ub_certain = false + else + return TypeVar end - return abstract_eval_constant(UnionAll(tv, body)) end + tv = TypeVar(nv, lb, ub) + return PartialTypeVar(tv, lb_certain, ub_certain) + end + return TypeVar + elseif f === UnionAll + if length(fargs) == 3 + canconst = true + if isa(argtypes[3], Const) + body = argtypes[3].val + elseif isType(argtypes[3]) + body = argtypes[3].parameters[1] + canconst = false + else + return Any + end + if isa(argtypes[2], Const) + tv = argtypes[2].val + elseif isa(argtypes[2], PartialTypeVar) + ptv = argtypes[2] + tv = ptv.tv + canconst = false + else + return Any + end + !isa(tv, TypeVar) && return Any + if !isa(body, Type) && !isa(body, TypeVar) + return Any + end + theunion = UnionAll(tv, body) + ret = canconst ? abstract_eval_constant(theunion) : Type{theunion} + return ret end return Any elseif f === return_type @@ -1605,7 +1708,16 @@ function abstract_call(f::ANY, fargs::Union{Tuple{},Vector{Any}}, argtypes::Vect if length(argtypes)>2 && argtypes[3] ⊑ Int at2 = widenconst(argtypes[2]) - if (at2 <: Tuple || + if at2 <: SimpleVector && istopfunction(tm, f, :getindex) + if isa(argtypes[2], Const) && isa(argtypes[3], Const) + svecval = argtypes[2].val + idx = argtypes[3].val + if isa(idx, Int) && 1 <= idx <= length(svecval) & + isassigned(svecval, idx) + return Const(getindex(svecval, idx)) + end + end + elseif (at2 <: Tuple || (isa(at2, DataType) && (at2::DataType).name === Pair_name())) # allow tuple indexing functions to take advantage of constant # index arguments. @@ -1627,6 +1739,12 @@ function abstract_call(f::ANY, fargs::Union{Tuple{},Vector{Any}}, argtypes::Vect if istopfunction(tm, f, :promote_type) || istopfunction(tm, f, :typejoin) return Type + elseif length(argtypes) == 2 && istopfunction(tm, f, :typename) + t = argtypes[2] + if isa(t, Const) || isType(t) + return typename_static(t) + end + return Any end if sv.params.inlining @@ -1915,6 +2033,7 @@ function widenconst(c::Const) return typeof(c.val) end end +widenconst(c::PartialTypeVar) = TypeVar widenconst(t::ANY) = t issubstate(a::VarState, b::VarState) = (a.typ ⊑ b.typ && a.undef <= b.undef) @@ -3562,7 +3681,7 @@ function inlineable(f::ANY, ft::ANY, e::Expr, atypes::Vector{Any}, sv::Inference if method.name == :getindex || method.name == :next || method.name == :indexed_next if length(atypes) > 2 && atypes[3] ⊑ Int at2 = widenconst(atypes[2]) - if (at2 <: Tuple || + if (at2 <: Tuple || at2 <: SimpleVector || (isa(at2, DataType) && (at2::DataType).name === Pair_name())) force_infer = true end diff --git a/base/reflection.jl b/base/reflection.jl index 651f7002061fb..d0bb5970f89ed 100644 --- a/base/reflection.jl +++ b/base/reflection.jl @@ -144,11 +144,12 @@ fieldnames(t::UnionAll) = fieldnames(unwrap_unionall(t)) fieldnames{T<:Tuple}(t::Type{T}) = Int[n for n in 1:nfields(t)] """ - Base.datatype_name(t::DataType) -> Symbol + Base.datatype_name(t) -> Symbol -Get the name of a `DataType` (without its parent module) as a symbol. +Get the name of a (potentially UnionAll-wrapped) `DataType` (without its parent module) as a symbol. """ datatype_name(t::DataType) = t.name.name +datatype_name(t::UnionAll) = datatype_name(unwrap_unionall(t)) """ Base.datatype_module(t::DataType) -> Module @@ -232,6 +233,39 @@ a concrete type that can have instances. """ isleaftype(t::ANY) = (@_pure_meta; isa(t, DataType) && t.isleaftype) +""" + Base.isabstract(T) + +Determine whether `T` was declared as an abstract type (i.e. using the +`abstract` keyword). +""" +function isabstract(t::ANY) + @_pure_meta + t = unwrap_unionall(t) + isa(t,DataType) && t.abstract +end + +""" + Base.parameter_upper_bound(t::UnionAll, idx) + +Determine the upper bound of a type parameter in the underlying type. E.g.: +```jldoctest +julia> immutable Foo{T<:AbstractFloat, N} + x::Tuple{T, N} + end + +julia> Base.parameter_upper_bound(Foo, 1) +AbstractFloat + +julia> Base.parameter_upper_bound(Foo, 2) +Any +``` +""" +function parameter_upper_bound(t::UnionAll, idx) + @_pure_meta + rewrap_unionall(unwrap_unionall(t).parameters[idx], t) +end + """ typeintersect(T, S) @@ -816,9 +850,14 @@ function method_exists(f::ANY, t::ANY) typemax(UInt)) != 0 end -function isambiguous(m1::Method, m2::Method) +function isambiguous(m1::Method, m2::Method, allow_bottom_tparams::Bool=true) ti = typeintersect(m1.sig, m2.sig) ti === Bottom && return false + if !allow_bottom_tparams + (_, env) = ccall(:jl_match_method, Ref{SimpleVector}, (Any, Any), + ti, m1.sig) + any(x->x === Bottom, env) && return false + end ml = _methods_by_ftype(ti, -1, typemax(UInt)) isempty(ml) && return true for m in ml diff --git a/base/test.jl b/base/test.jl index c7288202e8572..f21b8a0c2f830 100644 --- a/base/test.jl +++ b/base/test.jl @@ -1123,7 +1123,7 @@ defined in the specified modules. Use `imported=true` if you wish to also test functions that were imported into these modules from elsewhere. """ -function detect_ambiguities(mods...; imported::Bool=false) +function detect_ambiguities(mods...; imported::Bool=false, allow_bottom::Bool=true) function sortdefs(m1, m2) ord12 = m1.file < m2.file if !ord12 && (m1.file == m2.file) @@ -1145,7 +1145,7 @@ function detect_ambiguities(mods...; imported::Bool=false) for m in mt if m.ambig !== nothing for m2 in m.ambig - if Base.isambiguous(m, m2) + if Base.isambiguous(m, m2, allow_bottom) push!(ambs, sortdefs(m, m2)) end end diff --git a/test/inference.jl b/test/inference.jl index fe42a65f5da20..b13a34d720aa0 100644 --- a/test/inference.jl +++ b/test/inference.jl @@ -589,6 +589,17 @@ g11015(::Type{Bool}, ::Bool) = 2.0 @test Int <: Base.return_types(f11015, (AT11015,))[1] @test f11015(AT11015(true)) === 1 +# Inference for some type-level computation +fUnionAll{T}(::Type{T}) = Type{S} where S <: T +@inferred fUnionAll(Real) == Type{T} where T <: Real +@inferred fUnionAll(Rational{T} where T <: AbstractFloat) == Type{T} where T<:(Rational{S} where S <: AbstractFloat) + +fComplicatedUnionAll{T}(::Type{T}) = Type{Tuple{S,rand() >= 0.5 ? Int : Float64}} where S <: T +let pub = Base.parameter_upper_bound, x = fComplicatedUnionAll(Real) + @test pub(pub(x, 1), 1) == Real + @test pub(pub(x, 1), 2) == Int || pub(pub(x, 1), 2) == Float64 +end + # issue #20267 type T20267{T} inds::Vector{T} diff --git a/test/reflection.jl b/test/reflection.jl index e552a5e86bd38..fb35f5c968b73 100644 --- a/test/reflection.jl +++ b/test/reflection.jl @@ -615,3 +615,26 @@ end # PR #19964 @test isempty(subtypes(Float64)) + +# New reflection methods in 0.6 +immutable ReflectionExample{T<:AbstractFloat, N} + x::Tuple{T, N} +end + +@test Base.isabstract(AbstractArray) +@test !Base.isabstract(ReflectionExample) +@test !Base.isabstract(Int) + +@test Base.parameter_upper_bound(ReflectionExample, 1) === AbstractFloat +@test Base.parameter_upper_bound(ReflectionExample, 2) === Any +@test Base.parameter_upper_bound(ReflectionExample{T, N} where T where N <: Real, 2) === Real + +let + wrapperT(T) = Base.typename(T).wrapper + @test @inferred wrapperT(ReflectionExample{Float64, Int64}) == ReflectionExample + @test @inferred wrapperT(ReflectionExample{Float64, N} where N) == ReflectionExample + @test @inferred wrapperT(ReflectionExample{T, Int64} where T) == ReflectionExample + @test @inferred wrapperT(ReflectionExample) == ReflectionExample + @test @inferred wrapperT(Union{ReflectionExample{Union{},1},ReflectionExample{Float64,1}}) == ReflectionExample + @test_throws ErrorException Base.typename(Union{Int, Float64}) +end