From f7f69861bcea544e16aee02a5b0f817295c93a76 Mon Sep 17 00:00:00 2001 From: fennecdjay Date: Fri, 12 Jul 2019 02:48:37 +0200 Subject: [PATCH] :champagne: NonNull :champagne: --- include/operator.h | 1 + src/emit/emit.c | 4 +- src/lib/object.c | 77 ++++++++++++++++++++--- src/oo/value.c | 9 +-- src/parse/check.c | 2 +- src/parse/type_decl.c | 22 ++++++- src/vm/vm.c | 12 +++- tests/nonnull/dynamic_implicit_nonnull.gw | 3 + tests/nonnull/nonnull2nullable.gw | 2 + tests/nonnull/nonnull_assign_nonnull.gw | 2 + tests/nonnull/nonnull_cast_nonnull.gw | 1 + tests/nonnull/nonnull_err_cast_dynamic.gw | 3 + tests/nonnull/nonnull_err_dynamic.gw | 5 ++ tests/nonnull/nonnull_err_static_cast.gw | 3 + tests/nonnull/nonnull_implicit_nonnull.gw | 2 + tests/nonnull/ref_nonnull.gw | 3 + tests/nonnull/static_implicit_nonnull.gw | 4 ++ 17 files changed, 137 insertions(+), 18 deletions(-) create mode 100644 tests/nonnull/dynamic_implicit_nonnull.gw create mode 100644 tests/nonnull/nonnull2nullable.gw create mode 100644 tests/nonnull/nonnull_assign_nonnull.gw create mode 100644 tests/nonnull/nonnull_cast_nonnull.gw create mode 100644 tests/nonnull/nonnull_err_cast_dynamic.gw create mode 100644 tests/nonnull/nonnull_err_dynamic.gw create mode 100644 tests/nonnull/nonnull_err_static_cast.gw create mode 100644 tests/nonnull/nonnull_implicit_nonnull.gw create mode 100644 tests/nonnull/ref_nonnull.gw create mode 100644 tests/nonnull/static_implicit_nonnull.gw diff --git a/include/operator.h b/include/operator.h index 3fb4c4bd..27ab6673 100644 --- a/include/operator.h +++ b/include/operator.h @@ -19,6 +19,7 @@ struct Op_Import { struct Implicit { void* e; Type t; + loc_t pos; }; ANN m_bool add_op(const Gwion gwion, const struct Op_Import*); ANN Type op_check(const Env, struct Op_Import*); diff --git a/src/emit/emit.c b/src/emit/emit.c index e169d55e..ae00bef9 100644 --- a/src/emit/emit.c +++ b/src/emit/emit.c @@ -646,6 +646,8 @@ ANN static m_bool emit_exp_decl(const Emitter emit, const Exp_Decl* decl) { CHECK_BB(emit_exp_decl_non_static(emit, list->self, r, var)) else CHECK_BB(emit_exp_decl_global(emit, list->self, r, var)) + if(GET_FLAG(list->self->value->type, nonnull)) + emit_add_instr(emit, GWOP_EXCEPT); } while((list = list->next)); if(global) emit_pop(emit, scope); @@ -1014,7 +1016,7 @@ ANN static m_bool emit_exp_unary(const Emitter emit, const Exp_Unary* unary) { ANN static m_bool emit_implicit_cast(const Emitter emit, const restrict Exp from, const restrict Type to) { - const struct Implicit imp = { from, to }; + const struct Implicit imp = { from, to, from->pos }; struct Op_Import opi = { .op=insert_symbol("@implicit"), .lhs=from->type, .rhs=to, .data=(m_uint)&imp }; return op_emit(emit, &opi); } diff --git a/src/lib/object.c b/src/lib/object.c index a0a653a4..3d7c51d8 100644 --- a/src/lib/object.c +++ b/src/lib/object.c @@ -13,6 +13,7 @@ #include "object.h" #include "operator.h" #include "import.h" +#include "emit.h" ANN void exception(const VM_Shred shred, const m_str c) { gw_err("%s: shred[id=%" UINT_F ":%s], PC=[%" UINT_F "]\n", @@ -97,6 +98,22 @@ static INSTR(name##Object) {\ describe_logical(Eq, ==) describe_logical(Neq, !=) +static inline m_bool nonnull_check(const Type l, const Type r) { + return !GET_FLAG(l, nonnull) && GET_FLAG(r, nonnull); +} + +static inline Type check_nonnull(const Env env, const Type l, const Type r, + const m_str action, const loc_t pos) { + if(GET_FLAG(r, nonnull)) { + if(isa(l, t_null) > 0) + ERR_N(pos, _("can't %s '%s' to '%s'"), action, l->name, r->name); + return r->e->parent; + } + if(nonnull_check(l, r)) + ERR_N(pos, _("can't %s '%s' to '%s'"), action, l->name, r->name); + return r; +} + static OP_CHECK(at_object) { const Exp_Binary* bin = (Exp_Binary*)data; const Type l = bin->lhs->type; @@ -105,26 +122,39 @@ static OP_CHECK(at_object) { return t_null; if(bin->rhs->exp_type == ae_exp_decl) SET_FLAG(bin->rhs->d.exp_decl.td, ref); - if(l != t_null && isa(l, r) < 0) { - env_err(env, exp_self(bin)->pos, _("'%s' @=> '%s': not allowed"), l->name, r->name); + const Type t = check_nonnull(env, l, r, "assign", exp_self(bin)->pos); + if(t == t_null) return t_null; - } + if(l != t_null && isa(l, t) < 0) + ERR_N(exp_self(bin)->pos, _("'%s' @=> '%s': not allowed"), l->name, r->name); bin->rhs->emit_var = 1; return r; } +static OP_EMIT(opem_at_object) { + const Exp_Binary* bin = (Exp_Binary*)data; + const Type l = bin->lhs->type; + const Type r = bin->rhs->type; + if(nonnull_check(l, r)) { + const Instr instr = emit_add_instr(emit, GWOP_EXCEPT); + instr->m_val = SZ_INT; + } + emit_add_instr(emit, ObjectAssign); + return GW_OK; +} + #define STR_FORCE ":force" #define STRLEN_FORCE strlen(STR_FORCE) static inline Type new_force_type(MemPool p, const Type t, const Symbol sym) { const Type ret = type_copy(p, t); -// ret->name = s_name(sym); - SET_FLAG(ret, force); + ret->name = s_name(sym); + ret->flag = t->flag | ae_flag_force; // nspc_add_type(t->e->owner, sym, ret); // map_set(&t->e->owner->info->type->map, sym, ret); map_set(vector_front(&t->e->owner->info->type->ptr), sym, ret); return ret; -} + } static Type get_force_type(const Env env, const Type t) { const size_t len = strlen(t->name); @@ -140,14 +170,39 @@ static OP_CHECK(opck_object_cast) { const Exp_Cast* cast = (Exp_Cast*)data; const Type l = cast->exp->type; const Type r = exp_self(cast)->type; + if(check_nonnull(env, l, r, "cast", exp_self(cast)->pos) == t_null) + return t_null; return isa(l, r) > 0 ? get_force_type(env, r) : t_null; } +static OP_EMIT(opem_object_cast) { + const Exp_Cast* cast = (Exp_Cast*)data; + const Type l = cast->exp->type; + const Type r = exp_self(cast)->type; + if(nonnull_check(l, r)) + emit_add_instr(emit, GWOP_EXCEPT); + return GW_OK; +} + static OP_CHECK(opck_implicit_null2obj) { const struct Implicit* imp = (struct Implicit*)data; + const Type l = ((Exp)imp->e)->type; + const Type r = imp->t; + if(check_nonnull(env, l, r, "implicitly cast", imp->pos) == t_null) + return t_null; + ((Exp)imp->e)->cast_to = r; return imp->t; } +static OP_EMIT(opem_implicit_null2obj) { + const struct Implicit* imp = (struct Implicit*)data; + const Type l = ((Exp)imp->e)->type; + const Type r = imp->t; + if(nonnull_check(l, r)) + emit_add_instr(emit, GWOP_EXCEPT); + return GW_OK; +} + GWION_IMPORT(object) { t_object = gwi_mk_type(gwi, "Object", SZ_INT, NULL); GWI_BB(gwi_class_ini(gwi, t_object, NULL, NULL)) @@ -157,18 +212,22 @@ GWION_IMPORT(object) { GWI_BB(gwi_oper_end(gwi, "@=>", ObjectAssign)) GWI_BB(gwi_oper_ini(gwi, "Object", "Object", NULL)) GWI_BB(gwi_oper_add(gwi, at_object)) + GWI_BB(gwi_oper_emi(gwi, opem_at_object)) GWI_BB(gwi_oper_end(gwi, "@=>", ObjectAssign)) GWI_BB(gwi_oper_ini(gwi, "Object", "Object", "int")) GWI_BB(gwi_oper_end(gwi, "==", EqObject)) GWI_BB(gwi_oper_end(gwi, "!=", NeqObject)) GWI_BB(gwi_oper_add(gwi, opck_object_cast)) - GWI_BB(gwi_oper_emi(gwi, opem_basic_cast)) + GWI_BB(gwi_oper_emi(gwi, opem_object_cast)) GWI_BB(gwi_oper_end(gwi, "$", NULL)) + GWI_BB(gwi_oper_add(gwi, opck_implicit_null2obj)) + GWI_BB(gwi_oper_emi(gwi, opem_implicit_null2obj)) + GWI_BB(gwi_oper_end(gwi, "@implicit", NULL)) GWI_BB(gwi_oper_ini(gwi, "@null", "Object", "int")) GWI_BB(gwi_oper_end(gwi, "==", EqObject)) GWI_BB(gwi_oper_end(gwi, "!=", NeqObject)) - GWI_BB(gwi_oper_add(gwi, opck_basic_cast)) - GWI_BB(gwi_oper_emi(gwi, opem_basic_cast)) + GWI_BB(gwi_oper_add(gwi, opck_object_cast)) + GWI_BB(gwi_oper_emi(gwi, opem_object_cast)) GWI_BB(gwi_oper_end(gwi, "$", NULL)) GWI_BB(gwi_oper_add(gwi, opck_implicit_null2obj)) GWI_BB(gwi_oper_end(gwi, "@implicit", NULL)) diff --git a/src/oo/value.c b/src/oo/value.c index 225a4c0c..7fc89708 100644 --- a/src/oo/value.c +++ b/src/oo/value.c @@ -9,12 +9,13 @@ #include "type.h" ANN static void free_value(Value a, Gwion gwion) { + const Type t = !GET_FLAG(a->type, nonnull) ? a->type : a->type->e->parent; if(!GET_FLAG(a, func) && a->d.ptr && !GET_FLAG(a, union) && !(GET_FLAG(a, enum) && GET_FLAG(a, builtin) && a->owner_class) - && isa(a->type, t_object) < 0) - _mp_free(gwion->mp, a->type->size, a->d.ptr); - if(isa(a->type, t_class) > 0/* || isa(a->type, t_function) > 0*/) - REM_REF(a->type, gwion) + && isa(t, t_object) < 0) + _mp_free(gwion->mp, t->size, a->d.ptr); + if(isa(t, t_class) > 0/* || isa(a->type, t_function) > 0*/) + REM_REF(t, gwion) mp_free(gwion->mp, Value, a); } diff --git a/src/parse/check.c b/src/parse/check.c index 401ac531..7e654f7a 100644 --- a/src/parse/check.c +++ b/src/parse/check.c @@ -347,7 +347,7 @@ ANN static m_bool func_match_inner(const Env env, const Exp e, const Type t, return check_lambda(env, owner, &e->d.exp_lambda, t->e->d.func->def); } if(implicit) { - const struct Implicit imp = { e, t }; + const struct Implicit imp = { e, t, e->pos }; struct Op_Import opi = { .op=insert_symbol("@implicit"), .lhs=e->type, .rhs=t, .data=(m_uint)&imp, .pos=e->pos }; return op_check(env, &opi) ? 1 : -1; } diff --git a/src/parse/type_decl.c b/src/parse/type_decl.c index 92dea09c..630b3e0a 100644 --- a/src/parse/type_decl.c +++ b/src/parse/type_decl.c @@ -10,10 +10,30 @@ #include "traverse.h" #include "parse.h" +#define STR_NONNULL ":nonnull" +#define STRLEN_NONNULL strlen(STR_NONNULL) + ANN Type type_decl_resolve(const Env env, const Type_Decl* td) { DECL_OO(const Type, base, = find_type(env, td->xid)) DECL_OO(const Type, t, = scan_type(env, base, td)) - return !td->array ? t : array_type(env, t, td->array->depth); + const Type ret = !td->array ? t : array_type(env, t, td->array->depth); + if(GET_FLAG(td, nonnull)) { + char c[strlen(t->name) + 9]; + sprintf(c, "%s%s", ret->name, STR_NONNULL); + const Symbol sym = insert_symbol(c); + const Type exist = nspc_lookup_type1(t->e->owner, sym); + if(exist) + return exist; + const Type t = type_copy(env->gwion->mp, ret); +assert(t->size == SZ_INT); + t->name = s_name(sym); + t->e->parent = ret; + SET_FLAG(t, nonnull); + ADD_REF(ret); + map_set(vector_front(&t->e->owner->info->type->ptr), sym, t); + return t; + } + return ret; } struct td_info { diff --git a/src/vm/vm.c b/src/vm/vm.c index caa12396..ff2fcfc6 100644 --- a/src/vm/vm.c +++ b/src/vm/vm.c @@ -131,7 +131,7 @@ ANN static inline VM_Shred init_fork_shred(const VM_Shred shred, const VM_Code c return sh; } -#define TEST0(t, pos) if(!*(t*)(reg-pos)){ exception(shred, "ZeroDivideException"); break; } +#define TEST0(t, pos) if(!*(t*)(reg-pos)){ shred->pc = PC; exception(shred, "ZeroDivideException"); break; } #define ADVANCE() byte += BYTECODE_SZ; @@ -612,6 +612,7 @@ regtomemother: DISPATCH() overflow: if(overflow_(mem, shred)) { + shred->pc = PC; exception(shred, "StackOverflow"); continue; } @@ -693,6 +694,7 @@ arrayaccess: gw_err(_(" ... at dimension [%" INT_F "]\n"), VAL); shred->code = code; shred->mem = mem; + shred->pc = PC; exception(shred, "ArrayOutofBounds"); continue; } @@ -733,7 +735,13 @@ remref: release(*(M_Object*)(mem + VAL), shred); DISPATCH() except: - if(!(a.obj = *(M_Object*)(reg-SZ_INT))) { +/* TODO: Refactor except instruction * + * so that * + * VAL = offset (no default SZ_INT) * + * VAL2 = error message * + * grep for GWOP_EXCEPT and Except, exception... */ + if(!(a.obj = *(M_Object*)(reg-SZ_INT-VAL))) { + shred->pc = PC; exception(shred, "NullPtrException"); continue; } diff --git a/tests/nonnull/dynamic_implicit_nonnull.gw b/tests/nonnull/dynamic_implicit_nonnull.gw new file mode 100644 index 00000000..89748315 --- /dev/null +++ b/tests/nonnull/dynamic_implicit_nonnull.gw @@ -0,0 +1,3 @@ +fun void test(Object !o) { <<< o >>>; } + +Object @o => test; diff --git a/tests/nonnull/nonnull2nullable.gw b/tests/nonnull/nonnull2nullable.gw new file mode 100644 index 00000000..bed60ffe --- /dev/null +++ b/tests/nonnull/nonnull2nullable.gw @@ -0,0 +1,2 @@ +#! [contains] can't assign +Object ! o @=> Object @p; diff --git a/tests/nonnull/nonnull_assign_nonnull.gw b/tests/nonnull/nonnull_assign_nonnull.gw new file mode 100644 index 00000000..f282af6b --- /dev/null +++ b/tests/nonnull/nonnull_assign_nonnull.gw @@ -0,0 +1,2 @@ +Object ! o, p; +o @=> p; diff --git a/tests/nonnull/nonnull_cast_nonnull.gw b/tests/nonnull/nonnull_cast_nonnull.gw new file mode 100644 index 00000000..fc8b291e --- /dev/null +++ b/tests/nonnull/nonnull_cast_nonnull.gw @@ -0,0 +1 @@ +(Object! o) $ Object!; diff --git a/tests/nonnull/nonnull_err_cast_dynamic.gw b/tests/nonnull/nonnull_err_cast_dynamic.gw new file mode 100644 index 00000000..08c0545b --- /dev/null +++ b/tests/nonnull/nonnull_err_cast_dynamic.gw @@ -0,0 +1,3 @@ +#! [contains] NullPtrException +Object @o; +<<< o $ Object! >>>; diff --git a/tests/nonnull/nonnull_err_dynamic.gw b/tests/nonnull/nonnull_err_dynamic.gw new file mode 100644 index 00000000..f2ed8dbc --- /dev/null +++ b/tests/nonnull/nonnull_err_dynamic.gw @@ -0,0 +1,5 @@ +#! [contains} NullPtrException +fun void test(Object !o) { + <<< o >>>; +} +Object @ref => test; diff --git a/tests/nonnull/nonnull_err_static_cast.gw b/tests/nonnull/nonnull_err_static_cast.gw new file mode 100644 index 00000000..a86c1107 --- /dev/null +++ b/tests/nonnull/nonnull_err_static_cast.gw @@ -0,0 +1,3 @@ +#! [contains] NullPtrException +Object @ref; +<<< ref $ Object! >>>; diff --git a/tests/nonnull/nonnull_implicit_nonnull.gw b/tests/nonnull/nonnull_implicit_nonnull.gw new file mode 100644 index 00000000..96122fb9 --- /dev/null +++ b/tests/nonnull/nonnull_implicit_nonnull.gw @@ -0,0 +1,2 @@ +fun void test(Object !o) { <<< o >>>; } +Object! o => test; diff --git a/tests/nonnull/ref_nonnull.gw b/tests/nonnull/ref_nonnull.gw new file mode 100644 index 00000000..ce7b130e --- /dev/null +++ b/tests/nonnull/ref_nonnull.gw @@ -0,0 +1,3 @@ +#! [contains] NullPtrException +Object @a @=> Object ! @ o; +<<< o >>>; diff --git a/tests/nonnull/static_implicit_nonnull.gw b/tests/nonnull/static_implicit_nonnull.gw new file mode 100644 index 00000000..df693b00 --- /dev/null +++ b/tests/nonnull/static_implicit_nonnull.gw @@ -0,0 +1,4 @@ +#! [contains] can't implicitly cast +fun void test(Object !o) { <<< o >>>; } + +null => test; -- 2.43.0