Skip to content

Commit 01ee09b

Browse files
committed
move type-signature correction to a separate function
this allows us to correct the type signature before passing it to the generated function or returning it from ml_matches this also improves the fix for #11840 to cover Union but breaks the fix for #11355 -- fortunately, our improved cache should soon be up to the challenge of fixing it too
1 parent ca5d217 commit 01ee09b

File tree

1 file changed

+79
-144
lines changed

1 file changed

+79
-144
lines changed

src/gf.c

+79-144
Original file line numberDiff line numberDiff line change
@@ -281,6 +281,78 @@ static jl_value_t *lookup_match(jl_value_t *a, jl_value_t *b, jl_svec_t **penv,
281281
return ti;
282282
}
283283

284+
jl_value_t *jl_nth_slot_type(jl_tupletype_t *sig, size_t i)
285+
{
286+
size_t len = jl_datatype_nfields(sig);
287+
if (len == 0)
288+
return NULL;
289+
if (i < len-1)
290+
return jl_tparam(sig, i);
291+
if (jl_is_vararg_type(jl_tparam(sig,len-1)))
292+
return jl_tparam0(jl_tparam(sig,len-1));
293+
if (i == len-1)
294+
return jl_tparam(sig, i);
295+
return NULL;
296+
}
297+
298+
// after intersection, the argument tuple type needs to be corrected to reflect the signature match
299+
// that occurred, if the arguments contained a Type but the signature matched on the kind
300+
static jl_tupletype_t *join_tsig(jl_tupletype_t *tt, jl_tupletype_t *sig)
301+
{
302+
jl_svec_t *newparams = NULL;
303+
JL_GC_PUSH1(&newparams);
304+
int changed = 0;
305+
size_t i, np;
306+
for (i = 0, np = jl_nparams(tt); i < np; i++) {
307+
jl_value_t *elt = jl_tparam(tt, i);
308+
jl_value_t *newelt = NULL;
309+
jl_value_t *decl_i = jl_nth_slot_type(sig, i);
310+
int isva = jl_is_vararg_type(elt);
311+
if (isva) elt = jl_tparam0(elt);
312+
313+
if (jl_is_type_type(elt)) {
314+
// unless the type signature matched T in `::Type{T}`
315+
// the result of matching the type signature
316+
// needs to be corrected to the leaf type 'kind'
317+
// (for performance and/or correctness)
318+
jl_value_t *kind = jl_typeof(jl_tparam0(elt));
319+
if (jl_subtype(kind, decl_i, 0)) {
320+
if (jl_subtype((jl_value_t*)jl_type_type, decl_i, 0)) { // declared type was Any or Union{Type, ...}
321+
// if there are bound typevars, the user indicated this exact specialization was required
322+
// don't change it or it might mismatch the env
323+
if (!jl_has_typevars(decl_i))
324+
newelt = (jl_value_t*)jl_type_type;
325+
}
326+
else {
327+
// TypeConstructors are problematic because they can be alternate
328+
// representations of any type. If we matched this method because
329+
// it matched the leaf type TypeConstructor, then don't
330+
// cache something different since that doesn't necessarily actually apply
331+
//
332+
// similarly, if we matched Type{T<:Any}::DataType,
333+
// then we don't want to cache it that way
334+
// since lookup will think we matched ::Type{T}
335+
// and that is quite a different thing
336+
newelt = kind;
337+
}
338+
}
339+
}
340+
// prepare to build a new type with the replacement above
341+
if (newelt) {
342+
if (isva) newelt = (jl_value_t*)jl_wrap_vararg(newelt);
343+
if (!changed) {
344+
newparams = jl_svec_copy(tt->parameters);
345+
changed = 1;
346+
}
347+
jl_svecset(newparams, i, newelt);
348+
}
349+
}
350+
if (changed)
351+
tt = jl_apply_tuple_type(newparams);
352+
JL_GC_POP();
353+
return tt;
354+
}
355+
284356
static int sigs_eq(jl_value_t *a, jl_value_t *b, int useenv)
285357
{
286358
if (jl_has_typevars(a) || jl_has_typevars(b)) {
@@ -1056,27 +1128,6 @@ void jl_type_infer(jl_lambda_info_t *li, jl_value_t *toplevel)
10561128
#endif
10571129
}
10581130

1059-
jl_value_t *jl_nth_slot_type(jl_tupletype_t *sig, size_t i)
1060-
{
1061-
size_t len = jl_datatype_nfields(sig);
1062-
if (len == 0)
1063-
return NULL;
1064-
if (i < len-1)
1065-
return jl_tparam(sig, i);
1066-
if (jl_is_vararg_type(jl_tparam(sig,len-1)))
1067-
return jl_tparam0(jl_tparam(sig,len-1));
1068-
if (i == len-1)
1069-
return jl_tparam(sig, i);
1070-
return NULL;
1071-
}
1072-
1073-
static int very_general_type(jl_value_t *t)
1074-
{
1075-
return (t && (t==(jl_value_t*)jl_any_type || t == (jl_value_t*)jl_type_type ||
1076-
(jl_is_typevar(t) &&
1077-
((jl_tvar_t*)t)->ub==(jl_value_t*)jl_any_type)));
1078-
}
1079-
10801131
static int is_kind(jl_value_t *v)
10811132
{
10821133
return (v==(jl_value_t*)jl_uniontype_type ||
@@ -1110,27 +1161,6 @@ static jl_lambda_info_t *cache_method(jl_methtable_t *mt, union _jl_opaque_cache
11101161
for (i=0; i < np; i++) {
11111162
jl_value_t *elt = jl_tparam(type,i);
11121163
jl_value_t *decl_i = jl_nth_slot_type(decl,i);
1113-
if (!isstaged && jl_is_type_type(elt) && jl_is_tuple_type(jl_tparam0(elt)) &&
1114-
!(jl_subtype(decl_i, (jl_value_t*)jl_type_type, 0) && !is_kind(decl_i))) {
1115-
jl_methlist_t *curr = mt->defs;
1116-
int ok=1;
1117-
while (curr != (void*)jl_nothing) {
1118-
jl_value_t *slottype = jl_nth_slot_type(curr->sig, i);
1119-
if (slottype && curr->func != method) {
1120-
if (jl_is_type_type(slottype) &&
1121-
jl_type_intersection(slottype, decl_i) != jl_bottom_type) {
1122-
ok=0;
1123-
break;
1124-
}
1125-
}
1126-
curr = curr->next;
1127-
}
1128-
if (ok) {
1129-
elt = jl_typeof(jl_tparam0(elt));
1130-
jl_svecset(newparams, i, elt);
1131-
}
1132-
}
1133-
11341164
int set_to_any = 0;
11351165
int notcalled_func = (i>0 && i<=8 && !(spec->called&(1<<(i-1))) &&
11361166
jl_subtype(elt,(jl_value_t*)jl_function_type,0));
@@ -1179,24 +1209,9 @@ static jl_lambda_info_t *cache_method(jl_methtable_t *mt, union _jl_opaque_cache
11791209
}
11801210
}
11811211
}
1212+
11821213
if (set_to_any || isstaged) {
11831214
}
1184-
else if (jl_is_type_type(elt) && jl_is_typector(jl_tparam0(elt)) &&
1185-
decl_i == (jl_value_t*)jl_typector_type) {
1186-
// TypeConstructors are problematic because they can be alternate
1187-
// representations of any type. If we matched this method because
1188-
// it matched the leaf type TypeConstructor, then don't
1189-
// cache something different since that doesn't necessarily actually apply
1190-
jl_svecset(newparams, i, jl_typector_type);
1191-
}
1192-
else if (jl_is_type_type(elt) && decl_i == (jl_value_t*)jl_datatype_type) {
1193-
// similarly, if we matched Type{T<:Any}::DataType,
1194-
// then we don't want to cache it that way
1195-
// since lookup will think we matched ::Type{T}
1196-
// and that is quite a different thing
1197-
jl_svecset(newparams, i, jl_datatype_type);
1198-
need_guard_entries = 1; // DataType has a UID so its precedence in the cache may be too high
1199-
}
12001215
else if (jl_is_type_type(elt) && jl_is_type_type(jl_tparam0(elt)) &&
12011216
// give up on specializing static parameters for Type{Type{Type{...}}}
12021217
(jl_is_type_type(jl_tparam0(jl_tparam0(elt))) ||
@@ -1228,90 +1243,8 @@ static jl_lambda_info_t *cache_method(jl_methtable_t *mt, union _jl_opaque_cache
12281243
need_guard_entries = 1;
12291244
assert(jl_svecref(newparams,i) != (jl_value_t*)jl_bottom_type);
12301245
}
1231-
else if (jl_is_type_type(elt) && very_general_type(decl_i) &&
1232-
!jl_has_typevars(decl_i)) {
1233-
/*
1234-
here's a fairly complex heuristic: if this argument slot's
1235-
declared type is Any, and no definition overlaps with Type
1236-
for this slot, then don't specialize for every Type that
1237-
might be passed.
1238-
Since every type x has its own type Type{x}, this would be
1239-
excessive specialization for an Any slot.
1240-
1241-
TypeConstructors are problematic because they can be alternate
1242-
representations of any type. Extensionally, TC == TC.body, but
1243-
typeof(TC) != typeof(TC.body). This creates an ambiguity:
1244-
Type{TC} is type-equal to Type{TC.body}, yet a slot
1245-
x::TypeConstructor matches the first but not the second, while
1246-
also matching all other TypeConstructors. This means neither
1247-
Type{TC} nor TypeConstructor is more specific.
1248-
1249-
To solve this, we identify "kind slots", which are slots
1250-
for which some definition specifies a kind (e.g. DataType).
1251-
Those tend to be in reflective functions that look at types
1252-
themselves. For these slots we specialize on jl_typeof(T) instead
1253-
of Type{T}, i.e. the kind of the type rather than the specific
1254-
type.
1255-
*/
1256-
int ok=1, kindslot=0;
1257-
jl_methlist_t *curr = mt->defs;
1258-
jl_value_t *kind = (jl_value_t*)jl_typeof(jl_tparam0(elt));
1259-
while (curr != (void*)jl_nothing) {
1260-
jl_value_t *slottype = jl_nth_slot_type(curr->sig, i);
1261-
if (slottype && curr->func != method) {
1262-
if (slottype == kind) {
1263-
ok=0;
1264-
break;
1265-
}
1266-
if (is_kind(slottype))
1267-
kindslot=1;
1268-
}
1269-
curr = curr->next;
1270-
}
1271-
if (ok) {
1272-
if (kindslot) {
1273-
jl_svecset(newparams, i, kind);
1274-
}
1275-
else {
1276-
curr = mt->defs;
1277-
while (curr != (void*)jl_nothing) {
1278-
jl_value_t *slottype = jl_nth_slot_type(curr->sig, i);
1279-
if (slottype && curr->func != method) {
1280-
if (!very_general_type(slottype) &&
1281-
jl_type_intersection(slottype, (jl_value_t*)jl_type_type) !=
1282-
(jl_value_t*)jl_bottom_type) {
1283-
ok=0;
1284-
break;
1285-
}
1286-
}
1287-
curr = curr->next;
1288-
}
1289-
if (ok) {
1290-
jl_svecset(newparams, i, jl_typetype_type);
1291-
need_guard_entries = 1;
1292-
}
1293-
}
1294-
}
1295-
}
1296-
else if (is_kind(decl_i)) {
1297-
// if a slot is specialized for a particular kind, it can be
1298-
// considered a reflective method and so only needs to be
1299-
// specialized for type representation, not type extent.
1300-
jl_methlist_t *curr = mt->defs;
1301-
int ok=1;
1302-
while (curr != (void*)jl_nothing) {
1303-
jl_value_t *slottype = jl_nth_slot_type(curr->sig, i);
1304-
if (slottype && curr->func != method) {
1305-
if (jl_is_type_type(slottype) &&
1306-
jl_type_intersection(slottype, decl_i) != jl_bottom_type) {
1307-
ok=0;
1308-
break;
1309-
}
1310-
}
1311-
curr = curr->next;
1312-
}
1313-
if (ok)
1314-
jl_svecset(newparams, i, decl_i);
1246+
else if (is_kind(elt)) {
1247+
need_guard_entries = 1;
13151248
}
13161249
}
13171250

@@ -1394,6 +1327,7 @@ static jl_lambda_info_t *cache_method(jl_methtable_t *mt, union _jl_opaque_cache
13941327
}
13951328
}
13961329
if (unmatched_tvars) {
1330+
// FIXME: this is invalid (c.f. intersection of #11355 and #11840)
13971331
// if distinguishing a guard entry from the generalized signature
13981332
// would require matching type vars then bail out, since the
13991333
// method cache matching algorithm cannot do that.
@@ -1558,15 +1492,15 @@ static jl_lambda_info_t *jl_mt_assoc_by_type(jl_methtable_t *mt, jl_datatype_t *
15581492
jl_methlist_t *m = NULL;
15591493
jl_svec_t *env = jl_emptysvec;
15601494
jl_lambda_info_t *func = NULL;
1561-
JL_GC_PUSH3(&env, &m, &func);
1495+
JL_GC_PUSH4(&env, &m, &func, &tt);
15621496

15631497
m = jl_method_cache_assoc_by_type_(mt->defs, tt, inexact, &env);
15641498
if (m == NULL) {
15651499
JL_GC_POP();
15661500
return NULL;
15671501
}
15681502

1569-
assert(jl_is_svec(env));
1503+
tt = join_tsig(tt, m->sig);
15701504
func = m->func;
15711505
if (func->isstaged)
15721506
func = jl_instantiate_staged(func, tt, env);
@@ -2234,6 +2168,7 @@ jl_value_t *jl_gf_invoke(jl_tupletype_t *types0, jl_value_t **args, size_t nargs
22342168
assert(ti != (jl_value_t*)jl_bottom_type);
22352169
(void)ti;
22362170
}
2171+
tt = join_tsig(tt, m->sig);
22372172
jl_lambda_info_t *func = m->func;
22382173

22392174
if (func->invokes.nothing == NULL)

0 commit comments

Comments
 (0)