From cc29e3de02cfa64be92770f558f804205de96238 Mon Sep 17 00:00:00 2001
From: =?utf8?q?J=C3=A9r=C3=A9mie=20Astor?= <fennecdjay@gmail.com>
Date: Sat, 17 Jul 2021 20:44:59 +0200
Subject: [PATCH] :art: Constructors

---
 ast                         |  2 +-
 fmt                         |  2 +-
 src/clean.c                 |  4 +++-
 src/emit/emit.c             |  2 ++
 src/lib/opfunc.c            | 25 +++++++++++++++++++------
 src/parse/check.c           |  3 ++-
 src/parse/scan2.c           |  5 +++++
 tests/ctor/ctor_outside.gw  |  5 +++++
 tests/ctor/ctor_overload.gw | 14 ++++++++++++++
 9 files changed, 52 insertions(+), 10 deletions(-)
 create mode 100644 tests/ctor/ctor_outside.gw
 create mode 100644 tests/ctor/ctor_overload.gw

diff --git a/ast b/ast
index 7492fddf..68044023 160000
--- a/ast
+++ b/ast
@@ -1 +1 @@
-Subproject commit 7492fddf76bd861aea16e16f6cc66ac257c3c647
+Subproject commit 68044023ce834c80ff2992487835b91e139a467c
diff --git a/fmt b/fmt
index d78297aa..30bff4e9 160000
--- a/fmt
+++ b/fmt
@@ -1 +1 @@
-Subproject commit d78297aacc14071b97ae22e47f9e1c06054c15de
+Subproject commit 30bff4e9845b15c3af3f8caee300134a4d6fbd27
diff --git a/src/clean.c b/src/clean.c
index fb4dee1a..3adaf1d9 100644
--- a/src/clean.c
+++ b/src/clean.c
@@ -75,7 +75,9 @@ ANN static void clean_exp_unary(Clean *a, Exp_Unary *b) {
     clean_exp(a, b->exp);
     break;
   case unary_td:
-    clean_type_decl(a, b->td);
+    clean_type_decl(a, b->ctor.td);
+    if(b->ctor.exp)
+      clean_exp(a, b->ctor.exp);
     break;
   case unary_code:
     clean_stmt(a, b->code);
diff --git a/src/emit/emit.c b/src/emit/emit.c
index d2da9115..5f9091c6 100644
--- a/src/emit/emit.c
+++ b/src/emit/emit.c
@@ -2560,6 +2560,8 @@ ANN static VM_Code emit_internal(const Emitter emit, const Func f) {
 
 ANN static inline VM_Code _emit_func_def_code(const Emitter emit,
                                               const Func    func) {
+  if(!strcmp(s_name(func->def->base->xid), "new"))
+    emit_add_instr(emit, RegPushMem);
   return !fbflag(func->def->base, fbflag_internal) ? finalyze(emit, FuncReturn)
                                                    : emit_internal(emit, func);
 }
diff --git a/src/lib/opfunc.c b/src/lib/opfunc.c
index 195bb2b9..ac3c17f9 100644
--- a/src/lib/opfunc.c
+++ b/src/lib/opfunc.c
@@ -97,23 +97,36 @@ OP_CHECK(opck_post) {
 ANN Type check_td(const Env env, Type_Decl *td);
 
 OP_CHECK(opck_new) {
-  const Exp_Unary *unary = (Exp_Unary *)data;
-  DECL_ON(const Type, t, = known_type(env, unary->td));
+  Exp_Unary *unary = (Exp_Unary *)data;
+  DECL_ON(const Type, t, = known_type(env, unary->ctor.td));
+  if(unary->ctor.exp) {
+    const Exp self   = exp_self(unary);
+    const Exp args   = unary->ctor.exp;
+    const Exp base   = new_exp_unary2(env->gwion->mp, unary->op, unary->ctor.td, NULL, self->pos);
+    const Exp func   = new_exp_dot(env->gwion->mp, base, insert_symbol("new"), self->pos);
+    self->d.exp_call.func = func;
+    self->d.exp_call.args = args;
+    self->d.exp_call.tmpl = NULL;
+    self->exp_type = ae_exp_call;
+    CHECK_BN(traverse_exp(env, self));
+    return self->type;
+//    unarytype
+  }
   if (isa(t, env->gwion->type[et_object]) < 0)
     ERR_N(exp_self(unary)->pos, _("can't use 'new' on non-object types...\n"));
   if (type_ref(t))
-    ERR_N(unary->td->pos, _("can't use 'new' on ref type '%s'\n"), t->name);
+    ERR_N(unary->ctor.td->pos, _("can't use 'new' on ref type '%s'\n"), t->name);
   if (GET_FLAG(t, abstract))
-    ERR_N(unary->td->pos, _("can't use 'new' on abstract type '%s'\n"),
+    ERR_N(unary->ctor.td->pos, _("can't use 'new' on abstract type '%s'\n"),
           t->name);
-  if (unary->td->array) CHECK_BN(check_subscripts(env, unary->td->array, 1));
+  if (unary->ctor.td->array) CHECK_BN(check_subscripts(env, unary->ctor.td->array, 1));
   return t;
 }
 
 OP_EMIT(opem_new) {
   const Exp_Unary *unary = (Exp_Unary *)data;
   CHECK_BB(emit_instantiate_object(emit, exp_self(unary)->type,
-                                   unary->td->array, 0));
+                                   unary->ctor.td->array, 0));
   emit_gc(emit, -SZ_INT);
   return GW_OK;
 }
diff --git a/src/parse/check.c b/src/parse/check.c
index fbe06144..58952baa 100644
--- a/src/parse/check.c
+++ b/src/parse/check.c
@@ -1551,7 +1551,8 @@ ANN m_bool check_fdef(const Env env, const Func_Def fdef) {
     CHECK_BB(check_stmt_code(env, &fdef->d.code->d.stmt_code));
     env->scope->depth++;
   }
-  if (fdef->base->ret_type &&
+  // check fdef->base->td for `new`
+  if (fdef->base->td && fdef->base->ret_type &&
       fdef->base->ret_type != env->gwion->type[et_void] && fdef->d.code &&
       !fflag(fdef->base->func, fflag_return))
     ERR_B(fdef->base->td->pos,
diff --git a/src/parse/scan2.c b/src/parse/scan2.c
index 1350c178..3d78ffa0 100644
--- a/src/parse/scan2.c
+++ b/src/parse/scan2.c
@@ -546,6 +546,11 @@ static inline int is_cpy(const Func_Def fdef) {
 ANN m_bool scan2_func_def(const Env env, const Func_Def fdef) {
   if (tmpl_base(fdef->base->tmpl) && fbflag(fdef->base, fbflag_op))
     return GW_OK;
+  if(!strcmp(s_name(fdef->base->xid), "new")) {
+    if(!env->class_def)
+      ERR_B(fdef->base->pos, _("{G-}new{0} operator must be set inside {C+}class{0}"));
+    fdef->base->ret_type = env->class_def;
+  }
   if (GET_FLAG(fdef->base, global) && !env->class_def) env->context->global = 1;
   const Func_Def f = !is_cpy(fdef) ? fdef : scan2_cpy_fdef(env, fdef);
   const m_uint   scope =
diff --git a/tests/ctor/ctor_outside.gw b/tests/ctor/ctor_outside.gw
new file mode 100644
index 00000000..a7343cd2
--- /dev/null
+++ b/tests/ctor/ctor_outside.gw
@@ -0,0 +1,5 @@
+#! [contains] new operator must be set inside class
+var int i;
+operator new(int arg) {
+  arg => i;
+}
diff --git a/tests/ctor/ctor_overload.gw b/tests/ctor/ctor_overload.gw
new file mode 100644
index 00000000..3e22ad4a
--- /dev/null
+++ b/tests/ctor/ctor_overload.gw
@@ -0,0 +1,14 @@
+#! [contains] 42
+class C {
+  var int i;
+  operator new(int arg) {
+    arg => i;
+  }
+  operator new() {}
+}
+
+new C() => var auto c;
+new C(2) => var auto d;
+<<< "c:${c.i}, d:${d.i}" >>>;
+
+<<< "the answer: ${(new C(42)).i}" >>>;
-- 
2.43.0