]> Nishi Git Mirror - gwion.git/commitdiff
:champagne: NonNull :champagne:
authorfennecdjay <astor.jeremie@wanadoo.fr>
Fri, 12 Jul 2019 00:48:37 +0000 (02:48 +0200)
committerfennecdjay <astor.jeremie@wanadoo.fr>
Fri, 12 Jul 2019 00:48:37 +0000 (02:48 +0200)
17 files changed:
include/operator.h
src/emit/emit.c
src/lib/object.c
src/oo/value.c
src/parse/check.c
src/parse/type_decl.c
src/vm/vm.c
tests/nonnull/dynamic_implicit_nonnull.gw [new file with mode: 0644]
tests/nonnull/nonnull2nullable.gw [new file with mode: 0644]
tests/nonnull/nonnull_assign_nonnull.gw [new file with mode: 0644]
tests/nonnull/nonnull_cast_nonnull.gw [new file with mode: 0644]
tests/nonnull/nonnull_err_cast_dynamic.gw [new file with mode: 0644]
tests/nonnull/nonnull_err_dynamic.gw [new file with mode: 0644]
tests/nonnull/nonnull_err_static_cast.gw [new file with mode: 0644]
tests/nonnull/nonnull_implicit_nonnull.gw [new file with mode: 0644]
tests/nonnull/ref_nonnull.gw [new file with mode: 0644]
tests/nonnull/static_implicit_nonnull.gw [new file with mode: 0644]

index 3fb4c4bda3bcfd3e40ba9bd1b98db7d33921838d..27ab6673a4af18b075eed2c50b751673c7220d25 100644 (file)
@@ -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*);
index e169d55efe01de85b9844115314374d823592d26..ae00bef9ac6be1f4b3cc35a3e26e3b399e3cc5de 100644 (file)
@@ -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);
 }
index a0a653a48214d6d542747df4174c288b12810328..3d7c51d8c08bccd3752ad15d7f7fc846968b7cf7 100644 (file)
@@ -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))
index 225a4c0c25dcef9746d9544f9fe6002d6d81a2d9..7fc89708d1e5ed8df604a280964dbb2e33d0e57c 100644 (file)
@@ -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);
 }
 
index 401ac5313e9834cfd37117777d550d893e027200..7e654f7a15158ac2c239fea17b806ad23fb4d590 100644 (file)
@@ -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;
     }
index 92dea09c6779c9a06d0dea48175239118c47f90c..630b3e0aadfa16f0c30f7f7a3794a6b826a65849 100644 (file)
 #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 {
index caa12396baae14d206faff2bc904d2811b625928..ff2fcfc6674513bb348b7f3b87ff0a8f51c74ffd 100644 (file)
@@ -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 (file)
index 0000000..8974831
--- /dev/null
@@ -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 (file)
index 0000000..bed60ff
--- /dev/null
@@ -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 (file)
index 0000000..f282af6
--- /dev/null
@@ -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 (file)
index 0000000..fc8b291
--- /dev/null
@@ -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 (file)
index 0000000..08c0545
--- /dev/null
@@ -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 (file)
index 0000000..f2ed8db
--- /dev/null
@@ -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 (file)
index 0000000..a86c110
--- /dev/null
@@ -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 (file)
index 0000000..96122fb
--- /dev/null
@@ -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 (file)
index 0000000..ce7b130
--- /dev/null
@@ -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 (file)
index 0000000..df693b0
--- /dev/null
@@ -0,0 +1,4 @@
+#! [contains] can't implicitly cast
+fun void test(Object !o) { <<< o >>>; }
+
+null => test;