Skip to content

Commit

Permalink
add a fast path function for checking for empty intersections
Browse files Browse the repository at this point in the history
add `obviously_disjoint` fast path to type_morespecific

further helps #21173
  • Loading branch information
JeffBezanson committed Apr 12, 2017
1 parent bb1e493 commit 75a6a90
Show file tree
Hide file tree
Showing 4 changed files with 39 additions and 15 deletions.
6 changes: 2 additions & 4 deletions src/gf.c
Original file line number Diff line number Diff line change
Expand Up @@ -1397,7 +1397,7 @@ JL_DLLEXPORT void jl_method_table_insert(jl_methtable_t *mt, jl_method_t *method
size_t ins = 0;
for (i = 1; i < na; i += 2) {
jl_value_t *backedgetyp = backedges[i - 1];
if (jl_type_intersection(backedgetyp, (jl_value_t*)type) != (jl_value_t*)jl_bottom_type) {
if (!jl_has_empty_intersection(backedgetyp, (jl_value_t*)type)) {
jl_method_instance_t *backedge = (jl_method_instance_t*)backedges[i];
invalidate_method_instance(backedge, env.max_world, 0);
env.invalidated = 1;
Expand Down Expand Up @@ -1731,10 +1731,8 @@ JL_DLLEXPORT int jl_has_call_ambiguities(jl_tupletype_t *types, jl_method_t *m)
if (m->ambig == jl_nothing) return 0;
for (size_t i = 0; i < jl_array_len(m->ambig); i++) {
jl_method_t *mambig = (jl_method_t*)jl_array_ptr_ref(m->ambig, i);
if (jl_type_intersection((jl_value_t*)mambig->sig,
(jl_value_t*)types) != (jl_value_t*)jl_bottom_type) {
if (!jl_has_empty_intersection((jl_value_t*)mambig->sig, (jl_value_t*)types))
return 1;
}
}
return 0;
}
Expand Down
1 change: 1 addition & 0 deletions src/julia.h
Original file line number Diff line number Diff line change
Expand Up @@ -982,6 +982,7 @@ JL_DLLEXPORT int jl_isa(jl_value_t *a, jl_value_t *t);
JL_DLLEXPORT int jl_types_equal(jl_value_t *a, jl_value_t *b);
JL_DLLEXPORT jl_value_t *jl_type_union(jl_value_t **ts, size_t n);
JL_DLLEXPORT jl_value_t *jl_type_intersection(jl_value_t *a, jl_value_t *b);
JL_DLLEXPORT int jl_has_empty_intersection(jl_value_t *x, jl_value_t *y);
JL_DLLEXPORT jl_value_t *jl_type_unionall(jl_tvar_t *v, jl_value_t *body);
JL_DLLEXPORT const char *jl_typename_str(jl_value_t *v);
JL_DLLEXPORT const char *jl_typeof_str(jl_value_t *v);
Expand Down
45 changes: 35 additions & 10 deletions src/subtype.c
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ typedef struct {
int invdepth; // current number of invariant constructors we're nested in
int ignore_free; // treat free vars as black boxes; used during intersection
int intersection; // true iff subtype is being called from intersection
int emptiness_only; // true iff intersection only needs to test for emptiness
} jl_stenv_t;

// state manipulation utilities
Expand Down Expand Up @@ -238,7 +239,7 @@ static int obviously_unequal(jl_value_t *a, jl_value_t *b)
return 0;
}

static int obviously_disjoint(jl_value_t *a, jl_value_t *b)
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;
Expand Down Expand Up @@ -280,7 +281,8 @@ static int obviously_disjoint(jl_value_t *a, jl_value_t *b)
else if (jl_is_va_tuple(bd)) {
nb -= 1;
}
else if (na != nb) {
else if (!specificity && na != nb) {
// note: some disjoint types (e.g. tuples of different lengths) can be more specific
return 1;
}
np = na < nb ? na : nb;
Expand All @@ -298,13 +300,18 @@ static int obviously_disjoint(jl_value_t *a, jl_value_t *b)
if (jl_is_type(bi)) {
if (istuple && (ai == jl_bottom_type || bi == jl_bottom_type))
; // TODO: this can return 1 if and when Tuple{Union{}} === Union{}
else if (obviously_disjoint(ai, bi))
else if (obviously_disjoint(ai, bi, specificity))
return 1;
}
else {
else if (!specificity) {
// Tuple{1} is more specific than Tuple{Any}
return 1;
}
}
else if (jl_is_type(bi)) {
if (!specificity)
return 1;
}
else if (!jl_egal(ai, bi)) {
return 1;
}
Expand Down Expand Up @@ -534,8 +541,9 @@ static int subtype_unionall(jl_value_t *t, jl_unionall_t *u, jl_stenv_t *e, int8
// if the var for this unionall (based on identity) already appears somewhere
// in the environment, rename to get a fresh var.
while (btemp != NULL) {
if (btemp->var == u->var || jl_has_typevar(btemp->lb, u->var) ||
jl_has_typevar(btemp->ub, u->var)) {
if (btemp->var == u->var ||
(btemp->lb != jl_bottom_type && jl_has_typevar(btemp->lb, u->var)) ||
(btemp->ub != (jl_value_t*)jl_any_type && jl_has_typevar(btemp->ub, u->var))) {
u = rename_unionall(u);
break;
}
Expand Down Expand Up @@ -619,7 +627,7 @@ static int subtype_unionall(jl_value_t *t, jl_unionall_t *u, jl_stenv_t *e, int8
btemp = e->vars;
while (btemp != NULL) {
jl_value_t *vi = btemp->ub;
if (vi != (jl_value_t*)vb.var && jl_has_typevar(vi, vb.var)) {
if (vi != (jl_value_t*)vb.var && vi != (jl_value_t*)jl_any_type && jl_has_typevar(vi, vb.var)) {
btemp->ub = jl_new_struct(jl_unionall_type, vb.var, vi);
btemp->lb = jl_bottom_type;
}
Expand Down Expand Up @@ -1003,6 +1011,7 @@ static void init_stenv(jl_stenv_t *e, jl_value_t **env, int envsz)
e->invdepth = 0;
e->ignore_free = 0;
e->intersection = 0;
e->emptiness_only = 0;
e->Lunions.depth = 0; e->Runions.depth = 0;
e->Lunions.more = 0; e->Runions.more = 0;
}
Expand Down Expand Up @@ -2012,6 +2021,8 @@ static jl_value_t *intersect_all(jl_value_t *x, jl_value_t *y, jl_stenv_t *e)
int lastset = 0, niter = 0;
jl_value_t *ii = intersect(x, y, e, 0);
while (e->Runions.more) {
if (e->emptiness_only && ii != jl_bottom_type)
return ii;
e->Runions.depth = 0;
int set = e->Runions.more - 1;
e->Runions.more = 0;
Expand Down Expand Up @@ -2042,16 +2053,28 @@ static jl_value_t *intersect_all(jl_value_t *x, jl_value_t *y, jl_stenv_t *e)

// type intersection entry points

JL_DLLEXPORT jl_value_t *jl_intersect_types(jl_value_t *x, jl_value_t *y)
static jl_value_t *intersect_types(jl_value_t *x, jl_value_t *y, int emptiness_only)
{
jl_stenv_t e;
if (obviously_disjoint(x, y))
if (obviously_disjoint(x, y, 0))
return jl_bottom_type;
init_stenv(&e, NULL, 0);
e.intersection = 1;
e.emptiness_only = emptiness_only;
return intersect_all(x, y, &e);
}

JL_DLLEXPORT jl_value_t *jl_intersect_types(jl_value_t *x, jl_value_t *y)
{
return intersect_types(x, y, 0);
}

// TODO: this can probably be done more efficiently
JL_DLLEXPORT int jl_has_empty_intersection(jl_value_t *x, jl_value_t *y)
{
return intersect_types(x, y, 1) == jl_bottom_type;
}

// return a SimpleVector of all vars from UnionAlls wrapping a given type
jl_svec_t *jl_outer_unionall_vars(jl_value_t *u)
{
Expand All @@ -2073,7 +2096,7 @@ jl_svec_t *jl_outer_unionall_vars(jl_value_t *u)
jl_value_t *jl_type_intersection_env_s(jl_value_t *a, jl_value_t *b, jl_svec_t **penv, int *issubty)
{
if (issubty) *issubty = 0;
if (obviously_disjoint(a, b)) {
if (obviously_disjoint(a, b, 0)) {
if (issubty && a == jl_bottom_type) *issubty = 1;
return jl_bottom_type;
}
Expand Down Expand Up @@ -2570,6 +2593,8 @@ static int type_morespecific_(jl_value_t *a, jl_value_t *b, int invariant, jl_ty

JL_DLLEXPORT int jl_type_morespecific(jl_value_t *a, jl_value_t *b)
{
if (obviously_disjoint(a, b, 1))
return 0;
if (jl_subtype(b, a)) return 0;
if (jl_subtype(a, b)) return 1;
return type_morespecific_(a, b, 0, NULL);
Expand Down
2 changes: 1 addition & 1 deletion src/typemap.c
Original file line number Diff line number Diff line change
Expand Up @@ -515,7 +515,7 @@ int jl_typemap_intersection_visitor(union jl_typemap_t map, int offs,
else {
// else an array scan is required to check subtypes
// first, fast-path: optimized pre-intersection test to see if `ty` could intersect with any Type
if (typetype || jl_type_intersection((jl_value_t*)jl_type_type, ty) != jl_bottom_type)
if (typetype || !jl_has_empty_intersection((jl_value_t*)jl_type_type, ty))
if (!jl_typemap_intersection_array_visitor(&cache->targ, ty, 1, offs, closure)) return 0;
}
}
Expand Down

0 comments on commit 75a6a90

Please sign in to comment.