Skip to content

Commit

Permalink
Add static analysis annotations for subtype.c
Browse files Browse the repository at this point in the history
  • Loading branch information
Keno committed Aug 21, 2018
1 parent 13820d0 commit 4d6d216
Show file tree
Hide file tree
Showing 3 changed files with 42 additions and 27 deletions.
2 changes: 1 addition & 1 deletion src/julia.h
Original file line number Diff line number Diff line change
Expand Up @@ -1073,7 +1073,7 @@ JL_DLLEXPORT uintptr_t jl_object_id(jl_value_t *v) JL_NOTSAFEPOINT;

// type predicates and basic operations
JL_DLLEXPORT int jl_has_free_typevars(jl_value_t *v) JL_NOTSAFEPOINT;
JL_DLLEXPORT int jl_has_typevar(jl_value_t *t, jl_tvar_t *v);
JL_DLLEXPORT int jl_has_typevar(jl_value_t *t, jl_tvar_t *v) JL_NOTSAFEPOINT;
JL_DLLEXPORT int jl_has_typevar_from_unionall(jl_value_t *t, jl_unionall_t *ua);
JL_DLLEXPORT int jl_subtype_env_size(jl_value_t *t);
JL_DLLEXPORT int jl_subtype_env(jl_value_t *x, jl_value_t *y, jl_value_t **env, int envsz);
Expand Down
4 changes: 2 additions & 2 deletions src/julia_internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -397,10 +397,10 @@ jl_fptr_args_t jl_get_builtin_fptr(jl_value_t *b);
extern uv_loop_t *jl_io_loop;
void jl_uv_flush(uv_stream_t *stream);

