Skip to content

Commit c4565ae

Browse files
committed
Rename dispatchtuple to indivisible_type
This is hopefully a more intuitive (type-semantic) notion of what a "dispatch tuple" is. Ideally, it makes two key properties more obvious: 1. indivisible types are either type-equal or disjoint with each other (T == U or T != U) - they are never partially overlapping 2. these signatures are always compilable since they are "maximally specific" This new definition should be mostly equivalent to the existing one for Tuples, and it extends the predicate to be be analogously defined over other DataTypes (e.g. `Int` / `Vector{Any}`, which are also indivisible)
1 parent 748775b commit c4565ae

File tree

17 files changed

+111
-61
lines changed

17 files changed

+111
-61
lines changed

Compiler/src/abstractinterpretation.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,7 @@ function abstract_call_gf_by_type(interp::AbstractInterpreter, @nospecialize(fun
129129
local napplicable = length(applicable)
130130
for i = 1:napplicable
131131
local sig = applicable[i].match.spec_types
132-
if !isdispatchtuple(sig)
132+
if !isindivisibletype(sig)
133133
# only infer fully concrete call sites in top-level expressions (ignoring even isa_compileable_sig matches)
134134
add_remark!(interp, sv, "Refusing to infer non-concrete call site in top-level expression")
135135
return Future(CallMeta(Any, Any, Effects(), NoCallInfo()))

Compiler/src/ssair/inlining.jl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1394,8 +1394,8 @@ function compute_inlining_cases(@nospecialize(info::CallInfo), flag::UInt32, sig
13941394
# We will emit an inline MethodError in this case, but that info already came inference, so we must already have the uncovered edge for it
13951395
end
13961396
elseif !isempty(cases)
1397-
# if we've not seen all candidates, union split is valid only for dispatch tuples
1398-
filter!(case::InliningCase->isdispatchtuple(case.sig), cases)
1397+
# if we've not seen all candidates, union split is valid only for indivisible (dispatch) tuples
1398+
filter!(case::InliningCase->isindivisibletype(case.sig), cases)
13991399
end
14001400
return cases, handled_all_cases, fully_covered, joint_effects
14011401
end

Compiler/src/typeinfer.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -361,7 +361,7 @@ function inline_cost_model(interp::AbstractInterpreter, result::InferenceResult,
361361
return MAX_INLINE_COST
362362
end
363363

364-
if declared_inline && isdispatchtuple(specTypes)
364+
if declared_inline && isindivisibletype(specTypes)
365365
# obey @inline declaration if a dispatch barrier would not help
366366
return MIN_INLINE_COST
367367
else

Compiler/test/inline.jl

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -816,27 +816,27 @@ let src = code_typed((Union{Tuple{Int,Int,Int}, Vector{Int}},)) do xs
816816
@test count(isinvoke(:g42840), src.code) == 1
817817
end
818818

819-
# test single, non-dispatchtuple callsite inlining
819+
# test single, divisible (non-dispatchtuple) callsite inlining
820820

821-
@constprop :none @inline test_single_nondispatchtuple(@nospecialize(t)) =
821+
@constprop :none @inline test_single_divisible_type(@nospecialize(t)) =
822822
isa(t, DataType) && t.name === Type.body.name
823823
let
824824
src = code_typed1((Any,)) do x
825-
test_single_nondispatchtuple(x)
825+
test_single_divisible_type(x)
826826
end
827827
@test all(src.code) do @nospecialize x
828-
!(isinvoke(:test_single_nondispatchtuple, x) || iscall((src, test_single_nondispatchtuple), x))
828+
!(isinvoke(:test_single_divisible_type, x) || iscall((src, test_single_divisible_type), x))
829829
end
830830
end
831831

832-
@constprop :aggressive @inline test_single_nondispatchtuple(c, @nospecialize(t)) =
832+
@constprop :aggressive @inline test_single_divisible_type(c, @nospecialize(t)) =
833833
c && isa(t, DataType) && t.name === Type.body.name
834834
let
835835
src = code_typed1((Any,)) do x
836-
test_single_nondispatchtuple(true, x)
836+
test_single_divisible_type(true, x)
837837
end
838838
@test all(src.code) do @nospecialize(x)
839-
!(isinvoke(:test_single_nondispatchtuple, x) || iscall((src, test_single_nondispatchtuple), x))
839+
!(isinvoke(:test_single_divisible_type, x) || iscall((src, test_single_divisible_type), x))
840840
end
841841
end
842842

@@ -856,7 +856,7 @@ let
856856
@test invoke(Any[10]) === false
857857
end
858858

859-
# test union-split, non-dispatchtuple callsite inlining
859+
# test union-split, divisible (non-dispatchtuple) callsite inlining
860860

861861
@constprop :none @noinline abstract_unionsplit(@nospecialize x::Any) = Base.inferencebarrier(:Any)
862862
@constprop :none @noinline abstract_unionsplit(@nospecialize x::Number) = Base.inferencebarrier(:Number)

base/exports.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -818,7 +818,7 @@ export
818818
isprimitivetype,
819819
isstructtype,
820820
isconcretetype,
821-
isdispatchtuple,
821+
isindivisibletype,
822822
oftype,
823823
promote,
824824
promote_rule,

base/runtime_internals.jl

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -856,7 +856,18 @@ meaning it could appear as a type signature in dispatch
856856
and has no subtypes (or supertypes) which could appear in a call.
857857
If `T` is not a type, then return `false`.
858858
"""
859-
isdispatchtuple(@nospecialize(t)) = (@_total_meta; isa(t, DataType) && (t.flags & 0x0004) == 0x0004)
859+
isdispatchtuple(@nospecialize(t)) = (@_total_meta; isa(t, DataType) && t.name === Tuple.name && (t.flags & 0x0004) == 0x0004)
860+
861+
"""
862+
isindivisibletype(T)
863+
864+
Determine whether type `T` is an indivisible type, meaning that `T′ <: T`
865+
implies `T′ == T` for any inhabited T′. This is a conservative under-
866+
approximation, so it may sometimes return `false` even when T is indivisible.
867+
868+
If `T` is not a type, then return `false`.
869+
"""
870+
isindivisibletype(@nospecialize(t)) = (@_total_meta; isa(t, DataType) && (t.flags & 0x0004) == 0x0004)
860871

861872
datatype_ismutationfree(dt::DataType) = (@_total_meta; (dt.flags & 0x0100) == 0x0100)
862873

@@ -905,7 +916,7 @@ isconcretedispatch(@nospecialize t) = isconcretetype(t) && !iskindtype(t)
905916

906917
using Core: has_free_typevars
907918

908-
# equivalent to isa(v, Type) && isdispatchtuple(Tuple{v}) || v === Union{}
919+
# equivalent to isa(v, Type) && isindivisibletype(Tuple{v}) || v === Union{}
909920
# and is thus perhaps most similar to the old (pre-1.0) `isconcretetype` query
910921
function isdispatchelem(@nospecialize v)
911922
return (v === Bottom) || (v === typeof(Bottom)) || isconcretedispatch(v) ||
@@ -1521,7 +1532,7 @@ function may_invoke_generator(mi::MethodInstance)
15211532
end
15221533
function may_invoke_generator(method::Method, @nospecialize(atype), sparams::SimpleVector)
15231534
# If we have complete information, we may always call the generator
1524-
isdispatchtuple(atype) && return true
1535+
isindivisibletype(atype) && return true
15251536

15261537
# We don't have complete information, but it is possible that the generator
15271538
# syntactically doesn't make use of the information we don't have. Check

base/staticdata.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ get_require_world() = unsafe_load(cglobal(:jl_require_world, UInt))
7575

7676
function gen_staged_sig(def::Method, mi::MethodInstance)
7777
isdefined(def, :generator) || return nothing
78-
isdispatchtuple(mi.specTypes) || return nothing
78+
isindivisibletype(mi.specTypes) || return nothing
7979
gen = Core.Typeof(def.generator)
8080
return Tuple{gen, UInt, Method, Vararg}
8181
## more precise method lookup, but more costly and likely not actually better?

doc/src/base/base.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -191,7 +191,7 @@ Base.typeintersect
191191
Base.promote_type
192192
Base.promote_rule
193193
Base.promote_typejoin
194-
Base.isdispatchtuple
194+
Base.isindivisibletype
195195
```
196196

197197
### Declared structure

src/datatype.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ jl_datatype_t *jl_new_uninitialized_datatype(void)
100100
jl_set_typetagof(t, jl_datatype_tag, 0);
101101
t->hash = 0;
102102
t->hasfreetypevars = 0;
103-
t->isdispatchtuple = 0;
103+
t->isindivisibletype = 0;
104104
t->isbitstype = 0;
105105
t->isprimitivetype = 0;
106106
t->zeroinit = 0;

src/gf.c

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1014,7 +1014,7 @@ static void jl_compilation_sig(
10141014
}
10151015
}
10161016
else if (jl_is_kind(elt)) {
1017-
// not triggered for isdispatchtuple(tt), this attempts to handle
1017+
// not triggered for isindivisibletype(tt), this attempts to handle
10181018
// some cases of adapting a random signature into a compilation signature
10191019
// if we get a kind, where we don't expect to accept one, widen it to something more expected (Type{T})
10201020
if (!(jl_subtype(elt, type_i) && !jl_subtype((jl_value_t*)jl_type_type, type_i))) {
@@ -1044,11 +1044,11 @@ static void jl_compilation_sig(
10441044
}
10451045

10461046
if (jl_types_equal(elt, (jl_value_t*)jl_type_type)) { // elt == Type{T} where T
1047-
// not triggered for isdispatchtuple(tt), this attempts to handle
1047+
// not triggered for isindivisibletype(tt), this attempts to handle
10481048
// some cases of adapting a random signature into a compilation signature
10491049
}
10501050
else if (!jl_is_datatype(elt) && jl_subtype(elt, (jl_value_t*)jl_type_type)) { // elt <: Type{T}
1051-
// not triggered for isdispatchtuple(tt), this attempts to handle
1051+
// not triggered for isindivisibletype(tt), this attempts to handle
10521052
// some cases of adapting a random signature into a compilation signature
10531053
if (!*newparams) *newparams = jl_svec_copy(tt->parameters);
10541054
jl_svecset(*newparams, i, jl_type_type);
@@ -1197,7 +1197,7 @@ JL_DLLEXPORT int jl_isa_compileable_sig(
11971197
if (definition->generator) {
11981198
// staged functions aren't optimized
11991199
// so assume the caller was intelligent about calling us
1200-
return (definition->isva ? np >= nargs - 1 : np == nargs) && type->isdispatchtuple;
1200+
return (definition->isva ? np >= nargs - 1 : np == nargs) && type->isindivisibletype;
12011201
}
12021202

12031203
// for varargs methods, only specialize up to max_args (>= nargs + 1).
@@ -1636,9 +1636,9 @@ static jl_method_instance_t *jl_mt_assoc_by_type(jl_methtable_t *mt JL_PROPAGATE
16361636
jl_method_match_t *matc = NULL;
16371637
JL_GC_PUSH2(&tt, &matc);
16381638
JL_LOCK(&mt->writelock);
1639-
assert(tt->isdispatchtuple || tt->hasfreetypevars);
1639+
assert(tt->isindivisibletype || tt->hasfreetypevars);
16401640
jl_method_instance_t *mi = NULL;
1641-
if (tt->isdispatchtuple) {
1641+
if (tt->isindivisibletype) {
16421642
jl_genericmemory_t *leafcache = jl_atomic_load_relaxed(&mt->leafcache);
16431643
jl_typemap_entry_t *entry = lookup_leafcache(leafcache, (jl_value_t*)tt, world);
16441644
if (entry)
@@ -3132,7 +3132,7 @@ JL_DLLEXPORT jl_value_t *jl_normalize_to_compilable_sig(jl_methtable_t *mt, jl_t
31323132
jl_methtable_t *kwmt = mt == jl_kwcall_mt ? jl_kwmethod_table_for(m->sig) : mt;
31333133
intptr_t max_varargs = get_max_varargs(m, kwmt, mt, NULL);
31343134
jl_compilation_sig(ti, env, m, max_varargs, &newparams);
3135-
int is_compileable = ((jl_datatype_t*)ti)->isdispatchtuple;
3135+
int is_compileable = ((jl_datatype_t*)ti)->isindivisibletype;
31363136
if (newparams) {
31373137
tt = (jl_datatype_t*)jl_apply_tuple_type(newparams, 1);
31383138
if (!is_compileable) {
@@ -3184,7 +3184,7 @@ JL_DLLEXPORT jl_method_instance_t *jl_method_match_to_mi(jl_method_match_t *matc
31843184
assert(mt != NULL);
31853185
if ((jl_value_t*)mt != jl_nothing) {
31863186
// get the specialization, possibly also caching it
3187-
if (mt_cache && ((jl_datatype_t*)ti)->isdispatchtuple) {
3187+
if (mt_cache && ((jl_datatype_t*)ti)->isindivisibletype) {
31883188
// Since we also use this presence in the cache
31893189
// to trigger compilation when producing `.ji` files,
31903190
// inject it there now if we think it will be
@@ -3334,7 +3334,7 @@ JL_DLLEXPORT void jl_compile_method_instance(jl_method_instance_t *mi, jl_tuplet
33343334
// In addition to full compilation of the compilation-signature, if `types` is more specific (e.g. due to nospecialize),
33353335
// also run inference now on the original `types`, since that may help us guide inference to find
33363336
// additional useful methods that should be compiled
3337-
//ALT: if (jl_is_datatype(types) && ((jl_datatype_t*)types)->isdispatchtuple && !jl_egal(mi->specTypes, types))
3337+
//ALT: if (jl_is_datatype(types) && ((jl_datatype_t*)types)->isindivisibletype && !jl_egal(mi->specTypes, types))
33383338
//ALT: if (jl_subtype(types, mi->specTypes))
33393339
if (types && !jl_subtype(mi->specTypes, (jl_value_t*)types)) {
33403340
jl_svec_t *tpenv2 = jl_emptysvec;
@@ -3882,7 +3882,7 @@ static int ml_matches_visitor(jl_typemap_entry_t *ml, struct typemap_intersectio
38823882
return 1;
38833883
}
38843884
jl_method_t *meth = ml->func.method;
3885-
if (closure->lim >= 0 && jl_is_dispatch_tupletype(meth->sig)) {
3885+
if (closure->lim >= 0 && jl_is_indivisible_type(meth->sig)) {
38863886
int replaced = 0;
38873887
// check if this is replaced, in which case we need to avoid double-counting it against the limit
38883888
// (although it will figure out later which one to keep and return)
@@ -4233,7 +4233,7 @@ static jl_value_t *ml_matches(jl_methtable_t *mt,
42334233

42344234
if (mt) {
42354235
// check the leaf cache if this type can be in there
4236-
if (((jl_datatype_t*)unw)->isdispatchtuple) {
4236+
if (((jl_datatype_t*)unw)->isindivisibletype) {
42374237
jl_genericmemory_t *leafcache = jl_atomic_load_relaxed(&mt->leafcache);
42384238
jl_typemap_entry_t *entry = lookup_leafcache(leafcache, (jl_value_t*)type, world);
42394239
if (entry) {
@@ -4266,12 +4266,12 @@ static jl_value_t *ml_matches(jl_methtable_t *mt,
42664266
}
42674267
}
42684268
// then check the full cache if it seems profitable
4269-
if (((jl_datatype_t*)unw)->isdispatchtuple) {
4269+
if (((jl_datatype_t*)unw)->isindivisibletype) {
42704270
jl_typemap_entry_t *entry = jl_typemap_assoc_by_type(jl_atomic_load_relaxed(&mt->cache), &search, jl_cachearg_offset(mt), /*subtype*/1);
4271-
if (entry && (((jl_datatype_t*)unw)->isdispatchtuple || entry->guardsigs == jl_emptysvec)) {
4271+
if (entry && (((jl_datatype_t*)unw)->isindivisibletype || entry->guardsigs == jl_emptysvec)) {
42724272
jl_method_instance_t *mi = entry->func.linfo;
42734273
jl_method_t *meth = mi->def.method;
4274-
if (!jl_is_unionall(meth->sig) && ((jl_datatype_t*)unw)->isdispatchtuple) {
4274+
if (!jl_is_unionall(meth->sig) && ((jl_datatype_t*)unw)->isindivisibletype) {
42754275
env.match.env = jl_emptysvec;
42764276
env.match.ti = unw;
42774277
}
@@ -4560,7 +4560,7 @@ static jl_value_t *ml_matches(jl_methtable_t *mt,
45604560
if (env.match.max_valid > max_world)
45614561
env.match.max_valid = max_world;
45624562
}
4563-
if (mt && cache_result && ((jl_datatype_t*)unw)->isdispatchtuple) { // cache_result parameter keeps this from being recursive
4563+
if (mt && cache_result && ((jl_datatype_t*)unw)->isindivisibletype) { // cache_result parameter keeps this from being recursive
45644564
if (len == 1 && !has_ambiguity) {
45654565
env.matc = (jl_method_match_t*)jl_array_ptr_ref(env.t, 0);
45664566
jl_method_t *meth = env.matc->method;

0 commit comments

Comments
 (0)