Skip to content

Commit 7ebe203

Browse files
committed
typeintersect: followup cleanup for the nothrow path of type instantiation (#54514)
(cherry picked from commit af545b9)
1 parent c81d02c commit 7ebe203

File tree

4 files changed

+94
-50
lines changed

4 files changed

+94
-50
lines changed

src/jltypes.c

Lines changed: 67 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1468,11 +1468,11 @@ jl_unionall_t *jl_rename_unionall(jl_unionall_t *u)
14681468
return (jl_unionall_t*)t;
14691469
}
14701470

1471-
jl_value_t *jl_substitute_var_nothrow(jl_value_t *t, jl_tvar_t *var, jl_value_t *val)
1471+
jl_value_t *jl_substitute_var_nothrow(jl_value_t *t, jl_tvar_t *var, jl_value_t *val, int nothrow)
14721472
{
14731473
if (val == (jl_value_t*)var)
14741474
return t;
1475-
int nothrow = jl_is_typevar(val) ? 0 : 1;
1475+
nothrow = jl_is_typevar(val) ? 0 : nothrow;
14761476
jl_typeenv_t env = { var, val, NULL };
14771477
return inst_type_w_(t, &env, NULL, 1, nothrow);
14781478
}
@@ -1694,7 +1694,7 @@ void jl_precompute_memoized_dt(jl_datatype_t *dt, int cacheable)
16941694
dt->hash = typekey_hash(dt->name, jl_svec_data(dt->parameters), l, cacheable);
16951695
}
16961696

