# check_call.ludic — L4: a call gives each parameter the type it asks for, and as many arguments # as there are parameters. A function, an extern, a function value (a local, a global or a # record's field) and the builtins that convert are checked; a namespace call (Input.pad_axis) # gives the result type of the Ludic function behind it, and its arguments are the emitter's. function ck_params(f: Node, labels: []pointer, tys: []pointer) -> void { var i = 0 while i < len(f.kids) { if f.kids[i].kind == N_PARAM { push(labels, f.kids[i].s) push(tys, f.kids[i].ty) } i += 1 } } function ck_call_fn(e: Node, name: pointer, f: Node) -> pointer { call_fill_defaults(e, f) let labels = new []pointer let tys = new []pointer ck_params(f, labels, tys) ck_args(e, name, labels, tys) if f.ty == null { return "void" } return f.ty } function ck_call_alias(e: Node, name: pointer, al: int, f: Node) -> pointer { if g_al_labels[al] == null { call_fill_defaults(e, f) } else { call_mixed_to_named(e, ns_alias_labels(al)) } let labels = new []pointer let tys = new []pointer ck_params(f, labels, tys) let alabels = ns_alias_labels(al) if len(alabels) == len(tys) { ck_args(e, name, alabels, tys) } else { ck_args(e, name, labels, tys) } if f.ty == null { return "void" } return f.ty } function ck_call_sig(e: Node, name: pointer, t: pointer) -> pointer { ck_args(e, name, new []pointer, fn_ty_params(t)) return fn_ty_ret(t) } function ck_label_at(labels: []pointer, s: pointer) -> int { var i = 0 while i < len(labels) { if labels[i] == s { return i } i += 1 } return -1 } function ck_args(e: Node, name: pointer, labels: []pointer, tys: []pointer) -> void { let n = len(e.kids) if n > 0 and e.kids[0].kind == E_FINIT { var j = 0 while j < n { let fi = e.kids[j] let vt = ck_expr(fi.a) let at = ck_label_at(labels, fi.s) if at >= 0 { ck_give(tys[at], vt, fi.a, `argument {fi.s} of {name}`) } if at < 0 and len(labels) > 0 { ck_err("arity", fi.a, `{name} has no parameter {fi.s}`) } j += 1 } var m = 0 while m < len(labels) { if not ck_named_has(e, labels[m]) { ck_err("arity", e, `this call to {name} leaves out {labels[m]}, which has no default`) } m += 1 } return } if n < len(tys) and len(labels) == len(tys) { ck_err("arity", e, `this call to {name} leaves out {labels[n]}, which has no default`) } else { if n != len(tys) { ck_err("arity", e, `{name} takes {itoa(len(tys))} argument(s) and this call gives {itoa(n)}`) } } var i = 0 while i < n { if i < len(tys) { ck_expect = tys[i] } let at = ck_expr(e.kids[i]) if i < len(tys) { ck_give(tys[i], at, e.kids[i], `argument {itoa(i + 1)} of {name}`) } i += 1 } } function ck_named_has(e: Node, label: pointer) -> bool { var i = 0 while i < len(e.kids) { if (e.kids[i].s == label) { return true } i += 1 } return false } function ck_walk_args(e: Node) -> void { var i = 0 while i < len(e.kids) { ck_any(e.kids[i]) i += 1 } } # the builtins whose type is their name, and push, whose element must fit the slice function ck_builtin(e: Node, name: pointer) -> pointer { if (name == "string") or (name == "int") or (name == "float") or (name == "fixed") or (name == "long") or (name == "double") { ck_walk_args(e) return name } if (name == "as_int") or (name == "as_fixed") { ck_walk_args(e) return name[3..len(name)] } if (name == "len") or (name == "float_bits") { ck_walk_args(e) return "int" } if (name == "view") and len(e.kids) == 3 { let vt = ck_expr(e.kids[0]) ck_give("int", ck_expr(e.kids[1]), e.kids[1], "the start of a view") ck_give("int", ck_expr(e.kids[2]), e.kids[2], "the length of a view") if not ck_unknown(vt) and not is_slice_ty(vt) { ck_err("kind", e, `view takes a slice, and this is {ck_a(vt)}`) } return vt } if (name == "bytes") or (name == "offset") { ck_walk_args(e) return "pointer" } if (name == "data_of") { ck_walk_args(e) return "pointer" } if (name == "words") { ck_walk_args(e) return "[]int" } if (name == "buffer") { ck_walk_args(e) return "[]byte" } if (name == "fixeds") or (name == "pointers") { ck_walk_args(e) return "[]" + name[0 .. len(name) - 1] } if (name == "floats") or (name == "doubles") { ck_walk_args(e) return "[]" + name[0 .. len(name) - 1] } if (name == "float_from_bits") { ck_walk_args(e) return "float" } if (name == "print") { ck_walk_args(e) return "void" } if (name == "push") and len(e.kids) == 2 { let st = ck_expr(e.kids[0]) let vt = ck_expr(e.kids[1]) if not ck_unknown(st) and is_slice_ty(st) { ck_give(slice_elem(st), vt, e.kids[1], `push onto {ck_a(st)}`) } return "void" } return null } # a program's function named like the target of one of the engine's own namespace methods takes # that method's calls - a package's rng_range(a, b, c) made every Random.range a call to it, and # the checker then asked for its third argument. Refused where the method is called. var ck_shadow_seen: []pointer = new []pointer function ck_alias_shadow(e: Node, al: int, tf: Node) -> void { if not has_sub(g_al_file[al], "runtime/native/") { return } if tf.file == null or has_sub(tf.file, "runtime/native/") { return } var i = 0 while i < len(ck_shadow_seen) { if (ck_shadow_seen[i] == tf.s) { return } i += 1 } push(ck_shadow_seen, tf.s) ck_err("shadow", tf, `{tf.s} is the engine's {g_al_ns[al]}.{g_al_meth[al]}, which this program calls ({e.file}:{itoa(e.line)}), and every such call would reach this function instead; choose another name`) } function ck_call(e: Node) -> pointer { let c = e.a if c.kind == E_ID { return ck_call_named(e, c.s) } if c.kind == E_MEMBER { let b = c.a if b.kind == E_ID and ck_local(b.s) < 0 and ck_global(b.s) == null { # an alias (L6) is its target, labels and all, so its arguments are checked in full if (b.s == "Memory") { ck_raw(e, `Memory.{c.s}`) } let al = ns_alias_find(b.s, c.s) if al >= 0 { var tf = ck_fn(g_al_target[al]) if tf != null { ck_alias_shadow(e, al, tf) } if tf == null { tf = ck_extern(g_al_target[al]) if tf != null { ck_raw(e, `{b.s}.{c.s}, a C function`) } } if tf != null { if ck_fn(g_al_target[al]) != null { ck_vis(tf, g_al_target[al], e) } ck_extern_arg = ck_extern(g_al_target[al]) != null let at = ck_call_alias(e, `{b.s}.{c.s}`, al, tf) ck_extern_arg = false return at } ck_walk_args(e) return "?" } # any other Ns.fn is the emitter's own table, which supplies defaults and reorders, # so the Ludic function behind it gives the result's type and nothing about the arguments ck_walk_args(e) let nf = ck_fn(ns_lower(b.s) + "_" + c.s) if nf != null { ck_vis(nf, nf.s, e) } if nf != null and nf.ty != null { return nf.ty } return "?" } let r = ck_record(ck_expr(b)) if r != null { let ft = ck_field(r, c.s) if ft != null and is_fn_type(ft) { return ck_call_sig(e, c.s, ft) } } ck_walk_args(e) return "?" } ck_expr(c) ck_walk_args(e) return "?" } function ck_call_named(e: Node, name: pointer) -> pointer { let li = ck_local(name) if li >= 0 { if is_fn_type(ck_tys[li]) { return ck_call_sig(e, name, ck_tys[li]) } ck_walk_args(e) return "?" } if ck_raw_builtin(name) { ck_raw(e, `{name}()`) } let bt = ck_builtin(e, name) if bt != null { return bt } let f = ck_fn(name) if f != null { ck_vis(f, name, e) return ck_call_fn(e, name, f) } let gt = gen_template(g_gen_fns, name) if gt != null { ck_vis(gt, name, e) return gen_call(e, name, gt) } let x = ck_extern(name) if x != null { ck_raw(e, `the C function {name}`) ck_extern_arg = true let xt = ck_call_fn(e, name, x) ck_extern_arg = false return xt } let g = ck_global(name) if g != null and is_fn_type(g.ty) { return ck_call_sig(e, name, g.ty) } ck_walk_args(e) return "?" } # emit E(field: v): each field gets the type the event declares for it function ck_emit(s: Node) -> void { let ev = find_event(s.s) ck_vis(ev, s.s, s) if s.a == null { return } var i = 0 while i < len(s.a.kids) { let fi = s.a.kids[i] let vt = ck_expr(fi.a) if ev != null { let ft = ck_field(ev, fi.s) if ft != null { ck_give(ft, vt, fi.a, `field {fi.s} of {s.s}`) } } i += 1 } }