]> Nishi Git Mirror - gwion.git/commitdiff
:art: Initial tuple implementation
authorfennecdjay <astor.jeremie@wanadoo.fr>
Thu, 8 Aug 2019 13:30:00 +0000 (15:30 +0200)
committerfennecdjay <astor.jeremie@wanadoo.fr>
Thu, 8 Aug 2019 13:30:27 +0000 (15:30 +0200)
47 files changed:
ast
include/cpy_ast.h
include/instr.h
include/lang_private.h
include/operator.h
include/type.h
src/emit/emit.c
src/lib/engine.c
src/lib/func.c
src/lib/object.c
src/lib/prim.c
src/lib/ptr.c
src/lib/tuple.c [new file with mode: 0644]
src/oo/nspc.c
src/oo/type.c
src/parse/check.c
src/parse/cpy_ast.c
src/parse/scan1.c
src/parse/scan2.c
src/parse/scanx.c
src/parse/template.c
src/parse/tuple.c [new file with mode: 0644]
src/vm/vm.c
tests/nonnull/ref.gw [new file with mode: 0644]
tests/tuple/object2tuple.gw [new file with mode: 0644]
tests/tuple/object2tuple_err.gw [new file with mode: 0644]
tests/tuple/object_array_access_multi.gw [new file with mode: 0644]
tests/tuple/tupl_decl.gw [new file with mode: 0644]
tests/tuple/tuple.gw [new file with mode: 0644]
tests/tuple/tuple_abstract.gw [new file with mode: 0644]
tests/tuple/tuple_aray_access.gw [new file with mode: 0644]
tests/tuple/tuple_aray_access_exceed.gw [new file with mode: 0644]
tests/tuple/tuple_aray_access_multi.gw [new file with mode: 0644]
tests/tuple/tuple_aray_access_not_prim.gw [new file with mode: 0644]
tests/tuple/tuple_array_access_invalid.gw [new file with mode: 0644]
tests/tuple/tuple_at.gw [new file with mode: 0644]
tests/tuple/tuple_at_err.gw [new file with mode: 0644]
tests/tuple/tuple_at_lit.gw [new file with mode: 0644]
tests/tuple/tuple_cast.gw [new file with mode: 0644]
tests/tuple/tuple_cast2.gw [new file with mode: 0644]
tests/tuple/tuple_decl.gw [new file with mode: 0644]
tests/tuple/tuple_implicit.gw [new file with mode: 0644]
tests/tuple/tuple_lit_error.gw [new file with mode: 0644]
tests/tuple/tuple_skip.gw [new file with mode: 0644]
tests/tuple/tuple_skip2.gw [new file with mode: 0644]
tests/tuple/tuple_unknown.gw [new file with mode: 0644]
tests/tuple/tuple_unpack_already_declared.gw [new file with mode: 0644]

diff --git a/ast b/ast
index ac133c43c04c4e53331321c18596d22b214d93f8..d309c7d2f7152d08e0880fdd068666ebc963738a 160000 (submodule)
--- a/ast
+++ b/ast
@@ -1 +1 @@
-Subproject commit ac133c43c04c4e53331321c18596d22b214d93f8
+Subproject commit d309c7d2f7152d08e0880fdd068666ebc963738a
index eaf9115115c3fb88574ee47dbef96872301e70b7..61ae846f1b447a52dd18ae8b6e4f82fd1c091acd 100644 (file)
@@ -1,5 +1,9 @@
 #ifndef __CPY_AST
 #define __CPY_AST
+ANN Array_Sub cpy_array_sub(MemPool, Array_Sub);
+ANN Type_Decl* cpy_type_decl(MemPool, const Type_Decl*);
+ANN Type_List cpy_type_list(MemPool, const Type_List);
+ANN Func_Def cpy_func_def(MemPool, const Func_Def);
 ANN Func_Def cpy_func_def(MemPool, Func_Def);
 ANN struct Func_Base_* cpy_func_base(MemPool, struct Func_Base_*);
 ANN Class_Def cpy_class_def(MemPool, Class_Def);
index 1f97a27dc2cf8c4e070ac677eafb6cf88ab35f36..cd95e22ef5e66eec4a45a052cb9d4eb19dc4a954 100644 (file)
@@ -64,6 +64,9 @@ INSTR(PopArrayClass);
 INSTR(DotTmpl);
 INSTR(GTmpl);
 
