From 5cc69d138edf93535194a83d10a07dc8119618d6 Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Mon, 8 Jan 2018 17:45:17 -0500 Subject: [PATCH] fix type-predicate queries expand the set of them to be more "complete", and document them more fully --- NEWS.md | 8 +- base/abstractarray.jl | 2 +- base/broadcast.jl | 2 +- base/compiler/abstractinterpretation.jl | 45 ++++--- base/compiler/compiler.jl | 2 - base/compiler/inferencestate.jl | 2 +- base/compiler/optimize.jl | 62 +++++---- base/compiler/params.jl | 4 +- base/compiler/tfuncs.jl | 105 ++++++++------- base/compiler/typelimits.jl | 162 ++++++++--------------- base/compiler/typeutils.jl | 119 +++++------------ base/compiler/utilities.jl | 3 +- base/complex.jl | 2 +- base/deprecated.jl | 3 +- base/dict.jl | 4 +- base/exports.jl | 6 +- base/float.jl | 2 +- base/interactiveutil.jl | 6 +- base/promotion.jl | 10 +- base/reflection.jl | 168 +++++++++++++++++------- base/refpointer.jl | 30 +++-- base/set.jl | 4 +- base/show.jl | 8 +- doc/src/base/base.md | 56 +++++--- src/ccall.cpp | 26 +--- src/cgutils.cpp | 94 +++++++------ src/codegen.cpp | 148 +++++++++++---------- src/common_symbols2.inc | 1 - src/datatype.c | 77 ++++++++--- src/dump.c | 29 ++-- src/gf.c | 89 +++++-------- src/intrinsics.cpp | 16 +-- src/jltypes.c | 142 +++++++------------- src/julia.h | 26 ++-- src/method.c | 6 +- src/precompile.c | 12 +- src/runtime_intrinsics.c | 6 +- src/subtype.c | 38 ++---- src/typemap.c | 10 +- stdlib/LinearAlgebra/src/adjtrans.jl | 27 ++-- test/arrayops.jl | 2 +- test/channels.jl | 1 + test/compiler/compiler.jl | 49 ++----- test/core.jl | 12 +- test/reflection.jl | 79 ++++++----- test/subtype.jl | 3 +- 46 files changed, 830 insertions(+), 878 deletions(-) diff --git a/NEWS.md b/NEWS.md index f227a8549fd32..09672fbd99d32 100644 --- a/NEWS.md +++ b/NEWS.md @@ -827,10 +827,10 @@ Deprecated or removed been deprecated due to inconsistency with linear algebra. Use `.+` and `.-` for these operations instead ([#22880], [#22932]). - * `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]). + * `isleaftype` is deprecated in favor of the simpler predicates `isconcretetype` and `isdispatchtuple`. + Concrete types are those that might equal `typeof(x)` for some `x`; + `isleaftype` included some types for which this is not true. Those are now categorized more precisely + as "dispatch tuple types" and "!has_free_typevars" (not exported). ([#17086], [#25496]) * `contains(eq, itr, item)` is deprecated in favor of `any` with a predicate ([#23716]). diff --git a/base/abstractarray.jl b/base/abstractarray.jl index 11d8522867095..47b63205b0a8a 100644 --- a/base/abstractarray.jl +++ b/base/abstractarray.jl @@ -1985,7 +1985,7 @@ function hash(a::AbstractArray{T}, h::UInt) where T # to a range with more than two elements because more extreme values # cannot be represented. We must still hash the two first values as a # range since they can always be considered as such (in a wider type) - if isconcrete(T) + if isconcretetype(T) try step = x2 - x1 catch err diff --git a/base/broadcast.jl b/base/broadcast.jl index b40aaf3854eb2..24766f2ba97d4 100644 --- a/base/broadcast.jl +++ b/base/broadcast.jl @@ -622,7 +622,7 @@ julia> string.(("one","two","three","four"), ": ", 1:4) const NonleafHandlingTypes = Union{DefaultArrayStyle,ArrayConflict,VectorStyle,MatrixStyle} @inline function broadcast(f, s::NonleafHandlingTypes, ::Type{ElType}, inds::Indices, As...) where ElType - if !Base._isleaftype(ElType) + if !Base.isconcretetype(ElType) return broadcast_nonleaf(f, s, ElType, inds, As...) end dest = broadcast_similar(f, s, ElType, inds, As...) diff --git a/base/compiler/abstractinterpretation.jl b/base/compiler/abstractinterpretation.jl index 8e729cad6321c..e1e314be283c4 100644 --- a/base/compiler/abstractinterpretation.jl +++ b/base/compiler/abstractinterpretation.jl @@ -118,12 +118,10 @@ function abstract_call_method_with_const_args(@nospecialize(f), argtypes::Vector haveconst = false for i in 1:nargs a = argtypes[i] - if isa(a, Const) && !isdefined(typeof(a.val), :instance) - if !isleaftype(a.val) # alternately: !isa(a.val, DataType) || !isconstType(Type{a.val}) - # have new information from argtypes that wasn't available from the signature - haveconst = true - break - end + if isa(a, Const) && !isdefined(typeof(a.val), :instance) && !(isa(a.val, Type) && issingletontype(a.val)) + # have new information from argtypes that wasn't available from the signature + haveconst = true + break end end haveconst || return Any @@ -378,11 +376,13 @@ end # do apply(af, fargs...), where af is a function value function abstract_apply(@nospecialize(aft), fargs::Vector{Any}, aargtypes::Vector{Any}, vtypes::VarTable, sv::InferenceState) - if !isa(aft, Const) && !isconstType(aft) - if !(isleaftype(aft) || aft <: Type) || (aft <: Builtin) || (aft <: IntrinsicFunction) + if !isa(aft, Const) && (!isType(aft) || has_free_typevars(aft)) + if !isconcretetype(aft) || (aft <: Builtin) + # non-constant function of unknown type: bail now, + # since it seems unlikely that abstract_call will be able to do any better after splitting + # this also ensures we don't call abstract_call_gf_by_type below on an IntrinsicFunction or Builtin return Any end - # non-constant function, but type is known end res = Union{} nargs = length(fargs) @@ -394,7 +394,7 @@ function abstract_apply(@nospecialize(aft), fargs::Vector{Any}, aargtypes::Vecto for ti in (splitunions ? uniontypes(aargtypes[i]) : Any[aargtypes[i]]) cti = precise_container_type(fargs[i], ti, vtypes, sv) for ct in ctypes - if !isempty(ct) && isvarargtype(ct[end]) + if isvarargtype(ct[end]) tail = tuple_tail_elem(unwrapva(ct[end]), cti) push!(ctypes´, push!(ct[1:(end - 1)], tail)) else @@ -672,23 +672,22 @@ function abstract_eval_call(e::Expr, vtypes::VarTable, sv::InferenceState) ft = argtypes[1] if isa(ft, Const) f = ft.val + elseif isconstType(ft) + f = ft.parameters[1] + elseif isa(ft, DataType) && isdefined(ft, :instance) + f = ft.instance else - if isType(ft) && isleaftype(ft.parameters[1]) - f = ft.parameters[1] - elseif isleaftype(ft) && isdefined(ft, :instance) - f = ft.instance - else - for i = 2:(length(argtypes)-1) - if isvarargtype(argtypes[i]) - return Any - end - end - # non-constant function, but type is known - if (isleaftype(ft) || ft <: Type) && !(ft <: Builtin) && !(ft <: IntrinsicFunction) - return abstract_call_gf_by_type(nothing, argtypes, argtypes_to_type(argtypes), sv) + for i = 2:(length(argtypes) - 1) + if isvarargtype(argtypes[i]) + return Any end + end + # non-constant function, but the number of arguments is known + # and the ft is not a Builtin or IntrinsicFunction + if typeintersect(widenconst(ft), Builtin) != Union{} return Any end + return abstract_call_gf_by_type(nothing, argtypes, argtypes_to_type(argtypes), sv) end return abstract_call(f, e.args, argtypes, vtypes, sv) end diff --git a/base/compiler/compiler.jl b/base/compiler/compiler.jl index 87e5bc8644cca..847f0a8380b99 100644 --- a/base/compiler/compiler.jl +++ b/base/compiler/compiler.jl @@ -76,8 +76,6 @@ include("docs/core.jl") inlining_enabled() = (JLOptions().can_inline == 1) coverage_enabled() = (JLOptions().code_coverage != 0) -const isleaftype = _isleaftype - include("compiler/utilities.jl") include("compiler/validation.jl") diff --git a/base/compiler/inferencestate.jl b/base/compiler/inferencestate.jl index 29b8e708339b2..e585cda86cb78 100644 --- a/base/compiler/inferencestate.jl +++ b/base/compiler/inferencestate.jl @@ -84,7 +84,7 @@ mutable struct InferenceState at = (i > nargs) ? Bottom : argtypes[i] if !toplevel && linfo.def.isva && i == nargs if !(at == Tuple) # would just be a no-op - vararg_type_container = limit_tuple_depth(params, unwrap_unionall(at)) # TODO: should be limiting tuple depth much earlier than here + vararg_type_container = unwrap_unionall(at) vararg_type = tuple_tfunc(vararg_type_container) # returns a Const object, if applicable at = rewrap(vararg_type, linfo.specTypes) end diff --git a/base/compiler/optimize.jl b/base/compiler/optimize.jl index 336d91484bfa6..bd134f26f675e 100644 --- a/base/compiler/optimize.jl +++ b/base/compiler/optimize.jl @@ -827,8 +827,8 @@ function effect_free(@nospecialize(e), src::CodeInfo, mod::Module, allow_volatil return true end if head === :static_parameter - # if we aren't certain about the type, it might be an UndefVarError at runtime - return (isa(e.typ, DataType) && isleaftype(e.typ)) || isa(e.typ, Const) + # if we aren't certain enough about the type, it might be an UndefVarError at runtime + return isa(e.typ, Const) || issingletontype(widenconst(e.typ)) end if e.typ === Bottom return false @@ -841,17 +841,17 @@ function effect_free(@nospecialize(e), src::CodeInfo, mod::Module, allow_volatil return false elseif is_known_call(e, getfield, src, mod) nargs = length(ea) - (2 < nargs < 5) || return false + (3 < nargs < 4) || return false et = exprtype(e, src, mod) - if !isa(et, Const) && !(isType(et) && isleaftype(et)) + # TODO: check ninitialized + if !isa(et, Const) && !isconstType(et) # first argument must be immutable to ensure e is affect_free a = ea[2] - typ = widenconst(exprtype(a, src, mod)) - if isconstType(typ) - if Const(:uid) ⊑ exprtype(ea[3], src, mod) - return false # DataType uid field can change - end - elseif typ !== SimpleVector && (!isa(typ, DataType) || typ.mutable || typ.abstract) + typ = unwrap_unionall(widenconst(exprtype(a, src, mod))) + if isType(typ) + # all fields of subtypes of Type are effect-free + # (including the non-inferrable uid field) + elseif !isa(typ, DataType) || typ.abstract || (typ.mutable && length(typ.types) > 0) return false end end @@ -874,7 +874,8 @@ function effect_free(@nospecialize(e), src::CodeInfo, mod::Module, allow_volatil # `Expr(:new)` of unknown type could raise arbitrary TypeError. typ, isexact = instanceof_tfunc(typ) isexact || return false - (isleaftype(typ) && !iskindtype(typ)) || return false + isconcretetype(typ) || return false + !iskindtype(typ) || return false typ = typ::DataType if !allow_volatile && typ.mutable return false @@ -1086,11 +1087,11 @@ function inlineable(@nospecialize(f), @nospecialize(ft), e::Expr, atypes::Vector sv::OptimizationState) argexprs = e.args - if (f === typeassert || ft ⊑ typeof(typeassert)) && length(atypes)==3 + if (f === typeassert || ft ⊑ typeof(typeassert)) && length(atypes) == 3 # typeassert(x::S, T) => x, when S<:T a3 = atypes[3] - if (isType(a3) && isleaftype(a3) && atypes[2] ⊑ a3.parameters[1]) || - (isa(a3,Const) && isa(a3.val,Type) && atypes[2] ⊑ a3.val) + if (isType(a3) && !has_free_typevars(a3) && atypes[2] ⊑ a3.parameters[1]) || + (isa(a3, Const) && isa(a3.val, Type) && atypes[2] ⊑ a3.val) return (argexprs[2], ()) end end @@ -1119,7 +1120,9 @@ function inlineable(@nospecialize(f), @nospecialize(ft), e::Expr, atypes::Vector if f === Core.invoke && length(atypes) >= 3 ft = widenconst(atypes[2]) invoke_tt = widenconst(atypes[3]) - if !isleaftype(ft) || !isleaftype(invoke_tt) || !isType(invoke_tt) + if !(isconcretetype(ft) || ft <: Type) || !isType(invoke_tt) || + has_free_typevars(invoke_tt) || has_free_typevars(ft) || (ft <: Builtin) + # TODO: this is really aggressive at preventing inlining of closures. maybe drop `isconcretetype` requirement? return NOT_FOUND end if !(isa(invoke_tt.parameters[1], Type) && @@ -1282,12 +1285,10 @@ function inlineable(@nospecialize(f), @nospecialize(ft), e::Expr, atypes::Vector haveconst = false for i in 1:length(atypes) a = atypes[i] - if isa(a, Const) && !isdefined(typeof(a.val), :instance) - if !isleaftype(a.val) # alternately: !isa(a.val, DataType) || !isconstType(Type{a.val}) - # have new information from argtypes that wasn't available from the signature - haveconst = true - break - end + if isa(a, Const) && !isdefined(typeof(a.val), :instance) && !(isa(a.val, Type) && issingletontype(a.val)) + # have new information from argtypes that wasn't available from the signature + haveconst = true + break end end if haveconst @@ -1549,8 +1550,9 @@ end # saturating sum (inputs are nonnegative), prevents overflow with typemax(Int) below plus_saturate(x, y) = max(x, y, x+y) + # known return type -isknowntype(T) = (T == Union{}) || isleaftype(T) +isknowntype(@nospecialize T) = (T == Union{}) || isconcretetype(T) function statement_cost(ex::Expr, line::Int, src::CodeInfo, mod::Module, params::Params) head = ex.head @@ -1781,7 +1783,8 @@ function inline_call(e::Expr, sv::OptimizationState, stmts::Vector{Any}, boundsc ft = Bool else f = nothing - if !( isleaftype(ft) || ft<:Type ) + if !(isconcretetype(ft) || (widenconst(ft) <: Type)) || has_free_typevars(ft) + # TODO: this is really aggressive at preventing inlining of closures. maybe drop `isconcretetype` requirement? return e end end @@ -1938,7 +1941,8 @@ function inline_call(e::Expr, sv::OptimizationState, stmts::Vector{Any}, boundsc ft = Bool else f = nothing - if !( isleaftype(ft) || ft<:Type ) + if !(isconcretetype(ft) || (widenconst(ft) <: Type)) || has_free_typevars(ft) + # TODO: this is really aggressive at preventing inlining of closures. maybe drop `isconcretetype` requirement? return e end end @@ -2754,7 +2758,7 @@ end function split_disjoint_assign!(ctx::AllocOptContext, info, key) key.second && return false - isleaftype(ctx.sv.src.slottypes[key.first]) && return false + isdispatchelem(widenconst(ctx.sv.src.slottypes[key.first])) && return false # no splitting can be necessary alltypes = IdDict() ndefs = length(info.defs) deftypes = Vector{Any}(uninitialized, ndefs) @@ -2762,7 +2766,7 @@ function split_disjoint_assign!(ctx::AllocOptContext, info, key) def = info.defs[i] defex = (def.assign::Expr).args[2] rhstyp = widenconst(exprtype(defex, ctx.sv.src, ctx.sv.mod)) - isleaftype(rhstyp) || return false + isdispatchelem(rhstyp) || return false alltypes[rhstyp] = nothing deftypes[i] = rhstyp end @@ -2772,7 +2776,7 @@ function split_disjoint_assign!(ctx::AllocOptContext, info, key) slot = usex.args[use.exidx] if isa(slot, TypedSlot) usetyp = widenconst(slot.typ) - if isleaftype(usetyp) + if isdispatchelem(usetyp) alltypes[usetyp] = nothing continue end @@ -2827,7 +2831,7 @@ function split_disjoint_assign!(ctx::AllocOptContext, info, key) slot = usex.args[use.exidx] if isa(slot, TypedSlot) usetyp = widenconst(slot.typ) - if isleaftype(usetyp) + if isdispatchelem(usetyp) usetyp = widenconst(slot.typ) new_slot = alltypes[usetyp] if !isa(new_slot, SlotNumber) @@ -2997,7 +3001,7 @@ function split_struct_alloc!(ctx::AllocOptContext, info, key) elseif defex.head === :new typ = widenconst(exprtype(defex, ctx.sv.src, ctx.sv.mod)) # typ <: Tuple shouldn't happen but just in case someone generated invalid AST - if !isa(typ, DataType) || !isleaftype(typ) || typ <: Tuple + if !isa(typ, DataType) || !isdispatchelem(typ) || typ <: Tuple return false end si = structinfo_new(ctx, defex, typ) diff --git a/base/compiler/params.jl b/base/compiler/params.jl index 5806604f6feeb..dc376e03c0f4f 100644 --- a/base/compiler/params.jl +++ b/base/compiler/params.jl @@ -28,7 +28,6 @@ struct Params # parameters limiting large types MAX_TUPLETYPE_LEN::Int - MAX_TUPLE_DEPTH::Int # when attempting to inlining _apply, abort the optimization if the tuple # contains more than this many elements @@ -42,13 +41,12 @@ struct Params inline_tupleret_bonus::Int = 400, max_methods::Int = 4, tupletype_len::Int = 15, - tuple_depth::Int = 4, tuple_splat::Int = 16, union_splitting::Int = 4, apply_union_enum::Int = 8) return new(Vector{InferenceResult}(), world, inlining, true, false, inline_cost_threshold, inline_nonleaf_penalty, inline_tupleret_bonus, max_methods, union_splitting, apply_union_enum, - tupletype_len, tuple_depth, tuple_splat) + tupletype_len, tuple_splat) end end diff --git a/base/compiler/tfuncs.jl b/base/compiler/tfuncs.jl index 6576ccb6cc080..0d2465b97398f 100644 --- a/base/compiler/tfuncs.jl +++ b/base/compiler/tfuncs.jl @@ -31,6 +31,10 @@ const TYPENAME_WRAPPER_FIELDINDEX = fieldindex(TypeName, :wrapper) # tfuncs # ########## +# Note that in most places in the compiler here, we'll assume that T=Type{S} is well-formed, +# and implies that `S <: Type`, not `1::Type{1}`, for example. +# This means that isType(T) implies we can call subtype on T.parameters[1], etc. + function add_tfunc(f::IntrinsicFunction, minarg::Int, maxarg::Int, @nospecialize(tfunc), cost::Int) idx = reinterpret(Int32, f) + 1 T_IFUNC[idx] = (minarg, maxarg, tfunc) @@ -252,7 +256,7 @@ function isdefined_tfunc(args...) if 1 <= idx <= a1.ninitialized return Const(true) elseif a1.name === _NAMEDTUPLE_NAME - if isleaftype(a1) + if isconcretetype(a1) return Const(false) end elseif idx <= 0 || (!isvatuple(a1) && idx > fieldcount(a1)) @@ -268,31 +272,39 @@ function isdefined_tfunc(args...) end # TODO change INT_INF to 2 when deprecation is removed add_tfunc(isdefined, 1, INT_INF, isdefined_tfunc, 1) -_const_sizeof(@nospecialize(x)) = try +function _const_sizeof(@nospecialize(x)) # Constant Vector does not have constant size isa(x, Vector) && return Int - return Const(Core.sizeof(x)) -catch - return Int + size = try + Core.sizeof(x) + catch ex + # Might return + # "argument is an abstract type; size is indeterminate" or + # "type does not have a fixed size" + isa(ex, ErrorException) || rethrow(ex) + return Int + end + return Const(size) end add_tfunc(Core.sizeof, 1, 1, function (@nospecialize(x),) isa(x, Const) && return _const_sizeof(x.val) isa(x, Conditional) && return _const_sizeof(Bool) isconstType(x) && return _const_sizeof(x.parameters[1]) - x !== DataType && isleaftype(x) && return _const_sizeof(x) + x !== DataType && isconcretetype(x) && return _const_sizeof(x) return Int end, 0) -old_nfields(@nospecialize x) = length((isa(x,DataType) ? x : typeof(x)).types) +old_nfields(@nospecialize x) = length((isa(x, DataType) ? x : typeof(x)).types) add_tfunc(nfields, 1, 1, function (@nospecialize(x),) - isa(x,Const) && return Const(old_nfields(x.val)) - isa(x,Conditional) && return Const(old_nfields(Bool)) + isa(x, Const) && return Const(old_nfields(x.val)) + isa(x, Conditional) && return Const(old_nfields(Bool)) if isType(x) # TODO: remove with deprecation in builtins.c for nfields(::Type) - isleaftype(x.parameters[1]) && return Const(old_nfields(x.parameters[1])) - elseif isa(x,DataType) && !x.abstract && !(x.name === Tuple.name && isvatuple(x)) && x !== DataType - if !(x.name === _NAMEDTUPLE_NAME && !isleaftype(x)) + p = x.parameters[1] + issingletontype(p) && return Const(old_nfields(p)) + elseif isa(x, DataType) && !x.abstract && !(x.name === Tuple.name && isvatuple(x)) && x !== DataType + if !(x.name === _NAMEDTUPLE_NAME && !isconcretetype(x)) return Const(length(x.types)) end end @@ -326,13 +338,13 @@ function typeof_tfunc(@nospecialize(t)) return Const(Bool) elseif isType(t) tp = t.parameters[1] - if !isleaftype(tp) - return DataType # typeof(Kind::Type)::DataType + if issingletontype(tp) + return Const(typeof(tp)) else - return Const(typeof(tp)) # XXX: this is not necessarily true + return Type end elseif isa(t, DataType) - if isleaftype(t) || isvarargtype(t) + if isconcretetype(t) || isvarargtype(t) return Const(t) elseif t === Any return DataType @@ -379,7 +391,9 @@ add_tfunc(isa, 2, 2, if isexact return Const(true) end - elseif isa(v, Const) || isa(v, Conditional) || (isleaftype(v) && !iskindtype(v)) + elseif isa(v, Const) || isa(v, Conditional) || isdispatchelem(v) + # this tests for knowledge of a leaftype appearing on the LHS + # (ensuring the isa is precise) return Const(false) elseif isexact && typeintersect(v, t) === Bottom if !iskindtype(v) #= subtyping currently intentionally answers this query incorrectly for kinds =# @@ -481,7 +495,7 @@ function getfield_tfunc(@nospecialize(s00), @nospecialize(name)) end return Any end - if s.name === _NAMEDTUPLE_NAME && !isleaftype(s) + if s.name === _NAMEDTUPLE_NAME && !isconcretetype(s) # TODO: better approximate inference return Any end @@ -499,12 +513,7 @@ function getfield_tfunc(@nospecialize(s00), @nospecialize(name)) return rewrap_unionall(unwrapva(s.types[1]), s00) end # union together types of all fields - R = reduce(tmerge, Bottom, map(t -> rewrap_unionall(unwrapva(t), s00), s.types)) - # do the same limiting as the known-symbol case to preserve type-monotonicity - if isempty(s.parameters) - return R - end - return limit_type_depth(R, MAX_TYPE_DEPTH) + return reduce(tmerge, Bottom, map(t -> rewrap_unionall(unwrapva(t), s00), s.types)) end fld = name.val if isa(fld,Symbol) @@ -520,14 +529,14 @@ function getfield_tfunc(@nospecialize(s00), @nospecialize(name)) if fld < 1 || fld > nf return Bottom end - if isType(s00) && isleaftype(s00.parameters[1]) + if isconstType(s00) sp = s00.parameters[1] - elseif isa(s00, Const) && isa(s00.val, DataType) + elseif isa(s00, Const) sp = s00.val else sp = nothing end - if sp !== nothing + if isa(sp, DataType) t = const_datatype_getfield_tfunc(sp, fld) t !== nothing && return t end @@ -535,11 +544,7 @@ function getfield_tfunc(@nospecialize(s00), @nospecialize(name)) if isempty(s.parameters) return R end - # TODO jb/subtype is this still necessary? - # conservatively limit the type depth here, - # since the UnionAll type bound is otherwise incorrect - # in the current type system - return rewrap_unionall(limit_type_depth(R, MAX_TYPE_DEPTH), s00) + return rewrap_unionall(R, s00) end add_tfunc(getfield, 2, 3, getfield_tfunc, 1) add_tfunc(setfield!, 3, 3, (@nospecialize(o), @nospecialize(f), @nospecialize(v)) -> v, 3) @@ -568,7 +573,7 @@ function fieldtype_tfunc(@nospecialize(s0), @nospecialize(name)) if !isa(u,DataType) || u.abstract return Type end - if u.name === _NAMEDTUPLE_NAME && !isleaftype(u) + if u.name === _NAMEDTUPLE_NAME && !isconcretetype(u) return Type end ftypes = u.types @@ -613,7 +618,7 @@ add_tfunc(fieldtype, 2, 3, fieldtype_tfunc, 0) function apply_type_tfunc(@nospecialize(headtypetype), @nospecialize args...) if isa(headtypetype, Const) headtype = headtypetype.val - elseif isType(headtypetype) && isleaftype(headtypetype.parameters[1]) + elseif isconstType(headtypetype) headtype = headtypetype.parameters[1] else return Any @@ -636,7 +641,7 @@ function apply_type_tfunc(@nospecialize(headtypetype), @nospecialize args...) ai = args[i] if isType(ai) aty = ai.parameters[1] - isleaftype(aty) || (allconst = false) + allconst &= issingletontype(aty) else aty = (ai::Const).val end @@ -725,14 +730,11 @@ end add_tfunc(apply_type, 1, INT_INF, apply_type_tfunc, 10) function invoke_tfunc(@nospecialize(f), @nospecialize(types), @nospecialize(argtype), sv::InferenceState) - if !isleaftype(Type{types}) - return Any - end - argtype = typeintersect(types,limit_tuple_type(argtype, sv.params)) + argtype = typeintersect(types, limit_tuple_type(argtype, sv.params)) if argtype === Bottom return Bottom end - ft = type_typeof(f) + ft = Core.Typeof(f) types = rewrap_unionall(Tuple{ft, unwrap_unionall(types).parameters...}, types) argtype = Tuple{ft, argtype.parameters...} entry = ccall(:jl_gf_invoke_lookup, Any, (Any, UInt), types, sv.params.world) @@ -746,13 +748,15 @@ function invoke_tfunc(@nospecialize(f), @nospecialize(types), @nospecialize(argt return rt end +# convert the dispatch tuple type argtype to the real (concrete) type of +# the tuple of those values function tuple_tfunc(@nospecialize(argtype)) if isa(argtype, DataType) && argtype.name === Tuple.name p = Vector{Any}() for x in argtype.parameters - if isType(x) && !isa(x.parameters[1], TypeVar) + if isType(x) xparam = x.parameters[1] - if isleaftype(xparam) || xparam === Bottom + if issingletontype(xparam) || xparam === Bottom push!(p, typeof(xparam)) else push!(p, Type) @@ -775,7 +779,7 @@ function builtin_tfunction(@nospecialize(f), argtypes::Array{Any,1}, if f === tuple for a in argtypes if !isa(a, Const) - return tuple_tfunc(limit_tuple_depth(params, argtypes_to_type(argtypes))) + return tuple_tfunc(argtypes_to_type(argtypes)) end end return Const(tuple(anymap(a->a.val, argtypes)...)) @@ -812,7 +816,7 @@ function builtin_tfunction(@nospecialize(f), argtypes::Array{Any,1}, end return Expr elseif f === invoke - if length(argtypes)>1 && isa(argtypes[1], Const) + if length(argtypes) > 1 && isa(argtypes[1], Const) && sv !== nothing af = argtypes[1].val sig = argtypes[2] if isa(sig, Const) @@ -822,7 +826,7 @@ function builtin_tfunction(@nospecialize(f), argtypes::Array{Any,1}, else sigty = nothing end - if isa(sigty, Type) && sigty <: Tuple && sv !== nothing + if isa(sigty, Type) && !has_free_typevars(sigty) && sigty <: Tuple return invoke_tfunc(af, sigty, argtypes_to_type(argtypes[3:end]), sv) end end @@ -862,14 +866,14 @@ end # TODO: this function is a very buggy and poor model of the return_type function # since abstract_call_gf_by_type is a very inaccurate model of _method and of typeinf_type, -# while this assumes that it is a precisely accurate and exact model of both +# while this assumes that it is an absolutely precise and accurate and exact model of both function return_type_tfunc(argtypes::Vector{Any}, vtypes::VarTable, sv::InferenceState) if length(argtypes) == 3 tt = argtypes[3] if isa(tt, Const) || (isType(tt) && !has_free_typevars(tt)) aft = argtypes[2] if isa(aft, Const) || (isType(aft) && !has_free_typevars(aft)) || - (isleaftype(aft) && !(aft <: Builtin)) + (isconcretetype(aft) && !(aft <: Builtin)) af_argtype = isa(tt, Const) ? tt.val : tt.parameters[1] if isa(af_argtype, DataType) && af_argtype <: Tuple argtypes_vec = Any[aft, af_argtype.parameters...] @@ -887,15 +891,18 @@ function return_type_tfunc(argtypes::Vector{Any}, vtypes::VarTable, sv::Inferenc if isa(rt, Const) # output was computed to be constant return Const(typeof(rt.val)) - elseif isleaftype(rt) || rt === Bottom + elseif issingletontype(rt) || rt === Bottom # output type was known for certain return Const(rt) elseif (isa(tt, Const) || isconstType(tt)) && (isa(aft, Const) || isconstType(aft)) # input arguments were known for certain + # XXX: this doesn't imply we know anything about rt return Const(rt) + elseif isType(rt) + return Type{rt} else - return Type{<:rt} + return Type{<:widenconst(rt)} end end end diff --git a/base/compiler/typelimits.jl b/base/compiler/typelimits.jl index 2be057d0c2ecb..d537299089a1c 100644 --- a/base/compiler/typelimits.jl +++ b/base/compiler/typelimits.jl @@ -13,44 +13,10 @@ const TUPLE_COMPLEXITY_LIMIT_DEPTH = 3 # limitation heuristics # ######################### -function limit_type_depth(@nospecialize(t), d::Int) - r = limit_type_depth(t, d, true, TypeVar[]) - @assert !isa(t, Type) || t <: r - return r -end - -limit_tuple_depth(params::Params, @nospecialize(t)) = limit_tuple_depth_(params,t,0) - -function limit_tuple_depth_(params::Params, @nospecialize(t), d::Int) - if isa(t,Union) - # also limit within Union types. - # may have to recur into other stuff in the future too. - return Union{limit_tuple_depth_(params, t.a, d+1), - limit_tuple_depth_(params, t.b, d+1)} - elseif isa(t,UnionAll) - ub = limit_tuple_depth_(params, t.var.ub, d) - if ub !== t.var.ub - var = TypeVar(t.var.name, t.var.lb, ub) - body = t{var} - else - var = t.var - body = t.body - end - body = limit_tuple_depth_(params, body, d) - return UnionAll(var, body) - elseif !(isa(t,DataType) && t.name === Tuple.name) - return t - elseif d > params.MAX_TUPLE_DEPTH - return Tuple - end - p = map(x->limit_tuple_depth_(params,x,d+1), t.parameters) - Tuple{p...} -end - -limit_tuple_type = (@nospecialize(t), params::Params) -> limit_tuple_type_n(t, params.MAX_TUPLETYPE_LEN) +limit_tuple_type(@nospecialize(t), params::Params) = limit_tuple_type_n(t, params.MAX_TUPLETYPE_LEN) function limit_tuple_type_n(@nospecialize(t), lim::Int) - if isa(t,UnionAll) + if isa(t, UnionAll) return UnionAll(t.var, limit_tuple_type_n(t.body, lim)) end p = t.parameters @@ -62,75 +28,6 @@ function limit_tuple_type_n(@nospecialize(t), lim::Int) return t end -function limit_type_depth(@nospecialize(t), d::Int, cov::Bool, vars::Vector{TypeVar}=TypeVar[]) - if isa(t, Union) - if d < 0 - if cov - return Any - else - var = TypeVar(:_) - push!(vars, var) - return var - end - end - return Union{limit_type_depth(t.a, d - 1, cov, vars), - limit_type_depth(t.b, d - 1, cov, vars)} - elseif isa(t, UnionAll) - v = t.var - if v.ub === Any - if v.lb === Bottom - return UnionAll(t.var, limit_type_depth(t.body, d, cov, vars)) - end - ub = Any - else - ub = limit_type_depth(v.ub, d - 1, cov, vars) - end - if v.lb === Bottom || type_depth(v.lb) > d - # note: lower bounds need to be widened by making them lower - lb = Bottom - else - lb = v.lb - end - v2 = TypeVar(v.name, lb, ub) - return UnionAll(v2, limit_type_depth(t{v2}, d, cov, vars)) - elseif !isa(t,DataType) - return t - end - P = t.parameters - isempty(P) && return t - if d < 0 - if isvarargtype(t) - # never replace Vararg with non-Vararg - # passing depth=0 avoids putting a bare typevar here, for the diagonal rule - return Vararg{limit_type_depth(P[1], 0, cov, vars), P[2]} - end - widert = t.name.wrapper - if !(t <: widert) - # This can happen when a typevar has bounds too wide for its context, e.g. - # `Complex{T} where T` is not a subtype of `Complex`. In that case widen even - # faster to something safe to ensure the result is a supertype of the input. - widert = Any - end - cov && return widert - var = TypeVar(:_, widert) - push!(vars, var) - return var - end - stillcov = cov && (t.name === Tuple.name) - newdepth = d - 1 - if isvarargtype(t) - newdepth = max(newdepth, 0) - end - Q = map(x -> limit_type_depth(x, newdepth, stillcov, vars), P) - R = t.name.wrapper{Q...} - if cov && !stillcov - for var in vars - R = UnionAll(var, R) - end - end - return R -end - # limit the complexity of type `t` to be simpler than the comparison type `compare` # no new values may be introduced, so the parameter `source` encodes the set of all values already present # the outermost tuple type is permitted to have up to `allowed_tuplelen` parameters @@ -146,6 +43,61 @@ function limit_type_size(@nospecialize(t), @nospecialize(compare), @nospecialize return r end +# try to find `type` somewhere in `comparison` type +# at a minimum nesting depth of `mindepth` +function is_derived_type(@nospecialize(t), @nospecialize(c), mindepth::Int) + if mindepth > 0 + mindepth -= 1 + end + if t === c + return mindepth == 0 + end + if isa(c, TypeVar) + # see if it is replacing a TypeVar upper bound with something simpler + return is_derived_type(t, c.ub, mindepth) + elseif isa(c, Union) + # see if it is one of the elements of the union + return is_derived_type(t, c.a, mindepth + 1) || is_derived_type(t, c.b, mindepth + 1) + elseif isa(c, UnionAll) + # see if it is derived from the body + return is_derived_type(t, c.body, mindepth) + elseif isa(c, DataType) + if isa(t, DataType) + # see if it is one of the supertypes of a parameter + super = supertype(c) + while super !== Any + t === super && return true + super = supertype(super) + end + end + # see if it was extracted from a type parameter + cP = c.parameters + for p in cP + is_derived_type(t, p, mindepth) && return true + end + if isconcretetype(c) && isbits(c) + # see if it was extracted from a fieldtype + # however, only look through types that can be inlined + # to ensure monotonicity of derivation + # since we know that for immutable, concrete, bits types, + # the field types must have been constructed prior to the type, + # it cannot have a reference cycle in the type graph + cF = c.types + for f in cF + is_derived_type(t, f, mindepth) && return true + end + end + end + return false +end + +function is_derived_type_from_any(@nospecialize(t), sources::SimpleVector, mindepth::Int) + for s in sources + is_derived_type(t, s, mindepth) && return true + end + return false +end + # type vs. comparison or which was derived from source function _limit_type_size(@nospecialize(t), @nospecialize(c), sources::SimpleVector, depth::Int, allowed_tuplelen::Int) if t === c diff --git a/base/compiler/typeutils.jl b/base/compiler/typeutils.jl index 9e82535777786..edbb8b7ee9998 100644 --- a/base/compiler/typeutils.jl +++ b/base/compiler/typeutils.jl @@ -4,113 +4,54 @@ const _TYPE_NAME = Type.body.name isType(@nospecialize t) = isa(t, DataType) && (t::DataType).name === _TYPE_NAME -# true if Type is inlineable as constant (is a singleton) -function isconstType(@nospecialize t) - isType(t) || return false - p1 = t.parameters[1] - # typeof(Bottom) is special since even though it is as leaftype, +# true if Type{T} is inlineable as constant T +# requires that T is a singleton, s.t. T == S implies T === S +isconstType(@nospecialize t) = isType(t) && issingletontype(t.parameters[1]) + +# test whether T is a singleton type, s.t. T == S implies T === S +function issingletontype(@nospecialize t) + # typeof(Bottom) is special since even though it is a leaftype, # at runtime, it might be Type{Union{}} instead, so don't attempt inference of it - p1 === typeof(Union{}) && return false - p1 === Union{} && return true - isleaftype(p1) && return true + t === typeof(Union{}) && return false + t === Union{} && return true + isa(t, TypeVar) && return false # TypeVars are identified by address, not equality + iskindtype(typeof(t)) || return true # non-types are always compared by egal in the type system + isconcretetype(t) && return true # these are also interned and pointer comparable + if isa(t, DataType) && t.name !== Tuple.name && !isvarargtype(t) # invariant DataTypes + return all(p -> issingletontype(p), t.parameters) + end return false end iskindtype(@nospecialize t) = (t === DataType || t === UnionAll || t === Union || t === typeof(Bottom)) -argtypes_to_type(argtypes::Array{Any,1}) = Tuple{anymap(widenconst, argtypes)...} - -isknownlength(t::DataType) = !isvatuple(t) || (length(t.parameters) > 0 && isa(unwrap_unionall(t.parameters[end]).parameters[2],Int)) - -function type_depth(@nospecialize(t)) - if t === Bottom - return 0 - elseif isa(t, Union) - return max(type_depth(t.a), type_depth(t.b)) + 1 - elseif isa(t, DataType) - return (t::DataType).depth - elseif isa(t, UnionAll) - if t.var.ub === Any && t.var.lb === Bottom - return type_depth(t.body) - end - return max(type_depth(t.var.ub) + 1, type_depth(t.var.lb) + 1, type_depth(t.body)) - end - return 0 +# equivalent to isdispatchtuple(Tuple{v}) || v == Union{} +# and is thus perhaps most similar to the old (pre-1.0) `isleaftype` query +function isdispatchelem(@nospecialize v) + return (v === Bottom) || (v === typeof(Bottom)) || + (isconcretetype(v) && !iskindtype(v)) || + (isType(v) && !has_free_typevars(v)) end -# try to find `type` somewhere in `comparison` type -# at a minimum nesting depth of `mindepth` -function is_derived_type(@nospecialize(t), @nospecialize(c), mindepth::Int) - if mindepth > 0 - mindepth -= 1 - end - if t === c - return mindepth == 0 - end - if isa(c, TypeVar) - # see if it is replacing a TypeVar upper bound with something simpler - return is_derived_type(t, c.ub, mindepth) - elseif isa(c, Union) - # see if it is one of the elements of the union - return is_derived_type(t, c.a, mindepth + 1) || is_derived_type(t, c.b, mindepth + 1) - elseif isa(c, UnionAll) - # see if it is derived from the body - return is_derived_type(t, c.body, mindepth) - elseif isa(c, DataType) - if isa(t, DataType) - # see if it is one of the supertypes of a parameter - super = supertype(c) - while super !== Any - t === super && return true - super = supertype(super) - end - end - # see if it was extracted from a type parameter - cP = c.parameters - for p in cP - is_derived_type(t, p, mindepth) && return true - end - if isleaftype(c) && isbits(c) - # see if it was extracted from a fieldtype - # however, only look through types that can be inlined - # to ensure monotonicity of derivation - # since we know that for immutable types, - # the field types must have been constructed prior to the type, - # it cannot have a reference cycle in the type graph - cF = c.types - for f in cF - is_derived_type(t, f, mindepth) && return true - end - end - end - return false -end +argtypes_to_type(argtypes::Array{Any,1}) = Tuple{anymap(widenconst, argtypes)...} -function is_derived_type_from_any(@nospecialize(t), sources::SimpleVector, mindepth::Int) - for s in sources - is_derived_type(t, s, mindepth) && return true - end - return false +function isknownlength(t::DataType) + isvatuple(t) || return true + return length(t.parameters) > 0 && isa(unwrap_unionall(t.parameters[end]).parameters[2], Int) end +# test if non-Type, non-TypeVar `x` can be used to parameterize a type function valid_tparam(@nospecialize(x)) - if isa(x,Tuple) + if isa(x, Tuple) for t in x - !valid_tparam(t) && return false + isa(t, Symbol) || isbits(typeof(t)) || return false end return true end - return isa(x,Int) || isa(x,Symbol) || isa(x,Bool) || (!isa(x,Type) && isbits(x)) + return isa(x, Symbol) || isbits(typeof(x)) end -has_free_typevars(@nospecialize(t)) = ccall(:jl_has_free_typevars, Cint, (Any,), t)!=0 - -@pure function type_typeof(@nospecialize(v)) - if isa(v, Type) - return Type{v} - end - return typeof(v) -end +has_free_typevars(@nospecialize(t)) = ccall(:jl_has_free_typevars, Cint, (Any,), t) != 0 # return an upper-bound on type `a` with type `b` removed # such that `return <: a` && `Union{return, b} == Union{a, b}` diff --git a/base/compiler/utilities.jl b/base/compiler/utilities.jl index 9eef4e86755e6..b85cf5d3195e4 100644 --- a/base/compiler/utilities.jl +++ b/base/compiler/utilities.jl @@ -124,11 +124,10 @@ function code_for_method(method::Method, @nospecialize(atypes), sparams::SimpleV if world < min_world(method) return nothing end - if isdefined(method, :generator) && !isleaftype(atypes) + if isdefined(method, :generator) && !isdispatchtuple(atypes) # don't call staged functions on abstract types. # (see issues #8504, #10230) # we can't guarantee that their type behavior is monotonic. - # XXX: this test is wrong if Types (such as DataType or Bottom) are present return nothing end if preexisting diff --git a/base/complex.jl b/base/complex.jl index 18eacb453c69c..dbeb47bf55f90 100644 --- a/base/complex.jl +++ b/base/complex.jl @@ -976,7 +976,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 !isconcrete(T) + if !isconcretetype(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 b7ac204a9bdb9..e121bb8f5c925 100644 --- a/base/deprecated.jl +++ b/base/deprecated.jl @@ -651,7 +651,8 @@ import .Iterators.enumerate @deprecate map(f, d::T) where {T<:AbstractDict} T( f(p) for p in pairs(d) ) # issue #17086 -@deprecate isleaftype isconcrete +@deprecate isleaftype isconcretetype +@deprecate isabstract isabstracttype # PR #22932 @deprecate +(a::Number, b::AbstractArray) broadcast(+, a, b) diff --git a/base/dict.jl b/base/dict.jl index d42dc8d1ff362..246feb236b6fd 100644 --- a/base/dict.jl +++ b/base/dict.jl @@ -34,7 +34,7 @@ function show(io::IO, t::AbstractDict{K,V}) where V where K if isempty(t) print(io, typeof(t), "()") else - if _isleaftype(K) && _isleaftype(V) + if isconcretetype(K) && isconcretetype(V) print(io, typeof(t).name) else print(io, typeof(t)) @@ -158,7 +158,7 @@ dict_with_eltype(DT_apply, ::Type) = DT_apply(Any, Any)() dict_with_eltype(DT_apply::F, kv, t) where {F} = grow_to!(dict_with_eltype(DT_apply, @default_eltype(typeof(kv))), kv) function dict_with_eltype(DT_apply::F, kv::Generator, t) where F T = @default_eltype(kv) - if T <: Union{Pair, Tuple{Any, Any}} && _isleaftype(T) + if T <: Union{Pair, Tuple{Any, Any}} && isconcretetype(T) return dict_with_eltype(DT_apply, kv, T) end return grow_to!(dict_with_eltype(DT_apply, T), kv) diff --git a/base/exports.jl b/base/exports.jl index 9894fdff2a37f..d95371dde6c5b 100644 --- a/base/exports.jl +++ b/base/exports.jl @@ -755,7 +755,11 @@ export fieldnames, fieldcount, # propertynames, - isconcrete, + isabstracttype, + isprimitivetype, + isstructtype, + isconcretetype, + isdispatchtuple, oftype, promote, promote_rule, diff --git a/base/float.jl b/base/float.jl index c0f5f6a3894ba..9c632ff53895b 100644 --- a/base/float.jl +++ b/base/float.jl @@ -863,7 +863,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 !isconcrete(T) + if !isconcretetype(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/interactiveutil.jl b/base/interactiveutil.jl index 506c98d9af98e..fc25bc0c7b019 100644 --- a/base/interactiveutil.jl +++ b/base/interactiveutil.jl @@ -573,9 +573,9 @@ 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) || - (isa(x,Union) && isa(t,DataType) && (type_close_enough(x.a, t) || type_close_enough(x.b, t))) + # TODO: handle UnionAll properly + return (isa(x, DataType) && isa(t, DataType) && x.name === t.name && x <: t) || + (isa(x, Union) && isa(t, DataType) && (type_close_enough(x.a, t) || type_close_enough(x.b, t))) end # `methodswith` -- shows a list of methods using the type given diff --git a/base/promotion.jl b/base/promotion.jl index cafe1084b41fe..c8cfc5ca2c5f6 100644 --- a/base/promotion.jl +++ b/base/promotion.jl @@ -355,14 +355,16 @@ end 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 + TT = Tuple{_default_type(S)} + T = _return_type(f, TT) + isdispatchtuple(Tuple{S}) && return isdispatchtuple(Tuple{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 + TT = Tuple{_default_type(R), _default_type(S)} + T = _return_type(f, TT) + isdispatchtuple(Tuple{R}) && isdispatchtuple(Tuple{S}) && return isdispatchtuple(Tuple{T}) ? T : Any return typejoin(R, S, T) end diff --git a/base/reflection.jl b/base/reflection.jl index 5bbb4a3582c49..d6dd2a60b7b46 100644 --- a/base/reflection.jl +++ b/base/reflection.jl @@ -222,6 +222,8 @@ Get a hash value for `x` based on object identity. `objectid(x)==objectid(y)` if """ objectid(@nospecialize(x)) = ccall(:jl_object_id, UInt, (Any,), x) +# concrete datatype predicates + struct DataTypeLayout nfields::UInt32 alignment::UInt32 @@ -231,22 +233,64 @@ struct DataTypeLayout # fielddesc_type : 2; end +""" + Base.datatype_alignment(dt::DataType) -> Int + +Memory allocation minimum alignment for instances of this type. +Can be called on any `isconcretetype`. +""" +function datatype_alignment(dt::DataType) + @_pure_meta + dt.layout == C_NULL && throw(UndefRefError()) + alignment = unsafe_load(convert(Ptr{DataTypeLayout}, dt.layout)).alignment + return Int(alignment & 0x1FF) +end + +""" + Base.datatype_haspadding(dt::DataType) -> Bool + +Return whether the fields of instances of this type are packed in memory, +with no intervening padding bytes. +Can be called on any `isconcretetype`. +""" +function datatype_haspadding(dt::DataType) + @_pure_meta + dt.layout == C_NULL && throw(UndefRefError()) + alignment = unsafe_load(convert(Ptr{DataTypeLayout}, dt.layout)).alignment + return (alignment >> 9) & 1 == 1 +end -# type predicates -datatype_alignment(dt::DataType) = dt.layout == C_NULL ? throw(UndefRefError()) : - Int(unsafe_load(convert(Ptr{DataTypeLayout}, dt.layout)).alignment & 0x1FF) +""" + Base.datatype_pointerfree(dt::DataType) -> Bool -datatype_haspadding(dt::DataType) = dt.layout == C_NULL ? throw(UndefRefError()) : - (unsafe_load(convert(Ptr{DataTypeLayout}, dt.layout)).alignment >> 9) & 1 == 1 +Return whether instances of this type can contain references to gc-managed memory. +Can be called on any `isconcretetype`. +""" +function datatype_pointerfree(dt::DataType) + @_pure_meta + dt.layout == C_NULL && throw(UndefRefError()) + alignment = unsafe_load(convert(Ptr{DataTypeLayout}, dt.layout)).alignment + return (alignment >> 10) & 0xFFFFF == 0 +end -datatype_pointerfree(dt::DataType) = dt.layout == C_NULL ? throw(UndefRefError()) : - (unsafe_load(convert(Ptr{DataTypeLayout}, dt.layout)).alignment >> 10) & 0xFFFFF == 0 +""" + Base.datatype_fielddesc_type(dt::DataType) -> Int -datatype_fielddesc_type(dt::DataType) = dt.layout == C_NULL ? throw(UndefRefError()) : - (unsafe_load(convert(Ptr{DataTypeLayout}, dt.layout)).alignment >> 30) & 3 +Return the size in bytes of each field-description entry in the layout array, +located at `(dt.layout + sizeof(DataTypeLayout))`. +Can be called on any `isconcretetype`. +See also [`Base.fieldoffset`](@ref). """ - isimmutable(v) +function datatype_fielddesc_type(dt::DataType) + @_pure_meta + dt.layout == C_NULL && throw(UndefRefError()) + alignment = unsafe_load(convert(Ptr{DataTypeLayout}, dt.layout)).alignment + return (alignment >> 30) & 3 +end + +""" + isimmutable(v) -> Bool Return `true` iff value `v` is immutable. See [Mutable Composite Types](@ref) for a discussion of immutability. Note that this function works on values, so if you give it @@ -263,15 +307,45 @@ false """ isimmutable(@nospecialize(x)) = (@_pure_meta; !typeof(x).mutable) -isstructtype(t::DataType) = (@_pure_meta; length(t.types) != 0 || (t.size==0 && !t.abstract)) -isstructtype(x) = (@_pure_meta; false) +""" + Base.isstructtype(T) -> Bool + +Determine whether type `T` was declared as a struct type +(i.e. using the `struct` or `mutable struct` keyword). +""" +function isstructtype(@nospecialize(t::Type)) + @_pure_meta + t = unwrap_unionall(t) + # TODO: what to do for `Union`? + isa(t, DataType) || return false + return length(t.types) != 0 || (t.size == 0 && !t.abstract) +end + +""" + Base.isprimitivetype(T) -> Bool + +Determine whether type `T` was declared as a primitive type +(i.e. using the `primitive` keyword). +""" +function isprimitivetype(@nospecialize(t::Type)) + @_pure_meta + t = unwrap_unionall(t) + # TODO: what to do for `Union`? + isa(t, DataType) || return false + return length(t.types) == 0 && t.size != 0 && !t.abstract +end """ isbits(T) -Return `true` if `T` is a "plain data" type, meaning it is immutable and contains no -references to other values. Typical examples are numeric types such as [`UInt8`](@ref), +Return `true` if type `T` is a "plain data" type, +meaning it is immutable and contains no references to other values, +only `primitive` types and other `isbits` types. +Typical examples are numeric types such as [`UInt8`](@ref), [`Float64`](@ref), and [`Complex{Float64}`](@ref). +This category of types is significant since they are valid as type parameters, +may not track [`isdefined`](@ref) / [`isassigned`](@ref) status, +and have a defined layout that is compatible with C. # Examples ```jldoctest @@ -282,71 +356,75 @@ julia> isbits(Complex) false ``` """ -isbits(t::DataType) = (@_pure_meta; !t.mutable & (t.layout != C_NULL) && datatype_pointerfree(t)) -isbits(t::Type) = (@_pure_meta; false) -isbits(x) = (@_pure_meta; isbits(typeof(x))) +isbits(@nospecialize(t::Type)) = (@_pure_meta; isa(t, DataType) && t.isbitstype) +isbits(@nospecialize x) = (@_pure_meta; typeof(x).isbitstype) -_isleaftype(@nospecialize(t)) = (@_pure_meta; isa(t, DataType) && t.isleaftype) +""" + isdispatchtuple(T) +Determine whether type `T` is a tuple "leaf type", +meaning it could appear as a type signature in dispatch +and has no subtypes (or supertypes) which could appear in a call. """ - isconcrete(T) +isdispatchtuple(@nospecialize(t)) = (@_pure_meta; isa(t, DataType) && t.isdispatchtuple) -Determine whether `T` is a concrete type, meaning it can have direct instances +""" + isconcretetype(T) + +Determine whether type `T` is a concrete type, meaning it could have direct instances (values `x` such that `typeof(x) === T`). # Examples ```jldoctest -julia> isconcrete(Complex) +julia> isconcretetype(Complex) false -julia> isconcrete(Complex{Float32}) +julia> isconcretetype(Complex{Float32}) true -julia> isconcrete(Vector{Complex}) +julia> isconcretetype(Vector{Complex}) true -julia> isconcrete(Vector{Complex{Float32}}) +julia> isconcretetype(Vector{Complex{Float32}}) true -julia> isconcrete(Union{}) +julia> isconcretetype(Union{}) false -julia> isconcrete(Union{Int,String}) +julia> isconcretetype(Union{Int,String}) false ``` """ -function isconcrete(@nospecialize(t)) - @_pure_meta - return (isa(t, DataType) && !t.abstract && - !t.hasfreetypevars && - (t.name !== Tuple.name || all(isconcrete, t.parameters))) -end +isconcretetype(@nospecialize(t)) = (@_pure_meta; isa(t, DataType) && t.isconcretetype) """ - Base.isabstract(T) + Base.isabstracttype(T) -Determine whether `T` was declared as an abstract type (i.e. using the -`abstract` keyword). +Determine whether type `T` was declared as an abstract type +(i.e. using the `abstract` keyword). # Examples ```jldoctest -julia> Base.isabstract(AbstractArray) +julia> Base.isabstracttype(AbstractArray) true -julia> Base.isabstract(Vector) +julia> Base.isabstracttype(Vector) false ``` """ -function isabstract(@nospecialize(t)) +function isabstracttype(@nospecialize(t)) @_pure_meta t = unwrap_unionall(t) - isa(t,DataType) && t.abstract + # TODO: what to do for `Union`? + return 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.: +Determine the upper bound of a type parameter in the underlying datatype. +This method should generally not be relied upon: +code instead should usually use static parameters in dispatch to extract these values. # Examples ```jldoctest @@ -363,7 +441,7 @@ Any """ function parameter_upper_bound(t::UnionAll, idx) @_pure_meta - rewrap_unionall(unwrap_unionall(t).parameters[idx], t) + return rewrap_unionall((unwrap_unionall(t)::DataType).parameters[idx], t) end """ @@ -541,7 +619,7 @@ function _subtypes(m::Module, x::Union{DataType,UnionAll}, end function _subtypes_in(mods::Array, x::Union{DataType,UnionAll}) - if !isabstract(x) + if !isabstracttype(x) # Fast path return Union{DataType,UnionAll}[] end @@ -821,8 +899,8 @@ function _dump_function_linfo(linfo::Core.MethodInstance, world::UInt, native::B (Ptr{Cvoid}, Bool, Bool), llvmf, strip_ir_metadata, dump_module) 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) + # TODO: use jl_is_cacheable_sig instead of isdispatchtuple + isdispatchtuple(linfo.specTypes) || (str = "; WARNING: This code may not match what actually runs.\n" * str) return str end @@ -853,7 +931,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) && !_isleaftype(types) + if isdefined(m, :generator) && !isdispatchtuple(types) error("cannot call @generated function `", m, "` ", "with abstract argument types: ", types) end diff --git a/base/refpointer.jl b/base/refpointer.jl index 1ac34a64e79e4..8f95c67fc6db7 100644 --- a/base/refpointer.jl +++ b/base/refpointer.jl @@ -59,18 +59,18 @@ Ref(x::Ref, i::Integer) = (i != 1 && error("Ref only has one element"); x) Ref(x::Ptr{T}, i::Integer) where {T} = x + (i - 1) * Core.sizeof(T) 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) && T.mutable - return convert(P, pointer_from_objref(b.x)) + if datatype_pointerfree(RefValue{T}) + p = pointer_from_objref(b) + elseif isconcretetype(T) && T.mutable + p = pointer_from_objref(b.x) else - # If the slot is not leaf type, it could be either isbits or not. - # If it is actually an isbits type and the type inference can infer that - # it can rebox the `b.x` if we simply call `pointer_from_objref(b.x)` on it. - # Instead, explicitly load the pointer from the `RefValue` so that the pointer - # is the same as the one rooted in the `RefValue` object. - return convert(P, pointerref(Ptr{Ptr{Cvoid}}(pointer_from_objref(b)), 1, 0)) + # If the slot is not leaf type, it could be either immutable or not. + # If it is actually an immutable, then we can't take it's pointer directly + # Instead, explicitly load the pointer from the `RefValue`, + # which also ensures this returns same pointer as the one rooted in the `RefValue` object. + p = pointerref(Ptr{Ptr{Cvoid}}(pointer_from_objref(b)), 1, Core.sizeof(Ptr{Cvoid})) end + return convert(P, p) end function unsafe_convert(P::Type{Ptr{Any}}, b::RefValue{Any}) return convert(P, pointer_from_objref(b)) @@ -90,11 +90,15 @@ convert(::Type{Ref{T}}, x::AbstractArray{T}) where {T} = RefArray(x, 1) Ref(x::AbstractArray, i::Integer) = RefArray(x, i) function unsafe_convert(P::Type{Ptr{T}}, b::RefArray{T}) where T - if isbits(T) - convert(P, pointer(b.x, b.i)) + if datatype_pointerfree(RefValue{T}) + p = pointer(b.x, b.i) + elseif isconcretetype(T) && T.mutable + p = pointer_from_objref(b.x[b.i]) else - convert(P, pointer_from_objref(b.x[b.i])) + # see comment on equivalent branch for RefValue + p = pointerref(Ptr{Ptr{Cvoid}}(pointer(b.x, b.i)), 1, Core.sizeof(Ptr{Cvoid})) end + return convert(P, p) end function unsafe_convert(P::Type{Ptr{Any}}, b::RefArray{Any}) return convert(P, pointer(b.x, b.i)) diff --git a/base/set.jl b/base/set.jl index e58647df0f632..d5948e075907d 100644 --- a/base/set.jl +++ b/base/set.jl @@ -23,7 +23,7 @@ for sets of arbitrary objects. Set(itr) = Set{eltype(itr)}(itr) function Set(g::Generator) T = @default_eltype(g) - (_isleaftype(T) || T === Union{}) || return grow_to!(Set{T}(), g) + (isconcretetype(T) || T === Union{}) || return grow_to!(Set{T}(), g) return Set{T}(g) end @@ -353,7 +353,7 @@ function unique(itr) return out end x, i = next(itr, i) - if !_isleaftype(T) && IteratorEltype(itr) == EltypeUnknown() + if !isconcretetype(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 fb70c11fabc23..1145272938206 100644 --- a/base/show.jl +++ b/base/show.jl @@ -296,8 +296,8 @@ function show_type_name(io::IO, tn::TypeName) if globname !== nothing 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)) + isbindingresolved(tn.module, globname) && isdefined(tn.module, globname) && + isconcretetype(tn.wrapper) && isa(getfield(tn.module, globname), tn.wrapper)) globfunc = true end end @@ -736,9 +736,9 @@ 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 && (!isdispatchtuple(Tuple{ty}) || ty == Core.Box) if ty isa Union && is_expected_union(ty) - emphasize(io, "::$ty", Base.warn_color()) + emphasize(io, "::$ty", Base.warn_color()) # more mild user notification else emphasize(io, "::$ty") end diff --git a/doc/src/base/base.md b/doc/src/base/base.md index 7a94e593a5d14..18609e948c9cc 100644 --- a/doc/src/base/base.md +++ b/doc/src/base/base.md @@ -119,6 +119,7 @@ Base.copy Base.deepcopy Base.getproperty Base.setproperty! +Base.propertynames Core.getfield Core.setfield! Core.isdefined @@ -132,28 +133,57 @@ Base.identity ## Properties of Types +### Type relations + ```@docs Base.supertype Core.:(<:) Base.:(>:) Base.subtypes +Base.typejoin +Base.typeintersect +Base.promote_type +Base.promote_rule +Base.isdispatchtuple +``` + +### Declared structure + +```@docs +Base.isimmutable +Base.isabstracttype +Base.isprimitivetype +Base.isstructtype +Base.datatype_module +Base.datatype_name +Base.fieldnames +Base.fieldname +``` + +### Memory layout + +```@docs +Base.sizeof(::Type) +Base.isconcretetype +Base.isbits +Core.fieldtype +Base.fieldcount +Base.fieldoffset +Base.datatype_alignment +Base.datatype_haspadding +Base.datatype_pointerfree +``` + +### Special values + +```@docs Base.typemin Base.typemax Base.realmin Base.realmax Base.maxintfloat -Base.sizeof(::Type) Base.eps(::Type{<:AbstractFloat}) Base.eps(::AbstractFloat) -Base.promote_type -Base.promote_rule -Base.fieldoffset -Core.fieldtype -Base.isimmutable -Base.isbits -Base.isconcrete -Base.typejoin -Base.typeintersect Base.instances ``` @@ -311,12 +341,6 @@ Base.@__MODULE__ Base.fullname Base.names Core.nfields -Base.fieldnames -Base.fieldname -Base.fieldcount -Base.propertynames -Base.datatype_module -Base.datatype_name Base.isconst Base.function_name Base.function_module(::Function) diff --git a/src/ccall.cpp b/src/ccall.cpp index 7290b42b453e6..a3b4ed9bb6d31 100644 --- a/src/ccall.cpp +++ b/src/ccall.cpp @@ -1139,8 +1139,8 @@ static jl_cgval_t mark_or_box_ccall_result(jl_codectx_t &ctx, Value *result, boo if (!static_rt) { assert(!isboxed && ctx.spvals_ptr && unionall && jl_is_datatype(rt)); Value *runtime_dt = runtime_apply_type(ctx, rt, unionall); - // TODO: is this leaf check actually necessary, or is it structurally guaranteed? - emit_leafcheck(ctx, runtime_dt, "ccall: return type must be a leaf DataType"); + // TODO: is this concrete check actually necessary, or is it structurally guaranteed? + emit_concretecheck(ctx, runtime_dt, "ccall: return type must be a concrete DataType"); #if JL_LLVM_VERSION >= 40000 const DataLayout &DL = jl_data_layout; #else @@ -1414,11 +1414,11 @@ static const std::string verify_ccall_sig(size_t nccallargs, jl_value_t *&rt, jl } if (!retboxed && static_rt) { - if (!jl_is_leaf_type(rt)) { + if (!jl_is_concrete_type(rt)) { if (jl_is_cpointer_type(rt)) return "ccall: return type Ptr should have an element type (not Ptr{_<:T})"; else if (rt != jl_bottom_type) - return "ccall: return type must be a leaf DataType"; + return "ccall: return type must be a concrete DataType"; } } @@ -1741,20 +1741,6 @@ static jl_cgval_t emit_ccall(jl_codectx_t &ctx, jl_value_t **args, size_t nargs) ctx.builder.SetInsertPoint(contBB); return ghostValue(jl_void_type); } - else if (is_libjulia_func(jl_is_leaf_type)) { - assert(!isVa && !llvmcall && nargt == 1 && !addressOf.at(0)); - const jl_cgval_t &arg = argv[0]; - jl_value_t *ty = arg.constant; - if (!ty && jl_is_type_type(arg.typ) && !jl_has_free_typevars(arg.typ)) - ty = jl_tparam0(arg.typ); - if (ty) { - int isleaf = jl_is_leaf_type(ty); - JL_GC_POP(); - return mark_or_box_ccall_result(ctx, - ConstantInt::get(T_int32, isleaf), - false, rt, unionall, static_rt); - } - } else if (is_libjulia_func(jl_function_ptr)) { assert(!isVa && !llvmcall && nargt == 3); assert(lrt == T_size); @@ -1883,7 +1869,7 @@ jl_cgval_t function_sig_t::emit_a_ccall( jl_cgval_t &arg = argv[ai]; // if we know the function sparams, try to fill those in now - // so that the julia_to_native type checks are more likely to be doable (e.g. leaf types) at compile-time + // so that the julia_to_native type checks are more likely to be doable (e.g. concrete types) at compile-time jl_value_t *jargty_in_env = jargty; if (ctx.spvals_ptr == NULL && !toboxed && unionall_env && jl_has_typevar_from_unionall(jargty, unionall_env) && jl_svec_len(ctx.linfo->sparam_vals) > 0) { @@ -1941,7 +1927,7 @@ jl_cgval_t function_sig_t::emit_a_ccall( bool sretboxed = false; if (sret) { assert(!retboxed && jl_is_datatype(rt) && "sret return type invalid"); - if (jl_isbits(rt)) { + if (jl_justbits(rt)) { result = emit_static_alloca(ctx, lrt); } else { diff --git a/src/cgutils.cpp b/src/cgutils.cpp index d5ef2ea5710bc..b222e3eaad6ae 100644 --- a/src/cgutils.cpp +++ b/src/cgutils.cpp @@ -485,14 +485,12 @@ JL_DLLEXPORT Type *julia_type_to_llvm(jl_value_t *jt, bool *isboxed) if (isboxed) *isboxed = false; if (jt == (jl_value_t*)jl_bottom_type) return T_void; - if (jl_is_leaf_type(jt)) { - if ((jl_is_primitivetype(jt) || jl_isbits(jt))) { - if (jl_datatype_nbits(jt) == 0) - return T_void; - Type *t = julia_struct_to_llvm(jt, NULL, isboxed); - assert(t != NULL); - return t; - } + if (jl_justbits(jt)) { + if (jl_datatype_nbits(jt) == 0) + return T_void; + Type *t = julia_struct_to_llvm(jt, NULL, isboxed); + assert(t != NULL); + return t; } if (isboxed) *isboxed = true; return T_pjlvalue; @@ -517,12 +515,12 @@ static Type *bitstype_to_llvm(jl_value_t *bt) return Type::getIntNTy(jl_LLVMContext, nb * 8); } -// compute whether all leaf subtypes of this type have the same layout +// compute whether all concrete subtypes of this type have the same layout // (which is conservatively approximated here by asking whether the types of any of the // fields depend on any of the parameters of the containing type) static bool julia_struct_has_layout(jl_datatype_t *dt, jl_unionall_t *ua) { - if (dt->layout || dt->struct_decl || jl_is_primitivetype(dt) || jl_isbits(dt)) + if (dt->layout || dt->struct_decl || jl_justbits((jl_value_t*)dt)) return true; if (ua) { size_t i, ntypes = jl_svec_len(dt->types); @@ -680,9 +678,9 @@ static bool is_tupletype_homogeneous(jl_svec_t *t, bool allow_va = false) size_t i, l = jl_svec_len(t); if (l > 0) { jl_value_t *t0 = jl_svecref(t, 0); - if (!jl_is_leaf_type(t0)) { + if (!jl_is_concrete_type(t0)) { if (allow_va && jl_is_vararg_type(t0) && - jl_is_leaf_type(jl_unwrap_vararg(t0))) + jl_is_concrete_type(jl_unwrap_vararg(t0))) return true; return false; } @@ -692,7 +690,7 @@ static bool is_tupletype_homogeneous(jl_svec_t *t, bool allow_va = false) return false; continue; } - if (t0 != jl_svecref(t,i)) + if (t0 != jl_svecref(t, i)) return false; } } @@ -717,7 +715,7 @@ static bool for_each_uniontype_small( allunbox &= for_each_uniontype_small(f, ((jl_uniontype_t*)ty)->b, counter); return allunbox; } - else if (isbits_spec(ty)) { + else if (jl_justbits(ty)) { f(++counter, (jl_datatype_t*)ty); return true; } @@ -789,7 +787,14 @@ static jl_cgval_t emit_typeof(jl_codectx_t &ctx, const jl_cgval_t &p) // given p, compute its type if (p.constant) return mark_julia_const(jl_typeof(p.constant)); - if (p.isboxed && !jl_is_leaf_type(p.typ)) { + if (p.isboxed && !jl_is_concrete_type(p.typ)) { + if (jl_is_type_type(p.typ)) { + jl_value_t *tp = jl_tparam0(p.typ); + if (!jl_is_type(tp) || jl_is_concrete_type(tp)) { + // convert 1::Type{1} ==> typeof(1) ==> Int + return mark_julia_const(jl_typeof(tp)); + } + } return mark_julia_type(ctx, emit_typeof(ctx, p.V), true, jl_datatype_type); } if (p.TIndex) { @@ -846,13 +851,7 @@ static jl_cgval_t emit_typeof(jl_codectx_t &ctx, const jl_cgval_t &p) } return mark_julia_type(ctx, res, true, jl_datatype_type); } - jl_value_t *aty = p.typ; - if (jl_is_type_type(aty)) { - // convert Int::Type{Int} ==> typeof(Int) ==> DataType - // but convert 1::Type{1} ==> typeof(1) ==> Int - aty = (jl_value_t*)jl_typeof(jl_tparam0(aty)); - } - return mark_julia_const(aty); + return mark_julia_const(p.typ); } static Value *emit_typeof_boxed(jl_codectx_t &ctx, const jl_cgval_t &p) @@ -918,7 +917,7 @@ static Value *emit_sizeof(jl_codectx_t &ctx, const jl_cgval_t &p) #endif return size; } - else if (jl_is_leaf_type(p.typ)) { + else if (jl_is_concrete_type(p.typ)) { return ConstantInt::get(T_int32, jl_datatype_size(p.typ)); } else { @@ -938,6 +937,7 @@ static Value *emit_datatype_mutabl(jl_codectx_t &ctx, Value *dt) return ctx.builder.CreateTrunc(mutabl, T_int1); } +/* this is valid code, it's simply unused static Value *emit_datatype_abstract(jl_codectx_t &ctx, Value *dt) { Value *Ptr = emit_bitcast(ctx, decay_derived(dt), T_pint8); @@ -947,16 +947,14 @@ static Value *emit_datatype_abstract(jl_codectx_t &ctx, Value *dt) ctx.builder.CreateLoad(T_int8, ctx.builder.CreateGEP(T_int8, Ptr, Idx))); return ctx.builder.CreateTrunc(abstract, T_int1); } +*/ -static Value *emit_datatype_isbitstype(jl_codectx_t &ctx, Value *dt) +static Value *emit_datatype_isprimitivetype(jl_codectx_t &ctx, Value *dt) { - Value *immut = ctx.builder.CreateXor(emit_datatype_mutabl(ctx, dt), ConstantInt::get(T_int1, -1)); + Value *immut = ctx.builder.CreateNot(emit_datatype_mutabl(ctx, dt)); Value *nofields = ctx.builder.CreateICmpEQ(emit_datatype_nfields(ctx, dt), ConstantInt::get(T_size, 0)); - Value *isbitstype = ctx.builder.CreateAnd(immut, ctx.builder.CreateAnd(nofields, - ctx.builder.CreateXor(ctx.builder.CreateAnd(emit_datatype_abstract(ctx, dt), - ctx.builder.CreateICmpSGT(emit_datatype_size(ctx, dt), ConstantInt::get(T_int32, 0))), - ConstantInt::get(T_int1, -1)))); - return isbitstype; + Value *sized = ctx.builder.CreateICmpSGT(emit_datatype_size(ctx, dt), ConstantInt::get(T_int32, 0)); + return ctx.builder.CreateAnd(immut, ctx.builder.CreateAnd(nofields, sized)); } static Value *emit_datatype_name(jl_codectx_t &ctx, Value *dt) @@ -1075,8 +1073,8 @@ static std::pair emit_isa(jl_codectx_t &ctx, const jl_cgval_t &x, ctx.builder.CreateCall(prepare_call(jlisa_func), { vx, vtyp }), ConstantInt::get(T_int32, 0)), false); } - // tests for isa leaftype can be handled with pointer comparisons - if (jl_is_leaf_type(type)) { + // tests for isa concretetype can be handled with pointer comparisons + if (jl_is_concrete_type(type)) { if (x.TIndex) { unsigned tindex = get_box_tindex((jl_datatype_t*)type, x.typ); if (tindex > 0) { @@ -1101,7 +1099,7 @@ static std::pair emit_isa(jl_codectx_t &ctx, const jl_cgval_t &x, istype->addIncoming(istype_boxed, isaBB); return std::make_pair(istype, false); } else { - // handle the case where we know that `x` is unboxed (but of unknown type), but that leaf type `type` cannot be unboxed + // handle the case where we know that `x` is unboxed (but of unknown type), but that concrete type `type` cannot be unboxed return std::make_pair(ConstantInt::get(T_int1, 0), false); } } @@ -1135,15 +1133,15 @@ static void emit_typecheck(jl_codectx_t &ctx, const jl_cgval_t &x, jl_value_t *t } } -static void emit_leafcheck(jl_codectx_t &ctx, Value *typ, const std::string &msg) +static void emit_concretecheck(jl_codectx_t &ctx, Value *typ, const std::string &msg) { assert(typ->getType() == T_prjlvalue); emit_typecheck(ctx, mark_julia_type(ctx, typ, true, jl_any_type), (jl_value_t*)jl_datatype_type, msg); - Value *isleaf; - isleaf = ctx.builder.CreateConstInBoundsGEP1_32(T_int8, emit_bitcast(ctx, decay_derived(typ), T_pint8), offsetof(jl_datatype_t, isleaftype)); - isleaf = ctx.builder.CreateLoad(isleaf, tbaa_const); - isleaf = ctx.builder.CreateTrunc(isleaf, T_int1); - error_unless(ctx, isleaf, msg); + Value *isconcrete; + isconcrete = ctx.builder.CreateConstInBoundsGEP1_32(T_int8, emit_bitcast(ctx, decay_derived(typ), T_pint8), offsetof(jl_datatype_t, isconcretetype)); + isconcrete = ctx.builder.CreateLoad(isconcrete, tbaa_const); + isconcrete = ctx.builder.CreateTrunc(isconcrete, T_int1); + error_unless(ctx, isconcrete, msg); } #define CHECK_BOUNDS 1 @@ -1441,7 +1439,7 @@ static bool emit_getfield_unknownidx(jl_codectx_t &ctx, } } else if (is_tupletype_homogeneous(stt->types)) { - assert(jl_isbits(stt)); + assert(jl_justbits((jl_value_t*)stt)); if (nfields == 0) { idx = emit_bounds_check( ctx, ghostValue(stt), (jl_value_t*)stt, @@ -1592,7 +1590,7 @@ static Value *emit_n_varargs(jl_codectx_t &ctx) static bool arraytype_constshape(jl_value_t *ty) { - return (jl_is_array_type(ty) && jl_is_leaf_type(ty) && + return (jl_is_array_type(ty) && jl_is_concrete_type(ty) && jl_is_long(jl_tparam1(ty)) && jl_unbox_long(jl_tparam1(ty)) != 1); } @@ -2178,7 +2176,7 @@ static Value *boxed(jl_codectx_t &ctx, const jl_cgval_t &vinfo) } else { assert(vinfo.V && "Missing data for unboxed value."); - assert(jl_isbits(jt) && jl_is_leaf_type(jt) && "This type shouldn't have been unboxed."); + assert(jl_justbits(jt) && "This type shouldn't have been unboxed."); Type *t = julia_type_to_llvm(jt); assert(!type_is_ghost(t)); // ghost values should have been handled by vinfo.constant above! box = _boxed_special(ctx, vinfo, t); @@ -2193,16 +2191,16 @@ static Value *boxed(jl_codectx_t &ctx, const jl_cgval_t &vinfo) return box; } -// copy src to dest, if src is isbits. if skip is true, the value of dest is undefined +// copy src to dest, if src is justbits. if skip is true, the value of dest is undefined static void emit_unionmove(jl_codectx_t &ctx, Value *dest, const jl_cgval_t &src, Value *skip, bool isVolatile, MDNode *tbaa) { if (AllocaInst *ai = dyn_cast(dest)) ctx.builder.CreateStore(UndefValue::get(ai->getAllocatedType()), ai); - if (jl_is_leaf_type(src.typ) || src.constant) { + if (jl_is_concrete_type(src.typ) || src.constant) { jl_value_t *typ = src.constant ? jl_typeof(src.constant) : src.typ; Type *store_ty = julia_type_to_llvm(typ); - assert(skip || jl_isbits(typ)); - if (jl_isbits(typ)) { + assert(skip || jl_justbits(typ)); + if (jl_justbits(typ)) { if (!src.ispointer() || src.constant) { emit_unbox(ctx, store_ty, src, typ, dest, isVolatile); } @@ -2356,11 +2354,11 @@ static void emit_setfield(jl_codectx_t &ctx, static jl_cgval_t emit_new_struct(jl_codectx_t &ctx, jl_value_t *ty, size_t nargs, const jl_cgval_t *argv) { assert(jl_is_datatype(ty)); - assert(jl_is_leaf_type(ty)); + assert(jl_is_concrete_type(ty)); jl_datatype_t *sty = (jl_datatype_t*)ty; size_t nf = jl_datatype_nfields(sty); if (nf > 0) { - if (jl_isbits(sty)) { + if (jl_justbits(ty)) { Type *lt = julia_type_to_llvm(ty); unsigned na = nargs < nf ? nargs : nf; diff --git a/src/codegen.cpp b/src/codegen.cpp index 68d33ee4bf4e2..a4295a9cf7a87 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -363,12 +363,6 @@ static void add_return_attr(T *f, Attribute::AttrKind Kind) #endif } -static bool isbits_spec(jl_value_t *jt, bool allow_singleton = true) -{ - return jl_isbits(jt) && jl_is_leaf_type(jt) && - (allow_singleton || (jl_datatype_size(jt) > 0) || (jl_datatype_nfields(jt) > 0)); -} - static MDNode *best_tbaa(jl_value_t *jt) { jt = jl_unwrap_unionall(jt); if (!jl_is_datatype(jt)) @@ -380,6 +374,13 @@ static MDNode *best_tbaa(jl_value_t *jt) { return jl_is_mutable(jt) ? tbaa_mutab : tbaa_immut; } +// tracks whether codegen is currently able to simply stack-allocate this type +// note that this is guaranteed to include jl_isbits +static bool jl_justbits(jl_value_t* t) +{ + return jl_is_immutable_datatype(t) && ((jl_datatype_t*)t)->layout && ((jl_datatype_t*)t)->layout->npointers == 0; +} + // metadata tracking for a llvm Value* during codegen struct jl_cgval_t { Value *V; // may be of type T* or T, or set to NULL if ghost (or if the value has not been initialized yet, for a variable definition) @@ -401,11 +402,6 @@ struct jl_cgval_t { // whether this value is compatible with `data_pointer` return tbaa != nullptr; } - //bool isvalue() const - //{ - // // whether this value is compatible with loading into registers (`emit_unbox` without an explicit type) - // return isbits_spec(typ) && (!ispointer() || constant); - //} jl_cgval_t(Value *V, Value *gcroot, bool isboxed, jl_value_t *typ, Value *tindex) : // general constructor (with pointer type auto-detect) V(V), // V is allowed to be NULL in a jl_varinfo_t context, but not during codegen contexts Vboxed(isboxed ? V : nullptr), @@ -449,7 +445,7 @@ struct jl_cgval_t { // this constructor expects we had a badly or equivalently typed version // make sure we aren't discarding the actual type information if (v.TIndex) { - assert((TIndex == NULL) == jl_is_leaf_type(typ)); + assert((TIndex == NULL) == jl_is_concrete_type(typ)); } else { assert(isboxed || v.typ == typ || tindex); @@ -662,7 +658,7 @@ static inline jl_cgval_t mark_julia_type(jl_codectx_t &ctx, Value *v, bool isbox } if (jl_is_type_type(typ)) { jl_value_t *tp0 = jl_tparam0(typ); - if (jl_is_leaf_type(tp0) || tp0 == jl_bottom_type) { + if (jl_is_concrete_type(tp0) || tp0 == jl_bottom_type) { // replace T::Type{T} with T return ghostValue(typ); } @@ -697,8 +693,8 @@ static inline jl_cgval_t update_julia_type(jl_codectx_t &ctx, const jl_cgval_t & { if (v.typ == typ || v.typ == jl_bottom_type || v.constant || typ == (jl_value_t*)jl_any_type || jl_egal(v.typ, typ)) return v; // fast-path - if (jl_is_leaf_type(v.typ) && !jl_is_kind(v.typ)) { - if (jl_is_leaf_type(typ) && !jl_is_kind(typ) && !((jl_datatype_t*)typ)->abstract && !((jl_datatype_t*)v.typ)->abstract) { + if (jl_is_concrete_type(v.typ) && !jl_is_kind(v.typ)) { + if (jl_is_concrete_type(typ) && !jl_is_kind(typ)) { // type mismatch: changing from one leaftype to another CreateTrap(ctx.builder); return jl_cgval_t(); @@ -706,9 +702,7 @@ static inline jl_cgval_t update_julia_type(jl_codectx_t &ctx, const jl_cgval_t & return v; // doesn't improve type info } if (v.TIndex) { - if (!jl_is_leaf_type(typ)) - return v; // not worth trying to improve type info - if (!isbits_spec(typ)) { + if (jl_is_datatype(typ) && !jl_justbits(typ)) { // discovered that this union-split type must actually be isboxed if (v.Vboxed) { return jl_cgval_t(v.Vboxed, nullptr, true, typ, NULL); @@ -719,6 +713,8 @@ static inline jl_cgval_t update_julia_type(jl_codectx_t &ctx, const jl_cgval_t & return jl_cgval_t(); } } + if (!jl_is_concrete_type(typ)) + return v; // not generally worth trying to improve type info } Type *T = julia_type_to_llvm(typ); if (type_is_ghost(T)) @@ -964,8 +960,8 @@ static jl_cgval_t convert_julia_type(jl_codectx_t &ctx, const jl_cgval_t &v, jl_ if (type_is_ghost(T)) return ghostValue(typ); Value *new_tindex = NULL; - if (jl_is_leaf_type(typ)) { - if (v.TIndex && !isbits_spec(typ)) { + if (jl_is_concrete_type(typ)) { + if (v.TIndex && !jl_justbits(typ)) { // discovered that this union-split type must actually be isboxed if (v.Vboxed) { return jl_cgval_t(v.Vboxed, nullptr, true, typ, NULL); @@ -976,8 +972,8 @@ static jl_cgval_t convert_julia_type(jl_codectx_t &ctx, const jl_cgval_t &v, jl_ return jl_cgval_t(); } } - if (jl_is_leaf_type(v.typ) && !jl_is_kind(v.typ) && !((jl_datatype_t*)v.typ)->abstract) { - if (jl_is_leaf_type(typ) && !jl_is_kind(typ) && !((jl_datatype_t*)typ)->abstract) { + if (jl_is_concrete_type(v.typ) && !jl_is_kind(v.typ)) { + if (jl_is_concrete_type(typ) && !jl_is_kind(typ)) { // type mismatch: changing from one leaftype to another CreateTrap(ctx.builder); return jl_cgval_t(); @@ -991,7 +987,7 @@ static jl_cgval_t convert_julia_type(jl_codectx_t &ctx, const jl_cgval_t &v, jl_ } else if (!v.isboxed && jl_is_uniontype(typ)) { // previous value was unboxed (leaftype), statically compute union tindex - assert(jl_is_leaf_type(v.typ)); + assert(jl_is_concrete_type(v.typ)); unsigned new_idx = get_box_tindex((jl_datatype_t*)v.typ, typ); if (new_idx) { new_tindex = ConstantInt::get(T_int8, new_idx); @@ -1993,7 +1989,7 @@ static void simple_use_analysis(jl_codectx_t &ctx, jl_value_t *expr) static void jl_add_method_root(jl_codectx_t &ctx, jl_value_t *val) { - if (jl_is_leaf_type(val) || jl_is_bool(val) || jl_is_symbol(val) || + if (jl_is_concrete_type(val) || jl_is_bool(val) || jl_is_symbol(val) || val == (jl_value_t*)jl_any_type || val == (jl_value_t*)jl_bottom_type) return; JL_GC_PUSH1(&val); @@ -2037,7 +2033,7 @@ static jl_cgval_t emit_getfield(jl_codectx_t &ctx, const jl_cgval_t &strct, jl_s return emit_globalref(ctx, (jl_module_t*)strct.constant, name); jl_datatype_t *sty = (jl_datatype_t*)strct.typ; - if (jl_is_type_type((jl_value_t*)sty) && jl_is_leaf_type(jl_tparam0(sty))) + if (jl_is_type_type((jl_value_t*)sty) && jl_is_concrete_type(jl_tparam0(sty))) sty = (jl_datatype_t*)jl_typeof(jl_tparam0(sty)); sty = (jl_datatype_t*)jl_unwrap_unionall((jl_value_t*)sty); if (jl_is_structtype(sty) && sty != jl_module_type && sty->layout) { @@ -2094,8 +2090,9 @@ static Value *emit_bitsunion_compare(jl_codectx_t &ctx, const jl_cgval_t &arg1, static Value *emit_bits_compare(jl_codectx_t &ctx, const jl_cgval_t &arg1, const jl_cgval_t &arg2) { - assert(jl_is_datatype(arg1.typ) && arg1.typ == arg2.typ); - Type *at = julia_type_to_llvm(arg1.typ); + bool isboxed; + Type *at = julia_type_to_llvm(arg1.typ, &isboxed); + assert(jl_is_datatype(arg1.typ) && arg1.typ == arg2.typ && !isboxed); if (type_is_ghost(at)) return ConstantInt::get(T_int1, 1); @@ -2171,7 +2168,7 @@ static Value *emit_bits_compare(jl_codectx_t &ctx, const jl_cgval_t &arg1, const mark_julia_slot(fld2, fldty, tindex2, arg2.tbaa)); } else { - assert(jl_is_leaf_type(fldty)); + assert(jl_is_concrete_type(fldty)); subAns = emit_bits_compare(ctx, mark_julia_slot(fld1, fldty, NULL, arg1.tbaa), mark_julia_slot(fld2, fldty, NULL, arg2.tbaa)); @@ -2188,16 +2185,12 @@ static Value *emit_bits_compare(jl_codectx_t &ctx, const jl_cgval_t &arg1, const // emit code for is (===). static Value *emit_f_is(jl_codectx_t &ctx, const jl_cgval_t &arg1, const jl_cgval_t &arg2) { - jl_value_t *rt1 = arg1.typ, *rt2 = arg2.typ; - bool istypes = false; - if (jl_subtype(rt1, (jl_value_t*)jl_type_type) || - jl_subtype(rt2, (jl_value_t*)jl_type_type)) { - // need to use typeseq for datatypes - istypes = true; - } - if (!istypes && jl_is_leaf_type(rt1) && jl_is_leaf_type(rt2) && rt1 != rt2) + jl_value_t *rt1 = arg1.typ; + jl_value_t *rt2 = arg2.typ; + if (jl_is_concrete_type(rt1) && jl_is_concrete_type(rt2) && !jl_is_kind(rt1) && !jl_is_kind(rt2) && rt1 != rt2) { // disjoint concrete leaf types are never equal (quick test) return ConstantInt::get(T_int1, 0); + } if (arg1.isghost || arg2.isghost) { // comparing to a singleton object @@ -2217,9 +2210,10 @@ static Value *emit_f_is(jl_codectx_t &ctx, const jl_cgval_t &arg1, const jl_cgva if (jl_type_intersection(rt1, rt2) == (jl_value_t*)jl_bottom_type) // types are disjoint (exhaustive test) return ConstantInt::get(T_int1, 0); - bool isbits = jl_isbits(rt1) || jl_isbits(rt2); - if (isbits) { // whether this type is unique'd by value - jl_value_t *typ = jl_isbits(rt1) ? rt1 : rt2; + bool justbits1 = jl_justbits(rt1); + bool justbits2 = jl_justbits(rt2); + if (justbits1 || justbits2) { // whether this type is unique'd by value + jl_value_t *typ = justbits1 ? rt1 : rt2; if (rt1 == rt2) return emit_bits_compare(ctx, arg1, arg2); Value *same_type = (typ == rt2) ? emit_isa(ctx, arg1, typ, NULL).first : emit_isa(ctx, arg2, typ, NULL).first; @@ -2247,11 +2241,16 @@ static Value *emit_f_is(jl_codectx_t &ctx, const jl_cgval_t &arg1, const jl_cgva ptr_comparable = 1; if (jl_is_mutable_datatype(rt1) || jl_is_mutable_datatype(rt2)) // excludes abstract types ptr_comparable = 1; - if (istypes) // need to use typeseq for datatypes + if (jl_subtype(rt1, (jl_value_t*)jl_type_type) || + jl_subtype(rt2, (jl_value_t*)jl_type_type)) { + // need to use typeseq for most types ptr_comparable = 0; - if ((jl_is_type_type(rt1) && jl_is_leaf_type(jl_tparam0(rt1))) || - (jl_is_type_type(rt2) && jl_is_leaf_type(jl_tparam0(rt2)))) // can compare leaf types by pointer - ptr_comparable = 1; + if ((jl_is_type_type(rt1) && jl_is_concrete_type(jl_tparam0(rt1))) || + (jl_is_type_type(rt2) && jl_is_concrete_type(jl_tparam0(rt2)))) { + // but can compare some types by pointer + ptr_comparable = 1; + } + } if ((rt1 == (jl_value_t*)jl_string_type && rt2 == (jl_value_t*)jl_string_type) || (rt1 == (jl_value_t*)jl_simplevector_type && rt2 == (jl_value_t*)jl_simplevector_type)) ptr_comparable = 0; // technically mutable, but compared by contents @@ -2360,7 +2359,7 @@ static bool emit_builtin_call(jl_codectx_t &ctx, jl_cgval_t *ret, jl_value_t *f, *ret = ghostValue(jl_emptytuple_type); return true; } - if (jl_is_tuple_type(rt) && jl_is_leaf_type(rt) && nargs == jl_datatype_nfields(rt)) { + if (jl_is_tuple_type(rt) && jl_is_concrete_type(rt) && nargs == jl_datatype_nfields(rt)) { *ret = emit_new_struct(ctx, rt, nargs, &argv[1]); return true; } @@ -2699,12 +2698,12 @@ static bool emit_builtin_call(jl_codectx_t &ctx, jl_cgval_t *ret, jl_value_t *f, } if (jl_is_type_type(obj.typ)) { jl_value_t *tp0 = jl_tparam0(obj.typ); - if (jl_is_leaf_type(tp0)) { + if (jl_is_concrete_type(tp0)) { *ret = mark_julia_type(ctx, ConstantInt::get(T_size, jl_datatype_nfields(tp0)), false, jl_long_type); return true; } } - else if (jl_is_leaf_type(obj.typ) || obj.constant) { + else if (jl_is_concrete_type(obj.typ) || obj.constant) { Value *sz; if (obj.constant) { if (jl_typeof(obj.constant) == (jl_value_t*)jl_datatype_type) @@ -2727,7 +2726,7 @@ static bool emit_builtin_call(jl_codectx_t &ctx, jl_cgval_t *ret, jl_value_t *f, else if (f == jl_builtin_fieldtype && (nargs == 2 || nargs == 3)) { const jl_cgval_t &typ = argv[1]; const jl_cgval_t &fld = argv[2]; - if ((jl_is_type_type(typ.typ) && jl_is_leaf_type(jl_tparam0(typ.typ))) || + if ((jl_is_type_type(typ.typ) && jl_is_concrete_type(jl_tparam0(typ.typ))) || (typ.constant && jl_is_datatype(typ.constant)) || typ.typ == (jl_value_t*)jl_datatype_type) { if (fld.typ == (jl_value_t*)jl_long_type) { @@ -2791,7 +2790,7 @@ static bool emit_builtin_call(jl_codectx_t &ctx, jl_cgval_t *ret, jl_value_t *f, // exclude DataType, since each DataType has its own size, not sizeof(DataType). // this is issue #8798 sty != jl_datatype_type) { - if (jl_is_leaf_type((jl_value_t*)sty) || + if (jl_is_concrete_type((jl_value_t*)sty) || (jl_field_names(sty) == jl_emptysvec && jl_datatype_size(sty) > 0)) { *ret = mark_julia_type(ctx, ConstantInt::get(T_size, jl_datatype_size(sty)), false, jl_long_type); return true; @@ -2815,17 +2814,19 @@ static bool emit_builtin_call(jl_codectx_t &ctx, jl_cgval_t *ret, jl_value_t *f, const jl_cgval_t &obj = argv[1]; const jl_cgval_t &fld = argv[2]; jl_datatype_t *stt = (jl_datatype_t*)obj.typ; - if (!jl_is_leaf_type((jl_value_t*)stt) || jl_is_array_type(stt) || - stt == jl_module_type) { // TODO: use ->layout here instead of leaf_type - return false; - } if (jl_is_type_type((jl_value_t*)stt)) { - // the representation type of Type{T} is either DataType, or unknown - if (jl_is_leaf_type(jl_tparam0(stt))) - stt = jl_datatype_type; + // the representation type of Type{T} is either typeof(T), or unknown + // TODO: could use `issingletontype` predicate here, providing better type knowledge + // than only handling DataType + if (jl_is_concrete_type(jl_tparam0(stt))) + stt = (jl_datatype_t*)jl_typeof(jl_tparam0(stt)); else return false; } + if (!jl_is_concrete_type((jl_value_t*)stt) || jl_is_array_type(stt) || + stt == jl_module_type) { // TODO: use ->layout here instead of concrete_type + return false; + } assert(jl_is_datatype(stt)); ssize_t fieldidx = -1; @@ -3112,8 +3113,8 @@ static Value *global_binding_pointer(jl_codectx_t &ctx, jl_module_t *m, jl_sym_t assert(b != NULL); if (b->owner != m) { char *msg; - asprintf(&msg, "cannot assign variable %s.%s from module %s", - jl_symbol_name(b->owner->name), jl_symbol_name(s), jl_symbol_name(m->name)); + (void)asprintf(&msg, "cannot assign variable %s.%s from module %s", + jl_symbol_name(b->owner->name), jl_symbol_name(s), jl_symbol_name(m->name)); emit_error(ctx, msg); free(msg); } @@ -3448,7 +3449,7 @@ static void emit_vi_assignment_unboxed(jl_codectx_t &ctx, jl_varinfo_t &vi, Valu if (tbaa != tbaa_stack) tbaa = NULL; if (vi.pTIndex == NULL) { - assert(jl_is_leaf_type(vi.value.typ)); + assert(jl_is_concrete_type(vi.value.typ)); // Sometimes we can get into situations where the LHS and RHS // are the same slot. We're not allowed to memcpy in that case // under penalty of undefined behavior. This check should catch @@ -3882,7 +3883,7 @@ static jl_cgval_t emit_expr(jl_codectx_t &ctx, jl_value_t *expr) jl_value_t *ty = argv[0].typ; if (jl_is_type_type(ty) && jl_is_datatype(jl_tparam0(ty)) && - jl_is_leaf_type(jl_tparam0(ty))) { + jl_is_concrete_type(jl_tparam0(ty))) { assert(nargs <= jl_datatype_nfields(jl_tparam0(ty)) + 1); return emit_new_struct(ctx, jl_tparam0(ty), nargs - 1, &argv[1]); } @@ -4220,7 +4221,7 @@ static Function *gen_cfun_wrapper(jl_function_t *ff, jl_value_t *jlrettype, jl_t ctx.builder.CreateLoad(emit_bitcast(ctx, val, T_pprjlvalue)), true, jargty); } - else if (!jl_isbits(jargty)) { + else if (!jl_justbits(jargty)) { // must be a jl_value_t* (because it's mutable or contains gc roots) inputarg = mark_julia_type(ctx, maybe_decay_untracked(emit_bitcast(ctx, val, T_prjlvalue)), true, jargty); } @@ -4493,7 +4494,7 @@ static Function *jl_cfunction_object(jl_function_t *ff, jl_value_t *declrt, jl_t jl_error("cfunction: return type Ref should have an element type, not Ref{T}"); if (declrt == (jl_value_t*)jl_any_type) jl_error("cfunction: return type Ref{Any} is invalid. Use Any or Ptr{Any} instead."); - if (!jl_is_leaf_type(declrt)) + if (!jl_is_concrete_type(declrt)) jl_svecset(cfunc_sig, nargs + 1, declrt); // Ref{Abstract} is the same calling convention as Abstract crt = (jl_value_t*)jl_any_type; } @@ -4510,7 +4511,7 @@ static Function *jl_cfunction_object(jl_function_t *ff, jl_value_t *declrt, jl_t ati = jl_tparam0(ati); if (jl_is_typevar(ati)) jl_error("cfunction: argument type Ref should have an element type, not Ref{T}"); - if (ati != (jl_value_t*)jl_any_type && !jl_is_leaf_type(ati)) + if (ati != (jl_value_t*)jl_any_type && !jl_is_concrete_type(ati)) jl_svecset(cfunc_sig, i + 1, ati); // Ref{Abstract} is the same calling convention as Abstract } if (jl_is_pointer(ati) && jl_is_typevar(jl_tparam0(ati))) @@ -4683,14 +4684,14 @@ static bool uses_specsig(jl_value_t *sig, size_t nreq, jl_value_t *rettype, bool // For now we can only handle va tuples that will end up being // leaf types for (size_t i = nreq; i < jl_nparams(sig); i++) { - if (!isbits_spec(jl_tparam(sig, i))) + if (!jl_justbits(jl_tparam(sig, i))) return false; } } // not invalid, consider if specialized signature is worthwhile if (prefer_specsig) return true; - if (isbits_spec(rettype, false)) + if (jl_justbits(rettype) && !jl_is_datatype_singleton((jl_datatype_t*)rettype)) return true; if (jl_is_uniontype(rettype)) { bool allunbox; @@ -4700,7 +4701,8 @@ static bool uses_specsig(jl_value_t *sig, size_t nreq, jl_value_t *rettype, bool return true; // some elements of the union could be returned unboxed avoiding allocation } for (size_t i = 0; i < jl_nparams(sig); i++) { - if (isbits_spec(jl_tparam(sig, i), false)) { + jl_value_t *sigt = jl_tparam(sig, i); + if (jl_justbits(sigt) && !jl_is_datatype_singleton((jl_datatype_t*)sigt)) { return true; } } @@ -5204,7 +5206,7 @@ static std::unique_ptr emit_function( } else if (varinfo.isArgument && !(specsig && i == (size_t)ctx.vaSlot)) { // if we can unbox it, just use the input pointer - if (i != (size_t)ctx.vaSlot && isbits_spec(jt, false)) + if (i != (size_t)ctx.vaSlot && jl_justbits(jt)) continue; } else if (jl_is_uniontype(jt)) { @@ -5232,7 +5234,7 @@ static std::unique_ptr emit_function( if (allunbox) continue; } - else if (isbits_spec(jt, false)) { + else if (jl_justbits(jt)) { bool isboxed; Type *vtype = julia_type_to_llvm(jt, &isboxed); assert(!isboxed); @@ -5395,17 +5397,19 @@ static std::unique_ptr emit_function( assert(vi.boxroot == NULL); } else if (specsig) { - size_t nvargs = jl_nparams(lam->specTypes)-nreq; - jl_cgval_t *vargs = (jl_cgval_t*)alloca(sizeof(jl_cgval_t)*nvargs); + size_t nvargs = jl_nparams(lam->specTypes) - nreq; + jl_cgval_t *vargs = (jl_cgval_t*)alloca(sizeof(jl_cgval_t) * nvargs); for (size_t i = nreq; i < jl_nparams(lam->specTypes); ++i) { jl_value_t *argType = jl_nth_slot_type(lam->specTypes, i); bool isboxed; Type *llvmArgType = julia_type_to_llvm(argType, &isboxed); - vargs[i-nreq] = get_specsig_arg(argType, llvmArgType, isboxed); + vargs[i - nreq] = get_specsig_arg(argType, llvmArgType, isboxed); } jl_cgval_t tuple = emit_new_struct(ctx, vi.value.typ, nvargs, vargs); + // FIXME: this may assert since the type of vi might not be isbits here emit_vi_assignment_unboxed(ctx, vi, NULL, tuple); - } else { + } + else { // restarg = jl_f_tuple(NULL, &args[nreq], nargs - nreq) CallInst *restTuple = ctx.builder.CreateCall(prepare_call(jltuple_func), @@ -5733,7 +5737,7 @@ static std::unique_ptr emit_function( if (sret) { if (retvalinfo.ispointer()) { if (returninfo.cc == jl_returninfo_t::SRet) { - assert(jl_is_leaf_type(jlrettype)); + assert(jl_is_concrete_type(jlrettype)); emit_memcpy(ctx, sret, retvalinfo, jl_datatype_size(jlrettype), julia_alignment(jlrettype, 0)); } diff --git a/src/common_symbols2.inc b/src/common_symbols2.inc index d0ec352a7b8f6..391a447489353 100644 --- a/src/common_symbols2.inc +++ b/src/common_symbols2.inc @@ -161,7 +161,6 @@ jl_symbol("min"), jl_symbol("last"), jl_symbol("_length"), jl_symbol(">="), -jl_symbol("isleaftype"), jl_symbol("UInt32"), jl_symbol("Generator"), jl_symbol("TypeVar"), diff --git a/src/datatype.c b/src/datatype.c index daf5e80c39324..70151760b39d6 100644 --- a/src/datatype.c +++ b/src/datatype.c @@ -77,9 +77,11 @@ jl_datatype_t *jl_new_uninitialized_datatype(void) { jl_ptls_t ptls = jl_get_ptls_states(); jl_datatype_t *t = (jl_datatype_t*)jl_gc_alloc(ptls, sizeof(jl_datatype_t), jl_datatype_type); - t->depth = 0; t->hasfreetypevars = 0; - t->isleaftype = 1; + t->isdispatchtuple = 0; + t->isbitstype = 0; + t->zeroinit = 0; + t->isinlinealloc = 0; t->layout = NULL; t->names = NULL; return t; @@ -231,15 +233,6 @@ STATIC_INLINE void jl_allocate_singleton_instance(jl_datatype_t *st) } } -static int jl_layout_isbits(jl_value_t *ty) -{ - if (jl_isbits(ty) && jl_is_leaf_type(ty)) { - if (((jl_datatype_t*)ty)->layout) // layout check handles possible layout recursion - return 1; - } - return 0; -} - static unsigned union_isbits(jl_value_t *ty, size_t *nbytes, size_t *align) { if (jl_is_uniontype(ty)) { @@ -251,7 +244,7 @@ static unsigned union_isbits(jl_value_t *ty, size_t *nbytes, size_t *align) return 0; return na + nb; } - if (jl_layout_isbits(ty)) { + if (jl_isbits(ty)) { size_t sz = jl_datatype_size(ty); size_t al = jl_datatype_align(ty); if (*nbytes < sz) @@ -269,6 +262,29 @@ JL_DLLEXPORT int jl_islayout_inline(jl_value_t *eltype, size_t *fsz, size_t *al) return countbits > 0 && countbits < 127; } +static int references_name(jl_value_t *p, jl_typename_t *name) +{ + if (jl_is_uniontype(p)) + return references_name(((jl_uniontype_t*)p)->a, name) || + references_name(((jl_uniontype_t*)p)->b, name); + if (jl_is_unionall(p)) + return references_name((jl_value_t*)((jl_unionall_t*)p)->var, name) || + references_name(((jl_unionall_t*)p)->body, name); + if (jl_is_typevar(p)) + return references_name(((jl_tvar_t*)p)->ub, name) || + references_name(((jl_tvar_t*)p)->lb, name); + if (jl_is_datatype(p)) { + if (((jl_datatype_t*)p)->name == name) + return 1; + size_t i, l = jl_nparams(p); + for (i = 0; i < l; i++) { + if (references_name(jl_tparam(p, i), name)) + return 1; + } + } + return 0; +} + void jl_compute_field_offsets(jl_datatype_t *st) { size_t sz = 0, alignm = 1; @@ -278,18 +294,44 @@ void jl_compute_field_offsets(jl_datatype_t *st) uint64_t max_size = max_offset >> 1; if (st->name->wrapper) { + jl_datatype_t *w = (jl_datatype_t*)jl_unwrap_unionall(st->name->wrapper); + // compute whether this type can be inlined + // based on whether its definition is self-referential + if (w->types != NULL) { + st->isbitstype = st->isconcretetype && !st->mutabl; + size_t i, nf = jl_field_count(st); + for (i = 0; i < nf; i++) { + jl_value_t *fld = jl_field_type(st, i); + if (st->isbitstype) + st->isbitstype = jl_is_datatype(fld) && ((jl_datatype_t*)fld)->isbitstype; + if (!st->zeroinit) + st->zeroinit = (jl_is_datatype(fld) && ((jl_datatype_t*)fld)->isinlinealloc) ? ((jl_datatype_t*)fld)->zeroinit : 1; + } + if (st->isbitstype) { + st->isinlinealloc = 1; + size_t i, nf = jl_field_count(w); + for (i = 0; i < nf; i++) { + jl_value_t *fld = jl_field_type(w, i); + if (references_name(fld, w->name)) { + st->isinlinealloc = 0; + st->isbitstype = 0; + st->zeroinit = 1; + break; + } + } + } + } // If layout doesn't depend on type parameters, it's stored in st->name->wrapper // and reused by all subtypes. - jl_datatype_t *w = (jl_datatype_t*)jl_unwrap_unionall(st->name->wrapper); - if (st != w && // this check allows us to re-compute layout for some types during init - w->layout) { + if (st != w && // this check allows us to re-compute layout for some types during init + w->layout) { st->layout = w->layout; st->size = w->size; jl_allocate_singleton_instance(st); return; } } - if (st->types == NULL || (jl_is_namedtuple_type(st) && !jl_is_leaf_type((jl_value_t*)st))) + if (st->types == NULL || (jl_is_namedtuple_type(st) && !jl_is_concrete_type((jl_value_t*)st))) return; uint32_t nfields = jl_svec_len(st->types); if (nfields == 0) { @@ -310,7 +352,7 @@ void jl_compute_field_offsets(jl_datatype_t *st) } return; } - if (!jl_is_leaf_type((jl_value_t*)st)) { + if (!jl_is_concrete_type((jl_value_t*)st)) { // compute layout whenever field types have no free variables for (size_t i = 0; i < nfields; i++) { if (jl_has_free_typevars(jl_field_type(st, i))) @@ -476,6 +518,7 @@ JL_DLLEXPORT jl_datatype_t *jl_new_primitivetype(jl_value_t *name, jl_module_t * uint32_t alignm = next_power_of_two(nbytes); if (alignm > MAX_ALIGN) alignm = MAX_ALIGN; + bt->isbitstype = bt->isinlinealloc = (parameters == jl_emptysvec); bt->size = nbytes; bt->layout = jl_get_layout(0, alignm, 0, NULL); bt->instance = NULL; diff --git a/src/dump.c b/src/dump.c index 61c98723d3f53..6276c400ed466 100644 --- a/src/dump.c +++ b/src/dump.c @@ -334,9 +334,13 @@ static void jl_serialize_datatype(jl_serializer_state *s, jl_datatype_t *dt) write_int32(s->s, dt->size); int has_instance = (dt->instance != NULL); int has_layout = (dt->layout != NULL); - write_uint8(s->s, dt->abstract | (dt->mutabl<<1) | (has_layout<<2) | (has_instance<<3) | - (dt->hasfreetypevars<<4) | (dt->isleaftype<<5)); - write_int32(s->s, dt->depth); + write_uint8(s->s, dt->abstract | (dt->mutabl << 1) | (has_layout << 2) | (has_instance << 3)); + write_uint8(s->s, dt->hasfreetypevars + | (dt->isconcretetype << 1) + | (dt->isdispatchtuple << 2) + | (dt->isbitstype << 3) + | (dt->zeroinit << 4) + | (dt->isinlinealloc << 5)); if (!dt->abstract) { write_uint16(s->s, dt->ninitialized); } @@ -1154,7 +1158,7 @@ static jl_value_t *jl_deserialize_datatype(jl_serializer_state *s, int pos, jl_v } size_t size = read_int32(s->s); uint8_t flags = read_uint8(s->s); - uint8_t depth = read_int32(s->s); + uint8_t memflags = read_uint8(s->s); jl_datatype_t *dt = NULL; if (tag == 0 || tag == 5 || tag == 10) dt = jl_new_uninitialized_datatype(); @@ -1169,13 +1173,16 @@ static jl_value_t *jl_deserialize_datatype(jl_serializer_state *s, int pos, jl_v dt->struct_decl = NULL; dt->instance = NULL; dt->ditype = NULL; - dt->abstract = flags&1; - dt->mutabl = (flags>>1)&1; - int has_layout = (flags>>2)&1; - int has_instance = (flags>>3)&1; - dt->hasfreetypevars = (flags>>4)&1; - dt->isleaftype = (flags>>5)&1; - dt->depth = depth; + dt->abstract = flags & 1; + dt->mutabl = (flags >> 1) & 1; + int has_layout = (flags >> 2) & 1; + int has_instance = (flags >> 3) & 1; + dt->hasfreetypevars = memflags & 1; + dt->isconcretetype = (memflags >> 1) & 1; + dt->isdispatchtuple = (memflags >> 2) & 1; + dt->isbitstype = (memflags >> 3) & 1; + dt->zeroinit = (memflags >> 4) & 1; + dt->isinlinealloc = (memflags >> 5) & 1; dt->types = NULL; dt->parameters = NULL; dt->name = NULL; diff --git a/src/gf.c b/src/gf.c index ec99041f626fb..62806ce569820 100644 --- a/src/gf.c +++ b/src/gf.c @@ -563,13 +563,13 @@ static jl_tupletype_t *join_tsig(jl_tupletype_t *tt, jl_value_t *sig, int *sharp // if the declared type was not Any or Union{Type, ...}, // then the match must been with UnionAll or DataType // and the result of matching the type signature - // needs to be corrected to the leaf type 'kind' + // needs to be corrected to the concrete type 'kind' jl_value_t *kind = jl_typeof(jl_tparam0(elt)); if (jl_subtype(kind, decl_i)) { if (!jl_subtype((jl_value_t*)jl_type_type, decl_i)) { // UnionAlls are problematic because they can be alternate // representations of any type. If we matched this method because - // it matched the leaf type UnionAll, then don't cache something + // it matched the concrete type UnionAll, then don't cache something // different since that doesn't necessarily actually apply. // // similarly, if we matched Type{T<:Any}::DataType, @@ -728,14 +728,15 @@ JL_DLLEXPORT int jl_is_cacheable_sig( // compute whether this type signature is a possible return value from jl_cacheable_sig //return jl_cacheable_sig(type, NULL, definition->sig, definition, NULL, NULL); - if (definition->generator) - // staged functions can't be optimized - // so assume the caller was intelligent about calling us - return 1; - - if (!jl_is_datatype(type)) + if (!jl_is_datatype(type) || jl_has_free_typevars((jl_value_t*)type)) return 0; + if (definition->generator) { + // staged functions aren't optimized + // so assume the caller was intelligent about calling us + return type->isdispatchtuple; + } + size_t i, np = jl_nparams(type); for (i = 0; i < np; i++) { jl_value_t *elt = jl_tparam(type, i); @@ -743,8 +744,11 @@ JL_DLLEXPORT int jl_is_cacheable_sig( if (jl_is_vararg_type(elt)) // varargs are always considered compilable continue; - if (jl_is_kind(elt)) // kind slots always need guard entries (checking for subtypes of Type) - continue; + if (jl_is_kind(elt)) { // kind slots always get guard entries (checking for subtypes of Type) + if (decl_i == elt || jl_subtype((jl_value_t*)jl_type_type, decl_i)) + continue; + return 0; + } if (i > 0 && i <= sizeof(definition->nospecialize) * 8 && (definition->nospecialize & (1 << (i - 1))) && decl_i == (jl_value_t*)jl_any_type) { // TODO: nospecialize with other types @@ -754,9 +758,9 @@ JL_DLLEXPORT int jl_is_cacheable_sig( } if (jl_is_type_type(elt)) { // if join_tsig would make a swap // if the declared type was not Any or Union{Type, ...}, - // then the match must been with TypeConstructor or DataType + // then the match must been with kind, such as UnionAll or DataType, // and the result of matching the type signature - // needs to be corrected to the leaf type 'kind' + // needs to be corrected to the concrete type 'kind' jl_value_t *kind = jl_typeof(jl_tparam0(elt)); if (kind != (jl_value_t*)jl_tvar_type && jl_subtype(kind, decl_i)) { if (!jl_subtype((jl_value_t*)jl_type_type, decl_i)) @@ -845,7 +849,7 @@ JL_DLLEXPORT int jl_is_cacheable_sig( return 0; continue; } - else if (!jl_is_leaf_type(elt)) { + else if (!jl_is_concrete_type(elt) && !jl_is_type_type(elt)) { return 0; } } @@ -899,7 +903,7 @@ static jl_method_instance_t *cache_method( // then specialize as (Any...) // // note: this also protects the work join_tsig did to correct `types` for the - // leaftype signatures TypeConstructor and DataType + // concrete signatures UnionAll and DataType // (assuming those made an unlikely appearance in Varargs position) size_t j = i; int all_are_subtypes = 1; @@ -1601,17 +1605,17 @@ jl_tupletype_t *arg_type_tuple(jl_value_t **args, size_t nargs) { jl_tupletype_t *tt; size_t i; - if (nargs < jl_page_size/sizeof(jl_value_t*)) { + if (nargs * sizeof(jl_value_t*) < jl_page_size) { jl_value_t **types; JL_GC_PUSHARGS(types, nargs); - for(i=0; i < nargs; i++) { + for (i = 0; i < nargs; i++) { jl_value_t *ai = args[i]; if (jl_is_type(ai)) types[i] = (jl_value_t*)jl_wrap_Type(ai); else types[i] = jl_typeof(ai); } - // if `ai` has free type vars this will not be a leaf type. + // if `ai` has free type vars this will not be a valid (concrete) type. // TODO: it would be really nice to only dispatch and cache those as // `jl_typeof(ai)`, but that will require some redesign of the caching // logic. @@ -1621,7 +1625,7 @@ jl_tupletype_t *arg_type_tuple(jl_value_t **args, size_t nargs) else { jl_svec_t *types = jl_alloc_svec(nargs); JL_GC_PUSH1(&types); - for(i=0; i < nargs; i++) { + for (i = 0; i < nargs; i++) { jl_value_t *ai = args[i]; if (jl_is_type(ai)) jl_svecset(types, i, (jl_value_t*)jl_wrap_Type(ai)); @@ -1653,7 +1657,7 @@ jl_method_instance_t *jl_method_lookup_by_type(jl_methtable_t *mt, jl_tupletype_ JL_UNLOCK(&mt->writelock); return linfo; } - if (jl_is_leaf_type((jl_value_t*)types)) // FIXME: this is the wrong predicate + if (jl_is_datatype((jl_value_t*)types) && types->isdispatchtuple) cache = 1; jl_method_instance_t *sf = jl_mt_assoc_by_type(mt, types, cache, allow_exec, world); if (cache) { @@ -1787,7 +1791,8 @@ jl_llvm_functions_t jl_compile_for_dispatch(jl_method_instance_t **pli, size_t w jl_method_instance_t *jl_get_specialization1(jl_tupletype_t *types, size_t world) { JL_TIMING(METHOD_LOOKUP_COMPILE); - if (/* TODO: !jl_is_cacheable_sig((jl_value_t*)types) &&*/ !jl_is_leaf_type((jl_value_t*)types)) + if (!jl_is_datatype(((jl_value_t*)types)) || !types->isdispatchtuple) + /* TODO: if (!jl_is_cacheable_sig((jl_value_t*)types)) return NULL; */ return NULL; if (jl_has_free_typevars((jl_value_t*)types)) return NULL; // don't poison the cache due to a malformed query @@ -1885,7 +1890,7 @@ JL_DLLEXPORT int jl_is_call_ambiguous(jl_value_t *types, jl_method_t *m) } // see if a call to m with a subtype of `types` might be ambiguous -// if types is from a call signature (approximated by isleaftype), this is the same as jl_is_call_ambiguous above +// if types is from a call signature (isdispatchtuple), this is the same as jl_is_call_ambiguous above JL_DLLEXPORT int jl_has_call_ambiguities(jl_value_t *types, jl_method_t *m) { if (m->ambig == jl_nothing) @@ -2028,7 +2033,7 @@ STATIC_INLINE jl_method_instance_t *jl_lookup_generic_(jl_value_t **args, uint32 mt = jl_gf_mtable(F); entry = jl_typemap_assoc_exact(mt->cache, args, nargs, jl_cachearg_offset(mt), world); if (entry && entry->isleafsig && entry->simplesig == (void*)jl_nothing && entry->guardsigs == jl_emptysvec) { - // put the entry into the cache if it's valid for a leaftype lookup, + // put the entry into the cache if it's valid for a leafsig lookup, // using pick_which to slightly randomize where it ends up call_cache[cache_idx[++pick_which[cache_idx[0]] & 3]] = entry; } @@ -2165,44 +2170,13 @@ jl_value_t *jl_gf_invoke(jl_value_t *types0, jl_value_t **args, size_t nargs) return jl_call_method_internal(mfunc, args, nargs); } -typedef struct _tupletype_stack_t { - struct _tupletype_stack_t *parent; - jl_tupletype_t *tt; -} tupletype_stack_t; - -static int tupletype_on_stack(jl_tupletype_t *tt, tupletype_stack_t *stack) -{ - while (stack) { - if (tt == stack->tt) - return 1; - stack = stack->parent; - } - return 0; -} - -static int tupletype_has_datatype(jl_tupletype_t *tt, tupletype_stack_t *stack) -{ - for (int i = 0; i < jl_nparams(tt); i++) { - jl_value_t *ti = jl_tparam(tt, i); - if (ti == (jl_value_t*)jl_datatype_type) - return 1; - if (jl_is_tuple_type(ti)) { - jl_tupletype_t *tt1 = (jl_tupletype_t*)ti; - if (!tupletype_on_stack(tt1, stack) && - tupletype_has_datatype(tt1, stack)) { - return 1; - } - } - } - return 0; -} - JL_DLLEXPORT jl_value_t *jl_get_invoke_lambda(jl_methtable_t *mt, jl_typemap_entry_t *entry, jl_value_t *tt, size_t world) { - if (!jl_is_leaf_type((jl_value_t*)tt) || tupletype_has_datatype((jl_tupletype_t*)tt, NULL)) + // TODO: refactor this method to be more like `jl_get_specialization1` + if (!jl_is_datatype(tt) || !((jl_datatype_t*)tt)->isdispatchtuple) return jl_nothing; jl_method_t *method = entry->func.method; @@ -2355,9 +2329,10 @@ static int ml_matches_visitor(jl_typemap_entry_t *ml, struct typemap_intersectio // the "limited" mode used by type inference. for (i = 0; i < len; i++) { jl_value_t *prior_ti = jl_svecref(jl_array_ptr_ref(closure->t, i), 0); - // TODO: should be possible to remove the `jl_is_leaf_type` check, + // TODO: should be possible to remove the `isdispatchtuple` check, // but we still need it in case an intersection was approximate. - if (jl_is_leaf_type(prior_ti) && jl_subtype(closure->match.ti, prior_ti)) { + if (jl_is_datatype(prior_ti) && ((jl_datatype_t*)prior_ti)->isdispatchtuple && + jl_subtype(closure->match.ti, prior_ti)) { skip = 1; break; } diff --git a/src/intrinsics.cpp b/src/intrinsics.cpp index 2f215a1ebcbe0..ecbf1fb53aa39 100644 --- a/src/intrinsics.cpp +++ b/src/intrinsics.cpp @@ -148,7 +148,7 @@ static Value *uint_cnvt(jl_codectx_t &ctx, Type *to, Value *x) static Constant *julia_const_to_llvm(const void *ptr, jl_datatype_t *bt) { - // assumes `jl_isbits(bt)`. + // assumes `jl_justbits(bt)`. // `ptr` can point to a inline field, do not read the tag from it. // make sure to return exactly the type specified by // julia_type_to_llvm as this will be assumed by the callee. @@ -269,7 +269,7 @@ static Constant *julia_const_to_llvm(jl_value_t *e) if (e == jl_false) return ConstantInt::get(T_int8, 0); jl_value_t *bt = jl_typeof(e); - if (!jl_isbits(bt)) + if (!jl_justbits(bt)) return NULL; return julia_const_to_llvm(e, (jl_datatype_t*)bt); } @@ -444,15 +444,15 @@ static jl_cgval_t generic_bitcast(jl_codectx_t &ctx, const jl_cgval_t *argv) Value *typ = emit_typeof_boxed(ctx, v); if (!jl_is_primitivetype(v.typ)) { if (isboxed) { - Value *isbits = emit_datatype_isbitstype(ctx, typ); - error_unless(ctx, isbits, "bitcast: expected primitive type value for second argument"); + Value *isprimitive = emit_datatype_isprimitivetype(ctx, typ); + error_unless(ctx, isprimitive, "bitcast: expected primitive type value for second argument"); } else { emit_error(ctx, "bitcast: expected primitive type value for second argument"); return jl_cgval_t(); } } - if (jl_datatype_size(v.typ) != nb) { + if (!jl_is_datatype(v.typ) || jl_datatype_size(v.typ) != nb) { if (isboxed) { Value *size = emit_datatype_size(ctx, typ); error_unless(ctx, @@ -497,7 +497,7 @@ static jl_cgval_t generic_bitcast(jl_codectx_t &ctx, const jl_cgval_t *argv) vx = emit_bitcast(ctx, vx, llvmt); } - if (jl_is_leaf_type(bt)) { + if (jl_is_concrete_type(bt)) { return mark_julia_type(ctx, vx, false, bt); } else { @@ -628,7 +628,7 @@ static jl_cgval_t emit_pointerref(jl_codectx_t &ctx, jl_cgval_t *argv) ety); } else if (!jl_isbits(ety)) { - if (!jl_is_structtype(ety) || jl_is_array_type(ety) || !jl_is_leaf_type(ety)) { + if (!jl_is_structtype(ety) || jl_is_array_type(ety) || !jl_is_concrete_type(ety)) { emit_error(ctx, "pointerref: invalid pointer type"); return jl_cgval_t(); } @@ -696,7 +696,7 @@ static jl_cgval_t emit_pointerset(jl_codectx_t &ctx, jl_cgval_t *argv) tbaa_decorate(tbaa_data, store); } else if (!jl_isbits(ety)) { - if (!jl_is_structtype(ety) || jl_is_array_type(ety) || !jl_is_leaf_type(ety)) { + if (!jl_is_structtype(ety) || jl_is_array_type(ety) || !jl_is_concrete_type(ety)) { emit_error(ctx, "pointerset: invalid pointer type"); return jl_cgval_t(); } diff --git a/src/jltypes.c b/src/jltypes.c index b0b4b0dc73e9e..8d790c675b0e6 100644 --- a/src/jltypes.c +++ b/src/jltypes.c @@ -268,46 +268,6 @@ JL_DLLEXPORT int jl_has_typevar_from_unionall(jl_value_t *t, jl_unionall_t *ua) } -JL_DLLEXPORT int (jl_is_leaf_type)(jl_value_t *v) -{ - if (jl_is_datatype(v)) { - int isleaf = ((jl_datatype_t*)v)->isleaftype; -#ifdef JL_NDEBUG - return isleaf; -#else - if (((jl_datatype_t*)v)->abstract) { - int x = 0; - if (jl_is_type_type(v)) - x = !jl_has_free_typevars(jl_tparam0(v)); - assert(x == isleaf); - return x; - } - jl_svec_t *t = ((jl_datatype_t*)v)->parameters; - size_t l = jl_svec_len(t); - if (((jl_datatype_t*)v)->name == jl_tuple_typename) { - for(int i=0; i < l; i++) { - jl_value_t *p = jl_svecref(t,i); - if (!jl_is_leaf_type(p) && p != jl_bottom_type) { - assert(!isleaf); - return 0; - } - } - } - else { - for(int i=0; i < l; i++) { - if (jl_has_free_typevars(jl_svecref(t, i))) { - assert(!isleaf); - return 0; - } - } - } - assert(isleaf); - return 1; -#endif - } - return 0; -} - // Return true for any type (Integer or Unsigned) that can fit in a // size_t and pass back value, else return false JL_DLLEXPORT int jl_get_size(jl_value_t *val, size_t *pnt) @@ -791,7 +751,7 @@ static int is_cacheable(jl_datatype_t *type) if (jl_is_abstracttype(type)) return !jl_has_free_typevars((jl_value_t*)type); // ... or concrete types - return jl_is_leaf_type((jl_value_t*)type); + return jl_is_concrete_type((jl_value_t*)type); } static void cache_insert_type(jl_value_t *type, ssize_t insert_at, int ordered) @@ -1018,43 +978,26 @@ static jl_value_t *lookup_type_stack(jl_typestack_t *stack, jl_datatype_t *tt, s return NULL; } -static size_t jl_type_depth(jl_value_t *dt) -{ - if (jl_is_uniontype(dt)) { - size_t ad = jl_type_depth(((jl_uniontype_t*)dt)->a); - size_t bd = jl_type_depth(((jl_uniontype_t*)dt)->b); - return ad > bd ? ad : bd; - } - else if (jl_is_unionall(dt)) { - jl_unionall_t *ua = (jl_unionall_t*)dt; - size_t bd = jl_type_depth(ua->body); - if (ua->var->ub == (jl_value_t*)jl_any_type) - return bd; - size_t vd = jl_type_depth(ua->var->ub); - return vd+1 > bd ? vd+1 : bd; - } - else if (jl_is_datatype(dt)) { - return ((jl_datatype_t*)dt)->depth; - } - return 0; -} - void jl_precompute_memoized_dt(jl_datatype_t *dt) { - int istuple = dt->name == jl_tuple_typename; + int istuple = (dt->name == jl_tuple_typename); + dt->hasfreetypevars = 0; + dt->isconcretetype = !dt->abstract; + dt->isdispatchtuple = istuple; size_t i, l = jl_nparams(dt); - dt->isleaftype = !dt->abstract || (jl_type_type != NULL && dt->name == jl_type_typename); for (i = 0; i < l; i++) { jl_value_t *p = jl_tparam(dt, i); - size_t d = jl_type_depth(p) + 1; - if (d > dt->depth) - dt->depth = d; if (!dt->hasfreetypevars) dt->hasfreetypevars = jl_has_free_typevars(p); - if (dt->isleaftype) - dt->isleaftype = (istuple ? (jl_is_leaf_type(p) || p == jl_bottom_type) : - !dt->hasfreetypevars); + if (istuple && dt->isconcretetype) + dt->isconcretetype = (jl_is_datatype(p) && ((jl_datatype_t*)p)->isconcretetype) || p == jl_bottom_type; + if (dt->isdispatchtuple) + dt->isdispatchtuple = jl_is_datatype(p) && + ((!jl_is_kind(p) && ((jl_datatype_t*)p)->isconcretetype) || + (((jl_datatype_t*)p)->name == jl_type_typename && !((jl_datatype_t*)p)->hasfreetypevars)); } + if (dt->hasfreetypevars) + dt->isconcretetype = 0; } static void check_datatype_parameters(jl_typename_t *tn, jl_value_t **params, size_t np) @@ -1142,19 +1085,16 @@ static jl_value_t *inst_datatype_inner(jl_datatype_t *dt, jl_svec_t *p, jl_value if (cacheable) { JL_LOCK(&typecache_lock); // Might GC size_t i; - for(i=0; i < ntp; i++) { + for (i = 0; i < ntp; i++) { jl_value_t *pi = iparams[i]; if (pi == jl_bottom_type) continue; - if (jl_is_leaf_type(pi)) { - assert(jl_is_datatype(pi)); - if (!((jl_datatype_t*)pi)->abstract) - continue; - } - // normalize types equal to wrappers + if (jl_is_datatype(pi)) + continue; + // normalize types equal to wrappers (prepare for wrapper_id) jl_value_t *tw = extract_wrapper(pi); if (tw && tw != pi && (tn != jl_type_typename || jl_typeof(pi) == jl_typeof(tw)) && - jl_types_equal(pi, tw)) { + jl_types_equal(pi, tw)) { iparams[i] = tw; if (p) jl_gc_wb(p, tw); } @@ -1334,6 +1274,7 @@ static jl_value_t *inst_datatype_inner(jl_datatype_t *dt, jl_svec_t *p, jl_value if (jl_is_primitivetype(dt)) { ndt->size = dt->size; ndt->layout = dt->layout; + ndt->isbitstype = ndt->isinlinealloc = (!ndt->hasfreetypevars); } else if (cacheable && ndt->types != NULL && !ndt->abstract) { jl_compute_field_offsets(ndt); @@ -1377,8 +1318,8 @@ static jl_value_t *inst_datatype(jl_datatype_t *dt, jl_svec_t *p, jl_value_t **i static jl_tupletype_t *jl_apply_tuple_type_v_(jl_value_t **p, size_t np, jl_svec_t *params) { int cacheable = 1; - for(size_t i=0; i < np; i++) { - if (!jl_is_leaf_type(p[i])) + for (size_t i = 0; i < np; i++) { + if (!jl_is_concrete_type(p[i])) cacheable = 0; } jl_datatype_t *ndt = (jl_datatype_t*)inst_datatype(jl_anytuple_type, params, p, np, @@ -1461,13 +1402,13 @@ static jl_value_t *inst_tuple_w_(jl_value_t *t, jl_typeenv_t *env, jl_typestack_ if (jl_is_va_tuple(tt)) cacheable = 0; int i; - for(i=0; i < ntp; i++) { + for (i = 0; i < ntp; i++) { jl_value_t *elt = jl_svecref(tp, i); jl_value_t *pi = (jl_value_t*)inst_type_w_(elt, env, stack, 0); iparams[i] = pi; if (ip_heap) jl_gc_wb(ip_heap, pi); - if (cacheable && !jl_is_leaf_type(pi)) + if (cacheable && !jl_is_concrete_type(pi)) cacheable = 0; } jl_value_t *result = inst_datatype((jl_datatype_t*)tt, ip_heap, iparams, ntp, cacheable, stack); @@ -1728,7 +1669,7 @@ void jl_init_types(void) jl_datatype_type->name->wrapper = (jl_value_t*)jl_datatype_type; jl_datatype_type->super = (jl_datatype_t*)jl_type_type; jl_datatype_type->parameters = jl_emptysvec; - jl_datatype_type->name->names = jl_perm_symsvec(17, + jl_datatype_type->name->names = jl_perm_symsvec(20, "name", "super", "parameters", @@ -1741,12 +1682,15 @@ void jl_init_types(void) "uid", "abstract", "mutable", - "llvm::StructType", - "llvm::DIType", - "depth", "hasfreetypevars", - "isleaftype"); - jl_datatype_type->types = jl_svec(17, + "isconcretetype", + "isdispatchtuple", + "isbitstype", + "zeroinit", + "isinlinealloc", + "llvm::StructType", + "llvm::DIType"); + jl_datatype_type->types = jl_svec(20, jl_typename_type, jl_datatype_type, jl_simplevector_type, @@ -1754,7 +1698,8 @@ void jl_init_types(void) jl_any_type, // instance jl_any_type, jl_any_type, jl_any_type, jl_any_type, jl_any_type, jl_any_type, jl_any_type, jl_any_type, - jl_any_type, jl_any_type, jl_any_type); + jl_any_type, jl_any_type, jl_any_type, jl_any_type, + jl_any_type, jl_any_type); jl_datatype_type->instance = NULL; jl_datatype_type->uid = jl_assign_type_uid(); jl_datatype_type->struct_decl = NULL; @@ -1764,6 +1709,7 @@ void jl_init_types(void) // struct_decl fields are basically caches, which are mutated. jl_datatype_type->mutabl = 1; jl_datatype_type->ninitialized = 4; + jl_precompute_memoized_dt(jl_datatype_type); jl_typename_type->name = jl_new_typename_in(jl_symbol("TypeName"), core); jl_typename_type->name->wrapper = (jl_value_t*)jl_typename_type; @@ -1784,6 +1730,7 @@ void jl_init_types(void) jl_typename_type->abstract = 0; jl_typename_type->mutabl = 1; jl_typename_type->ninitialized = 2; + jl_precompute_memoized_dt(jl_typename_type); jl_methtable_type->name = jl_new_typename_in(jl_symbol("MethodTable"), core); jl_methtable_type->name->wrapper = (jl_value_t*)jl_methtable_type; @@ -1804,6 +1751,7 @@ void jl_init_types(void) jl_methtable_type->abstract = 0; jl_methtable_type->mutabl = 1; jl_methtable_type->ninitialized = 4; + jl_precompute_memoized_dt(jl_methtable_type); jl_sym_type->name = jl_new_typename_in(jl_symbol("Symbol"), core); jl_sym_type->name->wrapper = (jl_value_t*)jl_sym_type; @@ -1820,6 +1768,7 @@ void jl_init_types(void) jl_sym_type->abstract = 0; jl_sym_type->mutabl = 1; jl_sym_type->ninitialized = 0; + jl_precompute_memoized_dt(jl_sym_type); jl_simplevector_type->name = jl_new_typename_in(jl_symbol("SimpleVector"), core); jl_simplevector_type->name->wrapper = (jl_value_t*)jl_simplevector_type; @@ -1835,6 +1784,7 @@ void jl_init_types(void) jl_simplevector_type->abstract = 0; jl_simplevector_type->mutabl = 1; jl_simplevector_type->ninitialized = 0; + jl_precompute_memoized_dt(jl_simplevector_type); // now they can be used to create the remaining base kinds and types jl_void_type = jl_new_datatype(jl_symbol("Nothing"), core, jl_any_type, jl_emptysvec, @@ -1878,7 +1828,10 @@ void jl_init_types(void) jl_anytuple_type->layout = NULL; jl_anytuple_type->instance = NULL; jl_anytuple_type->hasfreetypevars = 0; - jl_anytuple_type->isleaftype = 0; + jl_anytuple_type->isconcretetype = 0; + jl_anytuple_type->isdispatchtuple = 0; + jl_anytuple_type->isbitstype = 0; + jl_anytuple_type->isinlinealloc = 0; jl_tvar_t *tttvar = tvar("T"); ((jl_datatype_t*)jl_type_type)->parameters = jl_svec(1, tttvar); @@ -2209,11 +2162,14 @@ void jl_init_types(void) jl_svecset(jl_datatype_type->types, 9, jl_int32_type); jl_svecset(jl_datatype_type->types, 10, jl_bool_type); jl_svecset(jl_datatype_type->types, 11, jl_bool_type); - jl_svecset(jl_datatype_type->types, 12, jl_voidpointer_type); - jl_svecset(jl_datatype_type->types, 13, jl_voidpointer_type); - jl_svecset(jl_datatype_type->types, 14, jl_int32_type); + jl_svecset(jl_datatype_type->types, 12, jl_bool_type); + jl_svecset(jl_datatype_type->types, 13, jl_bool_type); + jl_svecset(jl_datatype_type->types, 14, jl_bool_type); jl_svecset(jl_datatype_type->types, 15, jl_bool_type); jl_svecset(jl_datatype_type->types, 16, jl_bool_type); + jl_svecset(jl_datatype_type->types, 17, jl_bool_type); + jl_svecset(jl_datatype_type->types, 18, jl_voidpointer_type); + jl_svecset(jl_datatype_type->types, 19, jl_voidpointer_type); jl_svecset(jl_typename_type->types, 1, jl_module_type); jl_svecset(jl_typename_type->types, 6, jl_long_type); jl_svecset(jl_typename_type->types, 3, jl_type_type); diff --git a/src/julia.h b/src/julia.h index 3a207c61f8d5d..2c23085b85d8d 100644 --- a/src/julia.h +++ b/src/julia.h @@ -397,11 +397,14 @@ typedef struct _jl_datatype_t { uint8_t abstract; uint8_t mutabl; // memoized properties + uint8_t hasfreetypevars; // majority part of isconcrete computation + uint8_t isconcretetype; // whether this type can have instances + uint8_t isdispatchtuple; // aka isleaftupletype + uint8_t isbitstype; // relevant query for C-api and type-parameters + uint8_t zeroinit; // if one or more fields requires zero-initialization + uint8_t isinlinealloc; // if this is allocated inline void *struct_decl; //llvm::Type* void *ditype; // llvm::MDNode* to be used as llvm::DIType(ditype) - int32_t depth; - int8_t hasfreetypevars; - int8_t isleaftype; } jl_datatype_t; typedef struct { @@ -940,9 +943,7 @@ STATIC_INLINE int jl_is_structtype(void *v) STATIC_INLINE int jl_isbits(void *t) // corresponding to isbits() in julia { - return (jl_is_datatype(t) && ((jl_datatype_t*)t)->layout && - !((jl_datatype_t*)t)->mutabl && - ((jl_datatype_t*)t)->layout->npointers == 0); + return (jl_is_datatype(t) && ((jl_datatype_t*)t)->isbitstype); } STATIC_INLINE int jl_is_datatype_singleton(jl_datatype_t *d) @@ -1008,7 +1009,6 @@ JL_DLLEXPORT int jl_egal(jl_value_t *a, jl_value_t *b); JL_DLLEXPORT uintptr_t jl_object_id(jl_value_t *v); // type predicates and basic operations -JL_DLLEXPORT int jl_is_leaf_type(jl_value_t *v); JL_DLLEXPORT int jl_has_free_typevars(jl_value_t *v); JL_DLLEXPORT int jl_has_typevar(jl_value_t *t, jl_tvar_t *v); JL_DLLEXPORT int jl_has_typevar_from_unionall(jl_value_t *t, jl_unionall_t *ua); @@ -1026,13 +1026,15 @@ JL_DLLEXPORT int jl_type_morespecific(jl_value_t *a, jl_value_t *b); jl_value_t *jl_unwrap_unionall(jl_value_t *v); jl_value_t *jl_rewrap_unionall(jl_value_t *t, jl_value_t *u); -#if defined(JL_NDEBUG) -STATIC_INLINE int jl_is_leaf_type_(jl_value_t *v) +STATIC_INLINE int jl_is_dispatch_tupletype(jl_value_t *v) { - return jl_is_datatype(v) && ((jl_datatype_t*)v)->isleaftype; + return jl_is_datatype(v) && ((jl_datatype_t*)v)->isdispatchtuple; +} + +STATIC_INLINE int jl_is_concrete_type(jl_value_t *v) +{ + return jl_is_datatype(v) && ((jl_datatype_t*)v)->isconcretetype; } -#define jl_is_leaf_type(v) jl_is_leaf_type_(v) -#endif // type constructors JL_DLLEXPORT jl_typename_t *jl_new_typename_in(jl_sym_t *name, jl_module_t *inmodule); diff --git a/src/method.c b/src/method.c index 77f8d497a2621..2ad7cdf7f8092 100644 --- a/src/method.c +++ b/src/method.c @@ -693,10 +693,8 @@ JL_DLLEXPORT void jl_method_def(jl_svec_t *argdata, jl_datatype_t *ftype = jl_first_argument_datatype(argtype); if (ftype == NULL || - !(jl_is_type_type((jl_value_t*)ftype) || - (jl_is_datatype(ftype) && - (!ftype->abstract || jl_is_leaf_type((jl_value_t*)ftype)) && - ftype->name->mt != NULL))) + ((!jl_is_type_type((jl_value_t*)ftype)) && + (!jl_is_datatype(ftype) || ftype->abstract || ftype->name->mt == NULL))) jl_error("cannot add methods to an abstract type"); if (jl_subtype((jl_value_t*)ftype, (jl_value_t*)jl_builtin_type)) jl_error("cannot add methods to a builtin function"); diff --git a/src/precompile.c b/src/precompile.c index c498689d5e40d..2d14a2679b891 100644 --- a/src/precompile.c +++ b/src/precompile.c @@ -93,9 +93,9 @@ void jl_write_compiler_output(void) // and expanding the Union may give a leaf function static void _compile_all_tvar_union(jl_value_t *methsig) { - if (!jl_is_unionall(methsig) && jl_is_leaf_type(methsig)) { + if (!jl_is_unionall(methsig) && jl_is_dispatch_tupletype(methsig)) { // usually can create a specialized version of the function, - // if the signature is already a leaftype + // if the signature is already a dispatch type if (jl_compile_hint((jl_tupletype_t*)methsig)) return; } @@ -126,7 +126,7 @@ static void _compile_all_tvar_union(jl_value_t *methsig) } if (!jl_has_concrete_subtype(sig)) goto getnext; // signature wouldn't be callable / is invalid -- skip it - if (jl_is_leaf_type(sig)) { + if (jl_is_concrete_type(sig)) { if (jl_compile_hint((jl_tupletype_t*)sig)) goto getnext; // success } @@ -143,7 +143,7 @@ static void _compile_all_tvar_union(jl_value_t *methsig) } else { jl_value_t *ty = jl_nth_union_component(tv->ub, j); - if (!jl_is_leaf_type(ty)) + if (!jl_is_concrete_type(ty)) ty = (jl_value_t*)jl_new_typevar(tv->name, tv->lb, ty); env[2 * i + 1] = ty; idx[i] = j + 1; @@ -174,7 +174,9 @@ static void _compile_all_union(jl_value_t *sig) ++count_unions; else if (ty == jl_bottom_type) return; // why does this method exist? - else if (!jl_is_leaf_type(ty) && !jl_has_free_typevars(ty)) + else if (jl_is_datatype(ty) && !jl_has_free_typevars(ty) && + ((!jl_is_kind(ty) && ((jl_datatype_t*)ty)->isconcretetype) || + ((jl_datatype_t*)ty)->name == jl_type_typename)) return; // no amount of union splitting will make this a leaftype signature } diff --git a/src/runtime_intrinsics.c b/src/runtime_intrinsics.c index 33ed4c9907fa0..d5ace34a2e1ac 100644 --- a/src/runtime_intrinsics.c +++ b/src/runtime_intrinsics.c @@ -18,7 +18,7 @@ const unsigned int host_char_bit = 8; JL_DLLEXPORT jl_value_t *jl_bitcast(jl_value_t *ty, jl_value_t *v) { JL_TYPECHK(bitcast, datatype, ty); - if (!jl_is_leaf_type(ty) || !jl_is_primitivetype(ty)) + if (!jl_is_concrete_type(ty) || !jl_is_primitivetype(ty)) jl_error("bitcast: target type not a leaf primitive type"); if (!jl_is_primitivetype(jl_typeof(v))) jl_error("bitcast: value not a primitive type"); @@ -82,8 +82,8 @@ JL_DLLEXPORT jl_value_t *jl_cglobal(jl_value_t *v, jl_value_t *ty) v == (jl_value_t*)jl_void_type ? (jl_value_t*)jl_voidpointer_type : // a common case (jl_value_t*)jl_apply_type1((jl_value_t*)jl_pointer_type, ty); - if (!jl_is_leaf_type(rt)) - jl_error("cglobal: type argument not a leaftype"); + if (!jl_is_concrete_type(rt)) + jl_error("cglobal: type argument not concrete"); if (jl_is_tuple(v) && jl_nfields(v) == 1) v = jl_fieldref(v, 0); diff --git a/src/subtype.c b/src/subtype.c index 9101515dc5eb9..ca62f3adcb237 100644 --- a/src/subtype.c +++ b/src/subtype.c @@ -206,9 +206,7 @@ static int obviously_unequal(jl_value_t *a, jl_value_t *b) { if (a == b) return 0; - if (jl_is_leaf_type(a) && !((jl_datatype_t*)a)->abstract) - return 1; - if (jl_is_leaf_type(b) && !((jl_datatype_t*)b)->abstract) + if (jl_is_concrete_type(a) || jl_is_concrete_type(b)) return 1; if (jl_is_unionall(a)) a = jl_unwrap_unionall(a); if (jl_is_unionall(b)) b = jl_unwrap_unionall(b); @@ -250,8 +248,7 @@ static int obviously_disjoint(jl_value_t *a, jl_value_t *b, int specificity) { if (a == b || a == (jl_value_t*)jl_any_type || b == (jl_value_t*)jl_any_type) return 0; - if (jl_is_leaf_type(a) && !((jl_datatype_t*)a)->abstract && - jl_is_leaf_type(b) && !((jl_datatype_t*)b)->abstract && + if (jl_is_concrete_type(a) && jl_is_concrete_type(b) && // TODO: remove these 2 lines if and when Tuple{Union{}} === Union{} (((jl_datatype_t*)a)->name != jl_tuple_typename || ((jl_datatype_t*)b)->name != jl_tuple_typename)) @@ -505,27 +502,20 @@ static int var_gt(jl_tvar_t *b, jl_value_t *a, jl_stenv_t *e, int param) return 1; } -// check that a type is concrete. this is used to check concrete typevars; +// check that a type is concrete or quasi-concrete (Type{T}). +// this is used to check concrete typevars: // issubtype is false if the lower bound of a concrete type var is not concrete. static int is_leaf_bound(jl_value_t *v) { - if (v == jl_bottom_type) return 1; + if (v == jl_bottom_type) + return 1; if (jl_is_datatype(v)) { - if (((jl_datatype_t*)v)->isleaftype) return 1; if (((jl_datatype_t*)v)->abstract) { if (jl_is_type_type(v)) return 1;//!jl_has_free_typevars(jl_tparam0(v)); return 0; } - jl_svec_t *t = ((jl_datatype_t*)v)->parameters; - size_t l = jl_svec_len(t); - if (((jl_datatype_t*)v)->name == jl_tuple_typename) { - for(int i=0; i < l; i++) { - if (!is_leaf_bound(jl_svecref(t,i))) - return 0; - } - } - return 1; + return ((jl_datatype_t*)v)->isconcretetype; } return !jl_is_type(v) && !jl_is_typevar(v); } @@ -795,7 +785,7 @@ static int subtype_tuple(jl_datatype_t *xd, jl_datatype_t *yd, jl_stenv_t *e, in } if (xi == lastx && ((yi == lasty && !jl_has_free_typevars(xi) && !jl_has_free_typevars(yi)) || - (yi == lasty && !vx && vy && jl_is_leaf_type(xi)))) { + (yi == lasty && !vx && vy && jl_is_concrete_type(xi)))) { // fast path for repeated elements } else if (e->Runions.depth == 0 && e->Lunions.depth == 0 && !jl_has_free_typevars(xi) && !jl_has_free_typevars(yi)) { @@ -1175,11 +1165,10 @@ JL_DLLEXPORT int jl_isa(jl_value_t *x, jl_value_t *t) if (t == (jl_value_t*)jl_type_type) return 1; if (!jl_has_free_typevars(x)) { - if (jl_is_leaf_type(t)) { - if (jl_is_type_type(t)) - return jl_types_equal(x, jl_tparam0(t)); + if (jl_is_concrete_type(t)) return 0; - } + if (jl_is_type_type(t)) + return jl_types_equal(x, jl_tparam0(t)); jl_value_t *t2 = jl_unwrap_unionall(t); if (jl_is_datatype(t2)) { if (((jl_datatype_t*)t2)->name == jl_type_typename) { @@ -1207,7 +1196,7 @@ JL_DLLEXPORT int jl_isa(jl_value_t *x, jl_value_t *t) return 0; } } - if (jl_is_leaf_type(t)) + if (jl_is_concrete_type(t)) return 0; return jl_subtype(jl_typeof(x), t); } @@ -2194,7 +2183,8 @@ jl_value_t *jl_type_intersection_env_s(jl_value_t *a, jl_value_t *b, jl_svec_t * *ans = b; } else { - int lta = jl_is_leaf_type(a), ltb = jl_is_leaf_type(b); + int lta = jl_is_concrete_type(a); + int ltb = jl_is_concrete_type(b); if (lta && ltb) goto bot; jl_stenv_t e; diff --git a/src/typemap.c b/src/typemap.c index 74da3a3f750ab..d0ffe138e9276 100644 --- a/src/typemap.c +++ b/src/typemap.c @@ -1011,19 +1011,15 @@ jl_typemap_entry_t *jl_typemap_insert(union jl_typemap_t *cache, jl_value_t *par size_t i, l; for (i = 0, l = jl_field_count(ttype); i < l && newrec->issimplesig; i++) { jl_value_t *decl = jl_field_type(ttype, i); - if (decl == (jl_value_t*)jl_datatype_type) - newrec->isleafsig = 0; // Type{} may have a higher priority than DataType - else if (decl == (jl_value_t*)jl_unionall_type) - newrec->isleafsig = 0; // Type{} may have a higher priority than UnionAll - else if (decl == (jl_value_t*)jl_uniontype_type) - newrec->isleafsig = 0; // Type{} may have a higher priority than Union + if (jl_is_kind(decl)) + newrec->isleafsig = 0; // Type{} may have a higher priority than a kind else if (jl_is_type_type(decl)) newrec->isleafsig = 0; // Type{} may need special processing to compute the match else if (jl_is_vararg_type(decl)) newrec->isleafsig = 0; // makes iteration easier when the endpoints are the same else if (decl == (jl_value_t*)jl_any_type) newrec->isleafsig = 0; // Any needs to go in the general cache - else if (!jl_is_leaf_type(decl)) // anything else can go through the general subtyping test + else if (!jl_is_concrete_type(decl)) // anything else needs to go through the general subtyping test newrec->isleafsig = newrec->issimplesig = 0; } // TODO: assert that guardsigs == jl_emptysvec && simplesig == jl_nothing if isleafsig and optimize with that knowledge? diff --git a/stdlib/LinearAlgebra/src/adjtrans.jl b/stdlib/LinearAlgebra/src/adjtrans.jl index 7f462700e5601..a31f094d38134 100644 --- a/stdlib/LinearAlgebra/src/adjtrans.jl +++ b/stdlib/LinearAlgebra/src/adjtrans.jl @@ -1,6 +1,6 @@ # This file is a part of Julia. License is MIT: https://julialang.org/license -using Base: @pure, @propagate_inbounds, _return_type, _default_type, _isleaftype, @_inline_meta +using Base: @propagate_inbounds, _return_type, _default_type, @_inline_meta import Base: length, size, axes, IndexStyle, getindex, setindex!, parent, vec, convert, similar ### basic definitions (types, aliases, constructors, abstractarray interface, sundry similar) @@ -24,19 +24,22 @@ struct Transpose{T,S} <: AbstractMatrix{T} end function checkeltype_adjoint(::Type{ResultEltype}, ::Type{ParentEltype}) where {ResultEltype,ParentEltype} - ResultEltype === Base.promote_op(adjoint, ParentEltype) || error(string( - "Element type mismatch. Tried to create an `Adjoint{$ResultEltype}` ", - "from an object with eltype `$ParentEltype`, but the element type of ", - "the adjoint of an object with eltype `$ParentEltype` must be ", - "`$(Base.promote_op(adjoint, ParentEltype))`.")) + Expected = Base.promote_op(adjoint, ParentEltype) + ResultEltype === Expected || error(string( + "Element type mismatch. Tried to create an `Adjoint{", ResultEltype, "}` ", + "from an object with eltype `", ParentEltype, "`, but the element type of ", + "the adjoint of an object with eltype `", ParentEltype, "` must be ", + "`", Expected, "`.")) return nothing end -function checkeltype_transpose(::Type{ResultEltype}, ::Type{ParentEltype}) where {ResultEltype,ParentEltype} - ResultEltype === Base.promote_op(transpose, ParentEltype) || error(string( - "Element type mismatch. Tried to create a `Transpose{$ResultEltype}` ", - "from an object with eltype `$ParentEltype`, but the element type of ", - "the transpose of an object with eltype `$ParentEltype` must be ", - "`$(Base.promote_op(transpose, ParentEltype))`.")) + +function checkeltype_transpose(::Type{ResultEltype}, ::Type{ParentEltype}) where {ResultEltype, ParentEltype} + Expected = Base.promote_op(transpose, ParentEltype) + ResultEltype === Expected || error(string( + "Element type mismatch. Tried to create a `Transpose{", ResultEltype, "}` ", + "from an object with eltype `", ParentEltype, "`, but the element type of ", + "the transpose of an object with eltype `", ParentEltype, "` must be ", + "`", Expected, "`.")) return nothing end diff --git a/test/arrayops.jl b/test/arrayops.jl index 747a6af9e495a..793b173085061 100644 --- a/test/arrayops.jl +++ b/test/arrayops.jl @@ -1921,7 +1921,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 Base._isleaftype(Base.return_types(f, ())[1]) + @test isconcretetype(Base.return_types(f, ())[1]) end end diff --git a/test/channels.jl b/test/channels.jl index 10301bd3e804a..fc4591bf291f4 100644 --- a/test/channels.jl +++ b/test/channels.jl @@ -276,6 +276,7 @@ end let t = @schedule(nothing), ct = current_task(), testobject = "testobject" + # note: there is a low probability this test could fail, due to receiving network traffic simultaneously @test length(Base.Workqueue) == 1 @test Base.schedule_and_wait(ct, 8) == 8 @test isempty(Base.Workqueue) diff --git a/test/compiler/compiler.jl b/test/compiler/compiler.jl index 9845995c8ee59..df2c3944aca06 100644 --- a/test/compiler/compiler.jl +++ b/test/compiler/compiler.jl @@ -2,7 +2,6 @@ # tests for Core.Compiler correctness and precision import Core.Compiler: Const, Conditional, ⊑ -const isleaftype = Core.Compiler._isleaftype using Random @@ -210,8 +209,8 @@ end end let ast12474 = code_typed(f12474, Tuple{Float64}) - @test isleaftype(ast12474[1][2]) - @test all(isleaftype, ast12474[1][1].slottypes) + @test isconcretetype(ast12474[1][2]) + @test all(isconcretetype, ast12474[1][1].slottypes) end @@ -243,7 +242,7 @@ bar7810() = [Foo7810([(a,b) for a in 1:2]) for b in 3:4] # issue #11366 f11366(x::Type{Ref{T}}) where {T} = Ref{x} -@test !isleaftype(Base.return_types(f11366, (Any,))[1]) +@test !isconcretetype(Base.return_types(f11366, (Any,))[1]) let f(T) = Type{T} @@ -437,10 +436,10 @@ function is_typed_expr(e::Expr) return false end test_inferred_static(@nospecialize(other)) = true -test_inferred_static(slot::TypedSlot) = @test isleaftype(slot.typ) +test_inferred_static(slot::TypedSlot) = @test isconcretetype(slot.typ) function test_inferred_static(expr::Expr) if is_typed_expr(expr) - @test isleaftype(expr.typ) + @test isconcretetype(expr.typ) end for a in expr.args test_inferred_static(a) @@ -448,10 +447,10 @@ function test_inferred_static(expr::Expr) end function test_inferred_static(arrow::Pair) code, rt = arrow - @test isleaftype(rt) + @test isconcretetype(rt) @test code.inferred - @test all(x->isleaftype(x), code.slottypes) - @test all(x->isleaftype(x), code.ssavaluetypes) + @test all(isconcretetype, code.slottypes) + @test all(isconcretetype, code.ssavaluetypes) for e in code.code test_inferred_static(e) end @@ -652,7 +651,7 @@ function h20343() i = ntuple(i->n==i ? "" : 0, 3)::Union{Tuple{String,Int,Int},Tuple{Int,String,Int},Tuple{Int,Int,String}} f20343(i..., i...) end -@test all(t -> t<:Integer, Base.return_types(h20343, ())) +@test Base.return_types(h20343, ()) == [Union{Int8, Int}] function i20343() f20343([1,2,3]..., 4) end @@ -696,16 +695,6 @@ end f20267(x::T20267{T}, y::T) where (T) = f20267(Any[1][1], x.inds) @test Base.return_types(f20267, (Any, Any)) == Any[Union{}] -# issue #20615 -let A = 1:2, z = zip(A, A, A, A, A, A, A, A, A, A, A, A) - @test z isa Core.Compiler.limit_type_depth(typeof(z), 0) - @test start(z) == (1, (1, (1, (1, (1, (1, (1, (1, (1, (1, (1, 1))))))))))) -end -# introduce TypeVars in Unions in invariant position -let T = Val{Val{Val{Union{Int8,Int16,Int32,Int64,UInt8,UInt16,UInt32,UInt64}}}} - @test T <: Core.Compiler.limit_type_depth(T, 0) -end - # issue #20704 f20704(::Int) = 1 Base.@pure b20704(@nospecialize(x)) = f20704(x) @@ -855,17 +844,6 @@ for A in (1,) @test segfaultfunction_20847(tuplevec_20847) == nothing end -# issue #21848 -@test Core.Compiler.limit_type_depth(Ref{Complex{T} where T}, 0) == Ref -let T = Tuple{Tuple{Int64, Nothing}, - Tuple{Tuple{Int64, Nothing}, - Tuple{Int64, Tuple{Tuple{Int64, Nothing}, - Tuple{Tuple{Int64, Nothing}, Tuple{Int64, Tuple{Tuple{Int64, Nothing}, Tuple{Tuple, Tuple}}}}}}}} - @test Core.Compiler.limit_type_depth(T, 0) >: T - @test Core.Compiler.limit_type_depth(T, 1) >: T - @test Core.Compiler.limit_type_depth(T, 2) >: T -end - # Issue #20902, check that this doesn't error. @generated function test_20902() quote @@ -1150,7 +1128,7 @@ let isa_tfunc = Core.Compiler.T_FFUNC_VAL[ @test isa_tfunc(UnionAll, Const(Type{Array})) === Bool @test isa_tfunc(Union, Const(Union{Float32, Float64})) === Bool @test isa_tfunc(Union, Type{Union}) === Const(true) - @test isa_tfunc(typeof(Union{}), Const(Int)) === Bool # any result is ok + @test isa_tfunc(typeof(Union{}), Const(Int)) === Const(false) # any result is ok @test isa_tfunc(typeof(Union{}), Const(Union{})) === Const(false) @test isa_tfunc(typeof(Union{}), typeof(Union{})) === Const(false) @test isa_tfunc(typeof(Union{}), Union{}) === Const(false) # any result is ok @@ -1253,13 +1231,6 @@ let c(::Type{T}, x) where {T<:Array} = T, @test f() === Vector{Int} end -# issue #23786 -struct T23786{D<:Tuple{Vararg{Vector{T} where T}}, N} -end -let t = Tuple{Type{T23786{D, N} where N where D<:Tuple{Vararg{Array{T, 1} where T, N} where N}}} - @test Core.Compiler.limit_type_depth(t, 4) >: t -end - # issue #13183 _false13183 = false gg13183(x::X...) where {X} = (_false13183 ? gg13183(x, x) : 0) diff --git a/test/core.jl b/test/core.jl index b95915a8c40b6..81a5fcdb38c8b 100644 --- a/test/core.jl +++ b/test/core.jl @@ -175,7 +175,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 Base._isleaftype(elT2.body.types[1]) + @test Base.isconcretetype(elT2.body.types[1]) end # issue #3890 @@ -4511,7 +4511,7 @@ let a = Val{Val{TypeVar(:_, Int)}}, @test !isdefined(a, :instance) @test isdefined(b, :instance) - @test Base._isleaftype(b) + @test Base.isconcretetype(b) end # A return type widened to Type{Union{T,Nothing}} should not confuse @@ -5667,9 +5667,9 @@ let b3 = B23367[b][1] # copy b via array assignment addr(@nospecialize x) = ccall(:jl_value_ptr, Ptr{Cvoid}, (Any,), x) @test addr(b) == addr(b) - @test addr(b) != addr(b2) - @test addr(b) != addr(b3) - @test addr(b2) != addr(b3) + @test addr(b) == addr(b2) + @test addr(b) == addr(b3) + @test addr(b2) == addr(b3) @test b === b2 === b3 === b @test egal(b, b2) && egal(b2, b3) && egal(b3, b) @@ -5678,7 +5678,7 @@ let @test b.x === Int8(91) @test b.z === Int8(23) @test b.y === A23367((Int8(1), Int8(2), Int8(3), Int8(4), Int8(5), Int8(6), Int8(7))) - @test sizeof(b) == 12 + @test sizeof(b) == sizeof(Int) * 3 @test A23367(Int8(1)).x === Int8(1) @test A23367(Int8(0)).x === Int8(0) @test A23367(Int16(1)).x === Int16(1) diff --git a/test/reflection.jl b/test/reflection.jl index 1acd7ef38a1c3..8ff71db600c40 100644 --- a/test/reflection.jl +++ b/test/reflection.jl @@ -154,35 +154,30 @@ end # module WarnType @test isbits(Tuple{Vararg{Any, 0}}) # issue #16670 -@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}) +@test isconcretetype(Int) +@test isconcretetype(Vector{Int}) +@test isconcretetype(Tuple{Int, Vararg{Int, 2}}) +@test !isconcretetype(Tuple{Any}) +@test !isconcretetype(Tuple{Integer, Vararg{Int, 2}}) +@test !isconcretetype(Tuple{Int, Vararg{Int}}) +@test !isconcretetype(Type{Tuple{Integer, Vararg{Int}}}) +@test !isconcretetype(Type{Vector}) +@test !isconcretetype(Type{Int}) +@test !isconcretetype(Tuple{Type{Int}}) +@test isconcretetype(DataType) +@test isconcretetype(Union) +@test !isconcretetype(Union{}) +@test isconcretetype(Tuple{Union{}}) +@test !isconcretetype(Complex) +@test !isconcretetype(Complex.body) +@test !isconcretetype(AbstractArray{Int,1}) struct AlwaysHasLayout{T} x end -@test !isconcrete(AlwaysHasLayout) && !isconcrete(AlwaysHasLayout.body) -@test isconcrete(AlwaysHasLayout{Any}) -@test isconcrete(Ptr{Cvoid}) -@test !isconcrete(Ptr) && !isconcrete(Ptr.body) +@test !isconcretetype(AlwaysHasLayout) && !isconcretetype(AlwaysHasLayout.body) +@test isconcretetype(AlwaysHasLayout{Any}) +@test isconcretetype(Ptr{Cvoid}) +@test !isconcretetype(Ptr) && !isconcretetype(Ptr.body) # issue #10165 i10165(::Type) = 0 @@ -357,11 +352,7 @@ tlayout = TLayout(5,7,11) @test_throws BoundsError fieldname(NTuple{3, Int}, 0) @test_throws BoundsError fieldname(NTuple{3, Int}, 4) -import Base: isstructtype, datatype_alignment, return_types -@test !isstructtype(Union{}) -@test !isstructtype(Union{Int,Float64}) -@test !isstructtype(Int) -@test isstructtype(TLayout) +import Base: datatype_alignment, return_types @test datatype_alignment(UInt16) == 2 @test datatype_alignment(TLayout) == 4 let rts = return_types(TLayout) @@ -693,9 +684,29 @@ struct ReflectionExample{T<:AbstractFloat, N} x::Tuple{T, N} end -@test Base.isabstract(AbstractArray) -@test !Base.isabstract(ReflectionExample) -@test !Base.isabstract(Int) +@test !isabstracttype(Union{}) +@test !isabstracttype(Union{Int,Float64}) +@test isabstracttype(AbstractArray) +@test isabstracttype(AbstractSet{Int}) +@test !isabstracttype(ReflectionExample) +@test !isabstracttype(Int) +@test !isabstracttype(TLayout) + +@test !isprimitivetype(Union{}) +@test !isprimitivetype(Union{Int,Float64}) +@test !isprimitivetype(AbstractArray) +@test !isprimitivetype(AbstractSet{Int}) +@test !isprimitivetype(ReflectionExample) +@test isprimitivetype(Int) +@test !isprimitivetype(TLayout) + +@test !isstructtype(Union{}) +@test !isstructtype(Union{Int,Float64}) +@test !isstructtype(AbstractArray) +@test !isstructtype(AbstractSet{Int}) +@test isstructtype(ReflectionExample) +@test !isstructtype(Int) +@test isstructtype(TLayout) @test Base.parameter_upper_bound(ReflectionExample, 1) === AbstractFloat @test Base.parameter_upper_bound(ReflectionExample, 2) === Any diff --git a/test/subtype.jl b/test/subtype.jl index f6662353fc785..69150ee6fb34c 100644 --- a/test/subtype.jl +++ b/test/subtype.jl @@ -639,7 +639,6 @@ macro testintersect(a, b, result) else cmp = :(==) end - cmp = esc(cmp) a = esc(a) b = esc(b) result = esc(result) @@ -1068,7 +1067,7 @@ let a = Tuple{Float64,T7} where T7, end let a = Tuple{T1,T1} where T1, b = Tuple{Val{S2},S6} where S2 where S6 - @test_broken typeintersect(a, b) == typeintersect(b, a) + @test typeintersect(a, b) == typeintersect(b, a) end let a = Val{Tuple{T1,T1}} where T1, b = Val{Tuple{Val{S2},S6}} where S2 where S6