Skip to content

Commit

Permalink
fix type-predicate queries
Browse files Browse the repository at this point in the history
expand the set of them to be more "complete",
and document them more fully
  • Loading branch information
vtjnash committed Jan 19, 2018
1 parent c39f18b commit 88a4db2
Show file tree
Hide file tree
Showing 46 changed files with 829 additions and 877 deletions.
8 changes: 4 additions & 4 deletions NEWS.md
Expand Up @@ -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]).

Expand Down
2 changes: 1 addition & 1 deletion base/abstractarray.jl
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion base/broadcast.jl
Expand Up @@ -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...)
Expand Down
45 changes: 22 additions & 23 deletions base/compiler/abstractinterpretation.jl
Expand Up @@ -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
Expand Down Expand Up @@ -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)
Expand All @@ -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
Expand Down Expand Up @@ -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
Expand Down
2 changes: 0 additions & 2 deletions base/compiler/compiler.jl
Expand Up @@ -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")

Expand Down
2 changes: 1 addition & 1 deletion base/compiler/inferencestate.jl
Expand Up @@ -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
Expand Down
62 changes: 33 additions & 29 deletions base/compiler/optimize.jl
Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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) &&
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -2754,15 +2758,15 @@ 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)
for i in 1:ndefs
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
Expand All @@ -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
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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)
Expand Down
4 changes: 1 addition & 3 deletions base/compiler/params.jl
Expand Up @@ -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
Expand All @@ -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

0 comments on commit 88a4db2

Please sign in to comment.