+INSTR(TupleCtor);
+INSTR(TupleMember);
+
 struct dottmpl_ {
   size_t len;
   m_str name;
index f7bb22fe2676f05038666dde48b794654a611c90..251f18be159627557e075a97df87d9909f681875 100644 (file)
@@ -14,4 +14,5 @@ ANN m_bool import_array(const Gwi gwi);
 ANN m_bool import_ptr(const Gwi gwi);
 ANN m_bool import_func(const Gwi gwi);
 ANN m_bool import_modules(const Gwi gwi);
+ANN m_bool import_tuple(const Gwi gwi);
 #endif
index 27ab6673a4af18b075eed2c50b751673c7220d25..f7f8dbf0f4a33077223661044e87a0f8133556f6 100644 (file)
@@ -17,7 +17,7 @@ struct Op_Import {
 };
 
 struct Implicit {
-  void* e;
+  Exp e;
   Type  t;
   loc_t pos;
 };
index 082bf705dd5539dd3bf235828f60ac2e36c250ff..ddb647eba93f075597eff0140e6a97475eb67d16 100644 (file)
@@ -10,6 +10,9 @@ struct TypeInfo_ {
     Type      base_type;
   } d;
   struct Vector_ contains;
+  struct Vector_ tuple_form;
+  struct Vector_ tuple_offset;
+  Type_List tuple_tl;
 };
 
 struct Type_ {
@@ -25,7 +28,7 @@ struct Type_ {
 
 extern Type t_void, t_int, t_bool, t_float, t_dur, t_time, t_now, t_complex, t_polar, t_vec3, t_vec4,
   t_null, t_object, t_shred, t_fork, t_event, t_ugen, t_string, t_ptr, t_array, t_gack,
-  t_function, t_fptr, t_vararg, t_lambda, t_class, t_union, t_undefined, t_auto;
+  t_function, t_fptr, t_vararg, t_lambda, t_class, t_union, t_undefined, t_auto, t_tuple;
 
 ANN2(1,3) ANEW Type new_type(MemPool, const m_uint xid, const m_str name, const Type);
 ANEW ANN Type type_copy(MemPool, const Type type);
@@ -51,5 +54,9 @@ ANN static inline m_bool is_fptr(const Type t) {
   return isa(actual_type(t), t_fptr) > 0;
 }
 ANN m_uint get_depth(const Type type);
+
+
+ANN Type tuple_type(const Env, const Vector, const loc_t);
+ANN void tuple_info(const Env, Type_Decl*, const Var_Decl);
 #endif
 
index 303061b634795045fff543f14548ee1f0b929c9e..9737a9852519f4881c9eb0d7805b3c132ed22f81 100644 (file)
@@ -145,11 +145,11 @@ ANN static inline m_uint emit_code_size(const Emitter emit) {
   return vector_size(&emit->code->instr);
 }
 
-ANN static inline m_uint emit_code_offset(const Emitter emit) {
+ANN /*static inline */m_uint emit_code_offset(const Emitter emit) {
   return emit->code->frame->curr_offset;
 }
 
-ANN static inline m_uint emit_local(const Emitter emit, const m_uint size, const m_bool is_obj) {
+ANN /*static inline */m_uint emit_local(const Emitter emit, const m_uint size, const m_bool is_obj) {
   return frame_local(emit->gwion->mp, emit->code->frame, size, is_obj);
 }
 
@@ -202,7 +202,7 @@ ANN ArrayInfo* emit_array_extend_inner(const Emitter emit, const Type t, const E
   info->base = base;
   const Instr alloc = emit_add_instr(emit, ArrayAlloc);
   alloc->m_val = (m_uint)info;
-  if(isa(base, t_object) > 0) {
+  if(isa(base, t_object) > 0 && !GET_FLAG(base, abstract)) {
     emit_pre_constructor_array(emit, base);
     info->is_obj = 1;
   }
@@ -395,8 +395,54 @@ ANN static m_bool prim_array(const Emitter emit, const Exp_Primary * primary) {
 
 ANN static m_bool emit_exp_array(const Emitter emit, const Exp_Array* array) {
   const m_uint is_var = exp_self(array)->emit_var;
-  const m_uint depth = get_depth(array->base->type) - get_depth(exp_self(array)->type);
   CHECK_BB(emit_exp(emit, array->base, 0))
+  const m_uint depth = get_depth(array->base->type) - get_depth(exp_self(array)->type);
+
+const Type t_base = array->base->type;
+Type type = array_base(t_base) ?: t_base;
+if(isa(type, t_tuple) > 0 && isa(exp_self(array)->type, t_tuple) < 0) {
+  Exp e = array->array->exp;
+  while(e->next && e->next->next)
+    e = e->next;
+  const Exp next = e->next;
+  if(!next) {
+      emit_add_instr(emit, GWOP_EXCEPT);
+      const Instr instr = emit_add_instr(emit, TupleMember);
+      instr->m_val = array->array->exp->d.exp_primary.d.num;
+      instr->m_val2 = is_var;
+      emit_add_instr(emit, DotMember); // just a place holder.
+      return GW_OK;
+    }
+    emit_add_instr(emit, GcAdd);
+    e->next = NULL;
+    CHECK_BB(emit_exp(emit, array->array->exp, 0))
+    e->next = next;
+    regpop(emit, (depth) * SZ_INT);
+    emit_add_instr(emit, GWOP_EXCEPT);
+assert(depth);
+    if(depth)
+    for(m_uint i = 0; i < depth-1; ++i) {
+      const Instr access = emit_add_instr(emit, ArrayAccess);
+      access->m_val = i;
+      const Instr get = emit_add_instr(emit, ArrayGet);
+      get->m_val = i;
+      get->m_val2 = -SZ_INT;
+      emit_add_instr(emit, GWOP_EXCEPT);
+    }
+    regpop(emit, SZ_INT);
+    const Instr access = emit_add_instr(emit, ArrayAccess);
+    access->m_val = depth;
+    const Instr get = emit_add_instr(emit, ArrayGet);
+    get->m_val = depth;
+    const Instr push = emit_add_instr(emit, ArrayValid);
+    push->m_val = SZ_INT;
+    emit_add_instr(emit, GWOP_EXCEPT);
+    const Instr instr = emit_add_instr(emit, TupleMember);
+    instr->m_val = next->d.exp_primary.d.num;
+    instr->m_val2 = is_var;
+    emit_add_instr(emit, DotMember); // just a place holder.
+    return GW_OK;
+  }
   emit_add_instr(emit, GcAdd);
   CHECK_BB(emit_exp(emit, array->array->exp, 0))
   regpop(emit, depth * SZ_INT);
@@ -452,6 +498,13 @@ ANN static m_bool prim_id(const Emitter emit, const Exp_Primary* prim) {
   return GW_OK;
 }
 
+ANN static m_bool prim_tuple(const Emitter emit, const Exp_Primary * primary) {
+  CHECK_BB(emit_exp(emit, primary->d.tuple.exp, 1))
+  const Instr instr = emit_add_instr(emit, TupleCtor);
+  instr->m_val = (m_uint)exp_self(primary)->type;
+  return GW_OK;
+}
+
 ANN static m_bool prim_num(const Emitter emit, const Exp_Primary * primary) {
   regpushi(emit, primary->d.num);
   return GW_OK;
@@ -503,10 +556,12 @@ ANN static m_bool prim_gack(const Emitter emit, const Exp_Primary* primary) {
   return GW_OK;
 }
 
+#define prim_unpack dummy_func
 static const _exp_func prim_func[] = {
   (_exp_func)prim_id, (_exp_func)prim_num, (_exp_func)prim_float, (_exp_func)prim_str,
   (_exp_func)prim_array, (_exp_func)prim_gack, (_exp_func)prim_vec, (_exp_func)prim_vec,
-  (_exp_func)prim_vec, (_exp_func)prim_char, (_exp_func)dummy_func,
+  (_exp_func)prim_vec, (_exp_func)prim_tuple, (_exp_func)prim_unpack,
+  (_exp_func)prim_char, (_exp_func)dummy_func,
 };
 
 ANN static m_bool emit_exp_primary(const Emitter emit, const Exp_Primary* prim) {
@@ -551,7 +606,7 @@ ANN static m_bool emit_exp_decl_non_static(const Emitter emit, const Var_Decl va
   const m_bool is_obj = isa(type, t_object) > 0;
   const uint emit_addr = ((is_ref && !array) || isa(type, t_object) < 0) ?
     emit_var : 1;
-  if(is_obj && (is_array || !is_ref) && !GET_FLAG(var_decl->value, ref))
+  if(is_obj && (is_array || !is_ref)/* && !GET_FLAG(var_decl->value, ref)*/)
     CHECK_BB(emit_instantiate_object(emit, type, array, is_ref))
   f_instr *exec = (f_instr*)allocmember;
   if(!GET_FLAG(v, member)) {
@@ -614,14 +669,14 @@ ANN static inline m_bool emit_exp_decl_template(const Emitter emit, const Exp_De
 
 ANN static m_bool emit_exp_decl(const Emitter emit, const Exp_Decl* decl) {
   Var_Decl_List list = decl->list;
-  const uint  ref = GET_FLAG(decl->td, ref) || type_ref(decl->type);
+  const uint ref = GET_FLAG(decl->td, ref) || type_ref(decl->type);
   const uint var = exp_self(decl)->emit_var;
   if(GET_FLAG(decl->type, template))
     CHECK_BB(emit_exp_decl_template(emit, decl))
   const m_bool global = GET_FLAG(decl->td, global);
   const m_uint scope = !global ? emit->env->scope->depth : emit_push_global(emit);
   do {
-    const uint r = (uint)(GET_FLAG(list->self->value, ref) + ref);
+    const uint r = (list->self->array && list->self->array->exp && ref) ? 0 : (uint)(GET_FLAG(list->self->value, ref) + ref);
 //    if(!GET_FLAG(list->self->value, used))
 //      continue;
     if(GET_FLAG(decl->td, static))
@@ -924,7 +979,7 @@ ANN static m_bool spork_func(const Emitter emit, const Exp_Call* exp) {
 ANN m_bool emit_exp_spork(const Emitter emit, const Exp_Unary* unary) {
   const m_bool is_spork = unary->op == insert_symbol("spork");
   const Func f = !unary->code ? unary->exp->d.exp_call.m_func : NULL;
-  if(!f) {
+  if(unary->code) {
     emit_add_instr(emit, RegPushImm);
     push_spork_code(emit, is_spork ? SPORK_CODE_PREFIX : FORK_CODE_PREFIX, unary->code->pos);
     if(!SAFE_FLAG(emit->env->func, member))
@@ -1386,8 +1441,9 @@ ANN static m_bool emit_union_def(const Emitter emit, const Union_Def udef) {
     const Exp exp = new_exp_decl(emit->gwion->mp, type_decl, var_decl_list);
     exp->d.exp_decl.type = udef->value->type;
     var_decl->value = udef->value;
-    CHECK_BB(emit_exp_decl(emit, &exp->d.exp_decl))
+    const m_bool ret = emit_exp_decl(emit, &exp->d.exp_decl);
     free_exp(emit->gwion->mp, exp);
+    CHECK_BB(ret)
     if(global) {
       const M_Object o = new_object(emit->gwion->mp, NULL, udef->value->type);
       udef->value->d.ptr = (m_uint*)o;
index a3f498ad1daf64d4040e87027e2e74e34a920f5c..4313a2a39ee568041d4b3a2a4e1209486daf479c 100644 (file)
@@ -35,9 +35,10 @@ static FREEARG(freearg_gack) {
 }
 
 ANN static m_bool import_core_libs(const Gwi gwi) {
-  GWI_OB((t_undefined = gwi_mk_type(gwi, "@Undefined", SZ_INT, NULL))) // size = SZ_INT to enable declarations
   GWI_OB((t_class = gwi_mk_type(gwi, "Class", SZ_INT, NULL)))
   GWI_BB(gwi_add_type(gwi, t_class))
+  GWI_OB((t_undefined = gwi_mk_type(gwi, "@Undefined", SZ_INT, NULL))) // size = SZ_INT to enable declarations
+  GWI_BB(gwi_add_type(gwi, t_undefined))
   GWI_OB((t_auto = gwi_mk_type(gwi, "auto", SZ_INT, NULL))) // size = SZ_INT to enable declarations
   GWI_BB(gwi_add_type(gwi, t_auto))
   SET_FLAG(t_class, abstract);
@@ -50,7 +51,7 @@ ANN static m_bool import_core_libs(const Gwi gwi) {
   GWI_OB((t_fptr = gwi_mk_type(gwi, "@func_ptr", SZ_INT, t_function)))
   GWI_BB(gwi_add_type(gwi, t_fptr))
   GWI_OB((t_lambda = gwi_mk_type(gwi, "@lambda", SZ_INT, t_function)))
-  GWI_BB(gwi_add_type(gwi, t_fptr))
+  GWI_BB(gwi_add_type(gwi, t_lambda))
   GWI_OB((t_gack = gwi_mk_type(gwi, "@Gack", SZ_INT, NULL)))
   GWI_BB(gwi_add_type(gwi, t_gack))
   GWI_OB((t_int = gwi_mk_type(gwi, "int", SZ_INT, NULL)))
@@ -71,6 +72,7 @@ ANN static m_bool import_core_libs(const Gwi gwi) {
   GWI_OB((t_union = gwi_mk_type(gwi, "@Union", SZ_INT, t_object)))
   GWI_BB(gwi_class_ini(gwi, t_union, NULL, NULL))
   GWI_BB(gwi_class_end(gwi))
+  GWI_BB(import_tuple(gwi))
   GWI_BB(import_array(gwi))
   GWI_BB(import_event(gwi))
   GWI_BB(import_ugen(gwi))
index 68778e622462121d85561340ca99a991c2c4695a..bb431858eacd7f6d77df65b9cb79d94c088290aa 100644 (file)
@@ -241,8 +241,8 @@ static OP_EMIT(opem_fptr_cast) {
 
 static OP_CHECK(opck_fptr_impl) {
   struct Implicit *impl = (struct Implicit*)data;
-  struct FptrInfo info = { ((Exp)impl->e)->type->e->d.func, impl->t->e->d.func,
-      (Exp)impl->e, ((Exp)impl->e)->pos };
+  struct FptrInfo info = { impl->e->type->e->d.func, impl->t->e->d.func,
+      impl->e, impl->e->pos };
   CHECK_BO(fptr_do(env, &info))
   return ((Exp)impl->e)->cast_to = impl->t;
 }
index 932ccff4ee4cd6661cbb9a91126dcc65069f5911..e1c451feeaecb85eb61e7937a9814e8340740487 100644 (file)
@@ -183,17 +183,17 @@ static OP_EMIT(opem_object_cast) {
 
 static OP_CHECK(opck_implicit_null2obj) {
   const struct Implicit* imp = (struct Implicit*)data;
-  const Type l = ((Exp)imp->e)->type;
+  const Type l = 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;
+  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 l = imp->e->type;
   const Type r = imp->t;
   if(nonnull_check(l, r))
     emit_add_instr(emit, GWOP_EXCEPT);
index b52318ee6ec6ab4880328f7e1a51625b0ef380f0..e6788d6fc572efe64f8efa59adeb1998c18c0eaf 100644 (file)
@@ -139,7 +139,7 @@ static OP_CHECK(opck_implicit_f2i) {
 
 static OP_CHECK(opck_implicit_i2f) {
   struct Implicit* imp = (struct Implicit*)data;
-  ((Exp)imp->e)->cast_to = t_float;
+  imp->e->cast_to = t_float;
   return t_float;
 }
 
index 117ff5d47409a94571c4903dc7f056edfaa937db..73388a52a547c56734d831a4be59c3387eff889f 100644 (file)
@@ -54,7 +54,7 @@ static OP_CHECK(opck_ptr_cast) {
 
 static OP_CHECK(opck_implicit_ptr) {
   const struct Implicit* imp = (struct Implicit*)data;
-  const Exp e = (Exp)imp->e;
+  const Exp e = imp->e;
   if(!strcmp(get_type_name(env, imp->t->name, 1), e->type->name)) {
     if(e->meta == ae_meta_value)
       ERR_N(e->pos, _("can't cast constant to Ptr"));
diff --git a/src/lib/tuple.c b/src/lib/tuple.c
new file mode 100644 (file)
index 0000000..7426d6e
--- /dev/null
@@ -0,0 +1,231 @@
+#include "gwion_util.h"
+#include "gwion_ast.h"
+#include "oo.h"
+#include "vm.h"
+#include "env.h"
+#include "instr.h"
+#include "nspc.h"
+#include "type.h"
+#include "object.h"
+#include "emit.h"
+#include "env.h"
+#include "vm.h"
+#include "gwion.h"
+#include "operator.h"
+#include "import.h"
+#include "gwi.h"
+#include "engine.h"
+#include "parser.h"
+#include "value.h"
+#include "cpy_ast.h"
+#include "traverse.h"
+
+struct TupleEmit {
+  Exp e;
+  Vector v;
+  m_uint obj_offset;
+  m_uint tmp_offset;
+  m_uint mem_offset;
+  m_uint sz;
+  m_uint idx;
+};
+
+struct UnpackInfo_ {
+  m_uint obj_offset;
+  m_uint mem_offset;
+  m_uint size;
+};
+
+static INSTR(TupleUnpack) {
+  const M_Object o = *(M_Object*)(shred->reg - SZ_INT);
+  struct UnpackInfo_ *info = (struct UnpackInfo_*)instr->m_val;
+  memcpy(shred->mem + info->mem_offset, o->data + t_tuple->nspc->info->offset + info->obj_offset, info->size);
+}
+
+INSTR(TupleMember) {
+  const M_Object o = *(M_Object*)(shred->reg - SZ_INT);
+  const Type base = o->type_ref;
+  const m_bit* byte = shred->code->bytecode + shred->pc * BYTECODE_SZ;
+  const Type t = (Type)vector_at(&base->e->tuple_form, instr->m_val);
+  const m_uint offset = vector_at(&base->e->tuple_offset, instr->m_val);
+  *(m_uint*)(byte + SZ_INT) = offset;
+  if(!instr->m_val2) {
+    if(t->size == SZ_INT)
+  *(m_uint*)(byte) = eDotMember;
+    else if(t->size == SZ_FLOAT)
+  *(m_uint*)(byte) = eDotMember2;
+    else {
+  *(m_uint*)(byte) = eDotMember3;
+  *(m_uint*)(byte + SZ_INT*2) = t->size;
+    }
+  } else
+  *(m_uint*)(byte) = eDotMember4;
+}
+
+static FREEARG(freearg_tuple_at) {
+  mp_free(((Gwion)gwion)->mp, UnpackInfo, (struct UnpackInfo*)instr->m_val);
+}
+
+ANN static void emit_unpack_instr_inner(const Emitter emit, struct TupleEmit *te) {
+  const Instr instr = emit_add_instr(emit, TupleUnpack);
+  struct UnpackInfo_ *info = mp_malloc2(emit->gwion->mp, sizeof(struct UnpackInfo_));
+  info->obj_offset = te->tmp_offset;
+  info->mem_offset = te->mem_offset;
+  info->size = te->sz;
+  instr->m_val = (m_uint)info;
+}
+
+ANN static int tuple_continue(struct TupleEmit *te) {
+  const m_bool ret = (te->e = te->e->next) &&
+       ++te->idx < VLEN(te->v);
+  if(!ret)
+    te->e = NULL;
+  return ret;
+}
+
+ANN static void unpack_instr_decl(const Emitter emit, struct TupleEmit *te) {
+  m_uint sz = 0;
+  te->sz = 0;
+  do {
+    if(te->e->exp_type == ae_exp_decl) {
+      const Value value = te->e->d.exp_decl.list->self->value;
+      te->sz += value->type->size;
+      value->offset = emit_local(emit, value->type->size, 0);
+    te->tmp_offset = te->obj_offset;
+    te->obj_offset += ((Type)vector_at(te->v, te->idx))->size;
+    } else {
+      sz = ((Type)vector_at(te->v, te->idx))->size;
+      break;
+    }
+  } while(tuple_continue(te));
+      te->obj_offset += sz;
+}
+
+ANN void emit_unpack_instr(const Emitter emit, struct TupleEmit *te) {
+  te->mem_offset = emit_code_offset(emit);
+  unpack_instr_decl(emit, te);
+  if(te->sz)
+    emit_unpack_instr_inner(emit, te);
+  if(te->e && (te->e = te->e->next))
+    emit_unpack_instr(emit, te);
+}
+
+static m_bool tuple_match(const Env env, const Type type[2]) {
+  const Vector lv = &type[0]->e->tuple_form;
+  const Vector rv = &type[1]->e->tuple_form;
+  for(m_uint i = 0; i < vector_size(rv); i++) {
+    DECL_OB(const Type, l, = (Type)vector_at(lv, i))
+    const Type r = (Type)vector_at(rv, i);
+    if(r != t_undefined)
+    CHECK_BB(isa(l, r))
+  }
+  return GW_OK;
+}
+
+static OP_CHECK(opck_at_tuple) {
+  const Exp_Binary *bin = (Exp_Binary*)data;
+  if(bin->rhs->exp_type == ae_exp_primary &&
+    bin->rhs->d.exp_primary.primary_type == ae_primary_unpack) {
+    Exp e = bin->rhs->d.exp_primary.d.tuple.exp;
+    int i = 0;
+    do {
+      if(e->exp_type == ae_exp_decl) {
+        e->d.exp_decl.td->xid->xid = insert_symbol(env->gwion->st, // could be better
+             ((Type)VPTR(&bin->lhs->type->e->tuple_form, i))->name);
+        CHECK_BO(traverse_decl(env, &e->d.exp_decl))
+      }
+      ++i;
+    } while((e = e->next));
+  } else {
+    if(opck_rassign(env, data, mut) == t_null)
+      return t_null;
+    const Type type[2] = { bin->lhs->type, bin->rhs->type };
+    if(tuple_match(env, type) < 0)
+      return t_null;
+    bin->rhs->emit_var = 1;
+  }
+  return bin->lhs->type;
+}
+
+static OP_CHECK(opck_cast_tuple) {
+  Exp_Cast *cast = (Exp_Cast*)data;
+  if(exp_self(cast)->type->e->def && cast->exp->type->e->def) {
+    const Type type[2] = { exp_self(cast)->type, cast->exp->type };
+    CHECK_BO(tuple_match(env, type))
+  }
+  return exp_self(cast)->type;
+}
+
+static OP_CHECK(opck_impl_tuple) {
+  struct Implicit *imp = (struct Implicit*)data;
+  const Type type[2] = { imp->e->type, imp->t };
+  CHECK_BO(tuple_match(env, type))
+  return imp->t;
+}
+
+static OP_EMIT(opem_at_tuple) {
+  const Exp_Binary *bin = (Exp_Binary*)data;
+  if(!(bin->rhs->exp_type == ae_exp_primary &&
+    bin->rhs->d.exp_primary.primary_type == ae_primary_unpack)) {
+    emit_add_instr(emit, ObjectAssign);
+//    emit_add_instr(emit, int_r_assign);
+//    const Instr instr = emit_add_instr(emit, RegAddRef);
+//instr->m_val = 1;
+    return GW_OK;
+  }
+  const Exp e = bin->rhs->d.exp_primary.d.tuple.exp;
+  const Vector v = &bin->lhs->type->e->tuple_form;
+  struct TupleEmit te = { .e=e, .v=v };
+  emit_unpack_instr(emit, &te);
+  return GW_OK;
+}
+
+ANN void tuple_info(const Env env, Type_Decl *base, const Var_Decl var) {
+  const Value v = var->value;
+  if(v->name[0] != '@') {
+    const m_uint offset = vector_back(&env->class_def->e->tuple_offset);
+    vector_add(&env->class_def->e->tuple_form, (vtype)v->type);
+    vector_add(&env->class_def->e->tuple_offset, offset + v->type->size);
+    Type_Decl *td = cpy_type_decl(env->gwion->mp, base);
+    if(var->array) {
+      if(td->array)
+        td->array->depth += var->array->depth;
+      else
+        td->array = cpy_array_sub(env->gwion->mp, var->array);
+    }
+    if(env->class_def->e->tuple_tl) {
+      Type_List tl = env->class_def->e->tuple_tl;
+      while(tl->next)
+        tl = tl->next;
+      tl->next = new_type_list(env->gwion->mp, td, NULL);
+    } else
+    env->class_def->e->tuple_tl = new_type_list(env->gwion->mp, td, NULL);
+  }
+}
+
+INSTR(TupleCtor) {
+  const Type t = (Type)instr->m_val;
+  const M_Object o = new_object(shred->info->vm->gwion->mp, shred, t);
+  const size_t sz = t_tuple->nspc->info->offset;
+  memcpy(o->data + t_tuple->nspc->info->offset,
+      shred->reg - (t->nspc->info->offset - sz), (t->nspc->info->offset - sz));
+  shred->reg -= (t->nspc->info->offset - sz - SZ_INT);
+  *(M_Object*)(shred->reg - SZ_INT) = o;
+}
+
+GWION_IMPORT(tuple) {
+  GWI_OB((t_tuple = gwi_mk_type(gwi, "Tuple", SZ_INT, t_object)))
+  GWI_BB(gwi_class_ini(gwi, t_tuple, NULL, NULL))
+  GWI_BB(gwi_class_end(gwi))
+  SET_FLAG(t_tuple, abstract | ae_flag_template);
+  GWI_BB(gwi_oper_ini(gwi, "Object", "Tuple", NULL))
+  GWI_BB(gwi_oper_add(gwi, opck_at_tuple))
+  GWI_BB(gwi_oper_emi(gwi, opem_at_tuple))
+  GWI_BB(gwi_oper_end(gwi, "@=>", NULL))
+  GWI_BB(gwi_oper_add(gwi, opck_cast_tuple))
+  GWI_BB(gwi_oper_end(gwi, "$", NULL))
+  GWI_BB(gwi_oper_add(gwi, opck_impl_tuple))
+  GWI_BB(gwi_oper_end(gwi, "@implicit", NULL))
+  register_freearg(gwi, TupleUnpack, freearg_tuple_at);
+  return GW_OK;
+}
index 4da25de4e113138717213e767bf8076653bc451e..c3105d3f45c66e6adef7ba60fa21ecc396995fe0 100644 (file)
@@ -58,7 +58,7 @@ ANN static void free_nspc(Nspc a, Gwion gwion) {
   if(a->info->op_map.ptr)
     free_op_map(&a->info->op_map, gwion);
   nspc_free_type(a, gwion);
-  if(a->info->class_data)
+  if(a->info->class_data && a->info->class_data_size)
     mp_free2(gwion->mp, a->info->class_data_size, a->info->class_data);
   if(a->info->vtable.ptr)
     vector_release(&a->info->vtable);
index 60b4905474238528001f51b15eea95d22e25104b..d62b839781bde22718e028bd6a9a717c1b73b0a9 100644 (file)
@@ -18,11 +18,17 @@ ANN static void free_type(Type a, Gwion gwion) {
           free_union_def(gwion->mp, a->e->def->union_def);
       }
       a->e->def->union_def = NULL;
-    } else
+    } else if(a->e->def)
       free_class_def(gwion->mp, a->e->def);
   }
   if(a->nspc)
     REM_REF(a->nspc, gwion);
+  if(a->e->tuple_form.ptr)
+    vector_release(&a->e->tuple_form);
+  if(a->e->tuple_offset.ptr)
+    vector_release(&a->e->tuple_offset);
+  if(a->e->tuple_tl)
+    free_type_list(gwion->mp, a->e->tuple_tl);
   if(a->e->contains.ptr) {
     for(m_uint i = 0; i < vector_size(&a->e->contains); ++i)
       REM_REF((Type)vector_at(&a->e->contains, i), gwion);
@@ -38,8 +44,12 @@ Type new_type(MemPool p, const m_uint xid, const m_str name, const Type parent)
   type->name   = name;
   type->e = mp_calloc(p, TypeInfo);
   type->e->parent = parent;
-  if(type->e->parent)
+  if(type->e->parent) {
     type->size = parent->size;
+    vector_init(&type->e->tuple_form);
+    vector_init(&type->e->tuple_offset);
+    vector_add(&type->e->tuple_offset, 0);
+  }
   type->ref = new_refcount(p, free_type);
   return type;
 }
@@ -52,6 +62,12 @@ ANN Type type_copy(MemPool p, const Type type) {
   a->e->d.base_type   = type->e->d.base_type;
   a->array_depth   = type->array_depth;
   a->e->def           = type->e->def;
+  if(t_function && isa(type, t_function) > 0) {
+    vector_release(&a->e->tuple_form);
+    a->e->tuple_form.ptr = NULL;
+    vector_release(&a->e->tuple_offset);
+    a->e->tuple_offset.ptr = NULL;
+  }
   return a;
 }
 
@@ -138,8 +154,8 @@ ANN m_str get_type_name(const Env env, const m_str s, const m_uint index) {
   m_uint n = 1;
   const size_t len = name ? strlen(name) : 0;
   const size_t slen = strlen(s);
-  const size_t tlen = slen -len + 1;
-  char c[slen];
+  const size_t tlen = slen - len + 1;
+  char c[slen + 1];
 
   if(!name)
     return index ? NULL : s_name(insert_symbol(s));
@@ -178,4 +194,4 @@ ANN m_uint get_depth(const Type type) {
 
 Type t_void, t_int, t_bool, t_float, t_dur, t_time, t_now, t_complex, t_polar, t_vec3, t_vec4,
   t_null, t_object, t_shred, t_fork, t_event, t_ugen, t_string, t_ptr, t_array, t_gack,
-  t_function, t_fptr, t_vararg, t_lambda, t_class, t_union, t_undefined, t_auto;
+  t_function, t_fptr, t_vararg, t_lambda, t_class, t_union, t_undefined, t_auto, t_tuple;
index cbedfca587202a2d81508df9723b38e55a45019b..a40180e4d787539c433912b46427d9e5dce55f4a 100644 (file)
@@ -42,7 +42,8 @@ ANN static inline m_bool check_exp_decl_parent(const Env env, const Var_Decl var
 }
 
 #define describe_check_decl(a, b)                                 \
-ANN static inline void decl_##a(const Nspc nspc, const Value v) { \
+ANN static inline void decl_##a(const Env env, const Value v) { \
+  const Nspc nspc = env->curr;\
   SET_FLAG(v, a);                                                 \
   v->offset = nspc->info->b;                                      \
   nspc->info->b += v->type->size;                                 \
@@ -115,10 +116,13 @@ ANN Type check_exp_decl(const Env env, const Exp_Decl* decl) {
       CHECK_BO(check_exp_decl_parent(env, var))
     if(var->array && var->array->exp)
       CHECK_BO(check_exp_array_subscripts(env, var->array->exp))
-    if(GET_FLAG(decl->td, member))
-      decl_member(env->curr, v);
+    if(GET_FLAG(decl->td, member) && env->class_def) {
+      decl_member(env, v);
+      if(isa(env->class_def, t_object)  > 0)
+        tuple_info(env, decl->td, var);
+    }
     else if(GET_FLAG(decl->td, static))
-      decl_static(env->curr, v);
+      decl_static(env, v);
     else if(global || (env->func && GET_FLAG(env->func->def, global)))
       SET_FLAG(v, abstract);
     if(isa(decl->type, t_fptr) > 0)
@@ -220,17 +224,11 @@ ANN static Type check_exp_prim_this(const Env env, const Exp_Primary* primary) {
 ANN static Type prim_str(const Env env, Exp_Primary *const prim) {
   if(!prim->value) {
     const m_str str = prim->d.str;
+    const Value v = new_value(env->gwion->mp, t_string, str);
     char c[strlen(str) + 8];
     sprintf(c, "%s:string", str);
-    const Symbol sym = insert_symbol(c);
-    const Value exist = nspc_lookup_value0(env->global_nspc, sym);
-    if(exist)
-      prim->value = exist;
-    else {
-      const Value v = new_value(env->gwion->mp, t_string, s_name(sym));
-      nspc_add_value(env->global_nspc, sym, v);
-      prim->value = v;
-    }
+    nspc_add_value(env_nspc(env), insert_symbol(c), v);
+    prim->value = v;
   }
   return t_string;
 }
@@ -305,6 +303,19 @@ ANN static Type prim_gack(const Env env, const Exp_Primary * primary) {
   return t_gack;
 }
 
+ANN static Type prim_tuple(const Env env, const Exp_Primary * primary) {
+  CHECK_OO(check_exp(env, primary->d.tuple.exp))
+  Exp e = primary->d.tuple.exp;
+  struct Vector_ v;
+  vector_init(&v);
+  do {
+    vector_add(&v, (m_uint)e->type);
+  } while((e = e->next));
+  const Type ret = tuple_type(env, &v, exp_self(primary)->pos);
+  vector_release(&v);
+  return ret;
+}
+
 #define describe_prim_xxx(name, type) \
 ANN static Type prim_##name(const Env env NUSED, const Exp_Primary * primary NUSED) {\
   return type; \
@@ -312,12 +323,14 @@ ANN static Type prim_##name(const Env env NUSED, const Exp_Primary * primary NUS
 describe_prim_xxx(num, t_int)
 describe_prim_xxx(float, t_float)
 describe_prim_xxx(nil, t_void)
+describe_prim_xxx(unpack, t_tuple)
 
 typedef Type (*_type_func)(const Env, const void*);
 static const _type_func prim_func[] = {
-  (_type_func)prim_id,    (_type_func)prim_num,  (_type_func)prim_float, (_type_func)prim_str,
-  (_type_func)prim_array, (_type_func)prim_gack, (_type_func)prim_vec,   (_type_func)prim_vec,
-  (_type_func)prim_vec,   (_type_func)prim_num,  (_type_func)prim_nil,
+  (_type_func)prim_id,    (_type_func)prim_num,   (_type_func)prim_float, (_type_func)prim_str,
+  (_type_func)prim_array, (_type_func)prim_gack,  (_type_func)prim_vec,   (_type_func)prim_vec,
+  (_type_func)prim_vec,   (_type_func)prim_tuple, (_type_func)prim_unpack,
+  (_type_func)prim_num,   (_type_func)prim_nil,
 };
 
 ANN static Type check_exp_primary(const Env env, const Exp_Primary* primary) {
@@ -325,11 +338,13 @@ ANN static Type check_exp_primary(const Env env, const Exp_Primary* primary) {
 }
 
 ANN Type at_depth(const Env env, const Type t, const m_uint depth) {
+  if(!depth)
+    return t;
   if(GET_FLAG(t, typedef))
-    return !depth ? t : at_depth(env, t->e->parent, depth);
+    return at_depth(env, t->e->parent, depth);
   if(depth > t->array_depth)
     return at_depth(env, t->e->d.base_type, depth - t->array_depth);
-  return !depth ? t : array_type(env, array_base(t), t->array_depth - depth);
+  return array_type(env, array_base(t), t->array_depth - depth);
 }
 
 static inline m_bool index_is_int(const Env env, Exp e, m_uint *depth) {
@@ -346,16 +361,40 @@ static m_bool array_access_valid(const Env env, const Exp_Array* array) {
   if(depth != array->array->depth)
     ERR_B(exp_self(array)->pos, _("invalid array acces expression."))
   DECL_OB(const Type, t_base,  = check_exp(env, array->base))
-  if(depth > get_depth(t_base))
+  if(depth > get_depth(t_base)) {
+    const Type type = array_base(t_base) ?: t_base;
+    if(isa(type, t_tuple) > 0 && depth - get_depth(t_base) == 1) {
+      Exp e = array->array->exp;
+      while(e->next)
+        e = e->next;
+      if(e->exp_type != ae_exp_primary ||
+          e->d.exp_primary.primary_type != ae_primary_num)
+         ERR_B(exp_self(array)->pos, _("tuple subscripts must be litteral"))
+      if((Type)vector_at(&type->e->tuple_form, e->d.exp_primary.d.num) == t_undefined)
+         ERR_B(exp_self(array)->pos, _("tuple subscripts is undefined"))
+      return 0;
+    }
     ERR_B(exp_self(array)->pos,
       _("array subscripts (%i) exceeds defined dimension (%i)"),
-      array->array->depth, depth)
+          array->array->depth, get_depth(t_base))
+  }
   return GW_OK;
 }
 
 static ANN Type check_exp_array(const Env env, const Exp_Array* array) {
   CHECK_OO(check_exp(env, array->array->exp))
-  CHECK_BO(array_access_valid(env, array))
+  const m_bool ret = array_access_valid(env, array);
+  CHECK_BO(ret);
+  if(!ret) {
+    Exp e = array->array->exp;
+    while(e->next)
+      e = e->next;
+    // if we implement tuple with no type, err_msg
+    const Type type = array_base(array->base->type) ?: array->base->type;
+    if(e->d.exp_primary.d.num >= vector_size(&type->e->tuple_form))
+      ERR_O(exp_self(array)->pos, "Invalid tuple subscript")
+    return (Type)vector_at(&type->e->tuple_form, e->d.exp_primary.d.num);
+  }
   return at_depth(env, array->base->type, array->array->depth);
 }
 
@@ -486,7 +525,7 @@ ANN static m_bool check_func_args(const Env env, Arg_List arg_list) {
 }
 
 ANN static Func _find_template_match(const Env env, const Value v, const Exp_Call* exp) {
-  CHECK_BO(check_call(env, exp))
+CHECK_BO(check_call(env, exp))
   const Type_List types = exp->tmpl->call;
   Func m_func = NULL, former = env->func;
   DECL_OO(const m_str, tmpl_name, = tl2str(env, types))
@@ -498,7 +537,7 @@ ANN static Func _find_template_match(const Env env, const Value v, const Exp_Cal
   Func_Base *fbase = cpy_func_base(env->gwion->mp, base->base);
   fbase->xid = sym;
   fbase->tmpl->base = 0;
-  fbase->tmpl->call = cpy_type_list(env->gwion->mp, types);
+  fbase->tmpl->call = types;
   if(template_push_types(env, fbase->tmpl) > 0) {
     const Fptr_Def fptr = new_fptr_def(env->gwion->mp, fbase, base->flag);
     if(value) {
@@ -534,7 +573,7 @@ ANN static Func _find_template_match(const Env env, const Value v, const Exp_Cal
           continue;
         const Func_Def fdef = (Func_Def)cpy_func_def(env->gwion->mp, value->d.func_ref->def);
         SET_FLAG(fdef, template);
-        fdef->base->tmpl->call = cpy_type_list(env->gwion->mp, types);
+        fdef->base->tmpl->call = types;
         fdef->base->tmpl->base = i;
         if((m_func = ensure_tmpl(env, fdef, exp)))
           break;
@@ -604,7 +643,6 @@ ANN static Func get_template_func(const Env env, const Exp_Call* func, const Val
   if(f) {
     Tmpl* tmpl = new_tmpl_call(env->gwion->mp, func->tmpl->call);
     tmpl->list = v->d.func_ref ? v->d.func_ref->def->base->tmpl->list : func->func->type->e->d.func->def->base->tmpl->list;
-//    tmpl->list = cpy_id_list(env->gwion->mp, tmpl->list);
     ((Exp_Call*)func)->tmpl = tmpl;
     return ((Exp_Call*)func)->m_func = f;
   }
@@ -701,6 +739,10 @@ ANN Type check_exp_call1(const Env env, const Exp_Call *exp) {
   CHECK_BO(check_exp_call1_check(env, exp->func))
   if(exp->func->type == t_lambda)
     return check_lambda_call(env, exp);
+  if(GET_FLAG(exp->func->type->e->d.func, ref)) {
+    const Value value = exp->func->type->e->d.func->value_ref;
+    CHECK_BO(traverse_class_def(env, value->owner_class->e->def))
+  }
   if(exp->args)
     CHECK_OO(check_exp(env, exp->args))
   if(GET_FLAG(exp->func->type, func))
@@ -871,7 +913,7 @@ ANN static inline Type check_exp(const Env env, const Exp exp) {
 ANN m_bool check_enum_def(const Env env, const Enum_Def edef) {
   if(env->class_def) {
     ID_List list = edef->list;
-    do decl_static(env->curr, nspc_lookup_value0(env->curr, list->xid));
+    do decl_static(env, nspc_lookup_value0(env->curr, list->xid));
     while((list = list->next));
   }
   return GW_OK;
@@ -917,7 +959,7 @@ ANN static m_bool do_stmt_auto(const Env env, const Stmt_Auto stmt) {
   const m_uint depth = t->array_depth - 1;
   if(GET_FLAG(t, typedef))
     t = t->e->parent;
-  if(!t || !ptr || isa(t, t_array) < 0)
+  if(!ptr || isa(t, t_array) < 0)
     ERR_B(stmt_self(stmt)->pos, _("type '%s' is not array.\n"
           " This is not allowed in auto loop"), stmt->exp->type->name)
   if(stmt->is_ptr) {
@@ -941,10 +983,11 @@ ANN static m_bool do_stmt_auto(const Env env, const Stmt_Auto stmt) {
       array.depth = depth;
       td.array = &array;
     }
-    ptr = type_decl_resolve(env, &td);
+//    ptr = type_decl_resolve(env, &td); exit(3);
+    ptr = known_type(env, &td);
     if(!GET_FLAG(ptr, checked))
       check_class_def(env, ptr->e->def);
-//      CHECK_BB(traverse_class_def(env, ptr->e->def))
+//      traverse_class_def(env, ptr->e->def);
   }
   t = depth ? array_type(env, ptr, depth) : ptr;
   stmt->v = new_value(env->gwion->mp, t, s_name(stmt->sym));
@@ -1054,7 +1097,7 @@ ANN m_bool check_union_def(const Env env, const Union_Def udef) {
     return GW_OK;
   if(udef->xid) {
     if(env->class_def)
-      (!GET_FLAG(udef, static) ? decl_member : decl_static)(env->curr, udef->value);
+      (!GET_FLAG(udef, static) ? decl_member : decl_static)(env, udef->value);
   } else if(env->class_def)  {
     if(!GET_FLAG(udef, static))
       udef->o = env->class_def->nspc->info->offset;
index a92b6dc33a503bcd2cc8e502358f8ae54cd538b7..c91ee089c2fda38de9677bb482c25e247232feda 100644 (file)
@@ -21,7 +21,7 @@ ANN static void cpy_exp_lambda(MemPool p, Exp_Lambda *a, const Exp_Lambda *src)
   a->name = src->name;
 }
 
-ANN static Array_Sub cpy_array_sub(MemPool p, const Array_Sub src) {
+ANN Array_Sub cpy_array_sub(MemPool p, const Array_Sub src) {
   Array_Sub a = mp_calloc(p, Array_Sub);
   if(src->exp)
     a->exp = cpy_exp(p, src->exp);
@@ -51,7 +51,7 @@ ANN static Var_Decl_List cpy_var_decl_list(MemPool p, const Var_Decl_List src) {
   return a;
 }
 
-ANN static Type_Decl* cpy_type_decl(MemPool p, const Type_Decl* src) {
+ANN Type_Decl* cpy_type_decl(MemPool p, const Type_Decl* src) {
   Type_Decl* a = mp_calloc(p, Type_Decl);
   if(src->xid)
     a->xid = cpy_id_list(p, src->xid); // 1 
index a4827218dfc537c11125f8028aef647ba0f37d50..d4954890fe8ee543ed99ddd1174b8e8966c9374e 100644 (file)
@@ -16,6 +16,8 @@ ANN static m_bool scan1_stmt(const Env env, Stmt stmt);
 
 ANN static Type void_type(const Env env, const Type_Decl* td) {
   DECL_OO(const Type, t, = known_type(env, td))
+  if(t->e->def && !GET_FLAG(t, scan1))
+    CHECK_BO(scan1_cdef(env, t->e->def))
   if(t->size)
     return t;
   ERR_O(td_pos(td), _("cannot declare variables of size '0' (i.e. 'void')..."))
@@ -64,8 +66,8 @@ ANN static Type scan1_exp_decl_type(const Env env, Exp_Decl* decl) {
     if(!GET_FLAG(decl->td, static))
       SET_FLAG(decl->td, member);
   }
-  if(GET_FLAG(t, abstract) && !GET_FLAG(decl->td, ref))
-    ERR_O(exp_self(decl)->pos, _("Type '%s' is abstract, declare as ref. (use @)"), t->name)
+//  if(GET_FLAG(t, abstract) && !GET_FLAG(decl->td, ref))
+//    ERR_O(exp_self(decl)->pos, _("Type '%s' is abstract, declare as ref. (use @)"), t->name)
   if(GET_FLAG(t, private) && t->e->owner != env->curr)
     ERR_O(exp_self(decl)->pos, _("can't use private type %s"), t->name)
   if(GET_FLAG(t, protect) && (!env->class_def || isa(t, env->class_def) < 0))
@@ -87,22 +89,26 @@ ANN m_bool scan1_exp_decl(const Env env, const Exp_Decl* decl) {
     CHECK_BB(isres(env, var->xid, exp_self(decl)->pos))
     Type t = decl->type;
     const Value former = nspc_lookup_value0(env->curr, var->xid);
-    if(former)
+    if(former && t != t_auto)
       ERR_B(var->pos, _("variable %s has already been defined in the same scope..."),
               s_name(var->xid))
     if(var->array) {
       if(var->array->exp) {
-        if(GET_FLAG(decl->td, ref))
+//        if(GET_FLAG(decl->td, ref))
+        if(GET_FLAG(decl->td, ref) && isa(t, t_object) < 0)
           ERR_B(td_pos(decl->td), _("ref array must not have array expression.\n"
-            "e.g: int @my_array[];\nnot: @int my_array[2];"))
+            "e.g: int @my_array[];\nnot: int @my_array[2];"))
         CHECK_BB(scan1_exp(env, var->array->exp))
       }
       t = array_type(env, decl->type, var->array->depth);
-    }
-    assert(!var->value);
-    const Value v = var->value = new_value(env->gwion->mp, t, s_name(var->xid));
+    } else  if(GET_FLAG(t, abstract) && !GET_FLAG(decl->td, ref))
+      ERR_B(exp_self(decl)->pos, _("Type '%s' is abstract, declare as ref. (use @)"), t->name)
+
+    //assert(!var->value);
+    const Value v = var->value = former ?: new_value(env->gwion->mp, t, s_name(var->xid));
     nspc_add_value(nspc, var->xid, v);
     v->flag = decl->td->flag;
+    v->type = t;
     if(var->array && !var->array->exp)
       SET_FLAG(v, ref);
     if(!env->scope->depth && !env->class_def)
@@ -127,6 +133,10 @@ ANN static inline m_bool scan1_exp_primary(const Env env, const Exp_Primary* pri
     return scan1_exp(env, prim->d.exp);
   if(prim->primary_type == ae_primary_array && prim->d.array->exp)
     return scan1_exp(env, prim->d.array->exp);
+  if(prim->primary_type == ae_primary_tuple)
+    return scan1_exp(env, prim->d.tuple.exp);
+//  if(prim->primary_type == ae_primary_unpack)
+//    return scan1_exp(env, prim->d.tuple.exp);
   return GW_OK;
 }
 
@@ -246,7 +256,9 @@ ANN m_bool scan1_fptr_def(const Env env, const Fptr_Def fptr) {
 }
 
 ANN m_bool scan1_type_def(const Env env, const Type_Def tdef) {
-  return tdef->type->e->def ? scan1_cdef(env, tdef->type->e->def) : GW_OK;
+  if(!tdef->type->e->def)return GW_OK;
+//  return tdef->type->e->def ? scan1_cdef(env, tdef->type->e->def) : GW_OK;
+  return isa(tdef->type, t_fptr) < 0 ? scan1_cdef(env, tdef->type->e->def) : GW_OK;
 }
 
 ANN m_bool scan1_union_def(const Env env, const Union_Def udef) {
@@ -358,7 +370,8 @@ ANN static m_bool scan1_parent(const Env env, const Class_Def cdef) {
   if(isa(parent, t_object) < 0)
     ERR_B(pos, _("cannot extend primitive type '%s'"), parent->name)
   if(parent->e->def && !GET_FLAG(parent, scan1))
-    CHECK_BB(scanx_parent(parent, scan1_cdef, env))
+//    CHECK_BB(scanx_parent(parent, scan1_cdef, env))
+    CHECK_BB(scan1_cdef(env, parent->e->def))
   if(type_ref(parent))
     ERR_B(pos, _("can't use ref type in class extend"))
   return GW_OK;
index d717fca9701cdbe3b7ed86b68a4c33fae8b18d64..ca970270b29e6aa57a00f183ddd503290fc4ffff 100644 (file)
@@ -21,7 +21,7 @@ ANN m_bool scan2_exp_decl(const Env env, const Exp_Decl* decl) {
   const m_bool global = GET_FLAG(decl->td, global);
   const m_uint scope = !global ? env->scope->depth : env_push_global(env);
   const Type type = decl->type;
-  if(GET_FLAG(type, template) && !GET_FLAG(type, scan2))
+  if(type->e->def && /*GET_FLAG(type, template) &&*/ !GET_FLAG(type, scan2))
     CHECK_BB(scan2_cdef(env, decl->type->e->def))
   Var_Decl_List list = decl->list;
   do {
@@ -86,11 +86,14 @@ ANN m_bool scan2_fptr_def(const Env env, const Fptr_Def fptr) {
       CHECK_BB(scan2_args(env, def))
   } else
     SET_FLAG(fptr->type, func);
+//  nspc_add_func(fptr->type->e->owner, fptr->base->xid, fptr->base->func);
   return GW_OK;
 }
 
 ANN m_bool scan2_type_def(const Env env, const Type_Def tdef) {
-  return tdef->type->e->def ? scan2_class_def(env, tdef->type->e->def) : GW_OK;
+//  return tdef->type->e->def ? scan2_class_def(env, tdef->type->e->def) : GW_OK;
+  if(!tdef->type->e->def) return GW_OK;
+  return isa(tdef->type, t_fptr) < 0 ? scan2_class_def(env, tdef->type->e->def) : GW_OK;
 }
 
 ANN static inline Value prim_value(const Env env, const Symbol s) {
@@ -119,6 +122,10 @@ ANN static inline m_bool scan2_exp_primary(const Env env, const Exp_Primary* pri
       SET_FLAG(v, used);
   } else if(prim->primary_type == ae_primary_array && prim->d.array->exp)
     return scan2_exp(env, prim->d.array->exp);
+  if(prim->primary_type == ae_primary_tuple)
+    return scan2_exp(env, prim->d.tuple.exp);
+//  if(prim->primary_type == ae_primary_unpack)
+//    return scan2_exp(env, prim->d.tuple.exp);
   return GW_OK;
 }
 
@@ -308,7 +315,7 @@ ANN static Type func_type(const Env env, const Func func) {
 ANN2(1,2) static Value func_value(const Env env, const Func f,
     const Value overload) {
   const Type  t = func_type(env, f);
-  const Value v = new_value(env->gwion->mp, t, s_name(insert_symbol(t->name)));
+  const Value v = new_value(env->gwion->mp, t, t->name);
   CHECK_OO(scan2_func_assign(env, f->def, f, v))
   if(!overload) {
     ADD_REF(v);
index 3dc5882bdbad1e331948ff24fb011779e384218d..1bcf1c5104ee063d1634006edb130b659de00fab 100644 (file)
@@ -17,7 +17,7 @@ ANN static inline m_bool _body(const Env e, Class_Body b, const _exp_func f) {
 }
 
 ANN static inline int actual(const Tmpl *tmpl) {
-  return tmpl->call && tmpl->call != (Type_List)1;
+  return tmpl->call && tmpl->call != (Type_List)1 && tmpl->list;
 }
 
 ANN static inline m_bool tmpl_push(const Env env, const Tmpl* tmpl) {
@@ -31,7 +31,7 @@ ANN static inline m_int _push(const Env env, const Class_Def c) {
 }
 
 ANN static inline void _pop(const Env e, const Class_Def c, const m_uint s) {
-  if(c->base.tmpl && actual(c->base.tmpl))
+  if(c->base.tmpl && actual(c->base.tmpl) && c->base.tmpl->list)
     nspc_pop_type(e->gwion->mp, e->curr);
   env_pop(e, s);
 }
@@ -72,7 +72,7 @@ scanx_parent(const Type t, const _exp_func f, void* d) {
 
 ANN m_bool scanx_cdef(const Env env, void* opt, const Class_Def cdef,
     const _exp_func f_cdef, const _exp_func f_union) {
-  if(cdef->base.type && cdef->base.type->e->parent !=  t_union)
+  if(cdef->base.type->e->parent !=  t_union)
      return f_cdef(opt, cdef);
   CHECK_BB(template_push_types(env, cdef->base.tmpl))
   const m_bool ret = f_union(opt, cdef->union_def);
index d279925fa0d535355ac6272391c0ccec477c5142..03c955fddbcea7f4abf110f1a0a83874d95acb1f 100644 (file)
@@ -155,6 +155,22 @@ extern ANN m_bool scan1_class_def(const Env, const Class_Def);
 extern ANN m_bool traverse_func_def(const Env, const Func_Def);
 extern ANN m_bool traverse_class_def(const Env, const Class_Def);
 
+ANN Type scan_tuple(const Env env, const Type_Decl *td) {
+  struct Vector_ v;
+  vector_init(&v);
+  Type_List tl = td->types;
+  do {
+    const Type t = tl->td->xid->xid != insert_symbol("_") ?
+       known_type(env, tl->td) : 1;
+    if(!t)
+      break;
+    vector_add(&v, (m_uint)t);
+  } while((tl = tl->next));
+  const Type ret = tuple_type(env, &v, td_pos(td));
+  vector_release(&v);
+  return ret;
+}
+
 ANN Tmpl* mk_tmpl(const Env env, const Type t, const Tmpl *tm, const Type_List types) {
   Tmpl *tmpl = new_tmpl(env->gwion->mp, get_total_type_list(env, t, tm), 0);
   tmpl->call = cpy_type_list(env->gwion->mp, types);
@@ -165,34 +181,40 @@ ANN Type scan_type(const Env env, const Type t, const Type_Decl* type) {
   if(GET_FLAG(t, template)) {
     if(GET_FLAG(t, ref))
       return t;
-    if(!type->types)
-      ERR_O(t->e->def->pos,
-        _("you must provide template types for type '%s'"), t->name)
-    if(template_match(t->e->def->base.tmpl->list, type->types) < 0)
-      ERR_O(type->xid->pos, _("invalid template types number"))
-    DECL_OO(const Class_Def, a, = template_class(env, t->e->def, type->types))
-    SET_FLAG(a, ref);
-    if(a->base.type)
-      return a->base.type;
-    a->base.tmpl = mk_tmpl(env, t, t->e->def->base.tmpl, type->types);
-    if(t->e->parent !=  t_union) {
-      CHECK_BO(scan0_class_def(env, a))
-      map_set(&env->curr->info->type->map, (vtype)a->base.xid, (vtype)a->base.type);
-    } else {
-      a->union_def = new_union_def(env->gwion->mp, a->list, t->e->def->pos);
-      a->union_def->type_xid = a->base.xid;
-      CHECK_BO(scan0_union_def(env, a->union_def))
-      a->base.type = a->union_def->type;
-      a->base.type->e->def = a;
-      assert(GET_FLAG(a, union));
-    }
-    SET_FLAG(a->base.type, template | ae_flag_ref);
-    a->base.type->e->owner = t->e->owner;
-    if(GET_FLAG(t, builtin))
+    if(!type->types) {
+      if(t != t_tuple)
+        ERR_O(t->e->def->pos,
+          _("you must provide template types for type '%s'"), t->name)
+      return t;
+   }
+   if(t->e->def) {// not tuple
+     if(template_match(t->e->def->base.tmpl->list, type->types) < 0)
+       ERR_O(type->xid->pos, _("invalid template types number"))
+      DECL_OO(const Class_Def, a, = template_class(env, t->e->def, type->types))
+      SET_FLAG(a, ref);
+      if(a->base.type)
+        return a->base.type;
+      a->base.tmpl = mk_tmpl(env, t, t->e->def->base.tmpl, type->types);
+      if(t->e->parent !=  t_union) {
+        CHECK_BO(scan0_class_def(env, a))
+        map_set(&env->curr->info->type->map, (vtype)a->base.xid, (vtype)a->base.type);
+      } else {
+        a->union_def = new_union_def(env->gwion->mp, a->list, t->e->def->pos);
+        a->union_def->type_xid = a->base.xid;
+        CHECK_BO(scan0_union_def(env, a->union_def))
+        a->base.type = a->union_def->type;
+        a->base.type->e->def = a;
+        assert(GET_FLAG(a, union));
+      }
+      SET_FLAG(a->base.type, template | ae_flag_ref);
+      a->base.type->e->owner = t->e->owner;
+      if(GET_FLAG(t, builtin))
       SET_FLAG(a->base.type, builtin);
-    CHECK_BO(scan1_cdef(env, a))
-    return a->base.type;
-  } else if(type->types) { // TODO: clean me
+      CHECK_BO(scan1_cdef(env, a))
+      return a->base.type;
+    } else
+      return scan_tuple(env, type);
+   } else if(type->types) { // TODO: clean me
     if(isa(t, t_function) > 0 && t->e->d.func->def->base->tmpl) {
       DECL_OO(const m_str, tl_name, = tl2str(env, type->types))
       const Symbol sym = func_symbol(env, t->e->owner->name, t->e->d.func->name, tl_name, 0);
diff --git a/src/parse/tuple.c b/src/parse/tuple.c
new file mode 100644 (file)
index 0000000..38b88ea
--- /dev/null
@@ -0,0 +1,86 @@
+#include <string.h>
+#include "gwion_util.h"
+#include "gwion_ast.h"
+#include "oo.h"
+#include "vm.h"
+#include "env.h"
+#include "type.h"
+#include "func.h"
+#include "value.h"
+#include "nspc.h"
+#include "traverse.h"
+#include "template.h"
+#include "vm.h"
+#include "parse.h"
+#include "gwion.h"
+#include "cpy_ast.h"
+
+
+ANN static Symbol tuple_sym(const Env env, const Vector v) {
+  GwText text = { .mp=env->gwion->mp };
+  text_add(&text, t_tuple->name);
+  text_add(&text, "<~");
+  for(m_uint i = 0; i < vector_size(v); ++i) {
+    const Type t = (Type)vector_at(v, i);
+    text_add(&text, t != 1 ? t->name : "_");
+    if(i+1 < vector_size(v))
+      text_add(&text, ",");
+  }
+  text_add(&text, "~>");
+  const Symbol sym = insert_symbol(text.str);
+  text_release(&text);
+  return sym;
+}
+
+ANN Type tuple_type(const Env env, const Vector v, const loc_t pos) {
+  const Symbol sym = tuple_sym(env, v);
+  const Type exists = nspc_lookup_type1(env->curr, sym);
+  if(exists)
+    return exists;
+  Stmt_List base = NULL, curr = NULL;
+  Type_List tlbase = NULL, tl = NULL;
+  for(m_uint i = 0; i < vector_size(v); ++i) {
+    char name[num_digit(i) + 2];
+    sprintf(name, "e%lu", i);
+    const Symbol sym = insert_symbol(name);
+    const Type t = (Type)vector_at(v, i);
+    const Symbol tsym = insert_symbol(t != 1 ? t->name : "@Undefined");
+//    const Symbol tsym = t != 1 ? insert_symbol(t->name) : insert_symbol("@undefined");
+    Exp decl = decl_from_id(env->gwion->mp, tsym, sym, loc_cpy(env->gwion->mp, pos));
+    const Stmt stmt = new_stmt_exp(env->gwion->mp, ae_stmt_exp, decl);
+    const Stmt_List slist = new_stmt_list(env->gwion->mp, stmt, NULL);
+    if(curr)
+      curr->next = slist;
+    if(!base)
+      base = slist;
+{// build Type_List
+  const ID_List ilist = new_id_list(env->gwion->mp, tsym,
+      loc_cpy(env->gwion->mp, pos));
+  Type_Decl *td = new_type_decl(env->gwion->mp, ilist);
+  Type_List tmp_tl = new_type_list(env->gwion->mp, td, NULL);
+    if(tl)
+      tl->next = tmp_tl;
+    if(!tlbase)
+      tlbase = tl = tmp_tl;
+
+}
+    curr = slist;
+  }
+  Section * section = new_section_stmt_list(env->gwion->mp, base);
+  Class_Body body = new_class_body(env->gwion->mp, section, NULL);
+  const ID_List ilist = new_id_list(env->gwion->mp, insert_symbol(t_tuple->name),
+      loc_cpy(env->gwion->mp, pos));
+  Type_Decl *td = new_type_decl(env->gwion->mp, ilist);
+  Class_Def cdef = new_class_def(env->gwion->mp, ae_flag_template,
+        sym, td, body, loc_cpy(env->gwion->mp, pos));
+  Tmpl* tmpl = new_tmpl_call(env->gwion->mp, tlbase);
+  cdef->base.tmpl = tmpl;
+  CHECK_BO(scan0_class_def(env, cdef))
+  SET_FLAG(cdef->base.type, abstract);
+  cdef->base.type->e->tuple_tl = tlbase;
+//  CHECK_BO(scan1_cdef(env, cdef))
+  CHECK_BO(traverse_cdef(env, cdef))
+  nspc_add_type(env->curr, sym, cdef->base.type);
+//  map_set((Map)vector_front(&env->curr->info->type), sym, cdef->base.type);
+  return cdef->base.type;
+}
index db8e46c405c91ec8d697c36581a8cbe04faab9cd..129eba1d2dd6a1424af43dd45525786c791026e5 100644 (file)
@@ -762,6 +762,7 @@ allocmemberaddr:
   reg += SZ_INT;
   DISPATCH()
 dotmember:
+//printf("VAL %lu %p\n", VAL, );
   *(m_uint*)(reg-SZ_INT) = *(m_uint*)(a.obj->data + VAL);
   DISPATCH()
 dotfloat:
diff --git a/tests/nonnull/ref.gw b/tests/nonnull/ref.gw
new file mode 100644 (file)
index 0000000..34c9985
--- /dev/null
@@ -0,0 +1 @@
+new Object @=> Object ! @ o;
diff --git a/tests/tuple/object2tuple.gw b/tests/tuple/object2tuple.gw
new file mode 100644 (file)
index 0000000..4bb432e
--- /dev/null
@@ -0,0 +1,8 @@
+class Person {
+  "Phil" @=> string @name;
+  45 => int age;
+}
+
+Person p @=> <~string,int~>Tuple @t;
+<<<t.e0>>>;
+<<<t.e1>>>;
diff --git a/tests/tuple/object2tuple_err.gw b/tests/tuple/object2tuple_err.gw
new file mode 100644 (file)
index 0000000..525c774
--- /dev/null
@@ -0,0 +1,8 @@
+class Person {
+  "Phil" @=> string @name;
+  45 => int age;
+}
+
+Person p @=> <~string,string~>Tuple @t;
+<<<t.e0>>>;
+<<<t.e1>>>;
diff --git a/tests/tuple/object_array_access_multi.gw b/tests/tuple/object_array_access_multi.gw
new file mode 100644 (file)
index 0000000..e9f2f9f
--- /dev/null
@@ -0,0 +1,9 @@
+Object t[2];
+#!<<< t[0] >>>;
+#!null @=> t[0];
+<<< t >>>;
+<("tom", 12) @=> t[0];
+#!t << <("tom", 12);
+#!<<< t.size() >>>;
+#!<<< t[0][0] >>>;
+#!<<< t[0][1] >>>;
diff --git a/tests/tuple/tupl_decl.gw b/tests/tuple/tupl_decl.gw
new file mode 100644 (file)
index 0000000..9ee94d5
--- /dev/null
@@ -0,0 +1 @@
+Tuple @u;
diff --git a/tests/tuple/tuple.gw b/tests/tuple/tuple.gw
new file mode 100644 (file)
index 0000000..9ee94d5
--- /dev/null
@@ -0,0 +1 @@
+Tuple @u;
diff --git a/tests/tuple/tuple_abstract.gw b/tests/tuple/tuple_abstract.gw
new file mode 100644 (file)
index 0000000..3335d05
--- /dev/null
@@ -0,0 +1,2 @@
+#! [contains] Type 'Tuple' is abstract, declare as ref
+Tuple u;
diff --git a/tests/tuple/tuple_aray_access.gw b/tests/tuple/tuple_aray_access.gw
new file mode 100644 (file)
index 0000000..1c043df
--- /dev/null
@@ -0,0 +1,6 @@
+<<< <("Tom", 12)[0] >>> ;
+#! <<< <("Tom", 12)[1] >>> ;
+
+#! <<< <("Tom", 12) @=> <~string, int~>Tuple @t >>> ;
+#! <<< t[0] >>> ;
+#! <<< t[1] >>> ;
diff --git a/tests/tuple/tuple_aray_access_exceed.gw b/tests/tuple/tuple_aray_access_exceed.gw
new file mode 100644 (file)
index 0000000..69fd159
--- /dev/null
@@ -0,0 +1 @@
+<("Tom", 12)[0][2];
diff --git a/tests/tuple/tuple_aray_access_multi.gw b/tests/tuple/tuple_aray_access_multi.gw
new file mode 100644 (file)
index 0000000..0a30f81
--- /dev/null
@@ -0,0 +1,11 @@
+<~string,int~>Tuple t[2];
+#!<<< t[0] >>>;
+#!null @=> t[0];
+<<< t >>>;
+<<< t[0] >>>;
+<<< <("tom", 12) @=> t[0] >>> ;
+
+#!t << <("tom", 12);
+#!<<< t.size() >>>;
+#!<<< t[0][0] >>>;
+#!<<< t[0][1] >>>;
diff --git a/tests/tuple/tuple_aray_access_not_prim.gw b/tests/tuple/tuple_aray_access_not_prim.gw
new file mode 100644 (file)
index 0000000..3afc0e1
--- /dev/null
@@ -0,0 +1,2 @@
+int i;
+<("Tom", 12)[i];
diff --git a/tests/tuple/tuple_array_access_invalid.gw b/tests/tuple/tuple_array_access_invalid.gw
new file mode 100644 (file)
index 0000000..6b23d6b
--- /dev/null
@@ -0,0 +1 @@
+<(1.2,"test")[3];
diff --git a/tests/tuple/tuple_at.gw b/tests/tuple/tuple_at.gw
new file mode 100644 (file)
index 0000000..5954956
--- /dev/null
@@ -0,0 +1,4 @@
+<("Tom", 6) @=> 
+<~string, int~>Tuple @tup;
+<<< typeof(tup) >>>;
+<<< tup >>>;
diff --git a/tests/tuple/tuple_at_err.gw b/tests/tuple/tuple_at_err.gw
new file mode 100644 (file)
index 0000000..3ec6aa6
--- /dev/null
@@ -0,0 +1,4 @@
+<("Tom", 6) @=> 
+<~float, int~>Tuple @tup;
+<<< typeof(tup) >>>;
+<<< tup >>>;
diff --git a/tests/tuple/tuple_at_lit.gw b/tests/tuple/tuple_at_lit.gw
new file mode 100644 (file)
index 0000000..981b5e0
--- /dev/null
@@ -0,0 +1,2 @@
+#! [contains] cannot assign
+<("Tom", 6) @=> <("Pat", 22);
diff --git a/tests/tuple/tuple_cast.gw b/tests/tuple/tuple_cast.gw
new file mode 100644 (file)
index 0000000..2c03612
--- /dev/null
@@ -0,0 +1,4 @@
+<~int~>Tuple @a;
+
+<<< a >>>;
+<<< a $ Tuple >>>;
diff --git a/tests/tuple/tuple_cast2.gw b/tests/tuple/tuple_cast2.gw
new file mode 100644 (file)
index 0000000..df19732
--- /dev/null
@@ -0,0 +1,4 @@
+<~int,int~>Tuple @a;
+
+<<< a >>>;
+<<< a $ <~int~>Tuple >>>;
diff --git a/tests/tuple/tuple_decl.gw b/tests/tuple/tuple_decl.gw
new file mode 100644 (file)
index 0000000..c14ce68
--- /dev/null
@@ -0,0 +1 @@
+<~int~>Tuple @a;
diff --git a/tests/tuple/tuple_implicit.gw b/tests/tuple/tuple_implicit.gw
new file mode 100644 (file)
index 0000000..c589d30
--- /dev/null
@@ -0,0 +1,8 @@
+#! [contains] Tom
+fun void test(<~string~>Tuple t) {
+#!  <<< t.e0 >>>;
+  <<< t[0] >>>;
+}
+
+<( "Tom", 21) => test;
+#!<("Tom") => test;
diff --git a/tests/tuple/tuple_lit_error.gw b/tests/tuple/tuple_lit_error.gw
new file mode 100644 (file)
index 0000000..2e89d2d
--- /dev/null
@@ -0,0 +1 @@
+<( new int);
diff --git a/tests/tuple/tuple_skip.gw b/tests/tuple/tuple_skip.gw
new file mode 100644 (file)
index 0000000..73c6499
--- /dev/null
@@ -0,0 +1,2 @@
+<(1, 2, 3) @=> >(b, _, c);
+<<< c >>>;
diff --git a/tests/tuple/tuple_skip2.gw b/tests/tuple/tuple_skip2.gw
new file mode 100644 (file)
index 0000000..bc0abe7
--- /dev/null
@@ -0,0 +1,6 @@
+<("Tom", 2, "Taxi driver") @=> <~string,_, string~>Tuple @t;
+<<< t>>>;
+<<< t[0]>>>;
+<("Tom", 2.3, "Cook") @=> t;
+#!<<< t[1]>>>;
+<<< t[2]>>>;
diff --git a/tests/tuple/tuple_unknown.gw b/tests/tuple/tuple_unknown.gw
new file mode 100644 (file)
index 0000000..2e9ca60
--- /dev/null
@@ -0,0 +1,2 @@
+<~int, strong~>Tuple @a;
+<~int, string~>Tuple @a;
diff --git a/tests/tuple/tuple_unpack_already_declared.gw b/tests/tuple/tuple_unpack_already_declared.gw
new file mode 100644 (file)
index 0000000..e44abee
--- /dev/null
@@ -0,0 +1,3 @@
+#! [contains] already been defined in the same scope
+int i;
+<(12) @=> >(i);