1697-
static void check_datatype_parameters(jl_typename_t *tn, jl_value_t **params, size_t np)
1697+
static int check_datatype_parameters(jl_typename_t *tn, jl_value_t **params, size_t np, int nothrow)
16981698
{
16991699
jl_value_t *wrapper = tn->wrapper;
17001700
jl_value_t **bounds;
@@ -1712,6 +1712,10 @@ static void check_datatype_parameters(jl_typename_t *tn, jl_value_t **params, si
17121712
assert(jl_is_unionall(wrapper));
17131713
jl_tvar_t *tv = ((jl_unionall_t*)wrapper)->var;
17141714
if (!within_typevar(params[i], bounds[2*i], bounds[2*i+1])) {
1715+
if (nothrow) {
1716+
JL_GC_POP();
1717+
return 1;
1718+
}
17151719
if (tv->lb != bounds[2*i] || tv->ub != bounds[2*i+1])
17161720
// pass a new version of `tv` containing the instantiated bounds
17171721
tv = jl_new_typevar(tv->name, bounds[2*i], bounds[2*i+1]);
@@ -1721,12 +1725,26 @@ static void check_datatype_parameters(jl_typename_t *tn, jl_value_t **params, si
17211725
int j;
17221726
for (j = 2*i + 2; j < 2*np; j++) {
17231727
jl_value_t *bj = bounds[j];
1724-
if (bj != (jl_value_t*)jl_any_type && bj != jl_bottom_type)
1725-
bounds[j] = jl_substitute_var(bj, tv, params[i]);
1728+
if (bj != (jl_value_t*)jl_any_type && bj != jl_bottom_type) {
1729+
int isub = j & 1;
1730+
// use different nothrow level for lb and ub substitution.
1731+
// TODO: This assuming the top instantiation could only start with
1732+
// `nothrow == 2` or `nothrow == 0`. If `nothrow` is initially set to 1
1733+
// then we might miss some inner error, perhaps the normal path should
1734+
// also follow this rule?
1735+
jl_value_t *nb = jl_substitute_var_nothrow(bj, tv, params[i], nothrow ? (isub ? 2 : 1) : 0 );
1736+
if (nb == NULL) {
1737+
assert(nothrow);
1738+
JL_GC_POP();
1739+
return 1;
1740+
}
1741+
bounds[j] = nb;
1742+
}
17261743
}
17271744
wrapper = ((jl_unionall_t*)wrapper)->body;
17281745
}
17291746
JL_GC_POP();
1747+
return 0;
17301748
}
17311749

17321750
jl_value_t *extract_wrapper(jl_value_t *t JL_PROPAGATES_ROOT) JL_GLOBALLY_ROOTED
@@ -1943,13 +1961,8 @@ static jl_value_t *inst_datatype_inner(jl_datatype_t *dt, jl_svec_t *p, jl_value
19431961
// for whether this is even valid
19441962
if (check && !istuple) {
19451963
assert(ntp > 0);
1946-
JL_TRY {
1947-
check_datatype_parameters(tn, iparams, ntp);
1948-
}
1949-
JL_CATCH {
1950-
if (!nothrow) jl_rethrow();
1964+
if (check_datatype_parameters(tn, iparams, ntp, nothrow))
19511965
return NULL;
1952-
}
19531966
}
19541967
else if (ntp == 0 && jl_emptytuple_type != NULL) {
19551968
// empty tuple type case
@@ -2301,7 +2314,8 @@ static jl_value_t *inst_tuple_w_(jl_value_t *t, jl_typeenv_t *env, jl_typestack_
23012314
jl_value_t *elt = jl_svecref(tp, i);
23022315
jl_value_t *pi = inst_type_w_(elt, env, stack, check, nothrow);
23032316
if (pi == NULL) {
2304-
if (i == ntp-1 && jl_is_vararg(elt)) {
2317+
assert(nothrow);
2318+
if (nothrow == 1 || (i == ntp-1 && jl_is_vararg(elt))) {
23052319
t = NULL;
23062320
break;
23072321
}
@@ -2320,6 +2334,10 @@ static jl_value_t *inst_tuple_w_(jl_value_t *t, jl_typeenv_t *env, jl_typestack_
23202334
return t;
23212335
}
23222336

2337+
// `nothrow` means that when type checking fails, the type instantiation should
2338+
// return `NULL` instead of immediately throwing an error. If `nothrow` == 2 then
2339+
// we further assume that the imprecise instantiation for non invariant parameters
2340+
// is acceptable, and inner error (`NULL`) would be ignored.
23232341
static jl_value_t *inst_type_w_(jl_value_t *t, jl_typeenv_t *env, jl_typestack_t *stack, int check, int nothrow)
23242342
{
23252343
size_t i;
@@ -2340,11 +2358,10 @@ static jl_value_t *inst_type_w_(jl_value_t *t, jl_typeenv_t *env, jl_typestack_t
23402358
jl_value_t *var = NULL;
23412359
jl_value_t *newbody = NULL;
23422360
JL_GC_PUSH3(&lb, &var, &newbody);
2343-
JL_TRY {
2344-
lb = inst_type_w_(ua->var->lb, env, stack, check, 0);
2345-
}
2346-
JL_CATCH {
2347-
if (!nothrow) jl_rethrow();
2361+
// set nothrow <= 1 to ensure lb's accuracy.
2362+
lb = inst_type_w_(ua->var->lb, env, stack, check, nothrow ? 1 : 0);
2363+
if (lb == NULL) {
2364+
assert(nothrow);
23482365
t = NULL;
23492366
}
23502367
if (t != NULL) {
@@ -2368,11 +2385,9 @@ static jl_value_t *inst_type_w_(jl_value_t *t, jl_typeenv_t *env, jl_typestack_t
23682385
if (newbody == NULL) {
23692386
t = NULL;
23702387
}
2371-
else if (newbody == (jl_value_t*)jl_emptytuple_type) {
2372-
// NTuple{0} => Tuple{} can make a typevar disappear
2373-
t = (jl_value_t*)jl_emptytuple_type;
2374-
}
2375-
else if (nothrow && !jl_has_typevar(newbody, (jl_tvar_t *)var)) {
2388+
else if (!jl_has_typevar(newbody, (jl_tvar_t *)var)) {
2389+
// inner instantiation might make a typevar disappear, e.g.
2390+
// NTuple{0,T} => Tuple{}
23762391
t = newbody;
23772392
}
23782393
else if (newbody != ua->body || var != (jl_value_t*)ua->var) {
@@ -2389,16 +2404,21 @@ static jl_value_t *inst_type_w_(jl_value_t *t, jl_typeenv_t *env, jl_typestack_t
23892404
jl_value_t *b = NULL;
23902405
JL_GC_PUSH2(&a, &b);
23912406
b = inst_type_w_(u->b, env, stack, check, nothrow);
2407+
if (nothrow) {
2408+
// ensure jl_type_union nothrow.
2409+
if (a && !(jl_is_typevar(a) || jl_is_type(a)))
2410+
a = NULL;
2411+
if (b && !(jl_is_typevar(b) || jl_is_type(b)))
2412+
b = NULL;
2413+
}
23922414
if (a != u->a || b != u->b) {
23932415
if (!check) {
23942416
// fast path for `jl_rename_unionall`.
23952417
t = jl_new_struct(jl_uniontype_type, a, b);
23962418
}
2397-
else if (nothrow && a == NULL) {
2398-
t = b;
2399-
}
2400-
else if (nothrow && b == NULL) {
2401-
t = a;
2419+
else if (a == NULL || b == NULL) {
2420+
assert(nothrow);
2421+
t = nothrow == 1 ? NULL : a == NULL ? b : a;
24022422
}
24032423
else {
24042424
assert(a != NULL && b != NULL);
@@ -2416,15 +2436,21 @@ static jl_value_t *inst_type_w_(jl_value_t *t, jl_typeenv_t *env, jl_typestack_t
24162436
JL_GC_PUSH2(&T, &N);
24172437
if (v->T) {
24182438
T = inst_type_w_(v->T, env, stack, check, nothrow);
2419-
if (T == NULL)
2420-
T = jl_bottom_type;
2421-
if (v->N) // This branch should never throw.
2422-
N = inst_type_w_(v->N, env, stack, check, 0);
2439+
if (T == NULL) {
2440+
if (nothrow == 2)
2441+
T = jl_bottom_type;
2442+
else
2443+
t = NULL;
2444+
}
2445+
if (t && v->N) {
2446+
// set nothrow <= 1 to ensure invariant parameter's accuracy.
2447+
N = inst_type_w_(v->N, env, stack, check, nothrow ? 1 : 0);
2448+
if (N == NULL)
2449+
t = NULL;
2450+
}
24232451
}
2424-
if (T != v->T || N != v->N) {
2425-
// `Vararg` is special, we'd better handle inner error at Tuple level.
2452+
if (t && (T != v->T || N != v->N))
24262453
t = (jl_value_t*)jl_wrap_vararg(T, N, check, nothrow);
2427-
}
24282454
JL_GC_POP();
24292455
return t;
24302456
}
@@ -2443,16 +2469,15 @@ static jl_value_t *inst_type_w_(jl_value_t *t, jl_typeenv_t *env, jl_typestack_t
24432469
int bound = 0;
24442470
for (i = 0; i < ntp; i++) {
24452471
jl_value_t *elt = jl_svecref(tp, i);
2446-
JL_TRY {
2447-
jl_value_t *pi = inst_type_w_(elt, env, stack, check, 0);
2448-
iparams[i] = pi;
2449-
bound |= (pi != elt);
2450-
}
2451-
JL_CATCH {
2452-
if (!nothrow) jl_rethrow();
2472+
// set nothrow <= 1 to ensure invariant parameter's accuracy.
2473+
jl_value_t *pi = inst_type_w_(elt, env, stack, check, nothrow ? 1 : 0);
2474+
if (pi == NULL) {
2475+
assert(nothrow);
24532476
t = NULL;
2477+
break;
24542478
}
2455-
if (t == NULL) break;
2479+
iparams[i] = pi;
2480+
bound |= (pi != elt);
24562481
}
24572482
// if t's parameters are not bound in the environment, return it uncopied (#9378)
24582483
if (t != NULL && bound)

src/julia_internal.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -711,7 +711,7 @@ JL_DLLEXPORT int jl_type_morespecific_no_subtype(jl_value_t *a, jl_value_t *b);
711711
jl_value_t *jl_instantiate_type_with(jl_value_t *t, jl_value_t **env, size_t n);
712712
JL_DLLEXPORT jl_value_t *jl_instantiate_type_in_env(jl_value_t *ty, jl_unionall_t *env, jl_value_t **vals);
713713
jl_value_t *jl_substitute_var(jl_value_t *t, jl_tvar_t *var, jl_value_t *val);
714-
jl_value_t *jl_substitute_var_nothrow(jl_value_t *t, jl_tvar_t *var, jl_value_t *val);
714+
jl_value_t *jl_substitute_var_nothrow(jl_value_t *t, jl_tvar_t *var, jl_value_t *val, int nothrow);
715715
jl_unionall_t *jl_rename_unionall(jl_unionall_t *u);
716716
JL_DLLEXPORT jl_value_t *jl_unwrap_unionall(jl_value_t *v JL_PROPAGATES_ROOT) JL_NOTSAFEPOINT;
717717
JL_DLLEXPORT jl_value_t *jl_rewrap_unionall(jl_value_t *t, jl_value_t *u);

src/subtype.c

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2770,7 +2770,7 @@ static jl_value_t *omit_bad_union(jl_value_t *u, jl_tvar_t *t)
27702770
res = jl_bottom_type;
27712771
}
27722772
else if (obviously_egal(var->lb, ub)) {
2773-
res = jl_substitute_var_nothrow(body, var, ub);
2773+
res = jl_substitute_var_nothrow(body, var, ub, 2);
27742774
if (res == NULL)
27752775
res = jl_bottom_type;
27762776
}
@@ -2961,9 +2961,11 @@ static jl_value_t *finish_unionall(jl_value_t *res JL_MAYBE_UNROOTED, jl_varbind
29612961
}
29622962
if (varval) {
29632963
if (ub_has_dep) { // inner substitution has been handled
2964-
btemp->ub = jl_substitute_var_nothrow(btemp->ub, vb->var, varval);
2965-
if (btemp->ub == NULL)
2964+
jl_value_t *bub = jl_substitute_var_nothrow(btemp->ub, vb->var, varval, 2);
2965+
if (bub == NULL)
29662966
res = jl_bottom_type;
2967+
else
2968+
btemp->ub = bub;
29672969
}
29682970
}
29692971
else if (btemp->ub == (jl_value_t*)vb->var) {
@@ -2998,12 +3000,12 @@ static jl_value_t *finish_unionall(jl_value_t *res JL_MAYBE_UNROOTED, jl_varbind
29983000
if (varval) {
29993001
// you can construct `T{x} where x` even if T's parameter is actually
30003002
// limited. in that case we might get an invalid instantiation here.
3001-
res = jl_substitute_var_nothrow(res, vb->var, varval);
3003+
res = jl_substitute_var_nothrow(res, vb->var, varval, 2);
30023004
// simplify chains of UnionAlls where bounds become equal
30033005
while (res != NULL && jl_is_unionall(res) && obviously_egal(((jl_unionall_t*)res)->var->lb,
30043006
((jl_unionall_t*)res)->var->ub)) {
30053007
jl_unionall_t * ures = (jl_unionall_t *)res;
3006-
res = jl_substitute_var_nothrow(ures->body, ures->var, ures->var->lb);
3008+
res = jl_substitute_var_nothrow(ures->body, ures->var, ures->var->lb, 2);
30073009
}
30083010
if (res == NULL)
30093011
res = jl_bottom_type;

test/subtype.jl

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2608,13 +2608,30 @@ end
26082608
#issue 54356
26092609
abstract type A54356{T<:Real} end
26102610
struct B54356{T} <: A54356{T} end
2611-
let S = Tuple{Val, Val{T}} where {T}, R = Tuple{Val{Val{T}}, Val{T}} where {T}
2612-
# general parameters check
2611+
struct C54356{S,T<:Union{S,Complex{S}}} end
2612+
struct D54356{S<:Real,T} end
2613+
let S = Tuple{Val, Val{T}} where {T}, R = Tuple{Val{Val{T}}, Val{T}} where {T},
2614+
SS = Tuple{Val, Val{T}, Val{T}} where {T}, RR = Tuple{Val{Val{T}}, Val{T}, Val{T}} where {T}
2615+
# parameters check for self
26132616
@testintersect(Tuple{Val{A}, A} where {B, A<:Union{Val{B}, Complex{B}}}, S{1}, R{1})
2617+
# parameters check for supertype (B54356 -> A54356)
26142618
@testintersect(Tuple{Val{A}, A} where {B, A<:Union{Val{B}, B54356{B}}}, S{1}, R{1})
2619+
# enure unused TypeVar skips the `UnionAll` wrapping
2620+
@testintersect(Tuple{Val{A}, A} where {B, A<:(Union{Val{B}, D54356{B,C}} where {C})}, S{1}, R{1})
2621+
# invariant parameter should not get narrowed
2622+
@testintersect(Tuple{Val{A}, A} where {B, A<:Union{Val{B}, Val{Union{Int,Complex{B}}}}}, S{1}, R{1})
2623+
# bit value could not be `Union` element
2624+
@testintersect(Tuple{Val{A}, A, Val{B}} where {B, A<:Union{B, Val{B}}}, SS{1}, RR{1})
2625+
@testintersect(Tuple{Val{A}, A, Val{B}} where {B, A<:Union{B, Complex{B}}}, SS{1}, Union{})
2626+
# `check_datatype_parameters` should ignore bad `Union` elements in constraint's ub
2627+
T = Tuple{Val{Union{Val{Nothing}, Val{C54356{V,V}}}}, Val{Nothing}} where {Nothing<:V<:Nothing}
2628+
@test T <: S{Nothing}
2629+
@test T <: Tuple{Val{A}, A} where {B, C, A<:Union{Val{B}, Val{C54356{B,C}}}}
2630+
@test T <: typeintersect(Tuple{Val{A}, A} where {B, C, A<:Union{Val{B}, Val{C54356{B,C}}}}, S{Nothing})
26152631
# extra check for Vararg
26162632
@testintersect(Tuple{Val{A}, A} where {B, A<:Union{Val{B}, NTuple{B,Any}}}, S{-1}, R{-1})
26172633
@testintersect(Tuple{Val{A}, A} where {B, A<:Union{Val{B}, Tuple{Any,Vararg{Any,B}}}}, S{-1}, R{-1})
2634+
@testintersect(Tuple{Val{A}, A} where {B, A<:Union{Val{B}, Tuple{Vararg{Int,Union{Int,Complex{B}}}}}}, S{1}, R{1})
26182635
# extra check for NamedTuple
26192636
@testintersect(Tuple{Val{A}, A} where {B, A<:Union{Val{B}, NamedTuple{B,Tuple{Int}}}}, S{1}, R{1})
26202637
@testintersect(Tuple{Val{A}, A} where {B, A<:Union{Val{B}, NamedTuple{B,Tuple{Int}}}}, S{(1,)}, R{(1,)})

0 commit comments

Comments
 (0)