typedef struct _typeenv {
typedef struct jl_typeenv_t {
jl_tvar_t *var;
jl_value_t *val;
struct _typeenv *prev;
struct jl_typeenv_t *prev;
} jl_typeenv_t;

int jl_tuple_isa(jl_value_t **child, size_t cl, jl_datatype_t *pdt);
Expand Down
63 changes: 39 additions & 24 deletions src/subtype.c
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ typedef struct {
// during the computation.
// Most of the complexity is due to the "diagonal rule", requiring us to
// identify which type vars range over only concrete types.
typedef struct _varbinding {
typedef struct jl_varbinding_t {
jl_tvar_t *var;
jl_value_t *lb;
jl_value_t *ub;
Expand All @@ -79,14 +79,16 @@ typedef struct _varbinding {
// array of typevars that our bounds depend on, whose UnionAlls need to be
// moved outside ours.
jl_array_t *innervars;
struct _varbinding *prev;
struct jl_varbinding_t *prev;
} jl_varbinding_t;

// subtype algorithm state
typedef struct {
typedef struct jl_stenv_t {
// N.B.: varbindings are created on the stack and rooted there
jl_varbinding_t *vars; // type variable environment
jl_unionstate_t Lunions; // union state for unions on the left of A <: B
jl_unionstate_t Runions; // union state for unions on the right
// N.B.: envout is gc-rooted
jl_value_t **envout; // for passing caller the computed bounds of right-side variables
int envsz; // length of envout
int envidx; // current index in envout
Expand All @@ -99,7 +101,10 @@ typedef struct {
// state manipulation utilities

// look up a type variable in an environment
static jl_varbinding_t *lookup(jl_stenv_t *e, jl_tvar_t *v)
#ifdef __clang_analyzer__
static jl_varbinding_t *lookup(jl_stenv_t *e, jl_tvar_t *v) JL_GLOBALLY_ROOTED JL_NOTSAFEPOINT;
#else
static jl_varbinding_t *lookup(jl_stenv_t *e, jl_tvar_t *v) JL_GLOBALLY_ROOTED JL_NOTSAFEPOINT
{
jl_varbinding_t *b = e->vars;
while (b != NULL) {
Expand All @@ -108,15 +113,16 @@ static jl_varbinding_t *lookup(jl_stenv_t *e, jl_tvar_t *v)
}
return b;
}
#endif

static int statestack_get(jl_unionstate_t *st, int i)
static int statestack_get(jl_unionstate_t *st, int i) JL_NOTSAFEPOINT
{
assert(i >= 0 && i < sizeof(st->stack) * 8);
// get the `i`th bit in an array of 32-bit words
return (st->stack[i>>5] & (1<<(i&31))) != 0;
}

static void statestack_set(jl_unionstate_t *st, int i, int val)
static void statestack_set(jl_unionstate_t *st, int i, int val) JL_NOTSAFEPOINT
{
assert(i >= 0 && i < sizeof(st->stack) * 8);
if (val)
Expand All @@ -140,6 +146,10 @@ static void save_env(jl_stenv_t *e, jl_value_t **root, jl_savedenv_t *se)
}
*root = (jl_value_t*)jl_alloc_svec(len*3);
se->buf = (int8_t*)(len ? malloc(len*2) : NULL);
#ifdef __clang_analyzer__
if (len)
memset(se->buf, 0, len*2);
#endif
int i=0, j=0; v = e->vars;
while (v != NULL) {
jl_svecset(*root, i++, v->lb);
Expand All @@ -152,7 +162,7 @@ static void save_env(jl_stenv_t *e, jl_value_t **root, jl_savedenv_t *se)
se->rdepth = e->Runions.depth;
}

static void restore_env(jl_stenv_t *e, jl_value_t *root, jl_savedenv_t *se)
static void restore_env(jl_stenv_t *e, jl_value_t *root, jl_savedenv_t *se) JL_NOTSAFEPOINT
{
jl_varbinding_t *v = e->vars;
int i = 0, j = 0;
Expand All @@ -163,6 +173,7 @@ static void restore_env(jl_stenv_t *e, jl_value_t *root, jl_savedenv_t *se)
i++;
if (root) v->innervars = (jl_array_t*)jl_svecref(root, i);
i++;
assert(se->buf);
v->occurs_inv = se->buf[j++];
v->occurs_cov = se->buf[j++];
v = v->prev;
Expand Down Expand Up @@ -416,7 +427,7 @@ static jl_unionall_t *rename_unionall(jl_unionall_t *u)

static int subtype(jl_value_t *x, jl_value_t *y, jl_stenv_t *e, int param);

static jl_value_t *pick_union_element(jl_value_t *u, jl_stenv_t *e, int8_t R)
static jl_value_t *pick_union_element(jl_value_t *u JL_PROPAGATES_ROOT, jl_stenv_t *e, int8_t R) JL_NOTSAFEPOINT
{
jl_unionstate_t *state = R ? &e->Runions : &e->Lunions;
do {
Expand Down Expand Up @@ -467,7 +478,7 @@ static int subtype_ccheck(jl_value_t *x, jl_value_t *y, jl_stenv_t *e)

// use the current context to record where a variable occurred, for the purpose
// of determining whether the variable is concrete.
static void record_var_occurrence(jl_varbinding_t *vb, jl_stenv_t *e, int param)
static void record_var_occurrence(jl_varbinding_t *vb, jl_stenv_t *e, int param) JL_NOTSAFEPOINT
{
if (vb != NULL && param) {
// saturate counters at 2; we don't need values bigger than that
Expand Down Expand Up @@ -552,7 +563,7 @@ static int var_gt(jl_tvar_t *b, jl_value_t *a, jl_stenv_t *e, int param)
// check that a type is concrete or quasi-concrete (Type{T}).
// this is used to check concrete typevars:
// issubtype is false if the lower bound of a concrete type var is not concrete.
static int is_leaf_bound(jl_value_t *v)
static int is_leaf_bound(jl_value_t *v) JL_NOTSAFEPOINT
{
if (v == jl_bottom_type)
return 1;
Expand All @@ -567,12 +578,12 @@ static int is_leaf_bound(jl_value_t *v)
return !jl_is_type(v) && !jl_is_typevar(v);
}

static int is_leaf_typevar(jl_tvar_t *v)
static int is_leaf_typevar(jl_tvar_t *v) JL_NOTSAFEPOINT
{
return is_leaf_bound(v->lb);
}

static jl_value_t *widen_Type(jl_value_t *t)
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));
Expand All @@ -594,7 +605,7 @@ JL_DLLEXPORT jl_array_t *jl_find_free_typevars(jl_value_t *v);
// 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)
static jl_value_t *fix_inferred_var_bound(jl_tvar_t *var, jl_value_t *ty JL_MAYBE_UNROOTED)
{
if (!jl_is_typevar(ty) && jl_has_free_typevars(ty)) {
jl_value_t *ans = ty;
Expand All @@ -612,7 +623,7 @@ static jl_value_t *fix_inferred_var_bound(jl_tvar_t *var, jl_value_t *ty)
return ty;
}

static int var_occurs_inside(jl_value_t *v, jl_tvar_t *var, int inside, int want_inv);
static int var_occurs_inside(jl_value_t *v, jl_tvar_t *var, int inside, int want_inv) JL_NOTSAFEPOINT;

// compare UnionAll type `u` to `t`. `R==1` if `u` came from the right side of A <: B.
static int subtype_unionall(jl_value_t *t, jl_unionall_t *u, jl_stenv_t *e, int8_t R, int param)
Expand Down Expand Up @@ -729,8 +740,9 @@ static int subtype_unionall(jl_value_t *t, jl_unionall_t *u, jl_stenv_t *e, int8
}

// unwrap <=2 layers of UnionAlls, leaving the vars in *p1 and *p2 and returning the body
static jl_value_t *unwrap_2_unionall(jl_value_t *t, jl_tvar_t **p1, jl_tvar_t **p2)
static jl_value_t *unwrap_2_unionall(jl_value_t *t, jl_tvar_t **p1, jl_tvar_t **p2) JL_NOTSAFEPOINT
{
assert(t);
if (jl_is_unionall(t)) {
*p1 = ((jl_unionall_t*)t)->var;
t = ((jl_unionall_t*)t)->body;
Expand Down Expand Up @@ -950,6 +962,7 @@ static int subtype(jl_value_t *x, jl_value_t *y, jl_stenv_t *e, int param)
jl_value_t *ux = jl_unwrap_unionall(x);
jl_value_t *uy = jl_unwrap_unionall(y);
if ((x != ux || y != uy) && y != (jl_value_t*)jl_any_type && jl_is_datatype(ux) && jl_is_datatype(uy)) {
assert(ux);
jl_datatype_t *xd = (jl_datatype_t*)ux, *yd = (jl_datatype_t*)uy;
while (xd != NULL && xd != jl_any_type && xd->name != yd->name) {
xd = xd->super;
Expand Down Expand Up @@ -1330,7 +1343,7 @@ static jl_value_t *intersect_ufirst(jl_value_t *x, jl_value_t *y, jl_stenv_t *e,
}

// set a variable to a non-type constant
static jl_value_t *set_var_to_const(jl_varbinding_t *bb, jl_value_t *v, jl_varbinding_t *othervar)
static jl_value_t *set_var_to_const(jl_varbinding_t *bb, jl_value_t *v JL_MAYBE_UNROOTED, jl_varbinding_t *othervar)
{
int offset = bb->offset;
if (othervar && offset == 0)
Expand Down Expand Up @@ -1474,7 +1487,7 @@ static jl_value_t *intersect_var(jl_tvar_t *b, jl_value_t *a, jl_stenv_t *e, int
// test whether `var` occurs inside constructors. `want_inv` tests only inside
// invariant constructors. `inside` means we are currently inside a constructor of the
// requested kind.
static int var_occurs_inside(jl_value_t *v, jl_tvar_t *var, int inside, int want_inv)
static int var_occurs_inside(jl_value_t *v, jl_tvar_t *var, int inside, int want_inv) JL_NOTSAFEPOINT
{
if (v == (jl_value_t*)var) {
return inside;
Expand Down Expand Up @@ -1503,7 +1516,7 @@ static int var_occurs_inside(jl_value_t *v, jl_tvar_t *var, int inside, int want
}

// Caller might not have rooted `res`
static jl_value_t *finish_unionall(jl_value_t *res, jl_varbinding_t *vb, jl_stenv_t *e)
static jl_value_t *finish_unionall(jl_value_t *res JL_MAYBE_UNROOTED, jl_varbinding_t *vb, jl_stenv_t *e)
{
jl_value_t *varval = NULL;
jl_tvar_t *newvar = vb->var;
Expand Down Expand Up @@ -1605,7 +1618,7 @@ static jl_value_t *finish_unionall(jl_value_t *res, jl_varbinding_t *vb, jl_sten
if (res != jl_bottom_type && vb->innervars != NULL) {
int i;
for(i=0; i < jl_array_len(vb->innervars); i++) {
jl_tvar_t *var = (jl_tvar_t*)jl_arrayref(vb->innervars, i);
jl_tvar_t *var = (jl_tvar_t*)jl_array_ptr_ref(vb->innervars, i);
if (jl_has_typevar(res, var))
res = jl_new_struct(jl_unionall_type, (jl_tvar_t*)var, res);
}
Expand Down Expand Up @@ -1896,13 +1909,15 @@ static jl_value_t *intersect_invariant(jl_value_t *x, jl_value_t *y, jl_stenv_t
if (jl_is_typevar(x) && jl_is_typevar(y) && (jl_is_typevar(ii) || !jl_is_type(ii)))
return ii;
if (ii == jl_bottom_type) {
if (!subtype_in_env(x, ii, e))
if (!subtype_in_env(x, jl_bottom_type, e))
return NULL;
flip_vars(e);
if (!subtype_in_env(y, ii, e))
ii = NULL;
if (!subtype_in_env(y, jl_bottom_type, e)) {
flip_vars(e);
return NULL;
}
flip_vars(e);
return ii;
return jl_bottom_type;
}
/*
TODO: This is a band-aid for issue #23685. A better solution would be to
Expand Down Expand Up @@ -2482,7 +2497,7 @@ static int type_morespecific_(jl_value_t *a, jl_value_t *b, int invariant, jl_ty

static int num_occurs(jl_tvar_t *v, jl_typeenv_t *env);

static jl_value_t *nth_tuple_elt(jl_datatype_t *t, size_t i)
static jl_value_t *nth_tuple_elt(jl_datatype_t *t JL_PROPAGATES_ROOT, size_t i) JL_NOTSAFEPOINT
{
size_t len = jl_field_count(t);
if (len == 0)
Expand Down

0 comments on commit 4d6d216

Please sign in to comment.