Repository: JuliaLang/julia
File: src/subtype.c (C)

Code before:
{
    return is_leaf_bound(v->lb);
}

static jl_value_t *widen_Type(jl_value_t *t JL_PROPAGATES_ROOT) JL_NOTSAFEPOINT
{
    if (jl_is_type_type(t) && !jl_is_typevar(jl_tparam0(t)))
        return jl_typeof(jl_tparam0(t));
    if (jl_is_uniontype(t)) {
        jl_value_t *a = widen_Type(((jl_uniontype_t*)t)->a);
        jl_value_t *b = widen_Type(((jl_uniontype_t*)t)->b);
        if (a == b)
            return a;
    }
    return t;
}

// Map Type{X} to kind type (DataType, UnionAll, Union, TypeofBottom) over union
static jl_value_t *widen_Type_union_pointwise(jl_value_t *t)
{
    if (jl_is_type_type(t) && !jl_is_typevar(jl_tparam0(t)))
        return jl_typeof(jl_tparam0(t));
    if (jl_is_uniontype(t)) {
        jl_value_t *a = NULL, *b = NULL;
        JL_GC_PUSH2(&a, &b);
        a = widen_Type_union_pointwise(((jl_uniontype_t*)t)->a);
        b = widen_Type_union_pointwise(((jl_uniontype_t*)t)->b);
        jl_value_t *w;
        if (a != ((jl_uniontype_t*)t)->a || b != ((jl_uniontype_t*)t)->b)
            w = simple_join(a, b);
        else
            w = t;
        JL_GC_POP();
        return w;
    }
    return t;
}

// convert a type with free variables to a typevar bounded by a UnionAll-wrapped
// version of that type.
// TODO: This loses some inference precision. For example in a case where a
// variable bound is `Vector{_}`, we could potentially infer `Type{Vector{_}} where _`,
// but this causes us to infer the larger `Type{T} where T<:Vector` instead.
// However this is needed because many contexts check `isa(sp, TypeVar)` to determine
// when a static parameter value is not known exactly.
static jl_value_t *fix_inferred_var_bound(jl_tvar_t *var, jl_value_t *ty JL_MAYBE_UNROOTED)
{
    if (ty == NULL) // may happen if the user is intersecting with an incomplete type
        return (jl_value_t*)var;
    if (!jl_is_typevar(ty) && jl_has_free_typevars(ty)) {
        jl_value_t *ans = ty;

Review comment (suggestion):
Perhaps we can fuse this with the `widen_Type` by adding a  `concrete_only` like flag?
```c
static jl_value_t *widen_Type(jl_value_t *t JL_PROPAGATES_ROOT, int concrete_only) JL_NOTSAFEPOINT
{
    if (jl_is_type_type(t) && !jl_is_typevar(jl_tparam0(t)))
        return jl_typeof(jl_tparam0(t));
    if (jl_is_uniontype(t)) {
        jl_value_t *a = widen_Type(((jl_uniontype_t*)t)->a, concrete_only);
        jl_value_t *b = widen_Type(((jl_uniontype_t*)t)->b, concrete_only);
        if (concrete_only) {
            if (a == b) return a;
        }
        else {
            if (a != ((jl_uniontype_t*)t)->a || b != ((jl_uniontype_t*)t)->b) {
                JL_GC_PUSH2(&a, &b);
                jl_value_t *ans = simple_join(a, b);
                JL_GC_POP();
                return ans;
            }
        }
    }
    // add the missing unionall handling (`Tuple{Union{Type{Vector{T}} where {T},Type{Int}},Type{Int8}} <: Tuple{S, S} where {S}` returns false on master)
    if (jl_is_unionall(t)) {
        jl_unionall_t *u = (jl_unionall_t*)t;
        jl_value_t *body = widen_Type(u->body, concrete_only);
        if (body != u->body) {
            if (concrete_only || !jl_has_typevar(body, u->var)) {
                return body;
            }
            else {
                JL_GC_PUSH1(&body);
                jl_value_t *ans = (jl_value_t*)jl_type_unionall(u->var, body);
                JL_GC_POP();
                return ans;
            }
        }
    }
    return t;
}
```


Code after:
{
    return is_leaf_bound(v->lb);
}

static jl_value_t *widen_Type_if_concrete(jl_value_t *t JL_PROPAGATES_ROOT) JL_NOTSAFEPOINT
{
    if (jl_is_type_type(t) && !jl_is_typevar(jl_tparam0(t)))
        return jl_typeof(jl_tparam0(t));
    if (jl_is_uniontype(t)) {
        jl_value_t *a = widen_Type_if_concrete(((jl_uniontype_t*)t)->a);
        jl_value_t *b = widen_Type_if_concrete(((jl_uniontype_t*)t)->b);
        if (a == b)
            return a;
    }
    if (jl_is_unionall(t)) {
        jl_unionall_t *u = (jl_unionall_t*)t;
        jl_value_t *body = widen_Type_if_concrete(u->body);
        if (body != u->body && !jl_has_typevar(body, u->var))
            return body;
    }
    return t;
}

static int try_subtype_in_env(jl_value_t *a, jl_value_t *b, jl_stenv_t *e);

// Map Type{X} to kind type (DataType, UnionAll, Union, TypeofBottom) over union
// only if the widened kind satisfies `bound` , otherwise leave unchanged
static jl_value_t *widen_Type_to_union(jl_value_t *t, jl_value_t *bound, jl_stenv_t *e)
{
    if (jl_is_type_type(t) && !jl_is_typevar(jl_tparam0(t))) {
        jl_value_t *w = jl_typeof(jl_tparam0(t));
        if (!try_subtype_in_env(w, bound, e))
            return t;
        return w;
    }
    if (jl_is_uniontype(t)) {
        jl_value_t *wa = NULL, *wb = NULL;
        JL_GC_PUSH2(&wa, &wb);
        wa = widen_Type_to_union(((jl_uniontype_t*)t)->a, bound, e);
        wb = widen_Type_to_union(((jl_uniontype_t*)t)->b, bound, e);
        if (wa != ((jl_uniontype_t*)t)->a || wb != ((jl_uniontype_t*)t)->b)
            wa = simple_join(wa, wb);
        else
            wa = t;
        JL_GC_POP();
        return wa;
    }
    if (jl_is_unionall(t)) {
        jl_unionall_t *u = (jl_unionall_t*)t;
        jl_value_t *body = NULL;
        JL_GC_PUSH1(&body);