GCC Code Coverage Report
Directory: src/ Exec Total Coverage
File: src/lib/func.c Lines: 283 292 96.9 %
Date: 2020-09-14 00:22:58 Branches: 118 150 78.7 %

Line Branch Exec Source
1
#include <string.h>
2
#include "gwion_util.h"
3
#include "gwion_ast.h"
4
#include "gwion_env.h"
5
#include "vm.h"
6
#include "gwion.h"
7
#include "instr.h"
8
#include "emit.h"
9
#include "object.h"
10
#include "operator.h"
11
#include "import.h"
12
#include "traverse.h"
13
#include "template.h"
14
#include "parse.h"
15
16
73
static OP_CHECK(opck_func_call) {
17
73
  Exp_Binary* bin = (Exp_Binary*)data;
18
73
  Exp_Call call = { .func=bin->rhs, .args=bin->lhs };
19
73
  Exp e = exp_self(bin);
20
73
  e->exp_type = ae_exp_call;
21
73
  memcpy(&e->d.exp_call, &call, sizeof(Exp_Call));
22
73
  ++*mut;
23
73
  return check_exp_call1(env, &e->d.exp_call) ?: env->gwion->type[et_null];
24
}
25
26
10
static inline void fptr_instr(const Emitter emit, const Func f, const m_uint i) {
27
10
  const Instr set = emit_add_instr(emit, RegSetImm);
28
10
  set->m_val = (m_uint)f;
29
10
  set->m_val2 = -SZ_INT*i;
30
10
}
31
32
40
static OP_EMIT(opem_func_assign) {
33
40
  Exp_Binary* bin = (Exp_Binary*)data;
34
40
  if(bin->rhs->info->type->e->d.func->def->base->tmpl)
35
8
    fptr_instr(emit, bin->lhs->info->type->e->d.func, 2);
36
40
  const Instr instr = emit_add_instr(emit, int_r_assign);
37

40
  if(!is_fptr(emit->gwion, bin->lhs->info->type) && GET_FLAG(bin->rhs->info->type->e->d.func, member)) {
38
21
    const Instr pop = emit_add_instr(emit, RegPop);
39
21
    pop->m_val = SZ_INT;
40
21
    const Instr cpy = emit_add_instr(emit, Reg2Reg);
41
21
    cpy->m_val = -SZ_INT;
42
  }
43
40
  return instr;
44
}
45
46
struct FptrInfo {
47
        Func  lhs;
48
  const Func  rhs;
49
  const Exp   exp;
50
  const loc_t pos;
51
};
52
53
56
ANN static m_bool fptr_tmpl_push(const Env env, struct FptrInfo *info) {
54
56
  if(!info->rhs->def->base->tmpl)
55
41
    return GW_OK;
56
15
  ID_List t0 = info->lhs->def->base->tmpl->list,
57
15
          t1 = info->rhs->def->base->tmpl->list;
58
15
  nspc_push_type(env->gwion->mp, env->curr);
59
45
  while(t0) {
60
15
    nspc_add_type(env->curr, t0->xid, env->gwion->type[et_undefined]);
61
15
    nspc_add_type(env->curr, t1->xid, env->gwion->type[et_undefined]);
62
15
    t0 = t0->next;
63
15
    t1 = t1->next;
64
  }
65
15
  return GW_OK;
66
}
67
68
72
static m_bool td_match(const Env env, Type_Decl *id[2]) {
69
72
  DECL_OB(const Type, t0, = known_type(env, id[0]))
70
72
  DECL_OB(const Type, t1, = known_type(env, id[1]))
71
72
  return isa(t0, t1);
72
}
73
74
56
ANN static m_bool fptr_args(const Env env, Func_Base *base[2]) {
75
56
  Arg_List arg0 = base[0]->args, arg1 = base[1]->args;
76
128
  while(arg0) {
77
26
    CHECK_OB(arg1)
78
16
    Type_Decl* td[2] = { arg0->td, arg1->td };
79
16
    CHECK_BB(td_match(env, td))
80
16
    arg0 = arg0->next;
81
16
    arg1 = arg1->next;
82
  }
83
51
  return !arg1 ? GW_OK : GW_ERROR;
84
}
85
86
57
ANN static m_bool fptr_check(const Env env, struct FptrInfo *info) {
87
57
  if(!info->lhs->def->base->tmpl != !info->rhs->def->base->tmpl)
88
    return GW_ERROR;
89
57
  const Type l_type = info->lhs->value_ref->from->owner_class;
90
57
  const Type r_type = info->rhs->value_ref->from->owner_class;
91

57
  if(!r_type && l_type)
92
1
    ERR_B(info->pos, _("can't assign member function to non member function pointer"))
93

56
  else if(!l_type && r_type) {
94
1
    if(!GET_FLAG(info->rhs, global))
95
1
      ERR_B(info->pos, _("can't assign non member function to member function pointer"))
96

55
  } else if(l_type && isa(r_type, l_type) < 0)
97
1
      ERR_B(info->pos, _("can't assign member function to a pointer of an other class"))
98
54
  if(GET_FLAG(info->rhs, member)) {
99
24
    if(!GET_FLAG(info->lhs, member))
100
1
      ERR_B(info->pos, _("can't assign static function to member function pointer"))
101
30
  } else if(GET_FLAG(info->lhs, member))
102
2
      ERR_B(info->pos, _("can't assign member function to static function pointer"))
103
51
  return GW_OK;
104
}
105
106
56
ANN static inline m_bool fptr_rettype(const Env env, struct FptrInfo *info) {
107
112
  Type_Decl* td[2] = { info->lhs->def->base->td,
108
56
      info->rhs->def->base->td };
109
56
  return td_match(env, td);
110
}
111
112
56
ANN static inline m_bool fptr_arity(struct FptrInfo *info) {
113
112
  return GET_FLAG(info->lhs->def, variadic) ==
114
56
         GET_FLAG(info->rhs->def, variadic);
115
}
116
117
51
ANN static Type fptr_type(const Env env, struct FptrInfo *info) {
118
51
  const Value v = info->lhs->value_ref;
119
51
  const Nspc nspc = v->from->owner;
120
51
  const m_str c = s_name(info->lhs->def->base->xid),
121
51
    stmpl = !info->rhs->def->base->tmpl ? NULL : "template";
122
51
  Type type = NULL;
123

214
  for(m_uint i = 0; i <= v->from->offset && !type; ++i) {
124
127
    const Symbol sym = (!info->lhs->def->base->tmpl || i != 0) ?
125
97
        func_symbol(env, nspc->name, c, stmpl, i) : info->lhs->def->base->xid;
126
56
    if(!is_class(env->gwion, info->lhs->value_ref->type))
127
55
      CHECK_OO((info->lhs = nspc_lookup_func1(nspc, sym)))
128
    else {
129
1
      DECL_OO(const Type, t, = nspc_lookup_type1(nspc, info->lhs->def->base->xid))
130
1
      info->lhs = actual_type(env->gwion, t)->e->d.func;
131
    }
132
56
    Func_Base *base[2] =  { info->lhs->def->base, info->rhs->def->base };
133
56
    if(fptr_tmpl_push(env, info) > 0) {
134

112
      if(fptr_rettype(env, info) > 0 &&
135
112
           fptr_arity(info) && fptr_args(env, base) > 0)
136
50
      type = actual_type(env->gwion, info->lhs->value_ref->type) ?: info->lhs->value_ref->type;
137
56
      if(info->rhs->def->base->tmpl)
138
15
        nspc_pop_type(env->gwion->mp, env->curr);
139
    }
140
  }
141
51
  return type;
142
}
143
144
7
ANN static m_bool _check_lambda(const Env env, Exp_Lambda *l, const Func_Def def) {
145
7
  Arg_List base = def->base->args, arg = l->def->base->args;
146

20
  while(base && arg) {
147
6
    arg->td = base->td;
148
6
    base = base->next;
149
6
    arg = arg->next;
150
  }
151

7
  if(base || arg)
152
2
    ERR_B(exp_self(l)->pos, _("argument number does not match for lambda"))
153
5
  l->def->flag = def->flag;
154
5
  l->def->base->td = cpy_type_decl(env->gwion->mp, def->base->td);
155
5
  SET_FLAG(l->def, abstract); // mark as non immediate lambda
156
5
  map_set(&env->curr->info->func->map, (m_uint)l->def->base, env->scope->depth);
157
5
  const m_bool ret = check_traverse_fdef(env, l->def);
158
5
  map_remove(&env->curr->info->func->map, (m_uint)l->def->base);
159
5
  CHECK_BB(ret)
160
5
  arg = l->def->base->args;
161
14
  while(arg) {
162
4
    arg->td = NULL;
163
4
    arg = arg->next;
164
  }
165
5
  return GW_OK;
166
}
167
168
7
ANN m_bool check_lambda(const Env env, const Type t, Exp_Lambda *l) {
169
7
  const Func_Def fdef = t->e->d.func->def;
170
14
  struct EnvSet es = { .env=env, .data=env, .func=(_exp_func)check_cdef,
171
7
    .scope=env->scope->depth, .flag=ae_flag_check };
172
7
  l->owner = t->e->owner_class;
173
7
  CHECK_BB(envset_push(&es, l->owner, t->e->owner))
174
7
  const m_bool ret = _check_lambda(env, l, fdef);
175
7
  if(es.run)
176
    envset_pop(&es, l->owner);
177
7
  if(ret < 0)
178
2
    return GW_ERROR;
179
5
  exp_self(l)->info->type = l->def->base->func->value_ref->type;
180
5
  return GW_OK;
181
}
182
183
63
ANN static m_bool fptr_do(const Env env, struct FptrInfo *info) {
184
63
  if(isa(info->exp->info->type, env->gwion->type[et_lambda]) < 0) {
185
57
    m_bool nonnull = GET_FLAG(info->exp->info->type, nonnull);
186
57
    CHECK_BB(fptr_check(env, info))
187
51
    DECL_OB(const Type, t, = fptr_type(env, info))
188
50
    info->exp->info->type = !nonnull ? t : nonnul_type(env, t);
189
50
    return GW_OK;
190
  }
191
6
  Exp_Lambda *l = &info->exp->d.exp_lambda;
192
6
  return check_lambda(env, actual_type(env->gwion, info->rhs->value_ref->type), l);
193
}
194
195
1
static OP_CHECK(opck_auto_fptr) {
196
1
  const Exp_Binary* bin = (Exp_Binary*)data;
197
  // we'll only deal with auto fptr declaration
198

1
  if(bin->rhs->exp_type != ae_exp_decl && bin->rhs->d.exp_decl.td->xid != insert_symbol("auto"))
199
    return env->gwion->type[et_null];
200
  // create a matching signature
201
  // TODO: we could check first if there a matching existing one
202
1
  Func_Base *const fbase = cpy_func_base(env->gwion->mp, bin->lhs->info->type->e->d.func->def->base);
203
1
  const Fptr_Def fptr_def = new_fptr_def(env->gwion->mp, fbase, bin->lhs->info->type->e->d.func->def->flag);
204
1
  m_str name = NULL;
205
1
  asprintf(&name, "generated@%u:%u", bin->rhs->pos->first.line, bin->rhs->pos->first.column);
206
1
  fptr_def->base->xid = insert_symbol(name);
207
1
  free(name);
208
1
  const m_bool ret = traverse_fptr_def(env, fptr_def);
209
1
  const Type t = fptr_def->type;
210
1
  free_fptr_def(env->gwion->mp, fptr_def);
211
1
  REM_REF(t, env->gwion)
212
1
  bin->rhs->d.exp_decl.list->self->value->type = bin->rhs->info->type = bin->rhs->d.exp_decl.type = t;
213
1
  exp_setvar(bin->rhs, 1);
214
1
  return ret > 0 ? t : env->gwion->type[et_null];
215
}
216
217
54
static OP_CHECK(opck_fptr_at) {
218
54
  Exp_Binary* bin = (Exp_Binary*)data;
219

67
  if(bin->rhs->info->type->e->d.func->def->base->tmpl &&
220
13
     bin->rhs->info->type->e->d.func->def->base->tmpl->call) {
221
27
    struct FptrInfo info = { bin->lhs->info->type->e->d.func, bin->rhs->info->type->e->parent->e->d.func,
222
18
      bin->lhs, exp_self(bin)->pos };
223
9
    CHECK_BO(fptr_do(env, &info))
224
9
    exp_setvar(bin->rhs, 1);
225
9
    return bin->rhs->info->type;
226
  }
227
135
  struct FptrInfo info = { bin->lhs->info->type->e->d.func, bin->rhs->info->type->e->d.func,
228
90
      bin->lhs, exp_self(bin)->pos };
229
45
  CHECK_BO(fptr_do(env, &info))
230
36
  exp_setvar(bin->rhs, 1);
231
36
  return bin->rhs->info->type;
232
}
233
234
static OP_CHECK(opck_null_fptr_at) {
235
  Exp_Binary* bin = (Exp_Binary*)data;
236
  CHECK_NN(opck_const_rhs(env, bin, mut))
237
  exp_setvar(bin->rhs, 1);
238
  return bin->rhs->info->type;
239
}
240
241
4
static OP_CHECK(opck_fptr_cast) {
242
4
  Exp_Cast* cast = (Exp_Cast*)data;
243
4
  const Type t = exp_self(cast)->info->type;
244
12
  struct FptrInfo info = { cast->exp->info->type->e->d.func, t->e->d.func,
245
8
     cast->exp, exp_self(cast)->pos };
246
4
  CHECK_BO(fptr_do(env, &info))
247
4
  cast->func = cast->exp->info->type->e->d.func;
248
4
  return t;
249
}
250
251
6
static void member_fptr(const Emitter emit) {
252
6
  const Instr instr = emit_add_instr(emit, RegPop);
253
6
  instr->m_val = SZ_INT;
254
6
  const Instr dup = emit_add_instr(emit, Reg2Reg);
255
6
  dup->m_val = -SZ_INT;
256
6
}
257
258
9
static int is_member(const Type from, const Type to) {
259

21
  return GET_FLAG(from->e->d.func, member) &&
260
12
    !(GET_FLAG(from, nonnull) || GET_FLAG(to, nonnull));
261
}
262
263
4
static OP_EMIT(opem_fptr_cast) {
264
4
  const Exp_Cast* cast = (Exp_Cast*)data;
265
4
  if(exp_self(cast)->info->type->e->d.func->def->base->tmpl)
266
1
    fptr_instr(emit, cast->exp->info->type->e->d.func, 1);
267
4
  if(is_member(cast->exp->info->type, exp_self(cast)->info->type))
268
3
    member_fptr(emit);
269
4
  return (Instr)GW_OK;
270
}
271
272
5
static OP_CHECK(opck_fptr_impl) {
273
5
  struct Implicit *impl = (struct Implicit*)data;
274
15
  struct FptrInfo info = { impl->e->info->type->e->d.func, impl->t->e->d.func,
275
10
      impl->e, impl->e->pos };
276
5
  CHECK_BO(fptr_do(env, &info))
277
5
  return ((Exp)impl->e)->info->cast_to = impl->t;
278
}
279
280
5
static OP_EMIT(opem_fptr_impl) {
281
5
  struct Implicit *impl = (struct Implicit*)data;
282
5
  if(is_member(impl->e->info->type, impl->t))
283
3
    member_fptr(emit);
284
5
  if(impl->t->e->d.func->def->base->tmpl)
285
1
    fptr_instr(emit, ((Exp)impl->e)->info->type->e->d.func, 1);
286
5
  return (Instr)GW_OK;
287
}
288
289
ANN Type check_exp_unary_spork(const Env env, const Stmt code);
290
291
1
ANN static void fork_exp(const Env env, const Exp_Unary* unary) {
292
1
  const Stmt stmt = new_stmt_exp(env->gwion->mp, ae_stmt_exp, unary->exp);
293
1
  const Stmt_List list = new_stmt_list(env->gwion->mp, stmt, NULL);
294
1
  const Stmt code = new_stmt_code(env->gwion->mp, list);
295
1
  ((Exp_Unary*)unary)->exp = NULL;
296
1
  ((Exp_Unary*)unary)->code = code;
297
1
}
298
299
1
ANN static Type fork_type(const Env env, const Exp_Unary* unary) {
300
1
  const Type t = unary->exp->info->type;
301
1
  fork_exp(env, unary);
302
1
  if(t == env->gwion->type[et_void])
303
    return env->gwion->type[et_fork];
304
1
  Type_Decl td0 = { .xid=insert_symbol(t->name), .pos=exp_self(unary)->pos };
305
1
  struct Type_List_ tl = { .td=&td0 };
306
1
  Type_Decl td = { .xid=insert_symbol("TypedFork"), .types=&tl, .pos=exp_self(unary)->pos };
307
1
  return known_type(env, &td);
308
}
309
310
54
static OP_CHECK(opck_spork) {
311
54
  const Exp_Unary* unary = (Exp_Unary*)data;
312

54
  if(unary->exp && unary->exp->exp_type == ae_exp_call) {
313
28
    const m_bool is_spork = unary->op == insert_symbol("spork");
314
28
    return is_spork ? env->gwion->type[et_shred] : fork_type(env, unary);
315
  }
316
26
  if(unary->code) {
317
25
    ++env->scope->depth;
318
25
    nspc_push_value(env->gwion->mp, env->curr);
319
25
    const m_bool ret = check_stmt(env, unary->code);
320
25
    nspc_pop_value(env->gwion->mp, env->curr);
321
25
    --env->scope->depth;
322
25
    CHECK_BO(ret)
323
25
    return env->gwion->type[unary->op == insert_symbol("spork") ? et_shred : et_fork];
324
  }
325
1
  ERR_O(exp_self(unary)->pos, _("only function calls can be sporked..."))
326
}
327
328
53
static OP_EMIT(opem_spork) {
329
53
  const Exp_Unary* unary = (Exp_Unary*)data;
330
53
  return emit_exp_spork(emit, unary);
331
}
332
333
45
static FREEARG(freearg_xork) {
334
45
  REM_REF((VM_Code)instr->m_val, gwion)
335
45
}
336
337
45
static FREEARG(freearg_dottmpl) {
338
45
  struct dottmpl_ *dt = (struct dottmpl_*)instr->m_val;
339
45
  if(dt->tl)
340
45
    free_type_list(((Gwion)gwion)->mp, dt->tl);
341
45
  mp_free(((Gwion)gwion)->mp, dottmpl, dt);
342
45
}
343
344
711
GWION_IMPORT(func) {
345
711
  GWI_BB(gwi_oper_cond(gwi, "@func_ptr", BranchEqInt, BranchNeqInt))
346
711
  GWI_BB(gwi_oper_ini(gwi, (m_str)OP_ANY_TYPE, "@function", NULL))
347
711
  GWI_BB(gwi_oper_add(gwi, opck_func_call))
348
711
  GWI_BB(gwi_oper_end(gwi, "=>", NULL))
349
711
  GWI_BB(gwi_oper_ini(gwi, NULL, "@func_ptr", "bool"))
350
711
  GWI_BB(gwi_oper_end(gwi, "!", IntNot))
351
711
  GWI_BB(gwi_oper_ini(gwi, "@function", "@func_ptr", NULL))
352
711
  GWI_BB(gwi_oper_add(gwi, opck_fptr_at))
353
711
  GWI_BB(gwi_oper_emi(gwi, opem_func_assign))
354
711
  GWI_BB(gwi_oper_end(gwi, "@=>", NULL))
355
711
  GWI_BB(gwi_oper_add(gwi, opck_fptr_cast))
356
711
  GWI_BB(gwi_oper_emi(gwi, opem_fptr_cast))
357
711
  GWI_BB(gwi_oper_end(gwi, "$", NULL))
358
711
  GWI_BB(gwi_oper_add(gwi, opck_fptr_impl))
359
711
  GWI_BB(gwi_oper_emi(gwi, opem_fptr_impl))
360
711
  GWI_BB(gwi_oper_end(gwi, "@implicit", NULL))
361
711
  GWI_BB(gwi_oper_ini(gwi, "@null", "@func_ptr", NULL))
362
711
  GWI_BB(gwi_oper_add(gwi, opck_null_fptr_at))
363
711
  GWI_BB(gwi_oper_emi(gwi, opem_func_assign))
364
711
  GWI_BB(gwi_oper_end(gwi, "@=>", NULL))
365
711
  GWI_BB(gwi_oper_add(gwi, opck_fptr_cast))
366
711
  GWI_BB(gwi_oper_emi(gwi, opem_fptr_cast))
367
711
  GWI_BB(gwi_oper_end(gwi, "$", NULL))
368
711
  GWI_BB(gwi_oper_add(gwi, opck_fptr_impl))
369
711
  GWI_BB(gwi_oper_emi(gwi, opem_fptr_impl))
370
711
  GWI_BB(gwi_oper_end(gwi, "@implicit", NULL))
371
711
  GWI_BB(gwi_oper_ini(gwi, NULL, (m_str)OP_ANY_TYPE, NULL))
372
711
  GWI_BB(gwi_oper_add(gwi, opck_spork))
373
711
  GWI_BB(gwi_oper_emi(gwi, opem_spork))
374
711
  GWI_BB(gwi_oper_end(gwi, "spork", NULL))
375
711
  GWI_BB(gwi_oper_add(gwi, opck_spork))
376
711
  GWI_BB(gwi_oper_emi(gwi, opem_spork))
377
711
  GWI_BB(gwi_oper_end(gwi, "fork", NULL))
378
711
  GWI_BB(gwi_oper_ini(gwi, "@function", "@function", NULL))
379
711
  GWI_BB(gwi_oper_add(gwi, opck_auto_fptr))
380
711
  GWI_BB(gwi_oper_end(gwi, "@=>", NULL))
381
711
  gwi_register_freearg(gwi, SporkIni, freearg_xork);
382
711
  gwi_register_freearg(gwi, DotTmpl, freearg_dottmpl);
383
711
  return GW_OK;
384
}