Skip to content

Commit 4bba0aa

Browse files
authored
various improvements to subtyping (#34743)
* subtype: optimize even if envsz > 0 * typeintersect: fix bug with non-type and Any * intersect: skip intersection test for more concrete cases * obvious_subtype: most supertypes of Type are obvious
1 parent f5afdf9 commit 4bba0aa

File tree

2 files changed

+99
-26
lines changed

2 files changed

+99
-26
lines changed

src/subtype.c

Lines changed: 92 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -304,11 +304,13 @@ static int obviously_disjoint(jl_value_t *a, jl_value_t *b, int specificity)
304304
return 0;
305305
if (specificity && a == (jl_value_t*)jl_typeofbottom_type)
306306
return 0;
307-
if (jl_is_concrete_type(a) && jl_is_concrete_type(b) &&
308-
// TODO: remove these 2 lines if and when Tuple{Union{}} === Union{}
309-
(((jl_datatype_t*)a)->name != jl_tuple_typename ||
310-
((jl_datatype_t*)b)->name != jl_tuple_typename))
311-
return 1;
307+
// TODO: this would be a nice fast-path to have, unfortuanately,
308+
// datatype allocation fails to correctly hash-cons them
309+
// and the subtyping tests include tests for this case
310+
//if (jl_is_concrete_type(a) && jl_is_concrete_type(b) &&
311+
// (((jl_datatype_t*)a)->name != jl_tuple_typename ||
312+
// ((jl_datatype_t*)b)->name != jl_tuple_typename))
313+
// return 1;
312314
if (jl_is_unionall(a)) a = jl_unwrap_unionall(a);
313315
if (jl_is_unionall(b)) b = jl_unwrap_unionall(b);
314316
if (jl_is_datatype(a) && jl_is_datatype(b)) {
@@ -355,25 +357,24 @@ static int obviously_disjoint(jl_value_t *a, jl_value_t *b, int specificity)
355357
np = jl_nparams(ad);
356358
}
357359
size_t i;
358-
for(i=0; i < np; i++) {
359-
jl_value_t *ai = jl_tparam(ad,i);
360-
jl_value_t *bi = jl_tparam(bd,i);
360+
for (i = 0; i < np; i++) {
361+
jl_value_t *ai = jl_tparam(ad, i);
362+
jl_value_t *bi = jl_tparam(bd, i);
361363
if (jl_is_typevar(ai) || jl_is_typevar(bi))
362-
continue;
364+
continue; // it's possible that Union{} is in this intersection
363365
if (jl_is_type(ai)) {
364366
if (jl_is_type(bi)) {
365367
if (istuple && (ai == jl_bottom_type || bi == jl_bottom_type))
366368
; // TODO: this can return 1 if and when Tuple{Union{}} === Union{}
367369
else if (obviously_disjoint(ai, bi, specificity))
368370
return 1;
369371
}
370-
else if (!specificity) {
371-
// Tuple{1} is more specific than Tuple{Any}
372+
else if (ai != (jl_value_t*)jl_any_type) {
372373
return 1;
373374
}
374375
}
375376
else if (jl_is_type(bi)) {
376-
if (!specificity)
377+
if (bi != (jl_value_t*)jl_any_type)
377378
return 1;
378379
}
379380
else if (!jl_egal(ai, bi)) {
@@ -1620,17 +1621,35 @@ static int obvious_subtype(jl_value_t *x, jl_value_t *y, jl_value_t *y0, int *su
16201621
int istuple = (((jl_datatype_t*)y)->name == jl_tuple_typename);
16211622
int iscov = istuple || (((jl_datatype_t*)y)->name == jl_vararg_typename);
16221623
// TODO: this would be a nice fast-path to have, unfortuanately,
1623-
// datatype allocation fails to correctly cons them
1624+
// datatype allocation fails to correctly hash-cons them
16241625
// and the subtyping tests include tests for this case
16251626
//if (!iscov && ((jl_datatype_t*)y)->isconcretetype && !jl_is_type_type(x)) {
16261627
// *subtype = 0;
16271628
// return 1;
16281629
//}
16291630
if (jl_is_datatype(x)) {
1631+
// Weaker version of above, but runs into the same problem
1632+
//if (((jl_datatype_t*)x)->isconcretetype && ((jl_datatype_t*)y)->isconcretetype && (!istuple || !istuple_x)) {
1633+
// *subtype = 0;
1634+
// return 1;
1635+
//}
16301636
int uncertain = 0;
16311637
if (((jl_datatype_t*)x)->name != ((jl_datatype_t*)y)->name) {
1632-
if (jl_is_type_type(x) || jl_is_type_type(y))
1633-
return 0;
1638+
if (jl_is_type_type(x) && jl_is_kind(y)) {
1639+
jl_value_t *t0 = jl_tparam0(x);
1640+
if (jl_is_typevar(t0))
1641+
return 0;
1642+
*subtype = jl_typeof(t0) == y;
1643+
return 1;
1644+
}
1645+
if (jl_is_type_type(y)) {
1646+
jl_value_t *t0 = jl_tparam0(y);
1647+
assert(!jl_is_type_type(x));
1648+
if (jl_is_kind(x) && jl_is_typevar(t0))
1649+
return 0;
1650+
*subtype = 0;
1651+
return 1;
1652+
}
16341653
jl_datatype_t *temp = (jl_datatype_t*)x;
16351654
while (temp->name != ((jl_datatype_t*)y)->name) {
16361655
temp = temp->super;
@@ -1818,13 +1837,22 @@ JL_DLLEXPORT int jl_obvious_subtype(jl_value_t *x, jl_value_t *y, int *subtype)
18181837
JL_DLLEXPORT int jl_subtype_env(jl_value_t *x, jl_value_t *y, jl_value_t **env, int envsz)
18191838
{
18201839
jl_stenv_t e;
1821-
if (envsz == 0) {
1822-
if (y == (jl_value_t*)jl_any_type || x == jl_bottom_type || x == y)
1823-
return 1;
1824-
if (jl_typeof(x) == jl_typeof(y) &&
1825-
(jl_is_unionall(y) || jl_is_uniontype(y)) &&
1826-
jl_egal(x, y))
1827-
return 1;
1840+
if (y == (jl_value_t*)jl_any_type || x == jl_bottom_type)
1841+
return 1;
1842+
if (x == y ||
1843+
(jl_typeof(x) == jl_typeof(y) &&
1844+
(jl_is_unionall(y) || jl_is_uniontype(y)) &&
1845+
jl_egal(x, y))) {
1846+
if (envsz != 0) { // quickly copy env from x
1847+
jl_unionall_t *ua = (jl_unionall_t*)x;
1848+
int i;
1849+
for (i = 0; i < envsz; i++) {
1850+
assert(jl_is_unionall(ua));
1851+
env[i] = (jl_value_t*)ua->var;
1852+
ua = (jl_unionall_t*)ua->body;
1853+
}
1854+
}
1855+
return 1;
18281856
}
18291857
int obvious_subtype = 2;
18301858
if (jl_obvious_subtype(x, y, &obvious_subtype)) {
@@ -3226,6 +3254,37 @@ jl_value_t *switch_union_tuple(jl_value_t *a, jl_value_t *b)
32263254
return ans;
32273255
}
32283256

3257+
// `a` might have a non-empty intersection with some concrete type b even if !(a<:b) and !(b<:a)
3258+
// For example a=`Tuple{Type{<:Vector}}` and b=`Tuple{DataType}`
3259+
int might_intersect_concrete(jl_value_t *a)
3260+
{
3261+
if (jl_is_unionall(a))
3262+
a = jl_unwrap_unionall(a);
3263+
if (jl_is_typevar(a))
3264+
return 1; // (maybe)
3265+
if (jl_is_uniontype(a))
3266+
return might_intersect_concrete(((jl_uniontype_t*)a)->a) ||
3267+
might_intersect_concrete(((jl_uniontype_t*)a)->b);
3268+
if (jl_is_vararg_type(a))
3269+
return might_intersect_concrete(jl_tparam0(a));
3270+
if (jl_is_type_type(a))
3271+
return 1;
3272+
if (jl_is_datatype(a)) {
3273+
int tpl = jl_is_tuple_type(a);
3274+
int i, n = jl_nparams(a);
3275+
for (i = 0; i < n; i++) {
3276+
jl_value_t *p = jl_tparam(a, i);
3277+
if (jl_is_typevar(p))
3278+
return 1;
3279+
if (tpl && p == jl_bottom_type)
3280+
return 1;
3281+
if (tpl && might_intersect_concrete(p))
3282+
return 1;
3283+
}
3284+
}
3285+
return 0;
3286+
}
3287+
32293288
// sets *issubty to 1 iff `a` is a subtype of `b`
32303289
jl_value_t *jl_type_intersection_env_s(jl_value_t *a, jl_value_t *b, jl_svec_t **penv, int *issubty)
32313290
{
@@ -3238,18 +3297,25 @@ jl_value_t *jl_type_intersection_env_s(jl_value_t *a, jl_value_t *b, jl_svec_t *
32383297
int sz = 0, i = 0;
32393298
jl_value_t **env, **ans;
32403299
JL_GC_PUSHARGS(env, szb+1);
3241-
ans = &env[szb]; *ans = jl_bottom_type;
3300+
ans = &env[szb];
3301+
*ans = jl_bottom_type;
3302+
int lta = jl_is_concrete_type(a);
3303+
int ltb = jl_is_concrete_type(b);
32423304
if (jl_subtype_env(a, b, env, szb)) {
32433305
*ans = a; sz = szb;
32443306
if (issubty) *issubty = 1;
32453307
}
3308+
else if (lta && ltb) {
3309+
goto bot;
3310+
}
32463311
else if (jl_subtype(b, a)) {
32473312
*ans = b;
32483313
}
32493314
else {
3250-
int lta = jl_is_concrete_type(a);
3251-
int ltb = jl_is_concrete_type(b);
3252-
if (lta && ltb)
3315+
// TODO: these tests could probably be ordered better with above
3316+
if (lta && !might_intersect_concrete(b))
3317+
goto bot;
3318+
if (ltb && !might_intersect_concrete(a))
32533319
goto bot;
32543320
jl_stenv_t e;
32553321
init_stenv(&e, NULL, 0);

test/subtype.jl

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1043,6 +1043,13 @@ function test_intersection()
10431043
@testintersect(Tuple{Type{Tuple{Vararg{Union{Int,Symbol}}}}, Tuple},
10441044
Tuple{Type{Tuple{Vararg{V}}}, Tuple{Vararg{V}}} where {V},
10451045
Tuple{Type{Tuple{Vararg{Union{Int,Symbol},N} where N}},Tuple{Vararg{Union{Int,Symbol},N} where N}})
1046+
1047+
# non types
1048+
@testintersect(Tuple{1}, Tuple{Any}, Tuple{1})
1049+
1050+
# tests for robustness after incorrect datatype allocation normalization
1051+
@test typeintersect(Vector{Tuple{T, T} where Number<:T<:Number}, Vector{Tuple{Number, Number}}) === Vector{Tuple{T, T} where Number<:T<:Number}
1052+
@test typeintersect(Vector{Tuple{Number, Number}}, Vector{Tuple{T, T} where Number<:T<:Number}) === Vector{Tuple{Number, Number}}
10461053
end
10471054

10481055
function test_intersection_properties()

0 commit comments

Comments
 (0)