Skip to content

Commit 9e5e28f

Browse files
committed
ensure types are UnionAll wrapped are cached correctly for widened Vararg methods
And fix a related accuracy issue in jl_isa_compileable_sig Follow-up issue found while working on #47476
1 parent 71ab5fa commit 9e5e28f

File tree

2 files changed

+117
-59
lines changed

2 files changed

+117
-59
lines changed

src/gf.c

Lines changed: 108 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -606,6 +606,46 @@ jl_value_t *jl_nth_slot_type(jl_value_t *sig, size_t i) JL_NOTSAFEPOINT
606606
// return 1;
607607
//}
608608

609+
static jl_value_t *inst_varargp_in_env(jl_value_t *decl, jl_svec_t *sparams)
610+
{
611+
jl_value_t *unw = jl_unwrap_unionall(decl);
612+
jl_value_t *vm = jl_tparam(unw, jl_nparams(unw) - 1);
613+
assert(jl_is_vararg(vm));
614+
int nsp = jl_svec_len(sparams);
615+
if (nsp > 0 && jl_has_free_typevars(vm)) {
616+
JL_GC_PUSH1(&vm);
617+
assert(jl_subtype_env_size(decl) == nsp);
618+
vm = jl_instantiate_type_in_env(vm, (jl_unionall_t*)decl, jl_svec_data(sparams));
619+
assert(jl_is_vararg(vm));
620+
// rewrap_unionall(lastdeclt, sparams) if any sparams isa TypeVar
621+
// for example, `Tuple{Vararg{Union{Nothing,Int,Val{T}}}} where T`
622+
// and the user called it with `Tuple{Vararg{Union{Nothing,Int},N}}`, then T is unbound
623+
jl_value_t **sp = jl_svec_data(sparams);
624+
while (jl_is_unionall(decl)) {
625+
jl_tvar_t *v = (jl_tvar_t*)*sp;
626+
if (jl_is_typevar(v)) {
627+
// must unwrap and re-wrap Vararg object explicitly here since jl_type_unionall handles it differently
628+
jl_value_t *T = ((jl_vararg_t*)vm)->T;
629+
jl_value_t *N = ((jl_vararg_t*)vm)->N;
630+
int T_has_tv = T && jl_has_typevar(T, v);
631+
int N_has_tv = N && jl_has_typevar(N, v); // n.b. JL_VARARG_UNBOUND check means this should be false
632+
assert(!N_has_tv || N == (jl_value_t*)v);
633+
if (T_has_tv)
634+
vm = jl_type_unionall(v, T);
635+
if (N_has_tv)
636+
N = NULL;
637+
vm = (jl_value_t*)jl_wrap_vararg(vm, N); // this cannot throw for these inputs
638+
}
639+
sp++;
640+
decl = ((jl_unionall_t*)decl)->body;
641+
nsp--;
642+
}
643+
assert(nsp == 0);
644+
JL_GC_POP();
645+
}
646+
return vm;
647+
}
648+
609649
static jl_value_t *ml_matches(jl_methtable_t *mt,
610650
jl_tupletype_t *type, int lim, int include_ambiguous,
611651
int intersections, size_t world, int cache_result,
@@ -634,10 +674,12 @@ static void jl_compilation_sig(
634674
assert(jl_is_tuple_type(tt));
635675
size_t i, np = jl_nparams(tt);
636676
size_t nargs = definition->nargs; // == jl_nparams(jl_unwrap_unionall(decl));
677+
jl_value_t *type_i = NULL;
678+
JL_GC_PUSH1(&type_i);
637679
for (i = 0; i < np; i++) {
638680
jl_value_t *elt = jl_tparam(tt, i);
639681
jl_value_t *decl_i = jl_nth_slot_type(decl, i);
640-
jl_value_t *type_i = jl_rewrap_unionall(decl_i, decl);
682+
type_i = jl_rewrap_unionall(decl_i, decl);
641683
size_t i_arg = (i < nargs - 1 ? i : nargs - 1);
642684

643685
if (jl_is_kind(type_i)) {
@@ -779,55 +821,45 @@ static void jl_compilation_sig(
779821
// supertype of any other method signatures. so far we are conservative
780822
// and the types we find should be bigger.
781823
if (jl_nparams(tt) >= nspec && jl_va_tuple_kind((jl_datatype_t*)decl) == JL_VARARG_UNBOUND) {
782-
jl_svec_t *limited = jl_alloc_svec(nspec);
783-
JL_GC_PUSH1(&limited);
784824
if (!*newparams) *newparams = tt->parameters;
785-
size_t i;
786-
for (i = 0; i < nspec - 1; i++) {
787-
jl_svecset(limited, i, jl_svecref(*newparams, i));
788-
}
789-
jl_value_t *lasttype = jl_svecref(*newparams, i - 1);
790-
// if all subsequent arguments are subtypes of lasttype, specialize
825+
type_i = jl_svecref(*newparams, nspec - 2);
826+
// if all subsequent arguments are subtypes of type_i, specialize
791827
// on that instead of decl. for example, if decl is
792828
// (Any...)
793829
// and type is
794830
// (Symbol, Symbol, Symbol)
795831
// then specialize as (Symbol...), but if type is
796832
// (Symbol, Int32, Expr)
797833
// then specialize as (Any...)
798-
size_t j = i;
834+
size_t j = nspec - 1;
799835
int all_are_subtypes = 1;
800836
for (; j < jl_svec_len(*newparams); j++) {
801837
jl_value_t *paramj = jl_svecref(*newparams, j);
802838
if (jl_is_vararg(paramj))
803839
paramj = jl_unwrap_vararg(paramj);
804-
if (!jl_subtype(paramj, lasttype)) {
840+
if (!jl_subtype(paramj, type_i)) {
805841
all_are_subtypes = 0;
806842
break;
807843
}
808844
}
809845
if (all_are_subtypes) {
810846
// avoid Vararg{Type{Type{...}}}
811-
if (jl_is_type_type(lasttype) && jl_is_type_type(jl_tparam0(lasttype)))
812-
lasttype = (jl_value_t*)jl_type_type;
813-
jl_svecset(limited, i, jl_wrap_vararg(lasttype, (jl_value_t*)NULL));
847+
if (jl_is_type_type(type_i) && jl_is_type_type(jl_tparam0(type_i)))
848+
type_i = (jl_value_t*)jl_type_type;
849+
type_i = (jl_value_t*)jl_wrap_vararg(type_i, (jl_value_t*)NULL); // this cannot throw for these inputs
814850
}
815851
else {
816-
jl_value_t *unw = jl_unwrap_unionall(decl);
817-
jl_value_t *lastdeclt = jl_tparam(unw, jl_nparams(unw) - 1);
818-
assert(jl_is_vararg(lastdeclt));
819-
int nsp = jl_svec_len(sparams);
820-
if (nsp > 0 && jl_has_free_typevars(lastdeclt)) {
821-
assert(jl_subtype_env_size(decl) == nsp);
822-
lastdeclt = jl_instantiate_type_in_env(lastdeclt, (jl_unionall_t*)decl, jl_svec_data(sparams));
823-
// TODO: rewrap_unionall(lastdeclt, sparams) if any sparams isa TypeVar???
824-
// TODO: if we made any replacements above, sparams may now be incorrect
825-
}
826-
jl_svecset(limited, i, lastdeclt);
852+
type_i = inst_varargp_in_env(decl, sparams);
853+
}
854+
jl_svec_t *limited = jl_alloc_svec(nspec);
855+
size_t i;
856+
for (i = 0; i < nspec - 1; i++) {
857+
jl_svecset(limited, i, jl_svecref(*newparams, i));
827858
}
859+
jl_svecset(limited, i, type_i);
828860
*newparams = limited;
829-
JL_GC_POP();
830861
}
862+
JL_GC_POP();
831863
}
832864

833865
// compute whether this type signature is a possible return value from jl_compilation_sig given a concrete-type for `tt`
@@ -865,56 +897,58 @@ JL_DLLEXPORT int jl_isa_compileable_sig(
865897
jl_methtable_t *kwmt = mt == jl_kwcall_mt ? jl_kwmethod_table_for(decl) : mt;
866898
if ((jl_value_t*)mt != jl_nothing) {
867899
// try to refine estimate of min and max
868-
if (kwmt && kwmt != jl_type_type_mt && kwmt != jl_nonfunction_mt && kwmt != jl_kwcall_mt)
900+
if (kwmt != NULL && kwmt != jl_type_type_mt && kwmt != jl_nonfunction_mt && kwmt != jl_kwcall_mt)
901+
// new methods may be added, increasing nspec_min later
869902
nspec_min = jl_atomic_load_relaxed(&kwmt->max_args) + 2 + 2 * (mt == jl_kwcall_mt);
870903
else
904+
// nspec is always nargs+1, regardless of the other contents of these mt
871905
nspec_max = nspec_min;
872906
}
873-
int isbound = (jl_va_tuple_kind((jl_datatype_t*)decl) == JL_VARARG_UNBOUND);
907+
int isunbound = (jl_va_tuple_kind((jl_datatype_t*)decl) == JL_VARARG_UNBOUND);
874908
if (jl_is_vararg(jl_tparam(type, np - 1))) {
875-
if (!isbound || np < nspec_min || np > nspec_max)
909+
if (!isunbound || np < nspec_min || np > nspec_max)
876910
return 0;
877911
}
878912
else {
879-
if (np < nargs - 1 || (isbound && np >= nspec_max))
913+
if (np < nargs - 1 || (isunbound && np >= nspec_max))
880914
return 0;
881915
}
882916
}
883917
else if (np != nargs || jl_is_vararg(jl_tparam(type, np - 1))) {
884918
return 0;
885919
}
886920

921+
jl_value_t *type_i = NULL;
922+
JL_GC_PUSH1(&type_i);
887923
for (i = 0; i < np; i++) {
888924
jl_value_t *elt = jl_tparam(type, i);
889-
jl_value_t *decl_i = jl_nth_slot_type((jl_value_t*)decl, i);
890-
jl_value_t *type_i = jl_rewrap_unionall(decl_i, decl);
891925
size_t i_arg = (i < nargs - 1 ? i : nargs - 1);
892926

893927
if (jl_is_vararg(elt)) {
894-
elt = jl_unwrap_vararg(elt);
895-
if (jl_has_free_typevars(decl_i)) {
896-
// TODO: in this case, answer semi-conservatively that these varargs are always compilable
897-
// we don't have the ability to get sparams, so deciding if elt
898-
// is a potential result of jl_instantiate_type_in_env for decl_i
899-
// for any sparams that is consistent with the rest of the arguments
900-
// seems like it would be extremely difficult
901-
// and hopefully the upstream code probably gave us something reasonable
902-
continue;
903-
}
904-
else if (jl_egal(elt, decl_i)) {
905-
continue;
928+
type_i = inst_varargp_in_env(decl, sparams);
929+
if (jl_has_free_typevars(type_i)) {
930+
JL_GC_POP();
931+
return 0; // something went badly wrong?
906932
}
907-
else if (jl_is_type_type(elt) && jl_is_type_type(jl_tparam0(elt))) {
908-
return 0;
933+
if (jl_egal(elt, type_i))
934+
continue; // elt could be chosen by inst_varargp_in_env for these sparams
935+
elt = jl_unwrap_vararg(elt);
936+
if (jl_is_type_type(elt) && jl_is_type_type(jl_tparam0(elt))) {
937+
JL_GC_POP();
938+
return 0; // elt would be set equal to jl_type_type instead
909939
}
910-
// else, it needs to meet the usual rules
940+
// else, elt also needs to meet the usual rules
911941
}
912942

943+
jl_value_t *decl_i = jl_nth_slot_type(decl, i);
944+
type_i = jl_rewrap_unionall(decl_i, decl);
945+
913946
if (i_arg > 0 && i_arg <= sizeof(definition->nospecialize) * 8 &&
914947
(definition->nospecialize & (1 << (i_arg - 1)))) {
915948
if (!jl_has_free_typevars(decl_i) && !jl_is_kind(decl_i)) {
916949
if (jl_egal(elt, decl_i))
917950
continue;
951+
JL_GC_POP();
918952
return 0;
919953
}
920954
}
@@ -923,10 +957,12 @@ JL_DLLEXPORT int jl_isa_compileable_sig(
923957
// kind slots always get guard entries (checking for subtypes of Type)
924958
if (jl_subtype(elt, type_i) && !jl_subtype((jl_value_t*)jl_type_type, type_i))
925959
continue;
926-
// TODO: other code paths that could reach here
960+
// TODO: other code paths that could reach here?
961+
JL_GC_POP();
927962
return 0;
928963
}
929964
else if (jl_is_kind(type_i)) {
965+
JL_GC_POP();
930966
return 0;
931967
}
932968

@@ -938,22 +974,31 @@ JL_DLLEXPORT int jl_isa_compileable_sig(
938974
continue;
939975
if (i >= nargs && definition->isva)
940976
continue;
977+
JL_GC_POP();
941978
return 0;
942979
}
943-
if (!iscalled && very_general_type(type_i))
980+
if (!iscalled && very_general_type(type_i)) {
981+
JL_GC_POP();
944982
return 0;
945-
if (!jl_is_datatype(elt))
983+
}
984+
if (!jl_is_datatype(elt)) {
985+
JL_GC_POP();
946986
return 0;
987+
}
947988

948989
// if the declared type was not Any or Union{Type, ...},
949990
// then the match must been with kind, such as UnionAll or DataType,
950991
// and the result of matching the type signature
951992
// needs to be corrected to the concrete type 'kind' (and not to Type)
952993
jl_value_t *kind = jl_typeof(jl_tparam0(elt));
953-
if (kind == jl_bottom_type)
994+
if (kind == jl_bottom_type) {
995+
JL_GC_POP();
954996
return 0; // Type{Union{}} gets normalized to typeof(Union{})
955-
if (jl_subtype(kind, type_i) && !jl_subtype((jl_value_t*)jl_type_type, type_i))
997+
}
998+
if (jl_subtype(kind, type_i) && !jl_subtype((jl_value_t*)jl_type_type, type_i)) {
999+
JL_GC_POP();
9561000
return 0; // gets turned into a kind
1001+
}
9571002

9581003
else if (jl_is_type_type(jl_tparam0(elt)) &&
9591004
// give up on specializing static parameters for Type{Type{Type{...}}}
@@ -966,20 +1011,20 @@ JL_DLLEXPORT int jl_isa_compileable_sig(
9661011
this can be determined using a type intersection.
9671012
*/
9681013
if (i < nargs || !definition->isva) {
969-
jl_value_t *di = jl_type_intersection(type_i, (jl_value_t*)jl_type_type);
970-
JL_GC_PUSH1(&di);
971-
assert(di != (jl_value_t*)jl_bottom_type);
972-
if (jl_is_kind(di)) {
1014+
type_i = jl_type_intersection(type_i, (jl_value_t*)jl_type_type);
1015+
assert(type_i != (jl_value_t*)jl_bottom_type);
1016+
if (jl_is_kind(type_i)) {
9731017
JL_GC_POP();
9741018
return 0;
9751019
}
976-
else if (!jl_types_equal(di, elt)) {
1020+
else if (!jl_types_equal(type_i, elt)) {
9771021
JL_GC_POP();
9781022
return 0;
9791023
}
980-
JL_GC_POP();
1024+
continue;
9811025
}
9821026
else {
1027+
JL_GC_POP();
9831028
return 0;
9841029
}
9851030
}
@@ -1000,12 +1045,16 @@ JL_DLLEXPORT int jl_isa_compileable_sig(
10001045
// when called with a subtype of Function but is not called
10011046
if (elt == (jl_value_t*)jl_function_type)
10021047
continue;
1048+
JL_GC_POP();
10031049
return 0;
10041050
}
10051051

1006-
if (!jl_is_concrete_type(elt))
1052+
if (!jl_is_concrete_type(elt)) {
1053+
JL_GC_POP();
10071054
return 0;
1055+
}
10081056
}
1057+
JL_GC_POP();
10091058
return 1;
10101059
}
10111060

test/core.jl

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7918,3 +7918,12 @@ code_typed(f47476, (Int, Int, Int, Int, Vararg{Union{Int, NTuple{2,Int}}},))
79187918
vect47476(::Type{T}) where {T} = T
79197919
@test vect47476(Type{Type{Type{Int32}}}) === Type{Type{Type{Int32}}}
79207920
@test vect47476(Type{Type{Type{Int64}}}) === Type{Type{Type{Int64}}}
7921+
7922+
g47476(::Union{Nothing,Int,Val{T}}...) where {T} = T
7923+
@test_throws UndefVarError(:T) g47476(nothing, 1, nothing, 2, nothing, 3, nothing, 4, nothing, 5)
7924+
@test g47476(nothing, 1, nothing, 2, nothing, 3, nothing, 4, nothing, 5, Val(6)) === 6
7925+
let spec = only(methods(g47476)).specializations
7926+
@test !isempty(spec)
7927+
@test any(mi -> mi !== nothing && Base.isvatuple(mi.specTypes), spec)
7928+
@test all(mi -> mi === nothing || !Base.has_free_typevars(mi.specTypes), spec)
7929+
end

0 commit comments

Comments
 (0)