GCC Code Coverage Report
Directory: src/ Exec Total Coverage
File: src/lib/func.c Lines: 271 284 95.4 %
Date: 2020-08-07 19:15:19 Branches: 118 152 77.6 %

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
35
static OP_EMIT(opem_func_assign) {
33
35
  Exp_Binary* bin = (Exp_Binary*)data;
34
35
  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
35
  const Instr instr = emit_add_instr(emit, int_r_assign);
37

35
  if(!is_fptr(emit->gwion, bin->lhs->info->type) && GET_FLAG(bin->rhs->info->type->e->d.func, member)) {
38
20
    const Instr pop = emit_add_instr(emit, RegPop);
39
20
    pop->m_val = SZ_INT;
40
20
    const Instr cpy = emit_add_instr(emit, Reg2Reg);
41
20
    cpy->m_val = -SZ_INT;
42
  }
43
35
  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
50
ANN static m_bool fptr_tmpl_push(const Env env, struct FptrInfo *info) {
54
50
  if(!info->rhs->def->base->tmpl)
55
35
    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
66
static m_bool td_match(const Env env, Type_Decl *id[2]) {
69
66
  DECL_OB(const Type, t0, = known_type(env, id[0]))
70
66
  DECL_OB(const Type, t1, = known_type(env, id[1]))
71
66
  return isa(t0, t1);
72
}
73
74
50
ANN static m_bool fptr_args(const Env env, Func_Base *base[2]) {
75
50
  Arg_List arg0 = base[0]->args, arg1 = base[1]->args;
76
116
  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
45
  return !arg1 ? GW_OK : GW_ERROR;
84
}
85
86
51
ANN static m_bool fptr_check(const Env env, struct FptrInfo *info) {
87
51
  if(!info->lhs->def->base->tmpl != !info->rhs->def->base->tmpl)
88
    return GW_ERROR;
89
51
  const Type l_type = info->lhs->value_ref->from->owner_class;
90
51
  const Type r_type = info->rhs->value_ref->from->owner_class;
91

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

50
  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

49
  } 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
48
  if(GET_FLAG(info->rhs, member)) {
99
23
    if(!GET_FLAG(info->lhs, member))
100
1
      ERR_B(info->pos, _("can't assign static function to member function pointer"))
101
25
  } else if(GET_FLAG(info->lhs, member))
102
2
      ERR_B(info->pos, _("can't assign member function to static function pointer"))
103
45
  return GW_OK;
104
}
105
106
50
ANN static inline m_bool fptr_rettype(const Env env, struct FptrInfo *info) {
107
100
  Type_Decl* td[2] = { info->lhs->def->base->td,
108
50
      info->rhs->def->base->td };
109
50
  return td_match(env, td);
110
}
111
112
50
ANN static inline m_bool fptr_arity(struct FptrInfo *info) {
113
100
  return GET_FLAG(info->lhs->def, variadic) ==
114
50
         GET_FLAG(info->rhs->def, variadic);
115
}
116
117
45
ANN static Type fptr_type(const Env env, struct FptrInfo *info) {
118
45
  const Value v = info->lhs->value_ref;
119
45
  const Nspc nspc = v->from->owner;
120
45
  const m_str c = s_name(info->lhs->def->base->xid),
121
45
    stmpl = !info->rhs->def->base->tmpl ? NULL : "template";
122
45
  Type type = NULL;
123

190
  for(m_uint i = 0; i <= v->from->offset && !type; ++i) {
124
115
    const Symbol sym = (!info->lhs->def->base->tmpl || i != 0) ?
125
85
        func_symbol(env, nspc->name, c, stmpl, i) : info->lhs->def->base->xid;
126
50
    if(!is_class(env->gwion, info->lhs->value_ref->type))
127
49
      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
50
    Func_Base *base[2] =  { info->lhs->def->base, info->rhs->def->base };
133
50
    if(fptr_tmpl_push(env, info) > 0) {
134

100
      if(fptr_rettype(env, info) > 0 &&
135
100
           fptr_arity(info) && fptr_args(env, base) > 0)
136
44
      type = actual_type(env->gwion, info->lhs->value_ref->type) ?: info->lhs->value_ref->type;
137
50
      if(info->rhs->def->base->tmpl)
138
15
        nspc_pop_type(env->gwion->mp, env->curr);
139
    }
140
  }
141
45
  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
57
ANN static m_bool fptr_do(const Env env, struct FptrInfo *info) {
184
57
  if(isa(info->exp->info->type, env->gwion->type[et_lambda]) < 0) {
185
51
    m_bool nonnull = GET_FLAG(info->exp->info->type, nonnull);
186
51
    CHECK_BB(fptr_check(env, info))
187
45
    DECL_OB(const Type, t, = fptr_type(env, info))
188
44
    info->exp->info->type = !nonnull ? t : nonnul_type(env, t);
189
44
    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
  return ret > 0 ? t : env->gwion->type[et_null];
213
}
214
215
49
static OP_CHECK(opck_fptr_at) {
216
49
  Exp_Binary* bin = (Exp_Binary*)data;
217

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

20
  return GET_FLAG(from->e->d.func, member) &&
258
12
    !(GET_FLAG(from, nonnull) || GET_FLAG(to, nonnull));
259
}
260
261
3
static OP_EMIT(opem_fptr_cast) {
262
3
  const Exp_Cast* cast = (Exp_Cast*)data;
263
3
  if(exp_self(cast)->info->type->e->d.func->def->base->tmpl)
264
1
    fptr_instr(emit, cast->exp->info->type->e->d.func, 1);
265
3
  if(is_member(cast->exp->info->type, exp_self(cast)->info->type))
266
3
    member_fptr(emit);
267
3
  return (Instr)GW_OK;
268
}
269
270
5
static OP_CHECK(opck_fptr_impl) {
271
5
  struct Implicit *impl = (struct Implicit*)data;
272
15
  struct FptrInfo info = { impl->e->info->type->e->d.func, impl->t->e->d.func,
273
10
      impl->e, impl->e->pos };
274
5
  CHECK_BO(fptr_do(env, &info))
275
5
  return ((Exp)impl->e)->info->cast_to = impl->t;
276
}
277
278
5
static OP_EMIT(opem_fptr_impl) {
279
5
  struct Implicit *impl = (struct Implicit*)data;
280
5
  if(is_member(impl->e->info->type, impl->t))
281
3
    member_fptr(emit);
282
5
  if(impl->t->e->d.func->def->base->tmpl)
283
1
    fptr_instr(emit, ((Exp)impl->e)->info->type->e->d.func, 1);
284
5
  return (Instr)GW_OK;
285
}
286
287
ANN Type check_exp_unary_spork(const Env env, const Stmt code);
288
289
53
static OP_CHECK(opck_spork) {
290
53
  const Exp_Unary* unary = (Exp_Unary*)data;
291

53
  if(unary->exp && unary->exp->exp_type == ae_exp_call) {
292
27
    const m_bool is_spork = unary->op == insert_symbol("spork");
293
27
    if(!is_spork) {
294
      const Stmt stmt = new_stmt_exp(env->gwion->mp, ae_stmt_exp, unary->exp);
295
      const Stmt_List list = new_stmt_list(env->gwion->mp, stmt, NULL);
296
      const Stmt code = new_stmt_code(env->gwion->mp, list);
297
      ((Exp_Unary*)unary)->exp = NULL;
298
      ((Exp_Unary*)unary)->code = code;
299
    }
300
27
    return env->gwion->type[is_spork ? et_shred : et_fork];
301
  }
302
26
  if(unary->code) {
303
25
    ++env->scope->depth;
304
25
    nspc_push_value(env->gwion->mp, env->curr);
305
25
    const m_bool ret = check_stmt(env, unary->code);
306
25
    nspc_pop_value(env->gwion->mp, env->curr);
307
25
    --env->scope->depth;
308
25
    CHECK_BO(ret)
309
25
    return env->gwion->type[unary->op == insert_symbol("spork") ? et_shred : et_fork];
310
  }
311
1
  ERR_O(exp_self(unary)->pos, _("only function calls can be sporked..."))
312
}
313
314
52
static OP_EMIT(opem_spork) {
315
52
  const Exp_Unary* unary = (Exp_Unary*)data;
316
52
  const Env env = emit->env;
317
52
  const Instr ret = emit_exp_spork(emit, unary);
318
52
  if(unary->op == insert_symbol("fork"))
319
7
    emit_add_instr(emit, GcAdd);
320
52
  return ret;
321
}
322
323
45
static FREEARG(freearg_xork) {
324
45
  REM_REF((VM_Code)instr->m_val, gwion)
325
45
}
326
327
31
static FREEARG(freearg_dottmpl) {
328
31
  struct dottmpl_ *dt = (struct dottmpl_*)instr->m_val;
329
31
  if(dt->tl)
330
31
    free_type_list(((Gwion)gwion)->mp, dt->tl);
331
31
  mp_free(((Gwion)gwion)->mp, dottmpl, dt);
332
31
}
333
334
730
GWION_IMPORT(func) {
335
730
  GWI_BB(gwi_oper_cond(gwi, "@func_ptr", BranchEqInt, BranchNeqInt))
336
730
  GWI_BB(gwi_oper_ini(gwi, (m_str)OP_ANY_TYPE, "@function", NULL))
337
730
  GWI_BB(gwi_oper_add(gwi, opck_func_call))
338
730
  GWI_BB(gwi_oper_end(gwi, "=>", NULL))
339
730
  GWI_BB(gwi_oper_ini(gwi, NULL, "@func_ptr", "bool"))
340
730
  GWI_BB(gwi_oper_end(gwi, "!", IntNot))
341
730
  GWI_BB(gwi_oper_ini(gwi, "@function", "@func_ptr", NULL))
342
730
  GWI_BB(gwi_oper_add(gwi, opck_fptr_at))
343
730
  GWI_BB(gwi_oper_emi(gwi, opem_func_assign))
344
730
  GWI_BB(gwi_oper_end(gwi, "@=>", NULL))
345
730
  GWI_BB(gwi_oper_add(gwi, opck_fptr_cast))
346
730
  GWI_BB(gwi_oper_emi(gwi, opem_fptr_cast))
347
730
  GWI_BB(gwi_oper_end(gwi, "$", NULL))
348
730
  GWI_BB(gwi_oper_add(gwi, opck_fptr_impl))
349
730
  GWI_BB(gwi_oper_emi(gwi, opem_fptr_impl))
350
730
  GWI_BB(gwi_oper_end(gwi, "@implicit", NULL))
351
730
  GWI_BB(gwi_oper_ini(gwi, "@null", "@func_ptr", NULL))
352
730
  GWI_BB(gwi_oper_add(gwi, opck_null_fptr_at))
353
730
  GWI_BB(gwi_oper_emi(gwi, opem_func_assign))
354
730
  GWI_BB(gwi_oper_end(gwi, "@=>", NULL))
355
730
  GWI_BB(gwi_oper_add(gwi, opck_fptr_cast))
356
730
  GWI_BB(gwi_oper_emi(gwi, opem_fptr_cast))
357
730
  GWI_BB(gwi_oper_end(gwi, "$", NULL))
358
730
  GWI_BB(gwi_oper_add(gwi, opck_fptr_impl))
359
730
  GWI_BB(gwi_oper_emi(gwi, opem_fptr_impl))
360
730
  GWI_BB(gwi_oper_end(gwi, "@implicit", NULL))
361
730
  GWI_BB(gwi_oper_ini(gwi, NULL, (m_str)OP_ANY_TYPE, NULL))
362
730
  GWI_BB(gwi_oper_add(gwi, opck_spork))
363
730
  GWI_BB(gwi_oper_emi(gwi, opem_spork))
364
730
  GWI_BB(gwi_oper_end(gwi, "spork", NULL))
365
730
  GWI_BB(gwi_oper_add(gwi, opck_spork))
366
730
  GWI_BB(gwi_oper_emi(gwi, opem_spork))
367
730
  GWI_BB(gwi_oper_end(gwi, "fork", NULL))
368
730
  GWI_BB(gwi_oper_ini(gwi, "@function", "@function", NULL))
369
730
  GWI_BB(gwi_oper_add(gwi, opck_auto_fptr))
370
730
  GWI_BB(gwi_oper_end(gwi, "@=>", NULL))
371
730
  gwi_register_freearg(gwi, SporkIni, freearg_xork);
372
730
  gwi_register_freearg(gwi, DotTmpl, freearg_dottmpl);
373
730
  return GW_OK;
374
}