Skip to content

Commit 02baf98

Browse files
committed
fix type-predicate queries
expand the set of them to be more "complete", and document them more fully
1 parent 42b2ec7 commit 02baf98

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

47 files changed

+850
-892
lines changed

NEWS.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -819,10 +819,10 @@ Deprecated or removed
819819
been deprecated due to inconsistency with linear algebra. Use `.+` and `.-` for these operations
820820
instead ([#22880], [#22932]).
821821

822-
* `isleaftype` is deprecated in favor of a simpler predicate `isconcrete`. Concrete types are
823-
those that might equal `typeof(x)` for some `x`; `isleaftype` includes some types for which
824-
this is not true. If you are certain you need the old behavior, it is temporarily available
825-
as `Base._isleaftype` ([#17086]).
822+
* `isleaftype` is deprecated in favor of the simpler predicates `isconcretetype` and `isdispatchtuple`.
823+
Concrete types are those that might equal `typeof(x)` for some `x`;
824+
`isleaftype` included some types for which this is not true. Those are now categorized more precisely
825+
as "dispatch tuple types" and "!has_free_typevars" (not exported). ([#17086], [#25496])
826826

827827
* `contains(eq, itr, item)` is deprecated in favor of `any` with a predicate ([#23716]).
828828

base/abstractarray.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1985,7 +1985,7 @@ function hash(a::AbstractArray{T}, h::UInt) where T
19851985
# to a range with more than two elements because more extreme values
19861986
# cannot be represented. We must still hash the two first values as a
19871987
# range since they can always be considered as such (in a wider type)
1988-
if isconcrete(T)
1988+
if isconcretetype(T)
19891989
try
19901990
step = x2 - x1
19911991
catch err

base/broadcast.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -622,7 +622,7 @@ julia> string.(("one","two","three","four"), ": ", 1:4)
622622
const NonleafHandlingTypes = Union{DefaultArrayStyle,ArrayConflict,VectorStyle,MatrixStyle}
623623

624624
@inline function broadcast(f, s::NonleafHandlingTypes, ::Type{ElType}, inds::Indices, As...) where ElType
625-
if !Base._isleaftype(ElType)
625+
if !Base.isconcretetype(ElType)
626626
return broadcast_nonleaf(f, s, ElType, inds, As...)
627627
end
628628
dest = broadcast_similar(f, s, ElType, inds, As...)

base/compiler/abstractinterpretation.jl

Lines changed: 22 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -118,12 +118,10 @@ function abstract_call_method_with_const_args(@nospecialize(f), argtypes::Vector
118118
haveconst = false
119119
for i in 1:nargs
120120
a = argtypes[i]
121-
if isa(a, Const) && !isdefined(typeof(a.val), :instance)
122-
if !isleaftype(a.val) # alternately: !isa(a.val, DataType) || !isconstType(Type{a.val})
123-
# have new information from argtypes that wasn't available from the signature
124-
haveconst = true
125-
break
126-
end
121+
if isa(a, Const) && !isdefined(typeof(a.val), :instance) && !(isa(a.val, Type) && issingletontype(a.val))
122+
# have new information from argtypes that wasn't available from the signature
123+
haveconst = true
124+
break
127125
end
128126
end
129127
haveconst || return Any
@@ -378,11 +376,13 @@ end
378376

379377
# do apply(af, fargs...), where af is a function value
380378
function abstract_apply(@nospecialize(aft), fargs::Vector{Any}, aargtypes::Vector{Any}, vtypes::VarTable, sv::InferenceState)
381-
if !isa(aft, Const) && !isconstType(aft)
382-
if !(isleaftype(aft) || aft <: Type) || (aft <: Builtin) || (aft <: IntrinsicFunction)
379+
if !isa(aft, Const) && (!isType(aft) || has_free_typevars(aft))
380+
if !isconcretetype(aft) || (aft <: Builtin)
381+
# non-constant function of unknown type: bail now,
382+
# since it seems unlikely that abstract_call will be able to do any better after splitting
383+
# this also ensures we don't call abstract_call_gf_by_type below on an IntrinsicFunction or Builtin
383384
return Any
384385
end
385-
# non-constant function, but type is known
386386
end
387387
res = Union{}
388388
nargs = length(fargs)
@@ -394,7 +394,7 @@ function abstract_apply(@nospecialize(aft), fargs::Vector{Any}, aargtypes::Vecto
394394
for ti in (splitunions ? uniontypes(aargtypes[i]) : Any[aargtypes[i]])
395395
cti = precise_container_type(fargs[i], ti, vtypes, sv)
396396
for ct in ctypes
397-
if !isempty(ct) && isvarargtype(ct[end])
397+
if isvarargtype(ct[end])
398398
tail = tuple_tail_elem(unwrapva(ct[end]), cti)
399399
push!(ctypes´, push!(ct[1:(end - 1)], tail))
400400
else
@@ -672,23 +672,22 @@ function abstract_eval_call(e::Expr, vtypes::VarTable, sv::InferenceState)
672672
ft = argtypes[1]
673673
if isa(ft, Const)
674674
f = ft.val
675+
elseif isconstType(ft)
676+
f = ft.parameters[1]
677+
elseif isa(ft, DataType) && isdefined(ft, :instance)
678+
f = ft.instance
675679
else
676-
if isType(ft) && isleaftype(ft.parameters[1])
677-
f = ft.parameters[1]
678-
elseif isleaftype(ft) && isdefined(ft, :instance)
679-
f = ft.instance
680-
else
681-
for i = 2:(length(argtypes)-1)
682-
if isvarargtype(argtypes[i])
683-
return Any
684-
end
685-
end
686-
# non-constant function, but type is known
687-
if (isleaftype(ft) || ft <: Type) && !(ft <: Builtin) && !(ft <: IntrinsicFunction)
688-
return abstract_call_gf_by_type(nothing, argtypes, argtypes_to_type(argtypes), sv)
680+
for i = 2:(length(argtypes) - 1)
681+
if isvarargtype(argtypes[i])
682+
return Any
689683
end
684+
end
685+
# non-constant function, but the number of arguments is known
686+
# and the ft is not a Builtin or IntrinsicFunction
687+
if typeintersect(widenconst(ft), Builtin) != Union{}
690688
return Any
691689
end
690+
return abstract_call_gf_by_type(nothing, argtypes, argtypes_to_type(argtypes), sv)
692691
end
693692
return abstract_call(f, e.args, argtypes, vtypes, sv)
694693
end

base/compiler/compiler.jl

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -76,8 +76,6 @@ include("docs/core.jl")
7676
inlining_enabled() = (JLOptions().can_inline == 1)
7777
coverage_enabled() = (JLOptions().code_coverage != 0)
7878

79-
const isleaftype = _isleaftype
80-
8179
include("compiler/utilities.jl")
8280
include("compiler/validation.jl")
8381

base/compiler/inferencestate.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ mutable struct InferenceState
8484
at = (i > nargs) ? Bottom : argtypes[i]
8585
if !toplevel && linfo.def.isva && i == nargs
8686
if !(at == Tuple) # would just be a no-op
87-
vararg_type_container = limit_tuple_depth(params, unwrap_unionall(at)) # TODO: should be limiting tuple depth much earlier than here
87+
vararg_type_container = unwrap_unionall(at)
8888
vararg_type = tuple_tfunc(vararg_type_container) # returns a Const object, if applicable
8989
at = rewrap(vararg_type, linfo.specTypes)
9090
end

base/compiler/optimize.jl

Lines changed: 33 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -816,8 +816,8 @@ function effect_free(@nospecialize(e), src::CodeInfo, mod::Module, allow_volatil
816816
return true
817817
end
818818
if head === :static_parameter
819-
# if we aren't certain about the type, it might be an UndefVarError at runtime
820-
return (isa(e.typ, DataType) && isleaftype(e.typ)) || isa(e.typ, Const)
819+
# if we aren't certain enough about the type, it might be an UndefVarError at runtime
820+
return isa(e.typ, Const) || issingletontype(widenconst(e.typ))
821821
end
822822
if e.typ === Bottom
823823
return false
@@ -830,17 +830,17 @@ function effect_free(@nospecialize(e), src::CodeInfo, mod::Module, allow_volatil
830830
return false
831831
elseif is_known_call(e, getfield, src, mod)
832832
nargs = length(ea)
833-
(2 < nargs < 5) || return false
833+
(3 < nargs < 4) || return false
834834
et = exprtype(e, src, mod)
835-
if !isa(et, Const) && !(isType(et) && isleaftype(et))
835+
# TODO: check ninitialized
836+
if !isa(et, Const) && !isconstType(et)
836837
# first argument must be immutable to ensure e is affect_free
837838
a = ea[2]
838-
typ = widenconst(exprtype(a, src, mod))
839-
if isconstType(typ)
840-
if Const(:uid) exprtype(ea[3], src, mod)
841-
return false # DataType uid field can change
842-
end
843-
elseif typ !== SimpleVector && (!isa(typ, DataType) || typ.mutable || typ.abstract)
839+
typ = unwrap_unionall(widenconst(exprtype(a, src, mod)))
840+
if isType(typ)
841+
# all fields of subtypes of Type are effect-free
842+
# (including the non-inferrable uid field)
843+
elseif !isa(typ, DataType) || typ.abstract || (typ.mutable && length(typ.types) > 0)
844844
return false
845845
end
846846
end
@@ -863,7 +863,8 @@ function effect_free(@nospecialize(e), src::CodeInfo, mod::Module, allow_volatil
863863
# `Expr(:new)` of unknown type could raise arbitrary TypeError.
864864
typ, isexact = instanceof_tfunc(typ)
865865
isexact || return false
866-
(isleaftype(typ) && !iskindtype(typ)) || return false
866+
isconcretetype(typ) || return false
867+
!iskindtype(typ) || return false
867868
typ = typ::DataType
868869
if !allow_volatile && typ.mutable
869870
return false
@@ -1075,11 +1076,11 @@ function inlineable(@nospecialize(f), @nospecialize(ft), e::Expr, atypes::Vector
10751076
sv::OptimizationState)
10761077
argexprs = e.args
10771078

1078-
if (f === typeassert || ft typeof(typeassert)) && length(atypes)==3
1079+
if (f === typeassert || ft typeof(typeassert)) && length(atypes) == 3
10791080
# typeassert(x::S, T) => x, when S<:T
10801081
a3 = atypes[3]
1081-
if (isType(a3) && isleaftype(a3) && atypes[2] a3.parameters[1]) ||
1082-
(isa(a3,Const) && isa(a3.val,Type) && atypes[2] a3.val)
1082+
if (isType(a3) && !has_free_typevars(a3) && atypes[2] a3.parameters[1]) ||
1083+
(isa(a3, Const) && isa(a3.val, Type) && atypes[2] a3.val)
10831084
return (argexprs[2], ())
10841085
end
10851086
end
@@ -1108,7 +1109,9 @@ function inlineable(@nospecialize(f), @nospecialize(ft), e::Expr, atypes::Vector
11081109
if f === Core.invoke && length(atypes) >= 3
11091110
ft = widenconst(atypes[2])
11101111
invoke_tt = widenconst(atypes[3])
1111-
if !isleaftype(ft) || !isleaftype(invoke_tt) || !isType(invoke_tt)
1112+
if !(isconcretetype(ft) || ft <: Type) || !isType(invoke_tt) ||
1113+
has_free_typevars(invoke_tt) || has_free_typevars(ft) || (ft <: Builtin)
1114+
# TODO: this is really aggressive at preventing inlining of closures. maybe drop `isconcretetype` requirement?
11121115
return NOT_FOUND
11131116
end
11141117
if !(isa(invoke_tt.parameters[1], Type) &&
@@ -1271,12 +1274,10 @@ function inlineable(@nospecialize(f), @nospecialize(ft), e::Expr, atypes::Vector
12711274
haveconst = false
12721275
for i in 1:length(atypes)
12731276
a = atypes[i]
1274-
if isa(a, Const) && !isdefined(typeof(a.val), :instance)
1275-
if !isleaftype(a.val) # alternately: !isa(a.val, DataType) || !isconstType(Type{a.val})
1276-
# have new information from argtypes that wasn't available from the signature
1277-
haveconst = true
1278-
break
1279-
end
1277+
if isa(a, Const) && !isdefined(typeof(a.val), :instance) && !(isa(a.val, Type) && issingletontype(a.val))
1278+
# have new information from argtypes that wasn't available from the signature
1279+
haveconst = true
1280+
break
12801281
end
12811282
end
12821283
if haveconst
@@ -1538,8 +1539,9 @@ end
15381539

15391540
# saturating sum (inputs are nonnegative), prevents overflow with typemax(Int) below
15401541
plus_saturate(x, y) = max(x, y, x+y)
1542+
15411543
# known return type
1542-
isknowntype(T) = (T == Union{}) || isleaftype(T)
1544+
isknowntype(@nospecialize T) = (T == Union{}) || isconcretetype(T)
15431545

15441546
function statement_cost(ex::Expr, line::Int, src::CodeInfo, mod::Module, params::Params)
15451547
head = ex.head
@@ -1770,7 +1772,8 @@ function inline_call(e::Expr, sv::OptimizationState, stmts::Vector{Any}, boundsc
17701772
ft = Bool
17711773
else
17721774
f = nothing
1773-
if !( isleaftype(ft) || ft<:Type )
1775+
if !(isconcretetype(ft) || (widenconst(ft) <: Type)) || has_free_typevars(ft)
1776+
# TODO: this is really aggressive at preventing inlining of closures. maybe drop `isconcretetype` requirement?
17741777
return e
17751778
end
17761779
end
@@ -1927,7 +1930,8 @@ function inline_call(e::Expr, sv::OptimizationState, stmts::Vector{Any}, boundsc
19271930
ft = Bool
19281931
else
19291932
f = nothing
1930-
if !( isleaftype(ft) || ft<:Type )
1933+
if !(isconcretetype(ft) || (widenconst(ft) <: Type)) || has_free_typevars(ft)
1934+
# TODO: this is really aggressive at preventing inlining of closures. maybe drop `isconcretetype` requirement?
19311935
return e
19321936
end
19331937
end
@@ -2745,15 +2749,15 @@ end
27452749

27462750
function split_disjoint_assign!(ctx::AllocOptContext, info, key)
27472751
key.second && return false
2748-
isleaftype(ctx.sv.src.slottypes[key.first]) && return false
2752+
isdispatchelem(widenconst(ctx.sv.src.slottypes[key.first])) && return false # no splitting can be necessary
27492753
alltypes = ObjectIdDict()
27502754
ndefs = length(info.defs)
27512755
deftypes = Vector{Any}(uninitialized, ndefs)
27522756
for i in 1:ndefs
27532757
def = info.defs[i]
27542758
defex = (def.assign::Expr).args[2]
27552759
rhstyp = widenconst(exprtype(defex, ctx.sv.src, ctx.sv.mod))
2756-
isleaftype(rhstyp) || return false
2760+
isdispatchelem(rhstyp) || return false
27572761
alltypes[rhstyp] = nothing
27582762
deftypes[i] = rhstyp
27592763
end
@@ -2763,7 +2767,7 @@ function split_disjoint_assign!(ctx::AllocOptContext, info, key)
27632767
slot = usex.args[use.exidx]
27642768
if isa(slot, TypedSlot)
27652769
usetyp = widenconst(slot.typ)
2766-
if isleaftype(usetyp)
2770+
if isdispatchelem(usetyp)
27672771
alltypes[usetyp] = nothing
27682772
continue
27692773
end
@@ -2818,7 +2822,7 @@ function split_disjoint_assign!(ctx::AllocOptContext, info, key)
28182822
slot = usex.args[use.exidx]
28192823
if isa(slot, TypedSlot)
28202824
usetyp = widenconst(slot.typ)
2821-
if isleaftype(usetyp)
2825+
if isdispatchelem(usetyp)
28222826
usetyp = widenconst(slot.typ)
28232827
new_slot = alltypes[usetyp]
28242828
if !isa(new_slot, SlotNumber)
@@ -2988,7 +2992,7 @@ function split_struct_alloc!(ctx::AllocOptContext, info, key)
29882992
elseif defex.head === :new
29892993
typ = widenconst(exprtype(defex, ctx.sv.src, ctx.sv.mod))
29902994
# typ <: Tuple shouldn't happen but just in case someone generated invalid AST
2991-
if !isa(typ, DataType) || !isleaftype(typ) || typ <: Tuple
2995+
if !isa(typ, DataType) || !isdispatchelem(typ) || typ <: Tuple
29922996
return false
29932997
end
29942998
si = structinfo_new(ctx, defex, typ)

base/compiler/params.jl

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,6 @@ struct Params
2828

2929
# parameters limiting large types
3030
MAX_TUPLETYPE_LEN::Int
31-
MAX_TUPLE_DEPTH::Int
3231

3332
# when attempting to inlining _apply, abort the optimization if the tuple
3433
# contains more than this many elements
@@ -42,13 +41,12 @@ struct Params
4241
inline_tupleret_bonus::Int = 400,
4342
max_methods::Int = 4,
4443
tupletype_len::Int = 15,
45-
tuple_depth::Int = 4,
4644
tuple_splat::Int = 16,
4745
union_splitting::Int = 4,
4846
apply_union_enum::Int = 8)
4947
return new(Vector{InferenceResult}(),
5048
world, inlining, true, false, inline_cost_threshold, inline_nonleaf_penalty,
5149
inline_tupleret_bonus, max_methods, union_splitting, apply_union_enum,
52-
tupletype_len, tuple_depth, tuple_splat)
50+
tupletype_len, tuple_splat)
5351
end
5452
end

0 commit comments

Comments
 (0)