From 6b9fa3ac97ed604fc67b2a7480ca86435b195b10 Mon Sep 17 00:00:00 2001
From: =?utf8?q?J=C3=A9r=C3=A9mie=20Astor?= <fennecdjay@gmail.com>
Date: Wed, 27 Apr 2022 11:33:24 +0200
Subject: [PATCH] :art: Improve upvalue handling

---
 src/lib/deep_equal.c |  1 +
 src/lib/lib_func.c   |  3 ++-
 src/parse/check.c    | 27 +++++++++++++++++----------
 3 files changed, 20 insertions(+), 11 deletions(-)

diff --git a/src/lib/deep_equal.c b/src/lib/deep_equal.c
index 34fd7afe..ba177acb 100644
--- a/src/lib/deep_equal.c
+++ b/src/lib/deep_equal.c
@@ -156,6 +156,7 @@ ANN static void deep_emit_init(const Emitter emit, struct DeepEmit *d, const m_i
   const Instr instr = emit_add_instr(emit, Reg2Mem);
   instr->m_val2 = offset;
   d->val->from->offset = instr->m_val = emit_localn(emit, d->val->type);
+  d->val->from->loc = d->exp->pos;
 }
 
 ANN static void deep_emit_release(const Emitter emit, struct DeepEmit *d) {
diff --git a/src/lib/lib_func.c b/src/lib/lib_func.c
index 5ce11242..7ca4c554 100644
--- a/src/lib/lib_func.c
+++ b/src/lib/lib_func.c
@@ -684,10 +684,11 @@ static OP_CHECK(opck_spork) {
       }
     }
     const Func f = env->func;
-    struct Value_ value = {};
+    struct Value_ value = { .type = env->gwion->type[et_lambda]};
     if(env->class_def)
       set_vflag(&value, vflag_member);
     struct Func_Base_ fbase = { .xid=insert_symbol("in spork"), .values = scope};
+    set_fbflag(&fbase, fbflag_lambda);
     struct Func_Def_ fdef = { .base = &fbase};
     struct Func_ func = { .name = "in spork", .def = &fdef, .value_ref = &value};
     env->func = &func;
diff --git a/src/parse/check.c b/src/parse/check.c
index f9a8abc0..c2a8074f 100644
--- a/src/parse/check.c
+++ b/src/parse/check.c
@@ -274,8 +274,12 @@ ANN static inline Value get_value(const Env env, const Symbol sym) {
     if (!value->from->owner_class || isa(env->class_def, value->from->owner_class) > 0)
       return value;
   }
-  if (env->func && env->func->def->base->values)
-    return (Value)scope_lookup1(env->func->def->base->values, (vtype)sym);
+  if (env->func && env->func->def->base->values) {
+    DECL_OO(const Value, v, = (Value)scope_lookup1(env->func->def->base->values, (vtype)sym));
+    if(isa(env->func->value_ref->type, env->gwion->type[et_lambda]) > 0)
+      CHECK_OO(not_upvalue(env, v));
+    return v;
+  }
   return NULL;
 }
 
@@ -326,8 +330,7 @@ static inline Nspc value_owner(const Env env, const Value v) {
   return v ? v->from->owner : env->curr;
 }
 
-ANN static m_bool check_upvalue(const Env env, const Exp_Primary *prim) {
-  const Value v = prim->value;
+ANN static m_bool check_upvalue(const Env env, const Exp_Primary *prim, const Value v) {
   if(not_upvalue(env, v))
     return GW_OK;
   gwerr_basic(_("value not in lambda scope"), NULL, NULL, env->name, exp_self(prim)->pos, 4242);
@@ -358,9 +361,11 @@ ANN static Type prim_id_non_res(const Env env, const Symbol *data) {
       prim_self(data)->value = env->gwion->type[et_op]->info->value;
       return env->gwion->type[et_op];
     }
-    const m_str hint = (!env->func || strcmp(env->func->name, "in spork")) ?
-        NULL : "vapturelist?";
-    gwerr_basic(_("Invalid variable"), _("not legit at this point."), hint,
+    if (env->func && fbflag(env->func->def->base, fbflag_lambda) && env->func->def->base->values) {
+      const Value v = (Value)scope_lookup1(env->func->def->base->values, (vtype)sym);
+      if(v) CHECK_BO(check_upvalue(env, prim_self(data), v));
+    }
+    gwerr_basic(_("Invalid variable"), _("not legit at this point."), NULL,
                 env->name, prim_pos(data), 0);
     did_you_mean_nspc(v ? value_owner(env, v) : env->curr, s_name(sym));
     env_set_error(env);
@@ -373,7 +378,7 @@ ANN static Type prim_id_non_res(const Env env, const Symbol *data) {
     if (!GET_FLAG(v, const) && v->from->owner)
       unset_fflag(env->func, fflag_pure);
     if (fbflag(env->func->def->base, fbflag_lambda))
-      CHECK_BO(check_upvalue(env, prim_self(data)));
+      CHECK_BO(check_upvalue(env, prim_self(data), v));
   }
   // set_vflag(v->vflag, vflag_used);
   return v->type;
@@ -1217,8 +1222,9 @@ ANN static inline m_bool for_empty(const Env env, const Stmt_For stmt) {
 
 ANN static void check_idx(const Env env, const Type base, struct EachIdx_ *const idx) {
   idx->v = new_value(env->gwion->mp, base, s_name(idx->sym));
-  valuefrom(env, idx->v->from, idx->pos);
   valid_value(env, idx->sym, idx->v);
+  idx->v->from->loc = idx->pos;
+  idx->v->from->filename = env->name;
   SET_FLAG(idx->v, const);
 }
 
@@ -1254,7 +1260,8 @@ ANN static m_bool do_stmt_each(const Env env, const Stmt_Each stmt) {
   DECL_OB(const Type, ret, = check_each_val(env, stmt->exp));
   stmt->v = new_value(env->gwion->mp, ret, s_name(stmt->sym));
   valid_value(env, stmt->sym, stmt->v);
-  valuefrom(env, stmt->v->from, stmt->vpos);
+  stmt->v->from->loc = stmt->vpos;
+  stmt->v->from->filename = env->name;
   return check_conts(env, stmt_self(stmt), stmt->body);
 }
 
-- 
2.43.0