aboutsummaryrefslogtreecommitdiff
path: root/v_windows/v/vlib/v/gen/c
diff options
context:
space:
mode:
Diffstat (limited to 'v_windows/v/vlib/v/gen/c')
-rw-r--r--v_windows/v/vlib/v/gen/c/array.v723
-rw-r--r--v_windows/v/vlib/v/gen/c/assert.v159
-rw-r--r--v_windows/v/vlib/v/gen/c/auto_eq_methods.v343
-rw-r--r--v_windows/v/vlib/v/gen/c/auto_str_methods.v900
-rw-r--r--v_windows/v/vlib/v/gen/c/cgen.v6878
-rw-r--r--v_windows/v/vlib/v/gen/c/cheaders.v718
-rw-r--r--v_windows/v/vlib/v/gen/c/cmain.v214
-rw-r--r--v_windows/v/vlib/v/gen/c/comptime.v676
-rw-r--r--v_windows/v/vlib/v/gen/c/coutput_test.v174
-rw-r--r--v_windows/v/vlib/v/gen/c/ctempvars.v25
-rw-r--r--v_windows/v/vlib/v/gen/c/dumpexpr.v72
-rw-r--r--v_windows/v/vlib/v/gen/c/embed.v69
-rw-r--r--v_windows/v/vlib/v/gen/c/fn.v1558
-rw-r--r--v_windows/v/vlib/v/gen/c/index.v452
-rw-r--r--v_windows/v/vlib/v/gen/c/infix_expr.v628
-rw-r--r--v_windows/v/vlib/v/gen/c/json.v339
-rw-r--r--v_windows/v/vlib/v/gen/c/live.v104
-rw-r--r--v_windows/v/vlib/v/gen/c/profile.v52
-rw-r--r--v_windows/v/vlib/v/gen/c/sql.v836
-rw-r--r--v_windows/v/vlib/v/gen/c/str.v145
-rw-r--r--v_windows/v/vlib/v/gen/c/str_intp.v206
-rw-r--r--v_windows/v/vlib/v/gen/c/testdata/addition.c.must_have1
-rw-r--r--v_windows/v/vlib/v/gen/c/testdata/addition.out1
-rw-r--r--v_windows/v/vlib/v/gen/c/testdata/addition.vv4
-rw-r--r--v_windows/v/vlib/v/gen/c/testdata/const_references.c.must_have3
-rw-r--r--v_windows/v/vlib/v/gen/c/testdata/const_references.out3
-rw-r--r--v_windows/v/vlib/v/gen/c/testdata/const_references.vv11
-rw-r--r--v_windows/v/vlib/v/gen/c/utils.v49
28 files changed, 15343 insertions, 0 deletions
diff --git a/v_windows/v/vlib/v/gen/c/array.v b/v_windows/v/vlib/v/gen/c/array.v
new file mode 100644
index 0000000..74dc9aa
--- /dev/null
+++ b/v_windows/v/vlib/v/gen/c/array.v
@@ -0,0 +1,723 @@
+// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved.
+// Use of this source code is governed by an MIT license that can be found in the LICENSE file.
+module c
+
+import strings
+import v.ast
+
+fn (mut g Gen) array_init(node ast.ArrayInit) {
+ array_type := g.unwrap(node.typ)
+ mut array_styp := ''
+ elem_type := g.unwrap(node.elem_type)
+ mut shared_styp := '' // only needed for shared &[]{...}
+ is_amp := g.is_amp
+ g.is_amp = false
+ if is_amp {
+ g.out.go_back(1) // delete the `&` already generated in `prefix_expr()
+ }
+ if g.is_shared {
+ shared_styp = g.typ(array_type.typ.set_flag(.shared_f))
+ g.writeln('($shared_styp*)__dup_shared_array(&($shared_styp){.mtx = {0}, .val =')
+ } else if is_amp {
+ array_styp = g.typ(array_type.typ)
+ g.write('HEAP($array_styp, ')
+ }
+ if array_type.unaliased_sym.kind == .array_fixed {
+ g.write('{')
+ if node.has_val {
+ for i, expr in node.exprs {
+ if expr.is_auto_deref_var() {
+ g.write('*')
+ }
+ g.expr(expr)
+ if i != node.exprs.len - 1 {
+ g.write(', ')
+ }
+ }
+ } else if node.has_default {
+ g.expr(node.default_expr)
+ info := array_type.unaliased_sym.info as ast.ArrayFixed
+ for _ in 1 .. info.size {
+ g.write(', ')
+ g.expr(node.default_expr)
+ }
+ } else {
+ g.write('0')
+ }
+ g.write('}')
+ return
+ }
+ elem_styp := g.typ(elem_type.typ)
+ noscan := g.check_noscan(elem_type.typ)
+ if node.exprs.len == 0 {
+ is_default_array := elem_type.unaliased_sym.kind == .array && node.has_default
+ if is_default_array {
+ g.write('__new_array_with_array_default${noscan}(')
+ } else {
+ g.write('__new_array_with_default${noscan}(')
+ }
+ if node.has_len {
+ g.expr(node.len_expr)
+ g.write(', ')
+ } else {
+ g.write('0, ')
+ }
+ if node.has_cap {
+ g.expr(node.cap_expr)
+ g.write(', ')
+ } else {
+ g.write('0, ')
+ }
+ if elem_type.unaliased_sym.kind == .function {
+ g.write('sizeof(voidptr), ')
+ } else {
+ g.write('sizeof($elem_styp), ')
+ }
+ if is_default_array {
+ g.write('($elem_styp[]){')
+ g.expr(node.default_expr)
+ g.write('}[0])')
+ } else if node.has_default {
+ g.write('&($elem_styp[]){')
+ g.expr(node.default_expr)
+ g.write('})')
+ } else if node.has_len && node.elem_type == ast.string_type {
+ g.write('&($elem_styp[]){')
+ g.write('_SLIT("")')
+ g.write('})')
+ } else if node.has_len && elem_type.unaliased_sym.kind in [.array, .map] {
+ g.write('(voidptr)&($elem_styp[]){')
+ g.write(g.type_default(node.elem_type))
+ g.write('}[0])')
+ } else {
+ g.write('0)')
+ }
+ if g.is_shared {
+ g.write('}, sizeof($shared_styp))')
+ } else if is_amp {
+ g.write(')')
+ }
+ return
+ }
+ len := node.exprs.len
+ if elem_type.unaliased_sym.kind == .function {
+ g.write('new_array_from_c_array($len, $len, sizeof(voidptr), _MOV((voidptr[$len]){')
+ } else {
+ g.write('new_array_from_c_array${noscan}($len, $len, sizeof($elem_styp), _MOV(($elem_styp[$len]){')
+ }
+ if len > 8 {
+ g.writeln('')
+ g.write('\t\t')
+ }
+ for i, expr in node.exprs {
+ if node.expr_types[i] == ast.string_type && expr !is ast.StringLiteral
+ && expr !is ast.StringInterLiteral {
+ g.write('string_clone(')
+ g.expr(expr)
+ g.write(')')
+ } else {
+ g.expr_with_cast(expr, node.expr_types[i], node.elem_type)
+ }
+ if i != len - 1 {
+ if i > 0 && i & 7 == 0 { // i > 0 && i % 8 == 0
+ g.writeln(',')
+ g.write('\t\t')
+ } else {
+ g.write(', ')
+ }
+ }
+ }
+ g.write('}))')
+ if g.is_shared {
+ g.write('}, sizeof($shared_styp))')
+ } else if is_amp {
+ g.write('), sizeof($array_styp))')
+ }
+}
+
+// `nums.map(it % 2 == 0)`
+fn (mut g Gen) gen_array_map(node ast.CallExpr) {
+ g.inside_lambda = true
+ tmp := g.new_tmp_var()
+ s := g.go_before_stmt(0)
+ // println('filter s="$s"')
+ ret_typ := g.typ(node.return_type)
+ // inp_typ := g.typ(node.receiver_type)
+ ret_sym := g.table.get_type_symbol(node.return_type)
+ inp_sym := g.table.get_type_symbol(node.receiver_type)
+ ret_info := ret_sym.info as ast.Array
+ ret_elem_type := g.typ(ret_info.elem_type)
+ inp_info := inp_sym.info as ast.Array
+ inp_elem_type := g.typ(inp_info.elem_type)
+ if inp_sym.kind != .array {
+ verror('map() requires an array')
+ }
+ g.empty_line = true
+ g.write('${g.typ(node.left_type)} ${tmp}_orig = ')
+ g.expr(node.left)
+ g.writeln(';')
+ g.writeln('int ${tmp}_len = ${tmp}_orig.len;')
+ noscan := g.check_noscan(ret_info.elem_type)
+ g.writeln('$ret_typ $tmp = __new_array${noscan}(0, ${tmp}_len, sizeof($ret_elem_type));\n')
+ i := g.new_tmp_var()
+ g.writeln('for (int $i = 0; $i < ${tmp}_len; ++$i) {')
+ g.writeln('\t$inp_elem_type it = (($inp_elem_type*) ${tmp}_orig.data)[$i];')
+ mut is_embed_map_filter := false
+ mut expr := node.args[0].expr
+ match mut expr {
+ ast.AnonFn {
+ g.write('\t$ret_elem_type ti = ')
+ g.gen_anon_fn_decl(mut expr)
+ g.write('${expr.decl.name}(it)')
+ }
+ ast.Ident {
+ g.write('\t$ret_elem_type ti = ')
+ if expr.kind == .function {
+ g.write('${c_name(expr.name)}(it)')
+ } else if expr.kind == .variable {
+ var_info := expr.var_info()
+ sym := g.table.get_type_symbol(var_info.typ)
+ if sym.kind == .function {
+ g.write('${c_name(expr.name)}(it)')
+ } else {
+ g.expr(node.args[0].expr)
+ }
+ } else {
+ g.expr(node.args[0].expr)
+ }
+ }
+ ast.CallExpr {
+ if expr.name in ['map', 'filter'] {
+ is_embed_map_filter = true
+ g.stmt_path_pos << g.out.len
+ }
+ g.write('\t$ret_elem_type ti = ')
+ g.expr(node.args[0].expr)
+ }
+ else {
+ g.write('\t$ret_elem_type ti = ')
+ g.expr(node.args[0].expr)
+ }
+ }
+ g.writeln(';')
+ g.writeln('\tarray_push${noscan}((array*)&$tmp, &ti);')
+ g.writeln('}')
+ if !is_embed_map_filter {
+ g.stmt_path_pos << g.out.len
+ }
+ g.write('\n')
+ g.write(s)
+ g.write(tmp)
+ g.inside_lambda = false
+}
+
+// `users.sort(a.age < b.age)`
+fn (mut g Gen) gen_array_sort(node ast.CallExpr) {
+ // println('filter s="$s"')
+ rec_sym := g.table.get_type_symbol(node.receiver_type)
+ if rec_sym.kind != .array {
+ println(node.name)
+ println(g.typ(node.receiver_type))
+ // println(rec_sym.kind)
+ verror('.sort() is an array method')
+ }
+ if g.pref.is_bare {
+ g.writeln('bare_panic(_SLIT("sort does not work with -freestanding"))')
+ return
+ }
+ info := rec_sym.info as ast.Array
+ // `users.sort(a.age > b.age)`
+ // Generate a comparison function for a custom type
+ elem_stype := g.typ(info.elem_type)
+ mut compare_fn := 'compare_${elem_stype.replace('*', '_ptr')}'
+ mut comparison_type := g.unwrap(ast.void_type)
+ mut left_expr, mut right_expr := '', ''
+ // the only argument can only be an infix expression like `a < b` or `b.field > a.field`
+ if node.args.len == 0 {
+ comparison_type = g.unwrap(info.elem_type.set_nr_muls(0))
+ if compare_fn in g.array_sort_fn {
+ g.gen_array_sort_call(node, compare_fn)
+ return
+ }
+ left_expr = '*a'
+ right_expr = '*b'
+ } else {
+ infix_expr := node.args[0].expr as ast.InfixExpr
+ comparison_type = g.unwrap(infix_expr.left_type.set_nr_muls(0))
+ left_name := infix_expr.left.str()
+ if left_name.len > 1 {
+ compare_fn += '_by' + left_name[1..].replace_each(['.', '_', '[', '_', ']', '_'])
+ }
+ // is_reverse is `true` for `.sort(a > b)` and `.sort(b < a)`
+ is_reverse := (left_name.starts_with('a') && infix_expr.op == .gt)
+ || (left_name.starts_with('b') && infix_expr.op == .lt)
+ if is_reverse {
+ compare_fn += '_reverse'
+ }
+ if compare_fn in g.array_sort_fn {
+ g.gen_array_sort_call(node, compare_fn)
+ return
+ }
+ if left_name.starts_with('a') != is_reverse {
+ left_expr = g.expr_string(infix_expr.left)
+ right_expr = g.expr_string(infix_expr.right)
+ if infix_expr.left is ast.Ident {
+ left_expr = '*' + left_expr
+ }
+ if infix_expr.right is ast.Ident {
+ right_expr = '*' + right_expr
+ }
+ } else {
+ left_expr = g.expr_string(infix_expr.right)
+ right_expr = g.expr_string(infix_expr.left)
+ if infix_expr.left is ast.Ident {
+ right_expr = '*' + right_expr
+ }
+ if infix_expr.right is ast.Ident {
+ left_expr = '*' + left_expr
+ }
+ }
+ }
+
+ // Register a new custom `compare_xxx` function for qsort()
+ // TODO: move to checker
+ g.table.register_fn(name: compare_fn, return_type: ast.int_type)
+ g.array_sort_fn[compare_fn] = true
+
+ stype_arg := g.typ(info.elem_type)
+ g.definitions.writeln('int ${compare_fn}($stype_arg* a, $stype_arg* b) {')
+ c_condition := if comparison_type.sym.has_method('<') {
+ '${g.typ(comparison_type.typ)}__lt($left_expr, $right_expr)'
+ } else if comparison_type.unaliased_sym.has_method('<') {
+ '${g.typ(comparison_type.unaliased)}__lt($left_expr, $right_expr)'
+ } else {
+ '$left_expr < $right_expr'
+ }
+ g.definitions.writeln('\tif ($c_condition) return -1;')
+ g.definitions.writeln('\telse return 1;')
+ g.definitions.writeln('}\n')
+
+ // write call to the generated function
+ g.gen_array_sort_call(node, compare_fn)
+}
+
+fn (mut g Gen) gen_array_sort_call(node ast.CallExpr, compare_fn string) {
+ deref_field := if node.left_type.is_ptr() || node.left_type.is_pointer() { '->' } else { '.' }
+ // eprintln('> qsort: pointer $node.left_type | deref: `$deref`')
+ g.empty_line = true
+ g.write('qsort(')
+ g.expr(node.left)
+ g.write('${deref_field}data, ')
+ g.expr(node.left)
+ g.write('${deref_field}len, ')
+ g.expr(node.left)
+ g.write('${deref_field}element_size, (int (*)(const void *, const void *))&$compare_fn)')
+}
+
+// `nums.filter(it % 2 == 0)`
+fn (mut g Gen) gen_array_filter(node ast.CallExpr) {
+ tmp := g.new_tmp_var()
+ s := g.go_before_stmt(0)
+ // println('filter s="$s"')
+ sym := g.table.get_type_symbol(node.return_type)
+ if sym.kind != .array {
+ verror('filter() requires an array')
+ }
+ info := sym.info as ast.Array
+ styp := g.typ(node.return_type)
+ elem_type_str := g.typ(info.elem_type)
+ g.empty_line = true
+ g.write('${g.typ(node.left_type)} ${tmp}_orig = ')
+ g.expr(node.left)
+ g.writeln(';')
+ g.writeln('int ${tmp}_len = ${tmp}_orig.len;')
+ noscan := g.check_noscan(info.elem_type)
+ g.writeln('$styp $tmp = __new_array${noscan}(0, ${tmp}_len, sizeof($elem_type_str));\n')
+ i := g.new_tmp_var()
+ g.writeln('for (int $i = 0; $i < ${tmp}_len; ++$i) {')
+ g.writeln('\t$elem_type_str it = (($elem_type_str*) ${tmp}_orig.data)[$i];')
+ mut is_embed_map_filter := false
+ mut expr := node.args[0].expr
+ match mut expr {
+ ast.AnonFn {
+ g.write('\tif (')
+ g.gen_anon_fn_decl(mut expr)
+ g.write('${expr.decl.name}(it)')
+ }
+ ast.Ident {
+ g.write('\tif (')
+ if expr.kind == .function {
+ g.write('${c_name(expr.name)}(it)')
+ } else if expr.kind == .variable {
+ var_info := expr.var_info()
+ sym_t := g.table.get_type_symbol(var_info.typ)
+ if sym_t.kind == .function {
+ g.write('${c_name(expr.name)}(it)')
+ } else {
+ g.expr(node.args[0].expr)
+ }
+ } else {
+ g.expr(node.args[0].expr)
+ }
+ }
+ ast.CallExpr {
+ if expr.name in ['map', 'filter'] {
+ is_embed_map_filter = true
+ g.stmt_path_pos << g.out.len
+ }
+ g.write('\tif (')
+ g.expr(node.args[0].expr)
+ }
+ else {
+ g.write('\tif (')
+ g.expr(node.args[0].expr)
+ }
+ }
+ g.writeln(') {')
+ g.writeln('\t\tarray_push${noscan}((array*)&$tmp, &it); \n\t\t}')
+ g.writeln('}')
+ if !is_embed_map_filter {
+ g.stmt_path_pos << g.out.len
+ }
+ g.write('\n')
+ g.write(s)
+ g.write(tmp)
+}
+
+// `nums.insert(0, 2)` `nums.insert(0, [2,3,4])`
+fn (mut g Gen) gen_array_insert(node ast.CallExpr) {
+ left_sym := g.table.get_type_symbol(node.left_type)
+ left_info := left_sym.info as ast.Array
+ elem_type_str := g.typ(left_info.elem_type)
+ arg2_sym := g.table.get_type_symbol(node.args[1].typ)
+ is_arg2_array := arg2_sym.kind == .array && node.args[1].typ == node.left_type
+ noscan := g.check_noscan(left_info.elem_type)
+ if is_arg2_array {
+ g.write('array_insert_many${noscan}(&')
+ } else {
+ g.write('array_insert${noscan}(&')
+ }
+ g.expr(node.left)
+ g.write(', ')
+ g.expr(node.args[0].expr)
+ if is_arg2_array {
+ g.write(', ')
+ g.expr(node.args[1].expr)
+ g.write('.data, ')
+ g.expr(node.args[1].expr)
+ g.write('.len)')
+ } else {
+ g.write(', &($elem_type_str[]){')
+ if left_info.elem_type == ast.string_type {
+ g.write('string_clone(')
+ }
+ g.expr_with_cast(node.args[1].expr, node.args[1].typ, left_info.elem_type)
+ if left_info.elem_type == ast.string_type {
+ g.write(')')
+ }
+ g.write('})')
+ }
+}
+
+// `nums.prepend(2)` `nums.prepend([2,3,4])`
+fn (mut g Gen) gen_array_prepend(node ast.CallExpr) {
+ left_sym := g.table.get_type_symbol(node.left_type)
+ left_info := left_sym.info as ast.Array
+ elem_type_str := g.typ(left_info.elem_type)
+ arg_sym := g.table.get_type_symbol(node.args[0].typ)
+ is_arg_array := arg_sym.kind == .array && node.args[0].typ == node.left_type
+ noscan := g.check_noscan(left_info.elem_type)
+ if is_arg_array {
+ g.write('array_prepend_many${noscan}(&')
+ } else {
+ g.write('array_prepend${noscan}(&')
+ }
+ g.expr(node.left)
+ if is_arg_array {
+ g.write(', ')
+ g.expr(node.args[0].expr)
+ g.write('.data, ')
+ g.expr(node.args[0].expr)
+ g.write('.len)')
+ } else {
+ g.write(', &($elem_type_str[]){')
+ g.expr_with_cast(node.args[0].expr, node.args[0].typ, left_info.elem_type)
+ g.write('})')
+ }
+}
+
+fn (mut g Gen) gen_array_contains_method(left_type ast.Type) string {
+ mut unwrap_left_type := g.unwrap_generic(left_type)
+ if unwrap_left_type.share() == .shared_t {
+ unwrap_left_type = unwrap_left_type.clear_flag(.shared_f)
+ }
+ mut left_sym := g.table.get_type_symbol(unwrap_left_type)
+ left_final_sym := g.table.get_final_type_symbol(unwrap_left_type)
+ mut left_type_str := g.typ(unwrap_left_type).replace('*', '')
+ fn_name := '${left_type_str}_contains'
+ if !left_sym.has_method('contains') {
+ left_info := left_final_sym.info as ast.Array
+ mut elem_type_str := g.typ(left_info.elem_type)
+ elem_sym := g.table.get_type_symbol(left_info.elem_type)
+ if elem_sym.kind == .function {
+ left_type_str = 'Array_voidptr'
+ elem_type_str = 'voidptr'
+ }
+ g.type_definitions.writeln('static bool ${fn_name}($left_type_str a, $elem_type_str v); // auto')
+ mut fn_builder := strings.new_builder(512)
+ fn_builder.writeln('static bool ${fn_name}($left_type_str a, $elem_type_str v) {')
+ fn_builder.writeln('\tfor (int i = 0; i < a.len; ++i) {')
+ if elem_sym.kind == .string {
+ fn_builder.writeln('\t\tif (fast_string_eq(((string*)a.data)[i], v)) {')
+ } else if elem_sym.kind == .array && left_info.elem_type.nr_muls() == 0 {
+ ptr_typ := g.gen_array_equality_fn(left_info.elem_type)
+ fn_builder.writeln('\t\tif (${ptr_typ}_arr_eq((($elem_type_str*)a.data)[i], v)) {')
+ } else if elem_sym.kind == .function {
+ fn_builder.writeln('\t\tif (((voidptr*)a.data)[i] == v) {')
+ } else if elem_sym.kind == .map && left_info.elem_type.nr_muls() == 0 {
+ ptr_typ := g.gen_map_equality_fn(left_info.elem_type)
+ fn_builder.writeln('\t\tif (${ptr_typ}_map_eq((($elem_type_str*)a.data)[i], v)) {')
+ } else if elem_sym.kind == .struct_ && left_info.elem_type.nr_muls() == 0 {
+ ptr_typ := g.gen_struct_equality_fn(left_info.elem_type)
+ fn_builder.writeln('\t\tif (${ptr_typ}_struct_eq((($elem_type_str*)a.data)[i], v)) {')
+ } else {
+ fn_builder.writeln('\t\tif ((($elem_type_str*)a.data)[i] == v) {')
+ }
+ fn_builder.writeln('\t\t\treturn true;')
+ fn_builder.writeln('\t\t}')
+ fn_builder.writeln('\t}')
+ fn_builder.writeln('\treturn false;')
+ fn_builder.writeln('}')
+ g.auto_fn_definitions << fn_builder.str()
+ left_sym.register_method(&ast.Fn{
+ name: 'contains'
+ params: [ast.Param{
+ typ: unwrap_left_type
+ }, ast.Param{
+ typ: left_info.elem_type
+ }]
+ })
+ }
+ return fn_name
+}
+
+// `nums.contains(2)`
+fn (mut g Gen) gen_array_contains(node ast.CallExpr) {
+ fn_name := g.gen_array_contains_method(node.left_type)
+ g.write('${fn_name}(')
+ if node.left_type.is_ptr() && node.left_type.share() != .shared_t {
+ g.write('*')
+ }
+ g.expr(node.left)
+ if node.left_type.share() == .shared_t {
+ g.write('->val')
+ }
+ g.write(', ')
+ g.expr(node.args[0].expr)
+ g.write(')')
+}
+
+fn (mut g Gen) gen_array_index_method(left_type ast.Type) string {
+ unwrap_left_type := g.unwrap_generic(left_type)
+ mut left_sym := g.table.get_type_symbol(unwrap_left_type)
+ mut left_type_str := g.typ(unwrap_left_type).trim('*')
+ fn_name := '${left_type_str}_index'
+ if !left_sym.has_method('index') {
+ info := left_sym.info as ast.Array
+ mut elem_type_str := g.typ(info.elem_type)
+ elem_sym := g.table.get_type_symbol(info.elem_type)
+ if elem_sym.kind == .function {
+ left_type_str = 'Array_voidptr'
+ elem_type_str = 'voidptr'
+ }
+ g.type_definitions.writeln('static int ${fn_name}($left_type_str a, $elem_type_str v); // auto')
+ mut fn_builder := strings.new_builder(512)
+ fn_builder.writeln('static int ${fn_name}($left_type_str a, $elem_type_str v) {')
+ fn_builder.writeln('\t$elem_type_str* pelem = a.data;')
+ fn_builder.writeln('\tfor (int i = 0; i < a.len; ++i, ++pelem) {')
+ if elem_sym.kind == .string {
+ fn_builder.writeln('\t\tif (fast_string_eq(*pelem, v)) {')
+ } else if elem_sym.kind == .array && !info.elem_type.is_ptr() {
+ ptr_typ := g.gen_array_equality_fn(info.elem_type)
+ fn_builder.writeln('\t\tif (${ptr_typ}_arr_eq( *pelem, v)) {')
+ } else if elem_sym.kind == .function && !info.elem_type.is_ptr() {
+ fn_builder.writeln('\t\tif ( pelem == v) {')
+ } else if elem_sym.kind == .map && !info.elem_type.is_ptr() {
+ ptr_typ := g.gen_map_equality_fn(info.elem_type)
+ fn_builder.writeln('\t\tif (${ptr_typ}_map_eq(( *pelem, v))) {')
+ } else if elem_sym.kind == .struct_ && !info.elem_type.is_ptr() {
+ ptr_typ := g.gen_struct_equality_fn(info.elem_type)
+ fn_builder.writeln('\t\tif (${ptr_typ}_struct_eq( *pelem, v)) {')
+ } else {
+ fn_builder.writeln('\t\tif (*pelem == v) {')
+ }
+ fn_builder.writeln('\t\t\treturn i;')
+ fn_builder.writeln('\t\t}')
+ fn_builder.writeln('\t}')
+ fn_builder.writeln('\treturn -1;')
+ fn_builder.writeln('}')
+ g.auto_fn_definitions << fn_builder.str()
+ left_sym.register_method(&ast.Fn{
+ name: 'index'
+ params: [ast.Param{
+ typ: unwrap_left_type
+ }, ast.Param{
+ typ: info.elem_type
+ }]
+ })
+ }
+ return fn_name
+}
+
+// `nums.index(2)`
+fn (mut g Gen) gen_array_index(node ast.CallExpr) {
+ fn_name := g.gen_array_index_method(node.left_type)
+ g.write('${fn_name}(')
+ if node.left_type.is_ptr() {
+ g.write('*')
+ }
+ g.expr(node.left)
+ g.write(', ')
+ g.expr(node.args[0].expr)
+ g.write(')')
+}
+
+fn (mut g Gen) gen_array_wait(node ast.CallExpr) {
+ arr := g.table.get_type_symbol(node.receiver_type)
+ thread_type := arr.array_info().elem_type
+ thread_sym := g.table.get_type_symbol(thread_type)
+ thread_ret_type := thread_sym.thread_info().return_type
+ eltyp := g.table.get_type_symbol(thread_ret_type).cname
+ fn_name := g.register_thread_array_wait_call(eltyp)
+ g.write('${fn_name}(')
+ g.expr(node.left)
+ g.write(')')
+}
+
+fn (mut g Gen) gen_array_any(node ast.CallExpr) {
+ tmp := g.new_tmp_var()
+ s := g.go_before_stmt(0)
+ sym := g.table.get_type_symbol(node.left_type)
+ info := sym.info as ast.Array
+ // styp := g.typ(node.return_type)
+ elem_type_str := g.typ(info.elem_type)
+ g.empty_line = true
+ g.write('${g.typ(node.left_type)} ${tmp}_orig = ')
+ g.expr(node.left)
+ g.writeln(';')
+ g.writeln('int ${tmp}_len = ${tmp}_orig.len;')
+ g.writeln('bool $tmp = false;')
+ i := g.new_tmp_var()
+ g.writeln('for (int $i = 0; $i < ${tmp}_len; ++$i) {')
+ g.writeln('\t$elem_type_str it = (($elem_type_str*) ${tmp}_orig.data)[$i];')
+ mut is_embed_map_filter := false
+ mut expr := node.args[0].expr
+ match mut expr {
+ ast.AnonFn {
+ g.write('\tif (')
+ g.gen_anon_fn_decl(mut expr)
+ g.write('${expr.decl.name}(it)')
+ }
+ ast.Ident {
+ g.write('\tif (')
+ if expr.kind == .function {
+ g.write('${c_name(expr.name)}(it)')
+ } else if expr.kind == .variable {
+ var_info := expr.var_info()
+ sym_t := g.table.get_type_symbol(var_info.typ)
+ if sym_t.kind == .function {
+ g.write('${c_name(expr.name)}(it)')
+ } else {
+ g.expr(node.args[0].expr)
+ }
+ } else {
+ g.expr(node.args[0].expr)
+ }
+ }
+ ast.CallExpr {
+ if expr.name in ['map', 'filter'] {
+ is_embed_map_filter = true
+ g.stmt_path_pos << g.out.len
+ }
+ g.write('\tif (')
+ g.expr(node.args[0].expr)
+ }
+ else {
+ g.write('\tif (')
+ g.expr(node.args[0].expr)
+ }
+ }
+ g.writeln(') {')
+ g.writeln('\t\t$tmp = true;\n\t\t\tbreak;\n\t\t}')
+ g.writeln('}')
+ if !is_embed_map_filter {
+ g.stmt_path_pos << g.out.len
+ }
+ g.write('\n')
+ g.write(s)
+ g.write(tmp)
+}
+
+fn (mut g Gen) gen_array_all(node ast.CallExpr) {
+ tmp := g.new_tmp_var()
+ s := g.go_before_stmt(0)
+ sym := g.table.get_type_symbol(node.left_type)
+ info := sym.info as ast.Array
+ // styp := g.typ(node.return_type)
+ elem_type_str := g.typ(info.elem_type)
+ g.empty_line = true
+ g.write('${g.typ(node.left_type)} ${tmp}_orig = ')
+ g.expr(node.left)
+ g.writeln(';')
+ g.writeln('int ${tmp}_len = ${tmp}_orig.len;')
+ g.writeln('bool $tmp = true;')
+ i := g.new_tmp_var()
+ g.writeln('for (int $i = 0; $i < ${tmp}_len; ++$i) {')
+ g.writeln('\t$elem_type_str it = (($elem_type_str*) ${tmp}_orig.data)[$i];')
+ mut is_embed_map_filter := false
+ mut expr := node.args[0].expr
+ match mut expr {
+ ast.AnonFn {
+ g.write('\tif (!(')
+ g.gen_anon_fn_decl(mut expr)
+ g.write('${expr.decl.name}(it)')
+ }
+ ast.Ident {
+ g.write('\tif (!(')
+ if expr.kind == .function {
+ g.write('${c_name(expr.name)}(it)')
+ } else if expr.kind == .variable {
+ var_info := expr.var_info()
+ sym_t := g.table.get_type_symbol(var_info.typ)
+ if sym_t.kind == .function {
+ g.write('${c_name(expr.name)}(it)')
+ } else {
+ g.expr(node.args[0].expr)
+ }
+ } else {
+ g.expr(node.args[0].expr)
+ }
+ }
+ ast.CallExpr {
+ if expr.name in ['map', 'filter'] {
+ is_embed_map_filter = true
+ g.stmt_path_pos << g.out.len
+ }
+ g.write('\tif (!(')
+ g.expr(node.args[0].expr)
+ }
+ else {
+ g.write('\tif (!(')
+ g.expr(node.args[0].expr)
+ }
+ }
+ g.writeln(')) {')
+ g.writeln('\t\t$tmp = false;\n\t\t\tbreak;\n\t\t}')
+ g.writeln('}')
+ if !is_embed_map_filter {
+ g.stmt_path_pos << g.out.len
+ }
+ g.write('\n')
+ g.write(s)
+ g.write(tmp)
+}
diff --git a/v_windows/v/vlib/v/gen/c/assert.v b/v_windows/v/vlib/v/gen/c/assert.v
new file mode 100644
index 0000000..46b48f7
--- /dev/null
+++ b/v_windows/v/vlib/v/gen/c/assert.v
@@ -0,0 +1,159 @@
+// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved.
+// Use of this source code is governed by an MIT license
+// that can be found in the LICENSE file.
+module c
+
+import v.ast
+
+fn (mut g Gen) gen_assert_stmt(original_assert_statement ast.AssertStmt) {
+ if !original_assert_statement.is_used {
+ return
+ }
+ mut node := original_assert_statement
+ g.writeln('// assert')
+ if mut node.expr is ast.InfixExpr {
+ if mut node.expr.left is ast.CallExpr {
+ node.expr.left = g.new_ctemp_var_then_gen(node.expr.left, node.expr.left_type)
+ } else if mut node.expr.left is ast.ParExpr {
+ if node.expr.left.expr is ast.CallExpr {
+ node.expr.left = g.new_ctemp_var_then_gen(node.expr.left.expr, node.expr.left_type)
+ }
+ }
+ if mut node.expr.right is ast.CallExpr {
+ node.expr.right = g.new_ctemp_var_then_gen(node.expr.right, node.expr.right_type)
+ } else if mut node.expr.right is ast.ParExpr {
+ if node.expr.right.expr is ast.CallExpr {
+ node.expr.right = g.new_ctemp_var_then_gen(node.expr.right.expr, node.expr.right_type)
+ }
+ }
+ }
+ g.inside_ternary++
+ if g.is_test {
+ g.write('if (')
+ g.expr(node.expr)
+ g.write(')')
+ g.decrement_inside_ternary()
+ g.writeln(' {')
+ g.writeln('\tg_test_oks++;')
+ metaname_ok := g.gen_assert_metainfo(node)
+ g.writeln('\tmain__cb_assertion_ok(&$metaname_ok);')
+ g.writeln('} else {')
+ g.writeln('\tg_test_fails++;')
+ metaname_fail := g.gen_assert_metainfo(node)
+ g.writeln('\tmain__cb_assertion_failed(&$metaname_fail);')
+ g.gen_assert_postfailure_mode(node)
+ g.writeln('\tlongjmp(g_jump_buffer, 1);')
+ g.writeln('\t// TODO')
+ g.writeln('\t// Maybe print all vars in a test function if it fails?')
+ g.writeln('}')
+ } else {
+ g.write('if (!(')
+ g.expr(node.expr)
+ g.write('))')
+ g.decrement_inside_ternary()
+ g.writeln(' {')
+ metaname_panic := g.gen_assert_metainfo(node)
+ g.writeln('\t__print_assert_failure(&$metaname_panic);')
+ g.gen_assert_postfailure_mode(node)
+ g.writeln('\t_v_panic(_SLIT("Assertion failed..."));')
+ g.writeln('}')
+ }
+}
+
+fn (mut g Gen) gen_assert_postfailure_mode(node ast.AssertStmt) {
+ g.write_v_source_line_info(node.pos)
+ match g.pref.assert_failure_mode {
+ .default {}
+ .aborts {
+ g.writeln('\tabort();')
+ }
+ .backtraces {
+ g.writeln('\tprint_backtrace();')
+ }
+ }
+}
+
+fn (mut g Gen) gen_assert_metainfo(node ast.AssertStmt) string {
+ mod_path := cestring(g.file.path)
+ fn_name := g.fn_decl.name
+ line_nr := node.pos.line_nr
+ src := cestring(node.expr.str())
+ metaname := 'v_assert_meta_info_$g.new_tmp_var()'
+ g.writeln('\tVAssertMetaInfo $metaname = {0};')
+ g.writeln('\t${metaname}.fpath = ${ctoslit(mod_path)};')
+ g.writeln('\t${metaname}.line_nr = $line_nr;')
+ g.writeln('\t${metaname}.fn_name = ${ctoslit(fn_name)};')
+ metasrc := cnewlines(ctoslit(src))
+ g.writeln('\t${metaname}.src = $metasrc;')
+ match mut node.expr {
+ ast.InfixExpr {
+ expr_op_str := ctoslit(node.expr.op.str())
+ expr_left_str := cnewlines(ctoslit(node.expr.left.str()))
+ expr_right_str := cnewlines(ctoslit(node.expr.right.str()))
+ g.writeln('\t${metaname}.op = $expr_op_str;')
+ g.writeln('\t${metaname}.llabel = $expr_left_str;')
+ g.writeln('\t${metaname}.rlabel = $expr_right_str;')
+ g.write('\t${metaname}.lvalue = ')
+ g.gen_assert_single_expr(node.expr.left, node.expr.left_type)
+ g.writeln(';')
+ g.write('\t${metaname}.rvalue = ')
+ g.gen_assert_single_expr(node.expr.right, node.expr.right_type)
+ g.writeln(';')
+ }
+ ast.CallExpr {
+ g.writeln('\t${metaname}.op = _SLIT("call");')
+ }
+ else {}
+ }
+ return metaname
+}
+
+fn (mut g Gen) gen_assert_single_expr(expr ast.Expr, typ ast.Type) {
+ // eprintln('> gen_assert_single_expr typ: $typ | expr: $expr | typeof(expr): ${typeof(expr)}')
+ unknown_value := '*unknown value*'
+ match expr {
+ ast.CastExpr, ast.IfExpr, ast.IndexExpr, ast.MatchExpr {
+ g.write(ctoslit(unknown_value))
+ }
+ ast.PrefixExpr {
+ if expr.right is ast.CastExpr {
+ // TODO: remove this check;
+ // vlib/builtin/map_test.v (a map of &int, set to &int(0)) fails
+ // without special casing ast.CastExpr here
+ g.write(ctoslit(unknown_value))
+ } else {
+ g.gen_expr_to_string(expr, typ)
+ }
+ }
+ ast.TypeNode {
+ sym := g.table.get_type_symbol(g.unwrap_generic(typ))
+ g.write(ctoslit('$sym.name'))
+ }
+ else {
+ mut should_clone := true
+ if typ == ast.string_type && expr is ast.StringLiteral {
+ should_clone = false
+ }
+ if expr is ast.CTempVar {
+ if expr.orig is ast.CallExpr {
+ should_clone = false
+ if expr.orig.or_block.kind == .propagate {
+ should_clone = true
+ }
+ if expr.orig.is_method && expr.orig.args.len == 0
+ && expr.orig.name == 'type_name' {
+ should_clone = true
+ }
+ }
+ }
+ if should_clone {
+ g.write('string_clone(')
+ }
+ g.gen_expr_to_string(expr, typ)
+ if should_clone {
+ g.write(')')
+ }
+ }
+ }
+ g.write(' /* typeof: ' + expr.type_name() + ' type: ' + typ.str() + ' */ ')
+}
diff --git a/v_windows/v/vlib/v/gen/c/auto_eq_methods.v b/v_windows/v/vlib/v/gen/c/auto_eq_methods.v
new file mode 100644
index 0000000..16dd339
--- /dev/null
+++ b/v_windows/v/vlib/v/gen/c/auto_eq_methods.v
@@ -0,0 +1,343 @@
+// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved.
+// Use of this source code is governed by an MIT license that can be found in the LICENSE file.
+module c
+
+import strings
+import v.ast
+
+fn (mut g Gen) gen_sumtype_equality_fn(left_type ast.Type) string {
+ left := g.unwrap(left_type)
+ ptr_styp := g.typ(left.typ.set_nr_muls(0))
+ if ptr_styp in g.sumtype_fn_definitions {
+ return ptr_styp
+ }
+ g.sumtype_fn_definitions << ptr_styp
+ info := left.sym.sumtype_info()
+ g.type_definitions.writeln('static bool ${ptr_styp}_sumtype_eq($ptr_styp a, $ptr_styp b); // auto')
+
+ mut fn_builder := strings.new_builder(512)
+ fn_builder.writeln('static bool ${ptr_styp}_sumtype_eq($ptr_styp a, $ptr_styp b) {')
+ fn_builder.writeln('\tif (a._typ != b._typ) { return false; }')
+ for typ in info.variants {
+ variant := g.unwrap(typ)
+ fn_builder.writeln('\tif (a._typ == $variant.typ) {')
+ name := '_$variant.sym.cname'
+ if variant.sym.kind == .string {
+ fn_builder.writeln('\t\treturn string__eq(*a.$name, *b.$name);')
+ } else if variant.sym.kind == .sum_type && !typ.is_ptr() {
+ eq_fn := g.gen_sumtype_equality_fn(typ)
+ fn_builder.writeln('\t\treturn ${eq_fn}_sumtype_eq(*a.$name, *b.$name);')
+ } else if variant.sym.kind == .struct_ && !typ.is_ptr() {
+ eq_fn := g.gen_struct_equality_fn(typ)
+ fn_builder.writeln('\t\treturn ${eq_fn}_struct_eq(*a.$name, *b.$name);')
+ } else if variant.sym.kind == .array && !typ.is_ptr() {
+ eq_fn := g.gen_array_equality_fn(typ)
+ fn_builder.writeln('\t\treturn ${eq_fn}_arr_eq(*a.$name, *b.$name);')
+ } else if variant.sym.kind == .array_fixed && !typ.is_ptr() {
+ eq_fn := g.gen_fixed_array_equality_fn(typ)
+ fn_builder.writeln('\t\treturn ${eq_fn}_arr_eq(*a.$name, *b.$name);')
+ } else if variant.sym.kind == .map && !typ.is_ptr() {
+ eq_fn := g.gen_map_equality_fn(typ)
+ fn_builder.writeln('\t\treturn ${eq_fn}_map_eq(*a.$name, *b.$name);')
+ } else if variant.sym.kind == .alias && !typ.is_ptr() {
+ eq_fn := g.gen_alias_equality_fn(typ)
+ fn_builder.writeln('\t\treturn ${eq_fn}_alias_eq(*a.$name, *b.$name);')
+ } else if variant.sym.kind == .function {
+ fn_builder.writeln('\t\treturn *((voidptr*)(*a.$name)) == *((voidptr*)(*b.$name));')
+ } else {
+ fn_builder.writeln('\t\treturn *a.$name == *b.$name;')
+ }
+ fn_builder.writeln('\t}')
+ }
+ fn_builder.writeln('\treturn false;')
+ fn_builder.writeln('}')
+ g.auto_fn_definitions << fn_builder.str()
+ return ptr_styp
+}
+
+fn (mut g Gen) gen_struct_equality_fn(left_type ast.Type) string {
+ left := g.unwrap(left_type)
+ ptr_styp := g.typ(left.typ.set_nr_muls(0))
+ if ptr_styp in g.struct_fn_definitions {
+ return ptr_styp
+ }
+ g.struct_fn_definitions << ptr_styp
+ info := left.sym.struct_info()
+ g.type_definitions.writeln('static bool ${ptr_styp}_struct_eq($ptr_styp a, $ptr_styp b); // auto')
+
+ mut fn_builder := strings.new_builder(512)
+ defer {
+ g.auto_fn_definitions << fn_builder.str()
+ }
+ fn_builder.writeln('static bool ${ptr_styp}_struct_eq($ptr_styp a, $ptr_styp b) {')
+
+ // overloaded
+ if left.sym.has_method('==') {
+ fn_builder.writeln('\treturn ${ptr_styp}__eq(a, b);')
+ fn_builder.writeln('}')
+ return ptr_styp
+ }
+
+ fn_builder.write_string('\treturn ')
+ if info.fields.len > 0 {
+ for i, field in info.fields {
+ if i > 0 {
+ fn_builder.write_string('\n\t\t&& ')
+ }
+ field_type := g.unwrap(field.typ)
+ if field_type.sym.kind == .string {
+ fn_builder.write_string('string__eq(a.$field.name, b.$field.name)')
+ } else if field_type.sym.kind == .sum_type && !field.typ.is_ptr() {
+ eq_fn := g.gen_sumtype_equality_fn(field.typ)
+ fn_builder.write_string('${eq_fn}_sumtype_eq(a.$field.name, b.$field.name)')
+ } else if field_type.sym.kind == .struct_ && !field.typ.is_ptr() {
+ eq_fn := g.gen_struct_equality_fn(field.typ)
+ fn_builder.write_string('${eq_fn}_struct_eq(a.$field.name, b.$field.name)')
+ } else if field_type.sym.kind == .array && !field.typ.is_ptr() {
+ eq_fn := g.gen_array_equality_fn(field.typ)
+ fn_builder.write_string('${eq_fn}_arr_eq(a.$field.name, b.$field.name)')
+ } else if field_type.sym.kind == .array_fixed && !field.typ.is_ptr() {
+ eq_fn := g.gen_fixed_array_equality_fn(field.typ)
+ fn_builder.write_string('${eq_fn}_arr_eq(a.$field.name, b.$field.name)')
+ } else if field_type.sym.kind == .map && !field.typ.is_ptr() {
+ eq_fn := g.gen_map_equality_fn(field.typ)
+ fn_builder.write_string('${eq_fn}_map_eq(a.$field.name, b.$field.name)')
+ } else if field_type.sym.kind == .alias && !field.typ.is_ptr() {
+ eq_fn := g.gen_alias_equality_fn(field.typ)
+ fn_builder.write_string('${eq_fn}_alias_eq(a.$field.name, b.$field.name)')
+ } else if field_type.sym.kind == .function {
+ fn_builder.write_string('*((voidptr*)(a.$field.name)) == *((voidptr*)(b.$field.name))')
+ } else {
+ fn_builder.write_string('a.$field.name == b.$field.name')
+ }
+ }
+ } else {
+ fn_builder.write_string('true')
+ }
+ fn_builder.writeln(';')
+ fn_builder.writeln('}')
+ return ptr_styp
+}
+
+fn (mut g Gen) gen_alias_equality_fn(left_type ast.Type) string {
+ left := g.unwrap(left_type)
+ ptr_styp := g.typ(left.typ.set_nr_muls(0))
+ if ptr_styp in g.alias_fn_definitions {
+ return ptr_styp
+ }
+ g.alias_fn_definitions << ptr_styp
+ info := left.sym.info as ast.Alias
+ g.type_definitions.writeln('static bool ${ptr_styp}_alias_eq($ptr_styp a, $ptr_styp b); // auto')
+
+ mut fn_builder := strings.new_builder(512)
+ fn_builder.writeln('static bool ${ptr_styp}_alias_eq($ptr_styp a, $ptr_styp b) {')
+ sym := g.table.get_type_symbol(info.parent_type)
+ if sym.kind == .string {
+ fn_builder.writeln('\treturn string__eq(a, b);')
+ } else if sym.kind == .sum_type && !left.typ.is_ptr() {
+ eq_fn := g.gen_sumtype_equality_fn(info.parent_type)
+ fn_builder.writeln('\treturn ${eq_fn}_sumtype_eq(a, b);')
+ } else if sym.kind == .struct_ && !left.typ.is_ptr() {
+ eq_fn := g.gen_struct_equality_fn(info.parent_type)
+ fn_builder.writeln('\treturn ${eq_fn}_struct_eq(a, b);')
+ } else if sym.kind == .array && !left.typ.is_ptr() {
+ eq_fn := g.gen_array_equality_fn(info.parent_type)
+ fn_builder.writeln('\treturn ${eq_fn}_arr_eq(a, b);')
+ } else if sym.kind == .array_fixed && !left.typ.is_ptr() {
+ eq_fn := g.gen_fixed_array_equality_fn(info.parent_type)
+ fn_builder.writeln('\treturn ${eq_fn}_arr_eq(a, b);')
+ } else if sym.kind == .map && !left.typ.is_ptr() {
+ eq_fn := g.gen_map_equality_fn(info.parent_type)
+ fn_builder.writeln('\treturn ${eq_fn}_map_eq(a, b);')
+ } else if sym.kind == .function {
+ fn_builder.writeln('\treturn *((voidptr*)(a)) == *((voidptr*)(b));')
+ } else {
+ fn_builder.writeln('\treturn a == b;')
+ }
+ fn_builder.writeln('}')
+ g.auto_fn_definitions << fn_builder.str()
+ return ptr_styp
+}
+
+fn (mut g Gen) gen_array_equality_fn(left_type ast.Type) string {
+ left := g.unwrap(left_type)
+ ptr_styp := g.typ(left.typ.set_nr_muls(0))
+ if ptr_styp in g.array_fn_definitions {
+ return ptr_styp
+ }
+ g.array_fn_definitions << ptr_styp
+ elem := g.unwrap(left.sym.array_info().elem_type)
+ ptr_elem_styp := g.typ(elem.typ)
+ g.type_definitions.writeln('static bool ${ptr_styp}_arr_eq($ptr_styp a, $ptr_styp b); // auto')
+
+ mut fn_builder := strings.new_builder(512)
+ fn_builder.writeln('static bool ${ptr_styp}_arr_eq($ptr_styp a, $ptr_styp b) {')
+ fn_builder.writeln('\tif (a.len != b.len) {')
+ fn_builder.writeln('\t\treturn false;')
+ fn_builder.writeln('\t}')
+ fn_builder.writeln('\tfor (int i = 0; i < a.len; ++i) {')
+ // compare every pair of elements of the two arrays
+ if elem.sym.kind == .string {
+ fn_builder.writeln('\t\tif (!string__eq(*(($ptr_elem_styp*)((byte*)a.data+(i*a.element_size))), *(($ptr_elem_styp*)((byte*)b.data+(i*b.element_size))))) {')
+ } else if elem.sym.kind == .sum_type && !elem.typ.is_ptr() {
+ eq_fn := g.gen_sumtype_equality_fn(elem.typ)
+ fn_builder.writeln('\t\tif (!${eq_fn}_sumtype_eq((($ptr_elem_styp*)a.data)[i], (($ptr_elem_styp*)b.data)[i])) {')
+ } else if elem.sym.kind == .struct_ && !elem.typ.is_ptr() {
+ eq_fn := g.gen_struct_equality_fn(elem.typ)
+ fn_builder.writeln('\t\tif (!${eq_fn}_struct_eq((($ptr_elem_styp*)a.data)[i], (($ptr_elem_styp*)b.data)[i])) {')
+ } else if elem.sym.kind == .array && !elem.typ.is_ptr() {
+ eq_fn := g.gen_array_equality_fn(elem.typ)
+ fn_builder.writeln('\t\tif (!${eq_fn}_arr_eq((($ptr_elem_styp*)a.data)[i], (($ptr_elem_styp*)b.data)[i])) {')
+ } else if elem.sym.kind == .array_fixed && !elem.typ.is_ptr() {
+ eq_fn := g.gen_fixed_array_equality_fn(elem.typ)
+ fn_builder.writeln('\t\tif (!${eq_fn}_arr_eq((($ptr_elem_styp*)a.data)[i], (($ptr_elem_styp*)b.data)[i])) {')
+ } else if elem.sym.kind == .map && !elem.typ.is_ptr() {
+ eq_fn := g.gen_map_equality_fn(elem.typ)
+ fn_builder.writeln('\t\tif (!${eq_fn}_map_eq((($ptr_elem_styp*)a.data)[i], (($ptr_elem_styp*)b.data)[i])) {')
+ } else if elem.sym.kind == .alias && !elem.typ.is_ptr() {
+ eq_fn := g.gen_alias_equality_fn(elem.typ)
+ fn_builder.writeln('\t\tif (!${eq_fn}_alias_eq((($ptr_elem_styp*)a.data)[i], (($ptr_elem_styp*)b.data)[i])) {')
+ } else if elem.sym.kind == .function {
+ fn_builder.writeln('\t\tif (*((voidptr*)((byte*)a.data+(i*a.element_size))) != *((voidptr*)((byte*)b.data+(i*b.element_size)))) {')
+ } else {
+ fn_builder.writeln('\t\tif (*(($ptr_elem_styp*)((byte*)a.data+(i*a.element_size))) != *(($ptr_elem_styp*)((byte*)b.data+(i*b.element_size)))) {')
+ }
+ fn_builder.writeln('\t\t\treturn false;')
+ fn_builder.writeln('\t\t}')
+ fn_builder.writeln('\t}')
+ fn_builder.writeln('\treturn true;')
+ fn_builder.writeln('}')
+ g.auto_fn_definitions << fn_builder.str()
+ return ptr_styp
+}
+
+fn (mut g Gen) gen_fixed_array_equality_fn(left_type ast.Type) string {
+ left := g.unwrap(left_type)
+ ptr_styp := g.typ(left.typ.set_nr_muls(0))
+ if ptr_styp in g.array_fn_definitions {
+ return ptr_styp
+ }
+ g.array_fn_definitions << ptr_styp
+ elem_info := left.sym.array_fixed_info()
+ elem := g.unwrap(elem_info.elem_type)
+ size := elem_info.size
+ g.type_definitions.writeln('static bool ${ptr_styp}_arr_eq($ptr_styp a, $ptr_styp b); // auto')
+
+ mut fn_builder := strings.new_builder(512)
+ fn_builder.writeln('static bool ${ptr_styp}_arr_eq($ptr_styp a, $ptr_styp b) {')
+ fn_builder.writeln('\tfor (int i = 0; i < $size; ++i) {')
+ // compare every pair of elements of the two fixed arrays
+ if elem.sym.kind == .string {
+ fn_builder.writeln('\t\tif (!string__eq(a[i], b[i])) {')
+ } else if elem.sym.kind == .sum_type && !elem.typ.is_ptr() {
+ eq_fn := g.gen_sumtype_equality_fn(elem.typ)
+ fn_builder.writeln('\t\tif (!${eq_fn}_sumtype_eq(a[i], b[i])) {')
+ } else if elem.sym.kind == .struct_ && !elem.typ.is_ptr() {
+ eq_fn := g.gen_struct_equality_fn(elem.typ)
+ fn_builder.writeln('\t\tif (!${eq_fn}_struct_eq(a[i], b[i])) {')
+ } else if elem.sym.kind == .array && !elem.typ.is_ptr() {
+ eq_fn := g.gen_array_equality_fn(elem.typ)
+ fn_builder.writeln('\t\tif (!${eq_fn}_arr_eq(a[i], b[i])) {')
+ } else if elem.sym.kind == .array_fixed && !elem.typ.is_ptr() {
+ eq_fn := g.gen_fixed_array_equality_fn(elem.typ)
+ fn_builder.writeln('\t\tif (!${eq_fn}_arr_eq(a[i], b[i])) {')
+ } else if elem.sym.kind == .map && !elem.typ.is_ptr() {
+ eq_fn := g.gen_map_equality_fn(elem.typ)
+ fn_builder.writeln('\t\tif (!${eq_fn}_map_eq(a[i], b[i])) {')
+ } else if elem.sym.kind == .alias && !elem.typ.is_ptr() {
+ eq_fn := g.gen_alias_equality_fn(elem.typ)
+ fn_builder.writeln('\t\tif (!${eq_fn}_alias_eq(a[i], b[i])) {')
+ } else if elem.sym.kind == .function {
+ fn_builder.writeln('\t\tif (a[i] != b[i]) {')
+ } else {
+ fn_builder.writeln('\t\tif (a[i] != b[i]) {')
+ }
+ fn_builder.writeln('\t\t\treturn false;')
+ fn_builder.writeln('\t\t}')
+ fn_builder.writeln('\t}')
+ fn_builder.writeln('\treturn true;')
+ fn_builder.writeln('}')
+ g.auto_fn_definitions << fn_builder.str()
+ return ptr_styp
+}
+
+fn (mut g Gen) gen_map_equality_fn(left_type ast.Type) string {
+ left := g.unwrap(left_type)
+ ptr_styp := g.typ(left.typ.set_nr_muls(0))
+ if ptr_styp in g.map_fn_definitions {
+ return ptr_styp
+ }
+ g.map_fn_definitions << ptr_styp
+ value := g.unwrap(left.sym.map_info().value_type)
+ ptr_value_styp := g.typ(value.typ)
+ g.type_definitions.writeln('static bool ${ptr_styp}_map_eq($ptr_styp a, $ptr_styp b); // auto')
+
+ mut fn_builder := strings.new_builder(512)
+ fn_builder.writeln('static bool ${ptr_styp}_map_eq($ptr_styp a, $ptr_styp b) {')
+ fn_builder.writeln('\tif (a.len != b.len) {')
+ fn_builder.writeln('\t\treturn false;')
+ fn_builder.writeln('\t}')
+ fn_builder.writeln('\tfor (int i = 0; i < a.key_values.len; ++i) {')
+ fn_builder.writeln('\t\tif (!DenseArray_has_index(&a.key_values, i)) continue;')
+ fn_builder.writeln('\t\tvoidptr k = DenseArray_key(&a.key_values, i);')
+ fn_builder.writeln('\t\tif (!map_exists(&b, k)) return false;')
+ kind := g.table.type_kind(value.typ)
+ if kind == .function {
+ func := value.sym.info as ast.FnType
+ ret_styp := g.typ(func.func.return_type)
+ fn_builder.write_string('\t\t$ret_styp (*v) (')
+ arg_len := func.func.params.len
+ for j, arg in func.func.params {
+ arg_styp := g.typ(arg.typ)
+ fn_builder.write_string('$arg_styp $arg.name')
+ if j < arg_len - 1 {
+ fn_builder.write_string(', ')
+ }
+ }
+ fn_builder.writeln(') = *(voidptr*)map_get(&a, k, &(voidptr[]){ 0 });')
+ } else {
+ fn_builder.writeln('\t\t$ptr_value_styp v = *($ptr_value_styp*)map_get(&a, k, &($ptr_value_styp[]){ 0 });')
+ }
+ match kind {
+ .string {
+ fn_builder.writeln('\t\tif (!fast_string_eq(*(string*)map_get(&b, k, &(string[]){_SLIT("")}), v)) {')
+ }
+ .sum_type {
+ eq_fn := g.gen_sumtype_equality_fn(value.typ)
+ fn_builder.writeln('\t\tif (!${eq_fn}_sumtype_eq(*($ptr_value_styp*)map_get(&b, k, &($ptr_value_styp[]){ 0 }), v)) {')
+ }
+ .struct_ {
+ eq_fn := g.gen_struct_equality_fn(value.typ)
+ fn_builder.writeln('\t\tif (!${eq_fn}_struct_eq(*($ptr_value_styp*)map_get(&b, k, &($ptr_value_styp[]){ 0 }), v)) {')
+ }
+ .array {
+ eq_fn := g.gen_array_equality_fn(value.typ)
+ fn_builder.writeln('\t\tif (!${eq_fn}_arr_eq(*($ptr_value_styp*)map_get(&b, k, &($ptr_value_styp[]){ 0 }), v)) {')
+ }
+ .array_fixed {
+ eq_fn := g.gen_fixed_array_equality_fn(value.typ)
+ fn_builder.writeln('\t\tif (!${eq_fn}_arr_eq(*($ptr_value_styp*)map_get(&b, k, &($ptr_value_styp[]){ 0 }), v)) {')
+ }
+ .map {
+ eq_fn := g.gen_map_equality_fn(value.typ)
+ fn_builder.writeln('\t\tif (!${eq_fn}_map_eq(*($ptr_value_styp*)map_get(&b, k, &($ptr_value_styp[]){ 0 }), v)) {')
+ }
+ .alias {
+ eq_fn := g.gen_alias_equality_fn(value.typ)
+ fn_builder.writeln('\t\tif (!${eq_fn}_alias_eq(*($ptr_value_styp*)map_get(&b, k, &($ptr_value_styp[]){ 0 }), v)) {')
+ }
+ .function {
+ fn_builder.writeln('\t\tif (*(voidptr*)map_get(&b, k, &(voidptr[]){ 0 }) != v) {')
+ }
+ else {
+ fn_builder.writeln('\t\tif (*($ptr_value_styp*)map_get(&b, k, &($ptr_value_styp[]){ 0 }) != v) {')
+ }
+ }
+ fn_builder.writeln('\t\t\treturn false;')
+ fn_builder.writeln('\t\t}')
+ fn_builder.writeln('\t}')
+ fn_builder.writeln('\treturn true;')
+ fn_builder.writeln('}')
+ g.auto_fn_definitions << fn_builder.str()
+ return ptr_styp
+}
diff --git a/v_windows/v/vlib/v/gen/c/auto_str_methods.v b/v_windows/v/vlib/v/gen/c/auto_str_methods.v
new file mode 100644
index 0000000..ffec50c
--- /dev/null
+++ b/v_windows/v/vlib/v/gen/c/auto_str_methods.v
@@ -0,0 +1,900 @@
+// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved.
+// Use of this source code is governed by an MIT license that can be found in the LICENSE file.
+module c
+
+import v.ast
+import v.util
+import strings
+
+pub enum StrIntpType {
+ si_no_str = 0 // no parameter to print only fix string
+ si_c
+ si_u8
+ si_i8
+ si_u16
+ si_i16
+ si_u32
+ si_i32
+ si_u64
+ si_i64
+ si_e32
+ si_e64
+ si_f32
+ si_f64
+ si_g32
+ si_g64
+ si_s
+ si_p
+ si_vp
+}
+
+pub fn type_to_str(x StrIntpType) string {
+ match x {
+ .si_no_str { return 'no_str' }
+ .si_c { return 'c' }
+ .si_u8 { return 'u8' }
+ .si_i8 { return 'i8' }
+ .si_u16 { return 'u16' }
+ .si_i16 { return 'i16' }
+ .si_u32 { return 'u32' }
+ .si_i32 { return 'i32' }
+ .si_u64 { return 'u64' }
+ .si_i64 { return 'i64' }
+ .si_f32 { return 'f32' }
+ .si_f64 { return 'f64' }
+ .si_g32 { return 'f32' } // g32 format use f32 data
+ .si_g64 { return 'f64' } // g64 format use f64 data
+ .si_e32 { return 'f32' } // e32 format use f32 data
+ .si_e64 { return 'f64' } // e64 format use f64 data
+ .si_s { return 's' }
+ .si_p { return 'p' }
+ .si_vp { return 'vp' }
+ }
+}
+
+pub fn data_str(x StrIntpType) string {
+ match x {
+ .si_no_str { return 'no_str' }
+ .si_c { return 'd_c' }
+ .si_u8 { return 'd_u8' }
+ .si_i8 { return 'd_i8' }
+ .si_u16 { return 'd_u16' }
+ .si_i16 { return 'd_i16' }
+ .si_u32 { return 'd_u32' }
+ .si_i32 { return 'd_i32' }
+ .si_u64 { return 'd_u64' }
+ .si_i64 { return 'd_i64' }
+ .si_f32 { return 'd_f32' }
+ .si_f64 { return 'd_f64' }
+ .si_g32 { return 'd_f32' } // g32 format use f32 data
+ .si_g64 { return 'd_f64' } // g64 format use f64 data
+ .si_e32 { return 'd_f32' } // e32 format use f32 data
+ .si_e64 { return 'd_f64' } // e64 format use f64 data
+ .si_s { return 'd_s' }
+ .si_p { return 'd_p' }
+ .si_vp { return 'd_vp' }
+ }
+}
+
+const (
+ // BUG: this const is not released from the memory! use a const for now
+ // si_s_code = "0x" + int(StrIntpType.si_s).hex() // code for a simple string
+ si_s_code = '0xfe10'
+)
+
+fn should_use_indent_func(kind ast.Kind) bool {
+ return kind in [.struct_, .alias, .array, .array_fixed, .map, .sum_type, .interface_]
+}
+
+fn (mut g Gen) gen_str_default(sym ast.TypeSymbol, styp string, str_fn_name string) {
+ mut convertor := ''
+ mut typename_ := ''
+ if sym.parent_idx in ast.integer_type_idxs {
+ convertor = 'int'
+ typename_ = 'int'
+ } else if sym.parent_idx == ast.f32_type_idx {
+ convertor = 'float'
+ typename_ = 'f32'
+ } else if sym.parent_idx == ast.f64_type_idx {
+ convertor = 'double'
+ typename_ = 'f64'
+ } else if sym.parent_idx == ast.bool_type_idx {
+ convertor = 'bool'
+ typename_ = 'bool'
+ } else {
+ verror("could not generate string method for type '$styp'")
+ }
+ g.type_definitions.writeln('string ${str_fn_name}($styp it); // auto')
+ g.auto_str_funcs.writeln('string ${str_fn_name}($styp it) {')
+ if convertor == 'bool' {
+ g.auto_str_funcs.writeln('\tstring tmp1 = string__plus(_SLIT("${styp}("), ($convertor)it ? _SLIT("true") : _SLIT("false"));')
+ } else {
+ g.auto_str_funcs.writeln('\tstring tmp1 = string__plus(_SLIT("${styp}("), tos3(${typename_}_str(($convertor)it).str));')
+ }
+ g.auto_str_funcs.writeln('\tstring tmp2 = string__plus(tmp1, _SLIT(")"));')
+ g.auto_str_funcs.writeln('\tstring_free(&tmp1);')
+ g.auto_str_funcs.writeln('\treturn tmp2;')
+ g.auto_str_funcs.writeln('}')
+}
+
+fn (mut g Gen) gen_str_for_type(typ ast.Type) string {
+ styp := g.typ(typ).replace('*', '')
+ mut sym := g.table.get_type_symbol(g.unwrap_generic(typ))
+ mut str_fn_name := styp_to_str_fn_name(styp)
+ if mut sym.info is ast.Alias {
+ if sym.info.is_import {
+ sym = g.table.get_type_symbol(sym.info.parent_type)
+ str_fn_name = styp_to_str_fn_name(sym.name)
+ }
+ }
+ sym_has_str_method, str_method_expects_ptr, str_nr_args := sym.str_method_info()
+ already_generated_key := '$styp:$str_fn_name'
+ if !sym_has_str_method && already_generated_key !in g.str_types && !typ.has_flag(.optional) {
+ $if debugautostr ? {
+ eprintln('> gen_str_for_type: |typ: ${typ:5}, ${sym.name:20}|has_str: ${sym_has_str_method:5}|expects_ptr: ${str_method_expects_ptr:5}|nr_args: ${str_nr_args:1}|fn_name: ${str_fn_name:20}')
+ }
+ g.str_types << already_generated_key
+ match mut sym.info {
+ ast.Alias {
+ if sym.info.is_import {
+ g.gen_str_default(sym, styp, str_fn_name)
+ } else {
+ g.gen_str_for_alias(sym.info, styp, str_fn_name)
+ }
+ }
+ ast.Array {
+ g.gen_str_for_array(sym.info, styp, str_fn_name)
+ }
+ ast.ArrayFixed {
+ g.gen_str_for_array_fixed(sym.info, styp, str_fn_name)
+ }
+ ast.Enum {
+ g.gen_str_for_enum(sym.info, styp, str_fn_name)
+ }
+ ast.FnType {
+ g.gen_str_for_fn_type(sym.info, styp, str_fn_name)
+ }
+ ast.Struct {
+ g.gen_str_for_struct(sym.info, styp, str_fn_name)
+ }
+ ast.Map {
+ g.gen_str_for_map(sym.info, styp, str_fn_name)
+ }
+ ast.MultiReturn {
+ g.gen_str_for_multi_return(sym.info, styp, str_fn_name)
+ }
+ ast.SumType {
+ g.gen_str_for_union_sum_type(sym.info, styp, str_fn_name)
+ }
+ ast.Interface {
+ g.gen_str_for_interface(sym.info, styp, str_fn_name)
+ }
+ ast.Chan {
+ g.gen_str_for_chan(sym.info, styp, str_fn_name)
+ }
+ ast.Thread {
+ g.gen_str_for_thread(sym.info, styp, str_fn_name)
+ }
+ else {
+ verror("could not generate string method $str_fn_name for type '$styp'")
+ }
+ }
+ }
+ if typ.has_flag(.optional) {
+ option_already_generated_key := 'option_$already_generated_key'
+ if option_already_generated_key !in g.str_types {
+ g.gen_str_for_option(typ, styp, str_fn_name)
+ g.str_types << option_already_generated_key
+ }
+ return str_fn_name
+ }
+ return str_fn_name
+}
+
+fn (mut g Gen) gen_str_for_option(typ ast.Type, styp string, str_fn_name string) {
+ parent_type := typ.clear_flag(.optional)
+ sym := g.table.get_type_symbol(parent_type)
+ sym_has_str_method, _, _ := sym.str_method_info()
+ parent_str_fn_name := g.gen_str_for_type(parent_type)
+
+ g.type_definitions.writeln('string ${str_fn_name}($styp it); // auto')
+ g.auto_str_funcs.writeln('string ${str_fn_name}($styp it) { return indent_${str_fn_name}(it, 0); }')
+ g.type_definitions.writeln('string indent_${str_fn_name}($styp it, int indent_count); // auto')
+ g.auto_str_funcs.writeln('string indent_${str_fn_name}($styp it, int indent_count) {')
+ g.auto_str_funcs.writeln('\tstring res;')
+ g.auto_str_funcs.writeln('\tif (it.state == 0) {')
+ if sym.kind == .string {
+ tmp_res := '${parent_str_fn_name}(*($sym.cname*)it.data)'
+ g.auto_str_funcs.writeln('\t\tres = ${str_intp_sq(tmp_res)};')
+ } else if should_use_indent_func(sym.kind) && !sym_has_str_method {
+ g.auto_str_funcs.writeln('\t\tres = indent_${parent_str_fn_name}(*($sym.cname*)it.data, indent_count);')
+ } else {
+ g.auto_str_funcs.writeln('\t\tres = ${parent_str_fn_name}(*($sym.cname*)it.data);')
+ }
+ g.auto_str_funcs.writeln('\t} else {')
+
+ tmp_str := str_intp_sub('error: %%', 'IError_str(it.err)')
+ g.auto_str_funcs.writeln('\t\tres = $tmp_str;')
+ g.auto_str_funcs.writeln('\t}')
+
+ g.auto_str_funcs.writeln('\treturn ${str_intp_sub('Option(%%)', 'res')};')
+ g.auto_str_funcs.writeln('}')
+}
+
+fn (mut g Gen) gen_str_for_alias(info ast.Alias, styp string, str_fn_name string) {
+ parent_str_fn_name := g.gen_str_for_type(info.parent_type)
+ mut clean_type_v_type_name := util.strip_main_name(styp.replace('__', '.'))
+ g.type_definitions.writeln('static string ${str_fn_name}($styp it); // auto')
+ g.auto_str_funcs.writeln('static string ${str_fn_name}($styp it) { return indent_${str_fn_name}(it, 0); }')
+ g.type_definitions.writeln('static string indent_${str_fn_name}($styp it, int indent_count); // auto')
+ g.auto_str_funcs.writeln('static string indent_${str_fn_name}($styp it, int indent_count) {')
+ g.auto_str_funcs.writeln('\tstring indents = string_repeat(_SLIT(" "), indent_count);')
+ g.auto_str_funcs.writeln('\tstring tmp_ds = ${parent_str_fn_name}(it);')
+ g.auto_str_funcs.writeln('\tstring res = str_intp(3, _MOV((StrIntpData[]){
+ {_SLIT0, $c.si_s_code, {.d_s = indents }},
+ {_SLIT("${clean_type_v_type_name}("), $c.si_s_code, {.d_s = tmp_ds }},
+ {_SLIT(")"), 0, {.d_c = 0 }}
+ }));')
+ g.auto_str_funcs.writeln('\tstring_free(&indents);')
+ g.auto_str_funcs.writeln('\tstring_free(&tmp_ds);')
+ g.auto_str_funcs.writeln('\treturn res;')
+ g.auto_str_funcs.writeln('}')
+}
+
+fn (mut g Gen) gen_str_for_multi_return(info ast.MultiReturn, styp string, str_fn_name string) {
+ g.type_definitions.writeln('static string ${str_fn_name}($styp a); // auto')
+ mut fn_builder := strings.new_builder(512)
+ fn_builder.writeln('static string ${str_fn_name}($styp a) {')
+ fn_builder.writeln('\tstrings__Builder sb = strings__new_builder($info.types.len * 10);')
+ fn_builder.writeln('\tstrings__Builder_write_string(&sb, _SLIT("("));')
+ for i, typ in info.types {
+ sym := g.table.get_type_symbol(typ)
+ is_arg_ptr := typ.is_ptr()
+ sym_has_str_method, str_method_expects_ptr, _ := sym.str_method_info()
+ arg_str_fn_name := g.gen_str_for_type(typ)
+
+ if should_use_indent_func(sym.kind) && !sym_has_str_method {
+ fn_builder.writeln('\tstrings__Builder_write_string(&sb, ${arg_str_fn_name}(a.arg$i));')
+ } else if sym.kind in [.f32, .f64] {
+ if sym.kind == .f32 {
+ tmp_val := str_intp_g32('a.arg$i')
+ fn_builder.writeln('\tstrings__Builder_write_string(&sb, $tmp_val);')
+ } else {
+ tmp_val := str_intp_g64('a.arg$i')
+ fn_builder.writeln('\tstrings__Builder_write_string(&sb, $tmp_val);')
+ }
+ } else if sym.kind == .string {
+ tmp_str := str_intp_sq('a.arg$i')
+ fn_builder.writeln('\tstrings__Builder_write_string(&sb, $tmp_str);')
+ } else if sym.kind == .function {
+ fn_builder.writeln('\tstrings__Builder_write_string(&sb, ${arg_str_fn_name}());')
+ } else {
+ deref, deref_label := deref_kind(str_method_expects_ptr, is_arg_ptr, typ)
+ fn_builder.writeln('\t\tstrings__Builder_write_string(&sb, _SLIT("$deref_label"));')
+ fn_builder.writeln('\tstrings__Builder_write_string(&sb, ${arg_str_fn_name}( $deref a.arg$i));')
+ }
+ if i != info.types.len - 1 {
+ fn_builder.writeln('\tstrings__Builder_write_string(&sb, _SLIT(", "));')
+ }
+ }
+ fn_builder.writeln('\tstrings__Builder_write_string(&sb, _SLIT(")"));')
+ fn_builder.writeln('\tstring res = strings__Builder_str(&sb);')
+ fn_builder.writeln('\tstrings__Builder_free(&sb);')
+ fn_builder.writeln('\treturn res;')
+ fn_builder.writeln('}')
+ g.auto_fn_definitions << fn_builder.str()
+}
+
+fn (mut g Gen) gen_str_for_enum(info ast.Enum, styp string, str_fn_name string) {
+ s := util.no_dots(styp)
+ g.type_definitions.writeln('static string ${str_fn_name}($styp it); // auto')
+ g.auto_str_funcs.writeln('static string ${str_fn_name}($styp it) { /* gen_str_for_enum */')
+ // Enums tagged with `[flag]` are special in that they can be a combination of enum values
+ if info.is_flag {
+ clean_name := util.strip_main_name(styp.replace('__', '.'))
+ g.auto_str_funcs.writeln('\tstring ret = _SLIT("$clean_name{");')
+ g.auto_str_funcs.writeln('\tint first = 1;')
+ for i, val in info.vals {
+ g.auto_str_funcs.writeln('\tif (it & (1 << $i)) {if (!first) {ret = string__plus(ret, _SLIT(" | "));} ret = string__plus(ret, _SLIT(".$val")); first = 0;}')
+ }
+ g.auto_str_funcs.writeln('\tret = string__plus(ret, _SLIT("}"));')
+ g.auto_str_funcs.writeln('\treturn ret;')
+ } else {
+ g.auto_str_funcs.writeln('\tswitch(it) {')
+ // Only use the first multi value on the lookup
+ mut seen := []string{len: info.vals.len}
+ for val in info.vals {
+ if info.is_multi_allowed && val in seen {
+ continue
+ } else if info.is_multi_allowed {
+ seen << val
+ }
+ g.auto_str_funcs.writeln('\t\tcase ${s}__$val: return _SLIT("$val");')
+ }
+ g.auto_str_funcs.writeln('\t\tdefault: return _SLIT("unknown enum value");')
+ g.auto_str_funcs.writeln('\t}')
+ }
+ g.auto_str_funcs.writeln('}')
+}
+
+fn (mut g Gen) gen_str_for_interface(info ast.Interface, styp string, str_fn_name string) {
+ // _str() functions should have a single argument, the indenting ones take 2:
+ g.type_definitions.writeln('static string ${str_fn_name}($styp x); // auto')
+ g.auto_str_funcs.writeln('static string ${str_fn_name}($styp x) { return indent_${str_fn_name}(x, 0); }')
+ g.type_definitions.writeln('static string indent_${str_fn_name}($styp x, int indent_count); // auto')
+ mut fn_builder := strings.new_builder(512)
+ mut clean_interface_v_type_name := styp.replace('__', '.')
+ if styp.ends_with('*') {
+ clean_interface_v_type_name = '&' + clean_interface_v_type_name.replace('*', '')
+ }
+ if clean_interface_v_type_name.contains('_T_') {
+ clean_interface_v_type_name =
+ clean_interface_v_type_name.replace('Array_', '[]').replace('_T_', '<').replace('_', ', ') +
+ '>'
+ }
+ clean_interface_v_type_name = util.strip_main_name(clean_interface_v_type_name)
+ fn_builder.writeln('static string indent_${str_fn_name}($styp x, int indent_count) { /* gen_str_for_interface */')
+ for typ in info.types {
+ subtype := g.table.get_type_symbol(typ)
+ mut func_name := g.gen_str_for_type(typ)
+ sym_has_str_method, str_method_expects_ptr, _ := subtype.str_method_info()
+ if should_use_indent_func(subtype.kind) && !sym_has_str_method {
+ func_name = 'indent_$func_name'
+ }
+
+ // str_intp
+ deref := if sym_has_str_method && str_method_expects_ptr { ' ' } else { '*' }
+ if typ == ast.string_type {
+ mut val := '${func_name}(${deref}($subtype.cname*)x._$subtype.cname'
+ if should_use_indent_func(subtype.kind) && !sym_has_str_method {
+ val += ', indent_count'
+ }
+ val += ')'
+ res := 'str_intp(2, _MOV((StrIntpData[]){
+ {_SLIT("${clean_interface_v_type_name}(\'"), $c.si_s_code, {.d_s = $val}},
+ {_SLIT("\')"), 0, {.d_c = 0 }}
+ }))'
+ fn_builder.write_string('\tif (x._typ == _${styp}_${subtype.cname}_index)')
+ fn_builder.write_string(' return $res;')
+ } else {
+ mut val := '${func_name}(${deref}($subtype.cname*)x._$subtype.cname'
+ if should_use_indent_func(subtype.kind) && !sym_has_str_method {
+ val += ', indent_count'
+ }
+ val += ')'
+ res := 'str_intp(2, _MOV((StrIntpData[]){
+ {_SLIT("${clean_interface_v_type_name}("), $c.si_s_code, {.d_s = $val}},
+ {_SLIT(")"), 0, {.d_c = 0 }}
+ }))'
+ fn_builder.write_string('\tif (x._typ == _${styp}_${subtype.cname}_index)')
+ fn_builder.write_string(' return $res;\n')
+ }
+ }
+ fn_builder.writeln('\treturn _SLIT("unknown interface value");')
+ fn_builder.writeln('}')
+ g.auto_fn_definitions << fn_builder.str()
+}
+
+fn (mut g Gen) gen_str_for_union_sum_type(info ast.SumType, styp string, str_fn_name string) {
+ // _str() functions should have a single argument, the indenting ones take 2:
+ g.type_definitions.writeln('static string ${str_fn_name}($styp x); // auto')
+ g.auto_str_funcs.writeln('static string ${str_fn_name}($styp x) { return indent_${str_fn_name}(x, 0); }')
+ g.type_definitions.writeln('static string indent_${str_fn_name}($styp x, int indent_count); // auto')
+ mut fn_builder := strings.new_builder(512)
+ fn_builder.writeln('static string indent_${str_fn_name}($styp x, int indent_count) {')
+ mut clean_sum_type_v_type_name := styp.replace('__', '.')
+ if styp.ends_with('*') {
+ clean_sum_type_v_type_name = '&' + clean_sum_type_v_type_name.replace('*', '')
+ }
+ if clean_sum_type_v_type_name.contains('_T_') {
+ clean_sum_type_v_type_name =
+ clean_sum_type_v_type_name.replace('Array_', '[]').replace('_T_', '<').replace('_', ', ') +
+ '>'
+ }
+ clean_sum_type_v_type_name = util.strip_main_name(clean_sum_type_v_type_name)
+ fn_builder.writeln('\tswitch(x._typ) {')
+ for typ in info.variants {
+ typ_str := g.typ(typ)
+ mut func_name := g.gen_str_for_type(typ)
+ sym := g.table.get_type_symbol(typ)
+ sym_has_str_method, str_method_expects_ptr, _ := sym.str_method_info()
+ deref := if sym_has_str_method && str_method_expects_ptr { ' ' } else { '*' }
+ if should_use_indent_func(sym.kind) && !sym_has_str_method {
+ func_name = 'indent_$func_name'
+ }
+
+ // str_intp
+ if typ == ast.string_type {
+ mut val := '${func_name}(${deref}($typ_str*)x._$sym.cname'
+ if should_use_indent_func(sym.kind) && !sym_has_str_method {
+ val += ', indent_count'
+ }
+ val += ')'
+ res := 'str_intp(2, _MOV((StrIntpData[]){
+ {_SLIT("${clean_sum_type_v_type_name}(\'"), $c.si_s_code, {.d_s = $val}},
+ {_SLIT("\')"), 0, {.d_c = 0 }}
+ }))'
+ fn_builder.write_string('\t\tcase $typ: return $res;')
+ } else {
+ mut val := '${func_name}(${deref}($typ_str*)x._$sym.cname'
+ if should_use_indent_func(sym.kind) && !sym_has_str_method {
+ val += ', indent_count'
+ }
+ val += ')'
+ res := 'str_intp(2, _MOV((StrIntpData[]){
+ {_SLIT("${clean_sum_type_v_type_name}("), $c.si_s_code, {.d_s = $val}},
+ {_SLIT(")"), 0, {.d_c = 0 }}
+ }))'
+ fn_builder.write_string('\t\tcase $typ: return $res;')
+ }
+ }
+ fn_builder.writeln('\t\tdefault: return _SLIT("unknown sum type value");')
+ fn_builder.writeln('\t}')
+ fn_builder.writeln('}')
+ g.auto_fn_definitions << fn_builder.str()
+}
+
+fn (mut g Gen) fn_decl_str(info ast.FnType) string {
+ mut fn_str := 'fn ('
+ for i, arg in info.func.params {
+ if arg.is_mut {
+ fn_str += 'mut '
+ }
+ if i > 0 {
+ fn_str += ', '
+ }
+ fn_str += util.strip_main_name(g.table.get_type_name(g.unwrap_generic(arg.typ)))
+ }
+ fn_str += ')'
+ if info.func.return_type == ast.ovoid_type {
+ fn_str += ' ?'
+ } else if info.func.return_type != ast.void_type {
+ x := util.strip_main_name(g.table.get_type_name(g.unwrap_generic(info.func.return_type)))
+ if info.func.return_type.has_flag(.optional) {
+ fn_str += ' ?$x'
+ } else {
+ fn_str += ' $x'
+ }
+ }
+ return fn_str
+}
+
+fn (mut g Gen) gen_str_for_fn_type(info ast.FnType, styp string, str_fn_name string) {
+ g.type_definitions.writeln('static string ${str_fn_name}(); // auto')
+ g.auto_str_funcs.writeln('static string ${str_fn_name}() { return _SLIT("${g.fn_decl_str(info)}");}')
+}
+
+fn (mut g Gen) gen_str_for_chan(info ast.Chan, styp string, str_fn_name string) {
+ elem_type_name := util.strip_main_name(g.table.get_type_name(g.unwrap_generic(info.elem_type)))
+ g.type_definitions.writeln('static string ${str_fn_name}($styp x); // auto')
+ g.auto_str_funcs.writeln('static string ${str_fn_name}($styp x) { return sync__Channel_auto_str(x, _SLIT("$elem_type_name")); }')
+}
+
+fn (mut g Gen) gen_str_for_thread(info ast.Thread, styp string, str_fn_name string) {
+ ret_type_name := util.strip_main_name(g.table.get_type_name(info.return_type))
+ g.type_definitions.writeln('static string ${str_fn_name}($styp _); // auto}')
+ g.auto_str_funcs.writeln('static string ${str_fn_name}($styp _) { return _SLIT("thread($ret_type_name)");}')
+}
+
+[inline]
+fn styp_to_str_fn_name(styp string) string {
+ return styp.replace_each(['*', '', '.', '__', ' ', '__']) + '_str'
+}
+
+fn deref_kind(str_method_expects_ptr bool, is_elem_ptr bool, typ ast.Type) (string, string) {
+ if str_method_expects_ptr != is_elem_ptr {
+ if is_elem_ptr {
+ return '*'.repeat(typ.nr_muls()), '&'.repeat(typ.nr_muls())
+ } else {
+ return '&', ''
+ }
+ }
+ return '', ''
+}
+
+fn (mut g Gen) gen_str_for_array(info ast.Array, styp string, str_fn_name string) {
+ mut typ := info.elem_type
+ mut sym := g.table.get_type_symbol(info.elem_type)
+ if mut sym.info is ast.Alias {
+ typ = sym.info.parent_type
+ sym = g.table.get_type_symbol(typ)
+ }
+ field_styp := g.typ(typ)
+ is_elem_ptr := typ.is_ptr()
+ sym_has_str_method, str_method_expects_ptr, _ := sym.str_method_info()
+ mut elem_str_fn_name := g.gen_str_for_type(typ)
+ if sym.kind == .byte {
+ elem_str_fn_name = elem_str_fn_name + '_escaped'
+ }
+
+ g.type_definitions.writeln('static string ${str_fn_name}($styp a); // auto')
+ g.auto_str_funcs.writeln('static string ${str_fn_name}($styp a) { return indent_${str_fn_name}(a, 0);}')
+ g.type_definitions.writeln('static string indent_${str_fn_name}($styp a, int indent_count); // auto')
+ g.auto_str_funcs.writeln('static string indent_${str_fn_name}($styp a, int indent_count) {')
+ g.auto_str_funcs.writeln('\tstrings__Builder sb = strings__new_builder(a.len * 10);')
+ g.auto_str_funcs.writeln('\tstrings__Builder_write_string(&sb, _SLIT("["));')
+ g.auto_str_funcs.writeln('\tfor (int i = 0; i < a.len; ++i) {')
+ if sym.kind == .function {
+ g.auto_str_funcs.writeln('\t\tstring x = ${elem_str_fn_name}();')
+ } else {
+ if sym.kind == .array_fixed {
+ g.auto_str_funcs.writeln('\t\t$field_styp it;')
+ g.auto_str_funcs.writeln('\t\tmemcpy(*($field_styp*)it, (byte*)array_get(a, i), sizeof($field_styp));')
+ } else {
+ g.auto_str_funcs.writeln('\t\t$field_styp it = *($field_styp*)array_get(a, i);')
+ }
+ if should_use_indent_func(sym.kind) && !sym_has_str_method {
+ if is_elem_ptr {
+ g.auto_str_funcs.writeln('\t\tstring x = indent_${elem_str_fn_name}(*it, indent_count);')
+ } else {
+ g.auto_str_funcs.writeln('\t\tstring x = indent_${elem_str_fn_name}(it, indent_count);')
+ }
+ } else if sym.kind in [.f32, .f64] {
+ if sym.kind == .f32 {
+ g.auto_str_funcs.writeln('\t\tstring x = ${str_intp_g32('it')};')
+ } else {
+ g.auto_str_funcs.writeln('\t\tstring x = ${str_intp_g64('it')};')
+ }
+ } else if sym.kind == .rune {
+ // Rune are managed at this level as strings
+ g.auto_str_funcs.writeln('\t\tstring x = str_intp(2, _MOV((StrIntpData[]){{_SLIT("\`"), $c.si_s_code, {.d_s = ${elem_str_fn_name}(it) }}, {_SLIT("\`"), 0, {.d_c = 0 }}}));\n')
+ } else if sym.kind == .string {
+ g.auto_str_funcs.writeln('\t\tstring x = str_intp(2, _MOV((StrIntpData[]){{_SLIT("\'"), $c.si_s_code, {.d_s = it }}, {_SLIT("\'"), 0, {.d_c = 0 }}}));\n')
+ } else {
+ // There is a custom .str() method, so use it.
+ // NB: we need to take account of whether the user has defined
+ // `fn (x T) str() {` or `fn (x &T) str() {`, and convert accordingly
+ deref, deref_label := deref_kind(str_method_expects_ptr, is_elem_ptr, typ)
+ g.auto_str_funcs.writeln('\t\tstrings__Builder_write_string(&sb, _SLIT("$deref_label"));')
+ g.auto_str_funcs.writeln('\t\tstring x = ${elem_str_fn_name}( $deref it);')
+ }
+ }
+ g.auto_str_funcs.writeln('\t\tstrings__Builder_write_string(&sb, x);')
+ if g.is_autofree && typ != ast.bool_type {
+ // no need to free "true"/"false" literals
+ g.auto_str_funcs.writeln('\t\tstring_free(&x);')
+ }
+ g.auto_str_funcs.writeln('\t\tif (i < a.len-1) {')
+ g.auto_str_funcs.writeln('\t\t\tstrings__Builder_write_string(&sb, _SLIT(", "));')
+ g.auto_str_funcs.writeln('\t\t}')
+ g.auto_str_funcs.writeln('\t}')
+ g.auto_str_funcs.writeln('\tstrings__Builder_write_string(&sb, _SLIT("]"));')
+ g.auto_str_funcs.writeln('\tstring res = strings__Builder_str(&sb);')
+ g.auto_str_funcs.writeln('\tstrings__Builder_free(&sb);')
+ g.auto_str_funcs.writeln('\treturn res;')
+ g.auto_str_funcs.writeln('}')
+}
+
+fn (mut g Gen) gen_str_for_array_fixed(info ast.ArrayFixed, styp string, str_fn_name string) {
+ mut typ := info.elem_type
+ mut sym := g.table.get_type_symbol(info.elem_type)
+ if mut sym.info is ast.Alias {
+ typ = sym.info.parent_type
+ sym = g.table.get_type_symbol(typ)
+ }
+ is_elem_ptr := typ.is_ptr()
+ sym_has_str_method, str_method_expects_ptr, _ := sym.str_method_info()
+ elem_str_fn_name := g.gen_str_for_type(typ)
+
+ g.type_definitions.writeln('static string ${str_fn_name}($styp a); // auto')
+ g.auto_str_funcs.writeln('static string ${str_fn_name}($styp a) { return indent_${str_fn_name}(a, 0);}')
+ g.type_definitions.writeln('static string indent_${str_fn_name}($styp a, int indent_count); // auto')
+ g.auto_str_funcs.writeln('static string indent_${str_fn_name}($styp a, int indent_count) {')
+ g.auto_str_funcs.writeln('\tstrings__Builder sb = strings__new_builder($info.size * 10);')
+ g.auto_str_funcs.writeln('\tstrings__Builder_write_string(&sb, _SLIT("["));')
+ g.auto_str_funcs.writeln('\tfor (int i = 0; i < $info.size; ++i) {')
+ if sym.kind == .function {
+ g.auto_str_funcs.writeln('\t\tstring x = ${elem_str_fn_name}();')
+ g.auto_str_funcs.writeln('\t\tstrings__Builder_write_string(&sb, x);')
+ } else {
+ deref, deref_label := deref_kind(str_method_expects_ptr, is_elem_ptr, typ)
+ if should_use_indent_func(sym.kind) && !sym_has_str_method {
+ if is_elem_ptr {
+ g.auto_str_funcs.writeln('\t\tstrings__Builder_write_string(&sb, _SLIT("$deref_label"));')
+ g.auto_str_funcs.writeln('\t\tif ( 0 == a[i] ) {')
+ g.auto_str_funcs.writeln('\t\t\tstrings__Builder_write_string(&sb, _SLIT("0"));')
+ g.auto_str_funcs.writeln('\t\t}else{')
+ g.auto_str_funcs.writeln('\t\t\tstrings__Builder_write_string(&sb, ${elem_str_fn_name}( $deref a[i]) );')
+ g.auto_str_funcs.writeln('\t\t}')
+ } else {
+ g.auto_str_funcs.writeln('\t\tstrings__Builder_write_string(&sb, ${elem_str_fn_name}( $deref a[i]) );')
+ }
+ } else if sym.kind in [.f32, .f64] {
+ if sym.kind == .f32 {
+ g.auto_str_funcs.writeln('\t\tstrings__Builder_write_string(&sb, ${str_intp_g32('a[i]')} );')
+ } else {
+ g.auto_str_funcs.writeln('\t\tstrings__Builder_write_string(&sb, ${str_intp_g64('a[i]')} );')
+ }
+ } else if sym.kind == .string {
+ g.auto_str_funcs.writeln('\t\tstrings__Builder_write_string(&sb, ${str_intp_sq('a[i]')});')
+ } else if sym.kind == .rune {
+ tmp_str := str_intp_rune('${elem_str_fn_name}( $deref a[i])')
+ g.auto_str_funcs.writeln('\t\tstrings__Builder_write_string(&sb, $tmp_str);')
+ } else {
+ g.auto_str_funcs.writeln('\t\tstrings__Builder_write_string(&sb, ${elem_str_fn_name}( $deref a[i]));')
+ }
+ }
+ g.auto_str_funcs.writeln('\t\tif (i < ${info.size - 1}) {')
+ g.auto_str_funcs.writeln('\t\t\tstrings__Builder_write_string(&sb, _SLIT(", "));')
+ g.auto_str_funcs.writeln('\t\t}')
+ g.auto_str_funcs.writeln('\t}')
+ g.auto_str_funcs.writeln('\tstrings__Builder_write_string(&sb, _SLIT("]"));')
+ g.auto_str_funcs.writeln('\tstring res = strings__Builder_str(&sb);')
+ g.auto_str_funcs.writeln('\tstrings__Builder_free(&sb);')
+ g.auto_str_funcs.writeln('\treturn res;')
+ g.auto_str_funcs.writeln('}')
+}
+
+fn (mut g Gen) gen_str_for_map(info ast.Map, styp string, str_fn_name string) {
+ mut key_typ := info.key_type
+ mut key_sym := g.table.get_type_symbol(key_typ)
+ if mut key_sym.info is ast.Alias {
+ key_typ = key_sym.info.parent_type
+ key_sym = g.table.get_type_symbol(key_typ)
+ }
+ key_styp := g.typ(key_typ)
+ key_str_fn_name := key_styp.replace('*', '') + '_str'
+ if !key_sym.has_method('str') {
+ g.gen_str_for_type(key_typ)
+ }
+
+ mut val_typ := info.value_type
+ mut val_sym := g.table.get_type_symbol(val_typ)
+ if mut val_sym.info is ast.Alias {
+ val_typ = val_sym.info.parent_type
+ val_sym = g.table.get_type_symbol(val_typ)
+ }
+ val_styp := g.typ(val_typ)
+ elem_str_fn_name := val_styp.replace('*', '') + '_str'
+ if !val_sym.has_method('str') {
+ g.gen_str_for_type(val_typ)
+ }
+
+ g.type_definitions.writeln('static string ${str_fn_name}($styp m); // auto')
+ g.auto_str_funcs.writeln('static string ${str_fn_name}($styp m) { return indent_${str_fn_name}(m, 0);}')
+ g.type_definitions.writeln('static string indent_${str_fn_name}($styp m, int indent_count); // auto')
+ g.auto_str_funcs.writeln('static string indent_${str_fn_name}($styp m, int indent_count) { /* gen_str_for_map */')
+ g.auto_str_funcs.writeln('\tstrings__Builder sb = strings__new_builder(m.key_values.len*10);')
+ g.auto_str_funcs.writeln('\tstrings__Builder_write_string(&sb, _SLIT("{"));')
+ g.auto_str_funcs.writeln('\tfor (int i = 0; i < m.key_values.len; ++i) {')
+ g.auto_str_funcs.writeln('\t\tif (!DenseArray_has_index(&m.key_values, i)) { continue; }')
+
+ if key_sym.kind == .string {
+ g.auto_str_funcs.writeln('\t\tstring key = *(string*)DenseArray_key(&m.key_values, i);')
+ } else {
+ g.auto_str_funcs.writeln('\t\t$key_styp key = *($key_styp*)DenseArray_key(&m.key_values, i);')
+ }
+ if key_sym.kind == .string {
+ g.auto_str_funcs.writeln('\t\tstrings__Builder_write_string(&sb, ${str_intp_sq('key')});')
+ } else if key_sym.kind == .rune {
+ tmp_str := str_intp_rune('${key_str_fn_name}(key)')
+ g.auto_str_funcs.writeln('\t\tstrings__Builder_write_string(&sb, $tmp_str);')
+ } else {
+ g.auto_str_funcs.writeln('\t\tstrings__Builder_write_string(&sb, ${key_str_fn_name}(key));')
+ }
+ g.auto_str_funcs.writeln('\t\tstrings__Builder_write_string(&sb, _SLIT(": "));')
+ if val_sym.kind == .function {
+ g.auto_str_funcs.writeln('\t\tstrings__Builder_write_string(&sb, ${elem_str_fn_name}());')
+ } else if val_sym.kind == .string {
+ tmp_str := str_intp_sq('*($val_styp*)DenseArray_value(&m.key_values, i)')
+ g.auto_str_funcs.writeln('\t\tstrings__Builder_write_string(&sb, $tmp_str);')
+ } else if should_use_indent_func(val_sym.kind) && !val_sym.has_method('str') {
+ ptr_str := '*'.repeat(val_typ.nr_muls())
+ g.auto_str_funcs.writeln('\t\tstrings__Builder_write_string(&sb, indent_${elem_str_fn_name}(*${ptr_str}($val_styp*)DenseArray_value(&m.key_values, i), indent_count));')
+ } else if val_sym.kind in [.f32, .f64] {
+ tmp_val := '*($val_styp*)DenseArray_value(&m.key_values, i)'
+ if val_sym.kind == .f32 {
+ g.auto_str_funcs.writeln('\t\tstrings__Builder_write_string(&sb, ${str_intp_g32(tmp_val)});')
+ } else {
+ g.auto_str_funcs.writeln('\t\tstrings__Builder_write_string(&sb, ${str_intp_g64(tmp_val)});')
+ }
+ } else if val_sym.kind == .rune {
+ tmp_str := str_intp_rune('${elem_str_fn_name}(*($val_styp*)DenseArray_value(&m.key_values, i))')
+ g.auto_str_funcs.writeln('\t\tstrings__Builder_write_string(&sb, $tmp_str);')
+ } else {
+ g.auto_str_funcs.writeln('\t\tstrings__Builder_write_string(&sb, ${elem_str_fn_name}(*($val_styp*)DenseArray_value(&m.key_values, i)));')
+ }
+ g.auto_str_funcs.writeln('\t\tif (i != m.key_values.len-1) {')
+ g.auto_str_funcs.writeln('\t\t\tstrings__Builder_write_string(&sb, _SLIT(", "));')
+ g.auto_str_funcs.writeln('\t\t}')
+ g.auto_str_funcs.writeln('\t}')
+ g.auto_str_funcs.writeln('\tstrings__Builder_write_string(&sb, _SLIT("}"));')
+ g.auto_str_funcs.writeln('\tstring res = strings__Builder_str(&sb);')
+ g.auto_str_funcs.writeln('\tstrings__Builder_free(&sb);')
+ g.auto_str_funcs.writeln('\treturn res;')
+ g.auto_str_funcs.writeln('}')
+}
+
+fn (g &Gen) type_to_fmt1(typ ast.Type) StrIntpType {
+ if typ == ast.byte_type_idx {
+ return .si_u8
+ }
+ if typ == ast.char_type_idx {
+ return .si_c
+ }
+ if typ in ast.voidptr_types || typ in ast.byteptr_types {
+ return .si_p
+ }
+ if typ in ast.charptr_types {
+ // return '%C\\000' // a C string
+ return .si_s
+ }
+ sym := g.table.get_type_symbol(typ)
+ if typ.is_ptr() && (typ.is_int_valptr() || typ.is_float_valptr()) {
+ return .si_s
+ } else if sym.kind in [.struct_, .array, .array_fixed, .map, .bool, .enum_, .interface_,
+ .sum_type, .function, .alias, .chan] {
+ return .si_s
+ } else if sym.kind == .string {
+ return .si_s
+ // return "'%.*s\\000'"
+ } else if sym.kind in [.f32, .f64] {
+ if sym.kind == .f32 {
+ return .si_g32
+ }
+ return .si_g64
+ } else if sym.kind == .int {
+ return .si_i32
+ } else if sym.kind == .u32 {
+ return .si_u32
+ } else if sym.kind == .u64 {
+ return .si_u64
+ } else if sym.kind == .i64 {
+ return .si_i64
+ }
+ return .si_i32
+}
+
+fn (mut g Gen) gen_str_for_struct(info ast.Struct, styp string, str_fn_name string) {
+ // _str() functions should have a single argument, the indenting ones take 2:
+ g.type_definitions.writeln('static string ${str_fn_name}($styp it); // auto')
+ g.auto_str_funcs.writeln('static string ${str_fn_name}($styp it) { return indent_${str_fn_name}(it, 0);}')
+ g.type_definitions.writeln('static string indent_${str_fn_name}($styp it, int indent_count); // auto')
+ mut fn_builder := strings.new_builder(512)
+ defer {
+ g.auto_fn_definitions << fn_builder.str()
+ }
+ fn_builder.writeln('static string indent_${str_fn_name}($styp it, int indent_count) {')
+ mut clean_struct_v_type_name := styp.replace('__', '.')
+ if clean_struct_v_type_name.contains('_T_') {
+ // TODO: this is a bit hacky. styp shouldn't be even parsed with _T_
+ // use something different than g.typ for styp
+ clean_struct_v_type_name =
+ clean_struct_v_type_name.replace('Array_', '[]').replace('_T_', '<').replace('_', ', ') +
+ '>'
+ }
+ clean_struct_v_type_name = util.strip_main_name(clean_struct_v_type_name)
+ // generate ident / indent length = 4 spaces
+ if info.fields.len == 0 {
+ fn_builder.writeln('\treturn _SLIT("$clean_struct_v_type_name{}");')
+ fn_builder.writeln('}')
+ return
+ }
+
+ fn_builder.writeln('\tstring indents = string_repeat(_SLIT(" "), indent_count);')
+ fn_builder.writeln('\tstring res = str_intp( ${info.fields.len * 4 + 3}, _MOV((StrIntpData[]){')
+ fn_builder.writeln('\t\t{_SLIT("$clean_struct_v_type_name{\\n"), 0, {.d_c=0}},')
+
+ for i, field in info.fields {
+ mut ptr_amp := if field.typ.is_ptr() { '&' } else { '' }
+ base_fmt := g.type_to_fmt1(g.unwrap_generic(field.typ))
+
+ // manage prefix and quote symbol for the filed
+ mut quote_str := ''
+ mut prefix := ''
+ sym := g.table.get_type_symbol(g.unwrap_generic(field.typ))
+ if sym.kind == .string {
+ quote_str = "'"
+ } else if field.typ in ast.charptr_types {
+ quote_str = '\\"'
+ prefix = 'C'
+ }
+
+ // first fields doesn't need \n
+ if i == 0 {
+ fn_builder.write_string('\t\t{_SLIT0, $c.si_s_code, {.d_s=indents}}, {_SLIT(" $field.name: $ptr_amp$prefix"), 0, {.d_c=0}}, ')
+ } else {
+ fn_builder.write_string('\t\t{_SLIT("\\n"), $c.si_s_code, {.d_s=indents}}, {_SLIT(" $field.name: $ptr_amp$prefix"), 0, {.d_c=0}}, ')
+ }
+
+ // custom methods management
+ has_custom_str := sym.has_method('str')
+ mut field_styp := g.typ(field.typ).replace('*', '')
+ field_styp_fn_name := if has_custom_str {
+ '${field_styp}_str'
+ } else {
+ g.gen_str_for_type(field.typ)
+ }
+
+ // manage the fact hat with float we use always the g representation
+ if sym.kind !in [.f32, .f64] {
+ fn_builder.write_string('{_SLIT("$quote_str"), ${int(base_fmt)}, {.${data_str(base_fmt)}=')
+ } else {
+ g_fmt := '0x' + (u32(base_fmt) | u32(0x7F) << 9).hex()
+ fn_builder.write_string('{_SLIT("$quote_str"), $g_fmt, {.${data_str(base_fmt)}=')
+ }
+
+ mut func := struct_auto_str_func1(sym, field.typ, field_styp_fn_name, field.name)
+ if field.typ in ast.cptr_types {
+ func = '(voidptr) it.$field.name'
+ } else if field.typ.is_ptr() {
+ // reference types can be "nil"
+ fn_builder.write_string('isnil(it.${c_name(field.name)})')
+ fn_builder.write_string(' ? _SLIT("nil") : ')
+ // struct, floats and ints have a special case through the _str function
+ if sym.kind != .struct_ && !field.typ.is_int_valptr() && !field.typ.is_float_valptr() {
+ fn_builder.write_string('*')
+ }
+ }
+ // handle circular ref type of struct to the struct itself
+ if styp == field_styp {
+ fn_builder.write_string('_SLIT("<circular>")')
+ } else {
+ // manage C charptr
+ if field.typ in ast.charptr_types {
+ fn_builder.write_string('tos2((byteptr)$func)')
+ } else {
+ if field.typ.is_ptr() && sym.kind == .struct_ {
+ fn_builder.write_string('(indent_count > 25) ? _SLIT("<probably circular>") : ')
+ }
+ fn_builder.write_string(func)
+ }
+ }
+
+ fn_builder.writeln('}}, {_SLIT("$quote_str"), 0, {.d_c=0}},')
+ }
+ fn_builder.writeln('\t\t{_SLIT("\\n"), $c.si_s_code, {.d_s=indents}}, {_SLIT("}"), 0, {.d_c=0}},')
+ fn_builder.writeln('\t}));')
+ fn_builder.writeln('\tstring_free(&indents);')
+ fn_builder.writeln('\treturn res;')
+ fn_builder.writeln('}')
+}
+
+fn struct_auto_str_func1(sym &ast.TypeSymbol, field_type ast.Type, fn_name string, field_name string) string {
+ has_custom_str, expects_ptr, _ := sym.str_method_info()
+ if sym.kind == .enum_ {
+ return '${fn_name}(it.${c_name(field_name)})'
+ } else if should_use_indent_func(sym.kind) {
+ mut obj := 'it.${c_name(field_name)}'
+ if field_type.is_ptr() && !expects_ptr {
+ obj = '*$obj'
+ }
+ if has_custom_str {
+ return '${fn_name}($obj)'
+ }
+ return 'indent_${fn_name}($obj, indent_count + 1)'
+ } else if sym.kind in [.array, .array_fixed, .map, .sum_type] {
+ if has_custom_str {
+ return '${fn_name}(it.${c_name(field_name)})'
+ }
+ return 'indent_${fn_name}(it.${c_name(field_name)}, indent_count + 1)'
+ } else if sym.kind == .function {
+ return '${fn_name}()'
+ } else {
+ if sym.kind == .chan {
+ return '${fn_name}(it.${c_name(field_name)})'
+ }
+ mut method_str := 'it.${c_name(field_name)}'
+ if sym.kind == .bool {
+ method_str += ' ? _SLIT("true") : _SLIT("false")'
+ } else if (field_type.is_int_valptr() || field_type.is_float_valptr())
+ && field_type.is_ptr() && !expects_ptr {
+ // ptr int can be "nil", so this needs to be casted to a string
+ if sym.kind == .f32 {
+ return 'str_intp(1, _MOV((StrIntpData[]){
+ {_SLIT0, $si_g32_code, {.d_f32 = *$method_str }}
+ }))'
+ } else if sym.kind == .f64 {
+ return 'str_intp(1, _MOV((StrIntpData[]){
+ {_SLIT0, $si_g64_code, {.d_f64 = *$method_str }}
+ }))'
+ } else if sym.kind == .u64 {
+ fmt_type := StrIntpType.si_u64
+ return 'str_intp(1, _MOV((StrIntpData[]){{_SLIT0, ${u32(fmt_type) | 0xfe00}, {.d_u64 = *$method_str }}}))'
+ }
+ fmt_type := StrIntpType.si_i32
+ return 'str_intp(1, _MOV((StrIntpData[]){{_SLIT0, ${u32(fmt_type) | 0xfe00}, {.d_i32 = *$method_str }}}))'
+ }
+ return method_str
+ }
+}
diff --git a/v_windows/v/vlib/v/gen/c/cgen.v b/v_windows/v/vlib/v/gen/c/cgen.v
new file mode 100644
index 0000000..152d84d
--- /dev/null
+++ b/v_windows/v/vlib/v/gen/c/cgen.v
@@ -0,0 +1,6878 @@
+// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved.
+// Use of this source code is governed by an MIT license
+// that can be found in the LICENSE file.
+module c
+
+import os
+import strings
+import v.ast
+import v.pref
+import v.token
+import v.util
+import v.util.version
+import v.depgraph
+
+const (
+ // NB: some of the words in c_reserved, are not reserved in C,
+ // but are in C++, or have special meaning in V, thus need escaping too.
+ c_reserved = ['auto', 'break', 'calloc', 'case', 'char', 'class', 'const', 'continue',
+ 'default', 'delete', 'do', 'double', 'else', 'enum', 'error', 'exit', 'export', 'extern',
+ 'float', 'for', 'free', 'goto', 'if', 'inline', 'int', 'link', 'long', 'malloc', 'namespace',
+ 'new', 'panic', 'register', 'restrict', 'return', 'short', 'signed', 'sizeof', 'static',
+ 'struct', 'switch', 'typedef', 'typename', 'union', 'unix', 'unsigned', 'void', 'volatile',
+ 'while', 'template', 'stdout', 'stdin', 'stderr']
+ c_reserved_map = string_array_to_map(c_reserved)
+ // same order as in token.Kind
+ cmp_str = ['eq', 'ne', 'gt', 'lt', 'ge', 'le']
+ // when operands are switched
+ cmp_rev = ['eq', 'ne', 'lt', 'gt', 'le', 'ge']
+)
+
+fn string_array_to_map(a []string) map[string]bool {
+ mut res := map[string]bool{}
+ for x in a {
+ res[x] = true
+ }
+ return res
+}
+
+struct Gen {
+ pref &pref.Preferences
+ module_built string
+ field_data_type ast.Type // cache her to avoid map lookups
+mut:
+ table &ast.Table
+ out strings.Builder
+ cheaders strings.Builder
+ includes strings.Builder // all C #includes required by V modules
+ typedefs strings.Builder
+ typedefs2 strings.Builder
+ type_definitions strings.Builder // typedefs, defines etc (everything that goes to the top of the file)
+ definitions strings.Builder // typedefs, defines etc (everything that goes to the top of the file)
+ global_inits map[string]strings.Builder // default initializers for globals (goes in _vinit())
+ inits map[string]strings.Builder // contents of `void _vinit/2{}`
+ cleanups map[string]strings.Builder // contents of `void _vcleanup(){}`
+ gowrappers strings.Builder // all go callsite wrappers
+ stringliterals strings.Builder // all string literals (they depend on tos3() beeing defined
+ auto_str_funcs strings.Builder // function bodies of all auto generated _str funcs
+ comptime_defines strings.Builder // custom defines, given by -d/-define flags on the CLI
+ pcs_declarations strings.Builder // -prof profile counter declarations for each function
+ hotcode_definitions strings.Builder // -live declarations & functions
+ embedded_data strings.Builder // data to embed in the executable/binary
+ shared_types strings.Builder // shared/lock types
+ shared_functions strings.Builder // shared constructors
+ channel_definitions strings.Builder // channel related code
+ options_typedefs strings.Builder // Option typedefs
+ options strings.Builder // `Option_xxxx` types
+ json_forward_decls strings.Builder // json type forward decls
+ enum_typedefs strings.Builder // enum types
+ sql_buf strings.Builder // for writing exprs to args via `sqlite3_bind_int()` etc
+ file &ast.File
+ fn_decl &ast.FnDecl // pointer to the FnDecl we are currently inside otherwise 0
+ last_fn_c_name string
+ tmp_count int // counter for unique tmp vars (_tmp1, _tmp2 etc); resets at the start of each fn.
+ tmp_count2 int // a separate tmp var counter for autofree fn calls
+ tmp_count_declarations int // counter for unique tmp names (_d1, _d2 etc); does NOT reset, used for C declarations
+ global_tmp_count int // like tmp_count but global and not resetted in each function
+ is_assign_lhs bool // inside left part of assign expr (for array_set(), etc)
+ discard_or_result bool // do not safe last ExprStmt of `or` block in tmp variable to defer ongoing expr usage
+ is_void_expr_stmt bool // ExprStmt whos result is discarded
+ is_arraymap_set bool // map or array set value state
+ is_amp bool // for `&Foo{}` to merge PrefixExpr `&` and StructInit `Foo{}`; also for `&byte(0)` etc
+ is_sql bool // Inside `sql db{}` statement, generating sql instead of C (e.g. `and` instead of `&&` etc)
+ is_shared bool // for initialization of hidden mutex in `[rw]shared` literals
+ is_vlines_enabled bool // is it safe to generate #line directives when -g is passed
+ inside_cast_in_heap int // inside cast to interface type in heap (resolve recursive calls)
+ arraymap_set_pos int // map or array set value position
+ vlines_path string // set to the proper path for generating #line directives
+ optionals []string // to avoid duplicates TODO perf, use map
+ chan_pop_optionals []string // types for `x := <-ch or {...}`
+ chan_push_optionals []string // types for `ch <- x or {...}`
+ cur_lock ast.LockExpr
+ mtxs string // array of mutexes if the `lock` has multiple variables
+ labeled_loops map[string]&ast.Stmt
+ inner_loop &ast.Stmt
+ shareds []int // types with hidden mutex for which decl has been emitted
+ inside_ternary int // ?: comma separated statements on a single line
+ inside_map_postfix bool // inside map++/-- postfix expr
+ inside_map_infix bool // inside map<</+=/-= infix expr
+ inside_map_index bool
+ inside_opt_data bool
+ inside_if_optional bool
+ ternary_names map[string]string
+ ternary_level_names map[string][]string
+ stmt_path_pos []int // positions of each statement start, for inserting C statements before the current statement
+ skip_stmt_pos bool // for handling if expressions + autofree (since both prepend C statements)
+ right_is_opt bool
+ is_autofree bool // false, inside the bodies of fns marked with [manualfree], otherwise === g.pref.autofree
+ indent int
+ empty_line bool
+ is_test bool
+ assign_op token.Kind // *=, =, etc (for array_set)
+ defer_stmts []ast.DeferStmt
+ defer_ifdef string
+ defer_profile_code string
+ str_types []string // types that need automatic str() generation
+ threaded_fns []string // for generating unique wrapper types and fns for `go xxx()`
+ waiter_fns []string // functions that wait for `go xxx()` to finish
+ array_fn_definitions []string // array equality functions that have been defined
+ map_fn_definitions []string // map equality functions that have been defined
+ struct_fn_definitions []string // struct equality functions that have been defined
+ sumtype_fn_definitions []string // sumtype equality functions that have been defined
+ alias_fn_definitions []string // alias equality functions that have been defined
+ auto_fn_definitions []string // auto generated functions defination list
+ anon_fn_definitions []string // anon generated functions defination list
+ sumtype_definitions map[int]bool // `_TypeA_to_sumtype_TypeB()` fns that have been generated
+ is_json_fn bool // inside json.encode()
+ json_types []string // to avoid json gen duplicates
+ pcs []ProfileCounterMeta // -prof profile counter fn_names => fn counter name
+ is_builtin_mod bool
+ hotcode_fn_names []string
+ embedded_files []ast.EmbeddedFile
+ sql_i int
+ sql_stmt_name string
+ sql_bind_name string
+ sql_idents []string
+ sql_idents_types []ast.Type
+ sql_left_type ast.Type
+ sql_table_name string
+ sql_fkey string
+ sql_parent_id string
+ sql_side SqlExprSide // left or right, to distinguish idents in `name == name`
+ inside_vweb_tmpl bool
+ inside_return bool
+ inside_or_block bool
+ strs_to_free0 []string // strings.Builder
+ // strs_to_free []string // strings.Builder
+ inside_call bool
+ has_main bool
+ inside_const bool
+ comp_for_method string // $for method in T.methods {}
+ comp_for_field_var string // $for field in T.fields {}; the variable name
+ comp_for_field_value ast.StructField // value of the field variable
+ comp_for_field_type ast.Type // type of the field variable inferred from `$if field.typ is T {}`
+ comptime_var_type_map map[string]ast.Type
+ // tmp_arg_vars_to_free []string
+ // autofree_pregen map[string]string
+ // autofree_pregen_buf strings.Builder
+ // autofree_tmp_vars []string // to avoid redefining the same tmp vars in a single function
+ called_fn_name string
+ cur_mod ast.Module
+ is_js_call bool // for handling a special type arg #1 `json.decode(User, ...)`
+ is_fn_index_call bool
+ // nr_vars_to_free int
+ // doing_autofree_tmp bool
+ inside_lambda bool
+ prevent_sum_type_unwrapping_once bool // needed for assign new values to sum type
+ // used in match multi branch
+ // TypeOne, TypeTwo {}
+ // where an aggregate (at least two types) is generated
+ // sum type deref needs to know which index to deref because unions take care of the correct field
+ aggregate_type_idx int
+ returned_var_name string // to detect that a var doesn't need to be freed since it's being returned
+ branch_parent_pos int // used in BranchStmt (continue/break) for autofree stop position
+ infix_left_var_name string // a && if expr
+ timers &util.Timers = util.new_timers(false)
+ force_main_console bool // true when [console] used on fn main()
+ as_cast_type_names map[string]string // table for type name lookup in runtime (for __as_cast)
+ obf_table map[string]string
+ // main_fn_decl_node ast.FnDecl
+ expected_cast_type ast.Type // for match expr of sumtypes
+ defer_vars []string
+ anon_fn bool
+ array_sort_fn map[string]bool
+ nr_closures int
+}
+
+pub fn gen(files []&ast.File, table &ast.Table, pref &pref.Preferences) string {
+ // println('start cgen2')
+ mut module_built := ''
+ if pref.build_mode == .build_module {
+ for file in files {
+ if file.path.contains(pref.path)
+ && file.mod.short_name == pref.path.all_after_last(os.path_separator).trim_right(os.path_separator) {
+ module_built = file.mod.name
+ break
+ }
+ }
+ }
+ mut timers_should_print := false
+ $if time_cgening ? {
+ timers_should_print = true
+ }
+ mut g := Gen{
+ file: 0
+ out: strings.new_builder(512000)
+ cheaders: strings.new_builder(15000)
+ includes: strings.new_builder(100)
+ typedefs: strings.new_builder(100)
+ typedefs2: strings.new_builder(100)
+ type_definitions: strings.new_builder(100)
+ definitions: strings.new_builder(100)
+ gowrappers: strings.new_builder(100)
+ stringliterals: strings.new_builder(100)
+ auto_str_funcs: strings.new_builder(100)
+ comptime_defines: strings.new_builder(100)
+ pcs_declarations: strings.new_builder(100)
+ hotcode_definitions: strings.new_builder(100)
+ embedded_data: strings.new_builder(1000)
+ options_typedefs: strings.new_builder(100)
+ options: strings.new_builder(100)
+ shared_types: strings.new_builder(100)
+ shared_functions: strings.new_builder(100)
+ channel_definitions: strings.new_builder(100)
+ json_forward_decls: strings.new_builder(100)
+ enum_typedefs: strings.new_builder(100)
+ sql_buf: strings.new_builder(100)
+ table: table
+ pref: pref
+ fn_decl: 0
+ is_autofree: true
+ indent: -1
+ module_built: module_built
+ timers: util.new_timers(timers_should_print)
+ inner_loop: &ast.EmptyStmt{}
+ field_data_type: ast.Type(table.find_type_idx('FieldData'))
+ }
+ g.timers.start('cgen init')
+ for mod in g.table.modules {
+ g.inits[mod] = strings.new_builder(100)
+ g.global_inits[mod] = strings.new_builder(100)
+ g.cleanups[mod] = strings.new_builder(100)
+ }
+ g.init()
+ g.timers.show('cgen init')
+ mut tests_inited := false
+ mut autofree_used := false
+ for file in files {
+ g.timers.start('cgen_file $file.path')
+ g.file = file
+ if g.pref.is_vlines {
+ g.vlines_path = util.vlines_escape_path(file.path, g.pref.ccompiler)
+ }
+ // println('\ncgen "$g.file.path" nr_stmts=$file.stmts.len')
+ // building_v := true && (g.file.path.contains('/vlib/') || g.file.path.contains('cmd/v'))
+ g.is_test = g.pref.is_test
+ if g.file.path == '' || !g.pref.autofree {
+ // cgen test or building V
+ // println('autofree=false')
+ g.is_autofree = false
+ } else {
+ g.is_autofree = true
+ autofree_used = true
+ }
+ // anon fn may include assert and thus this needs
+ // to be included before any test contents are written
+ if g.is_test && !tests_inited {
+ g.write_tests_definitions()
+ tests_inited = true
+ }
+ g.stmts(file.stmts)
+ // Transfer embedded files
+ if file.embedded_files.len > 0 {
+ for path in file.embedded_files {
+ if path !in g.embedded_files {
+ g.embedded_files << path
+ }
+ }
+ }
+ g.timers.show('cgen_file $file.path')
+ }
+ g.timers.start('cgen common')
+ if autofree_used {
+ g.is_autofree = true // so that void _vcleanup is generated
+ }
+ // to make sure type idx's are the same in cached mods
+ if g.pref.build_mode == .build_module {
+ for idx, typ in g.table.type_symbols {
+ if idx == 0 {
+ continue
+ }
+ g.definitions.writeln('int _v_type_idx_${typ.cname}();')
+ }
+ } else if g.pref.use_cache {
+ for idx, typ in g.table.type_symbols {
+ if idx == 0 {
+ continue
+ }
+ g.definitions.writeln('int _v_type_idx_${typ.cname}() { return $idx; };')
+ }
+ }
+ //
+ g.dump_expr_definitions()
+ // v files are finished, what remains is pure C code
+ g.gen_vlines_reset()
+ if g.pref.build_mode != .build_module {
+ // no init in builtin.o
+ g.write_init_function()
+ }
+
+ g.finish()
+
+ mut b := strings.new_builder(640000)
+ b.write_string(g.hashes())
+ b.writeln('\n// V comptime_defines:')
+ b.write_string(g.comptime_defines.str())
+ b.writeln('\n// V typedefs:')
+ b.write_string(g.typedefs.str())
+ b.writeln('\n// V typedefs2:')
+ b.write_string(g.typedefs2.str())
+ b.writeln('\n// V cheaders:')
+ b.write_string(g.cheaders.str())
+ if g.pcs_declarations.len > 0 {
+ b.writeln('\n// V profile counters:')
+ b.write_string(g.pcs_declarations.str())
+ }
+ b.writeln('\n// V includes:')
+ b.write_string(g.includes.str())
+ b.writeln('\n// Enum definitions:')
+ b.write_string(g.enum_typedefs.str())
+ b.writeln('\n// V type definitions:')
+ b.write_string(g.type_definitions.str())
+ b.writeln('\n// V shared types:')
+ b.write_string(g.shared_types.str())
+ b.writeln('\n// V Option_xxx definitions:')
+ b.write_string(g.options.str())
+ b.writeln('\n// V json forward decls:')
+ b.write_string(g.json_forward_decls.str())
+ b.writeln('\n// V definitions:')
+ b.write_string(g.definitions.str())
+ interface_table := g.interface_table()
+ if interface_table.len > 0 {
+ b.writeln('\n// V interface table:')
+ b.write_string(interface_table)
+ }
+ if g.gowrappers.len > 0 {
+ b.writeln('\n// V gowrappers:')
+ b.write_string(g.gowrappers.str())
+ }
+ if g.hotcode_definitions.len > 0 {
+ b.writeln('\n// V hotcode definitions:')
+ b.write_string(g.hotcode_definitions.str())
+ }
+ if g.embedded_data.len > 0 {
+ b.writeln('\n// V embedded data:')
+ b.write_string(g.embedded_data.str())
+ }
+ if g.options_typedefs.len > 0 {
+ b.writeln('\n// V option typedefs:')
+ b.write_string(g.options_typedefs.str())
+ }
+ if g.shared_functions.len > 0 {
+ b.writeln('\n// V shared type functions:')
+ b.write_string(g.shared_functions.str())
+ b.write_string(c_concurrency_helpers)
+ }
+ if g.channel_definitions.len > 0 {
+ b.writeln('\n// V channel code:')
+ b.write_string(g.channel_definitions.str())
+ }
+ if g.stringliterals.len > 0 {
+ b.writeln('\n// V stringliterals:')
+ b.write_string(g.stringliterals.str())
+ }
+ if g.auto_str_funcs.len > 0 {
+ // if g.pref.build_mode != .build_module {
+ b.writeln('\n// V auto str functions:')
+ b.write_string(g.auto_str_funcs.str())
+ // }
+ }
+ if g.auto_fn_definitions.len > 0 {
+ for fn_def in g.auto_fn_definitions {
+ b.writeln(fn_def)
+ }
+ }
+ if g.anon_fn_definitions.len > 0 {
+ if g.nr_closures > 0 {
+ b.writeln('\n// V closure helpers')
+ b.writeln(c_closure_helpers(g.pref))
+ }
+ for fn_def in g.anon_fn_definitions {
+ b.writeln(fn_def)
+ }
+ }
+ b.writeln('\n// V out')
+ b.write_string(g.out.str())
+ b.writeln('\n// THE END.')
+ g.timers.show('cgen common')
+ return b.str()
+}
+
+pub fn (g &Gen) hashes() string {
+ mut res := c_commit_hash_default.replace('@@@', version.vhash())
+ res += c_current_commit_hash_default.replace('@@@', version.githash(g.pref.building_v))
+ return res
+}
+
+pub fn (mut g Gen) init() {
+ if g.pref.custom_prelude != '' {
+ g.cheaders.writeln(g.pref.custom_prelude)
+ } else if !g.pref.no_preludes {
+ g.cheaders.writeln('// Generated by the V compiler')
+ tcc_undef_has_include := '
+#if defined(__TINYC__) && defined(__has_include)
+// tcc does not support has_include properly yet, turn it off completely
+#undef __has_include
+#endif'
+ g.cheaders.writeln(tcc_undef_has_include)
+ g.includes.writeln(tcc_undef_has_include)
+ g.cheaders.writeln(get_guarded_include_text('<inttypes.h>', 'The C compiler can not find <inttypes.h> . Please install build-essentials')) // int64_t etc
+ g.cheaders.writeln(c_builtin_types)
+ if g.pref.is_bare {
+ g.cheaders.writeln(c_bare_headers)
+ } else {
+ g.cheaders.writeln(c_headers)
+ }
+ if !g.pref.skip_unused || g.table.used_maps > 0 {
+ g.cheaders.writeln(c_wyhash_headers)
+ }
+ }
+ if g.pref.os == .ios {
+ g.cheaders.writeln('#define __TARGET_IOS__ 1')
+ g.cheaders.writeln('#include <spawn.h>')
+ }
+ g.write_builtin_types()
+ g.write_typedef_types()
+ g.write_typeof_functions()
+ g.write_sorted_types()
+ g.write_multi_return_types()
+ g.definitions.writeln('// end of definitions #endif')
+ //
+ g.stringliterals.writeln('')
+ g.stringliterals.writeln('// >> string literal consts')
+ if g.pref.build_mode != .build_module {
+ g.stringliterals.writeln('void vinit_string_literals(){')
+ }
+ if g.pref.compile_defines_all.len > 0 {
+ g.comptime_defines.writeln('// V compile time defines by -d or -define flags:')
+ g.comptime_defines.writeln('// All custom defines : ' +
+ g.pref.compile_defines_all.join(','))
+ g.comptime_defines.writeln('// Turned ON custom defines: ' +
+ g.pref.compile_defines.join(','))
+ for cdefine in g.pref.compile_defines {
+ g.comptime_defines.writeln('#define CUSTOM_DEFINE_$cdefine')
+ }
+ g.comptime_defines.writeln('')
+ }
+ if g.table.gostmts > 0 {
+ g.comptime_defines.writeln('#define __VTHREADS__ (1)')
+ }
+ if g.pref.gc_mode in [.boehm_full, .boehm_incr, .boehm_full_opt, .boehm_incr_opt, .boehm_leak] {
+ g.comptime_defines.writeln('#define _VGCBOEHM (1)')
+ }
+ if g.pref.is_debug || 'debug' in g.pref.compile_defines {
+ g.comptime_defines.writeln('#define _VDEBUG (1)')
+ }
+ if g.pref.is_prod || 'prod' in g.pref.compile_defines {
+ g.comptime_defines.writeln('#define _VPROD (1)')
+ }
+ if g.pref.is_test || 'test' in g.pref.compile_defines {
+ g.comptime_defines.writeln('#define _VTEST (1)')
+ }
+ if g.pref.autofree {
+ g.comptime_defines.writeln('#define _VAUTOFREE (1)')
+ // g.comptime_defines.writeln('unsigned char* g_cur_str;')
+ }
+ if g.pref.prealloc {
+ g.comptime_defines.writeln('#define _VPREALLOC (1)')
+ }
+ if g.pref.is_livemain || g.pref.is_liveshared {
+ g.generate_hotcode_reloading_declarations()
+ }
+ // Obfuscate only functions in the main module for now.
+ // Generate the obf_ast.
+ if g.pref.obfuscate {
+ mut i := 0
+ // fns
+ for key, f in g.table.fns {
+ if f.mod != 'main' && key != 'main' { // !key.starts_with('main.') {
+ continue
+ }
+ g.obf_table[key] = '_f$i'
+ i++
+ }
+ // methods
+ for type_sym in g.table.type_symbols {
+ if type_sym.mod != 'main' {
+ continue
+ }
+ for method in type_sym.methods {
+ g.obf_table[type_sym.name + '.' + method.name] = '_f$i'
+ i++
+ }
+ }
+ }
+}
+
+pub fn (mut g Gen) finish() {
+ if g.pref.build_mode != .build_module {
+ g.stringliterals.writeln('}')
+ }
+ g.stringliterals.writeln('// << string literal consts')
+ g.stringliterals.writeln('')
+ if g.pref.is_prof && g.pref.build_mode != .build_module {
+ g.gen_vprint_profile_stats()
+ }
+ if g.pref.is_livemain || g.pref.is_liveshared {
+ g.generate_hotcode_reloader_code()
+ }
+ if g.embed_file_is_prod_mode() && g.embedded_files.len > 0 {
+ g.gen_embedded_data()
+ }
+ if g.pref.is_test {
+ g.gen_c_main_for_tests()
+ } else {
+ g.gen_c_main()
+ }
+}
+
+pub fn (mut g Gen) write_typeof_functions() {
+ g.writeln('')
+ g.writeln('// >> typeof() support for sum types / interfaces')
+ for ityp, typ in g.table.type_symbols {
+ if typ.kind == .sum_type {
+ sum_info := typ.info as ast.SumType
+ if sum_info.is_generic {
+ continue
+ }
+ g.writeln('static char * v_typeof_sumtype_${typ.cname}(int sidx) { /* $typ.name */ ')
+ if g.pref.build_mode == .build_module {
+ g.writeln('\t\tif( sidx == _v_type_idx_${typ.cname}() ) return "${util.strip_main_name(typ.name)}";')
+ for v in sum_info.variants {
+ subtype := g.table.get_type_symbol(v)
+ g.writeln('\tif( sidx == _v_type_idx_${subtype.cname}() ) return "${util.strip_main_name(subtype.name)}";')
+ }
+ g.writeln('\treturn "unknown ${util.strip_main_name(typ.name)}";')
+ } else {
+ tidx := g.table.find_type_idx(typ.name)
+ g.writeln('\tswitch(sidx) {')
+ g.writeln('\t\tcase $tidx: return "${util.strip_main_name(typ.name)}";')
+ for v in sum_info.variants {
+ subtype := g.table.get_type_symbol(v)
+ g.writeln('\t\tcase $v: return "${util.strip_main_name(subtype.name)}";')
+ }
+ g.writeln('\t\tdefault: return "unknown ${util.strip_main_name(typ.name)}";')
+ g.writeln('\t}')
+ }
+ g.writeln('}')
+ g.writeln('')
+ g.writeln('int v_typeof_sumtype_idx_${typ.cname}(int sidx) { /* $typ.name */ ')
+ if g.pref.build_mode == .build_module {
+ g.writeln('\t\tif( sidx == _v_type_idx_${typ.cname}() ) return ${int(ityp)};')
+ for v in sum_info.variants {
+ subtype := g.table.get_type_symbol(v)
+ g.writeln('\tif( sidx == _v_type_idx_${subtype.cname}() ) return ${int(v)};')
+ }
+ g.writeln('\treturn ${int(ityp)};')
+ } else {
+ tidx := g.table.find_type_idx(typ.name)
+ g.writeln('\tswitch(sidx) {')
+ g.writeln('\t\tcase $tidx: return ${int(ityp)};')
+ for v in sum_info.variants {
+ g.writeln('\t\tcase $v: return ${int(v)};')
+ }
+ g.writeln('\t\tdefault: return ${int(ityp)};')
+ g.writeln('\t}')
+ }
+ g.writeln('}')
+ } else if typ.kind == .interface_ {
+ inter_info := typ.info as ast.Interface
+ if inter_info.is_generic {
+ continue
+ }
+ g.definitions.writeln('static char * v_typeof_interface_${typ.cname}(int sidx);')
+ g.writeln('static char * v_typeof_interface_${typ.cname}(int sidx) { /* $typ.name */ ')
+ for t in inter_info.types {
+ subtype := g.table.get_type_symbol(t)
+ g.writeln('\tif (sidx == _${typ.cname}_${subtype.cname}_index) return "${util.strip_main_name(subtype.name)}";')
+ }
+ g.writeln('\treturn "unknown ${util.strip_main_name(typ.name)}";')
+ g.writeln('}')
+ g.writeln('')
+ g.writeln('static int v_typeof_interface_idx_${typ.cname}(int sidx) { /* $typ.name */ ')
+ for t in inter_info.types {
+ subtype := g.table.get_type_symbol(t)
+ g.writeln('\tif (sidx == _${typ.cname}_${subtype.cname}_index) return ${int(t)};')
+ }
+ g.writeln('\treturn ${int(ityp)};')
+ g.writeln('}')
+ }
+ }
+ g.writeln('// << typeof() support for sum types')
+ g.writeln('')
+}
+
+// V type to C typecc
+fn (mut g Gen) typ(t ast.Type) string {
+ styp := g.base_type(t)
+ if t.has_flag(.optional) {
+ // Register an optional if it's not registered yet
+ return g.register_optional(t)
+ }
+ /*
+ if styp.starts_with('C__') {
+ return styp[3..]
+ }
+ */
+ return styp
+}
+
+fn (mut g Gen) base_type(t ast.Type) string {
+ share := t.share()
+ mut styp := if share == .atomic_t { t.atomic_typename() } else { g.cc_type(t, true) }
+ if t.has_flag(.shared_f) {
+ styp = g.find_or_register_shared(t, styp)
+ }
+ if !t.has_flag(.variadic) {
+ nr_muls := g.unwrap_generic(t).nr_muls()
+ if nr_muls > 0 {
+ styp += strings.repeat(`*`, nr_muls)
+ }
+ }
+ return styp
+}
+
+fn (mut g Gen) generic_fn_name(types []ast.Type, before string, is_decl bool) string {
+ if types.len == 0 {
+ return before
+ }
+ // Using _T_ to differentiate between get<string> and get_string
+ // `foo<int>()` => `foo_T_int()`
+ mut name := before + '_T'
+ for typ in types {
+ name += '_' + strings.repeat_string('__ptr__', typ.nr_muls()) + g.typ(typ.set_nr_muls(0))
+ }
+ return name
+}
+
+fn (mut g Gen) expr_string(expr ast.Expr) string {
+ pos := g.out.len
+ g.expr(expr)
+ return g.out.cut_to(pos).trim_space()
+}
+
+// Surround a potentially multi-statement expression safely with `prepend` and `append`.
+// (and create a statement)
+fn (mut g Gen) expr_string_surround(prepend string, expr ast.Expr, append string) string {
+ pos := g.out.len
+ g.stmt_path_pos << pos
+ defer {
+ g.stmt_path_pos.delete_last()
+ }
+ g.write(prepend)
+ g.expr(expr)
+ g.write(append)
+ return g.out.cut_to(pos)
+}
+
+// TODO this really shouldnt be seperate from typ
+// but I(emily) would rather have this generation
+// all unified in one place so that it doesnt break
+// if one location changes
+fn (mut g Gen) optional_type_name(t ast.Type) (string, string) {
+ base := g.base_type(t)
+ mut styp := 'Option_$base'
+ if t.is_ptr() {
+ styp = styp.replace('*', '_ptr')
+ }
+ return styp, base
+}
+
+fn (g &Gen) optional_type_text(styp string, base string) string {
+ // replace void with something else
+ size := if base == 'void' { 'byte' } else { base }
+ ret := 'struct $styp {
+ byte state;
+ IError err;
+ byte data[sizeof($size)];
+}'
+ return ret
+}
+
+fn (mut g Gen) register_optional(t ast.Type) string {
+ styp, base := g.optional_type_name(t)
+ if styp !in g.optionals {
+ g.typedefs2.writeln('typedef struct $styp $styp;')
+ g.options.write_string(g.optional_type_text(styp, base))
+ g.options.writeln(';\n')
+ g.optionals << styp.clone()
+ }
+ return styp
+}
+
+fn (mut g Gen) find_or_register_shared(t ast.Type, base string) string {
+ sh_typ := '__shared__$base'
+ t_idx := t.idx()
+ if t_idx in g.shareds {
+ return sh_typ
+ }
+ mtx_typ := 'sync__RwMutex'
+ g.shared_types.writeln('struct $sh_typ { $base val; $mtx_typ mtx; };')
+ g.shared_functions.writeln('static inline voidptr __dup${sh_typ}(voidptr src, int sz) {')
+ g.shared_functions.writeln('\t$sh_typ* dest = memdup(src, sz);')
+ g.shared_functions.writeln('\tsync__RwMutex_init(&dest->mtx);')
+ g.shared_functions.writeln('\treturn dest;')
+ g.shared_functions.writeln('}')
+ g.typedefs2.writeln('typedef struct $sh_typ $sh_typ;')
+ // println('registered shared type $sh_typ')
+ g.shareds << t_idx
+ return sh_typ
+}
+
+fn (mut g Gen) register_thread_array_wait_call(eltyp string) string {
+ is_void := eltyp == 'void'
+ thread_typ := if is_void { '__v_thread' } else { '__v_thread_$eltyp' }
+ ret_typ := if is_void { 'void' } else { 'Array_$eltyp' }
+ thread_arr_typ := 'Array_$thread_typ'
+ fn_name := '${thread_arr_typ}_wait'
+ if fn_name !in g.waiter_fns {
+ g.waiter_fns << fn_name
+ if is_void {
+ g.gowrappers.writeln('
+void ${fn_name}($thread_arr_typ a) {
+ for (int i = 0; i < a.len; ++i) {
+ $thread_typ t = (($thread_typ*)a.data)[i];
+ __v_thread_wait(t);
+ }
+}')
+ } else {
+ g.gowrappers.writeln('
+$ret_typ ${fn_name}($thread_arr_typ a) {
+ $ret_typ res = __new_array_with_default(a.len, a.len, sizeof($eltyp), 0);
+ for (int i = 0; i < a.len; ++i) {
+ $thread_typ t = (($thread_typ*)a.data)[i];
+ (($eltyp*)res.data)[i] = __v_thread_${eltyp}_wait(t);
+ }
+ return res;
+}')
+ }
+ }
+ return fn_name
+}
+
+fn (mut g Gen) register_chan_pop_optional_call(opt_el_type string, styp string) {
+ if opt_el_type !in g.chan_pop_optionals {
+ g.chan_pop_optionals << opt_el_type
+ g.channel_definitions.writeln('
+static inline $opt_el_type __Option_${styp}_popval($styp ch) {
+ $opt_el_type _tmp = {0};
+ if (sync__Channel_try_pop_priv(ch, _tmp.data, false)) {
+ return ($opt_el_type){ .state = 2, .err = _v_error(_SLIT("channel closed")), .data = {EMPTY_STRUCT_INITIALIZATION} };
+ }
+ return _tmp;
+}')
+ }
+}
+
+fn (mut g Gen) register_chan_push_optional_call(el_type string, styp string) {
+ if styp !in g.chan_push_optionals {
+ g.chan_push_optionals << styp
+ g.register_optional(ast.void_type.set_flag(.optional))
+ g.channel_definitions.writeln('
+static inline Option_void __Option_${styp}_pushval($styp ch, $el_type e) {
+ if (sync__Channel_try_push_priv(ch, &e, false)) {
+ return (Option_void){ .state = 2, .err = _v_error(_SLIT("channel closed")), .data = {EMPTY_STRUCT_INITIALIZATION} };
+ }
+ return (Option_void){0};
+}')
+ }
+}
+
+// cc_type whether to prefix 'struct' or not (C__Foo -> struct Foo)
+fn (mut g Gen) cc_type(typ ast.Type, is_prefix_struct bool) string {
+ sym := g.table.get_type_symbol(g.unwrap_generic(typ))
+ mut styp := sym.cname
+ // TODO: this needs to be removed; cgen shouldn't resolve generic types (job of checker)
+ match mut sym.info {
+ ast.Struct, ast.Interface, ast.SumType {
+ if sym.info.is_generic {
+ mut sgtyps := '_T'
+ for gt in sym.info.generic_types {
+ gts := g.table.get_type_symbol(g.unwrap_generic(gt))
+ sgtyps += '_$gts.cname'
+ }
+ styp += sgtyps
+ }
+ }
+ ast.MultiReturn {
+ // TODO: this doesn't belong here, but makes it working for now
+ mut cname := 'multi_return'
+ for mr_typ in sym.info.types {
+ mr_type_sym := g.table.get_type_symbol(g.unwrap_generic(mr_typ))
+ cname += '_$mr_type_sym.cname'
+ }
+ return cname
+ }
+ else {}
+ }
+ if is_prefix_struct && styp.starts_with('C__') {
+ styp = styp[3..]
+ if sym.kind == .struct_ {
+ info := sym.info as ast.Struct
+ if !info.is_typedef {
+ styp = 'struct $styp'
+ }
+ }
+ }
+ return styp
+}
+
+[inline]
+fn (g &Gen) type_sidx(t ast.Type) string {
+ if g.pref.build_mode == .build_module {
+ sym := g.table.get_type_symbol(t)
+ return '_v_type_idx_${sym.cname}()'
+ }
+ return '$t.idx()'
+}
+
+//
+pub fn (mut g Gen) write_typedef_types() {
+ for typ in g.table.type_symbols {
+ if typ.name in c.builtins {
+ continue
+ }
+ match typ.kind {
+ .array {
+ info := typ.info as ast.Array
+ elem_sym := g.table.get_type_symbol(info.elem_type)
+ if elem_sym.kind != .placeholder {
+ g.type_definitions.writeln('typedef array $typ.cname;')
+ }
+ }
+ .array_fixed {
+ info := typ.info as ast.ArrayFixed
+ elem_sym := g.table.get_type_symbol(info.elem_type)
+ if elem_sym.is_builtin() {
+ // .array_fixed {
+ styp := typ.cname
+ // array_fixed_char_300 => char x[300]
+ mut fixed := styp[12..]
+ len := styp.after('_')
+ fixed = fixed[..fixed.len - len.len - 1]
+ if fixed.starts_with('C__') {
+ fixed = fixed[3..]
+ }
+ if elem_sym.info is ast.FnType {
+ pos := g.out.len
+ g.write_fn_ptr_decl(&elem_sym.info, '')
+ fixed = g.out.cut_to(pos)
+ mut def_str := 'typedef $fixed;'
+ def_str = def_str.replace_once('(*)', '(*$styp[$len])')
+ g.type_definitions.writeln(def_str)
+ } else {
+ g.type_definitions.writeln('typedef $fixed $styp [$len];')
+ }
+ }
+ }
+ .chan {
+ if typ.name != 'chan' {
+ g.type_definitions.writeln('typedef chan $typ.cname;')
+ chan_inf := typ.chan_info()
+ chan_elem_type := chan_inf.elem_type
+ if !chan_elem_type.has_flag(.generic) {
+ el_stype := g.typ(chan_elem_type)
+ g.channel_definitions.writeln('
+static inline $el_stype __${typ.cname}_popval($typ.cname ch) {
+ $el_stype val;
+ sync__Channel_try_pop_priv(ch, &val, false);
+ return val;
+}')
+ g.channel_definitions.writeln('
+static inline void __${typ.cname}_pushval($typ.cname ch, $el_stype val) {
+ sync__Channel_try_push_priv(ch, &val, false);
+}')
+ }
+ }
+ }
+ .map {
+ g.type_definitions.writeln('typedef map $typ.cname;')
+ }
+ else {
+ continue
+ }
+ }
+ }
+ for typ in g.table.type_symbols {
+ if typ.kind == .alias && typ.name !in c.builtins {
+ g.write_alias_typesymbol_declaration(typ)
+ }
+ }
+ for typ in g.table.type_symbols {
+ if typ.kind == .function && typ.name !in c.builtins {
+ g.write_fn_typesymbol_declaration(typ)
+ }
+ }
+ // Generating interfaces after all the common types have been defined
+ // to prevent generating interface struct before definition of field types
+ for typ in g.table.type_symbols {
+ if typ.kind == .interface_ && typ.name !in c.builtins {
+ g.write_interface_typedef(typ)
+ }
+ }
+ for typ in g.table.type_symbols {
+ if typ.kind == .interface_ && typ.name !in c.builtins {
+ g.write_interface_typesymbol_declaration(typ)
+ }
+ }
+}
+
+pub fn (mut g Gen) write_alias_typesymbol_declaration(sym ast.TypeSymbol) {
+ parent := unsafe { &g.table.type_symbols[sym.parent_idx] }
+ is_c_parent := parent.name.len > 2 && parent.name[0] == `C` && parent.name[1] == `.`
+ mut is_typedef := false
+ if parent.info is ast.Struct {
+ is_typedef = parent.info.is_typedef
+ }
+ mut parent_styp := parent.cname
+ if is_c_parent {
+ if !is_typedef {
+ parent_styp = 'struct ' + parent.cname[3..]
+ } else {
+ parent_styp = parent.cname[3..]
+ }
+ } else {
+ if sym.info is ast.Alias {
+ parent_styp = g.typ(sym.info.parent_type)
+ }
+ }
+ if parent_styp == 'byte' && sym.cname == 'u8' {
+ // TODO: remove this check; it is here just to fix V rebuilding in -cstrict mode with clang-12
+ return
+ }
+ g.type_definitions.writeln('typedef $parent_styp $sym.cname;')
+}
+
+pub fn (mut g Gen) write_interface_typedef(sym ast.TypeSymbol) {
+ struct_name := c_name(sym.cname)
+ g.typedefs.writeln('typedef struct $struct_name $struct_name;')
+}
+
+pub fn (mut g Gen) write_interface_typesymbol_declaration(sym ast.TypeSymbol) {
+ info := sym.info as ast.Interface
+ if info.is_generic {
+ return
+ }
+ struct_name := c_name(sym.cname)
+ g.type_definitions.writeln('struct $struct_name {')
+ g.type_definitions.writeln('\tunion {')
+ g.type_definitions.writeln('\t\tvoid* _object;')
+ for variant in info.types {
+ vcname := g.table.get_type_symbol(variant).cname
+ g.type_definitions.writeln('\t\t$vcname* _$vcname;')
+ }
+ g.type_definitions.writeln('\t};')
+ g.type_definitions.writeln('\tint _typ;')
+ for field in info.fields {
+ styp := g.typ(field.typ)
+ cname := c_name(field.name)
+ g.type_definitions.writeln('\t$styp* $cname;')
+ }
+ g.type_definitions.writeln('};')
+}
+
+pub fn (mut g Gen) write_fn_typesymbol_declaration(sym ast.TypeSymbol) {
+ info := sym.info as ast.FnType
+ func := info.func
+ is_fn_sig := func.name == ''
+ not_anon := !info.is_anon
+ mut has_generic_arg := false
+ for param in func.params {
+ if param.typ.has_flag(.generic) {
+ has_generic_arg = true
+ break
+ }
+ }
+ if !info.has_decl && (not_anon || is_fn_sig) && !func.return_type.has_flag(.generic)
+ && !has_generic_arg {
+ fn_name := sym.cname
+ g.type_definitions.write_string('typedef ${g.typ(func.return_type)} (*$fn_name)(')
+ for i, param in func.params {
+ g.type_definitions.write_string(g.typ(param.typ))
+ if i < func.params.len - 1 {
+ g.type_definitions.write_string(',')
+ }
+ }
+ g.type_definitions.writeln(');')
+ }
+}
+
+pub fn (mut g Gen) write_multi_return_types() {
+ g.typedefs.writeln('\n// BEGIN_multi_return_typedefs')
+ g.type_definitions.writeln('\n// BEGIN_multi_return_structs')
+ for sym in g.table.type_symbols {
+ if sym.kind != .multi_return {
+ continue
+ }
+ info := sym.mr_info()
+ if info.types.filter(it.has_flag(.generic)).len > 0 {
+ continue
+ }
+ g.typedefs.writeln('typedef struct $sym.cname $sym.cname;')
+ g.type_definitions.writeln('struct $sym.cname {')
+ for i, mr_typ in info.types {
+ type_name := g.typ(mr_typ)
+ g.type_definitions.writeln('\t$type_name arg$i;')
+ }
+ g.type_definitions.writeln('};\n')
+ }
+ g.typedefs.writeln('// END_multi_return_typedefs\n')
+ g.type_definitions.writeln('// END_multi_return_structs\n')
+}
+
+pub fn (mut g Gen) write(s string) {
+ $if trace_gen ? {
+ eprintln('gen file: ${g.file.path:-30} | last_fn_c_name: ${g.last_fn_c_name:-45} | write: $s')
+ }
+ if g.indent > 0 && g.empty_line {
+ g.out.write_string(util.tabs(g.indent))
+ }
+ g.out.write_string(s)
+ g.empty_line = false
+}
+
+pub fn (mut g Gen) writeln(s string) {
+ $if trace_gen ? {
+ eprintln('gen file: ${g.file.path:-30} | last_fn_c_name: ${g.last_fn_c_name:-45} | writeln: $s')
+ }
+ if g.indent > 0 && g.empty_line {
+ g.out.write_string(util.tabs(g.indent))
+ }
+ g.out.writeln(s)
+ g.empty_line = true
+}
+
+pub fn (mut g Gen) new_tmp_var() string {
+ g.tmp_count++
+ return '_t$g.tmp_count'
+}
+
+pub fn (mut g Gen) new_global_tmp_var() string {
+ g.global_tmp_count++
+ return '_t$g.global_tmp_count'
+}
+
+pub fn (mut g Gen) new_tmp_declaration_name() string {
+ g.tmp_count_declarations++
+ return '_d$g.tmp_count_declarations'
+}
+
+pub fn (mut g Gen) current_tmp_var() string {
+ return '_t$g.tmp_count'
+}
+
+/*
+pub fn (mut g Gen) new_tmp_var2() string {
+ g.tmp_count2++
+ return '_tt$g.tmp_count2'
+}
+*/
+pub fn (mut g Gen) reset_tmp_count() {
+ g.tmp_count = 0
+}
+
+fn (mut g Gen) decrement_inside_ternary() {
+ key := g.inside_ternary.str()
+ for name in g.ternary_level_names[key] {
+ g.ternary_names.delete(name)
+ }
+ g.ternary_level_names.delete(key)
+ g.inside_ternary--
+}
+
+fn (mut g Gen) stmts(stmts []ast.Stmt) {
+ g.stmts_with_tmp_var(stmts, '')
+}
+
+fn is_noreturn_callexpr(expr ast.Expr) bool {
+ if expr is ast.CallExpr {
+ return expr.is_noreturn
+ }
+ return false
+}
+
+// tmp_var is used in `if` expressions only
+fn (mut g Gen) stmts_with_tmp_var(stmts []ast.Stmt, tmp_var string) {
+ g.indent++
+ if g.inside_ternary > 0 {
+ g.write('(')
+ }
+ for i, stmt in stmts {
+ if i == stmts.len - 1 && tmp_var != '' {
+ // Handle if expressions, set the value of the last expression to the temp var.
+ if g.inside_if_optional {
+ g.stmt_path_pos << g.out.len
+ g.skip_stmt_pos = true
+ if stmt is ast.ExprStmt {
+ if stmt.typ == ast.error_type_idx || stmt.expr is ast.None {
+ g.writeln('${tmp_var}.state = 2;')
+ g.write('${tmp_var}.err = ')
+ g.expr(stmt.expr)
+ g.writeln(';')
+ } else {
+ mut styp := g.base_type(stmt.typ)
+ $if tinyc && x32 && windows {
+ if stmt.typ == ast.int_literal_type {
+ styp = 'int'
+ } else if stmt.typ == ast.float_literal_type {
+ styp = 'f64'
+ }
+ }
+ g.write('opt_ok(&($styp[]) { ')
+ g.stmt(stmt)
+ g.writeln(' }, (Option*)(&$tmp_var), sizeof($styp));')
+ }
+ }
+ } else {
+ g.stmt_path_pos << g.out.len
+ g.skip_stmt_pos = true
+ mut is_noreturn := false
+ if stmt is ast.ExprStmt {
+ is_noreturn = is_noreturn_callexpr(stmt.expr)
+ }
+ if !is_noreturn {
+ g.write('$tmp_var = ')
+ }
+ g.stmt(stmt)
+ if !g.out.last_n(2).contains(';') {
+ g.writeln(';')
+ }
+ }
+ } else {
+ g.stmt(stmt)
+ if g.inside_if_optional && stmt is ast.ExprStmt {
+ g.writeln(';')
+ }
+ }
+ g.skip_stmt_pos = false
+ if g.inside_ternary > 0 && i < stmts.len - 1 {
+ g.write(',')
+ }
+ }
+ g.indent--
+ if g.inside_ternary > 0 {
+ g.write('')
+ g.write(')')
+ }
+ if g.is_autofree && !g.inside_vweb_tmpl && stmts.len > 0 {
+ // use the first stmt to get the scope
+ stmt := stmts[0]
+ // stmt := stmts[stmts.len-1]
+ if stmt !is ast.FnDecl && g.inside_ternary == 0 {
+ // g.trace_autofree('// autofree scope')
+ // g.trace_autofree('// autofree_scope_vars($stmt.pos.pos) | ${typeof(stmt)}')
+ // go back 1 position is important so we dont get the
+ // internal scope of for loops and possibly other nodes
+ // g.autofree_scope_vars(stmt.pos.pos - 1)
+ mut stmt_pos := stmt.pos
+ if stmt_pos.pos == 0 {
+ // Do not autofree if the position is 0, since the correct scope won't be found.
+ // Report a bug, since position shouldn't be 0 for most nodes.
+ if stmt is ast.Module {
+ return
+ }
+ if stmt is ast.ExprStmt {
+ // For some reason ExprStmt.pos is 0 when ExprStmt.expr is comp if expr
+ // Extract the pos. TODO figure out why and fix.
+ stmt_pos = stmt.expr.position()
+ }
+ if stmt_pos.pos == 0 {
+ $if trace_autofree ? {
+ println('autofree: first stmt pos = 0. $stmt.type_name()')
+ }
+ return
+ }
+ }
+ g.autofree_scope_vars(stmt_pos.pos - 1, stmt_pos.line_nr, false)
+ }
+ }
+}
+
+[inline]
+fn (mut g Gen) write_v_source_line_info(pos token.Position) {
+ if g.inside_ternary == 0 && g.pref.is_vlines && g.is_vlines_enabled {
+ nline := pos.line_nr + 1
+ lineinfo := '\n#line $nline "$g.vlines_path"'
+ g.writeln(lineinfo)
+ }
+}
+
+fn (mut g Gen) stmt(node ast.Stmt) {
+ if !g.skip_stmt_pos {
+ g.stmt_path_pos << g.out.len
+ }
+ defer {
+ }
+ // println('g.stmt()')
+ // g.writeln('//// stmt start')
+ match node {
+ ast.EmptyStmt {}
+ ast.AsmStmt {
+ g.write_v_source_line_info(node.pos)
+ g.gen_asm_stmt(node)
+ }
+ ast.AssertStmt {
+ g.write_v_source_line_info(node.pos)
+ g.gen_assert_stmt(node)
+ }
+ ast.AssignStmt {
+ g.write_v_source_line_info(node.pos)
+ g.gen_assign_stmt(node)
+ }
+ ast.Block {
+ g.write_v_source_line_info(node.pos)
+ if node.is_unsafe {
+ g.writeln('{ // Unsafe block')
+ } else {
+ g.writeln('{')
+ }
+ g.stmts(node.stmts)
+ g.writeln('}')
+ }
+ ast.BranchStmt {
+ g.write_v_source_line_info(node.pos)
+
+ if node.label != '' {
+ x := g.labeled_loops[node.label] or {
+ panic('$node.label doesn\'t exist $g.file.path, $node.pos')
+ }
+ match x {
+ ast.ForCStmt {
+ if x.scope.contains(g.cur_lock.pos.pos) {
+ g.unlock_locks()
+ }
+ }
+ ast.ForInStmt {
+ if x.scope.contains(g.cur_lock.pos.pos) {
+ g.unlock_locks()
+ }
+ }
+ ast.ForStmt {
+ if x.scope.contains(g.cur_lock.pos.pos) {
+ g.unlock_locks()
+ }
+ }
+ else {}
+ }
+
+ if node.kind == .key_break {
+ g.writeln('goto ${node.label}__break;')
+ } else {
+ // assert node.kind == .key_continue
+ g.writeln('goto ${node.label}__continue;')
+ }
+ } else {
+ inner_loop := g.inner_loop
+ match inner_loop {
+ ast.ForCStmt {
+ if inner_loop.scope.contains(g.cur_lock.pos.pos) {
+ g.unlock_locks()
+ }
+ }
+ ast.ForInStmt {
+ if inner_loop.scope.contains(g.cur_lock.pos.pos) {
+ g.unlock_locks()
+ }
+ }
+ ast.ForStmt {
+ if inner_loop.scope.contains(g.cur_lock.pos.pos) {
+ g.unlock_locks()
+ }
+ }
+ else {}
+ }
+ // continue or break
+ if g.is_autofree && !g.is_builtin_mod {
+ g.trace_autofree('// free before continue/break')
+ g.autofree_scope_vars_stop(node.pos.pos - 1, node.pos.line_nr, true,
+ g.branch_parent_pos)
+ }
+ g.writeln('$node.kind;')
+ }
+ }
+ ast.ConstDecl {
+ g.write_v_source_line_info(node.pos)
+ // if g.pref.build_mode != .build_module {
+ g.const_decl(node)
+ // }
+ }
+ ast.CompFor {
+ g.comp_for(node)
+ }
+ ast.DeferStmt {
+ mut defer_stmt := node
+ defer_stmt.ifdef = g.defer_ifdef
+ g.writeln('${g.defer_flag_var(defer_stmt)} = true;')
+ g.defer_stmts << defer_stmt
+ }
+ ast.EnumDecl {
+ enum_name := util.no_dots(node.name)
+ is_flag := node.is_flag
+ g.enum_typedefs.writeln('typedef enum {')
+ mut cur_enum_expr := ''
+ mut cur_enum_offset := 0
+ for i, field in node.fields {
+ g.enum_typedefs.write_string('\t${enum_name}__$field.name')
+ if field.has_expr {
+ g.enum_typedefs.write_string(' = ')
+ expr_str := g.expr_string(field.expr)
+ g.enum_typedefs.write_string(expr_str)
+ cur_enum_expr = expr_str
+ cur_enum_offset = 0
+ } else if is_flag {
+ g.enum_typedefs.write_string(' = ')
+ cur_enum_expr = '1 << $i'
+ g.enum_typedefs.write_string((1 << i).str())
+ cur_enum_offset = 0
+ }
+ cur_value := if cur_enum_offset > 0 {
+ '$cur_enum_expr+$cur_enum_offset'
+ } else {
+ cur_enum_expr
+ }
+ g.enum_typedefs.writeln(', // $cur_value')
+ cur_enum_offset++
+ }
+ g.enum_typedefs.writeln('} $enum_name;\n')
+ }
+ ast.ExprStmt {
+ g.write_v_source_line_info(node.pos)
+ // af := g.autofree && node.expr is ast.CallExpr && !g.is_builtin_mod
+ // if af {
+ // g.autofree_call_pregen(node.expr as ast.CallExpr)
+ // }
+ old_is_void_expr_stmt := g.is_void_expr_stmt
+ g.is_void_expr_stmt = !node.is_expr
+ if node.typ != ast.void_type && g.expected_cast_type != 0 {
+ g.expr_with_cast(node.expr, node.typ, g.expected_cast_type)
+ } else {
+ g.expr(node.expr)
+ }
+ g.is_void_expr_stmt = old_is_void_expr_stmt
+ // if af {
+ // g.autofree_call_postgen()
+ // }
+ if g.inside_ternary == 0 && !g.inside_if_optional && !node.is_expr
+ && node.expr !is ast.IfExpr {
+ g.writeln(';')
+ }
+ }
+ ast.FnDecl {
+ g.process_fn_decl(node)
+ }
+ ast.ForCStmt {
+ prev_branch_parent_pos := g.branch_parent_pos
+ g.branch_parent_pos = node.pos.pos
+ save_inner_loop := g.inner_loop
+ g.inner_loop = unsafe { &node }
+ if node.label != '' {
+ g.labeled_loops[node.label] = unsafe { &node }
+ }
+ g.write_v_source_line_info(node.pos)
+ g.for_c_stmt(node)
+ g.branch_parent_pos = prev_branch_parent_pos
+ g.labeled_loops.delete(node.label)
+ g.inner_loop = save_inner_loop
+ }
+ ast.ForInStmt {
+ prev_branch_parent_pos := g.branch_parent_pos
+ g.branch_parent_pos = node.pos.pos
+ save_inner_loop := g.inner_loop
+ g.inner_loop = unsafe { &node }
+ if node.label != '' {
+ g.labeled_loops[node.label] = unsafe { &node }
+ }
+ g.write_v_source_line_info(node.pos)
+ g.for_in_stmt(node)
+ g.branch_parent_pos = prev_branch_parent_pos
+ g.labeled_loops.delete(node.label)
+ g.inner_loop = save_inner_loop
+ }
+ ast.ForStmt {
+ prev_branch_parent_pos := g.branch_parent_pos
+ g.branch_parent_pos = node.pos.pos
+ save_inner_loop := g.inner_loop
+ g.inner_loop = unsafe { &node }
+ if node.label != '' {
+ g.labeled_loops[node.label] = unsafe { &node }
+ }
+ g.write_v_source_line_info(node.pos)
+ g.for_stmt(node)
+ g.branch_parent_pos = prev_branch_parent_pos
+ g.labeled_loops.delete(node.label)
+ g.inner_loop = save_inner_loop
+ }
+ ast.GlobalDecl {
+ g.global_decl(node)
+ }
+ ast.GotoLabel {
+ g.writeln('$node.name: {}')
+ }
+ ast.GotoStmt {
+ g.write_v_source_line_info(node.pos)
+ g.writeln('goto $node.name;')
+ }
+ ast.HashStmt {
+ mut ct_condition := ''
+ if node.ct_conds.len > 0 {
+ ct_condition_start := g.out.len
+ for idx, ct_expr in node.ct_conds {
+ g.comp_if_cond(ct_expr, false)
+ if idx < node.ct_conds.len - 1 {
+ g.write(' && ')
+ }
+ }
+ ct_condition = g.out.cut_to(ct_condition_start).trim_space()
+ // dump(node)
+ // dump(ct_condition)
+ }
+ // #include etc
+ if node.kind == 'include' {
+ mut missing_message := 'Header file $node.main, needed for module `$node.mod` was not found.'
+ if node.msg != '' {
+ missing_message += ' ${node.msg}.'
+ } else {
+ missing_message += ' Please install the corresponding development headers.'
+ }
+ mut guarded_include := get_guarded_include_text(node.main, missing_message)
+ if node.main == '<errno.h>' {
+ // fails with musl-gcc and msvc; but an unguarded include works:
+ guarded_include = '#include $node.main'
+ }
+ if node.main.contains('.m') {
+ g.definitions.writeln('\n')
+ if ct_condition.len > 0 {
+ g.definitions.writeln('#if $ct_condition')
+ }
+ // Objective C code import, include it after V types, so that e.g. `string` is
+ // available there
+ g.definitions.writeln('// added by module `$node.mod`')
+ g.definitions.writeln(guarded_include)
+ if ct_condition.len > 0 {
+ g.definitions.writeln('#endif // \$if $ct_condition')
+ }
+ g.definitions.writeln('\n')
+ } else {
+ g.includes.writeln('\n')
+ if ct_condition.len > 0 {
+ g.includes.writeln('#if $ct_condition')
+ }
+ g.includes.writeln('// added by module `$node.mod`')
+ g.includes.writeln(guarded_include)
+ if ct_condition.len > 0 {
+ g.includes.writeln('#endif // \$if $ct_condition')
+ }
+ g.includes.writeln('\n')
+ }
+ } else if node.kind == 'define' {
+ if ct_condition.len > 0 {
+ g.includes.writeln('#if $ct_condition')
+ }
+ g.includes.writeln('// defined by module `$node.mod`')
+ g.includes.writeln('#define $node.main')
+ if ct_condition.len > 0 {
+ g.includes.writeln('#endif // \$if $ct_condition')
+ }
+ }
+ }
+ ast.Import {}
+ ast.InterfaceDecl {
+ // definitions are sorted and added in write_types
+ ts := g.table.get_type_symbol(node.typ)
+ if !(ts.info as ast.Interface).is_generic {
+ for method in node.methods {
+ if method.return_type.has_flag(.optional) {
+ // Register an optional if it's not registered yet
+ g.register_optional(method.return_type)
+ }
+ }
+ }
+ }
+ ast.Module {
+ // g.is_builtin_mod = node.name == 'builtin'
+ g.is_builtin_mod = node.name in ['builtin', 'os', 'strconv', 'strings', 'gg']
+ // g.cur_mod = node.name
+ g.cur_mod = node
+ }
+ ast.NodeError {}
+ ast.Return {
+ g.return_stmt(node)
+ }
+ ast.SqlStmt {
+ g.sql_stmt(node)
+ }
+ ast.StructDecl {
+ name := if node.language == .c { util.no_dots(node.name) } else { c_name(node.name) }
+ // TODO For some reason, build fails with autofree with this line
+ // as it's only informative, comment it for now
+ // g.gen_attrs(node.attrs)
+ // g.writeln('typedef struct {')
+ // for field in it.fields {
+ // field_type_sym := g.table.get_type_symbol(field.typ)
+ // g.writeln('\t$field_type_sym.name $field.name;')
+ // }
+ // g.writeln('} $name;')
+ if node.language == .c {
+ return
+ }
+ if node.is_union {
+ g.typedefs.writeln('typedef union $name $name;')
+ } else {
+ /*
+ attrs := if node.attrs.contains('packed') {
+ '__attribute__((__packed__))'
+ } else {
+ ''
+ }
+ */
+ g.typedefs.writeln('typedef struct $name $name;')
+ }
+ }
+ ast.TypeDecl {
+ if !g.pref.skip_unused {
+ g.writeln('// TypeDecl')
+ }
+ }
+ }
+ if !g.skip_stmt_pos { // && g.stmt_path_pos.len > 0 {
+ g.stmt_path_pos.delete_last()
+ }
+ // If we have temporary string exprs to free after this statement, do it. e.g.:
+ // `foo('a' + 'b')` => `tmp := 'a' + 'b'; foo(tmp); string_free(&tmp);`
+ if g.is_autofree {
+ // if node is ast.ExprStmt {&& node.expr is ast.CallExpr {
+ if node !is ast.FnDecl {
+ // p := node.position()
+ // g.autofree_call_postgen(p.pos)
+ }
+ }
+}
+
+fn (mut g Gen) write_defer_stmts() {
+ for i := g.defer_stmts.len - 1; i >= 0; i-- {
+ defer_stmt := g.defer_stmts[i]
+ g.writeln('// Defer begin')
+ g.writeln('if (${g.defer_flag_var(defer_stmt)}) {')
+ g.indent++
+ if defer_stmt.ifdef.len > 0 {
+ g.writeln(defer_stmt.ifdef)
+ g.stmts(defer_stmt.stmts)
+ g.writeln('')
+ g.writeln('#endif')
+ } else {
+ g.indent--
+ g.stmts(defer_stmt.stmts)
+ g.indent++
+ }
+ g.indent--
+ g.writeln('}')
+ g.writeln('// Defer end')
+ }
+}
+
+fn (mut g Gen) for_c_stmt(node ast.ForCStmt) {
+ if node.is_multi {
+ g.is_vlines_enabled = false
+ if node.label.len > 0 {
+ g.writeln('$node.label:')
+ }
+ g.writeln('{')
+ g.indent++
+ if node.has_init {
+ g.stmt(node.init)
+ }
+ g.writeln('bool _is_first = true;')
+ g.writeln('while (true) {')
+ g.writeln('\tif (_is_first) {')
+ g.writeln('\t\t_is_first = false;')
+ g.writeln('\t} else {')
+ if node.has_inc {
+ g.indent++
+ g.stmt(node.inc)
+ g.writeln(';')
+ g.indent--
+ }
+ g.writeln('}')
+ if node.has_cond {
+ g.write('if (!(')
+ g.expr(node.cond)
+ g.writeln(')) break;')
+ }
+ g.is_vlines_enabled = true
+ g.stmts(node.stmts)
+ if node.label.len > 0 {
+ g.writeln('${node.label}__continue: {}')
+ }
+ g.writeln('}')
+ g.indent--
+ g.writeln('}')
+ if node.label.len > 0 {
+ g.writeln('${node.label}__break: {}')
+ }
+ } else {
+ g.is_vlines_enabled = false
+ if node.label.len > 0 {
+ g.writeln('$node.label:')
+ }
+ g.write('for (')
+ if !node.has_init {
+ g.write('; ')
+ } else {
+ g.stmt(node.init)
+ // Remove excess return and add space
+ if g.out.last_n(1) == '\n' {
+ g.out.go_back(1)
+ g.empty_line = false
+ g.write(' ')
+ }
+ }
+ if node.has_cond {
+ g.expr(node.cond)
+ }
+ g.write('; ')
+ if node.has_inc {
+ g.stmt(node.inc)
+ }
+ g.writeln(') {')
+ g.is_vlines_enabled = true
+ g.stmts(node.stmts)
+ if node.label.len > 0 {
+ g.writeln('${node.label}__continue: {}')
+ }
+ g.writeln('}')
+ if node.label.len > 0 {
+ g.writeln('${node.label}__break: {}')
+ }
+ }
+}
+
+fn (mut g Gen) for_stmt(node ast.ForStmt) {
+ g.is_vlines_enabled = false
+ if node.label.len > 0 {
+ g.writeln('$node.label:')
+ }
+ g.writeln('for (;;) {')
+ if !node.is_inf {
+ g.indent++
+ g.stmt_path_pos << g.out.len
+ g.write('if (!(')
+ g.expr(node.cond)
+ g.writeln(')) break;')
+ g.indent--
+ }
+ g.is_vlines_enabled = true
+ g.stmts(node.stmts)
+ if node.label.len > 0 {
+ g.writeln('\t${node.label}__continue: {}')
+ }
+ g.writeln('}')
+ if node.label.len > 0 {
+ g.writeln('${node.label}__break: {}')
+ }
+}
+
+fn (mut g Gen) for_in_stmt(node ast.ForInStmt) {
+ if node.label.len > 0 {
+ g.writeln('\t$node.label: {}')
+ }
+ if node.is_range {
+ // `for x in 1..10 {`
+ i := if node.val_var == '_' { g.new_tmp_var() } else { c_name(node.val_var) }
+ val_typ := g.table.mktyp(node.val_type)
+ g.write('for (${g.typ(val_typ)} $i = ')
+ g.expr(node.cond)
+ g.write('; $i < ')
+ g.expr(node.high)
+ g.writeln('; ++$i) {')
+ } else if node.kind == .array {
+ // `for num in nums {`
+ // g.writeln('// FOR IN array')
+ styp := g.typ(node.val_type)
+ val_sym := g.table.get_type_symbol(node.val_type)
+ mut cond_var := ''
+ if node.cond is ast.Ident || node.cond is ast.SelectorExpr {
+ cond_var = g.expr_string(node.cond)
+ } else {
+ cond_var = g.new_tmp_var()
+ g.write(g.typ(node.cond_type))
+ g.write(' $cond_var = ')
+ g.expr(node.cond)
+ g.writeln(';')
+ }
+ i := if node.key_var in ['', '_'] { g.new_tmp_var() } else { node.key_var }
+ field_accessor := if node.cond_type.is_ptr() { '->' } else { '.' }
+ share_accessor := if node.cond_type.share() == .shared_t { 'val.' } else { '' }
+ op_field := field_accessor + share_accessor
+ g.empty_line = true
+ g.writeln('for (int $i = 0; $i < $cond_var${op_field}len; ++$i) {')
+ if node.val_var != '_' {
+ if val_sym.kind == .function {
+ g.write('\t')
+ g.write_fn_ptr_decl(val_sym.info as ast.FnType, c_name(node.val_var))
+ g.writeln(' = ((voidptr*)$cond_var${op_field}data)[$i];')
+ } else if val_sym.kind == .array_fixed && !node.val_is_mut {
+ right := '(($styp*)$cond_var${op_field}data)[$i]'
+ g.writeln('\t$styp ${c_name(node.val_var)};')
+ g.writeln('\tmemcpy(*($styp*)${c_name(node.val_var)}, (byte*)$right, sizeof($styp));')
+ } else {
+ // If val is mutable (pointer behind the scenes), we need to generate
+ // `int* val = ((int*)arr.data) + i;`
+ // instead of
+ // `int* val = ((int**)arr.data)[i];`
+ // right := if node.val_is_mut { styp } else { styp + '*' }
+ right := if node.val_is_mut {
+ '(($styp)$cond_var${op_field}data) + $i'
+ } else {
+ '(($styp*)$cond_var${op_field}data)[$i]'
+ }
+ g.writeln('\t$styp ${c_name(node.val_var)} = $right;')
+ }
+ }
+ } else if node.kind == .array_fixed {
+ mut cond_var := ''
+ cond_type_is_ptr := node.cond_type.is_ptr()
+ cond_is_literal := node.cond is ast.ArrayInit
+ if cond_is_literal {
+ cond_var = g.new_tmp_var()
+ g.write(g.typ(node.cond_type))
+ g.write(' $cond_var = ')
+ g.expr(node.cond)
+ g.writeln(';')
+ } else if cond_type_is_ptr {
+ cond_var = g.new_tmp_var()
+ cond_var_type := g.typ(node.cond_type).trim('*')
+ if !node.cond.is_lvalue() {
+ g.write('$cond_var_type *$cond_var = (($cond_var_type)')
+ } else {
+ g.write('$cond_var_type *$cond_var = (')
+ }
+ g.expr(node.cond)
+ g.writeln(');')
+ } else {
+ cond_var = g.expr_string(node.cond)
+ }
+ idx := if node.key_var in ['', '_'] { g.new_tmp_var() } else { node.key_var }
+ cond_sym := g.table.get_type_symbol(node.cond_type)
+ info := cond_sym.info as ast.ArrayFixed
+ g.writeln('for (int $idx = 0; $idx != $info.size; ++$idx) {')
+ if node.val_var != '_' {
+ val_sym := g.table.get_type_symbol(node.val_type)
+ is_fixed_array := val_sym.kind == .array_fixed && !node.val_is_mut
+ if val_sym.kind == .function {
+ g.write('\t')
+ g.write_fn_ptr_decl(val_sym.info as ast.FnType, c_name(node.val_var))
+ } else if is_fixed_array {
+ styp := g.typ(node.val_type)
+ g.writeln('\t$styp ${c_name(node.val_var)};')
+ g.writeln('\tmemcpy(*($styp*)${c_name(node.val_var)}, (byte*)$cond_var[$idx], sizeof($styp));')
+ } else {
+ styp := g.typ(node.val_type)
+ g.write('\t$styp ${c_name(node.val_var)}')
+ }
+ if !is_fixed_array {
+ addr := if node.val_is_mut { '&' } else { '' }
+ if cond_type_is_ptr {
+ g.writeln(' = ${addr}(*$cond_var)[$idx];')
+ } else if cond_is_literal {
+ g.writeln(' = $addr$cond_var[$idx];')
+ } else {
+ g.write(' = $addr')
+ g.expr(node.cond)
+ g.writeln('[$idx];')
+ }
+ }
+ }
+ } else if node.kind == .map {
+ // `for key, val in map {
+ // g.writeln('// FOR IN map')
+ mut cond_var := ''
+ if node.cond is ast.Ident {
+ cond_var = g.expr_string(node.cond)
+ } else {
+ cond_var = g.new_tmp_var()
+ g.write(g.typ(node.cond_type))
+ g.write(' $cond_var = ')
+ g.expr(node.cond)
+ g.writeln(';')
+ }
+ mut arw_or_pt := if node.cond_type.is_ptr() { '->' } else { '.' }
+ if node.cond_type.has_flag(.shared_f) {
+ arw_or_pt = '->val.'
+ }
+ idx := g.new_tmp_var()
+ map_len := g.new_tmp_var()
+ g.empty_line = true
+ g.writeln('int $map_len = $cond_var${arw_or_pt}key_values.len;')
+ g.writeln('for (int $idx = 0; $idx < $map_len; ++$idx ) {')
+ // TODO: don't have this check when the map has no deleted elements
+ g.indent++
+ diff := g.new_tmp_var()
+ g.writeln('int $diff = $cond_var${arw_or_pt}key_values.len - $map_len;')
+ g.writeln('$map_len = $cond_var${arw_or_pt}key_values.len;')
+ // TODO: optimize this
+ g.writeln('if ($diff < 0) {')
+ g.writeln('\t$idx = -1;')
+ g.writeln('\tcontinue;')
+ g.writeln('}')
+ g.writeln('if (!DenseArray_has_index(&$cond_var${arw_or_pt}key_values, $idx)) {continue;}')
+ if node.key_var != '_' {
+ key_styp := g.typ(node.key_type)
+ key := c_name(node.key_var)
+ g.writeln('$key_styp $key = /*key*/ *($key_styp*)DenseArray_key(&$cond_var${arw_or_pt}key_values, $idx);')
+ // TODO: analyze whether node.key_type has a .clone() method and call .clone() for all types:
+ if node.key_type == ast.string_type {
+ g.writeln('$key = string_clone($key);')
+ }
+ }
+ if node.val_var != '_' {
+ val_sym := g.table.get_type_symbol(node.val_type)
+ if val_sym.kind == .function {
+ g.write_fn_ptr_decl(val_sym.info as ast.FnType, c_name(node.val_var))
+ g.write(' = (*(voidptr*)')
+ g.writeln('DenseArray_value(&$cond_var${arw_or_pt}key_values, $idx));')
+ } else if val_sym.kind == .array_fixed && !node.val_is_mut {
+ val_styp := g.typ(node.val_type)
+ g.writeln('$val_styp ${c_name(node.val_var)};')
+ g.writeln('memcpy(*($val_styp*)${c_name(node.val_var)}, (byte*)DenseArray_value(&$cond_var${arw_or_pt}key_values, $idx), sizeof($val_styp));')
+ } else {
+ val_styp := g.typ(node.val_type)
+ if node.val_type.is_ptr() {
+ g.write('$val_styp ${c_name(node.val_var)} = &(*($val_styp)')
+ } else {
+ g.write('$val_styp ${c_name(node.val_var)} = (*($val_styp*)')
+ }
+ g.writeln('DenseArray_value(&$cond_var${arw_or_pt}key_values, $idx));')
+ }
+ }
+ g.indent--
+ } else if node.kind == .string {
+ cond := if node.cond is ast.StringLiteral || node.cond is ast.StringInterLiteral {
+ ast.Expr(g.new_ctemp_var_then_gen(node.cond, ast.string_type))
+ } else {
+ node.cond
+ }
+ i := if node.key_var in ['', '_'] { g.new_tmp_var() } else { node.key_var }
+ g.write('for (int $i = 0; $i < ')
+ g.expr(cond)
+ g.writeln('.len; ++$i) {')
+ if node.val_var != '_' {
+ g.write('\tbyte ${c_name(node.val_var)} = ')
+ g.expr(cond)
+ g.writeln('.str[$i];')
+ }
+ } else if node.kind == .struct_ {
+ cond_type_sym := g.table.get_type_symbol(node.cond_type)
+ next_fn := cond_type_sym.find_method('next') or {
+ verror('`next` method not found')
+ return
+ }
+ ret_typ := next_fn.return_type
+ t_expr := g.new_tmp_var()
+ g.write('${g.typ(node.cond_type)} $t_expr = ')
+ g.expr(node.cond)
+ g.writeln(';')
+ g.writeln('while (1) {')
+ t_var := g.new_tmp_var()
+ receiver_typ := next_fn.params[0].typ
+ receiver_styp := g.typ(receiver_typ)
+ fn_name := receiver_styp.replace_each(['*', '', '.', '__']) + '_next'
+ g.write('\t${g.typ(ret_typ)} $t_var = ${fn_name}(')
+ if !node.cond_type.is_ptr() && receiver_typ.is_ptr() {
+ g.write('&')
+ }
+ g.writeln('$t_expr);')
+ g.writeln('\tif (${t_var}.state != 0) break;')
+ val := if node.val_var in ['', '_'] { g.new_tmp_var() } else { node.val_var }
+ val_styp := g.typ(node.val_type)
+ g.writeln('\t$val_styp $val = *($val_styp*)${t_var}.data;')
+ } else {
+ typ_str := g.table.type_to_str(node.cond_type)
+ g.error('for in: unhandled symbol `$node.cond` of type `$typ_str`', node.pos)
+ }
+ g.stmts(node.stmts)
+ if node.label.len > 0 {
+ g.writeln('\t${node.label}__continue: {}')
+ }
+
+ if node.kind == .map {
+ // diff := g.new_tmp_var()
+ // g.writeln('int $diff = $cond_var${arw_or_pt}key_values.len - $map_len;')
+ // g.writeln('if ($diff < 0) {')
+ // g.writeln('\t$idx = -1;')
+ // g.writeln('\t$map_len = $cond_var${arw_or_pt}key_values.len;')
+ // g.writeln('}')
+ }
+
+ g.writeln('}')
+ if node.label.len > 0 {
+ g.writeln('\t${node.label}__break: {}')
+ }
+}
+
+fn (mut g Gen) write_sumtype_casting_fn(got_ ast.Type, exp_ ast.Type) {
+ got, exp := got_.idx(), exp_.idx()
+ i := got | (exp << 16)
+ if got == exp || g.sumtype_definitions[i] {
+ return
+ }
+ g.sumtype_definitions[i] = true
+ got_sym := g.table.get_type_symbol(got)
+ exp_sym := g.table.get_type_symbol(exp)
+ mut sb := strings.new_builder(128)
+ got_cname, exp_cname := got_sym.cname, exp_sym.cname
+ sb.writeln('static inline $exp_cname ${got_cname}_to_sumtype_${exp_cname}($got_cname* x) {')
+ sb.writeln('\t$got_cname* ptr = memdup(x, sizeof($got_cname));')
+ for embed_hierarchy in g.table.get_embeds(got_sym) {
+ // last embed in the hierarchy
+ mut embed_cname := ''
+ mut embed_name := ''
+ mut accessor := '&x->'
+ for j, embed in embed_hierarchy {
+ embed_sym := g.table.get_type_symbol(embed)
+ embed_cname = embed_sym.cname
+ embed_name = embed_sym.embed_name()
+ if j > 0 {
+ accessor += '.'
+ }
+ accessor += embed_name
+ }
+ // if the variable is not used, the C compiler will optimize it away
+ sb.writeln('\t$embed_cname* ${embed_name}_ptr = memdup($accessor, sizeof($embed_cname));')
+ }
+ sb.write_string('\treturn ($exp_cname){ ._$got_cname = ptr, ._typ = ${g.type_sidx(got)}')
+ for field in (exp_sym.info as ast.SumType).fields {
+ mut ptr := 'ptr'
+ mut type_cname := got_cname
+ _, embed_types := g.table.find_field_from_embeds_recursive(got_sym, field.name) or {
+ ast.StructField{}, []ast.Type{}
+ }
+ if embed_types.len > 0 {
+ embed_sym := g.table.get_type_symbol(embed_types.last())
+ ptr = '${embed_sym.embed_name()}_ptr'
+ type_cname = embed_sym.cname
+ }
+ field_styp := g.typ(field.typ)
+ if got_sym.kind in [.sum_type, .interface_] {
+ // the field is already a wrapped pointer; we shouldn't wrap it once again
+ sb.write_string(', .$field.name = ptr->$field.name')
+ } else {
+ sb.write_string(', .$field.name = ($field_styp*)((char*)$ptr + __offsetof_ptr($ptr, $type_cname, $field.name))')
+ }
+ }
+ sb.writeln('};\n}')
+ g.auto_fn_definitions << sb.str()
+}
+
+fn (mut g Gen) call_cfn_for_casting_expr(fname string, expr ast.Expr, exp_is_ptr bool, exp_styp string, got_is_ptr bool, got_styp string) {
+ mut rparen_n := 1
+ if exp_is_ptr {
+ g.write('HEAP($exp_styp, ')
+ rparen_n++
+ }
+ g.write('${fname}(')
+ if !got_is_ptr {
+ if !expr.is_lvalue()
+ || (expr is ast.Ident && (expr as ast.Ident).obj.is_simple_define_const()) {
+ g.write('ADDR($got_styp, (')
+ rparen_n += 2
+ } else {
+ g.write('&')
+ }
+ }
+ g.expr(expr)
+ g.write(')'.repeat(rparen_n))
+}
+
+// use instead of expr() when you need to cast to a different type
+fn (mut g Gen) expr_with_cast(expr ast.Expr, got_type_raw ast.Type, expected_type ast.Type) {
+ got_type := g.table.mktyp(got_type_raw)
+ exp_sym := g.table.get_type_symbol(expected_type)
+ expected_is_ptr := expected_type.is_ptr()
+ got_is_ptr := got_type.is_ptr()
+ got_sym := g.table.get_type_symbol(got_type)
+ // allow using the new Error struct as a string, to avoid a breaking change
+ // TODO: temporary to allow people to migrate their code; remove soon
+ if got_type == ast.error_type_idx && expected_type == ast.string_type_idx {
+ g.write('(*(')
+ g.expr(expr)
+ g.write('.msg))')
+ return
+ }
+ if got_sym.kind == .none_ && exp_sym.name == 'IError' {
+ g.expr(expr)
+ return
+ }
+ if exp_sym.info is ast.Interface && got_type_raw.idx() != expected_type.idx()
+ && !expected_type.has_flag(.optional) {
+ if expr is ast.StructInit && !got_type.is_ptr() {
+ g.inside_cast_in_heap++
+ got_styp := g.cc_type(got_type.to_ptr(), true)
+ // TODO: why does cc_type even add this in the first place?
+ exp_styp := exp_sym.cname
+ mut fname := 'I_${got_styp}_to_Interface_$exp_styp'
+ if exp_sym.info.is_generic {
+ fname = g.generic_fn_name(exp_sym.info.concrete_types, fname, false)
+ }
+ g.call_cfn_for_casting_expr(fname, expr, expected_is_ptr, exp_styp, true,
+ got_styp)
+ g.inside_cast_in_heap--
+ } else {
+ got_styp := g.cc_type(got_type, true)
+ exp_styp := exp_sym.cname
+ mut fname := '/*$exp_sym*/I_${got_styp}_to_Interface_$exp_styp'
+ if exp_sym.info.is_generic {
+ fname = g.generic_fn_name(exp_sym.info.concrete_types, fname, false)
+ }
+ g.call_cfn_for_casting_expr(fname, expr, expected_is_ptr, exp_styp, got_is_ptr,
+ got_styp)
+ }
+ return
+ }
+ // cast to sum type
+ exp_styp := g.typ(expected_type)
+ got_styp := g.typ(got_type)
+ if expected_type != ast.void_type {
+ expected_deref_type := if expected_is_ptr { expected_type.deref() } else { expected_type }
+ got_deref_type := if got_is_ptr { got_type.deref() } else { got_type }
+ if g.table.sumtype_has_variant(expected_deref_type, got_deref_type) {
+ mut is_already_sum_type := false
+ scope := g.file.scope.innermost(expr.position().pos)
+ if expr is ast.Ident {
+ if v := scope.find_var(expr.name) {
+ if v.smartcasts.len > 0 {
+ is_already_sum_type = true
+ }
+ }
+ } else if expr is ast.SelectorExpr {
+ if _ := scope.find_struct_field(expr.expr.str(), expr.expr_type, expr.field_name) {
+ is_already_sum_type = true
+ }
+ }
+ if is_already_sum_type {
+ // Don't create a new sum type wrapper if there is already one
+ g.prevent_sum_type_unwrapping_once = true
+ g.expr(expr)
+ } else {
+ g.write_sumtype_casting_fn(got_type, expected_type)
+ fname := '${got_sym.cname}_to_sumtype_$exp_sym.cname'
+ g.call_cfn_for_casting_expr(fname, expr, expected_is_ptr, exp_sym.cname,
+ got_is_ptr, got_styp)
+ }
+ return
+ }
+ }
+ // Generic dereferencing logic
+ neither_void := ast.voidptr_type !in [got_type, expected_type]
+ to_shared := expected_type.has_flag(.shared_f) && !got_type_raw.has_flag(.shared_f)
+ && !expected_type.has_flag(.optional)
+ // from_shared := got_type_raw.has_flag(.shared_f) && !expected_type.has_flag(.shared_f)
+ if to_shared {
+ shared_styp := exp_styp[0..exp_styp.len - 1] // `shared` implies ptr, so eat one `*`
+ if got_type_raw.is_ptr() {
+ g.error('cannot convert reference to `shared`', expr.position())
+ }
+ if exp_sym.kind == .array {
+ g.writeln('($shared_styp*)__dup_shared_array(&($shared_styp){.mtx = {0}, .val =')
+ } else if exp_sym.kind == .map {
+ g.writeln('($shared_styp*)__dup_shared_map(&($shared_styp){.mtx = {0}, .val =')
+ } else {
+ g.writeln('($shared_styp*)__dup${shared_styp}(&($shared_styp){.mtx = {0}, .val =')
+ }
+ old_is_shared := g.is_shared
+ g.is_shared = false
+ g.expr(expr)
+ g.is_shared = old_is_shared
+ g.writeln('}, sizeof($shared_styp))')
+ return
+ }
+ if got_is_ptr && !expected_is_ptr && neither_void
+ && exp_sym.kind !in [.interface_, .placeholder] && expr !is ast.InfixExpr {
+ got_deref_type := got_type.deref()
+ deref_sym := g.table.get_type_symbol(got_deref_type)
+ deref_will_match := expected_type in [got_type, got_deref_type, deref_sym.parent_idx]
+ got_is_opt := got_type.has_flag(.optional)
+ if deref_will_match || got_is_opt {
+ g.write('*')
+ }
+ }
+ if expected_type.has_flag(.optional) && expr is ast.None {
+ g.gen_optional_error(expected_type, expr)
+ return
+ }
+ if expr is ast.IntegerLiteral {
+ if expected_type in [ast.u64_type, ast.u32_type, ast.u16_type] && expr.val[0] != `-` {
+ g.expr(expr)
+ g.write('U')
+ return
+ }
+ }
+ if exp_sym.kind == .function {
+ g.write('(voidptr)')
+ }
+ // no cast
+ g.expr(expr)
+}
+
+// cestring returns a V string, properly escaped for embeddeding in a C string literal.
+fn cestring(s string) string {
+ return s.replace('\\', '\\\\').replace('"', "'")
+}
+
+// ctoslit returns a '_SLIT("$s")' call, where s is properly escaped.
+fn ctoslit(s string) string {
+ return '_SLIT("' + cestring(s) + '")'
+}
+
+fn (mut g Gen) gen_attrs(attrs []ast.Attr) {
+ if g.pref.skip_unused {
+ return
+ }
+ for attr in attrs {
+ g.writeln('// Attr: [$attr.name]')
+ }
+}
+
+fn (mut g Gen) gen_asm_stmt(stmt ast.AsmStmt) {
+ g.write('__asm__')
+ if stmt.is_volatile {
+ g.write(' volatile')
+ }
+ if stmt.is_goto {
+ g.write(' goto')
+ }
+ g.writeln(' (')
+ g.indent++
+ for template_tmp in stmt.templates {
+ mut template := template_tmp
+ g.write('"')
+ if template.is_directive {
+ g.write('.')
+ }
+ g.write(template.name)
+ if template.is_label {
+ g.write(':')
+ } else {
+ g.write(' ')
+ }
+ // swap destionation and operands for att syntax
+ if template.args.len != 0 && !template.is_directive {
+ template.args.prepend(template.args[template.args.len - 1])
+ template.args.delete(template.args.len - 1)
+ }
+
+ for i, arg in template.args {
+ if stmt.arch == .amd64 && (template.name == 'call' || template.name[0] == `j`)
+ && arg is ast.AsmRegister {
+ g.write('*') // indirect branching
+ }
+
+ g.asm_arg(arg, stmt)
+ if i + 1 < template.args.len {
+ g.write(', ')
+ }
+ }
+
+ if !template.is_label {
+ g.write(';')
+ }
+ g.writeln('"')
+ }
+
+ if stmt.output.len != 0 || stmt.input.len != 0 || stmt.clobbered.len != 0 || stmt.is_goto {
+ g.write(': ')
+ }
+ g.gen_asm_ios(stmt.output)
+ if stmt.input.len != 0 || stmt.clobbered.len != 0 || stmt.is_goto {
+ g.write(': ')
+ }
+ g.gen_asm_ios(stmt.input)
+ if stmt.clobbered.len != 0 || stmt.is_goto {
+ g.write(': ')
+ }
+ for i, clob in stmt.clobbered {
+ g.write('"')
+ g.write(clob.reg.name)
+ g.write('"')
+ if i + 1 < stmt.clobbered.len {
+ g.writeln(',')
+ } else {
+ g.writeln('')
+ }
+ }
+ if stmt.is_goto {
+ g.write(': ')
+ }
+ for i, label in stmt.global_labels {
+ g.write(label)
+ if i + 1 < stmt.clobbered.len {
+ g.writeln(',')
+ } else {
+ g.writeln('')
+ }
+ }
+ g.indent--
+ g.writeln(');')
+}
+
+fn (mut g Gen) asm_arg(arg ast.AsmArg, stmt ast.AsmStmt) {
+ match arg {
+ ast.AsmAlias {
+ name := arg.name
+ if name in stmt.local_labels || name in stmt.global_labels
+ || name in g.file.global_labels || stmt.is_basic
+ || (name !in stmt.input.map(it.alias) && name !in stmt.output.map(it.alias)) {
+ asm_formatted_name := if name in stmt.global_labels { '%l[$name]' } else { name }
+ g.write(asm_formatted_name)
+ } else {
+ g.write('%[$name]')
+ }
+ }
+ ast.CharLiteral {
+ g.write("'$arg.val'")
+ }
+ ast.IntegerLiteral, ast.FloatLiteral {
+ g.write('\$$arg.val')
+ }
+ ast.BoolLiteral {
+ g.write('\$$arg.val.str()')
+ }
+ ast.AsmRegister {
+ if !stmt.is_basic {
+ g.write('%') // escape percent with percent in extended assembly
+ }
+ g.write('%$arg.name')
+ }
+ ast.AsmAddressing {
+ base := arg.base
+ index := arg.index
+ displacement := arg.displacement
+ scale := arg.scale
+ match arg.mode {
+ .base {
+ g.write('(')
+ g.asm_arg(base, stmt)
+ g.write(')')
+ }
+ .displacement {
+ g.asm_arg(displacement, stmt)
+ g.write('()')
+ }
+ .base_plus_displacement {
+ g.asm_arg(displacement, stmt)
+ g.write('(')
+ g.asm_arg(base, stmt)
+ g.write(')')
+ }
+ .index_times_scale_plus_displacement {
+ if displacement is ast.AsmDisp {
+ g.asm_arg(displacement, stmt)
+ g.write('(, ')
+ } else if displacement is ast.AsmRegister {
+ g.write('(')
+ g.asm_arg(displacement, stmt)
+ g.write(',')
+ } else {
+ panic('unexpected $displacement.type_name()')
+ }
+ g.asm_arg(index, stmt)
+ g.write(',$scale)')
+ }
+ .base_plus_index_plus_displacement {
+ g.asm_arg(displacement, stmt)
+ g.write('(')
+ g.asm_arg(base, stmt)
+ g.write(',')
+ g.asm_arg(index, stmt)
+ g.write(',1)')
+ }
+ .base_plus_index_times_scale_plus_displacement {
+ g.asm_arg(displacement, stmt)
+ g.write('(')
+ g.asm_arg(base, stmt)
+ g.write(',')
+ g.asm_arg(index, stmt)
+ g.write(',$scale)')
+ }
+ .rip_plus_displacement {
+ g.asm_arg(displacement, stmt)
+ g.write('(')
+ g.asm_arg(base, stmt)
+ g.write(')')
+ }
+ .invalid {
+ g.error('invalid addressing mode', arg.pos)
+ }
+ }
+ }
+ ast.AsmDisp {
+ g.write(arg.val)
+ }
+ string {
+ g.write(arg)
+ }
+ }
+}
+
+fn (mut g Gen) gen_asm_ios(ios []ast.AsmIO) {
+ for i, io in ios {
+ if io.alias != '' {
+ g.write('[$io.alias] ')
+ }
+ g.write('"$io.constraint" (')
+ g.expr(io.expr)
+ g.write(')')
+ if i + 1 < ios.len {
+ g.writeln(',')
+ } else {
+ g.writeln('')
+ }
+ }
+}
+
+fn cnewlines(s string) string {
+ return s.replace('\n', r'\n')
+}
+
+fn (mut g Gen) write_fn_ptr_decl(func &ast.FnType, ptr_name string) {
+ ret_styp := g.typ(func.func.return_type)
+ g.write('$ret_styp (*$ptr_name) (')
+ arg_len := func.func.params.len
+ for i, arg in func.func.params {
+ arg_styp := g.typ(arg.typ)
+ g.write('$arg_styp $arg.name')
+ if i < arg_len - 1 {
+ g.write(', ')
+ }
+ }
+ g.write(')')
+}
+
+// TODO this function is scary. Simplify/split up.
+fn (mut g Gen) gen_assign_stmt(assign_stmt ast.AssignStmt) {
+ if assign_stmt.is_static {
+ g.write('static ')
+ }
+ mut return_type := ast.void_type
+ is_decl := assign_stmt.op == .decl_assign
+ g.assign_op = assign_stmt.op
+ op := if is_decl { token.Kind.assign } else { assign_stmt.op }
+ right_expr := assign_stmt.right[0]
+ match right_expr {
+ ast.CallExpr { return_type = right_expr.return_type }
+ ast.LockExpr { return_type = right_expr.typ }
+ ast.MatchExpr { return_type = right_expr.return_type }
+ ast.IfExpr { return_type = right_expr.typ }
+ else {}
+ }
+ // Free the old value assigned to this string var (only if it's `str = [new value]`
+ // or `x.str = [new value]` )
+ mut af := g.is_autofree && !g.is_builtin_mod && assign_stmt.op == .assign
+ && assign_stmt.left_types.len == 1
+ && (assign_stmt.left[0] is ast.Ident || assign_stmt.left[0] is ast.SelectorExpr)
+ // assign_stmt.left_types[0] in [ast.string_type, ast.array_type] &&
+ mut sref_name := ''
+ mut type_to_free := ''
+ if af {
+ first_left_type := assign_stmt.left_types[0]
+ first_left_sym := g.table.get_type_symbol(assign_stmt.left_types[0])
+ if first_left_type == ast.string_type || first_left_sym.kind == .array {
+ type_to_free = if first_left_type == ast.string_type { 'string' } else { 'array' }
+ mut ok := true
+ left0 := assign_stmt.left[0]
+ if left0 is ast.Ident {
+ if left0.name == '_' {
+ ok = false
+ }
+ }
+ if ok {
+ sref_name = '_sref$assign_stmt.pos.pos'
+ g.write('$type_to_free $sref_name = (') // TODO we are copying the entire string here, optimize
+ // we can't just do `.str` since we need the extra data from the string struct
+ // doing `&string` is also not an option since the stack memory with the data will be overwritten
+ g.expr(left0) // assign_stmt.left[0])
+ g.writeln('); // free $type_to_free on re-assignment2')
+ defer {
+ if af {
+ g.writeln('${type_to_free}_free(&$sref_name);')
+ }
+ }
+ } else {
+ af = false
+ }
+ } else {
+ af = false
+ }
+ }
+ // Autofree tmp arg vars
+ // first_right := assign_stmt.right[0]
+ // af := g.autofree && first_right is ast.CallExpr && !g.is_builtin_mod
+ // if af {
+ // g.autofree_call_pregen(first_right as ast.CallExpr)
+ // }
+ //
+ //
+ // Handle optionals. We need to declare a temp variable for them, that's why they are handled
+ // here, not in call_expr().
+ // `pos := s.index('x') or { return }`
+ // ==========>
+ // Option_int _t190 = string_index(s, _STR("x")); // _STR() no more used!!
+ // if (_t190.state != 2) {
+ // Error err = _t190.err;
+ // return;
+ // }
+ // int pos = *(int*)_t190.data;
+ // mut tmp_opt := ''
+ /*
+ is_optional := false && g.is_autofree && (assign_stmt.op in [.decl_assign, .assign])
+ && assign_stmt.left_types.len == 1 && assign_stmt.right[0] is ast.CallExpr
+ if is_optional {
+ // g.write('/* optional assignment */')
+ call_expr := assign_stmt.right[0] as ast.CallExpr
+ if call_expr.or_block.kind != .absent {
+ styp := g.typ(call_expr.return_type.set_flag(.optional))
+ tmp_opt = g.new_tmp_var()
+ g.write('/*AF opt*/$styp $tmp_opt = ')
+ g.expr(assign_stmt.right[0])
+ g.or_block(tmp_opt, call_expr.or_block, call_expr.return_type)
+ g.writeln('/*=============ret*/')
+ // if af && is_optional {
+ // g.autofree_call_postgen()
+ // }
+ // return
+ }
+ }
+ */
+ // json_test failed w/o this check
+ if return_type != ast.void_type && return_type != 0 {
+ sym := g.table.get_type_symbol(return_type)
+ if sym.kind == .multi_return {
+ // multi return
+ // TODO Handle in if_expr
+ is_opt := return_type.has_flag(.optional)
+ mr_var_name := 'mr_$assign_stmt.pos.pos'
+ mr_styp := g.typ(return_type)
+ g.write('$mr_styp $mr_var_name = ')
+ g.expr(assign_stmt.right[0])
+ g.writeln(';')
+ for i, lx in assign_stmt.left {
+ mut is_auto_heap := false
+ mut ident := ast.Ident{
+ scope: 0
+ }
+ if lx is ast.Ident {
+ ident = lx
+ if lx.kind == .blank_ident {
+ continue
+ }
+ if lx.obj is ast.Var {
+ is_auto_heap = lx.obj.is_auto_heap
+ }
+ }
+ styp := if ident.name in g.defer_vars {
+ ''
+ } else {
+ g.typ(assign_stmt.left_types[i])
+ }
+ if assign_stmt.op == .decl_assign {
+ g.write('$styp ')
+ if is_auto_heap {
+ g.write('*')
+ }
+ }
+ if lx.is_auto_deref_var() {
+ g.write('*')
+ }
+ g.expr(lx)
+ noscan := if is_auto_heap { g.check_noscan(return_type) } else { '' }
+ if g.is_arraymap_set {
+ if is_opt {
+ mr_base_styp := g.base_type(return_type)
+ if is_auto_heap {
+ g.writeln('HEAP${noscan}($mr_base_styp, *($mr_base_styp*)${mr_var_name}.data).arg$i) });')
+ } else {
+ g.writeln('(*($mr_base_styp*)${mr_var_name}.data).arg$i });')
+ }
+ } else {
+ if is_auto_heap {
+ g.writeln('HEAP${noscan}($styp, ${mr_var_name}.arg$i) });')
+ } else {
+ g.writeln('${mr_var_name}.arg$i });')
+ }
+ }
+ } else {
+ if is_opt {
+ mr_base_styp := g.base_type(return_type)
+ if is_auto_heap {
+ g.writeln(' = HEAP${noscan}($mr_base_styp, *($mr_base_styp*)${mr_var_name}.data).arg$i);')
+ } else {
+ g.writeln(' = (*($mr_base_styp*)${mr_var_name}.data).arg$i;')
+ }
+ } else {
+ if is_auto_heap {
+ g.writeln(' = HEAP${noscan}($styp, ${mr_var_name}.arg$i);')
+ } else {
+ g.writeln(' = ${mr_var_name}.arg$i;')
+ }
+ }
+ }
+ }
+ if g.is_arraymap_set {
+ g.is_arraymap_set = false
+ }
+ return
+ }
+ }
+ // TODO: non idents on left (exprs)
+ if assign_stmt.has_cross_var {
+ for i, left in assign_stmt.left {
+ match left {
+ ast.Ident {
+ left_typ := assign_stmt.left_types[i]
+ left_sym := g.table.get_type_symbol(left_typ)
+ if left_sym.kind == .function {
+ g.write_fn_ptr_decl(left_sym.info as ast.FnType, '_var_$left.pos.pos')
+ g.writeln(' = $left.name;')
+ } else {
+ styp := g.typ(left_typ)
+ g.writeln('$styp _var_$left.pos.pos = $left.name;')
+ }
+ }
+ ast.IndexExpr {
+ sym := g.table.get_type_symbol(left.left_type)
+ if sym.kind == .array {
+ info := sym.info as ast.Array
+ elem_typ := g.table.get_type_symbol(info.elem_type)
+ if elem_typ.kind == .function {
+ left_typ := assign_stmt.left_types[i]
+ left_sym := g.table.get_type_symbol(left_typ)
+ g.write_fn_ptr_decl(left_sym.info as ast.FnType, '_var_$left.pos.pos')
+ g.write(' = *(voidptr*)array_get(')
+ } else {
+ styp := g.typ(info.elem_type)
+ g.write('$styp _var_$left.pos.pos = *($styp*)array_get(')
+ }
+ if left.left_type.is_ptr() {
+ g.write('*')
+ }
+ needs_clone := info.elem_type == ast.string_type && g.is_autofree
+ if needs_clone {
+ g.write('/*1*/string_clone(')
+ }
+ g.expr(left.left)
+ if needs_clone {
+ g.write(')')
+ }
+ g.write(', ')
+ g.expr(left.index)
+ g.writeln(');')
+ } else if sym.kind == .map {
+ info := sym.info as ast.Map
+ skeytyp := g.typ(info.key_type)
+ styp := g.typ(info.value_type)
+ zero := g.type_default(info.value_type)
+ val_typ := g.table.get_type_symbol(info.value_type)
+ if val_typ.kind == .function {
+ left_type := assign_stmt.left_types[i]
+ left_sym := g.table.get_type_symbol(left_type)
+ g.write_fn_ptr_decl(left_sym.info as ast.FnType, '_var_$left.pos.pos')
+ g.write(' = *(voidptr*)map_get(')
+ } else {
+ g.write('$styp _var_$left.pos.pos = *($styp*)map_get(')
+ }
+ if !left.left_type.is_ptr() {
+ g.write('ADDR(map, ')
+ g.expr(left.left)
+ g.write(')')
+ } else {
+ g.expr(left.left)
+ }
+ g.write(', &($skeytyp[]){')
+ g.expr(left.index)
+ g.write('}')
+ if val_typ.kind == .function {
+ g.writeln(', &(voidptr[]){ $zero });')
+ } else {
+ g.writeln(', &($styp[]){ $zero });')
+ }
+ }
+ }
+ ast.SelectorExpr {
+ styp := g.typ(left.typ)
+ g.write('$styp _var_$left.pos.pos = ')
+ g.expr(left.expr)
+ mut sel := '.'
+ if left.expr_type.is_ptr() {
+ if left.expr_type.has_flag(.shared_f) {
+ sel = '->val.'
+ } else {
+ sel = '->'
+ }
+ }
+ g.writeln('$sel$left.field_name;')
+ }
+ else {}
+ }
+ }
+ }
+ // `a := 1` | `a,b := 1,2`
+ for i, left in assign_stmt.left {
+ mut is_auto_heap := false
+ mut var_type := assign_stmt.left_types[i]
+ mut val_type := assign_stmt.right_types[i]
+ val := assign_stmt.right[i]
+ mut is_call := false
+ mut blank_assign := false
+ mut ident := ast.Ident{
+ scope: 0
+ }
+ left_sym := g.table.get_type_symbol(var_type)
+ if left is ast.Ident {
+ ident = left
+ // id_info := ident.var_info()
+ // var_type = id_info.typ
+ blank_assign = left.kind == .blank_ident
+ // TODO: temporary, remove this
+ left_info := left.info
+ if left_info is ast.IdentVar {
+ share := left_info.share
+ if share == .shared_t {
+ var_type = var_type.set_flag(.shared_f)
+ }
+ if share == .atomic_t {
+ var_type = var_type.set_flag(.atomic_f)
+ }
+ }
+ if left.obj is ast.Var {
+ is_auto_heap = left.obj.is_auto_heap
+ }
+ }
+ styp := g.typ(var_type)
+ mut is_fixed_array_init := false
+ mut has_val := false
+ match val {
+ ast.ArrayInit {
+ is_fixed_array_init = val.is_fixed
+ has_val = val.has_val
+ }
+ ast.CallExpr {
+ is_call = true
+ return_type = val.return_type
+ }
+ // TODO: no buffer fiddling
+ ast.AnonFn {
+ if blank_assign {
+ g.write('{')
+ }
+ // if it's a decl assign (`:=`) or a blank assignment `_ =`/`_ :=` then generate `void (*ident) (args) =`
+ if (is_decl || blank_assign) && left is ast.Ident {
+ ret_styp := g.typ(val.decl.return_type)
+ g.write('$ret_styp (*$ident.name) (')
+ def_pos := g.definitions.len
+ g.fn_args(val.decl.params, voidptr(0))
+ g.definitions.go_back(g.definitions.len - def_pos)
+ g.write(') = ')
+ } else {
+ g.is_assign_lhs = true
+ g.assign_op = assign_stmt.op
+ g.expr(left)
+ g.is_assign_lhs = false
+ g.is_arraymap_set = false
+ if left is ast.IndexExpr {
+ sym := g.table.get_type_symbol(left.left_type)
+ if sym.kind in [.map, .array] {
+ g.expr(val)
+ g.writeln('});')
+ continue
+ }
+ }
+ g.write(' = ')
+ }
+ g.expr(val)
+ g.writeln(';')
+ if blank_assign {
+ g.write('}')
+ }
+ continue
+ }
+ else {}
+ }
+ unwrapped_val_type := g.unwrap_generic(val_type)
+ right_sym := g.table.get_type_symbol(unwrapped_val_type)
+ unaliased_right_sym := g.table.get_final_type_symbol(unwrapped_val_type)
+ is_fixed_array_var := unaliased_right_sym.kind == .array_fixed && val !is ast.ArrayInit
+ && (val is ast.Ident || val is ast.IndexExpr || val is ast.CallExpr
+ || (val is ast.CastExpr && (val as ast.CastExpr).expr !is ast.ArrayInit)
+ || val is ast.SelectorExpr)
+ g.is_assign_lhs = true
+ g.assign_op = assign_stmt.op
+ if val_type.has_flag(.optional) {
+ g.right_is_opt = true
+ }
+ if blank_assign {
+ if is_call {
+ old_is_void_expr_stmt := g.is_void_expr_stmt
+ g.is_void_expr_stmt = true
+ g.expr(val)
+ g.is_void_expr_stmt = old_is_void_expr_stmt
+ } else {
+ g.write('{$styp _ = ')
+ g.expr(val)
+ g.writeln(';}')
+ }
+ } else if assign_stmt.op == .assign
+ && (is_fixed_array_init || (right_sym.kind == .array_fixed && val is ast.Ident)) {
+ mut v_var := ''
+ arr_typ := styp.trim('*')
+ if is_fixed_array_init {
+ right := val as ast.ArrayInit
+ v_var = g.new_tmp_var()
+ g.write('$arr_typ $v_var = ')
+ g.expr(right)
+ g.writeln(';')
+ } else {
+ right := val as ast.Ident
+ v_var = right.name
+ }
+ pos := g.out.len
+ g.expr(left)
+
+ if g.is_arraymap_set && g.arraymap_set_pos > 0 {
+ g.out.go_back_to(g.arraymap_set_pos)
+ g.write(', &$v_var)')
+ g.is_arraymap_set = false
+ g.arraymap_set_pos = 0
+ } else {
+ g.out.go_back_to(pos)
+ is_var_mut := !is_decl && left.is_auto_deref_var()
+ addr := if is_var_mut { '' } else { '&' }
+ g.writeln('')
+ g.write('memcpy($addr')
+ g.expr(left)
+ g.writeln(', &$v_var, sizeof($arr_typ));')
+ }
+ g.is_assign_lhs = false
+ } else {
+ is_inside_ternary := g.inside_ternary != 0
+ cur_line := if is_inside_ternary && is_decl {
+ g.register_ternary_name(ident.name)
+ g.empty_line = false
+ g.go_before_ternary()
+ } else {
+ ''
+ }
+ mut str_add := false
+ mut op_overloaded := false
+ mut op_expected_left := ast.Type(0)
+ mut op_expected_right := ast.Type(0)
+ if var_type == ast.string_type_idx && assign_stmt.op == .plus_assign {
+ if left is ast.IndexExpr {
+ // a[0] += str => `array_set(&a, 0, &(string[]) {string__plus(...))})`
+ g.expr(left)
+ g.write('string__plus(')
+ } else {
+ // str += str2 => `str = string__plus(str, str2)`
+ g.expr(left)
+ g.write(' = /*f*/string__plus(')
+ }
+ g.is_assign_lhs = false
+ str_add = true
+ }
+ // Assignment Operator Overloading
+ if ((left_sym.kind == .struct_ && right_sym.kind == .struct_)
+ || (left_sym.kind == .alias && right_sym.kind == .alias))
+ && assign_stmt.op in [.plus_assign, .minus_assign, .div_assign, .mult_assign, .mod_assign] {
+ extracted_op := match assign_stmt.op {
+ .plus_assign { '+' }
+ .minus_assign { '-' }
+ .div_assign { '/' }
+ .mod_assign { '%' }
+ .mult_assign { '*' }
+ else { 'unknown op' }
+ }
+ g.expr(left)
+ g.write(' = ${styp}_${util.replace_op(extracted_op)}(')
+ method := g.table.type_find_method(left_sym, extracted_op) or {
+ // the checker will most likely have found this, already...
+ g.error('assignemnt operator `$extracted_op=` used but no `$extracted_op` method defined',
+ assign_stmt.pos)
+ ast.Fn{}
+ }
+ op_expected_left = method.params[0].typ
+ op_expected_right = method.params[1].typ
+ op_overloaded = true
+ }
+ if right_sym.kind == .function && is_decl {
+ if is_inside_ternary && is_decl {
+ g.out.write_string(util.tabs(g.indent - g.inside_ternary))
+ }
+ func := right_sym.info as ast.FnType
+ ret_styp := g.typ(func.func.return_type)
+ g.write('$ret_styp (*${g.get_ternary_name(ident.name)}) (')
+ def_pos := g.definitions.len
+ g.fn_args(func.func.params, voidptr(0))
+ g.definitions.go_back(g.definitions.len - def_pos)
+ g.write(')')
+ } else {
+ if is_decl {
+ if is_inside_ternary {
+ g.out.write_string(util.tabs(g.indent - g.inside_ternary))
+ }
+ mut is_used_var_styp := false
+ if ident.name !in g.defer_vars {
+ val_sym := g.table.get_type_symbol(val_type)
+ if val_sym.info is ast.Struct {
+ if val_sym.info.generic_types.len > 0 {
+ if val is ast.StructInit {
+ var_styp := g.typ(val.typ)
+ g.write('$var_styp ')
+ is_used_var_styp = true
+ } else if val is ast.PrefixExpr {
+ if val.op == .amp && val.right is ast.StructInit {
+ var_styp := g.typ(val.right.typ.to_ptr())
+ g.write('$var_styp ')
+ is_used_var_styp = true
+ }
+ }
+ }
+ }
+ if !is_used_var_styp {
+ g.write('$styp ')
+ }
+ if is_auto_heap {
+ g.write('*')
+ }
+ }
+ }
+ if left is ast.Ident || left is ast.SelectorExpr {
+ g.prevent_sum_type_unwrapping_once = true
+ }
+ if !is_fixed_array_var || is_decl {
+ if op_overloaded {
+ g.op_arg(left, op_expected_left, var_type)
+ } else {
+ if !is_decl && left.is_auto_deref_var() {
+ g.write('*')
+ }
+ g.expr(left)
+ }
+ }
+ }
+ if is_inside_ternary && is_decl {
+ g.write(';\n$cur_line')
+ g.out.write_string(util.tabs(g.indent))
+ g.expr(left)
+ }
+ g.is_assign_lhs = false
+ if is_fixed_array_var {
+ if is_decl {
+ g.writeln(';')
+ }
+ } else if !g.is_arraymap_set && !str_add && !op_overloaded {
+ g.write(' $op ')
+ } else if str_add || op_overloaded {
+ g.write(', ')
+ }
+ mut cloned := false
+ if g.is_autofree && right_sym.kind in [.array, .string] {
+ if g.gen_clone_assignment(val, right_sym, false) {
+ cloned = true
+ }
+ }
+ unwrap_optional := !var_type.has_flag(.optional) && val_type.has_flag(.optional)
+ if unwrap_optional {
+ // Unwrap the optional now that the testing code has been prepended.
+ // `pos := s.index(...
+ // `int pos = *(int)_t10.data;`
+ // if g.is_autofree {
+ /*
+ if is_optional {
+ g.write('*($styp*)')
+ g.write(tmp_opt + '.data/*FFz*/')
+ g.right_is_opt = false
+ if g.inside_ternary == 0 && !assign_stmt.is_simple {
+ g.writeln(';')
+ }
+ return
+ }
+ */
+ }
+ g.is_shared = var_type.has_flag(.shared_f)
+ if !cloned {
+ if is_fixed_array_var {
+ typ_str := g.typ(val_type).trim('*')
+ ref_str := if val_type.is_ptr() { '' } else { '&' }
+ g.write('memcpy(($typ_str*)')
+ g.expr(left)
+ g.write(', (byte*)$ref_str')
+ g.expr(val)
+ g.write(', sizeof($typ_str))')
+ } else if is_decl {
+ if is_fixed_array_init && !has_val {
+ if val is ast.ArrayInit {
+ if val.has_default {
+ g.write('{')
+ g.expr(val.default_expr)
+ info := right_sym.info as ast.ArrayFixed
+ for _ in 1 .. info.size {
+ g.write(', ')
+ g.expr(val.default_expr)
+ }
+ g.write('}')
+ } else {
+ g.write('{0}')
+ }
+ } else {
+ g.write('{0}')
+ }
+ } else {
+ if is_auto_heap {
+ g.write('HEAP($styp, (')
+ }
+ if val.is_auto_deref_var() {
+ g.write('*')
+ }
+ g.expr(val)
+ if is_auto_heap {
+ g.write('))')
+ }
+ }
+ } else {
+ if assign_stmt.has_cross_var {
+ g.gen_cross_tmp_variable(assign_stmt.left, val)
+ } else {
+ if op_overloaded {
+ g.op_arg(val, op_expected_right, val_type)
+ } else {
+ g.expr_with_cast(val, val_type, var_type)
+ }
+ }
+ }
+ }
+ if str_add || op_overloaded {
+ g.write(')')
+ }
+ if g.is_arraymap_set {
+ g.write(' })')
+ g.is_arraymap_set = false
+ }
+ g.is_shared = false
+ }
+ g.right_is_opt = false
+ if g.inside_ternary == 0 && (assign_stmt.left.len > 1 || !assign_stmt.is_simple) {
+ g.writeln(';')
+ }
+ }
+}
+
+fn (mut g Gen) gen_cross_tmp_variable(left []ast.Expr, val ast.Expr) {
+ val_ := val
+ match val {
+ ast.Ident {
+ mut has_var := false
+ for lx in left {
+ if lx is ast.Ident {
+ if val.name == lx.name {
+ g.write('_var_')
+ g.write(lx.pos.pos.str())
+ has_var = true
+ break
+ }
+ }
+ }
+ if !has_var {
+ g.expr(val_)
+ }
+ }
+ ast.IndexExpr {
+ mut has_var := false
+ for lx in left {
+ if val_.str() == lx.str() {
+ g.write('_var_')
+ g.write(lx.position().pos.str())
+ has_var = true
+ break
+ }
+ }
+ if !has_var {
+ g.expr(val_)
+ }
+ }
+ ast.InfixExpr {
+ g.gen_cross_tmp_variable(left, val.left)
+ g.write(val.op.str())
+ g.gen_cross_tmp_variable(left, val.right)
+ }
+ ast.PrefixExpr {
+ g.write(val.op.str())
+ g.gen_cross_tmp_variable(left, val.right)
+ }
+ ast.PostfixExpr {
+ g.gen_cross_tmp_variable(left, val.expr)
+ g.write(val.op.str())
+ }
+ ast.SelectorExpr {
+ mut has_var := false
+ for lx in left {
+ if val_.str() == lx.str() {
+ g.write('_var_')
+ g.write(lx.position().pos.str())
+ has_var = true
+ break
+ }
+ }
+ if !has_var {
+ g.expr(val_)
+ }
+ }
+ else {
+ g.expr(val_)
+ }
+ }
+}
+
+fn (mut g Gen) register_ternary_name(name string) {
+ level_key := g.inside_ternary.str()
+ if level_key !in g.ternary_level_names {
+ g.ternary_level_names[level_key] = []string{}
+ }
+ new_name := g.new_tmp_var()
+ g.ternary_names[name] = new_name
+ g.ternary_level_names[level_key] << name
+}
+
+fn (mut g Gen) get_ternary_name(name string) string {
+ if g.inside_ternary == 0 {
+ return name
+ }
+ if name !in g.ternary_names {
+ return name
+ }
+ return g.ternary_names[name]
+}
+
+fn (mut g Gen) gen_clone_assignment(val ast.Expr, right_sym ast.TypeSymbol, add_eq bool) bool {
+ if val !is ast.Ident && val !is ast.SelectorExpr {
+ return false
+ }
+ if g.is_autofree && right_sym.kind == .array {
+ // `arr1 = arr2` => `arr1 = arr2.clone()`
+ if add_eq {
+ g.write('=')
+ }
+ g.write(' array_clone_static_to_depth(')
+ g.expr(val)
+ elem_type := (right_sym.info as ast.Array).elem_type
+ array_depth := g.get_array_depth(elem_type)
+ g.write(', $array_depth)')
+ } else if g.is_autofree && right_sym.kind == .string {
+ if add_eq {
+ g.write('=')
+ }
+ // `str1 = str2` => `str1 = str2.clone()`
+ g.write(' string_clone_static(')
+ g.expr(val)
+ g.write(')')
+ }
+ return true
+}
+
+fn (mut g Gen) autofree_scope_vars(pos int, line_nr int, free_parent_scopes bool) {
+ g.autofree_scope_vars_stop(pos, line_nr, free_parent_scopes, -1)
+}
+
+fn (mut g Gen) autofree_scope_vars_stop(pos int, line_nr int, free_parent_scopes bool, stop_pos int) {
+ if g.is_builtin_mod {
+ // In `builtin` everything is freed manually.
+ return
+ }
+ if pos == -1 {
+ // TODO why can pos be -1?
+ return
+ }
+ // eprintln('> free_scope_vars($pos)')
+ scope := g.file.scope.innermost(pos)
+ if scope.start_pos == 0 {
+ // TODO why can scope.pos be 0? (only outside fns?)
+ return
+ }
+ g.trace_autofree('// autofree_scope_vars(pos=$pos line_nr=$line_nr scope.pos=$scope.start_pos scope.end_pos=$scope.end_pos)')
+ g.autofree_scope_vars2(scope, scope.start_pos, scope.end_pos, line_nr, free_parent_scopes,
+ stop_pos)
+}
+
+[if trace_autofree ?]
+fn (mut g Gen) trace_autofree(line string) {
+ g.writeln(line)
+}
+
+// fn (mut g Gen) autofree_scope_vars2(scope &ast.Scope, end_pos int) {
+fn (mut g Gen) autofree_scope_vars2(scope &ast.Scope, start_pos int, end_pos int, line_nr int, free_parent_scopes bool, stop_pos int) {
+ if isnil(scope) {
+ return
+ }
+ for _, obj in scope.objects {
+ match obj {
+ ast.Var {
+ g.trace_autofree('// var "$obj.name" var.pos=$obj.pos.pos var.line_nr=$obj.pos.line_nr')
+ if obj.name == g.returned_var_name {
+ g.trace_autofree('// skipping returned var')
+ continue
+ }
+ if obj.is_or {
+ // Skip vars inited with the `or {}`, since they are generated
+ // after the or block in C.
+ g.trace_autofree('// skipping `or{}` var "$obj.name"')
+ continue
+ }
+ if obj.is_tmp {
+ // Skip for loop vars
+ g.trace_autofree('// skipping tmp var "$obj.name"')
+ continue
+ }
+ if obj.is_inherited {
+ g.trace_autofree('// skipping inherited var "$obj.name"')
+ continue
+ }
+ // if var.typ == 0 {
+ // // TODO why 0?
+ // continue
+ // }
+ // if v.pos.pos > end_pos {
+ if obj.pos.pos > end_pos || (obj.pos.pos < start_pos && obj.pos.line_nr == line_nr) {
+ // Do not free vars that were declared after this scope
+ continue
+ }
+ is_optional := obj.typ.has_flag(.optional)
+ if is_optional {
+ // TODO: free optionals
+ continue
+ }
+ g.autofree_variable(obj)
+ }
+ else {}
+ }
+ }
+ // Free all vars in parent scopes as well:
+ // ```
+ // s := ...
+ // if ... {
+ // s.free()
+ // return
+ // }
+ // ```
+ // if !isnil(scope.parent) && line_nr > 0 {
+ if free_parent_scopes && !isnil(scope.parent)
+ && (stop_pos == -1 || scope.parent.start_pos >= stop_pos) {
+ g.trace_autofree('// af parent scope:')
+ g.autofree_scope_vars2(scope.parent, start_pos, end_pos, line_nr, true, stop_pos)
+ }
+}
+
+fn (mut g Gen) autofree_variable(v ast.Var) {
+ sym := g.table.get_type_symbol(v.typ)
+ // if v.name.contains('output2') {
+ // eprintln(' > var name: ${v.name:-20s} | is_arg: ${v.is_arg.str():6} | var type: ${int(v.typ):8} | type_name: ${sym.name:-33s}')
+ // }
+ if sym.kind == .array {
+ if sym.has_method('free') {
+ free_method_name := g.typ(v.typ) + '_free'
+ g.autofree_var_call(free_method_name, v)
+ return
+ }
+ g.autofree_var_call('array_free', v)
+ return
+ }
+ if sym.kind == .string {
+ // Don't free simple string literals.
+ match v.expr {
+ ast.StringLiteral {
+ g.trace_autofree('// str literal')
+ }
+ else {
+ // NOTE/TODO: assign_stmt multi returns variables have no expr
+ // since the type comes from the called fns return type
+ /*
+ f := v.name[0]
+ if
+ //!(f >= `a` && f <= `d`) {
+ //f != `c` {
+ v.name!='cvar_name' {
+ t := typeof(v.expr)
+ return '// other ' + t + '\n'
+ }
+ */
+ }
+ }
+ g.autofree_var_call('string_free', v)
+ return
+ }
+ if sym.has_method('free') {
+ g.autofree_var_call(c_name(sym.name) + '_free', v)
+ }
+}
+
+fn (mut g Gen) autofree_var_call(free_fn_name string, v ast.Var) {
+ if v.is_arg {
+ // fn args should not be autofreed
+ return
+ }
+ if v.is_used && v.is_autofree_tmp {
+ // tmp expr vars do not need to be freed again here
+ return
+ }
+ if g.is_builtin_mod {
+ return
+ }
+ if !g.is_autofree {
+ return
+ }
+ // if v.is_autofree_tmp && !g.doing_autofree_tmp {
+ // return
+ // }
+ if v.name.contains('expr_write_string_1_') {
+ // TODO remove this temporary hack
+ return
+ }
+ if v.typ.is_ptr() {
+ g.writeln('\t${free_fn_name}(${c_name(v.name)}); // autofreed ptr var')
+ } else {
+ if v.typ == ast.error_type && !v.is_autofree_tmp {
+ return
+ }
+ g.writeln('\t${free_fn_name}(&${c_name(v.name)}); // autofreed var $g.cur_mod.name $g.is_builtin_mod')
+ }
+}
+
+fn (mut g Gen) map_fn_ptrs(key_typ ast.TypeSymbol) (string, string, string, string) {
+ mut hash_fn := ''
+ mut key_eq_fn := ''
+ mut clone_fn := ''
+ mut free_fn := '&map_free_nop'
+ match key_typ.kind {
+ .byte, .i8, .char {
+ hash_fn = '&map_hash_int_1'
+ key_eq_fn = '&map_eq_int_1'
+ clone_fn = '&map_clone_int_1'
+ }
+ .i16, .u16 {
+ hash_fn = '&map_hash_int_2'
+ key_eq_fn = '&map_eq_int_2'
+ clone_fn = '&map_clone_int_2'
+ }
+ .int, .u32, .rune, .f32, .enum_ {
+ hash_fn = '&map_hash_int_4'
+ key_eq_fn = '&map_eq_int_4'
+ clone_fn = '&map_clone_int_4'
+ }
+ .voidptr {
+ ts := if g.pref.m64 {
+ unsafe { &g.table.type_symbols[ast.u64_type_idx] }
+ } else {
+ unsafe { &g.table.type_symbols[ast.u32_type_idx] }
+ }
+ return g.map_fn_ptrs(ts)
+ }
+ .u64, .i64, .f64 {
+ hash_fn = '&map_hash_int_8'
+ key_eq_fn = '&map_eq_int_8'
+ clone_fn = '&map_clone_int_8'
+ }
+ .string {
+ hash_fn = '&map_hash_string'
+ key_eq_fn = '&map_eq_string'
+ clone_fn = '&map_clone_string'
+ free_fn = '&map_free_string'
+ }
+ else {
+ verror('map key type not supported')
+ }
+ }
+ return hash_fn, key_eq_fn, clone_fn, free_fn
+}
+
+fn (mut g Gen) expr(node ast.Expr) {
+ // println('cgen expr() line_nr=$node.pos.line_nr')
+ old_discard_or_result := g.discard_or_result
+ old_is_void_expr_stmt := g.is_void_expr_stmt
+ if g.is_void_expr_stmt {
+ g.discard_or_result = true
+ g.is_void_expr_stmt = false
+ } else {
+ g.discard_or_result = false
+ }
+ // NB: please keep the type names in the match here in alphabetical order:
+ match mut node {
+ ast.EmptyExpr {
+ g.error('g.expr(): unhandled EmptyExpr', token.Position{})
+ }
+ ast.AnonFn {
+ g.gen_anon_fn(mut node)
+ }
+ ast.ArrayDecompose {
+ g.expr(node.expr)
+ }
+ ast.ArrayInit {
+ g.array_init(node)
+ }
+ ast.AsCast {
+ g.as_cast(node)
+ }
+ ast.Assoc {
+ g.assoc(node)
+ }
+ ast.BoolLiteral {
+ g.write(node.val.str())
+ }
+ ast.CallExpr {
+ // if g.fileis('1.strings') {
+ // println('\ncall_expr()()')
+ // }
+ ret_type := if node.or_block.kind == .absent {
+ node.return_type
+ } else {
+ node.return_type.clear_flag(.optional)
+ }
+ mut shared_styp := ''
+ if g.is_shared && !ret_type.has_flag(.shared_f) {
+ ret_sym := g.table.get_type_symbol(ret_type)
+ shared_typ := ret_type.set_flag(.shared_f)
+ shared_styp = g.typ(shared_typ)
+ if ret_sym.kind == .array {
+ g.writeln('($shared_styp*)__dup_shared_array(&($shared_styp){.mtx = {0}, .val =')
+ } else if ret_sym.kind == .map {
+ g.writeln('($shared_styp*)__dup_shared_map(&($shared_styp){.mtx = {0}, .val =')
+ } else {
+ g.writeln('($shared_styp*)__dup${shared_styp}(&($shared_styp){.mtx = {0}, .val =')
+ }
+ }
+ last_stmt_pos := g.stmt_path_pos.last()
+ g.call_expr(node)
+ // if g.fileis('1.strings') {
+ // println('before:' + node.autofree_pregen)
+ // }
+ if g.is_autofree && !g.is_builtin_mod && !g.is_js_call && g.strs_to_free0.len == 0
+ && !g.inside_lambda { // && g.inside_ternary ==
+ // if len != 0, that means we are handling call expr inside call expr (arg)
+ // and it'll get messed up here, since it's handled recursively in autofree_call_pregen()
+ // so just skip it
+ g.autofree_call_pregen(node)
+ if g.strs_to_free0.len > 0 {
+ g.insert_at(last_stmt_pos, g.strs_to_free0.join('\n') + '/* inserted before */')
+ }
+ g.strs_to_free0 = []
+ // println('pos=$node.pos.pos')
+ }
+ if g.is_shared && !ret_type.has_flag(.shared_f) {
+ g.writeln('}, sizeof($shared_styp))')
+ }
+ // if g.autofree && node.autofree_pregen != '' { // g.strs_to_free0.len != 0 {
+ /*
+ if g.autofree {
+ s := g.autofree_pregen[node.pos.pos.str()]
+ if s != '' {
+ // g.insert_before_stmt('/*START2*/' + g.strs_to_free0.join('\n') + '/*END*/')
+ // g.insert_before_stmt('/*START3*/' + node.autofree_pregen + '/*END*/')
+ g.insert_before_stmt('/*START3*/' + s + '/*END*/')
+ // for s in g.strs_to_free0 {
+ }
+ // //g.writeln(s)
+ // }
+ g.strs_to_free0 = []
+ }
+ */
+ }
+ ast.CastExpr {
+ g.cast_expr(node)
+ }
+ ast.ChanInit {
+ elem_typ_str := g.typ(node.elem_type)
+ noscan := g.check_noscan(node.elem_type)
+ g.write('sync__new_channel_st${noscan}(')
+ if node.has_cap {
+ g.expr(node.cap_expr)
+ } else {
+ g.write('0')
+ }
+ g.write(', sizeof(')
+ g.write(elem_typ_str)
+ g.write('))')
+ }
+ ast.CharLiteral {
+ if node.val == r'\`' {
+ g.write("'`'")
+ } else {
+ // TODO: optimize use L-char instead of u32 when possible
+ if utf8_str_len(node.val) < node.val.len {
+ g.write('((rune)0x$node.val.utf32_code().hex() /* `$node.val` */)')
+ } else {
+ g.write("'$node.val'")
+ }
+ }
+ }
+ ast.DumpExpr {
+ g.dump_expr(node)
+ }
+ ast.AtExpr {
+ g.comp_at(node)
+ }
+ ast.ComptimeCall {
+ g.comptime_call(node)
+ }
+ ast.ComptimeSelector {
+ g.comptime_selector(node)
+ }
+ ast.Comment {}
+ ast.ConcatExpr {
+ g.concat_expr(node)
+ }
+ ast.CTempVar {
+ // g.write('/*ctmp .orig: $node.orig.str() , ._typ: $node.typ, .is_ptr: $node.is_ptr */ ')
+ g.write(node.name)
+ }
+ ast.EnumVal {
+ // g.write('${it.mod}${it.enum_name}_$it.val')
+ // g.enum_expr(node)
+ styp := g.typ(node.typ)
+ g.write('${styp}__$node.val')
+ }
+ ast.FloatLiteral {
+ g.write(node.val)
+ }
+ ast.GoExpr {
+ g.go_expr(node)
+ }
+ ast.Ident {
+ g.ident(node)
+ }
+ ast.IfExpr {
+ g.if_expr(node)
+ }
+ ast.IfGuardExpr {
+ g.write('/* guard */')
+ }
+ ast.IndexExpr {
+ g.index_expr(node)
+ }
+ ast.InfixExpr {
+ if node.op in [.left_shift, .plus_assign, .minus_assign] {
+ g.inside_map_infix = true
+ g.infix_expr(node)
+ g.inside_map_infix = false
+ } else {
+ g.infix_expr(node)
+ }
+ }
+ ast.IntegerLiteral {
+ if node.val.starts_with('0o') {
+ g.write('0')
+ g.write(node.val[2..])
+ } else if node.val.starts_with('-0o') {
+ g.write('-0')
+ g.write(node.val[3..])
+ } else {
+ g.write(node.val) // .int().str())
+ }
+ }
+ ast.LockExpr {
+ g.lock_expr(node)
+ }
+ ast.MatchExpr {
+ g.match_expr(node)
+ }
+ ast.MapInit {
+ g.map_init(node)
+ }
+ ast.NodeError {}
+ ast.None {
+ g.write('_const_none__')
+ }
+ ast.OrExpr {
+ // this should never appear here
+ }
+ ast.ParExpr {
+ g.write('(')
+ g.expr(node.expr)
+ g.write(')')
+ }
+ ast.PostfixExpr {
+ if node.auto_locked != '' {
+ g.writeln('sync__RwMutex_lock(&$node.auto_locked->mtx);')
+ }
+ g.inside_map_postfix = true
+ if node.expr.is_auto_deref_var() {
+ g.write('(*')
+ g.expr(node.expr)
+ g.write(')')
+ } else {
+ g.expr(node.expr)
+ }
+ g.inside_map_postfix = false
+ g.write(node.op.str())
+ if node.auto_locked != '' {
+ g.writeln(';')
+ g.write('sync__RwMutex_unlock(&$node.auto_locked->mtx)')
+ }
+ }
+ ast.PrefixExpr {
+ gen_or := node.op == .arrow && (node.or_block.kind != .absent || node.is_option)
+ if node.op == .amp {
+ g.is_amp = true
+ }
+ if node.op == .arrow {
+ styp := g.typ(node.right_type)
+ right_sym := g.table.get_type_symbol(node.right_type)
+ mut right_inf := right_sym.info as ast.Chan
+ elem_type := right_inf.elem_type
+ is_gen_or_and_assign_rhs := gen_or && !g.discard_or_result
+ cur_line := if is_gen_or_and_assign_rhs {
+ line := g.go_before_stmt(0)
+ g.out.write_string(util.tabs(g.indent))
+ line
+ } else {
+ ''
+ }
+ tmp_opt := if gen_or { g.new_tmp_var() } else { '' }
+ if gen_or {
+ opt_elem_type := g.typ(elem_type.set_flag(.optional))
+ g.register_chan_pop_optional_call(opt_elem_type, styp)
+ g.write('$opt_elem_type $tmp_opt = __Option_${styp}_popval(')
+ } else {
+ g.write('__${styp}_popval(')
+ }
+ g.expr(node.right)
+ g.write(')')
+ if gen_or {
+ if !node.is_option {
+ g.or_block(tmp_opt, node.or_block, elem_type)
+ }
+ if is_gen_or_and_assign_rhs {
+ elem_styp := g.typ(elem_type)
+ g.write(';\n$cur_line*($elem_styp*)${tmp_opt}.data')
+ }
+ }
+ } else {
+ // g.write('/*pref*/')
+ if !(g.is_amp && node.right.is_auto_deref_var()) {
+ g.write(node.op.str())
+ }
+ // g.write('(')
+ g.expr(node.right)
+ }
+ g.is_amp = false
+ }
+ ast.RangeExpr {
+ // Only used in IndexExpr
+ }
+ ast.SelectExpr {
+ g.select_expr(node)
+ }
+ ast.SizeOf {
+ typ := if node.typ == g.field_data_type { g.comp_for_field_value.typ } else { node.typ }
+ node_typ := g.unwrap_generic(typ)
+ sym := g.table.get_type_symbol(node_typ)
+ if sym.language == .v && sym.kind in [.placeholder, .any] {
+ g.error('unknown type `$sym.name`', node.pos)
+ }
+ styp := g.typ(node_typ)
+ g.write('/*SizeOf*/ sizeof(${util.no_dots(styp)})')
+ }
+ ast.IsRefType {
+ typ := if node.typ == g.field_data_type { g.comp_for_field_value.typ } else { node.typ }
+ node_typ := g.unwrap_generic(typ)
+ sym := g.table.get_type_symbol(node_typ)
+ if sym.language == .v && sym.kind in [.placeholder, .any] {
+ g.error('unknown type `$sym.name`', node.pos)
+ }
+ is_ref_type := g.contains_ptr(node_typ)
+ g.write('/*IsRefType*/ $is_ref_type')
+ }
+ ast.OffsetOf {
+ styp := g.typ(node.struct_type)
+ g.write('/*OffsetOf*/ (u32)(__offsetof(${util.no_dots(styp)}, $node.field))')
+ }
+ ast.SqlExpr {
+ g.sql_select_expr(node)
+ }
+ ast.StringLiteral {
+ g.string_literal(node)
+ }
+ ast.StringInterLiteral {
+ g.string_inter_literal(node)
+ }
+ ast.StructInit {
+ if node.unresolved {
+ g.expr(ast.resolve_init(node, g.unwrap_generic(node.typ), g.table))
+ } else {
+ // `user := User{name: 'Bob'}`
+ g.struct_init(node)
+ }
+ }
+ ast.SelectorExpr {
+ g.selector_expr(node)
+ }
+ ast.TypeNode {
+ // match sum Type
+ // g.write('/* Type */')
+ // type_idx := node.typ.idx()
+ typ := g.unwrap_generic(node.typ)
+ sym := g.table.get_type_symbol(typ)
+ sidx := g.type_sidx(typ)
+ // g.write('$type_idx /* $sym.name */')
+ g.write('$sidx /* $sym.name */')
+ }
+ ast.TypeOf {
+ g.typeof_expr(node)
+ }
+ ast.Likely {
+ if node.is_likely {
+ g.write('_likely_')
+ } else {
+ g.write('_unlikely_')
+ }
+ g.write('(')
+ g.expr(node.expr)
+ g.write(')')
+ }
+ ast.UnsafeExpr {
+ g.expr(node.expr)
+ }
+ }
+ g.discard_or_result = old_discard_or_result
+ g.is_void_expr_stmt = old_is_void_expr_stmt
+}
+
+// T.name, typeof(expr).name
+fn (mut g Gen) type_name(raw_type ast.Type) {
+ typ := if raw_type == g.field_data_type { g.comp_for_field_value.typ } else { raw_type }
+ sym := g.table.get_type_symbol(typ)
+ mut s := ''
+ if sym.kind == .function {
+ if typ.is_ptr() {
+ s = '&' + g.fn_decl_str(sym.info as ast.FnType)
+ } else {
+ s = g.fn_decl_str(sym.info as ast.FnType)
+ }
+ } else {
+ s = g.table.type_to_str(g.unwrap_generic(typ))
+ }
+ g.write('_SLIT("${util.strip_main_name(s)}")')
+}
+
+fn (mut g Gen) typeof_expr(node ast.TypeOf) {
+ typ := if node.expr_type == g.field_data_type {
+ g.comp_for_field_value.typ
+ } else {
+ node.expr_type
+ }
+ sym := g.table.get_type_symbol(typ)
+ if sym.kind == .sum_type {
+ // When encountering a .sum_type, typeof() should be done at runtime,
+ // because the subtype of the expression may change:
+ g.write('charptr_vstring_literal( /* $sym.name */ v_typeof_sumtype_${sym.cname}( (')
+ g.expr(node.expr)
+ g.write(')._typ ))')
+ } else if sym.kind == .array_fixed {
+ fixed_info := sym.info as ast.ArrayFixed
+ typ_name := g.table.get_type_name(fixed_info.elem_type)
+ g.write('_SLIT("[$fixed_info.size]${util.strip_main_name(typ_name)}")')
+ } else if sym.kind == .function {
+ info := sym.info as ast.FnType
+ g.write('_SLIT("${g.fn_decl_str(info)}")')
+ } else if typ.has_flag(.variadic) {
+ varg_elem_type_sym := g.table.get_type_symbol(g.table.value_type(typ))
+ g.write('_SLIT("...${util.strip_main_name(varg_elem_type_sym.name)}")')
+ } else {
+ x := g.table.type_to_str(typ)
+ y := util.strip_main_name(x)
+ g.write('_SLIT("$y")')
+ }
+}
+
+fn (mut g Gen) selector_expr(node ast.SelectorExpr) {
+ prevent_sum_type_unwrapping_once := g.prevent_sum_type_unwrapping_once
+ g.prevent_sum_type_unwrapping_once = false
+ if node.name_type > 0 {
+ match node.gkind_field {
+ .name {
+ g.type_name(node.name_type)
+ return
+ }
+ .typ {
+ g.write(int(g.unwrap_generic(node.name_type)).str())
+ return
+ }
+ .unknown {
+ if node.field_name == 'name' {
+ // typeof(expr).name
+ g.type_name(node.name_type)
+ return
+ } else if node.field_name == 'idx' {
+ // typeof(expr).idx
+ g.write(int(g.unwrap_generic(node.name_type)).str())
+ return
+ }
+ g.error('unknown generic field', node.pos)
+ }
+ }
+ }
+ if node.expr_type == 0 {
+ g.checker_bug('unexpected SelectorExpr.expr_type = 0', node.pos)
+ }
+ sym := g.table.get_type_symbol(g.unwrap_generic(node.expr_type))
+ // if node expr is a root ident and an optional
+ mut is_optional := node.expr is ast.Ident && node.expr_type.has_flag(.optional)
+ if is_optional {
+ opt_base_typ := g.base_type(node.expr_type)
+ g.writeln('(*($opt_base_typ*)')
+ }
+ if sym.kind in [.interface_, .sum_type] {
+ g.write('(*(')
+ }
+ if sym.kind == .array_fixed {
+ if node.field_name != 'len' {
+ g.error('field_name should be `len`', node.pos)
+ }
+ info := sym.info as ast.ArrayFixed
+ g.write('$info.size')
+ return
+ }
+ if sym.kind == .chan && (node.field_name == 'len' || node.field_name == 'closed') {
+ g.write('sync__Channel_${node.field_name}(')
+ g.expr(node.expr)
+ g.write(')')
+ return
+ }
+ mut sum_type_deref_field := ''
+ mut sum_type_dot := '.'
+ if f := g.table.find_field(sym, node.field_name) {
+ field_sym := g.table.get_type_symbol(f.typ)
+ if field_sym.kind in [.sum_type, .interface_] {
+ if !prevent_sum_type_unwrapping_once {
+ // check first if field is sum type because scope searching is expensive
+ scope := g.file.scope.innermost(node.pos.pos)
+ if field := scope.find_struct_field(node.expr.str(), node.expr_type, node.field_name) {
+ if field.orig_type.is_ptr() {
+ sum_type_dot = '->'
+ }
+ for i, typ in field.smartcasts {
+ g.write('(')
+ if field_sym.kind == .sum_type {
+ g.write('*')
+ }
+ cast_sym := g.table.get_type_symbol(typ)
+ if i != 0 {
+ dot := if field.typ.is_ptr() { '->' } else { '.' }
+ sum_type_deref_field += ')$dot'
+ }
+ if mut cast_sym.info is ast.Aggregate {
+ agg_sym := g.table.get_type_symbol(cast_sym.info.types[g.aggregate_type_idx])
+ sum_type_deref_field += '_$agg_sym.cname'
+ } else {
+ sum_type_deref_field += '_$cast_sym.cname'
+ }
+ }
+ }
+ }
+ }
+ }
+ n_ptr := node.expr_type.nr_muls() - 1
+ if n_ptr > 0 {
+ g.write('(')
+ g.write('*'.repeat(n_ptr))
+ g.expr(node.expr)
+ g.write(')')
+ } else {
+ g.expr(node.expr)
+ }
+ if is_optional {
+ g.write('.data)')
+ }
+ // struct embedding
+ if sym.info is ast.Struct || sym.info is ast.Aggregate {
+ if node.from_embed_type != 0 {
+ embed_sym := g.table.get_type_symbol(node.from_embed_type)
+ embed_name := embed_sym.embed_name()
+ if node.expr_type.is_ptr() {
+ g.write('->')
+ } else {
+ g.write('.')
+ }
+ g.write(embed_name)
+ }
+ }
+ if (node.expr_type.is_ptr() || sym.kind == .chan) && node.from_embed_type == 0 {
+ g.write('->')
+ } else {
+ // g.write('. /*typ= $it.expr_type */') // ${g.typ(it.expr_type)} /')
+ g.write('.')
+ }
+ if node.expr_type.has_flag(.shared_f) {
+ g.write('val.')
+ }
+ if node.expr_type == 0 {
+ verror('cgen: SelectorExpr | expr_type: 0 | it.expr: `$node.expr` | field: `$node.field_name` | file: $g.file.path | line: $node.pos.line_nr')
+ }
+ field_name := if sym.language == .v { c_name(node.field_name) } else { node.field_name }
+ g.write(field_name)
+ if sum_type_deref_field != '' {
+ g.write('$sum_type_dot$sum_type_deref_field)')
+ }
+ if sym.kind in [.interface_, .sum_type] {
+ g.write('))')
+ }
+}
+
+fn (mut g Gen) enum_expr(node ast.Expr) {
+ match node {
+ ast.EnumVal {
+ g.write(node.val)
+ }
+ else {
+ g.expr(node)
+ }
+ }
+}
+
+fn (mut g Gen) lock_expr(node ast.LockExpr) {
+ g.cur_lock = unsafe { node } // is ok because it is discarded at end of fn
+ defer {
+ g.cur_lock = ast.LockExpr{
+ scope: 0
+ }
+ }
+ tmp_result := if node.is_expr { g.new_tmp_var() } else { '' }
+ mut cur_line := ''
+ if node.is_expr {
+ styp := g.typ(node.typ)
+ cur_line = g.go_before_stmt(0)
+ g.writeln('$styp $tmp_result;')
+ }
+ mut mtxs := ''
+ if node.lockeds.len == 0 {
+ // this should not happen
+ } else if node.lockeds.len == 1 {
+ lock_prefix := if node.is_rlock[0] { 'r' } else { '' }
+ g.write('sync__RwMutex_${lock_prefix}lock(&')
+ g.expr(node.lockeds[0])
+ g.writeln('->mtx);')
+ } else {
+ mtxs = g.new_tmp_var()
+ g.writeln('uintptr_t _arr_$mtxs[$node.lockeds.len];')
+ g.writeln('bool _isrlck_$mtxs[$node.lockeds.len];')
+ mut j := 0
+ for i, is_rlock in node.is_rlock {
+ if !is_rlock {
+ g.write('_arr_$mtxs[$j] = (uintptr_t)&')
+ g.expr(node.lockeds[i])
+ g.writeln('->mtx;')
+ g.writeln('_isrlck_$mtxs[$j] = false;')
+ j++
+ }
+ }
+ for i, is_rlock in node.is_rlock {
+ if is_rlock {
+ g.write('_arr_$mtxs[$j] = (uintptr_t)&')
+ g.expr(node.lockeds[i])
+ g.writeln('->mtx;')
+ g.writeln('_isrlck_$mtxs[$j] = true;')
+ j++
+ }
+ }
+ if node.lockeds.len == 2 {
+ g.writeln('if (_arr_$mtxs[0] > _arr_$mtxs[1]) {')
+ g.writeln('\tuintptr_t _ptr_$mtxs = _arr_$mtxs[0];')
+ g.writeln('\t_arr_$mtxs[0] = _arr_$mtxs[1];')
+ g.writeln('\t_arr_$mtxs[1] = _ptr_$mtxs;')
+ g.writeln('\tbool _bool_$mtxs = _isrlck_$mtxs[0];')
+ g.writeln('\t_isrlck_$mtxs[0] = _isrlck_$mtxs[1];')
+ g.writeln('\t_isrlck_$mtxs[1] = _bool_$mtxs;')
+ g.writeln('}')
+ } else {
+ g.writeln('__sort_ptr(_arr_$mtxs, _isrlck_$mtxs, $node.lockeds.len);')
+ }
+ g.writeln('for (int $mtxs=0; $mtxs<$node.lockeds.len; $mtxs++) {')
+ g.writeln('\tif ($mtxs && _arr_$mtxs[$mtxs] == _arr_$mtxs[$mtxs-1]) continue;')
+ g.writeln('\tif (_isrlck_$mtxs[$mtxs])')
+ g.writeln('\t\tsync__RwMutex_rlock((sync__RwMutex*)_arr_$mtxs[$mtxs]);')
+ g.writeln('\telse')
+ g.writeln('\t\tsync__RwMutex_lock((sync__RwMutex*)_arr_$mtxs[$mtxs]);')
+ g.writeln('}')
+ }
+ g.mtxs = mtxs
+ defer {
+ g.mtxs = ''
+ }
+ g.writeln('/*lock*/ {')
+ g.stmts_with_tmp_var(node.stmts, tmp_result)
+ if node.is_expr {
+ g.writeln(';')
+ }
+ g.writeln('}')
+ g.unlock_locks()
+ if node.is_expr {
+ g.writeln('')
+ g.write(cur_line)
+ g.write('$tmp_result')
+ }
+}
+
+fn (mut g Gen) unlock_locks() {
+ if g.cur_lock.lockeds.len == 0 {
+ } else if g.cur_lock.lockeds.len == 1 {
+ lock_prefix := if g.cur_lock.is_rlock[0] { 'r' } else { '' }
+ g.write('sync__RwMutex_${lock_prefix}unlock(&')
+ g.expr(g.cur_lock.lockeds[0])
+ g.write('->mtx);')
+ } else {
+ g.writeln('for (int $g.mtxs=${g.cur_lock.lockeds.len - 1}; $g.mtxs>=0; $g.mtxs--) {')
+ g.writeln('\tif ($g.mtxs && _arr_$g.mtxs[$g.mtxs] == _arr_$g.mtxs[$g.mtxs-1]) continue;')
+ g.writeln('\tif (_isrlck_$g.mtxs[$g.mtxs])')
+ g.writeln('\t\tsync__RwMutex_runlock((sync__RwMutex*)_arr_$g.mtxs[$g.mtxs]);')
+ g.writeln('\telse')
+ g.writeln('\t\tsync__RwMutex_unlock((sync__RwMutex*)_arr_$g.mtxs[$g.mtxs]);')
+ g.write('}')
+ }
+}
+
+fn (mut g Gen) need_tmp_var_in_match(node ast.MatchExpr) bool {
+ if node.is_expr && node.return_type != ast.void_type && node.return_type != 0 {
+ sym := g.table.get_type_symbol(node.return_type)
+ if sym.kind == .multi_return {
+ return false
+ }
+ for branch in node.branches {
+ if branch.stmts.len > 1 {
+ return true
+ }
+ if branch.stmts.len == 1 {
+ if branch.stmts[0] is ast.ExprStmt {
+ stmt := branch.stmts[0] as ast.ExprStmt
+ if stmt.expr is ast.CallExpr || stmt.expr is ast.IfExpr
+ || stmt.expr is ast.MatchExpr || (stmt.expr is ast.IndexExpr
+ && (stmt.expr as ast.IndexExpr).or_expr.kind != .absent) {
+ return true
+ }
+ }
+ }
+ }
+ }
+ return false
+}
+
+fn (mut g Gen) match_expr(node ast.MatchExpr) {
+ // println('match expr typ=$it.expr_type')
+ // TODO
+ if node.cond_type == 0 {
+ g.writeln('// match 0')
+ return
+ }
+ need_tmp_var := g.need_tmp_var_in_match(node)
+ is_expr := (node.is_expr && node.return_type != ast.void_type) || g.inside_ternary > 0
+ mut cond_var := ''
+ mut tmp_var := ''
+ mut cur_line := ''
+ if is_expr && !need_tmp_var {
+ g.inside_ternary++
+ }
+ if node.cond is ast.Ident || node.cond is ast.SelectorExpr || node.cond is ast.IntegerLiteral
+ || node.cond is ast.StringLiteral || node.cond is ast.FloatLiteral {
+ cond_var = g.expr_string(node.cond)
+ } else {
+ line := if is_expr {
+ g.empty_line = true
+ g.go_before_stmt(0)
+ } else {
+ ''
+ }
+ cond_var = g.new_tmp_var()
+ g.write('${g.typ(node.cond_type)} $cond_var = ')
+ g.expr(node.cond)
+ g.writeln(';')
+ g.stmt_path_pos << g.out.len
+ g.write(line)
+ }
+ if need_tmp_var {
+ g.empty_line = true
+ cur_line = g.go_before_stmt(0).trim_left(' \t')
+ tmp_var = g.new_tmp_var()
+ g.writeln('${g.typ(node.return_type)} $tmp_var = ${g.type_default(node.return_type)};')
+ }
+
+ if is_expr && !need_tmp_var {
+ // brackets needed otherwise '?' will apply to everything on the left
+ g.write('(')
+ }
+ if node.is_sum_type {
+ g.match_expr_sumtype(node, is_expr, cond_var, tmp_var)
+ } else {
+ g.match_expr_classic(node, is_expr, cond_var, tmp_var)
+ }
+ g.write(cur_line)
+ if need_tmp_var {
+ g.write('$tmp_var')
+ }
+ if is_expr && !need_tmp_var {
+ g.write(')')
+ g.decrement_inside_ternary()
+ }
+}
+
+fn (mut g Gen) match_expr_sumtype(node ast.MatchExpr, is_expr bool, cond_var string, tmp_var string) {
+ for j, branch in node.branches {
+ mut sumtype_index := 0
+ // iterates through all types in sumtype branches
+ for {
+ g.aggregate_type_idx = sumtype_index
+ is_last := j == node.branches.len - 1
+ sym := g.table.get_type_symbol(node.cond_type)
+ if branch.is_else || (node.is_expr && is_last && tmp_var.len == 0) {
+ if is_expr && tmp_var.len == 0 {
+ // TODO too many branches. maybe separate ?: matches
+ g.write(' : ')
+ } else {
+ g.writeln('')
+ g.write_v_source_line_info(branch.pos)
+ g.writeln('else {')
+ }
+ } else {
+ if j > 0 || sumtype_index > 0 {
+ if is_expr && tmp_var.len == 0 {
+ g.write(' : ')
+ } else {
+ g.write_v_source_line_info(branch.pos)
+ g.write('else ')
+ }
+ }
+ if is_expr && tmp_var.len == 0 {
+ g.write('(')
+ } else {
+ if j == 0 && sumtype_index == 0 {
+ g.empty_line = true
+ }
+ g.write_v_source_line_info(branch.pos)
+ g.write('if (')
+ }
+ g.write(cond_var)
+ dot_or_ptr := if node.cond_type.is_ptr() { '->' } else { '.' }
+ if sym.kind == .sum_type {
+ g.write('${dot_or_ptr}_typ == ')
+ g.expr(branch.exprs[sumtype_index])
+ } else if sym.kind == .interface_ {
+ if branch.exprs[sumtype_index] is ast.TypeNode {
+ typ := branch.exprs[sumtype_index] as ast.TypeNode
+ branch_sym := g.table.get_type_symbol(g.unwrap_generic(typ.typ))
+ g.write('${dot_or_ptr}_typ == _${sym.cname}_${branch_sym.cname}_index')
+ } else if branch.exprs[sumtype_index] is ast.None && sym.name == 'IError' {
+ g.write('${dot_or_ptr}_typ == _IError_None___index')
+ }
+ }
+ if is_expr && tmp_var.len == 0 {
+ g.write(') ? ')
+ } else {
+ g.writeln(') {')
+ }
+ }
+ if is_expr && tmp_var.len > 0
+ && g.table.get_type_symbol(node.return_type).kind == .sum_type {
+ g.expected_cast_type = node.return_type
+ }
+ g.stmts_with_tmp_var(branch.stmts, tmp_var)
+ g.expected_cast_type = 0
+ if g.inside_ternary == 0 {
+ g.writeln('}')
+ g.stmt_path_pos << g.out.len
+ }
+ sumtype_index++
+ if branch.exprs.len == 0 || sumtype_index == branch.exprs.len {
+ break
+ }
+ }
+ // reset global field for next use
+ g.aggregate_type_idx = 0
+ }
+}
+
+fn (mut g Gen) match_expr_classic(node ast.MatchExpr, is_expr bool, cond_var string, tmp_var string) {
+ type_sym := g.table.get_type_symbol(node.cond_type)
+ for j, branch in node.branches {
+ is_last := j == node.branches.len - 1
+ if branch.is_else || (node.is_expr && is_last && tmp_var.len == 0) {
+ if node.branches.len > 1 {
+ if is_expr && tmp_var.len == 0 {
+ // TODO too many branches. maybe separate ?: matches
+ g.write(' : ')
+ } else {
+ g.writeln('')
+ g.write_v_source_line_info(branch.pos)
+ g.writeln('else {')
+ }
+ }
+ } else {
+ if j > 0 {
+ if is_expr && tmp_var.len == 0 {
+ g.write(' : ')
+ } else {
+ g.writeln('')
+ g.write_v_source_line_info(branch.pos)
+ g.write('else ')
+ }
+ }
+ if is_expr && tmp_var.len == 0 {
+ g.write('(')
+ } else {
+ if j == 0 {
+ g.writeln('')
+ }
+ g.write_v_source_line_info(branch.pos)
+ g.write('if (')
+ }
+ for i, expr in branch.exprs {
+ if i > 0 {
+ g.write(' || ')
+ }
+ match type_sym.kind {
+ .array {
+ ptr_typ := g.gen_array_equality_fn(node.cond_type)
+ g.write('${ptr_typ}_arr_eq($cond_var, ')
+ g.expr(expr)
+ g.write(')')
+ }
+ .array_fixed {
+ ptr_typ := g.gen_fixed_array_equality_fn(node.cond_type)
+ g.write('${ptr_typ}_arr_eq($cond_var, ')
+ g.expr(expr)
+ g.write(')')
+ }
+ .map {
+ ptr_typ := g.gen_map_equality_fn(node.cond_type)
+ g.write('${ptr_typ}_map_eq($cond_var, ')
+ g.expr(expr)
+ g.write(')')
+ }
+ .string {
+ g.write('string__eq($cond_var, ')
+ g.expr(expr)
+ g.write(')')
+ }
+ .struct_ {
+ ptr_typ := g.gen_struct_equality_fn(node.cond_type)
+ g.write('${ptr_typ}_struct_eq($cond_var, ')
+ g.expr(expr)
+ g.write(')')
+ }
+ else {
+ if expr is ast.RangeExpr {
+ // if type is unsigned and low is 0, check is unneeded
+ mut skip_low := false
+ if expr.low is ast.IntegerLiteral {
+ if node.cond_type in [ast.u16_type, ast.u32_type, ast.u64_type]
+ && expr.low.val == '0' {
+ skip_low = true
+ }
+ }
+ g.write('(')
+ if !skip_low {
+ g.write('$cond_var >= ')
+ g.expr(expr.low)
+ g.write(' && ')
+ }
+ g.write('$cond_var <= ')
+ g.expr(expr.high)
+ g.write(')')
+ } else {
+ g.write('$cond_var == (')
+ g.expr(expr)
+ g.write(')')
+ }
+ }
+ }
+ }
+ if is_expr && tmp_var.len == 0 {
+ g.write(') ? ')
+ } else {
+ g.writeln(') {')
+ }
+ }
+ g.stmts_with_tmp_var(branch.stmts, tmp_var)
+ if g.inside_ternary == 0 && node.branches.len >= 1 {
+ g.write('}')
+ }
+ }
+}
+
+fn (mut g Gen) map_init(node ast.MapInit) {
+ key_typ_str := g.typ(node.key_type)
+ value_typ_str := g.typ(node.value_type)
+ value_typ := g.table.get_type_symbol(node.value_type)
+ key_typ := g.table.get_final_type_symbol(node.key_type)
+ hash_fn, key_eq_fn, clone_fn, free_fn := g.map_fn_ptrs(key_typ)
+ size := node.vals.len
+ mut shared_styp := '' // only needed for shared &[]{...}
+ mut styp := ''
+ is_amp := g.is_amp
+ g.is_amp = false
+ if is_amp {
+ g.out.go_back(1) // delete the `&` already generated in `prefix_expr()
+ }
+ if g.is_shared {
+ mut shared_typ := node.typ.set_flag(.shared_f)
+ shared_styp = g.typ(shared_typ)
+ g.writeln('($shared_styp*)__dup_shared_map(&($shared_styp){.mtx = {0}, .val =')
+ } else if is_amp {
+ styp = g.typ(node.typ)
+ g.write('($styp*)memdup(ADDR($styp, ')
+ }
+ noscan_key := g.check_noscan(node.key_type)
+ noscan_value := g.check_noscan(node.value_type)
+ mut noscan := if noscan_key.len != 0 || noscan_value.len != 0 { '_noscan' } else { '' }
+ if noscan.len != 0 {
+ if noscan_key.len != 0 {
+ noscan += '_key'
+ }
+ if noscan_value.len != 0 {
+ noscan += '_value'
+ }
+ }
+ if size > 0 {
+ if value_typ.kind == .function {
+ g.write('new_map_init${noscan}($hash_fn, $key_eq_fn, $clone_fn, $free_fn, $size, sizeof($key_typ_str), sizeof(voidptr), _MOV(($key_typ_str[$size]){')
+ } else {
+ g.write('new_map_init${noscan}($hash_fn, $key_eq_fn, $clone_fn, $free_fn, $size, sizeof($key_typ_str), sizeof($value_typ_str), _MOV(($key_typ_str[$size]){')
+ }
+ for expr in node.keys {
+ g.expr(expr)
+ g.write(', ')
+ }
+ if value_typ.kind == .function {
+ g.write('}), _MOV((voidptr[$size]){')
+ } else {
+ g.write('}), _MOV(($value_typ_str[$size]){')
+ }
+ for expr in node.vals {
+ if expr.is_auto_deref_var() {
+ g.write('*')
+ }
+ g.expr(expr)
+ g.write(', ')
+ }
+ g.write('}))')
+ } else {
+ g.write('new_map${noscan}(sizeof($key_typ_str), sizeof($value_typ_str), $hash_fn, $key_eq_fn, $clone_fn, $free_fn)')
+ }
+ if g.is_shared {
+ g.write('}, sizeof($shared_styp))')
+ } else if is_amp {
+ g.write('), sizeof($styp))')
+ }
+}
+
+fn (mut g Gen) select_expr(node ast.SelectExpr) {
+ is_expr := node.is_expr || g.inside_ternary > 0
+ cur_line := if is_expr {
+ g.empty_line = true
+ g.go_before_stmt(0)
+ } else {
+ ''
+ }
+ n_channels := if node.has_exception { node.branches.len - 1 } else { node.branches.len }
+ mut channels := []ast.Expr{cap: n_channels}
+ mut objs := []ast.Expr{cap: n_channels}
+ mut tmp_objs := []string{cap: n_channels}
+ mut elem_types := []string{cap: n_channels}
+ mut is_push := []bool{cap: n_channels}
+ mut has_else := false
+ mut has_timeout := false
+ mut timeout_expr := ast.empty_expr()
+ mut exception_branch := -1
+ for j, branch in node.branches {
+ if branch.is_else {
+ has_else = true
+ exception_branch = j
+ } else if branch.is_timeout {
+ has_timeout = true
+ exception_branch = j
+ timeout_expr = (branch.stmt as ast.ExprStmt).expr
+ } else {
+ match branch.stmt {
+ ast.ExprStmt {
+ // send expression
+ expr := branch.stmt.expr as ast.InfixExpr
+ channels << expr.left
+ if expr.right is ast.Ident || expr.right is ast.IndexExpr
+ || expr.right is ast.SelectorExpr || expr.right is ast.StructInit {
+ // addressable objects in the `C` output
+ objs << expr.right
+ tmp_objs << ''
+ elem_types << ''
+ } else {
+ // must be evaluated to tmp var before real `select` is performed
+ objs << ast.empty_expr()
+ tmp_obj := g.new_tmp_var()
+ tmp_objs << tmp_obj
+ el_stype := g.typ(g.table.mktyp(expr.right_type))
+ g.writeln('$el_stype $tmp_obj;')
+ }
+ is_push << true
+ }
+ ast.AssignStmt {
+ rec_expr := branch.stmt.right[0] as ast.PrefixExpr
+ channels << rec_expr.right
+ is_push << false
+ // create tmp unless the object with *exactly* the type we need exists already
+ if branch.stmt.op == .decl_assign
+ || branch.stmt.right_types[0] != branch.stmt.left_types[0] {
+ tmp_obj := g.new_tmp_var()
+ tmp_objs << tmp_obj
+ el_stype := g.typ(branch.stmt.right_types[0])
+ elem_types << if branch.stmt.op == .decl_assign {
+ el_stype + ' '
+ } else {
+ ''
+ }
+ g.writeln('$el_stype $tmp_obj;')
+ } else {
+ tmp_objs << ''
+ elem_types << ''
+ }
+ objs << branch.stmt.left[0]
+ }
+ else {}
+ }
+ }
+ }
+ chan_array := g.new_tmp_var()
+ g.write('Array_sync__Channel_ptr $chan_array = new_array_from_c_array($n_channels, $n_channels, sizeof(sync__Channel*), _MOV((sync__Channel*[$n_channels]){')
+ for i in 0 .. n_channels {
+ if i > 0 {
+ g.write(', ')
+ }
+ g.write('(sync__Channel*)(')
+ g.expr(channels[i])
+ g.write(')')
+ }
+ g.writeln('}));\n')
+ directions_array := g.new_tmp_var()
+ g.write('Array_sync__Direction $directions_array = new_array_from_c_array($n_channels, $n_channels, sizeof(sync__Direction), _MOV((sync__Direction[$n_channels]){')
+ for i in 0 .. n_channels {
+ if i > 0 {
+ g.write(', ')
+ }
+ if is_push[i] {
+ g.write('sync__Direction__push')
+ } else {
+ g.write('sync__Direction__pop')
+ }
+ }
+ g.writeln('}));\n')
+ objs_array := g.new_tmp_var()
+ g.write('Array_voidptr $objs_array = new_array_from_c_array($n_channels, $n_channels, sizeof(voidptr), _MOV((voidptr[$n_channels]){')
+ for i in 0 .. n_channels {
+ if i > 0 {
+ g.write(', &')
+ } else {
+ g.write('&')
+ }
+ if tmp_objs[i] == '' {
+ g.expr(objs[i])
+ } else {
+ g.write(tmp_objs[i])
+ }
+ }
+ g.writeln('}));\n')
+ select_result := g.new_tmp_var()
+ g.write('int $select_result = sync__channel_select(&/*arr*/$chan_array, $directions_array, &/*arr*/$objs_array, ')
+ if has_timeout {
+ g.expr(timeout_expr)
+ } else if has_else {
+ g.write('0')
+ } else {
+ g.write('_const_time__infinite')
+ }
+ g.writeln(');')
+ // free the temps that were created
+ g.writeln('array_free(&$objs_array);')
+ g.writeln('array_free(&$directions_array);')
+ g.writeln('array_free(&$chan_array);')
+ mut i := 0
+ for j in 0 .. node.branches.len {
+ if j > 0 {
+ g.write('} else ')
+ }
+ g.write('if ($select_result == ')
+ if j == exception_branch {
+ g.writeln('-1) {')
+ } else {
+ g.writeln('$i) {')
+ if !is_push[i] && tmp_objs[i] != '' {
+ g.write('\t${elem_types[i]}')
+ g.expr(objs[i])
+ g.writeln(' = ${tmp_objs[i]};')
+ }
+ i++
+ }
+ g.stmts(node.branches[j].stmts)
+ }
+ g.writeln('}')
+ if is_expr {
+ g.empty_line = false
+ g.write(cur_line)
+ g.write('($select_result != -2)')
+ }
+}
+
+fn (mut g Gen) ident(node ast.Ident) {
+ prevent_sum_type_unwrapping_once := g.prevent_sum_type_unwrapping_once
+ g.prevent_sum_type_unwrapping_once = false
+ if node.name == 'lld' {
+ return
+ }
+ if node.name.starts_with('C.') {
+ g.write(util.no_dots(node.name[2..]))
+ return
+ }
+ if node.kind == .constant { // && !node.name.starts_with('g_') {
+ // TODO globals hack
+ g.write('_const_')
+ }
+ mut name := c_name(node.name)
+ // TODO: temporary, remove this
+ node_info := node.info
+ mut is_auto_heap := false
+ if node_info is ast.IdentVar {
+ // x ?int
+ // `x = 10` => `x.data = 10` (g.right_is_opt == false)
+ // `x = new_opt()` => `x = new_opt()` (g.right_is_opt == true)
+ // `println(x)` => `println(*(int*)x.data)`
+ if node_info.is_optional && !(g.is_assign_lhs && g.right_is_opt) {
+ g.write('/*opt*/')
+ styp := g.base_type(node_info.typ)
+ g.write('(*($styp*)${name}.data)')
+ return
+ }
+ if !g.is_assign_lhs && node_info.share == .shared_t {
+ g.write('${name}.val')
+ return
+ }
+ scope := g.file.scope.innermost(node.pos.pos)
+ if v := scope.find_var(node.name) {
+ is_auto_heap = v.is_auto_heap && (!g.is_assign_lhs || g.assign_op != .decl_assign)
+ if is_auto_heap {
+ g.write('(*(')
+ }
+ if v.smartcasts.len > 0 {
+ v_sym := g.table.get_type_symbol(v.typ)
+ if !prevent_sum_type_unwrapping_once {
+ for _ in v.smartcasts {
+ g.write('(')
+ if v_sym.kind == .sum_type && !is_auto_heap {
+ g.write('*')
+ }
+ }
+ for i, typ in v.smartcasts {
+ cast_sym := g.table.get_type_symbol(g.unwrap_generic(typ))
+ mut is_ptr := false
+ if i == 0 {
+ g.write(name)
+ if v.orig_type.is_ptr() {
+ is_ptr = true
+ }
+ }
+ dot := if is_ptr || is_auto_heap { '->' } else { '.' }
+ if mut cast_sym.info is ast.Aggregate {
+ sym := g.table.get_type_symbol(cast_sym.info.types[g.aggregate_type_idx])
+ g.write('${dot}_$sym.cname')
+ } else {
+ g.write('${dot}_$cast_sym.cname')
+ }
+ g.write(')')
+ }
+ if is_auto_heap {
+ g.write('))')
+ }
+ return
+ }
+ }
+ if v.is_inherited {
+ g.write(closure_ctx + '->')
+ }
+ }
+ } else if node_info is ast.IdentFn {
+ if g.pref.obfuscate && g.cur_mod.name == 'main' && name.starts_with('main__') {
+ key := node.name
+ g.write('/* obf identfn: $key */')
+ name = g.obf_table[key] or {
+ panic('cgen: obf name "$key" not found, this should never happen')
+ }
+ }
+ }
+ g.write(g.get_ternary_name(name))
+ if is_auto_heap {
+ g.write('))')
+ }
+}
+
+fn (mut g Gen) cast_expr(node ast.CastExpr) {
+ if g.is_amp {
+ // &Foo(0) => ((Foo*)0)
+ g.out.go_back(1)
+ }
+ g.is_amp = false
+ sym := g.table.get_type_symbol(node.typ)
+ if sym.kind == .string && !node.typ.is_ptr() {
+ // `string(x)` needs `tos()`, but not `&string(x)
+ // `tos(str, len)`, `tos2(str)`
+ if node.has_arg {
+ g.write('tos((byteptr)')
+ } else {
+ g.write('tos2((byteptr)')
+ }
+ g.expr(node.expr)
+ expr_sym := g.table.get_type_symbol(node.expr_type)
+ if expr_sym.kind == .array {
+ // if we are casting an array, we need to add `.data`
+ g.write('.data')
+ }
+ if node.has_arg {
+ // len argument
+ g.write(', ')
+ g.expr(node.arg)
+ }
+ g.write(')')
+ } else if sym.kind in [.sum_type, .interface_] {
+ g.expr_with_cast(node.expr, node.expr_type, node.typ)
+ } else if sym.kind == .struct_ && !node.typ.is_ptr() && !(sym.info as ast.Struct).is_typedef {
+ // deprecated, replaced by Struct{...exr}
+ styp := g.typ(node.typ)
+ g.write('*(($styp *)(&')
+ g.expr(node.expr)
+ g.write('))')
+ } else if sym.kind == .alias && g.table.get_final_type_symbol(node.typ).kind == .array_fixed {
+ g.expr(node.expr)
+ } else {
+ styp := g.typ(node.typ)
+ mut cast_label := ''
+ // `ast.string_type` is done for MSVC's bug
+ if sym.kind != .alias
+ || (sym.info as ast.Alias).parent_type !in [node.expr_type, ast.string_type] {
+ cast_label = '($styp)'
+ }
+ if node.typ.has_flag(.optional) && node.expr is ast.None {
+ g.gen_optional_error(node.typ, node.expr)
+ } else {
+ g.write('(${cast_label}(')
+ g.expr(node.expr)
+ if node.expr is ast.IntegerLiteral {
+ if node.typ in [ast.u64_type, ast.u32_type, ast.u16_type] {
+ if !node.expr.val.starts_with('-') {
+ g.write('U')
+ }
+ }
+ }
+ g.write('))')
+ }
+ }
+}
+
+fn (mut g Gen) concat_expr(node ast.ConcatExpr) {
+ styp := g.typ(node.return_type)
+ sym := g.table.get_type_symbol(node.return_type)
+ is_multi := sym.kind == .multi_return
+ if !is_multi {
+ g.expr(node.vals[0])
+ } else {
+ g.write('($styp){')
+ for i, expr in node.vals {
+ g.write('.arg$i=')
+ g.expr(expr)
+ if i < node.vals.len - 1 {
+ g.write(',')
+ }
+ }
+ g.write('}')
+ }
+}
+
+fn (mut g Gen) need_tmp_var_in_if(node ast.IfExpr) bool {
+ if node.is_expr && g.inside_ternary == 0 {
+ if g.is_autofree || node.typ.has_flag(.optional) {
+ return true
+ }
+ for branch in node.branches {
+ if branch.cond is ast.IfGuardExpr || branch.stmts.len > 1 {
+ return true
+ }
+ if branch.stmts.len == 1 {
+ if branch.stmts[0] is ast.ExprStmt {
+ stmt := branch.stmts[0] as ast.ExprStmt
+ if stmt.expr is ast.CallExpr {
+ if stmt.expr.is_method {
+ left_sym := g.table.get_type_symbol(stmt.expr.receiver_type)
+ if left_sym.kind in [.array, .array_fixed, .map] {
+ return true
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ return false
+}
+
+fn (mut g Gen) if_expr(node ast.IfExpr) {
+ if node.is_comptime {
+ g.comp_if(node)
+ return
+ }
+ // For simpe if expressions we can use C's `?:`
+ // `if x > 0 { 1 } else { 2 }` => `(x > 0) ? (1) : (2)`
+ // For if expressions with multiple statements or another if expression inside, it's much
+ // easier to use a temp var, than do C tricks with commas, introduce special vars etc
+ // (as it used to be done).
+ // Always use this in -autofree, since ?: can have tmp expressions that have to be freed.
+ needs_tmp_var := g.need_tmp_var_in_if(node)
+ tmp := if needs_tmp_var { g.new_tmp_var() } else { '' }
+ mut cur_line := ''
+ if needs_tmp_var {
+ if node.typ.has_flag(.optional) {
+ g.inside_if_optional = true
+ }
+ styp := g.typ(node.typ)
+ cur_line = g.go_before_stmt(0)
+ g.empty_line = true
+ g.writeln('$styp $tmp; /* if prepend */')
+ if g.infix_left_var_name.len > 0 {
+ g.writeln('if ($g.infix_left_var_name) {')
+ g.indent++
+ }
+ } else if node.is_expr || g.inside_ternary != 0 {
+ g.inside_ternary++
+ g.write('(')
+ for i, branch in node.branches {
+ if i > 0 {
+ g.write(' : ')
+ }
+ if i < node.branches.len - 1 || !node.has_else {
+ g.expr(branch.cond)
+ g.write(' ? ')
+ }
+ g.stmts(branch.stmts)
+ }
+ if node.branches.len == 1 {
+ g.write(': 0')
+ }
+ g.write(')')
+ g.decrement_inside_ternary()
+ return
+ }
+ mut is_guard := false
+ mut guard_idx := 0
+ mut guard_vars := []string{}
+ for i, branch in node.branches {
+ cond := branch.cond
+ if cond is ast.IfGuardExpr {
+ if !is_guard {
+ is_guard = true
+ guard_idx = i
+ guard_vars = []string{len: node.branches.len}
+ }
+ if cond.expr !is ast.IndexExpr && cond.expr !is ast.PrefixExpr {
+ var_name := g.new_tmp_var()
+ guard_vars[i] = var_name
+ g.writeln('${g.typ(cond.expr_type)} $var_name;')
+ } else {
+ guard_vars[i] = ''
+ }
+ }
+ }
+ for i, branch in node.branches {
+ if i > 0 {
+ g.write('} else ')
+ }
+ // if last branch is `else {`
+ if i == node.branches.len - 1 && node.has_else {
+ g.writeln('{')
+ // define `err` only for simple `if val := opt {...} else {`
+ if is_guard && guard_idx == i - 1 {
+ cvar_name := guard_vars[guard_idx]
+ g.writeln('\tIError err = ${cvar_name}.err;')
+ }
+ } else {
+ match branch.cond {
+ ast.IfGuardExpr {
+ mut var_name := guard_vars[i]
+ mut short_opt := false
+ if var_name == '' {
+ short_opt = true // we don't need a further tmp, so use the one we'll get later
+ var_name = g.new_tmp_var()
+ guard_vars[i] = var_name // for `else`
+ g.tmp_count--
+ g.writeln('if (${var_name}.state == 0) {')
+ } else {
+ g.write('if ($var_name = ')
+ g.expr(branch.cond.expr)
+ g.writeln(', ${var_name}.state == 0) {')
+ }
+ if short_opt || branch.cond.var_name != '_' {
+ base_type := g.base_type(branch.cond.expr_type)
+ if short_opt {
+ cond_var_name := if branch.cond.var_name == '_' {
+ '_dummy_${g.tmp_count + 1}'
+ } else {
+ branch.cond.var_name
+ }
+ g.write('\t$base_type $cond_var_name = ')
+ g.expr(branch.cond.expr)
+ g.writeln(';')
+ } else {
+ mut is_auto_heap := false
+ if branch.stmts.len > 0 {
+ scope := g.file.scope.innermost(ast.Node(branch.stmts[branch.stmts.len - 1]).position().pos)
+ if v := scope.find_var(branch.cond.var_name) {
+ is_auto_heap = v.is_auto_heap
+ }
+ }
+ if is_auto_heap {
+ g.writeln('\t$base_type* $branch.cond.var_name = HEAP($base_type, *($base_type*)${var_name}.data);')
+ } else {
+ g.writeln('\t$base_type $branch.cond.var_name = *($base_type*)${var_name}.data;')
+ }
+ }
+ }
+ }
+ else {
+ g.write('if (')
+ g.expr(branch.cond)
+ g.writeln(') {')
+ }
+ }
+ }
+ if needs_tmp_var {
+ g.stmts_with_tmp_var(branch.stmts, tmp)
+ } else {
+ // restore if_expr stmt header pos
+ stmt_pos := g.nth_stmt_pos(0)
+ g.stmts(branch.stmts)
+ g.stmt_path_pos << stmt_pos
+ }
+ }
+ g.writeln('}')
+ if needs_tmp_var {
+ if g.infix_left_var_name.len > 0 {
+ g.indent--
+ g.writeln('}')
+ }
+ g.empty_line = false
+ g.write('$cur_line $tmp')
+ }
+ if node.typ.has_flag(.optional) {
+ g.inside_if_optional = false
+ }
+}
+
+[inline]
+fn (g &Gen) expr_is_multi_return_call(expr ast.Expr) bool {
+ match expr {
+ ast.CallExpr { return g.table.get_type_symbol(expr.return_type).kind == .multi_return }
+ else { return false }
+ }
+}
+
+fn (mut g Gen) gen_optional_error(target_type ast.Type, expr ast.Expr) {
+ styp := g.typ(target_type)
+ g.write('($styp){ .state=2, .err=')
+ g.expr(expr)
+ g.write(', .data={EMPTY_STRUCT_INITIALIZATION} }')
+}
+
+fn (mut g Gen) return_stmt(node ast.Return) {
+ g.write_v_source_line_info(node.pos)
+ if node.exprs.len > 0 {
+ // skip `return $vweb.html()`
+ if node.exprs[0] is ast.ComptimeCall {
+ g.expr(node.exprs[0])
+ g.writeln(';')
+ return
+ }
+ }
+
+ g.inside_return = true
+ defer {
+ g.inside_return = false
+ }
+ // got to do a correct check for multireturn
+ sym := g.table.get_type_symbol(g.fn_decl.return_type)
+ fn_return_is_multi := sym.kind == .multi_return
+ fn_return_is_optional := g.fn_decl.return_type.has_flag(.optional)
+ mut has_semicolon := false
+ if node.exprs.len == 0 {
+ g.write_defer_stmts_when_needed()
+ if fn_return_is_optional {
+ styp := g.typ(g.fn_decl.return_type)
+ g.writeln('return ($styp){0};')
+ } else {
+ if g.is_autofree {
+ g.trace_autofree('// free before return (no values returned)')
+ g.autofree_scope_vars(node.pos.pos - 1, node.pos.line_nr, true)
+ }
+ g.writeln('return;')
+ }
+ return
+ }
+ tmpvar := g.new_tmp_var()
+ ret_typ := g.typ(g.fn_decl.return_type)
+ mut use_tmp_var := g.defer_stmts.len > 0 || g.defer_profile_code.len > 0
+ // handle promoting none/error/function returning 'Option'
+ if fn_return_is_optional {
+ optional_none := node.exprs[0] is ast.None
+ ftyp := g.typ(node.types[0])
+ mut is_regular_option := ftyp == 'Option'
+ if optional_none || is_regular_option || node.types[0] == ast.error_type_idx {
+ if !isnil(g.fn_decl) && g.fn_decl.is_test {
+ test_error_var := g.new_tmp_var()
+ g.write('$ret_typ $test_error_var = ')
+ g.gen_optional_error(g.fn_decl.return_type, node.exprs[0])
+ g.writeln(';')
+ g.write_defer_stmts_when_needed()
+ g.gen_failing_return_error_for_test_fn(node, test_error_var)
+ return
+ }
+ if use_tmp_var {
+ g.write('$ret_typ $tmpvar = ')
+ } else {
+ g.write('return ')
+ }
+ g.gen_optional_error(g.fn_decl.return_type, node.exprs[0])
+ g.writeln(';')
+ if use_tmp_var {
+ g.write_defer_stmts_when_needed()
+ g.writeln('return $tmpvar;')
+ }
+ return
+ }
+ }
+ // regular cases
+ if fn_return_is_multi && node.exprs.len > 0 && !g.expr_is_multi_return_call(node.exprs[0]) {
+ if node.exprs.len == 1 && node.exprs[0] is ast.IfExpr {
+ // use a temporary for `return if cond { x,y } else { a,b }`
+ g.write('$ret_typ $tmpvar = ')
+ g.expr(node.exprs[0])
+ g.writeln(';')
+ g.write_defer_stmts_when_needed()
+ g.writeln('return $tmpvar;')
+ return
+ }
+ // typ_sym := g.table.get_type_symbol(g.fn_decl.return_type)
+ // mr_info := typ_sym.info as ast.MultiReturn
+ mut styp := ''
+ if fn_return_is_optional {
+ g.writeln('$ret_typ $tmpvar;')
+ styp = g.base_type(g.fn_decl.return_type)
+ g.write('opt_ok(&($styp/*X*/[]) { ')
+ } else {
+ if use_tmp_var {
+ g.write('$ret_typ $tmpvar = ')
+ } else {
+ g.write('return ')
+ }
+ styp = g.typ(g.fn_decl.return_type)
+ }
+ // Use this to keep the tmp assignments in order
+ mut multi_unpack := ''
+ g.write('($styp){')
+ mut arg_idx := 0
+ for i, expr in node.exprs {
+ // Check if we are dealing with a multi return and handle it seperately
+ if g.expr_is_multi_return_call(expr) {
+ c := expr as ast.CallExpr
+ expr_sym := g.table.get_type_symbol(c.return_type)
+ // Create a tmp for this call
+ mut tmp := g.new_tmp_var()
+ if !c.return_type.has_flag(.optional) {
+ s := g.go_before_stmt(0)
+ expr_styp := g.typ(c.return_type)
+ g.write('$expr_styp $tmp=')
+ g.expr(expr)
+ g.writeln(';')
+ multi_unpack += g.go_before_stmt(0)
+ g.write(s)
+ } else {
+ s := g.go_before_stmt(0)
+ // TODO
+ // I (emily) am sorry for doing this
+ // I cant find another way to do this so right now
+ // this will have to do.
+ g.tmp_count--
+ g.expr(expr)
+ multi_unpack += g.go_before_stmt(0)
+ g.write(s)
+ // modify tmp so that it is the opt deref
+ // TODO copy-paste from cgen.v:2397
+ expr_styp := g.base_type(c.return_type)
+ tmp = ('/*opt*/(*($expr_styp*)${tmp}.data)')
+ }
+ expr_types := expr_sym.mr_info().types
+ for j, _ in expr_types {
+ g.write('.arg$arg_idx=${tmp}.arg$j')
+ if j < expr_types.len || i < node.exprs.len - 1 {
+ g.write(',')
+ }
+ arg_idx++
+ }
+ continue
+ }
+ g.write('.arg$arg_idx=')
+ if expr.is_auto_deref_var() {
+ g.write('*')
+ }
+ g.expr(expr)
+ arg_idx++
+ if i < node.exprs.len - 1 {
+ g.write(', ')
+ }
+ }
+ g.write('}')
+ if fn_return_is_optional {
+ g.writeln(' }, (Option*)(&$tmpvar), sizeof($styp));')
+ g.write_defer_stmts_when_needed()
+ g.write('return $tmpvar')
+ }
+ // Make sure to add our unpacks
+ if multi_unpack.len > 0 {
+ g.insert_before_stmt(multi_unpack)
+ }
+ if use_tmp_var && !fn_return_is_optional {
+ if !has_semicolon {
+ g.writeln(';')
+ }
+ g.write_defer_stmts_when_needed()
+ g.writeln('return $tmpvar;')
+ has_semicolon = true
+ }
+ } else if node.exprs.len >= 1 {
+ // normal return
+ return_sym := g.table.get_type_symbol(node.types[0])
+ expr0 := node.exprs[0]
+ // `return opt_ok(expr)` for functions that expect an optional
+ expr_type_is_opt := match expr0 {
+ ast.CallExpr {
+ expr0.return_type.has_flag(.optional) && expr0.or_block.kind == .absent
+ }
+ else {
+ node.types[0].has_flag(.optional)
+ }
+ }
+ if fn_return_is_optional && !expr_type_is_opt && return_sym.name != 'Option' {
+ styp := g.base_type(g.fn_decl.return_type)
+ g.writeln('$ret_typ $tmpvar;')
+ g.write('opt_ok(&($styp[]) { ')
+ if !g.fn_decl.return_type.is_ptr() && node.types[0].is_ptr() {
+ if !(node.exprs[0] is ast.Ident && !g.is_amp) {
+ g.write('*')
+ }
+ }
+ for i, expr in node.exprs {
+ g.expr_with_cast(expr, node.types[i], g.fn_decl.return_type.clear_flag(.optional))
+ if i < node.exprs.len - 1 {
+ g.write(', ')
+ }
+ }
+ g.writeln(' }, (Option*)(&$tmpvar), sizeof($styp));')
+ g.write_defer_stmts_when_needed()
+ g.autofree_scope_vars(node.pos.pos - 1, node.pos.line_nr, true)
+ g.writeln('return $tmpvar;')
+ return
+ }
+ // autofree before `return`
+ // set free_parent_scopes to true, since all variables defined in parent
+ // scopes need to be freed before the return
+ if g.is_autofree {
+ expr := node.exprs[0]
+ if expr is ast.Ident {
+ g.returned_var_name = expr.name
+ }
+ }
+ // free := g.is_autofree && !g.is_builtin_mod // node.exprs[0] is ast.CallExpr
+ // Create a temporary variable for the return expression
+ use_tmp_var = use_tmp_var || !g.is_builtin_mod // node.exprs[0] is ast.CallExpr
+ if use_tmp_var {
+ // `return foo(a, b, c)`
+ // `tmp := foo(a, b, c); free(a); free(b); free(c); return tmp;`
+ // Save return value in a temp var so that all args (a,b,c) can be freed
+ // Don't use a tmp var if a variable is simply returned: `return x`
+ // Just in case of defer statements exists, that the return values cannot
+ // be modified.
+ if node.exprs[0] !is ast.Ident || use_tmp_var {
+ g.write('$ret_typ $tmpvar = ')
+ } else {
+ use_tmp_var = false
+ g.write_defer_stmts_when_needed()
+ if !g.is_builtin_mod {
+ g.autofree_scope_vars(node.pos.pos - 1, node.pos.line_nr, true)
+ }
+ g.write('return ')
+ }
+ } else {
+ g.autofree_scope_vars(node.pos.pos - 1, node.pos.line_nr, true)
+ g.write('return ')
+ }
+ if expr0.is_auto_deref_var() {
+ if g.fn_decl.return_type.is_ptr() {
+ var_str := g.expr_string(expr0)
+ g.write(var_str.trim('&'))
+ } else {
+ g.write('*')
+ g.expr(expr0)
+ }
+ } else {
+ g.expr_with_cast(node.exprs[0], node.types[0], g.fn_decl.return_type)
+ }
+ if use_tmp_var {
+ g.writeln(';')
+ has_semicolon = true
+ g.write_defer_stmts_when_needed()
+ if !g.is_builtin_mod {
+ g.autofree_scope_vars(node.pos.pos - 1, node.pos.line_nr, true)
+ }
+ g.write('return $tmpvar')
+ has_semicolon = false
+ }
+ } else { // if node.exprs.len == 0 {
+ println('this should never happen')
+ g.write('/*F*/return')
+ }
+ if !has_semicolon {
+ g.writeln(';')
+ }
+}
+
+fn (mut g Gen) const_decl(node ast.ConstDecl) {
+ g.inside_const = true
+ defer {
+ g.inside_const = false
+ }
+ for field in node.fields {
+ if g.pref.skip_unused {
+ if field.name !in g.table.used_consts {
+ $if trace_skip_unused_consts ? {
+ eprintln('>> skipping unused const name: $field.name')
+ }
+ continue
+ }
+ }
+ name := c_name(field.name)
+ field_expr := field.expr
+ match field.expr {
+ ast.ArrayInit {
+ if field.expr.is_fixed {
+ styp := g.typ(field.expr.typ)
+ if g.pref.build_mode != .build_module {
+ val := g.expr_string(field.expr)
+ g.definitions.writeln('$styp _const_$name = $val; // fixed array const')
+ } else {
+ g.definitions.writeln('$styp _const_$name; // fixed array const')
+ }
+ } else {
+ g.const_decl_init_later(field.mod, name, field.expr, field.typ, false)
+ }
+ }
+ ast.StringLiteral {
+ g.definitions.writeln('string _const_$name; // a string literal, inited later')
+ if g.pref.build_mode != .build_module {
+ val := g.expr_string(field.expr)
+ g.stringliterals.writeln('\t_const_$name = $val;')
+ }
+ }
+ ast.CallExpr {
+ if field.expr.return_type.has_flag(.optional) {
+ unwrap_option := field.expr.or_block.kind != .absent
+ g.const_decl_init_later(field.mod, name, field.expr, field.typ, unwrap_option)
+ } else {
+ g.const_decl_init_later(field.mod, name, field.expr, field.typ, false)
+ }
+ }
+ else {
+ if g.pref.build_mode != .build_module {
+ if ct_value := field.comptime_expr_value() {
+ if g.const_decl_precomputed(field.mod, name, ct_value, field.typ) {
+ continue
+ }
+ }
+ }
+ if field.is_simple_define_const() {
+ // "Simple" expressions are not going to need multiple statements,
+ // only the ones which are inited later, so it's safe to use expr_string
+ g.const_decl_simple_define(name, g.expr_string(field_expr))
+ } else {
+ g.const_decl_init_later(field.mod, name, field.expr, field.typ, false)
+ }
+ }
+ }
+ }
+}
+
+fn (mut g Gen) const_decl_precomputed(mod string, name string, ct_value ast.ComptTimeConstValue, typ ast.Type) bool {
+ mut styp := g.typ(typ)
+ cname := '_const_$name'
+ $if trace_const_precomputed ? {
+ eprintln('> styp: $styp | cname: $cname | ct_value: $ct_value | $ct_value.type_name()')
+ }
+ match ct_value {
+ i8 {
+ g.const_decl_write_precomputed(styp, cname, ct_value.str())
+ }
+ i16 {
+ g.const_decl_write_precomputed(styp, cname, ct_value.str())
+ }
+ int {
+ g.const_decl_write_precomputed(styp, cname, ct_value.str())
+ }
+ i64 {
+ if typ == ast.i64_type {
+ return false
+ }
+ if typ == ast.int_type {
+ // TODO: use g.const_decl_write_precomputed here too.
+ // For now, use #define macros, so existing code compiles
+ // with -cstrict. Add checker errors for overflows instead,
+ // so V can catch them earlier, instead of relying on the
+ // C compiler for that.
+ g.const_decl_simple_define(name, ct_value.str())
+ return true
+ }
+ if typ == ast.u64_type {
+ g.const_decl_write_precomputed(styp, cname, ct_value.str() + 'U')
+ } else {
+ g.const_decl_write_precomputed(styp, cname, ct_value.str())
+ }
+ }
+ byte {
+ g.const_decl_write_precomputed(styp, cname, ct_value.str())
+ }
+ u16 {
+ g.const_decl_write_precomputed(styp, cname, ct_value.str())
+ }
+ u32 {
+ g.const_decl_write_precomputed(styp, cname, ct_value.str())
+ }
+ u64 {
+ g.const_decl_write_precomputed(styp, cname, ct_value.str() + 'U')
+ }
+ f32 {
+ g.const_decl_write_precomputed(styp, cname, ct_value.str())
+ }
+ f64 {
+ g.const_decl_write_precomputed(styp, cname, ct_value.str())
+ }
+ rune {
+ rune_code := u32(ct_value)
+ if rune_code <= 255 {
+ if rune_code in [`"`, `\\`, `'`] {
+ return false
+ }
+ escval := util.smart_quote(byte(rune_code).ascii_str(), false)
+ g.const_decl_write_precomputed(styp, cname, "'$escval'")
+ } else {
+ g.const_decl_write_precomputed(styp, cname, u32(ct_value).str())
+ }
+ }
+ string {
+ escaped_val := util.smart_quote(ct_value, false)
+ // g.const_decl_write_precomputed(styp, cname, '_SLIT("$escaped_val")')
+ // TODO: ^ the above for strings, cause:
+ // `error C2099: initializer is not a constant` errors in MSVC,
+ // so fall back to the delayed initialisation scheme:
+ g.definitions.writeln('$styp $cname; // inited later')
+ g.inits[mod].writeln('\t$cname = _SLIT("$escaped_val");')
+ if g.is_autofree {
+ g.cleanups[mod].writeln('\tstring_free(&$cname);')
+ }
+ }
+ ast.EmptyExpr {
+ return false
+ }
+ }
+ return true
+}
+
+fn (mut g Gen) const_decl_write_precomputed(styp string, cname string, ct_value string) {
+ g.definitions.writeln('$styp $cname = $ct_value; // precomputed')
+}
+
+fn (mut g Gen) const_decl_simple_define(name string, val string) {
+ // Simple expressions should use a #define
+ // so that we don't pollute the binary with unnecessary global vars
+ // Do not do this when building a module, otherwise the consts
+ // will not be accessible.
+ g.definitions.write_string('#define _const_$name ')
+ g.definitions.writeln(val)
+}
+
+fn (mut g Gen) const_decl_init_later(mod string, name string, expr ast.Expr, typ ast.Type, unwrap_option bool) {
+ // Initialize more complex consts in `void _vinit/2{}`
+ // (C doesn't allow init expressions that can't be resolved at compile time).
+ mut styp := g.typ(typ)
+ cname := '_const_$name'
+ g.definitions.writeln('$styp $cname; // inited later')
+ if cname == '_const_os__args' {
+ if g.pref.os == .windows {
+ g.inits[mod].writeln('\t_const_os__args = os__init_os_args_wide(___argc, (byteptr*)___argv);')
+ } else {
+ g.inits[mod].writeln('\t_const_os__args = os__init_os_args(___argc, (byte**)___argv);')
+ }
+ } else {
+ if unwrap_option {
+ g.inits[mod].writeln(g.expr_string_surround('\t$cname = *($styp*)', expr,
+ '.data;'))
+ } else {
+ g.inits[mod].writeln(g.expr_string_surround('\t$cname = ', expr, ';'))
+ }
+ }
+ if g.is_autofree {
+ sym := g.table.get_type_symbol(typ)
+ if styp.starts_with('Array_') {
+ g.cleanups[mod].writeln('\tarray_free(&$cname);')
+ } else if styp == 'string' {
+ g.cleanups[mod].writeln('\tstring_free(&$cname);')
+ } else if sym.kind == .map {
+ g.cleanups[mod].writeln('\tmap_free(&$cname);')
+ } else if styp == 'IError' {
+ g.cleanups[mod].writeln('\tIError_free(&$cname);')
+ }
+ }
+}
+
+fn (mut g Gen) global_decl(node ast.GlobalDecl) {
+ mod := if g.pref.build_mode == .build_module && g.is_builtin_mod { 'static ' } else { '' }
+ key := node.mod // module name
+ for field in node.fields {
+ if g.pref.skip_unused {
+ if field.name !in g.table.used_globals {
+ $if trace_skip_unused_globals ? {
+ eprintln('>> skipping unused global name: $field.name')
+ }
+ continue
+ }
+ }
+ styp := g.typ(field.typ)
+ if field.has_expr {
+ g.definitions.write_string('$mod$styp $field.name')
+ if field.expr.is_literal() {
+ g.definitions.writeln(' = ${g.expr_string(field.expr)}; // global')
+ } else {
+ g.definitions.writeln(';')
+ g.global_inits[key].writeln('\t$field.name = ${g.expr_string(field.expr)}; // global')
+ }
+ } else {
+ default_initializer := g.type_default(field.typ)
+ if default_initializer == '{0}' {
+ g.definitions.writeln('$mod$styp $field.name = {0}; // global')
+ } else {
+ g.definitions.writeln('$mod$styp $field.name; // global')
+ if field.name !in ['as_cast_type_indexes', 'g_memory_block'] {
+ g.global_inits[key].writeln('\t$field.name = *($styp*)&(($styp[]){${g.type_default(field.typ)}}[0]); // global')
+ }
+ }
+ }
+ }
+}
+
+fn (mut g Gen) go_back_out(n int) {
+ g.out.go_back(n)
+}
+
+const (
+ skip_struct_init = ['struct stat', 'struct addrinfo']
+)
+
+fn (mut g Gen) struct_init(struct_init ast.StructInit) {
+ styp := g.typ(struct_init.typ)
+ mut shared_styp := '' // only needed for shared x := St{...
+ if styp in c.skip_struct_init {
+ // needed for c++ compilers
+ g.go_back_out(3)
+ return
+ }
+ sym := g.table.get_final_type_symbol(g.unwrap_generic(struct_init.typ))
+ is_amp := g.is_amp
+ is_multiline := struct_init.fields.len > 5
+ g.is_amp = false // reset the flag immediately so that other struct inits in this expr are handled correctly
+ if is_amp {
+ g.out.go_back(1) // delete the `&` already generated in `prefix_expr()
+ }
+ if g.is_shared && !g.inside_opt_data && !g.is_arraymap_set {
+ mut shared_typ := struct_init.typ.set_flag(.shared_f)
+ shared_styp = g.typ(shared_typ)
+ g.writeln('($shared_styp*)__dup${shared_styp}(&($shared_styp){.mtx = {0}, .val =($styp){')
+ } else if is_amp || g.inside_cast_in_heap > 0 {
+ g.write('($styp*)memdup(&($styp){')
+ } else if struct_init.typ.is_ptr() {
+ basetyp := g.typ(struct_init.typ.set_nr_muls(0))
+ if is_multiline {
+ g.writeln('&($basetyp){')
+ } else {
+ g.write('&($basetyp){')
+ }
+ } else {
+ if is_multiline {
+ g.writeln('($styp){')
+ } else {
+ g.write('($styp){')
+ }
+ }
+ // mut fields := []string{}
+ mut inited_fields := map[string]int{} // TODO this is done in checker, move to ast node
+ /*
+ if struct_init.fields.len == 0 && struct_init.exprs.len > 0 {
+ // Get fields for {a,b} short syntax. Fields array wasn't set in the parser.
+ for f in info.fields {
+ fields << f.name
+ }
+ } else {
+ fields = struct_init.fields
+ }
+ */
+ if is_multiline {
+ g.indent++
+ }
+ // User set fields
+ mut initialized := false
+ mut old_is_shared := g.is_shared
+ for i, field in struct_init.fields {
+ if !field.typ.has_flag(.shared_f) {
+ g.is_shared = false
+ }
+ inited_fields[field.name] = i
+ if sym.kind != .struct_ {
+ field_name := if sym.language == .v { c_name(field.name) } else { field.name }
+ g.write('.$field_name = ')
+ if field.typ == 0 {
+ g.checker_bug('struct init, field.typ is 0', field.pos)
+ }
+ field_type_sym := g.table.get_type_symbol(field.typ)
+ mut cloned := false
+ if g.is_autofree && !field.typ.is_ptr() && field_type_sym.kind in [.array, .string] {
+ g.write('/*clone1*/')
+ if g.gen_clone_assignment(field.expr, field_type_sym, false) {
+ cloned = true
+ }
+ }
+ if !cloned {
+ if (field.expected_type.is_ptr() && !field.expected_type.has_flag(.shared_f))
+ && !(field.typ.is_ptr() || field.typ.is_pointer()) && !field.typ.is_number() {
+ g.write('/* autoref */&')
+ }
+ g.expr_with_cast(field.expr, field.typ, field.expected_type)
+ }
+ if i != struct_init.fields.len - 1 {
+ if is_multiline {
+ g.writeln(',')
+ } else {
+ g.write(', ')
+ }
+ }
+ initialized = true
+ }
+ g.is_shared = old_is_shared
+ }
+ g.is_shared = old_is_shared
+ // The rest of the fields are zeroed.
+ // `inited_fields` is a list of fields that have been init'ed, they are skipped
+ mut nr_fields := 1
+ if sym.kind == .struct_ {
+ info := sym.info as ast.Struct
+ nr_fields = info.fields.len
+ if info.is_union && struct_init.fields.len > 1 {
+ verror('union must not have more than 1 initializer')
+ }
+ if !info.is_union {
+ old_is_shared2 := g.is_shared
+ mut used_embed_fields := []string{}
+ init_field_names := info.fields.map(it.name)
+ // fields that are initialized but belong to the embedding
+ init_fields_to_embed := struct_init.fields.filter(it.name !in init_field_names)
+ for embed in info.embeds {
+ embed_sym := g.table.get_type_symbol(embed)
+ embed_name := embed_sym.embed_name()
+ if embed_name !in inited_fields {
+ embed_info := embed_sym.info as ast.Struct
+ embed_field_names := embed_info.fields.map(it.name)
+ fields_to_embed := init_fields_to_embed.filter(it.name !in used_embed_fields
+ && it.name in embed_field_names)
+ used_embed_fields << fields_to_embed.map(it.name)
+ default_init := ast.StructInit{
+ typ: embed
+ fields: fields_to_embed
+ }
+ g.write('.$embed_name = ')
+ g.struct_init(default_init)
+ if is_multiline {
+ g.writeln(',')
+ } else {
+ g.write(',')
+ }
+ initialized = true
+ }
+ }
+ g.is_shared = old_is_shared2
+ }
+ // g.zero_struct_fields(info, inited_fields)
+ // nr_fields = info.fields.len
+ for mut field in info.fields {
+ if !field.typ.has_flag(.shared_f) {
+ g.is_shared = false
+ }
+ if mut sym.info is ast.Struct {
+ mut found_equal_fields := 0
+ for mut sifield in sym.info.fields {
+ if sifield.name == field.name {
+ found_equal_fields++
+ break
+ }
+ }
+ if found_equal_fields == 0 {
+ continue
+ }
+ }
+ if field.name in inited_fields {
+ sfield := struct_init.fields[inited_fields[field.name]]
+ field_name := if sym.language == .v { c_name(field.name) } else { field.name }
+ if sfield.typ == 0 {
+ continue
+ }
+ g.write('.$field_name = ')
+ field_type_sym := g.table.get_type_symbol(sfield.typ)
+ mut cloned := false
+ if g.is_autofree && !sfield.typ.is_ptr() && field_type_sym.kind in [.array, .string] {
+ g.write('/*clone1*/')
+ if g.gen_clone_assignment(sfield.expr, field_type_sym, false) {
+ cloned = true
+ }
+ }
+ if !cloned {
+ if field_type_sym.kind == .array_fixed && sfield.expr is ast.Ident {
+ fixed_array_info := field_type_sym.info as ast.ArrayFixed
+ g.write('{')
+ for i in 0 .. fixed_array_info.size {
+ g.expr(sfield.expr)
+ g.write('[$i]')
+ if i != fixed_array_info.size - 1 {
+ g.write(', ')
+ }
+ }
+ g.write('}')
+ } else {
+ if (sfield.expected_type.is_ptr()
+ && !sfield.expected_type.has_flag(.shared_f)) && !(sfield.typ.is_ptr()
+ || sfield.typ.is_pointer()) && !sfield.typ.is_number() {
+ g.write('/* autoref */&')
+ }
+ g.expr_with_cast(sfield.expr, sfield.typ, sfield.expected_type)
+ }
+ }
+ if is_multiline {
+ g.writeln(',')
+ } else {
+ g.write(',')
+ }
+ initialized = true
+ continue
+ }
+ if info.is_union {
+ // unions thould have exactly one explicit initializer
+ continue
+ }
+ if field.typ.has_flag(.optional) {
+ field_name := c_name(field.name)
+ g.write('.$field_name = {EMPTY_STRUCT_INITIALIZATION},')
+ initialized = true
+ continue
+ }
+ if field.typ in info.embeds {
+ continue
+ }
+ if struct_init.has_update_expr {
+ g.expr(struct_init.update_expr)
+ if struct_init.update_expr_type.is_ptr() {
+ g.write('->')
+ } else {
+ g.write('.')
+ }
+ g.write(field.name)
+ } else {
+ if !g.zero_struct_field(field) {
+ nr_fields--
+ continue
+ }
+ }
+ if is_multiline {
+ g.writeln(',')
+ } else {
+ g.write(',')
+ }
+ initialized = true
+ g.is_shared = old_is_shared
+ }
+ g.is_shared = old_is_shared
+ }
+ if is_multiline {
+ g.indent--
+ }
+
+ if !initialized {
+ if nr_fields > 0 {
+ g.write('0')
+ } else {
+ g.write('EMPTY_STRUCT_INITIALIZATION')
+ }
+ }
+
+ g.write('}')
+ if g.is_shared && !g.inside_opt_data && !g.is_arraymap_set {
+ g.write('}, sizeof($shared_styp))')
+ } else if is_amp || g.inside_cast_in_heap > 0 {
+ g.write(', sizeof($styp))')
+ }
+}
+
+fn (mut g Gen) zero_struct_field(field ast.StructField) bool {
+ sym := g.table.get_type_symbol(field.typ)
+ if sym.kind == .struct_ {
+ info := sym.info as ast.Struct
+ if info.fields.len == 0 {
+ return false
+ }
+ }
+ field_name := if sym.language == .v { c_name(field.name) } else { field.name }
+ g.write('.$field_name = ')
+ if field.has_default_expr {
+ if sym.kind in [.sum_type, .interface_] {
+ g.expr_with_cast(field.default_expr, field.default_expr_typ, field.typ)
+ return true
+ }
+ g.expr(field.default_expr)
+ } else {
+ g.write(g.type_default(field.typ))
+ }
+ return true
+}
+
+// fn (mut g Gen) zero_struct_fields(info ast.Struct, inited_fields map[string]int) {
+// }
+// { user | name: 'new name' }
+fn (mut g Gen) assoc(node ast.Assoc) {
+ g.writeln('// assoc')
+ if node.typ == 0 {
+ return
+ }
+ styp := g.typ(node.typ)
+ g.writeln('($styp){')
+ mut inited_fields := map[string]int{}
+ for i, field in node.fields {
+ inited_fields[field] = i
+ }
+ // Merge inited_fields in the rest of the fields.
+ sym := g.table.get_type_symbol(node.typ)
+ info := sym.info as ast.Struct
+ for field in info.fields {
+ field_name := c_name(field.name)
+ if field.name in inited_fields {
+ g.write('\t.$field_name = ')
+ g.expr(node.exprs[inited_fields[field.name]])
+ g.writeln(', ')
+ } else {
+ g.writeln('\t.$field_name = ${node.var_name}.$field_name,')
+ }
+ }
+ g.write('}')
+ if g.is_amp {
+ g.write(', sizeof($styp))')
+ }
+}
+
+[noreturn]
+fn verror(s string) {
+ util.verror('cgen error', s)
+}
+
+[noreturn]
+fn (g &Gen) error(s string, pos token.Position) {
+ ferror := util.formatted_error('cgen error:', s, g.file.path, pos)
+ eprintln(ferror)
+ exit(1)
+}
+
+fn (g &Gen) checker_bug(s string, pos token.Position) {
+ g.error('checker bug; $s', pos)
+}
+
+fn (mut g Gen) write_init_function() {
+ if g.pref.is_liveshared {
+ return
+ }
+ fn_vinit_start_pos := g.out.len
+ // ___argv is declared as voidptr here, because that unifies the windows/unix logic
+ g.writeln('void _vinit(int ___argc, voidptr ___argv) {')
+ if g.pref.prealloc {
+ g.writeln('prealloc_vinit();')
+ }
+ // NB: the as_cast table should be *before* the other constant initialize calls,
+ // because it may be needed during const initialization of builtin and during
+ // calling module init functions too, just in case they do fail...
+ g.write('\tas_cast_type_indexes = ')
+ g.writeln(g.as_cast_name_table())
+ //
+ g.writeln('\tbuiltin_init();')
+ g.writeln('\tvinit_string_literals();')
+ //
+ for mod_name in g.table.modules {
+ g.writeln('\t// Initializations for module $mod_name :')
+ g.write(g.inits[mod_name].str())
+ g.write(g.global_inits[mod_name].str())
+ init_fn_name := '${mod_name}.init'
+ if initfn := g.table.find_fn(init_fn_name) {
+ if initfn.return_type == ast.void_type && initfn.params.len == 0 {
+ mod_c_name := util.no_dots(mod_name)
+ init_fn_c_name := '${mod_c_name}__init'
+ g.writeln('\t${init_fn_c_name}();')
+ }
+ }
+ }
+ g.writeln('}')
+ if g.pref.printfn_list.len > 0 && '_vinit' in g.pref.printfn_list {
+ println(g.out.after(fn_vinit_start_pos))
+ }
+ //
+ fn_vcleanup_start_pos := g.out.len
+ g.writeln('void _vcleanup() {')
+ if g.is_autofree {
+ // g.writeln('puts("cleaning up...");')
+ reversed_table_modules := g.table.modules.reverse()
+ for mod_name in reversed_table_modules {
+ g.writeln('\t// Cleanups for module $mod_name :')
+ g.writeln(g.cleanups[mod_name].str())
+ }
+ g.writeln('\tarray_free(&as_cast_type_indexes);')
+ }
+ g.writeln('}')
+ if g.pref.printfn_list.len > 0 && '_vcleanup' in g.pref.printfn_list {
+ println(g.out.after(fn_vcleanup_start_pos))
+ }
+ //
+ needs_constructor := g.pref.is_shared && g.pref.os != .windows
+ if needs_constructor {
+ // shared libraries need a way to call _vinit/2. For that purpose,
+ // provide a constructor/destructor pair, ensuring that all constants
+ // are initialized just once, and that they will be freed too.
+ // NB: os.args in this case will be [].
+ g.writeln('__attribute__ ((constructor))')
+ g.writeln('void _vinit_caller() {')
+ g.writeln('\tstatic bool once = false; if (once) {return;} once = true;')
+ g.writeln('\t_vinit(0,0);')
+ g.writeln('}')
+
+ g.writeln('__attribute__ ((destructor))')
+ g.writeln('void _vcleanup_caller() {')
+ g.writeln('\tstatic bool once = false; if (once) {return;} once = true;')
+ g.writeln('\t_vcleanup();')
+ g.writeln('}')
+ }
+}
+
+const (
+ builtins = ['string', 'array', 'DenseArray', 'map', 'Error', 'IError', 'Option']
+)
+
+fn (mut g Gen) write_builtin_types() {
+ mut builtin_types := []ast.TypeSymbol{} // builtin types
+ // builtin types need to be on top
+ // everything except builtin will get sorted
+ for builtin_name in c.builtins {
+ sym := g.table.type_symbols[g.table.type_idxs[builtin_name]]
+ if sym.kind == .interface_ {
+ g.write_interface_typedef(sym)
+ g.write_interface_typesymbol_declaration(sym)
+ } else {
+ builtin_types << sym
+ }
+ }
+ g.write_types(builtin_types)
+}
+
+// C struct definitions, ordered
+// Sort the types, make sure types that are referenced by other types
+// are added before them.
+fn (mut g Gen) write_sorted_types() {
+ mut types := []ast.TypeSymbol{} // structs that need to be sorted
+ for typ in g.table.type_symbols {
+ if typ.name !in c.builtins {
+ types << typ
+ }
+ }
+ // sort structs
+ types_sorted := g.sort_structs(types)
+ // Generate C code
+ g.type_definitions.writeln('// builtin types:')
+ g.type_definitions.writeln('//------------------ #endbuiltin')
+ g.write_types(types_sorted)
+}
+
+fn (mut g Gen) write_types(types []ast.TypeSymbol) {
+ for typ in types {
+ if typ.name.starts_with('C.') {
+ continue
+ }
+ // sym := g.table.get_type_symbol(typ)
+ mut name := typ.cname
+ match mut typ.info {
+ ast.Struct {
+ if typ.info.is_generic {
+ continue
+ }
+ if name.contains('_T_') {
+ g.typedefs.writeln('typedef struct $name $name;')
+ }
+ // TODO avoid buffer manip
+ start_pos := g.type_definitions.len
+
+ mut pre_pragma := ''
+ mut post_pragma := ''
+
+ for attr in typ.info.attrs {
+ match attr.name {
+ '_pack' {
+ pre_pragma += '#pragma pack(push, $attr.arg)\n'
+ post_pragma += '#pragma pack(pop)'
+ }
+ else {}
+ }
+ }
+
+ g.type_definitions.writeln(pre_pragma)
+
+ if typ.info.is_union {
+ g.type_definitions.writeln('union $name {')
+ } else {
+ g.type_definitions.writeln('struct $name {')
+ }
+ if typ.info.fields.len > 0 || typ.info.embeds.len > 0 {
+ for field in typ.info.fields {
+ // Some of these structs may want to contain
+ // optionals that may not be defined at this point
+ // if this is the case then we are going to
+ // buffer manip out in front of the struct
+ // write the optional in and then continue
+ if field.typ.has_flag(.optional) {
+ // Dont use g.typ() here becuase it will register
+ // optional and we dont want that
+ styp, base := g.optional_type_name(field.typ)
+ if styp !in g.optionals {
+ last_text := g.type_definitions.cut_to(start_pos).clone()
+ g.optionals << styp
+ g.typedefs2.writeln('typedef struct $styp $styp;')
+ g.type_definitions.writeln('${g.optional_type_text(styp,
+ base)};')
+ g.type_definitions.write_string(last_text)
+ }
+ }
+ type_name := g.typ(field.typ)
+ field_name := c_name(field.name)
+ g.type_definitions.writeln('\t$type_name $field_name;')
+ }
+ } else {
+ g.type_definitions.writeln('\tEMPTY_STRUCT_DECLARATION;')
+ }
+ // g.type_definitions.writeln('} $name;\n')
+ //
+ ti_attrs := if typ.info.attrs.contains('packed') {
+ '__attribute__((__packed__))'
+ } else {
+ ''
+ }
+ g.type_definitions.writeln('}$ti_attrs;\n')
+ g.type_definitions.writeln(post_pragma)
+ }
+ ast.Alias {
+ // ast.Alias { TODO
+ }
+ ast.Thread {
+ if g.pref.os == .windows {
+ if name == '__v_thread' {
+ g.type_definitions.writeln('typedef HANDLE $name;')
+ } else {
+ // Windows can only return `u32` (no void*) from a thread, so the
+ // V gohandle must maintain a pointer to the return value
+ g.type_definitions.writeln('typedef struct {')
+ g.type_definitions.writeln('\tvoid* ret_ptr;')
+ g.type_definitions.writeln('\tHANDLE handle;')
+ g.type_definitions.writeln('} $name;')
+ }
+ } else {
+ if !g.pref.is_bare {
+ g.type_definitions.writeln('typedef pthread_t $name;')
+ }
+ }
+ }
+ ast.SumType {
+ if typ.info.is_generic {
+ continue
+ }
+ g.typedefs.writeln('typedef struct $name $name;')
+ g.type_definitions.writeln('')
+ g.type_definitions.writeln('// Union sum type $name = ')
+ for variant in typ.info.variants {
+ g.type_definitions.writeln('// | ${variant:4d} = ${g.typ(variant.idx()):-20s}')
+ }
+ g.type_definitions.writeln('struct $name {')
+ g.type_definitions.writeln('\tunion {')
+ for variant in typ.info.variants {
+ variant_sym := g.table.get_type_symbol(variant)
+ g.type_definitions.writeln('\t\t${g.typ(variant.to_ptr())} _$variant_sym.cname;')
+ }
+ g.type_definitions.writeln('\t};')
+ g.type_definitions.writeln('\tint _typ;')
+ if typ.info.fields.len > 0 {
+ g.writeln('\t// pointers to common sumtype fields')
+ for field in typ.info.fields {
+ g.type_definitions.writeln('\t${g.typ(field.typ.to_ptr())} $field.name;')
+ }
+ }
+ g.type_definitions.writeln('};')
+ g.type_definitions.writeln('')
+ }
+ ast.ArrayFixed {
+ elem_sym := g.table.get_type_symbol(typ.info.elem_type)
+ if !elem_sym.is_builtin() && !typ.info.elem_type.has_flag(.generic) {
+ // .array_fixed {
+ styp := typ.cname
+ // array_fixed_char_300 => char x[300]
+ // [16]&&&EventListener{} => Array_fixed_main__EventListener_16_ptr3
+ // => typedef main__EventListener*** Array_fixed_main__EventListener_16_ptr3 [16]
+ mut fixed_elem_name := g.typ(typ.info.elem_type.set_nr_muls(0))
+ if typ.info.elem_type.is_ptr() {
+ fixed_elem_name += '*'.repeat(typ.info.elem_type.nr_muls())
+ }
+ len := typ.info.size
+ if fixed_elem_name.starts_with('C__') {
+ fixed_elem_name = fixed_elem_name[3..]
+ }
+ if elem_sym.info is ast.FnType {
+ pos := g.out.len
+ g.write_fn_ptr_decl(&elem_sym.info, '')
+ fixed_elem_name = g.out.cut_to(pos)
+ mut def_str := 'typedef $fixed_elem_name;'
+ def_str = def_str.replace_once('(*)', '(*$styp[$len])')
+ g.type_definitions.writeln(def_str)
+ } else {
+ g.type_definitions.writeln('typedef $fixed_elem_name $styp [$len];')
+ }
+ }
+ }
+ else {}
+ }
+ }
+}
+
+// sort structs by dependant fields
+fn (g &Gen) sort_structs(typesa []ast.TypeSymbol) []ast.TypeSymbol {
+ mut dep_graph := depgraph.new_dep_graph()
+ // types name list
+ mut type_names := []string{}
+ for typ in typesa {
+ type_names << typ.name
+ }
+ // loop over types
+ for t in typesa {
+ if t.kind == .interface_ {
+ dep_graph.add(t.name, [])
+ continue
+ }
+ // create list of deps
+ mut field_deps := []string{}
+ match mut t.info {
+ ast.ArrayFixed {
+ dep := g.table.get_type_symbol(t.info.elem_type).name
+ if dep in type_names {
+ field_deps << dep
+ }
+ }
+ ast.Struct {
+ for embed in t.info.embeds {
+ dep := g.table.get_type_symbol(embed).name
+ // skip if not in types list or already in deps
+ if dep !in type_names || dep in field_deps {
+ continue
+ }
+ field_deps << dep
+ }
+ for field in t.info.fields {
+ dep := g.table.get_type_symbol(field.typ).name
+ // skip if not in types list or already in deps
+ if dep !in type_names || dep in field_deps || field.typ.is_ptr() {
+ continue
+ }
+ field_deps << dep
+ }
+ }
+ // ast.Interface {}
+ else {}
+ }
+ // add type and dependant types to graph
+ dep_graph.add(t.name, field_deps)
+ }
+ // sort graph
+ dep_graph_sorted := dep_graph.resolve()
+ if !dep_graph_sorted.acyclic {
+ // this should no longer be called since it's catched in the parser
+ // TODO: should it be removed?
+ verror('cgen.sort_structs(): the following structs form a dependency cycle:\n' +
+ dep_graph_sorted.display_cycles() +
+ '\nyou can solve this by making one or both of the dependant struct fields references, eg: field &MyStruct' +
+ '\nif you feel this is an error, please create a new issue here: https://github.com/vlang/v/issues and tag @joe-conigliaro')
+ }
+ // sort types
+ mut types_sorted := []ast.TypeSymbol{}
+ for node in dep_graph_sorted.nodes {
+ types_sorted << g.table.type_symbols[g.table.type_idxs[node.name]]
+ }
+ return types_sorted
+}
+
+[inline]
+fn (g &Gen) nth_stmt_pos(n int) int {
+ return g.stmt_path_pos[g.stmt_path_pos.len - (1 + n)]
+}
+
+fn (mut g Gen) go_before_stmt(n int) string {
+ stmt_pos := g.nth_stmt_pos(n)
+ return g.out.cut_to(stmt_pos)
+}
+
+[inline]
+fn (mut g Gen) go_before_ternary() string {
+ return g.go_before_stmt(g.inside_ternary)
+}
+
+fn (mut g Gen) insert_before_stmt(s string) {
+ cur_line := g.go_before_stmt(0)
+ g.writeln(s)
+ g.write(cur_line)
+}
+
+fn (mut g Gen) insert_at(pos int, s string) {
+ cur_line := g.out.cut_to(pos)
+ g.writeln(s)
+ g.write(cur_line)
+}
+
+// fn (mut g Gen) start_tmp() {
+// }
+// If user is accessing the return value eg. in assigment, pass the variable name.
+// If the user is not using the optional return value. We need to pass a temp var
+// to access its fields (`.ok`, `.error` etc)
+// `os.cp(...)` => `Option bool tmp = os__cp(...); if (tmp.state != 0) { ... }`
+// Returns the type of the last stmt
+fn (mut g Gen) or_block(var_name string, or_block ast.OrExpr, return_type ast.Type) {
+ cvar_name := c_name(var_name)
+ mr_styp := g.base_type(return_type)
+ is_none_ok := return_type == ast.ovoid_type
+ g.writeln(';')
+ if is_none_ok {
+ g.writeln('if (${cvar_name}.state != 0 && ${cvar_name}.err._typ != _IError_None___index) {')
+ } else {
+ g.writeln('if (${cvar_name}.state != 0) { /*or block*/ ')
+ }
+ if or_block.kind == .block {
+ if g.inside_or_block {
+ g.writeln('\terr = ${cvar_name}.err;')
+ } else {
+ g.writeln('\tIError err = ${cvar_name}.err;')
+ }
+ g.inside_or_block = true
+ defer {
+ g.inside_or_block = false
+ }
+ stmts := or_block.stmts
+ if stmts.len > 0 && stmts[or_block.stmts.len - 1] is ast.ExprStmt
+ && (stmts[stmts.len - 1] as ast.ExprStmt).typ != ast.void_type {
+ g.indent++
+ for i, stmt in stmts {
+ if i == stmts.len - 1 {
+ expr_stmt := stmt as ast.ExprStmt
+ g.stmt_path_pos << g.out.len
+ g.write('*($mr_styp*) ${cvar_name}.data = ')
+ old_inside_opt_data := g.inside_opt_data
+ g.inside_opt_data = true
+ g.expr_with_cast(expr_stmt.expr, expr_stmt.typ, return_type.clear_flag(.optional))
+ g.inside_opt_data = old_inside_opt_data
+ if g.inside_ternary == 0 {
+ g.writeln(';')
+ }
+ g.stmt_path_pos.delete_last()
+ } else {
+ g.stmt(stmt)
+ }
+ }
+ g.indent--
+ } else {
+ g.stmts(stmts)
+ if stmts.len > 0 && stmts[or_block.stmts.len - 1] is ast.ExprStmt {
+ g.writeln(';')
+ }
+ }
+ } else if or_block.kind == .propagate {
+ if g.file.mod.name == 'main' && (isnil(g.fn_decl) || g.fn_decl.is_main) {
+ // In main(), an `opt()?` call is sugar for `opt() or { panic(err) }`
+ if g.pref.is_debug {
+ paline, pafile, pamod, pafn := g.panic_debug_info(or_block.pos)
+ g.writeln('panic_debug($paline, tos3("$pafile"), tos3("$pamod"), tos3("$pafn"), *${cvar_name}.err.msg );')
+ } else {
+ g.writeln('\tpanic_optional_not_set(*${cvar_name}.err.msg);')
+ }
+ } else if !isnil(g.fn_decl) && g.fn_decl.is_test {
+ g.gen_failing_error_propagation_for_test_fn(or_block, cvar_name)
+ } else {
+ // In ordinary functions, `opt()?` call is sugar for:
+ // `opt() or { return err }`
+ // Since we *do* return, first we have to ensure that
+ // the defered statements are generated.
+ g.write_defer_stmts()
+ // Now that option types are distinct we need a cast here
+ if g.fn_decl.return_type == ast.void_type {
+ g.writeln('\treturn;')
+ } else {
+ styp := g.typ(g.fn_decl.return_type)
+ err_obj := g.new_tmp_var()
+ g.writeln('\t$styp $err_obj;')
+ g.writeln('\tmemcpy(&$err_obj, &$cvar_name, sizeof(Option));')
+ g.writeln('\treturn $err_obj;')
+ }
+ }
+ }
+ g.writeln('}')
+ g.stmt_path_pos << g.out.len
+}
+
+[inline]
+fn c_name(name_ string) string {
+ name := util.no_dots(name_)
+ if name in c.c_reserved_map {
+ return '_v_$name'
+ }
+ return name
+}
+
+fn (mut g Gen) type_default(typ_ ast.Type) string {
+ typ := g.unwrap_generic(typ_)
+ if typ.has_flag(.optional) {
+ return '{0}'
+ }
+ // Always set pointers to 0
+ if typ.is_ptr() && !typ.has_flag(.shared_f) {
+ return '0'
+ }
+ if typ.idx() < ast.string_type_idx {
+ // Default values for other types are not needed because of mandatory initialization
+ return '0'
+ }
+ sym := g.table.get_type_symbol(typ)
+ match sym.kind {
+ .string {
+ return '(string){.str=(byteptr)"", .is_lit=1}'
+ }
+ .interface_, .sum_type, .array_fixed, .multi_return {
+ return '{0}'
+ }
+ .alias {
+ return g.type_default((sym.info as ast.Alias).parent_type)
+ }
+ .chan {
+ elem_type := sym.chan_info().elem_type
+ elemtypstr := g.typ(elem_type)
+ noscan := g.check_noscan(elem_type)
+ return 'sync__new_channel_st${noscan}(0, sizeof($elemtypstr))'
+ }
+ .array {
+ elem_typ := sym.array_info().elem_type
+ elem_sym := g.typ(elem_typ)
+ mut elem_type_str := util.no_dots(elem_sym)
+ if elem_type_str.starts_with('C__') {
+ elem_type_str = elem_type_str[3..]
+ }
+ noscan := g.check_noscan(elem_typ)
+ init_str := '__new_array${noscan}(0, 0, sizeof($elem_type_str))'
+ if typ.has_flag(.shared_f) {
+ atyp := '__shared__Array_${g.table.get_type_symbol(elem_typ).cname}'
+ return '($atyp*)__dup_shared_array(&($atyp){.mtx = {0}, .val =$init_str}, sizeof($atyp))'
+ } else {
+ return init_str
+ }
+ }
+ .map {
+ info := sym.map_info()
+ key_typ := g.table.get_type_symbol(info.key_type)
+ hash_fn, key_eq_fn, clone_fn, free_fn := g.map_fn_ptrs(key_typ)
+ noscan_key := g.check_noscan(info.key_type)
+ noscan_value := g.check_noscan(info.value_type)
+ mut noscan := if noscan_key.len != 0 || noscan_value.len != 0 { '_noscan' } else { '' }
+ if noscan.len != 0 {
+ if noscan_key.len != 0 {
+ noscan += '_key'
+ }
+ if noscan_value.len != 0 {
+ noscan += '_value'
+ }
+ }
+ init_str := 'new_map${noscan}(sizeof(${g.typ(info.key_type)}), sizeof(${g.typ(info.value_type)}), $hash_fn, $key_eq_fn, $clone_fn, $free_fn)'
+ if typ.has_flag(.shared_f) {
+ mtyp := '__shared__Map_${key_typ.cname}_${g.table.get_type_symbol(info.value_type).cname}'
+ return '($mtyp*)__dup_shared_map(&($mtyp){.mtx = {0}, .val =$init_str}, sizeof($mtyp))'
+ } else {
+ return init_str
+ }
+ }
+ .struct_ {
+ mut has_none_zero := false
+ mut init_str := '{'
+ info := sym.info as ast.Struct
+ typ_is_shared_f := typ.has_flag(.shared_f)
+ if sym.language == .v && !typ_is_shared_f {
+ for field in info.fields {
+ field_sym := g.table.get_type_symbol(field.typ)
+ if field.has_default_expr
+ || field_sym.kind in [.array, .map, .string, .bool, .alias, .size_t, .i8, .i16, .int, .i64, .byte, .u16, .u32, .u64, .char, .voidptr, .byteptr, .charptr, .struct_] {
+ field_name := c_name(field.name)
+ if field.has_default_expr {
+ expr_str := g.expr_string(field.default_expr)
+ init_str += '.$field_name = $expr_str,'
+ } else {
+ init_str += '.$field_name = ${g.type_default(field.typ)},'
+ }
+ has_none_zero = true
+ }
+ }
+ }
+ if has_none_zero {
+ init_str += '}'
+ type_name := g.typ(typ)
+ init_str = '($type_name)' + init_str
+ } else {
+ init_str += '0}'
+ }
+ if typ.has_flag(.shared_f) {
+ styp := '__shared__${g.table.get_type_symbol(typ).cname}'
+ return '($styp*)__dup${styp}(&($styp){.mtx = {0}, .val =$init_str}, sizeof($styp))'
+ } else {
+ return init_str
+ }
+ }
+ else {
+ return '0'
+ }
+ }
+}
+
+fn (g &Gen) get_all_test_function_names() []string {
+ mut tfuncs := []string{}
+ mut tsuite_begin := ''
+ mut tsuite_end := ''
+ for _, f in g.table.fns {
+ if f.name.ends_with('.testsuite_begin') {
+ tsuite_begin = f.name
+ continue
+ }
+ if f.name.contains('.test_') {
+ tfuncs << f.name
+ continue
+ }
+ if f.name.ends_with('.testsuite_end') {
+ tsuite_end = f.name
+ continue
+ }
+ }
+ mut all_tfuncs := []string{}
+ if tsuite_begin.len > 0 {
+ all_tfuncs << tsuite_begin
+ }
+ all_tfuncs << tfuncs
+ if tsuite_end.len > 0 {
+ all_tfuncs << tsuite_end
+ }
+ return all_tfuncs
+}
+
+fn (g &Gen) is_importing_os() bool {
+ return 'os' in g.table.imports
+}
+
+fn (mut g Gen) go_expr(node ast.GoExpr) {
+ line := g.go_before_stmt(0)
+ mut handle := ''
+ tmp := g.new_tmp_var()
+ mut expr := node.call_expr
+ mut name := expr.name // util.no_dots(expr.name)
+ // TODO: fn call is duplicated. merge with fn_call().
+ for i, concrete_type in expr.concrete_types {
+ if concrete_type != ast.void_type && concrete_type != 0 {
+ // Using _T_ to differentiate between get<string> and get_string
+ // `foo<int>()` => `foo_T_int()`
+ if i == 0 {
+ name += '_T'
+ }
+ name += '_' + g.typ(concrete_type)
+ }
+ }
+ if expr.is_method {
+ receiver_sym := g.table.get_type_symbol(expr.receiver_type)
+ name = receiver_sym.name + '_' + name
+ } else if mut expr.left is ast.AnonFn {
+ g.gen_anon_fn_decl(mut expr.left)
+ name = expr.left.decl.name
+ }
+ name = util.no_dots(name)
+ if g.pref.obfuscate && g.cur_mod.name == 'main' && name.starts_with('main__') {
+ mut key := expr.name
+ if expr.is_method {
+ sym := g.table.get_type_symbol(expr.receiver_type)
+ key = sym.name + '.' + expr.name
+ }
+ g.write('/* obf go: $key */')
+ name = g.obf_table[key] or {
+ panic('cgen: obf name "$key" not found, this should never happen')
+ }
+ }
+ g.writeln('// go')
+ wrapper_struct_name := 'thread_arg_' + name
+ wrapper_fn_name := name + '_thread_wrapper'
+ arg_tmp_var := 'arg_' + tmp
+ g.writeln('$wrapper_struct_name *$arg_tmp_var = malloc(sizeof(thread_arg_$name));')
+ if expr.is_method {
+ g.write('$arg_tmp_var->arg0 = ')
+ // TODO is this needed?
+ /*
+ if false && !expr.return_type.is_ptr() {
+ g.write('&')
+ }
+ */
+ g.expr(expr.left)
+ g.writeln(';')
+ }
+ for i, arg in expr.args {
+ g.write('$arg_tmp_var->arg${i + 1} = ')
+ g.expr(arg.expr)
+ g.writeln(';')
+ }
+ s_ret_typ := g.typ(node.call_expr.return_type)
+ if g.pref.os == .windows && node.call_expr.return_type != ast.void_type {
+ g.writeln('$arg_tmp_var->ret_ptr = malloc(sizeof($s_ret_typ));')
+ }
+ is_opt := node.call_expr.return_type.has_flag(.optional)
+ mut gohandle_name := ''
+ if node.call_expr.return_type == ast.void_type {
+ gohandle_name = if is_opt { '__v_thread_Option_void' } else { '__v_thread' }
+ } else {
+ opt := if is_opt { 'Option_' } else { '' }
+ gohandle_name = '__v_thread_$opt${g.table.get_type_symbol(g.unwrap_generic(node.call_expr.return_type)).cname}'
+ }
+ if g.pref.os == .windows {
+ simple_handle := if node.is_expr && node.call_expr.return_type != ast.void_type {
+ 'thread_handle_$tmp'
+ } else {
+ 'thread_$tmp'
+ }
+ g.writeln('HANDLE $simple_handle = CreateThread(0,0, (LPTHREAD_START_ROUTINE)$wrapper_fn_name, $arg_tmp_var, 0,0);')
+ g.writeln('if (!$simple_handle) panic_lasterr(tos3("`go ${name}()`: "));')
+ if node.is_expr && node.call_expr.return_type != ast.void_type {
+ g.writeln('$gohandle_name thread_$tmp = {')
+ g.writeln('\t.ret_ptr = $arg_tmp_var->ret_ptr,')
+ g.writeln('\t.handle = thread_handle_$tmp')
+ g.writeln('};')
+ }
+ if !node.is_expr {
+ g.writeln('CloseHandle(thread_$tmp);')
+ }
+ } else {
+ g.writeln('pthread_t thread_$tmp;')
+ g.writeln('int ${tmp}_thr_res = pthread_create(&thread_$tmp, NULL, (void*)$wrapper_fn_name, $arg_tmp_var);')
+ g.writeln('if (${tmp}_thr_res) panic_error_number(tos3("`go ${name}()`: "), ${tmp}_thr_res);')
+ if !node.is_expr {
+ g.writeln('pthread_detach(thread_$tmp);')
+ }
+ }
+ g.writeln('// endgo\n')
+ if node.is_expr {
+ handle = 'thread_$tmp'
+ // create wait handler for this return type if none exists
+ waiter_fn_name := gohandle_name + '_wait'
+ if waiter_fn_name !in g.waiter_fns {
+ g.gowrappers.writeln('\n$s_ret_typ ${waiter_fn_name}($gohandle_name thread) {')
+ mut c_ret_ptr_ptr := 'NULL'
+ if node.call_expr.return_type != ast.void_type {
+ g.gowrappers.writeln('\t$s_ret_typ* ret_ptr;')
+ c_ret_ptr_ptr = '&ret_ptr'
+ }
+ if g.pref.os == .windows {
+ if node.call_expr.return_type == ast.void_type {
+ g.gowrappers.writeln('\tu32 stat = WaitForSingleObject(thread, INFINITE);')
+ } else {
+ g.gowrappers.writeln('\tu32 stat = WaitForSingleObject(thread.handle, INFINITE);')
+ g.gowrappers.writeln('\tret_ptr = thread.ret_ptr;')
+ }
+ } else {
+ g.gowrappers.writeln('\tint stat = pthread_join(thread, (void **)$c_ret_ptr_ptr);')
+ }
+ g.gowrappers.writeln('\tif (stat != 0) { _v_panic(_SLIT("unable to join thread")); }')
+ if g.pref.os == .windows {
+ if node.call_expr.return_type == ast.void_type {
+ g.gowrappers.writeln('\tCloseHandle(thread);')
+ } else {
+ g.gowrappers.writeln('\tCloseHandle(thread.handle);')
+ }
+ }
+ if node.call_expr.return_type != ast.void_type {
+ g.gowrappers.writeln('\t$s_ret_typ ret = *ret_ptr;')
+ g.gowrappers.writeln('\tfree(ret_ptr);')
+ g.gowrappers.writeln('\treturn ret;')
+ } else {
+ g.gowrappers.writeln('\treturn;')
+ }
+ g.gowrappers.writeln('}')
+ g.waiter_fns << waiter_fn_name
+ }
+ }
+ // Register the wrapper type and function
+ if name !in g.threaded_fns {
+ g.type_definitions.writeln('\ntypedef struct $wrapper_struct_name {')
+ if expr.is_method {
+ styp := g.typ(expr.receiver_type)
+ g.type_definitions.writeln('\t$styp arg0;')
+ }
+ need_return_ptr := g.pref.os == .windows && node.call_expr.return_type != ast.void_type
+ if expr.args.len == 0 && !need_return_ptr {
+ g.type_definitions.writeln('EMPTY_STRUCT_DECLARATION;')
+ } else {
+ for i, arg in expr.args {
+ styp := g.typ(arg.typ)
+ g.type_definitions.writeln('\t$styp arg${i + 1};')
+ }
+ }
+ if need_return_ptr {
+ g.type_definitions.writeln('\tvoid* ret_ptr;')
+ }
+ g.type_definitions.writeln('} $wrapper_struct_name;')
+ thread_ret_type := if g.pref.os == .windows { 'u32' } else { 'void*' }
+ g.type_definitions.writeln('$thread_ret_type ${wrapper_fn_name}($wrapper_struct_name *arg);')
+ g.gowrappers.writeln('$thread_ret_type ${wrapper_fn_name}($wrapper_struct_name *arg) {')
+ if node.call_expr.return_type != ast.void_type {
+ if g.pref.os == .windows {
+ g.gowrappers.write_string('\t*(($s_ret_typ*)(arg->ret_ptr)) = ')
+ } else {
+ g.gowrappers.writeln('\t$s_ret_typ* ret_ptr = malloc(sizeof($s_ret_typ));')
+ g.gowrappers.write_string('\t*ret_ptr = ')
+ }
+ } else {
+ g.gowrappers.write_string('\t')
+ }
+ if expr.is_method {
+ unwrapped_rec_type := g.unwrap_generic(expr.receiver_type)
+ typ_sym := g.table.get_type_symbol(unwrapped_rec_type)
+ if typ_sym.kind == .interface_
+ && (typ_sym.info as ast.Interface).defines_method(expr.name) {
+ rec_cc_type := g.cc_type(unwrapped_rec_type, false)
+ receiver_type_name := util.no_dots(rec_cc_type)
+ g.gowrappers.write_string('${c_name(receiver_type_name)}_name_table[')
+ g.gowrappers.write_string('arg->arg0')
+ dot := if expr.left_type.is_ptr() { '->' } else { '.' }
+ mname := c_name(expr.name)
+ g.gowrappers.write_string('${dot}_typ]._method_${mname}(')
+ g.gowrappers.write_string('arg->arg0')
+ g.gowrappers.write_string('${dot}_object')
+ } else {
+ g.gowrappers.write_string('${name}(')
+ g.gowrappers.write_string('arg->arg0')
+ }
+ if expr.args.len > 0 {
+ g.gowrappers.write_string(', ')
+ }
+ } else {
+ g.gowrappers.write_string('${name}(')
+ }
+ if expr.args.len > 0 {
+ mut has_cast := false
+ for i in 0 .. expr.args.len {
+ if g.table.get_type_symbol(expr.expected_arg_types[i]).kind == .interface_
+ && g.table.get_type_symbol(expr.args[i].typ).kind != .interface_ {
+ has_cast = true
+ break
+ }
+ }
+ if has_cast {
+ pos := g.out.len
+ g.call_args(expr)
+ mut call_args_str := g.out.after(pos)
+ g.out.go_back(call_args_str.len)
+ mut rep_group := []string{cap: 2 * expr.args.len}
+ for i in 0 .. expr.args.len {
+ rep_group << g.expr_string(expr.args[i].expr)
+ rep_group << 'arg->arg${i + 1}'
+ }
+ call_args_str = call_args_str.replace_each(rep_group)
+ g.gowrappers.write_string(call_args_str)
+ } else {
+ for i in 0 .. expr.args.len {
+ g.gowrappers.write_string('arg->arg${i + 1}')
+ if i != expr.args.len - 1 {
+ g.gowrappers.write_string(', ')
+ }
+ }
+ }
+ }
+ g.gowrappers.writeln(');')
+ g.gowrappers.writeln('\tfree(arg);')
+ if g.pref.os != .windows && node.call_expr.return_type != ast.void_type {
+ g.gowrappers.writeln('\treturn ret_ptr;')
+ } else {
+ g.gowrappers.writeln('\treturn 0;')
+ }
+ g.gowrappers.writeln('}')
+ g.threaded_fns << name
+ }
+ if node.is_expr {
+ g.empty_line = false
+ g.write(line)
+ g.write(handle)
+ }
+}
+
+fn (mut g Gen) as_cast(node ast.AsCast) {
+ // Make sure the sum type can be cast to this type (the types
+ // are the same), otherwise panic.
+ // g.insert_before('
+ styp := g.typ(node.typ)
+ sym := g.table.get_type_symbol(node.typ)
+ mut expr_type_sym := g.table.get_type_symbol(node.expr_type)
+ if mut expr_type_sym.info is ast.SumType {
+ dot := if node.expr_type.is_ptr() { '->' } else { '.' }
+ g.write('/* as */ *($styp*)__as_cast(')
+ g.write('(')
+ g.expr(node.expr)
+ g.write(')')
+ g.write(dot)
+ g.write('_$sym.cname,')
+ g.write('(')
+ g.expr(node.expr)
+ g.write(')')
+ g.write(dot)
+ // g.write('typ, /*expected:*/$node.typ)')
+ sidx := g.type_sidx(node.typ)
+ expected_sym := g.table.get_type_symbol(node.typ)
+ g.write('_typ, $sidx) /*expected idx: $sidx, name: $expected_sym.name */ ')
+
+ // fill as cast name table
+ for variant in expr_type_sym.info.variants {
+ idx := u32(variant).str()
+ if idx in g.as_cast_type_names {
+ continue
+ }
+ variant_sym := g.table.get_type_symbol(variant)
+ g.as_cast_type_names[idx] = variant_sym.name
+ }
+ } else if expr_type_sym.kind == .interface_ && sym.kind == .interface_ {
+ g.write('I_${expr_type_sym.cname}_as_I_${sym.cname}(')
+ if node.expr_type.is_ptr() {
+ g.write('*')
+ }
+ g.expr(node.expr)
+ g.write(')')
+
+ mut info := expr_type_sym.info as ast.Interface
+ if node.typ !in info.conversions {
+ left_variants := g.table.iface_types[expr_type_sym.name]
+ right_variants := g.table.iface_types[sym.name]
+ info.conversions[node.typ] = left_variants.filter(it in right_variants)
+ }
+ expr_type_sym.info = info
+ } else {
+ g.expr(node.expr)
+ }
+}
+
+fn (g Gen) as_cast_name_table() string {
+ if g.as_cast_type_names.len == 0 {
+ return 'new_array_from_c_array(1, 1, sizeof(VCastTypeIndexName), _MOV((VCastTypeIndexName[1]){(VCastTypeIndexName){.tindex = 0,.tname = _SLIT("unknown")}}));\n'
+ }
+ mut name_ast := strings.new_builder(1024)
+ casts_len := g.as_cast_type_names.len + 1
+ name_ast.writeln('new_array_from_c_array($casts_len, $casts_len, sizeof(VCastTypeIndexName), _MOV((VCastTypeIndexName[$casts_len]){')
+ name_ast.writeln('\t\t (VCastTypeIndexName){.tindex = 0, .tname = _SLIT("unknown")}')
+ for key, value in g.as_cast_type_names {
+ name_ast.writeln('\t\t, (VCastTypeIndexName){.tindex = $key, .tname = _SLIT("$value")}')
+ }
+ name_ast.writeln('\t}));\n')
+ return name_ast.str()
+}
+
+// Generates interface table and interface indexes
+fn (mut g Gen) interface_table() string {
+ mut sb := strings.new_builder(100)
+ mut conversion_functions := strings.new_builder(100)
+ for ityp in g.table.type_symbols {
+ if ityp.kind != .interface_ {
+ continue
+ }
+ inter_info := ityp.info as ast.Interface
+ if inter_info.is_generic {
+ continue
+ }
+ // interface_name is for example Speaker
+ interface_name := ityp.cname
+ // generate a struct that references interface methods
+ methods_struct_name := 'struct _${interface_name}_interface_methods'
+ mut methods_struct_def := strings.new_builder(100)
+ methods_struct_def.writeln('$methods_struct_name {')
+ mut methodidx := map[string]int{}
+ for k, method in inter_info.methods {
+ methodidx[method.name] = k
+ ret_styp := g.typ(method.return_type)
+ methods_struct_def.write_string('\t$ret_styp (*_method_${c_name(method.name)})(void* _')
+ // the first param is the receiver, it's handled by `void*` above
+ for i in 1 .. method.params.len {
+ arg := method.params[i]
+ methods_struct_def.write_string(', ${g.typ(arg.typ)} $arg.name')
+ }
+ // TODO g.fn_args(method.args[1..])
+ methods_struct_def.writeln(');')
+ }
+ methods_struct_def.writeln('};')
+ // generate an array of the interface methods for the structs using the interface
+ // as well as case functions from the struct to the interface
+ mut methods_struct := strings.new_builder(100)
+ //
+ iname_table_length := inter_info.types.len
+ if iname_table_length == 0 {
+ // msvc can not process `static struct x[0] = {};`
+ methods_struct.writeln('$methods_struct_name ${interface_name}_name_table[1];')
+ } else {
+ if g.pref.build_mode != .build_module {
+ methods_struct.writeln('$methods_struct_name ${interface_name}_name_table[$iname_table_length] = {')
+ } else {
+ methods_struct.writeln('$methods_struct_name ${interface_name}_name_table[$iname_table_length];')
+ }
+ }
+ mut cast_functions := strings.new_builder(100)
+ mut methods_wrapper := strings.new_builder(100)
+ methods_wrapper.writeln('// Methods wrapper for interface "$interface_name"')
+ mut already_generated_mwrappers := map[string]int{}
+ iinidx_minimum_base := 1000 // NB: NOT 0, to avoid map entries set to 0 later, so `if already_generated_mwrappers[name] > 0 {` works.
+ mut current_iinidx := iinidx_minimum_base
+ for st in inter_info.types {
+ st_sym := g.table.get_type_symbol(st)
+ // cctype is the Cleaned Concrete Type name, *without ptr*,
+ // i.e. cctype is always just Cat, not Cat_ptr:
+ cctype := g.cc_type(st, true)
+ $if debug_interface_table ? {
+ eprintln(
+ '>> interface name: $ityp.name | concrete type: $st.debug() | st symname: ' +
+ st_sym.name)
+ }
+ // Speaker_Cat_index = 0
+ interface_index_name := '_${interface_name}_${cctype}_index'
+ if already_generated_mwrappers[interface_index_name] > 0 {
+ continue
+ }
+ already_generated_mwrappers[interface_index_name] = current_iinidx
+ current_iinidx++
+ if ityp.name != 'vweb.DbInterface' { // TODO remove this
+ // eprintln('>>> current_iinidx: ${current_iinidx-iinidx_minimum_base} | interface_index_name: $interface_index_name')
+ sb.writeln('static $interface_name I_${cctype}_to_Interface_${interface_name}($cctype* x);')
+ mut cast_struct := strings.new_builder(100)
+ cast_struct.writeln('($interface_name) {')
+ cast_struct.writeln('\t\t._$cctype = x,')
+ cast_struct.writeln('\t\t._typ = $interface_index_name,')
+ for field in inter_info.fields {
+ cname := c_name(field.name)
+ field_styp := g.typ(field.typ)
+ if _ := st_sym.find_field(field.name) {
+ cast_struct.writeln('\t\t.$cname = ($field_styp*)((char*)x + __offsetof_ptr(x, $cctype, $cname)),')
+ } else {
+ // the field is embedded in another struct
+ cast_struct.write_string('\t\t.$cname = ($field_styp*)((char*)x')
+ if st == ast.voidptr_type {
+ cast_struct.write_string('/*.... ast.voidptr_type */')
+ } else {
+ for embed_type in st_sym.struct_info().embeds {
+ embed_sym := g.table.get_type_symbol(embed_type)
+ if _ := embed_sym.find_field(field.name) {
+ cast_struct.write_string(' + __offsetof_ptr(x, $cctype, $embed_sym.embed_name()) + __offsetof_ptr(x, $embed_sym.cname, $cname)')
+ break
+ }
+ }
+ }
+ cast_struct.writeln('),')
+ }
+ }
+ cast_struct.write_string('\t}')
+ cast_struct_str := cast_struct.str()
+
+ cast_functions.writeln('
+// Casting functions for converting "$cctype" to interface "$interface_name"
+static inline $interface_name I_${cctype}_to_Interface_${interface_name}($cctype* x) {
+ return $cast_struct_str;
+}')
+ }
+
+ if g.pref.build_mode != .build_module {
+ methods_struct.writeln('\t{')
+ }
+ if st == ast.voidptr_type {
+ for mname, _ in methodidx {
+ if g.pref.build_mode != .build_module {
+ methods_struct.writeln('\t\t._method_${c_name(mname)} = (void*) 0,')
+ }
+ }
+ }
+ mut methods := st_sym.methods
+ match st_sym.info {
+ ast.Struct, ast.Interface, ast.SumType {
+ if st_sym.info.parent_type.has_flag(.generic) {
+ parent_sym := g.table.get_type_symbol(st_sym.info.parent_type)
+ for method in parent_sym.methods {
+ if method.name in methodidx {
+ methods << st_sym.find_method_with_generic_parent(method.name) or {
+ continue
+ }
+ }
+ }
+ }
+ }
+ else {}
+ }
+ for method in methods {
+ mut name := method.name
+ if inter_info.parent_type.has_flag(.generic) {
+ parent_sym := g.table.get_type_symbol(inter_info.parent_type)
+ match mut parent_sym.info {
+ ast.Struct, ast.Interface, ast.SumType {
+ name = g.generic_fn_name(parent_sym.info.concrete_types, method.name,
+ false)
+ }
+ else {}
+ }
+ }
+
+ if method.name !in methodidx {
+ // a method that is not part of the interface should be just skipped
+ continue
+ }
+ // .speak = Cat_speak
+ if st_sym.info is ast.Struct {
+ if st_sym.info.parent_type.has_flag(.generic) {
+ name = g.generic_fn_name(st_sym.info.concrete_types, method.name,
+ false)
+ }
+ }
+ mut method_call := '${cctype}_$name'
+ if !method.params[0].typ.is_ptr() {
+ // inline void Cat_speak_Interface_Animal_method_wrapper(Cat c) { return Cat_speak(*c); }
+ iwpostfix := '_Interface_${interface_name}_method_wrapper'
+ methods_wrapper.write_string('static inline ${g.typ(method.return_type)} $method_call${iwpostfix}(')
+ //
+ params_start_pos := g.out.len
+ mut params := method.params.clone()
+ // hack to mutate typ
+ params[0] = ast.Param{
+ ...params[0]
+ typ: params[0].typ.set_nr_muls(1)
+ }
+ fargs, _, _ := g.fn_args(params, voidptr(0))
+ methods_wrapper.write_string(g.out.cut_last(g.out.len - params_start_pos))
+ methods_wrapper.writeln(') {')
+ methods_wrapper.write_string('\t')
+ if method.return_type != ast.void_type {
+ methods_wrapper.write_string('return ')
+ }
+ methods_wrapper.writeln('${method_call}(*${fargs.join(', ')});')
+ methods_wrapper.writeln('}')
+ // .speak = Cat_speak_Interface_Animal_method_wrapper
+ method_call += iwpostfix
+ }
+ if g.pref.build_mode != .build_module {
+ methods_struct.writeln('\t\t._method_${c_name(method.name)} = (void*) $method_call,')
+ }
+ }
+ if g.pref.build_mode != .build_module {
+ methods_struct.writeln('\t},')
+ }
+ iin_idx := already_generated_mwrappers[interface_index_name] - iinidx_minimum_base
+ if g.pref.build_mode != .build_module {
+ sb.writeln('const int $interface_index_name = $iin_idx;')
+ } else {
+ sb.writeln('extern const int $interface_index_name;')
+ }
+ }
+ for vtyp, variants in inter_info.conversions {
+ vsym := g.table.get_type_symbol(vtyp)
+ conversion_functions.write_string('static inline bool I_${interface_name}_is_I_${vsym.cname}($interface_name x) {\n\treturn ')
+ for i, variant in variants {
+ variant_sym := g.table.get_type_symbol(variant)
+ if i > 0 {
+ conversion_functions.write_string(' || ')
+ }
+ conversion_functions.write_string('(x._typ == _${interface_name}_${variant_sym.cname}_index)')
+ }
+ conversion_functions.writeln(';\n}')
+
+ conversion_functions.writeln('static inline $vsym.cname I_${interface_name}_as_I_${vsym.cname}($interface_name x) {')
+ for variant in variants {
+ variant_sym := g.table.get_type_symbol(variant)
+ conversion_functions.writeln('\tif (x._typ == _${interface_name}_${variant_sym.cname}_index) return I_${variant_sym.cname}_to_Interface_${vsym.cname}(x._$variant_sym.cname);')
+ }
+ pmessage := 'string__plus(string__plus(tos3("`as_cast`: cannot convert "), tos3(v_typeof_interface_${interface_name}(x._typ))), tos3(" to ${util.strip_main_name(vsym.name)}"))'
+ if g.pref.is_debug {
+ // TODO: actually return a valid position here
+ conversion_functions.write_string('\tpanic_debug(1, tos3("builtin.v"), tos3("builtin"), tos3("__as_cast"), ')
+ conversion_functions.write_string(pmessage)
+ conversion_functions.writeln(');')
+ } else {
+ conversion_functions.write_string('\t_v_panic(')
+ conversion_functions.write_string(pmessage)
+ conversion_functions.writeln(');')
+ }
+ conversion_functions.writeln('\treturn ($vsym.cname){0};')
+ conversion_functions.writeln('}')
+ }
+ sb.writeln('// ^^^ number of types for interface $interface_name: ${current_iinidx - iinidx_minimum_base}')
+ if iname_table_length == 0 {
+ methods_struct.writeln('')
+ } else {
+ if g.pref.build_mode != .build_module {
+ methods_struct.writeln('};')
+ }
+ }
+ // add line return after interface index declarations
+ sb.writeln('')
+ if inter_info.methods.len > 0 {
+ sb.writeln(methods_wrapper.str())
+ sb.writeln(methods_struct_def.str())
+ sb.writeln(methods_struct.str())
+ }
+ sb.writeln(cast_functions.str())
+ }
+ sb.writeln(conversion_functions.str())
+ return sb.str()
+}
+
+fn (mut g Gen) panic_debug_info(pos token.Position) (int, string, string, string) {
+ paline := pos.line_nr + 1
+ if isnil(g.fn_decl) {
+ return paline, '', 'main', 'C._vinit'
+ }
+ pafile := g.fn_decl.file.replace('\\', '/')
+ pafn := g.fn_decl.name.after('.')
+ pamod := g.fn_decl.modname()
+ return paline, pafile, pamod, pafn
+}
+
+pub fn get_guarded_include_text(iname string, imessage string) string {
+ res := '
+ |#if defined(__has_include)
+ |
+ |#if __has_include($iname)
+ |#include $iname
+ |#else
+ |#error VERROR_MESSAGE $imessage
+ |#endif
+ |
+ |#else
+ |#include $iname
+ |#endif
+ '.strip_margin()
+ return res
+}
+
+fn (mut g Gen) trace(fbase string, message string) {
+ if g.file.path_base == fbase {
+ println('> g.trace | ${fbase:-10s} | $message')
+ }
+}
+
+pub fn (mut g Gen) get_array_depth(el_typ ast.Type) int {
+ typ := g.unwrap_generic(el_typ)
+ sym := g.table.get_final_type_symbol(typ)
+ if sym.kind == .array {
+ info := sym.info as ast.Array
+ return 1 + g.get_array_depth(info.elem_type)
+ } else {
+ return 0
+ }
+}
+
+// returns true if `t` includes any pointer(s) - during garbage collection heap regions
+// that contain no pointers do not have to be scanned
+pub fn (mut g Gen) contains_ptr(el_typ ast.Type) bool {
+ if el_typ.is_ptr() || el_typ.is_pointer() {
+ return true
+ }
+ typ := g.unwrap_generic(el_typ)
+ if typ.is_ptr() {
+ return true
+ }
+ sym := g.table.get_final_type_symbol(typ)
+ if sym.language != .v {
+ return true
+ }
+ match sym.kind {
+ .i8, .i16, .int, .i64, .byte, .u16, .u32, .u64, .f32, .f64, .char, .size_t, .rune, .bool,
+ .enum_ {
+ return false
+ }
+ .array_fixed {
+ info := sym.info as ast.ArrayFixed
+ return g.contains_ptr(info.elem_type)
+ }
+ .struct_ {
+ info := sym.info as ast.Struct
+ for embed in info.embeds {
+ if g.contains_ptr(embed) {
+ return true
+ }
+ }
+ for field in info.fields {
+ if g.contains_ptr(field.typ) {
+ return true
+ }
+ }
+ return false
+ }
+ .aggregate {
+ info := sym.info as ast.Aggregate
+ for atyp in info.types {
+ if g.contains_ptr(atyp) {
+ return true
+ }
+ }
+ return false
+ }
+ .multi_return {
+ info := sym.info as ast.MultiReturn
+ for mrtyp in info.types {
+ if g.contains_ptr(mrtyp) {
+ return true
+ }
+ }
+ return false
+ }
+ else {
+ return true
+ }
+ }
+}
+
+fn (mut g Gen) check_noscan(elem_typ ast.Type) string {
+ if g.pref.gc_mode in [.boehm_full_opt, .boehm_incr_opt] {
+ if !g.contains_ptr(elem_typ) {
+ return '_noscan'
+ }
+ }
+ return ''
+}
diff --git a/v_windows/v/vlib/v/gen/c/cheaders.v b/v_windows/v/vlib/v/gen/c/cheaders.v
new file mode 100644
index 0000000..1c03c6f
--- /dev/null
+++ b/v_windows/v/vlib/v/gen/c/cheaders.v
@@ -0,0 +1,718 @@
+module c
+
+import strings
+import v.pref
+
+// NB: @@@ here serve as placeholders.
+// They will be replaced with correct strings
+// for each constant, during C code generation.
+
+// V_COMMIT_HASH is generated by cmd/tools/gen_vc.v .
+const c_commit_hash_default = '
+#ifndef V_COMMIT_HASH
+ #define V_COMMIT_HASH "@@@"
+#endif
+'
+
+// V_CURRENT_COMMIT_HASH is updated, when V is rebuilt inside a git repo.
+const c_current_commit_hash_default = '
+#ifndef V_CURRENT_COMMIT_HASH
+ #define V_CURRENT_COMMIT_HASH "@@@"
+#endif
+'
+
+const c_concurrency_helpers = '
+typedef struct __shared_map __shared_map;
+struct __shared_map { map val; sync__RwMutex mtx; };
+static inline voidptr __dup_shared_map(voidptr src, int sz) {
+ __shared_map* dest = memdup(src, sz);
+ sync__RwMutex_init(&dest->mtx);
+ return dest;
+}
+typedef struct __shared_array __shared_array;
+struct __shared_array { array val; sync__RwMutex mtx; };
+static inline voidptr __dup_shared_array(voidptr src, int sz) {
+ __shared_array* dest = memdup(src, sz);
+ sync__RwMutex_init(&dest->mtx);
+ return dest;
+}
+static inline void __sort_ptr(uintptr_t a[], bool b[], int l) {
+ for (int i=1; i<l; i++) {
+ uintptr_t ins = a[i];
+ bool insb = b[i];
+ int j = i;
+ while(j>0 && a[j-1] > ins) {
+ a[j] = a[j-1];
+ b[j] = b[j-1];
+ j--;
+ }
+ a[j] = ins;
+ b[j] = insb;
+ }
+}
+'
+
+// Heavily based on Chris Wellons's work
+// https://nullprogram.com/blog/2017/01/08/
+
+fn c_closure_helpers(pref &pref.Preferences) string {
+ if pref.os == .windows {
+ verror('closures are not implemented on Windows yet')
+ }
+ if pref.arch != .amd64 {
+ verror('closures are not implemented on this architecture yet: $pref.arch')
+ }
+ mut builder := strings.new_builder(2048)
+ if pref.os != .windows {
+ builder.writeln('#include <sys/mman.h>')
+ }
+ if pref.arch == .amd64 {
+ builder.write_string('
+static unsigned char __closure_thunk[6][13] = {
+ {
+ 0x48, 0x8b, 0x3d, 0xe9, 0xff, 0xff, 0xff,
+ 0xff, 0x25, 0xeb, 0xff, 0xff, 0xff
+ }, {
+ 0x48, 0x8b, 0x35, 0xe9, 0xff, 0xff, 0xff,
+ 0xff, 0x25, 0xeb, 0xff, 0xff, 0xff
+ }, {
+ 0x48, 0x8b, 0x15, 0xe9, 0xff, 0xff, 0xff,
+ 0xff, 0x25, 0xeb, 0xff, 0xff, 0xff
+ }, {
+ 0x48, 0x8b, 0x0d, 0xe9, 0xff, 0xff, 0xff,
+ 0xff, 0x25, 0xeb, 0xff, 0xff, 0xff
+ }, {
+ 0x4C, 0x8b, 0x05, 0xe9, 0xff, 0xff, 0xff,
+ 0xff, 0x25, 0xeb, 0xff, 0xff, 0xff
+ }, {
+ 0x4C, 0x8b, 0x0d, 0xe9, 0xff, 0xff, 0xff,
+ 0xff, 0x25, 0xeb, 0xff, 0xff, 0xff
+ },
+};
+')
+ }
+ builder.write_string('
+static void __closure_set_data(void *closure, void *data) {
+ void **p = closure;
+ p[-2] = data;
+}
+
+static void __closure_set_function(void *closure, void *f) {
+ void **p = closure;
+ p[-1] = f;
+}
+')
+ if pref.os != .windows {
+ builder.write_string('
+static void * __closure_create(void *f, int nargs, void *userdata) {
+ long page_size = sysconf(_SC_PAGESIZE);
+ int prot = PROT_READ | PROT_WRITE;
+ int flags = MAP_ANONYMOUS | MAP_PRIVATE;
+ char *p = mmap(0, page_size * 2, prot, flags, -1, 0);
+ if (p == MAP_FAILED)
+ return 0;
+ void *closure = p + page_size;
+ memcpy(closure, __closure_thunk[nargs - 1], sizeof(__closure_thunk[0]));
+ mprotect(closure, page_size, PROT_READ | PROT_EXEC);
+ __closure_set_function(closure, f);
+ __closure_set_data(closure, userdata);
+ return closure;
+}
+
+static void __closure_destroy(void *closure) {
+ long page_size = sysconf(_SC_PAGESIZE);
+ munmap((char *)closure - page_size, page_size * 2);
+}
+')
+ }
+ return builder.str()
+}
+
+const c_common_macros = '
+#define EMPTY_VARG_INITIALIZATION 0
+#define EMPTY_STRUCT_DECLARATION
+#define EMPTY_STRUCT_INITIALIZATION
+// Due to a tcc bug, the length of an array needs to be specified, but GCC crashes if it is...
+#define EMPTY_ARRAY_OF_ELEMS(x,n) (x[])
+#define TCCSKIP(x) x
+
+#define __NOINLINE __attribute__((noinline))
+#define __IRQHANDLER __attribute__((interrupt))
+
+#define __V_architecture 0
+#if defined(__x86_64__)
+ #define __V_amd64 1
+ #undef __V_architecture
+ #define __V_architecture 1
+#endif
+
+#if defined(__aarch64__) || defined(__arm64__)
+ #define __V_arm64 1
+ #undef __V_architecture
+ #define __V_architecture 2
+#endif
+
+// Using just __GNUC__ for detecting gcc, is not reliable because other compilers define it too:
+#ifdef __GNUC__
+ #define __V_GCC__
+#endif
+#ifdef __TINYC__
+ #undef __V_GCC__
+#endif
+#ifdef __cplusplus
+ #undef __V_GCC__
+#endif
+#ifdef __clang__
+ #undef __V_GCC__
+#endif
+#ifdef _MSC_VER
+ #undef __V_GCC__
+ #undef EMPTY_STRUCT_INITIALIZATION
+ #define EMPTY_STRUCT_INITIALIZATION 0
+#endif
+
+#ifdef __TINYC__
+ #undef EMPTY_STRUCT_DECLARATION
+ #define EMPTY_STRUCT_DECLARATION char _dummy
+ #undef EMPTY_ARRAY_OF_ELEMS
+ #define EMPTY_ARRAY_OF_ELEMS(x,n) (x[n])
+ #undef __NOINLINE
+ #undef __IRQHANDLER
+ // tcc does not support inlining at all
+ #define __NOINLINE
+ #define __IRQHANDLER
+ #undef TCCSKIP
+ #define TCCSKIP(x)
+ // #include <byteswap.h>
+ #ifndef _WIN32
+ #include <execinfo.h>
+ int tcc_backtrace(const char *fmt, ...);
+ #endif
+#endif
+
+// Use __offsetof_ptr instead of __offset_of, when you *do* have a valid pointer, to avoid UB:
+#ifndef __offsetof_ptr
+ #define __offsetof_ptr(ptr,PTYPE,FIELDNAME) ((size_t)((byte *)&((PTYPE *)ptr)->FIELDNAME - (byte *)ptr))
+#endif
+
+// for __offset_of
+#ifndef __offsetof
+ #define __offsetof(PTYPE,FIELDNAME) ((size_t)((char *)&((PTYPE *)0)->FIELDNAME - (char *)0))
+#endif
+
+#define OPTION_CAST(x) (x)
+
+#ifndef V64_PRINTFORMAT
+ #ifdef PRIx64
+ #define V64_PRINTFORMAT "0x%"PRIx64
+ #elif defined(__WIN32__)
+ #define V64_PRINTFORMAT "0x%I64x"
+ #elif defined(__linux__) && defined(__LP64__)
+ #define V64_PRINTFORMAT "0x%lx"
+ #else
+ #define V64_PRINTFORMAT "0x%llx"
+ #endif
+#endif
+
+#if defined(_WIN32) || defined(__CYGWIN__)
+ #define VV_EXPORTED_SYMBOL extern __declspec(dllexport)
+ #define VV_LOCAL_SYMBOL static
+#else
+ // 4 < gcc < 5 is used by some older Ubuntu LTS and Centos versions,
+ // and does not support __has_attribute(visibility) ...
+ #ifndef __has_attribute
+ #define __has_attribute(x) 0 // Compatibility with non-clang compilers.
+ #endif
+ #if (defined(__GNUC__) && (__GNUC__ >= 4)) || (defined(__clang__) && __has_attribute(visibility))
+ #ifdef ARM
+ #define VV_EXPORTED_SYMBOL extern __attribute__((externally_visible,visibility("default")))
+ #else
+ #define VV_EXPORTED_SYMBOL extern __attribute__((visibility("default")))
+ #endif
+ #define VV_LOCAL_SYMBOL __attribute__ ((visibility ("hidden")))
+ #else
+ #define VV_EXPORTED_SYMBOL extern
+ #define VV_LOCAL_SYMBOL static
+ #endif
+#endif
+
+#ifdef __cplusplus
+ #include <utility>
+ #define _MOV std::move
+#else
+ #define _MOV
+#endif
+
+// tcc does not support has_include properly yet, turn it off completely
+#if defined(__TINYC__) && defined(__has_include)
+#undef __has_include
+#endif
+
+#if !defined(VNORETURN)
+ #if defined(__TINYC__)
+ #include <stdnoreturn.h>
+ #define VNORETURN noreturn
+ #endif
+ # if !defined(__TINYC__) && defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L
+ # define VNORETURN _Noreturn
+ # elif defined(__GNUC__) && __GNUC__ >= 2
+ # define VNORETURN __attribute__((noreturn))
+ # endif
+ #ifndef VNORETURN
+ #define VNORETURN
+ #endif
+#endif
+
+#if !defined(VUNREACHABLE)
+ #if defined(__GNUC__) && !defined(__clang__)
+ #define V_GCC_VERSION (__GNUC__ * 10000L + __GNUC_MINOR__ * 100L + __GNUC_PATCHLEVEL__)
+ #if (V_GCC_VERSION >= 40500L)
+ #define VUNREACHABLE() do { __builtin_unreachable(); } while (0)
+ #endif
+ #endif
+ #if defined(__clang__) && defined(__has_builtin)
+ #if __has_builtin(__builtin_unreachable)
+ #define VUNREACHABLE() do { __builtin_unreachable(); } while (0)
+ #endif
+ #endif
+ #ifndef VUNREACHABLE
+ #define VUNREACHABLE() do { } while (0)
+ #endif
+ #if defined(__FreeBSD__) && defined(__TINYC__)
+ #define VUNREACHABLE() do { } while (0)
+ #endif
+#endif
+
+//likely and unlikely macros
+#if defined(__GNUC__) || defined(__INTEL_COMPILER) || defined(__clang__)
+ #define _likely_(x) __builtin_expect(x,1)
+ #define _unlikely_(x) __builtin_expect(x,0)
+#else
+ #define _likely_(x) (x)
+ #define _unlikely_(x) (x)
+#endif
+
+'
+
+const c_unsigned_comparison_functions = '
+// unsigned/signed comparisons
+static inline bool _us32_gt(uint32_t a, int32_t b) { return a > INT32_MAX || (int32_t)a > b; }
+static inline bool _us32_ge(uint32_t a, int32_t b) { return a >= INT32_MAX || (int32_t)a >= b; }
+static inline bool _us32_eq(uint32_t a, int32_t b) { return a <= INT32_MAX && (int32_t)a == b; }
+static inline bool _us32_ne(uint32_t a, int32_t b) { return a > INT32_MAX || (int32_t)a != b; }
+static inline bool _us32_le(uint32_t a, int32_t b) { return a <= INT32_MAX && (int32_t)a <= b; }
+static inline bool _us32_lt(uint32_t a, int32_t b) { return a < INT32_MAX && (int32_t)a < b; }
+static inline bool _us64_gt(uint64_t a, int64_t b) { return a > INT64_MAX || (int64_t)a > b; }
+static inline bool _us64_ge(uint64_t a, int64_t b) { return a >= INT64_MAX || (int64_t)a >= b; }
+static inline bool _us64_eq(uint64_t a, int64_t b) { return a <= INT64_MAX && (int64_t)a == b; }
+static inline bool _us64_ne(uint64_t a, int64_t b) { return a > INT64_MAX || (int64_t)a != b; }
+static inline bool _us64_le(uint64_t a, int64_t b) { return a <= INT64_MAX && (int64_t)a <= b; }
+static inline bool _us64_lt(uint64_t a, int64_t b) { return a < INT64_MAX && (int64_t)a < b; }
+'
+
+const c_helper_macros = '//============================== HELPER C MACROS =============================*/
+// _SLIT0 is used as NULL string for literal arguments
+// `"" s` is used to enforce a string literal argument
+#define _SLIT0 (string){.str=(byteptr)(""), .len=0, .is_lit=1}
+#define _SLIT(s) ((string){.str=(byteptr)("" s), .len=(sizeof(s)-1), .is_lit=1})
+#define _SLEN(s, n) ((string){.str=(byteptr)("" s), .len=n, .is_lit=1})
+
+// take the address of an rvalue
+#define ADDR(type, expr) (&((type[]){expr}[0]))
+
+// copy something to the heap
+#define HEAP(type, expr) ((type*)memdup((void*)&((type[]){expr}[0]), sizeof(type)))
+#define HEAP_noscan(type, expr) ((type*)memdup_noscan((void*)&((type[]){expr}[0]), sizeof(type)))
+
+#define _PUSH_MANY(arr, val, tmp, tmp_typ) {tmp_typ tmp = (val); array_push_many(arr, tmp.data, tmp.len);}
+#define _PUSH_MANY_noscan(arr, val, tmp, tmp_typ) {tmp_typ tmp = (val); array_push_many_noscan(arr, tmp.data, tmp.len);}
+'
+
+const c_headers = c_helper_macros + c_unsigned_comparison_functions + c_common_macros +
+ r'
+// c_headers
+typedef int (*qsort_callback_func)(const void*, const void*);
+#include <stdio.h> // TODO remove all these includes, define all function signatures and types manually
+#include <stdlib.h>
+#include <string.h>
+
+#if defined(_WIN32) || defined(__CYGWIN__)
+ #define VV_EXPORTED_SYMBOL extern __declspec(dllexport)
+ #define VV_LOCAL_SYMBOL static
+#else
+ // 4 < gcc < 5 is used by some older Ubuntu LTS and Centos versions,
+ // and does not support __has_attribute(visibility) ...
+ #ifndef __has_attribute
+ #define __has_attribute(x) 0 // Compatibility with non-clang compilers.
+ #endif
+ #if (defined(__GNUC__) && (__GNUC__ >= 4)) || (defined(__clang__) && __has_attribute(visibility))
+ #ifdef ARM
+ #define VV_EXPORTED_SYMBOL extern __attribute__((externally_visible,visibility("default")))
+ #else
+ #define VV_EXPORTED_SYMBOL extern __attribute__((visibility("default")))
+ #endif
+ #define VV_LOCAL_SYMBOL __attribute__ ((visibility ("hidden")))
+ #else
+ #define VV_EXPORTED_SYMBOL extern
+ #define VV_LOCAL_SYMBOL static
+ #endif
+#endif
+
+#if defined(__TINYC__) && defined(__has_include)
+// tcc does not support has_include properly yet, turn it off completely
+#undef __has_include
+#endif
+
+#ifndef _WIN32
+ #if defined __has_include
+ #if __has_include (<execinfo.h>)
+ #include <execinfo.h>
+ #else
+ // Most probably musl OR __ANDROID__ ...
+ int backtrace (void **__array, int __size) { return 0; }
+ char **backtrace_symbols (void *const *__array, int __size){ return 0; }
+ void backtrace_symbols_fd (void *const *__array, int __size, int __fd){}
+ #endif
+ #endif
+#endif
+
+#include <stdarg.h> // for va_list
+
+//================================== GLOBALS =================================*/
+int load_so(byteptr);
+void reload_so();
+void _vinit(int ___argc, voidptr ___argv);
+void _vcleanup();
+#define sigaction_size sizeof(sigaction);
+#define _ARR_LEN(a) ( (sizeof(a)) / (sizeof(a[0])) )
+
+void v_free(voidptr ptr);
+voidptr memdup(voidptr src, int sz);
+
+#if INTPTR_MAX == INT32_MAX
+ #define TARGET_IS_32BIT 1
+#elif INTPTR_MAX == INT64_MAX
+ #define TARGET_IS_64BIT 1
+#else
+ #error "The environment is not 32 or 64-bit."
+#endif
+
+#if defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ || defined(__BYTE_ORDER) && __BYTE_ORDER == __BIG_ENDIAN || defined(__BIG_ENDIAN__) || defined(__ARMEB__) || defined(__THUMBEB__) || defined(__AARCH64EB__) || defined(_MIBSEB) || defined(__MIBSEB) || defined(__MIBSEB__)
+ #define TARGET_ORDER_IS_BIG 1
+#elif defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ || defined(__BYTE_ORDER) && __BYTE_ORDER == __LITTLE_ENDIAN || defined(__LITTLE_ENDIAN__) || defined(__ARMEL__) || defined(__THUMBEL__) || defined(__AARCH64EL__) || defined(_MIPSEL) || defined(__MIPSEL) || defined(__MIPSEL__) || defined(_M_AMD64) || defined(_M_X64) || defined(_M_IX86)
+ #define TARGET_ORDER_IS_LITTLE 1
+#else
+ #error "Unknown architecture endianness"
+#endif
+
+#ifndef _WIN32
+ #include <ctype.h>
+ #include <locale.h> // tolower
+ #include <sys/time.h>
+ #include <unistd.h> // sleep
+ extern char **environ;
+#endif
+
+#if defined(__CYGWIN__) && !defined(_WIN32)
+ #error Cygwin is not supported, please use MinGW or Visual Studio.
+#endif
+
+#if defined(__linux__) || defined(__FreeBSD__) || defined(__DragonFly__) || defined(__vinix__) || defined(__serenity__) || defined(__sun)
+ #include <sys/types.h>
+ #include <sys/wait.h> // os__wait uses wait on nix
+#endif
+
+#ifdef __OpenBSD__
+ #include <sys/types.h>
+ #include <sys/resource.h>
+ #include <sys/wait.h> // os__wait uses wait on nix
+#endif
+
+#ifdef __NetBSD__
+ #include <sys/wait.h> // os__wait uses wait on nix
+#endif
+
+#ifdef _WIN32
+ #define WINVER 0x0600
+ #ifdef _WIN32_WINNT
+ #undef _WIN32_WINNT
+ #endif
+ #define _WIN32_WINNT 0x0600
+ #ifndef WIN32_FULL
+ #define WIN32_LEAN_AND_MEAN
+ #endif
+ #ifndef _UNICODE
+ #define _UNICODE
+ #endif
+ #ifndef UNICODE
+ #define UNICODE
+ #endif
+ #include <windows.h>
+
+ #include <io.h> // _waccess
+ #include <direct.h> // _wgetcwd
+
+ #ifdef _MSC_VER
+ // On MSVC these are the same (as long as /volatile:ms is passed)
+ #define _Atomic volatile
+
+ // MSVC cannot parse some things properly
+ #undef EMPTY_STRUCT_DECLARATION
+ #undef OPTION_CAST
+
+ #define EMPTY_STRUCT_DECLARATION char __pad
+ #define OPTION_CAST(x)
+ #undef __NOINLINE
+ #undef __IRQHANDLER
+ #define __NOINLINE __declspec(noinline)
+ #define __IRQHANDLER __declspec(naked)
+
+ #include <dbghelp.h>
+ #pragma comment(lib, "Dbghelp")
+ #endif
+#else
+ #include <pthread.h>
+ #ifndef PTHREAD_RWLOCK_PREFER_WRITER_NONRECURSIVE_NP
+ // musl does not have that
+ #define pthread_rwlockattr_setkind_np(a, b)
+ #endif
+#endif
+
+// g_live_info is used by live.info()
+static void* g_live_info = NULL;
+
+#if defined(__MINGW32__) || defined(__MINGW64__) || (defined(_WIN32) && defined(__TINYC__))
+ #undef PRId64
+ #undef PRIi64
+ #undef PRIo64
+ #undef PRIu64
+ #undef PRIx64
+ #undef PRIX64
+ #define PRId64 "lld"
+ #define PRIi64 "lli"
+ #define PRIo64 "llo"
+ #define PRIu64 "llu"
+ #define PRIx64 "llx"
+ #define PRIX64 "llX"
+#endif
+
+#ifdef _VFREESTANDING
+#undef _VFREESTANDING
+#endif
+'
+
+const c_builtin_types = '
+//================================== builtin types ================================*/
+typedef int64_t i64;
+typedef int16_t i16;
+typedef int8_t i8;
+typedef uint64_t u64;
+typedef uint32_t u32;
+typedef uint8_t u8;
+typedef uint16_t u16;
+typedef uint8_t byte;
+typedef uint32_t rune;
+typedef float f32;
+typedef double f64;
+typedef int64_t int_literal;
+typedef double float_literal;
+typedef unsigned char* byteptr;
+typedef void* voidptr;
+typedef char* charptr;
+typedef byte array_fixed_byte_300 [300];
+
+typedef struct sync__Channel* chan;
+
+#ifndef __cplusplus
+ #ifndef bool
+ typedef byte bool;
+ #define true 1
+ #define false 0
+ #endif
+#endif
+
+typedef u64 (*MapHashFn)(voidptr);
+typedef bool (*MapEqFn)(voidptr, voidptr);
+typedef void (*MapCloneFn)(voidptr, voidptr);
+typedef void (*MapFreeFn)(voidptr);
+'
+
+const c_bare_headers = c_helper_macros + c_unsigned_comparison_functions + c_common_macros +
+ '
+#define _VFREESTANDING
+
+typedef long unsigned int size_t;
+
+// Memory allocation related headers
+void *malloc(size_t size);
+void *calloc(size_t nitems, size_t size);
+void *realloc(void *ptr, size_t size);
+void *memcpy(void *dest, void *src, size_t n);
+void *memset(void *s, int c, size_t n);
+void *memmove(void *dest, void *src, size_t n);
+
+// varargs implementation, TODO: works on tcc and gcc, but is very unportable and hacky
+typedef __builtin_va_list va_list;
+#define va_start(a, b) __builtin_va_start(a, b)
+#define va_end(a) __builtin_va_end(a)
+#define va_arg(a, b) __builtin_va_arg(a, b)
+#define va_copy(a, b) __builtin_va_copy(a, b)
+
+//================================== GLOBALS =================================*/
+int load_so(byteptr);
+void reload_so();
+void _vinit(int ___argc, voidptr ___argv);
+void _vcleanup();
+#define sigaction_size sizeof(sigaction);
+#define _ARR_LEN(a) ( (sizeof(a)) / (sizeof(a[0])) )
+
+void v_free(voidptr ptr);
+voidptr memdup(voidptr src, int sz);
+
+'
+
+const c_wyhash_headers = '
+// ============== wyhash ==============
+#ifndef wyhash_final_version_3
+#define wyhash_final_version_3
+
+#ifndef WYHASH_CONDOM
+// protections that produce different results:
+// 1: normal valid behavior
+// 2: extra protection against entropy loss (probability=2^-63), aka. "blind multiplication"
+#define WYHASH_CONDOM 1
+#endif
+
+#ifndef WYHASH_32BIT_MUM
+// 0: normal version, slow on 32 bit systems
+// 1: faster on 32 bit systems but produces different results, incompatible with wy2u0k function
+#define WYHASH_32BIT_MUM 0
+#endif
+
+// includes
+#include <stdint.h>
+#if defined(_MSC_VER) && defined(_M_X64)
+ #include <intrin.h>
+ #pragma intrinsic(_umul128)
+#endif
+
+// 128bit multiply function
+static inline uint64_t _wyrot(uint64_t x) { return (x>>32)|(x<<32); }
+static inline void _wymum(uint64_t *A, uint64_t *B){
+#if(WYHASH_32BIT_MUM)
+ uint64_t hh=(*A>>32)*(*B>>32), hl=(*A>>32)*(uint32_t)*B, lh=(uint32_t)*A*(*B>>32), ll=(uint64_t)(uint32_t)*A*(uint32_t)*B;
+ #if(WYHASH_CONDOM>1)
+ *A^=_wyrot(hl)^hh; *B^=_wyrot(lh)^ll;
+ #else
+ *A=_wyrot(hl)^hh; *B=_wyrot(lh)^ll;
+ #endif
+#elif defined(__SIZEOF_INT128__)
+ __uint128_t r=*A; r*=*B;
+ #if(WYHASH_CONDOM>1)
+ *A^=(uint64_t)r; *B^=(uint64_t)(r>>64);
+ #else
+ *A=(uint64_t)r; *B=(uint64_t)(r>>64);
+ #endif
+#elif defined(_MSC_VER) && defined(_M_X64)
+ #if(WYHASH_CONDOM>1)
+ uint64_t a, b;
+ a=_umul128(*A,*B,&b);
+ *A^=a; *B^=b;
+ #else
+ *A=_umul128(*A,*B,B);
+ #endif
+#else
+ uint64_t ha=*A>>32, hb=*B>>32, la=(uint32_t)*A, lb=(uint32_t)*B, hi, lo;
+ uint64_t rh=ha*hb, rm0=ha*lb, rm1=hb*la, rl=la*lb, t=rl+(rm0<<32), c=t<rl;
+ lo=t+(rm1<<32); c+=lo<t; hi=rh+(rm0>>32)+(rm1>>32)+c;
+ #if(WYHASH_CONDOM>1)
+ *A^=lo; *B^=hi;
+ #else
+ *A=lo; *B=hi;
+ #endif
+#endif
+}
+
+// multiply and xor mix function, aka MUM
+static inline uint64_t _wymix(uint64_t A, uint64_t B){ _wymum(&A,&B); return A^B; }
+
+// endian macros
+#ifndef WYHASH_LITTLE_ENDIAN
+ #ifdef TARGET_ORDER_IS_LITTLE
+ #define WYHASH_LITTLE_ENDIAN 1
+ #else
+ #define WYHASH_LITTLE_ENDIAN 0
+ #endif
+#endif
+
+// read functions
+#if (WYHASH_LITTLE_ENDIAN)
+ static inline uint64_t _wyr8(const uint8_t *p) { uint64_t v; memcpy(&v, p, 8); return v;}
+ static inline uint64_t _wyr4(const uint8_t *p) { uint32_t v; memcpy(&v, p, 4); return v;}
+#elif defined(__GNUC__) || defined(__INTEL_COMPILER) || defined(__clang__)
+ static inline uint64_t _wyr8(const uint8_t *p) { uint64_t v; memcpy(&v, p, 8); return __builtin_bswap64(v);}
+ static inline uint64_t _wyr4(const uint8_t *p) { uint32_t v; memcpy(&v, p, 4); return __builtin_bswap32(v);}
+#elif defined(_MSC_VER)
+ static inline uint64_t _wyr8(const uint8_t *p) { uint64_t v; memcpy(&v, p, 8); return _byteswap_uint64(v);}
+ static inline uint64_t _wyr4(const uint8_t *p) { uint32_t v; memcpy(&v, p, 4); return _byteswap_ulong(v);}
+#else
+ static inline uint64_t _wyr8(const uint8_t *p) {
+ uint64_t v; memcpy(&v, p, 8);
+ return (((v >> 56) & 0xff)| ((v >> 40) & 0xff00)| ((v >> 24) & 0xff0000)| ((v >> 8) & 0xff000000)| ((v << 8) & 0xff00000000)| ((v << 24) & 0xff0000000000)| ((v << 40) & 0xff000000000000)| ((v << 56) & 0xff00000000000000));
+ }
+ static inline uint64_t _wyr4(const uint8_t *p) {
+ uint32_t v; memcpy(&v, p, 4);
+ return (((v >> 24) & 0xff)| ((v >> 8) & 0xff00)| ((v << 8) & 0xff0000)| ((v << 24) & 0xff000000));
+ }
+#endif
+static inline uint64_t _wyr3(const uint8_t *p, size_t k) { return (((uint64_t)p[0])<<16)|(((uint64_t)p[k>>1])<<8)|p[k-1];}
+// wyhash main function
+static inline uint64_t wyhash(const void *key, size_t len, uint64_t seed, const uint64_t *secret){
+ const uint8_t *p=(const uint8_t *)key; seed^=*secret; uint64_t a, b;
+ if (_likely_(len<=16)) {
+ if (_likely_(len>=4)) { a=(_wyr4(p)<<32)|_wyr4(p+((len>>3)<<2)); b=(_wyr4(p+len-4)<<32)|_wyr4(p+len-4-((len>>3)<<2)); }
+ else if (_likely_(len>0)) { a=_wyr3(p,len); b=0; }
+ else a=b=0;
+ } else {
+ size_t i=len;
+ if (_unlikely_(i>48)) {
+ uint64_t see1=seed, see2=seed;
+ do {
+ seed=_wymix(_wyr8(p)^secret[1],_wyr8(p+8)^seed);
+ see1=_wymix(_wyr8(p+16)^secret[2],_wyr8(p+24)^see1);
+ see2=_wymix(_wyr8(p+32)^secret[3],_wyr8(p+40)^see2);
+ p+=48; i-=48;
+ } while(_likely_(i>48));
+ seed^=see1^see2;
+ }
+ while(_unlikely_(i>16)) { seed=_wymix(_wyr8(p)^secret[1],_wyr8(p+8)^seed); i-=16; p+=16; }
+ a=_wyr8(p+i-16); b=_wyr8(p+i-8);
+ }
+ return _wymix(secret[1]^len,_wymix(a^secret[1],b^seed));
+}
+// the default secret parameters
+static const uint64_t _wyp[4] = {0xa0761d6478bd642full, 0xe7037ed1a0b428dbull, 0x8ebc6af09c88c6e3ull, 0x589965cc75374cc3ull};
+
+// a useful 64bit-64bit mix function to produce deterministic pseudo random numbers that can pass BigCrush and PractRand
+static inline uint64_t wyhash64(uint64_t A, uint64_t B){ A^=0xa0761d6478bd642full; B^=0xe7037ed1a0b428dbull; _wymum(&A,&B); return _wymix(A^0xa0761d6478bd642full,B^0xe7037ed1a0b428dbull);}
+
+// the wyrand PRNG that pass BigCrush and PractRand
+static inline uint64_t wyrand(uint64_t *seed){ *seed+=0xa0761d6478bd642full; return _wymix(*seed,*seed^0xe7037ed1a0b428dbull);}
+
+#ifndef __vinix__
+// convert any 64 bit pseudo random numbers to uniform distribution [0,1). It can be combined with wyrand, wyhash64 or wyhash.
+static inline double wy2u01(uint64_t r){ const double _wynorm=1.0/(1ull<<52); return (r>>12)*_wynorm;}
+
+// convert any 64 bit pseudo random numbers to APPROXIMATE Gaussian distribution. It can be combined with wyrand, wyhash64 or wyhash.
+static inline double wy2gau(uint64_t r){ const double _wynorm=1.0/(1ull<<20); return ((r&0x1fffff)+((r>>21)&0x1fffff)+((r>>42)&0x1fffff))*_wynorm-3.0;}
+#endif
+
+#if(!WYHASH_32BIT_MUM)
+// fast range integer random number generation on [0,k) credit to Daniel Lemire. May not work when WYHASH_32BIT_MUM=1. It can be combined with wyrand, wyhash64 or wyhash.
+static inline uint64_t wy2u0k(uint64_t r, uint64_t k){ _wymum(&r,&k); return k; }
+#endif
+#endif
+
+#define _IN_MAP(val, m) map_exists(m, val)
+
+'
diff --git a/v_windows/v/vlib/v/gen/c/cmain.v b/v_windows/v/vlib/v/gen/c/cmain.v
new file mode 100644
index 0000000..6e741a6
--- /dev/null
+++ b/v_windows/v/vlib/v/gen/c/cmain.v
@@ -0,0 +1,214 @@
+module c
+
+import v.util
+import v.ast
+
+pub fn (mut g Gen) gen_c_main() {
+ if !g.has_main {
+ return
+ }
+ if g.pref.is_liveshared {
+ return
+ }
+ g.out.writeln('')
+ main_fn_start_pos := g.out.len
+
+ is_sokol := 'sokol' in g.table.imports
+
+ if (g.pref.os == .android && g.pref.is_apk) || (g.pref.os == .ios && is_sokol) {
+ g.gen_c_android_sokol_main()
+ } else {
+ g.gen_c_main_header()
+ g.writeln('\tmain__main();')
+ g.gen_c_main_footer()
+ if g.pref.printfn_list.len > 0 && 'main' in g.pref.printfn_list {
+ println(g.out.after(main_fn_start_pos))
+ }
+ }
+}
+
+fn (mut g Gen) gen_vlines_reset() {
+ if g.pref.is_vlines {
+ // At this point, the v files are transpiled.
+ // The rest is auto generated code, which will not have
+ // different .v source file/line numbers.
+ //
+ // TODO: calculate the proper line here, based on
+ // the actual C lines in all the buffers
+ lines_so_far := 1000000
+ g.vlines_path = util.vlines_escape_path(g.pref.out_name_c, g.pref.ccompiler)
+ g.writeln('')
+ g.writeln('\n// Reset the file/line numbers')
+ g.writeln('\n#line $lines_so_far "$g.vlines_path"')
+ g.writeln('')
+ }
+}
+
+fn (mut g Gen) gen_c_main_function_header() {
+ if g.pref.os == .windows {
+ if g.is_gui_app() {
+ $if msvc {
+ // This is kinda bad but I dont see a way that is better
+ g.writeln('#pragma comment(linker, "/SUBSYSTEM:WINDOWS")')
+ }
+ // GUI application
+ g.writeln('int WINAPI wWinMain(HINSTANCE instance, HINSTANCE prev_instance, LPWSTR cmd_line, int show_cmd){')
+ g.writeln('\tLPWSTR full_cmd_line = GetCommandLineW(); // NB: do not use cmd_line')
+ g.writeln('\ttypedef LPWSTR*(WINAPI *cmd_line_to_argv)(LPCWSTR, int*);')
+ g.writeln('\tHMODULE shell32_module = LoadLibrary(L"shell32.dll");')
+ g.writeln('\tcmd_line_to_argv CommandLineToArgvW = (cmd_line_to_argv)GetProcAddress(shell32_module, "CommandLineToArgvW");')
+ g.writeln('\tint ___argc;')
+ g.writeln('\twchar_t** ___argv = CommandLineToArgvW(full_cmd_line, &___argc);')
+ } else {
+ // Console application
+ g.writeln('int wmain(int ___argc, wchar_t* ___argv[], wchar_t* ___envp[]){')
+ }
+ } else {
+ g.writeln('int main(int ___argc, char** ___argv){')
+ }
+}
+
+fn (mut g Gen) gen_c_main_header() {
+ g.gen_c_main_function_header()
+ if g.pref.gc_mode in [.boehm_full, .boehm_incr, .boehm_full_opt, .boehm_incr_opt, .boehm_leak] {
+ g.writeln('#if defined(_VGCBOEHM)')
+ if g.pref.gc_mode == .boehm_leak {
+ g.writeln('\tGC_set_find_leak(1);')
+ }
+ g.writeln('\tGC_INIT();')
+ if g.pref.gc_mode in [.boehm_incr, .boehm_incr_opt] {
+ g.writeln('\tGC_enable_incremental();')
+ }
+ g.writeln('#endif')
+ }
+ g.writeln('\t_vinit(___argc, (voidptr)___argv);')
+ if g.pref.is_prof {
+ g.writeln('')
+ g.writeln('\tatexit(vprint_profile_stats);')
+ g.writeln('')
+ }
+ if g.pref.is_livemain {
+ g.generate_hotcode_reloading_main_caller()
+ }
+}
+
+pub fn (mut g Gen) gen_c_main_footer() {
+ g.writeln('\t_vcleanup();')
+ g.writeln('\treturn 0;')
+ g.writeln('}')
+}
+
+pub fn (mut g Gen) gen_c_android_sokol_main() {
+ // Weave autofree into sokol lifecycle callback(s)
+ if g.is_autofree {
+ g.writeln('// Wrapping cleanup/free callbacks for sokol to include _vcleanup()
+void (*_vsokol_user_cleanup_ptr)(void);
+void (*_vsokol_user_cleanup_cb_ptr)(void *);
+
+void (_vsokol_cleanup_cb)(void) {
+ if (_vsokol_user_cleanup_ptr) {
+ _vsokol_user_cleanup_ptr();
+ }
+ _vcleanup();
+}
+
+void (_vsokol_cleanup_userdata_cb)(void* user_data) {
+ if (_vsokol_user_cleanup_cb_ptr) {
+ _vsokol_user_cleanup_cb_ptr(g_desc.user_data);
+ }
+ _vcleanup();
+}
+')
+ }
+ g.writeln('// The sokol_main entry point on Android
+sapp_desc sokol_main(int argc, char* argv[]) {
+ (void)argc; (void)argv;
+
+ _vinit(argc, (voidptr)argv);
+ main__main();
+')
+ if g.is_autofree {
+ g.writeln(' // Wrap user provided cleanup/free functions for sokol to be able to call _vcleanup()
+ if (g_desc.cleanup_cb) {
+ _vsokol_user_cleanup_ptr = g_desc.cleanup_cb;
+ g_desc.cleanup_cb = _vsokol_cleanup_cb;
+ }
+ else if (g_desc.cleanup_userdata_cb) {
+ _vsokol_user_cleanup_cb_ptr = g_desc.cleanup_userdata_cb;
+ g_desc.cleanup_userdata_cb = _vsokol_cleanup_userdata_cb;
+ }
+')
+ }
+ g.writeln(' return g_desc;')
+ g.writeln('}')
+}
+
+pub fn (mut g Gen) write_tests_definitions() {
+ g.includes.writeln('#include <setjmp.h> // write_tests_main')
+ g.definitions.writeln('int g_test_oks = 0;')
+ g.definitions.writeln('int g_test_fails = 0;')
+ g.definitions.writeln('jmp_buf g_jump_buffer;')
+}
+
+pub fn (mut g Gen) gen_failing_error_propagation_for_test_fn(or_block ast.OrExpr, cvar_name string) {
+ // in test_() functions, an `opt()?` call is sugar for
+ // `or { cb_propagate_test_error(@LINE, @FILE, @MOD, @FN, err.msg) }`
+ // and the test is considered failed
+ paline, pafile, pamod, pafn := g.panic_debug_info(or_block.pos)
+ g.writeln('\tmain__cb_propagate_test_error($paline, tos3("$pafile"), tos3("$pamod"), tos3("$pafn"), *(${cvar_name}.err.msg) );')
+ g.writeln('\tg_test_fails++;')
+ g.writeln('\tlongjmp(g_jump_buffer, 1);')
+}
+
+pub fn (mut g Gen) gen_failing_return_error_for_test_fn(return_stmt ast.Return, cvar_name string) {
+ // in test_() functions, a `return error('something')` is sugar for
+ // `or { err := error('something') cb_propagate_test_error(@LINE, @FILE, @MOD, @FN, err.msg) return err }`
+ // and the test is considered failed
+ paline, pafile, pamod, pafn := g.panic_debug_info(return_stmt.pos)
+ g.writeln('\tmain__cb_propagate_test_error($paline, tos3("$pafile"), tos3("$pamod"), tos3("$pafn"), *(${cvar_name}.err.msg) );')
+ g.writeln('\tg_test_fails++;')
+ g.writeln('\tlongjmp(g_jump_buffer, 1);')
+}
+
+pub fn (mut g Gen) gen_c_main_for_tests() {
+ main_fn_start_pos := g.out.len
+ g.writeln('')
+ g.gen_c_main_function_header()
+ if g.pref.gc_mode in [.boehm_full, .boehm_incr, .boehm_full_opt, .boehm_incr_opt, .boehm_leak] {
+ g.writeln('#if defined(_VGCBOEHM)')
+ if g.pref.gc_mode == .boehm_leak {
+ g.writeln('\tGC_set_find_leak(1);')
+ }
+ g.writeln('\tGC_INIT();')
+ if g.pref.gc_mode in [.boehm_incr, .boehm_incr_opt] {
+ g.writeln('\tGC_enable_incremental();')
+ }
+ g.writeln('#endif')
+ }
+ g.writeln('\t_vinit(___argc, (voidptr)___argv);')
+ all_tfuncs := g.get_all_test_function_names()
+ if g.pref.is_stats {
+ g.writeln('\tmain__BenchedTests bt = main__start_testing($all_tfuncs.len, _SLIT("$g.pref.path"));')
+ }
+ g.writeln('')
+ for tname in all_tfuncs {
+ tcname := util.no_dots(tname)
+ if g.pref.is_stats {
+ g.writeln('\tmain__BenchedTests_testing_step_start(&bt, _SLIT("$tcname"));')
+ }
+ g.writeln('\tif (!setjmp(g_jump_buffer)) ${tcname}();')
+ if g.pref.is_stats {
+ g.writeln('\tmain__BenchedTests_testing_step_end(&bt);')
+ }
+ }
+ g.writeln('')
+ if g.pref.is_stats {
+ g.writeln('\tmain__BenchedTests_end_testing(&bt);')
+ }
+ g.writeln('\t_vcleanup();')
+ g.writeln('\treturn g_test_fails > 0;')
+ g.writeln('}')
+ if g.pref.printfn_list.len > 0 && 'main' in g.pref.printfn_list {
+ println(g.out.after(main_fn_start_pos))
+ }
+}
diff --git a/v_windows/v/vlib/v/gen/c/comptime.v b/v_windows/v/vlib/v/gen/c/comptime.v
new file mode 100644
index 0000000..9689a8e
--- /dev/null
+++ b/v_windows/v/vlib/v/gen/c/comptime.v
@@ -0,0 +1,676 @@
+// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved.
+// Use of this source code is governed by an MIT license
+// that can be found in the LICENSE file.
+module c
+
+import os
+import v.ast
+import v.util
+import v.pref
+
+fn (mut g Gen) comptime_selector(node ast.ComptimeSelector) {
+ g.expr(node.left)
+ if node.left_type.is_ptr() {
+ g.write('->')
+ } else {
+ g.write('.')
+ }
+ // check for field.name
+ if node.field_expr is ast.SelectorExpr {
+ if node.field_expr.expr is ast.Ident {
+ if node.field_expr.expr.name == g.comp_for_field_var
+ && node.field_expr.field_name == 'name' {
+ g.write(c_name(g.comp_for_field_value.name))
+ return
+ }
+ }
+ }
+ g.expr(node.field_expr)
+}
+
+fn (mut g Gen) comptime_call(node ast.ComptimeCall) {
+ if node.is_embed {
+ // $embed_file('/path/to/file')
+ g.gen_embed_file_init(node)
+ return
+ }
+ if node.method_name == 'env' {
+ // $env('ENV_VAR_NAME')
+ val := util.cescaped_path(os.getenv(node.args_var))
+ g.write('_SLIT("$val")')
+ return
+ }
+ if node.is_vweb {
+ is_html := node.method_name == 'html'
+ for stmt in node.vweb_tmpl.stmts {
+ if stmt is ast.FnDecl {
+ // insert stmts from vweb_tmpl fn
+ if stmt.name.starts_with('main.vweb_tmpl') {
+ if is_html {
+ g.inside_vweb_tmpl = true
+ }
+ g.stmts(stmt.stmts)
+ g.inside_vweb_tmpl = false
+ break
+ }
+ }
+ }
+ if is_html {
+ // return vweb html template
+ g.writeln('vweb__Context_html(&app->Context, _tmpl_res_$g.fn_decl.name); strings__Builder_free(&sb); string_free(&_tmpl_res_$g.fn_decl.name);')
+ } else {
+ // return $tmpl string
+ fn_name := g.fn_decl.name.replace('.', '__')
+ g.writeln('return _tmpl_res_$fn_name;')
+ }
+ return
+ }
+ g.trace_autofree('// \$method call. sym="$node.sym.name"')
+ if node.method_name == 'method' {
+ // `app.$method()`
+ m := node.sym.find_method(g.comp_for_method) or { return }
+ /*
+ vals := m.attrs[0].split('/')
+ args := vals.filter(it.starts_with(':')).map(it[1..])
+ println(vals)
+ for val in vals {
+ }
+ */
+ expand_strs := if node.args.len > 0 && m.params.len - 1 >= node.args.len {
+ arg := node.args[node.args.len - 1]
+ param := m.params[node.args.len]
+
+ arg.expr is ast.Ident && g.table.type_to_str(arg.typ) == '[]string'
+ && g.table.type_to_str(param.typ) != '[]string'
+ } else {
+ false
+ }
+ // check argument length and types
+ if m.params.len - 1 != node.args.len && !expand_strs {
+ // do not generate anything if the argument lengths don't match
+ g.writeln('/* skipping ${node.sym.name}.$m.name due to mismatched arguments list */')
+ // verror('expected ${m.params.len-1} arguments to method ${node.sym.name}.$m.name, but got $node.args.len')
+ return
+ }
+ // TODO: check argument types
+ g.write('${util.no_dots(node.sym.name)}_${g.comp_for_method}(')
+
+ // try to see if we need to pass a pointer
+ if node.left is ast.Ident {
+ scope := g.file.scope.innermost(node.pos.pos)
+ if v := scope.find_var(node.left.name) {
+ if m.params[0].typ.is_ptr() && !v.typ.is_ptr() {
+ g.write('&')
+ }
+ }
+ }
+ g.expr(node.left)
+ if m.params.len > 1 {
+ g.write(', ')
+ }
+ for i in 1 .. m.params.len {
+ if node.left is ast.Ident {
+ if m.params[i].name == node.left.name {
+ continue
+ }
+ }
+ if i - 1 < node.args.len - 1 {
+ g.expr(node.args[i - 1].expr)
+ g.write(', ')
+ } else if !expand_strs && i == node.args.len {
+ g.expr(node.args[i - 1].expr)
+ break
+ } else {
+ // last argument; try to expand if it's []string
+ idx := i - node.args.len
+ if m.params[i].typ.is_int() || m.params[i].typ.idx() == ast.bool_type_idx {
+ // Gets the type name and cast the string to the type with the string_<type> function
+ type_name := g.table.type_symbols[int(m.params[i].typ)].str()
+ g.write('string_${type_name}(((string*)${node.args[node.args.len - 1]}.data) [$idx])')
+ } else {
+ g.write('((string*)${node.args[node.args.len - 1]}.data) [$idx] ')
+ }
+ if i < m.params.len - 1 {
+ g.write(', ')
+ }
+ }
+ }
+ g.write(' ); // vweb action call with args')
+ return
+ }
+ mut j := 0
+ for method in node.sym.methods {
+ // if method.return_type != ast.void_type {
+ if method.return_type != node.result_type {
+ continue
+ }
+ if method.params.len != 1 {
+ continue
+ }
+ // receiver := method.args[0]
+ // if !p.expr_var.ptr {
+ // p.error('`$p.expr_var.name` needs to be a reference')
+ // }
+ amp := '' // if receiver.is_mut && !p.expr_var.ptr { '&' } else { '' }
+ if node.is_vweb {
+ if j > 0 {
+ g.write(' else ')
+ }
+ g.write('if (string__eq($node.method_name, _SLIT("$method.name"))) ')
+ }
+ g.write('${util.no_dots(node.sym.name)}_${method.name}($amp ')
+ g.expr(node.left)
+ g.writeln(');')
+ j++
+ }
+}
+
+fn cgen_attrs(attrs []ast.Attr) []string {
+ mut res := []string{cap: attrs.len}
+ for attr in attrs {
+ // we currently don't quote 'arg' (otherwise we could just use `s := attr.str()`)
+ mut s := attr.name
+ if attr.arg.len > 0 {
+ s += ': $attr.arg'
+ }
+ res << '_SLIT("$s")'
+ }
+ return res
+}
+
+fn (mut g Gen) comp_at(node ast.AtExpr) {
+ if node.kind == .vmod_file {
+ val := cnewlines(node.val.replace('\r', ''))
+ g.write('_SLIT("$val")')
+ } else {
+ val := node.val.replace('\\', '\\\\')
+ g.write('_SLIT("$val")')
+ }
+}
+
+fn (mut g Gen) comp_if(node ast.IfExpr) {
+ if !node.is_expr && !node.has_else && node.branches.len == 1 {
+ if node.branches[0].stmts.len == 0 {
+ // empty ifdef; result of target OS != conditional => skip
+ return
+ }
+ if !g.pref.output_cross_c {
+ if node.branches[0].cond is ast.Ident {
+ if g.pref.os == (pref.os_from_string(node.branches[0].cond.name) or {
+ pref.OS._auto
+ }) {
+ // Same target OS as the conditional...
+ // => skip the #if defined ... #endif wrapper
+ // and just generate the branch statements:
+ g.indent--
+ g.stmts(node.branches[0].stmts)
+ g.indent++
+ return
+ }
+ }
+ }
+ }
+ line := if node.is_expr {
+ stmt_str := g.go_before_stmt(0)
+ g.write(util.tabs(g.indent))
+ stmt_str.trim_space()
+ } else {
+ ''
+ }
+ mut comp_if_stmts_skip := false // don't write any statements if the condition is false
+ // (so that for example windows calls don't get generated inside `$if macos` which
+ // will lead to compilation errors)
+
+ for i, branch in node.branches {
+ start_pos := g.out.len
+ if i == node.branches.len - 1 && node.has_else {
+ g.writeln('#else')
+ comp_if_stmts_skip = false
+ } else {
+ if i == 0 {
+ g.write('#if ')
+ } else {
+ g.write('#elif ')
+ }
+ comp_if_stmts_skip = !g.comp_if_cond(branch.cond, branch.pkg_exist)
+ g.writeln('')
+ }
+ expr_str := g.out.last_n(g.out.len - start_pos).trim_space()
+ g.defer_ifdef = expr_str
+ if node.is_expr {
+ len := branch.stmts.len
+ if len > 0 {
+ last := branch.stmts[len - 1] as ast.ExprStmt
+ if len > 1 {
+ tmp := g.new_tmp_var()
+ styp := g.typ(last.typ)
+ g.indent++
+ g.writeln('$styp $tmp;')
+ g.writeln('{')
+ g.stmts(branch.stmts[0..len - 1])
+ g.write('\t$tmp = ')
+ g.stmt(last)
+ g.writeln('}')
+ g.indent--
+ g.writeln('$line $tmp;')
+ } else {
+ g.write('$line ')
+ g.stmt(last)
+ }
+ }
+ } else {
+ // Only wrap the contents in {} if we're inside a function, not on the top level scope
+ should_create_scope := g.fn_decl != 0
+ if should_create_scope {
+ g.writeln('{')
+ }
+ if !comp_if_stmts_skip {
+ g.stmts(branch.stmts)
+ }
+ if should_create_scope {
+ g.writeln('}')
+ }
+ }
+ g.defer_ifdef = ''
+ }
+ g.writeln('#endif')
+}
+
+/*
+// returning `false` means the statements inside the $if can be skipped
+*/
+// returns the value of the bool comptime expression
+fn (mut g Gen) comp_if_cond(cond ast.Expr, pkg_exist bool) bool {
+ match cond {
+ ast.BoolLiteral {
+ g.expr(cond)
+ return true
+ }
+ ast.ParExpr {
+ g.write('(')
+ is_cond_true := g.comp_if_cond(cond.expr, pkg_exist)
+ g.write(')')
+ return is_cond_true
+ }
+ ast.PrefixExpr {
+ g.write(cond.op.str())
+ return g.comp_if_cond(cond.right, pkg_exist)
+ }
+ ast.PostfixExpr {
+ ifdef := g.comp_if_to_ifdef((cond.expr as ast.Ident).name, true) or {
+ verror(err.msg)
+ return false
+ }
+ g.write('defined($ifdef)')
+ return true
+ }
+ ast.InfixExpr {
+ match cond.op {
+ .and, .logical_or {
+ l := g.comp_if_cond(cond.left, pkg_exist)
+ g.write(' $cond.op ')
+ r := g.comp_if_cond(cond.right, pkg_exist)
+ return if cond.op == .and { l && r } else { l || r }
+ }
+ .key_is, .not_is {
+ left := cond.left
+ mut name := ''
+ mut exp_type := ast.Type(0)
+ got_type := (cond.right as ast.TypeNode).typ
+ // Handle `$if x is Interface {`
+ // mut matches_interface := 'false'
+ if left is ast.TypeNode && cond.right is ast.TypeNode
+ && g.table.get_type_symbol(got_type).kind == .interface_ {
+ // `$if Foo is Interface {`
+ interface_sym := g.table.get_type_symbol(got_type)
+ if interface_sym.info is ast.Interface {
+ // q := g.table.get_type_symbol(interface_sym.info.types[0])
+ checked_type := g.unwrap_generic(left.typ)
+ // TODO PERF this check is run twice (also in the checker)
+ // store the result in a field
+ is_true := g.table.does_type_implement_interface(checked_type,
+ got_type)
+ // true // exp_type in interface_sym.info.types
+ if cond.op == .key_is {
+ if is_true {
+ g.write('1')
+ } else {
+ g.write('0')
+ }
+ return is_true
+ } else if cond.op == .not_is {
+ if is_true {
+ g.write('0')
+ } else {
+ g.write('1')
+ }
+ return !is_true
+ }
+ // matches_interface = '/*iface:$got_type $exp_type*/ true'
+ //}
+ }
+ } else if left is ast.SelectorExpr {
+ name = '${left.expr}.$left.field_name'
+ exp_type = g.comptime_var_type_map[name]
+ } else if left is ast.TypeNode {
+ name = left.str()
+ // this is only allowed for generics currently, otherwise blocked by checker
+ exp_type = g.unwrap_generic(left.typ)
+ }
+
+ if cond.op == .key_is {
+ g.write('$exp_type == $got_type')
+ return exp_type == got_type
+ } else {
+ g.write('$exp_type != $got_type')
+ return exp_type != got_type
+ }
+ }
+ .eq, .ne {
+ // TODO Implement `$if method.args.len == 1`
+ g.write('1')
+ return true
+ }
+ else {
+ return true
+ }
+ }
+ }
+ ast.Ident {
+ ifdef := g.comp_if_to_ifdef(cond.name, false) or { 'true' } // handled in checker
+ g.write('defined($ifdef)')
+ return true
+ }
+ ast.ComptimeCall {
+ g.write('$pkg_exist')
+ return true
+ }
+ else {
+ // should be unreachable, but just in case
+ g.write('1')
+ return true
+ }
+ }
+}
+
+fn (mut g Gen) comp_for(node ast.CompFor) {
+ sym := g.table.get_type_symbol(g.unwrap_generic(node.typ))
+ g.writeln('/* \$for $node.val_var in ${sym.name}($node.kind.str()) */ {')
+ g.indent++
+ // vweb_result_type := ast.new_type(g.table.find_type_idx('vweb.Result'))
+ mut i := 0
+ // g.writeln('string method = _SLIT("");')
+ if node.kind == .methods {
+ mut methods := sym.methods.filter(it.attrs.len == 0) // methods without attrs first
+ methods_with_attrs := sym.methods.filter(it.attrs.len > 0) // methods with attrs second
+ methods << methods_with_attrs
+ if methods.len > 0 {
+ g.writeln('FunctionData $node.val_var = {0};')
+ }
+ for method in methods { // sym.methods {
+ /*
+ if method.return_type != vweb_result_type { // ast.void_type {
+ continue
+ }
+ */
+ g.comp_for_method = method.name
+ g.writeln('/* method $i */ {')
+ g.writeln('\t${node.val_var}.name = _SLIT("$method.name");')
+ if method.attrs.len == 0 {
+ g.writeln('\t${node.val_var}.attrs = __new_array_with_default(0, 0, sizeof(string), 0);')
+ } else {
+ attrs := cgen_attrs(method.attrs)
+ g.writeln(
+ '\t${node.val_var}.attrs = new_array_from_c_array($attrs.len, $attrs.len, sizeof(string), _MOV((string[$attrs.len]){' +
+ attrs.join(', ') + '}));\n')
+ }
+ if method.params.len < 2 {
+ // 0 or 1 (the receiver) args
+ g.writeln('\t${node.val_var}.args = __new_array_with_default(0, 0, sizeof(MethodArgs), 0);')
+ } else {
+ len := method.params.len - 1
+ g.write('\t${node.val_var}.args = new_array_from_c_array($len, $len, sizeof(MethodArgs), _MOV((MethodArgs[$len]){')
+ // Skip receiver arg
+ for j, arg in method.params[1..] {
+ typ := arg.typ.idx()
+ g.write('{$typ.str(), _SLIT("$arg.name")}')
+ if j < len - 1 {
+ g.write(', ')
+ }
+ g.comptime_var_type_map['${node.val_var}.args[$j].typ'] = typ
+ }
+ g.writeln('}));\n')
+ }
+ mut sig := 'anon_fn_'
+ // skip the first (receiver) arg
+ for j, arg in method.params[1..] {
+ // TODO: ignore mut/pts in sig for now
+ typ := arg.typ.set_nr_muls(0)
+ sig += '$typ'
+ if j < method.params.len - 2 {
+ sig += '_'
+ }
+ }
+ sig += '_$method.return_type'
+ styp := g.table.find_type_idx(sig)
+ // println(styp)
+ // if styp == 0 { }
+ // TODO: type aliases
+ ret_typ := method.return_type.idx()
+ g.writeln('\t${node.val_var}.typ = $styp;')
+ g.writeln('\t${node.val_var}.return_type = $ret_typ;')
+ //
+ g.comptime_var_type_map['${node.val_var}.return_type'] = ret_typ
+ g.comptime_var_type_map['${node.val_var}.typ'] = styp
+ g.stmts(node.stmts)
+ i++
+ g.writeln('}')
+ //
+ mut delete_keys := []string{}
+ for key, _ in g.comptime_var_type_map {
+ if key.starts_with(node.val_var) {
+ delete_keys << key
+ }
+ }
+ for key in delete_keys {
+ g.comptime_var_type_map.delete(key)
+ }
+ }
+ } else if node.kind == .fields {
+ // TODO add fields
+ if sym.info is ast.Struct {
+ mut fields := sym.info.fields.filter(it.attrs.len == 0)
+ fields_with_attrs := sym.info.fields.filter(it.attrs.len > 0)
+ fields << fields_with_attrs
+ if fields.len > 0 {
+ g.writeln('\tFieldData $node.val_var = {0};')
+ }
+ for field in fields {
+ g.comp_for_field_var = node.val_var
+ g.comp_for_field_value = field
+ g.writeln('/* field $i */ {')
+ g.writeln('\t${node.val_var}.name = _SLIT("$field.name");')
+ if field.attrs.len == 0 {
+ g.writeln('\t${node.val_var}.attrs = __new_array_with_default(0, 0, sizeof(string), 0);')
+ } else {
+ attrs := cgen_attrs(field.attrs)
+ g.writeln(
+ '\t${node.val_var}.attrs = new_array_from_c_array($attrs.len, $attrs.len, sizeof(string), _MOV((string[$attrs.len]){' +
+ attrs.join(', ') + '}));\n')
+ }
+ // field_sym := g.table.get_type_symbol(field.typ)
+ // g.writeln('\t${node.val_var}.typ = _SLIT("$field_sym.name");')
+ styp := field.typ
+ g.writeln('\t${node.val_var}.typ = $styp;')
+ g.writeln('\t${node.val_var}.is_pub = $field.is_pub;')
+ g.writeln('\t${node.val_var}.is_mut = $field.is_mut;')
+ g.writeln('\t${node.val_var}.is_shared = ${field.typ.has_flag(.shared_f)};')
+ g.comptime_var_type_map['${node.val_var}.typ'] = styp
+ g.stmts(node.stmts)
+ i++
+ g.writeln('}')
+ }
+ g.comptime_var_type_map.delete(node.val_var)
+ }
+ } else if node.kind == .attributes {
+ if sym.info is ast.Struct {
+ if sym.info.attrs.len > 0 {
+ g.writeln('\tStructAttribute $node.val_var = {0};')
+ }
+ for attr in sym.info.attrs {
+ g.writeln('/* attribute $i */ {')
+ g.writeln('\t${node.val_var}.name = _SLIT("$attr.name");')
+ g.writeln('\t${node.val_var}.has_arg = $attr.has_arg;')
+ g.writeln('\t${node.val_var}.arg = _SLIT("$attr.arg");')
+ g.writeln('\t${node.val_var}.kind = AttributeKind__$attr.kind;')
+ g.stmts(node.stmts)
+ g.writeln('}')
+ }
+ }
+ }
+ g.indent--
+ g.writeln('}// \$for')
+}
+
+fn (mut g Gen) comp_if_to_ifdef(name string, is_comptime_optional bool) ?string {
+ match name {
+ // platforms/os-es:
+ 'windows' {
+ return '_WIN32'
+ }
+ 'ios' {
+ return '__TARGET_IOS__'
+ }
+ 'macos' {
+ return '__APPLE__'
+ }
+ 'mach' {
+ return '__MACH__'
+ }
+ 'darwin' {
+ return '__DARWIN__'
+ }
+ 'hpux' {
+ return '__HPUX__'
+ }
+ 'gnu' {
+ return '__GNU__'
+ }
+ 'qnx' {
+ return '__QNX__'
+ }
+ 'linux' {
+ return '__linux__'
+ }
+ 'serenity' {
+ return '__serenity__'
+ }
+ 'vinix' {
+ return '__vinix__'
+ }
+ 'freebsd' {
+ return '__FreeBSD__'
+ }
+ 'openbsd' {
+ return '__OpenBSD__'
+ }
+ 'netbsd' {
+ return '__NetBSD__'
+ }
+ 'bsd' {
+ return '__BSD__'
+ }
+ 'dragonfly' {
+ return '__DragonFly__'
+ }
+ 'android' {
+ return '__ANDROID__'
+ }
+ 'solaris' {
+ return '__sun'
+ }
+ 'haiku' {
+ return '__HAIKU__'
+ }
+ //
+ 'js' {
+ return '_VJS'
+ }
+ // compilers:
+ 'gcc' {
+ return '__V_GCC__'
+ }
+ 'tinyc' {
+ return '__TINYC__'
+ }
+ 'clang' {
+ return '__clang__'
+ }
+ 'mingw' {
+ return '__MINGW32__'
+ }
+ 'msvc' {
+ return '_MSC_VER'
+ }
+ 'cplusplus' {
+ return '__cplusplus'
+ }
+ // other:
+ 'threads' {
+ return '__VTHREADS__'
+ }
+ 'gcboehm' {
+ return '_VGCBOEHM'
+ }
+ 'debug' {
+ return '_VDEBUG'
+ }
+ 'prod' {
+ return '_VPROD'
+ }
+ 'test' {
+ return '_VTEST'
+ }
+ 'glibc' {
+ return '__GLIBC__'
+ }
+ 'prealloc' {
+ return '_VPREALLOC'
+ }
+ 'no_bounds_checking' {
+ return 'CUSTOM_DEFINE_no_bounds_checking'
+ }
+ 'freestanding' {
+ return '_VFREESTANDING'
+ }
+ // architectures:
+ 'amd64' {
+ return '__V_amd64'
+ }
+ 'aarch64', 'arm64' {
+ return '__V_arm64'
+ }
+ // bitness:
+ 'x64' {
+ return 'TARGET_IS_64BIT'
+ }
+ 'x32' {
+ return 'TARGET_IS_32BIT'
+ }
+ // endianness:
+ 'little_endian' {
+ return 'TARGET_ORDER_IS_LITTLE'
+ }
+ 'big_endian' {
+ return 'TARGET_ORDER_IS_BIG'
+ }
+ else {
+ if is_comptime_optional
+ || (g.pref.compile_defines_all.len > 0 && name in g.pref.compile_defines_all) {
+ return 'CUSTOM_DEFINE_$name'
+ }
+ return error('bad os ifdef name "$name"') // should never happen, caught in the checker
+ }
+ }
+ return none
+}
diff --git a/v_windows/v/vlib/v/gen/c/coutput_test.v b/v_windows/v/vlib/v/gen/c/coutput_test.v
new file mode 100644
index 0000000..b8e2729
--- /dev/null
+++ b/v_windows/v/vlib/v/gen/c/coutput_test.v
@@ -0,0 +1,174 @@
+import os
+import rand
+import term
+import v.util.diff
+import v.util.vtest
+
+const vexe = @VEXE
+
+const vroot = os.real_path(@VMODROOT)
+
+const testdata_folder = os.join_path(vroot, 'vlib', 'v', 'gen', 'c', 'testdata')
+
+const diff_cmd = diff.find_working_diff_command() or { '' }
+
+fn test_out_files() ? {
+ println(term.colorize(term.green, '> testing whether .out files match:'))
+ os.chdir(vroot) or {}
+ output_path := os.join_path(os.temp_dir(), 'coutput', 'out')
+ os.mkdir_all(output_path) ?
+ defer {
+ os.rmdir_all(output_path) or {}
+ }
+ files := os.ls(testdata_folder) or { [] }
+ tests := files.filter(it.ends_with('.out'))
+ if tests.len == 0 {
+ eprintln('no `.out` tests found in $testdata_folder')
+ return
+ }
+ paths := vtest.filter_vtest_only(tests, basepath: testdata_folder)
+ mut total_errors := 0
+ for out_path in paths {
+ basename, path, relpath, out_relpath := target2paths(out_path, '.out')
+ print(term.colorize(term.magenta, 'v run $relpath') + ' == ' +
+ term.colorize(term.magenta, out_relpath) + ' ')
+ pexe := os.join_path(output_path, '${basename}.exe')
+ compilation := os.execute('$vexe -o $pexe $path')
+ ensure_compilation_succeeded(compilation)
+ res := os.execute(pexe)
+ if res.exit_code < 0 {
+ println('nope')
+ panic(res.output)
+ }
+ mut found := res.output.trim_right('\r\n').replace('\r\n', '\n')
+ mut expected := os.read_file(out_path) ?
+ expected = expected.trim_right('\r\n').replace('\r\n', '\n')
+ if expected.contains('================ V panic ================') {
+ // panic include backtraces and absolute file paths, so can't do char by char comparison
+ n_found := normalize_panic_message(found, vroot)
+ n_expected := normalize_panic_message(expected, vroot)
+ if found.contains('================ V panic ================') {
+ if n_found.starts_with(n_expected) {
+ println(term.green('OK (panic)'))
+ continue
+ } else {
+ // Both have panics, but there was a difference...
+ // Pass the normalized strings for further reporting.
+ // There is no point in comparing the backtraces too.
+ found = n_found
+ expected = n_expected
+ }
+ }
+ }
+ if expected != found {
+ println(term.red('FAIL'))
+ println(term.header('expected:', '-'))
+ println(expected)
+ println(term.header('found:', '-'))
+ println(found)
+ if diff_cmd != '' {
+ println(term.header('difference:', '-'))
+ println(diff.color_compare_strings(diff_cmd, rand.ulid(), expected, found))
+ } else {
+ println(term.h_divider('-'))
+ }
+ total_errors++
+ } else {
+ println(term.green('OK'))
+ }
+ }
+ assert total_errors == 0
+}
+
+fn test_c_must_have_files() ? {
+ println(term.colorize(term.green, '> testing whether `.c.must_have` files match:'))
+ os.chdir(vroot) or {}
+ output_path := os.join_path(os.temp_dir(), 'coutput', 'c_must_have')
+ os.mkdir_all(output_path) ?
+ defer {
+ os.rmdir_all(output_path) or {}
+ }
+ files := os.ls(testdata_folder) or { [] }
+ tests := files.filter(it.ends_with('.c.must_have'))
+ if tests.len == 0 {
+ eprintln('no `.c.must_have` files found in $testdata_folder')
+ return
+ }
+ paths := vtest.filter_vtest_only(tests, basepath: testdata_folder)
+ mut total_errors := 0
+ for must_have_path in paths {
+ basename, path, relpath, must_have_relpath := target2paths(must_have_path, '.c.must_have')
+ print(term.colorize(term.magenta, 'v -o - $relpath') + ' matches all line paterns in ' +
+ term.colorize(term.magenta, must_have_relpath) + ' ')
+ compilation := os.execute('$vexe -o - $path')
+ ensure_compilation_succeeded(compilation)
+ expected_lines := os.read_lines(must_have_path) or { [] }
+ generated_c_lines := compilation.output.split_into_lines()
+ mut nmatches := 0
+ for idx_expected_line, eline in expected_lines {
+ if does_line_match_one_of_generated_lines(eline, generated_c_lines) {
+ nmatches++
+ // eprintln('> testing: $must_have_path has line: $eline')
+ } else {
+ println(term.red('FAIL'))
+ eprintln('$must_have_path:${idx_expected_line + 1}: expected match error:')
+ eprintln('`$vexe -o - $path` does NOT produce expected line:')
+ eprintln(term.colorize(term.red, eline))
+ total_errors++
+ continue
+ }
+ }
+ if nmatches == expected_lines.len {
+ println(term.green('OK'))
+ } else {
+ eprintln('> ALL lines:')
+ eprintln(compilation.output)
+ }
+ }
+ assert total_errors == 0
+}
+
+fn does_line_match_one_of_generated_lines(line string, generated_c_lines []string) bool {
+ for cline in generated_c_lines {
+ if line == cline {
+ return true
+ }
+ if cline.contains(line) {
+ return true
+ }
+ }
+ return false
+}
+
+fn normalize_panic_message(message string, vroot string) string {
+ mut msg := message.all_before('=========================================')
+ // change windows to nix path
+ s := vroot.replace(os.path_separator, '/')
+ msg = msg.replace(s + '/', '')
+ msg = msg.trim_space()
+ return msg
+}
+
+fn vroot_relative(opath string) string {
+ nvroot := vroot.replace(os.path_separator, '/') + '/'
+ npath := opath.replace(os.path_separator, '/')
+ return npath.replace(nvroot, '')
+}
+
+fn ensure_compilation_succeeded(compilation os.Result) {
+ if compilation.exit_code < 0 {
+ panic(compilation.output)
+ }
+ if compilation.exit_code != 0 {
+ panic('compilation failed: $compilation.output')
+ }
+}
+
+fn target2paths(target_path string, postfix string) (string, string, string, string) {
+ basename := os.file_name(target_path).replace(postfix, '')
+ target_dir := os.dir(target_path)
+ path := os.join_path(target_dir, '${basename}.vv')
+ relpath := vroot_relative(path)
+ target_relpath := vroot_relative(target_path)
+ return basename, path, relpath, target_relpath
+}
diff --git a/v_windows/v/vlib/v/gen/c/ctempvars.v b/v_windows/v/vlib/v/gen/c/ctempvars.v
new file mode 100644
index 0000000..f0484ed
--- /dev/null
+++ b/v_windows/v/vlib/v/gen/c/ctempvars.v
@@ -0,0 +1,25 @@
+module c
+
+import v.ast
+
+fn (mut g Gen) new_ctemp_var(expr ast.Expr, expr_type ast.Type) ast.CTempVar {
+ return ast.CTempVar{
+ name: g.new_tmp_var()
+ typ: expr_type
+ is_ptr: expr_type.is_ptr()
+ orig: expr
+ }
+}
+
+fn (mut g Gen) new_ctemp_var_then_gen(expr ast.Expr, expr_type ast.Type) ast.CTempVar {
+ x := g.new_ctemp_var(expr, expr_type)
+ g.gen_ctemp_var(x)
+ return x
+}
+
+fn (mut g Gen) gen_ctemp_var(tvar ast.CTempVar) {
+ styp := g.typ(tvar.typ)
+ g.write('$styp $tvar.name = ')
+ g.expr(tvar.orig)
+ g.writeln(';')
+}
diff --git a/v_windows/v/vlib/v/gen/c/dumpexpr.v b/v_windows/v/vlib/v/gen/c/dumpexpr.v
new file mode 100644
index 0000000..fcb6bbd
--- /dev/null
+++ b/v_windows/v/vlib/v/gen/c/dumpexpr.v
@@ -0,0 +1,72 @@
+module c
+
+import v.ast
+import strings
+
+fn (mut g Gen) dump_expr(node ast.DumpExpr) {
+ sexpr := ctoslit(node.expr.str())
+ fpath := cestring(g.file.path)
+ line := node.pos.line_nr + 1
+ if 'nop_dump' in g.pref.compile_defines {
+ g.expr(node.expr)
+ return
+ }
+ dump_fn_name := '_v_dump_expr_$node.cname' + (if node.expr_type.is_ptr() { '_ptr' } else { '' })
+ g.write(' ${dump_fn_name}(${ctoslit(fpath)}, $line, $sexpr, ')
+ g.expr(node.expr)
+ g.write(' )')
+}
+
+fn (mut g Gen) dump_expr_definitions() {
+ mut dump_typedefs := map[string]bool{}
+ mut dump_fns := strings.new_builder(100)
+ for dump_type, cname in g.table.dumps {
+ to_string_fn_name := g.gen_str_for_type(dump_type)
+ is_ptr := ast.Type(dump_type).is_ptr()
+ ptr_asterisk := if is_ptr { '*' } else { '' }
+ dump_sym := g.table.get_type_symbol(dump_type)
+ mut str_dumparg_type := '$cname$ptr_asterisk'
+ if dump_sym.kind == .function {
+ fninfo := dump_sym.info as ast.FnType
+ str_dumparg_type = 'DumpFNType_$cname'
+ tdef_pos := g.out.len
+ g.write_fn_ptr_decl(&fninfo, str_dumparg_type)
+ str_tdef := g.out.after(tdef_pos)
+ g.out.go_back(str_tdef.len)
+ dump_typedefs['typedef $str_tdef;'] = true
+ }
+ dump_fn_name := '_v_dump_expr_$cname' + (if is_ptr { '_ptr' } else { '' })
+ if g.writeln_fn_header('$str_dumparg_type ${dump_fn_name}(string fpath, int line, string sexpr, $str_dumparg_type x)', mut
+ dump_fns)
+ {
+ continue
+ }
+ dump_fns.writeln('\teprint(${ctoslit('[')});')
+ dump_fns.writeln('\teprint(fpath);')
+ dump_fns.writeln('\teprint(${ctoslit(':')});')
+ dump_fns.writeln('\teprint(int_str(line));')
+ dump_fns.writeln('\teprint(${ctoslit('] ')});')
+ // dump_fns.writeln('\t/* dump_type: $dump_type | to_string_fn_name: $to_string_fn_name | is_ptr: $is_ptr | ptr_asterisk: $ptr_asterisk | dump_fn_name: $dump_fn_name | cnam: $cname */')
+ dump_fns.writeln('\teprint(sexpr);')
+ dump_fns.writeln('\teprint(${ctoslit(': ')});')
+ if is_ptr {
+ dump_fns.writeln('\teprint(${ctoslit('&')});')
+ }
+ dump_fns.writeln('\teprintln(${to_string_fn_name}(${ptr_asterisk}x));')
+ dump_fns.writeln('\treturn x;')
+ dump_fns.writeln('}')
+ }
+ for tdef, _ in dump_typedefs {
+ g.definitions.writeln(tdef)
+ }
+ g.definitions.writeln(dump_fns.str())
+}
+
+fn (mut g Gen) writeln_fn_header(s string, mut sb strings.Builder) bool {
+ if g.pref.build_mode == .build_module {
+ sb.writeln('$s;')
+ return true
+ }
+ sb.writeln('$s {')
+ return false
+}
diff --git a/v_windows/v/vlib/v/gen/c/embed.v b/v_windows/v/vlib/v/gen/c/embed.v
new file mode 100644
index 0000000..4ee02eb
--- /dev/null
+++ b/v_windows/v/vlib/v/gen/c/embed.v
@@ -0,0 +1,69 @@
+module c
+
+import os
+import v.ast
+
+fn (mut g Gen) embed_file_is_prod_mode() bool {
+ if g.pref.is_prod || 'debug_embed_file_in_prod' in g.pref.compile_defines {
+ return true
+ }
+ return false
+}
+
+// gen_embed_file_struct generates C code for `$embed_file('...')` calls.
+fn (mut g Gen) gen_embed_file_init(node ast.ComptimeCall) {
+ g.writeln('(v__embed_file__EmbedFileData){')
+ g.writeln('\t\t.path = ${ctoslit(node.embed_file.rpath)},')
+ g.writeln('\t\t.apath = ${ctoslit(node.embed_file.apath)},')
+ file_size := os.file_size(node.embed_file.apath)
+ if file_size > 5242880 {
+ eprintln('Warning: embedding of files >= ~5MB is currently not supported')
+ }
+ if g.embed_file_is_prod_mode() {
+ // Use function generated in Gen.gen_embedded_data()
+ g.writeln('\t\t.compressed = v__embed_file__find_index_entry_by_path((voidptr)_v_embed_file_index, ${ctoslit(node.embed_file.rpath)})->data,')
+ }
+ g.writeln('\t\t.uncompressed = NULL,')
+ g.writeln('\t\t.free_compressed = 0,')
+ g.writeln('\t\t.free_uncompressed = 0,')
+ g.writeln('\t\t.len = $file_size')
+ g.writeln('} // \$embed_file("$node.embed_file.apath")')
+}
+
+// gen_embedded_data embeds data into the V target executable.
+fn (mut g Gen) gen_embedded_data() {
+ /*
+ TODO implement compression.
+ See also the vlib/embed module where decompression should occur.
+ */
+ /*
+ TODO implement support for large files - right now the setup has problems
+ // with even just 10 - 50 MB files - the problem is both in V and C compilers.
+ // maybe we need to write to separate files or have an external tool for large files
+ // like the `rcc` tool in Qt?
+ */
+ for i, emfile in g.embedded_files {
+ fbytes := os.read_bytes(emfile.apath) or { panic('Error while embedding file: $err') }
+ g.embedded_data.write_string('static const unsigned char _v_embed_blob_$i[$fbytes.len] = {\n ')
+ for j := 0; j < fbytes.len; j++ {
+ b := fbytes[j].hex()
+ if j < fbytes.len - 1 {
+ g.embedded_data.write_string('0x$b,')
+ } else {
+ g.embedded_data.write_string('0x$b')
+ }
+ if 0 == ((j + 1) % 16) {
+ g.embedded_data.write_string('\n ')
+ }
+ }
+ g.embedded_data.writeln('\n};')
+ }
+ g.embedded_data.writeln('')
+ g.embedded_data.writeln('const v__embed_file__EmbedFileIndexEntry _v_embed_file_index[] = {')
+ for i, emfile in g.embedded_files {
+ g.embedded_data.writeln('\t{$i, { .str=(byteptr)("${cestring(emfile.rpath)}"), .len=${emfile.rpath.len - 1}, .is_lit=1 }, _v_embed_blob_$i},')
+ }
+ g.embedded_data.writeln('\t{-1, { .str=(byteptr)(""), .len=0, .is_lit=1 }, NULL}')
+ g.embedded_data.writeln('};')
+ // see vlib/v/embed_file/embed_file.v, find_index_entry_by_id/2 and find_index_entry_by_path/2
+}
diff --git a/v_windows/v/vlib/v/gen/c/fn.v b/v_windows/v/vlib/v/gen/c/fn.v
new file mode 100644
index 0000000..7838221
--- /dev/null
+++ b/v_windows/v/vlib/v/gen/c/fn.v
@@ -0,0 +1,1558 @@
+// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved.
+// Use of this source code is governed by an MIT license
+// that can be found in the LICENSE file.
+module c
+
+import strings
+import v.ast
+import v.util
+
+fn (mut g Gen) is_used_by_main(node ast.FnDecl) bool {
+ mut is_used_by_main := true
+ if g.pref.skip_unused {
+ fkey := node.fkey()
+ is_used_by_main = g.table.used_fns[fkey]
+ $if trace_skip_unused_fns ? {
+ println('> is_used_by_main: $is_used_by_main | node.name: $node.name | fkey: $fkey | node.is_method: $node.is_method')
+ }
+ if !is_used_by_main {
+ $if trace_skip_unused_fns_in_c_code ? {
+ g.writeln('// trace_skip_unused_fns_in_c_code, $node.name, fkey: $fkey')
+ }
+ }
+ } else {
+ $if trace_skip_unused_fns_in_c_code ? {
+ g.writeln('// trace_skip_unused_fns_in_c_code, $node.name, fkey: $node.fkey()')
+ }
+ }
+ return is_used_by_main
+}
+
+fn (mut g Gen) process_fn_decl(node ast.FnDecl) {
+ if !g.is_used_by_main(node) {
+ return
+ }
+ if node.should_be_skipped {
+ return
+ }
+ if g.is_builtin_mod && g.pref.gc_mode == .boehm_leak && node.name == 'malloc' {
+ g.definitions.write_string('#define _v_malloc GC_MALLOC\n')
+ return
+ }
+ g.gen_attrs(node.attrs)
+ mut skip := false
+ pos := g.out.len
+ should_bundle_module := util.should_bundle_module(node.mod)
+ if g.pref.build_mode == .build_module {
+ // if node.name.contains('parse_text') {
+ // println('!!! $node.name mod=$node.mod, built=$g.module_built')
+ // }
+ // TODO true for not just "builtin"
+ // TODO: clean this up
+ mod := if g.is_builtin_mod { 'builtin' } else { node.name.all_before_last('.') }
+ if (mod != g.module_built && node.mod != g.module_built.after('/')) || should_bundle_module {
+ // Skip functions that don't have to be generated for this module.
+ // println('skip bm $node.name mod=$node.mod module_built=$g.module_built')
+ skip = true
+ }
+ if g.is_builtin_mod && g.module_built == 'builtin' && node.mod == 'builtin' {
+ skip = false
+ }
+ if !skip && g.pref.is_verbose {
+ println('build module `$g.module_built` fn `$node.name`')
+ }
+ }
+ if g.pref.use_cache {
+ // We are using prebuilt modules, we do not need to generate
+ // their functions in main.c.
+ if node.mod != 'main' && node.mod != 'help' && !should_bundle_module && !g.pref.is_test
+ && node.generic_names.len == 0 {
+ skip = true
+ }
+ }
+ keep_fn_decl := g.fn_decl
+ unsafe {
+ g.fn_decl = &node
+ }
+ if node.is_main {
+ g.has_main = true
+ }
+ if node.name == 'backtrace' || node.name == 'backtrace_symbols'
+ || node.name == 'backtrace_symbols_fd' {
+ g.write('\n#ifndef __cplusplus\n')
+ }
+ g.gen_fn_decl(node, skip)
+ if node.name == 'backtrace' || node.name == 'backtrace_symbols'
+ || node.name == 'backtrace_symbols_fd' {
+ g.write('\n#endif\n')
+ }
+ g.fn_decl = keep_fn_decl
+ if skip {
+ g.out.go_back_to(pos)
+ }
+ if !g.pref.skip_unused {
+ if node.language != .c {
+ g.writeln('')
+ }
+ }
+}
+
+fn (mut g Gen) gen_fn_decl(node &ast.FnDecl, skip bool) {
+ // TODO For some reason, build fails with autofree with this line
+ // as it's only informative, comment it for now
+ // g.gen_attrs(it.attrs)
+ if node.language == .c {
+ // || node.no_body {
+ return
+ }
+
+ tmp_defer_vars := g.defer_vars // must be here because of workflow
+ if !g.anon_fn {
+ g.defer_vars = []string{}
+ } else {
+ if node.defer_stmts.len > 0 {
+ g.defer_vars = []string{}
+ defer {
+ g.defer_vars = tmp_defer_vars
+ }
+ }
+ }
+ // Skip [if xxx] if xxx is not defined
+ /*
+ for attr in node.attrs {
+ if !attr.is_comptime_define {
+ continue
+ }
+ if attr.name !in g.pref.compile_defines_all {
+ // println('skipping [if]')
+ return
+ }
+ }
+ */
+
+ g.returned_var_name = ''
+ //
+ old_g_autofree := g.is_autofree
+ if node.is_manualfree {
+ g.is_autofree = false
+ }
+ defer {
+ g.is_autofree = old_g_autofree
+ }
+ //
+ // if g.fileis('vweb.v') {
+ // println('\ngen_fn_decl() $node.name $node.is_generic $g.cur_generic_type')
+ // }
+ if node.generic_names.len > 0 && g.table.cur_concrete_types.len == 0 { // need the cur_concrete_type check to avoid inf. recursion
+ // loop thru each generic type and generate a function
+ for concrete_types in g.table.fn_generic_types[node.name] {
+ if g.pref.is_verbose {
+ syms := concrete_types.map(g.table.get_type_symbol(it))
+ the_type := syms.map(it.name).join(', ')
+ println('gen fn `$node.name` for type `$the_type`')
+ }
+ g.table.cur_concrete_types = concrete_types
+ g.gen_fn_decl(node, skip)
+ }
+ g.table.cur_concrete_types = []
+ return
+ }
+ cur_fn_save := g.table.cur_fn
+ defer {
+ g.table.cur_fn = cur_fn_save
+ }
+ unsafe {
+ // TODO remove unsafe
+ g.table.cur_fn = node
+ }
+ fn_start_pos := g.out.len
+ is_closure := node.scope.has_inherited_vars()
+ mut cur_closure_ctx := ''
+ if is_closure {
+ cur_closure_ctx = closure_ctx_struct(node)
+ // declare the struct before its implementation
+ g.definitions.write_string(cur_closure_ctx)
+ g.definitions.writeln(';')
+ }
+
+ g.write_v_source_line_info(node.pos)
+ msvc_attrs := g.write_fn_attrs(node.attrs)
+ // Live
+ is_livefn := node.attrs.contains('live')
+ is_livemain := g.pref.is_livemain && is_livefn
+ is_liveshared := g.pref.is_liveshared && is_livefn
+ is_livemode := g.pref.is_livemain || g.pref.is_liveshared
+ is_live_wrap := is_livefn && is_livemode
+ if is_livefn && !is_livemode {
+ eprintln('INFO: compile with `v -live $g.pref.path `, if you want to use the [live] function $node.name .')
+ }
+ //
+ mut name := node.name
+ if name in ['+', '-', '*', '/', '%', '<', '=='] {
+ name = util.replace_op(name)
+ }
+ if node.is_method {
+ unwrapped_rec_sym := g.table.get_type_symbol(g.unwrap_generic(node.receiver.typ))
+ if unwrapped_rec_sym.kind == .placeholder {
+ return
+ }
+ name = g.cc_type(node.receiver.typ, false) + '_' + name
+ // name = g.table.get_type_symbol(node.receiver.typ).name + '_' + name
+ }
+ if node.language == .c {
+ name = util.no_dots(name)
+ } else {
+ name = c_name(name)
+ }
+ mut type_name := g.typ(node.return_type)
+
+ name = g.generic_fn_name(g.table.cur_concrete_types, name, true)
+
+ if g.pref.obfuscate && g.cur_mod.name == 'main' && name.starts_with('main__') && !node.is_main
+ && node.name != 'str' {
+ mut key := node.name
+ if node.is_method {
+ sym := g.table.get_type_symbol(node.receiver.typ)
+ key = sym.name + '.' + node.name
+ }
+ g.writeln('/* obf: $key */')
+ name = g.obf_table[key] or {
+ panic('cgen: fn_decl: obf name "$key" not found, this should never happen')
+ }
+ }
+ // if g.pref.show_cc && it.is_builtin {
+ // println(name)
+ // }
+ // type_name := g.ast.Type_to_str(it.return_type)
+ // Live functions are protected by a mutex, because otherwise they
+ // can be changed by the live reload thread, *while* they are
+ // running, with unpredictable results (usually just crashing).
+ // For this purpose, the actual body of the live function,
+ // is put under a non publicly accessible function, that is prefixed
+ // with 'impl_live_' .
+ if is_livemain {
+ g.hotcode_fn_names << name
+ }
+ mut impl_fn_name := name
+ if is_live_wrap {
+ impl_fn_name = 'impl_live_$name'
+ }
+ last_fn_c_name_save := g.last_fn_c_name
+ defer {
+ g.last_fn_c_name = last_fn_c_name_save
+ }
+ g.last_fn_c_name = impl_fn_name
+ //
+ if is_live_wrap {
+ if is_livemain {
+ g.definitions.write_string('$type_name (* $impl_fn_name)(')
+ g.write('$type_name no_impl_${name}(')
+ }
+ if is_liveshared {
+ g.definitions.write_string('$type_name ${impl_fn_name}(')
+ g.write('$type_name ${impl_fn_name}(')
+ }
+ } else {
+ if !(node.is_pub || g.pref.is_debug) {
+ // Private functions need to marked as static so that they are not exportable in the
+ // binaries
+ if g.pref.build_mode != .build_module && !g.pref.use_cache {
+ // if !(g.pref.build_mode == .build_module && g.is_builtin_mod) {
+ // If we are building vlib/builtin, we need all private functions like array_get
+ // to be public, so that all V programs can access them.
+ g.write('VV_LOCAL_SYMBOL ')
+ g.definitions.write_string('VV_LOCAL_SYMBOL ')
+ }
+ }
+ fn_header := if msvc_attrs.len > 0 {
+ '$type_name $msvc_attrs ${name}('
+ } else {
+ '$type_name ${name}('
+ }
+ g.definitions.write_string(fn_header)
+ g.write(fn_header)
+ }
+ arg_start_pos := g.out.len
+ fargs, fargtypes, heap_promoted := g.fn_args(node.params, node.scope)
+ if is_closure {
+ mut s := '$cur_closure_ctx *$c.closure_ctx'
+ if node.params.len > 0 {
+ s = ', ' + s
+ } else {
+ // remove generated `void`
+ g.out.cut_to(arg_start_pos)
+ }
+ g.definitions.write_string(s)
+ g.write(s)
+ g.nr_closures++
+ }
+ arg_str := g.out.after(arg_start_pos)
+ if node.no_body || ((g.pref.use_cache && g.pref.build_mode != .build_module) && node.is_builtin
+ && !g.is_test) || skip {
+ // Just a function header. Builtin function bodies are defined in builtin.o
+ g.definitions.writeln(');') // // NO BODY')
+ g.writeln(');')
+ return
+ }
+ g.definitions.writeln(');')
+ g.writeln(') {')
+ for i, is_promoted in heap_promoted {
+ if is_promoted {
+ g.writeln('${fargtypes[i]}* ${fargs[i]} = HEAP(${fargtypes[i]}, _v_toheap_${fargs[i]});')
+ }
+ }
+ for defer_stmt in node.defer_stmts {
+ g.writeln('bool ${g.defer_flag_var(defer_stmt)} = false;')
+ for var in defer_stmt.defer_vars {
+ if var.name in fargs || var.kind == .constant {
+ continue
+ }
+ if var.kind == .variable {
+ if var.name !in g.defer_vars {
+ g.defer_vars << var.name
+ mut deref := ''
+ if v := var.scope.find_var(var.name) {
+ if v.is_auto_heap {
+ deref = '*'
+ }
+ }
+ info := var.obj as ast.Var
+ g.writeln('${g.typ(info.typ)}$deref $var.name;')
+ }
+ }
+ }
+ }
+ if is_live_wrap {
+ // The live function just calls its implementation dual, while ensuring
+ // that the call is wrapped by the mutex lock & unlock calls.
+ // Adding the mutex lock/unlock inside the body of the implementation
+ // function is not reliable, because the implementation function can do
+ // an early exit, which will leave the mutex locked.
+ mut fn_args_list := []string{}
+ for ia, fa in fargs {
+ fn_args_list << '${fargtypes[ia]} $fa'
+ }
+ mut live_fncall := '${impl_fn_name}(' + fargs.join(', ') + ');'
+ mut live_fnreturn := ''
+ if type_name != 'void' {
+ live_fncall = '$type_name res = $live_fncall'
+ live_fnreturn = 'return res;'
+ }
+ g.definitions.writeln('$type_name ${name}(' + fn_args_list.join(', ') + ');')
+ g.hotcode_definitions.writeln('$type_name ${name}(' + fn_args_list.join(', ') + '){')
+ g.hotcode_definitions.writeln(' pthread_mutex_lock(&live_fn_mutex);')
+ g.hotcode_definitions.writeln(' $live_fncall')
+ g.hotcode_definitions.writeln(' pthread_mutex_unlock(&live_fn_mutex);')
+ g.hotcode_definitions.writeln(' $live_fnreturn')
+ g.hotcode_definitions.writeln('}')
+ }
+ // Profiling mode? Start counting at the beginning of the function (save current time).
+ if g.pref.is_prof && g.pref.build_mode != .build_module {
+ g.profile_fn(node)
+ }
+ // we could be in an anon fn so save outer fn defer stmts
+ prev_defer_stmts := g.defer_stmts
+ g.defer_stmts = []
+ ctmp := g.tmp_count
+ g.tmp_count = 0
+ defer {
+ g.tmp_count = ctmp
+ }
+ g.stmts(node.stmts)
+ if node.is_noreturn {
+ g.writeln('\twhile(1);')
+ }
+ // clear g.fn_mut_arg_names
+
+ if !node.has_return {
+ g.write_defer_stmts_when_needed()
+ }
+ if node.is_anon {
+ g.defer_stmts = prev_defer_stmts
+ } else {
+ g.defer_stmts = []
+ }
+ if node.return_type != ast.void_type && node.stmts.len > 0 && node.stmts.last() !is ast.Return
+ && !node.attrs.contains('_naked') {
+ default_expr := g.type_default(node.return_type)
+ // TODO: perf?
+ if default_expr == '{0}' {
+ // if node.return_type.idx() == 1 && node.return_type.has_flag(.optional) {
+ // // The default return for anonymous functions that return `?,
+ // // should have .ok = true set, otherwise calling them with
+ // // optfn() or { panic(err) } will cause a panic:
+ // g.writeln('\treturn (Option_void){0};')
+ // } else {
+ g.writeln('\treturn ($type_name)$default_expr;')
+ // }
+ } else {
+ g.writeln('\treturn $default_expr;')
+ }
+ }
+ g.writeln('}')
+ if g.pref.printfn_list.len > 0 && g.last_fn_c_name in g.pref.printfn_list {
+ println(g.out.after(fn_start_pos))
+ }
+ for attr in node.attrs {
+ if attr.name == 'export' {
+ g.writeln('// export alias: $attr.arg -> $name')
+ export_alias := '$type_name ${attr.arg}($arg_str)'
+ g.definitions.writeln('VV_EXPORTED_SYMBOL $export_alias; // exported fn $node.name')
+ g.writeln('$export_alias {')
+ g.write('\treturn ${name}(')
+ g.write(fargs.join(', '))
+ g.writeln(');')
+ g.writeln('}')
+ }
+ }
+}
+
+const closure_ctx = '_V_closure_ctx'
+
+fn closure_ctx_struct(node ast.FnDecl) string {
+ return 'struct _V_${node.name}_Ctx'
+}
+
+fn (mut g Gen) gen_anon_fn(mut node ast.AnonFn) {
+ g.gen_anon_fn_decl(mut node)
+ if !node.decl.scope.has_inherited_vars() {
+ g.write(node.decl.name)
+ return
+ }
+ // it may be possible to optimize `memdup` out if the closure never leaves current scope
+ ctx_var := g.new_tmp_var()
+ cur_line := g.go_before_stmt(0)
+ ctx_struct := closure_ctx_struct(node.decl)
+ g.writeln('$ctx_struct* $ctx_var = ($ctx_struct*) memdup(&($ctx_struct){')
+ g.indent++
+ for var in node.inherited_vars {
+ g.writeln('.$var.name = $var.name,')
+ }
+ g.indent--
+ g.writeln('}, sizeof($ctx_struct));')
+ g.empty_line = false
+ g.write(cur_line)
+ // TODO in case of an assignment, this should only call "__closure_set_data" and "__closure_set_function" (and free the former data)
+ g.write('__closure_create($node.decl.name, ${node.decl.params.len + 1}, $ctx_var)')
+}
+
+fn (mut g Gen) gen_anon_fn_decl(mut node ast.AnonFn) {
+ if node.has_gen {
+ return
+ }
+ node.has_gen = true
+ mut builder := strings.new_builder(256)
+ if node.inherited_vars.len > 0 {
+ ctx_struct := closure_ctx_struct(node.decl)
+ builder.writeln('$ctx_struct {')
+ for var in node.inherited_vars {
+ styp := g.typ(var.typ)
+ builder.writeln('\t$styp $var.name;')
+ }
+ builder.writeln('};\n')
+ }
+ pos := g.out.len
+ was_anon_fn := g.anon_fn
+ g.anon_fn = true
+ g.process_fn_decl(node.decl)
+ g.anon_fn = was_anon_fn
+ builder.write_string(g.out.cut_to(pos))
+ g.anon_fn_definitions << builder.str()
+}
+
+fn (g &Gen) defer_flag_var(stmt &ast.DeferStmt) string {
+ return '${g.last_fn_c_name}_defer_$stmt.idx_in_fn'
+}
+
+fn (mut g Gen) write_defer_stmts_when_needed() {
+ // unlock all mutexes, in case we are in a lock statement. defers are not allowed in lock statements
+ g.unlock_locks()
+ if g.defer_stmts.len > 0 {
+ g.write_defer_stmts()
+ }
+ if g.defer_profile_code.len > 0 {
+ g.writeln('')
+ g.writeln('\t// defer_profile_code')
+ g.writeln(g.defer_profile_code)
+ g.writeln('')
+ }
+}
+
+// fn decl args
+fn (mut g Gen) fn_args(args []ast.Param, scope &ast.Scope) ([]string, []string, []bool) {
+ mut fargs := []string{}
+ mut fargtypes := []string{}
+ mut heap_promoted := []bool{}
+ if args.len == 0 {
+ // in C, `()` is untyped, unlike `(void)`
+ g.write('void')
+ }
+ for i, arg in args {
+ mut caname := if arg.name == '_' { g.new_tmp_declaration_name() } else { c_name(arg.name) }
+ typ := g.unwrap_generic(arg.typ)
+ arg_type_sym := g.table.get_type_symbol(typ)
+ mut arg_type_name := g.typ(typ) // util.no_dots(arg_type_sym.name)
+ if arg_type_sym.kind == .function {
+ info := arg_type_sym.info as ast.FnType
+ func := info.func
+ g.write('${g.typ(func.return_type)} (*$caname)(')
+ g.definitions.write_string('${g.typ(func.return_type)} (*$caname)(')
+ g.fn_args(func.params, voidptr(0))
+ g.write(')')
+ g.definitions.write_string(')')
+ fargs << caname
+ fargtypes << arg_type_name
+ } else {
+ mut heap_prom := false
+ if scope != voidptr(0) {
+ if arg.name != '_' {
+ if v := scope.find_var(arg.name) {
+ if !v.is_stack_obj && v.is_auto_heap {
+ heap_prom = true
+ }
+ }
+ }
+ }
+ var_name_prefix := if heap_prom { '_v_toheap_' } else { '' }
+ const_prefix := if arg.typ.is_any_kind_of_pointer() && !arg.is_mut
+ && arg.name.starts_with('const_') {
+ 'const '
+ } else {
+ ''
+ }
+ s := '$const_prefix$arg_type_name $var_name_prefix$caname'
+ g.write(s)
+ g.definitions.write_string(s)
+ fargs << caname
+ fargtypes << arg_type_name
+ heap_promoted << heap_prom
+ }
+ if i < args.len - 1 {
+ g.write(', ')
+ g.definitions.write_string(', ')
+ }
+ }
+ return fargs, fargtypes, heap_promoted
+}
+
+fn (mut g Gen) call_expr(node ast.CallExpr) {
+ // g.write('/*call expr*/')
+ // NOTE: everything could be done this way
+ // see my comment in parser near anon_fn
+ if node.left is ast.AnonFn {
+ g.expr(node.left)
+ }
+ if node.left is ast.IndexExpr && node.name == '' {
+ g.is_fn_index_call = true
+ g.expr(node.left)
+ g.is_fn_index_call = false
+ }
+ if node.should_be_skipped {
+ return
+ }
+ g.inside_call = true
+ defer {
+ g.inside_call = false
+ }
+ gen_keep_alive := node.is_keep_alive && node.return_type != ast.void_type
+ && g.pref.gc_mode in [.boehm_full, .boehm_incr, .boehm_full_opt, .boehm_incr_opt]
+ gen_or := node.or_block.kind != .absent // && !g.is_autofree
+ is_gen_or_and_assign_rhs := gen_or && !g.discard_or_result
+ cur_line := if is_gen_or_and_assign_rhs || gen_keep_alive { // && !g.is_autofree {
+ // `x := foo() or { ...}`
+ // cut everything that has been generated to prepend optional variable creation
+ line := g.go_before_stmt(0)
+ g.out.write_string(util.tabs(g.indent))
+ // g.write('/*is_gen_or_and_assign_rhs*/')
+ line
+ } else {
+ ''
+ }
+ tmp_opt := if gen_or || gen_keep_alive { g.new_tmp_var() } else { '' }
+ if gen_or || gen_keep_alive {
+ mut ret_typ := node.return_type
+ if gen_or {
+ ret_typ = ret_typ.set_flag(.optional)
+ }
+ styp := g.typ(ret_typ)
+ g.write('$styp $tmp_opt = ')
+ }
+ if node.is_method && !node.is_field {
+ if node.name == 'writeln' && g.pref.experimental && node.args.len > 0
+ && node.args[0].expr is ast.StringInterLiteral
+ && g.table.get_type_symbol(node.receiver_type).name == 'strings.Builder' {
+ g.string_inter_literal_sb_optimized(node)
+ } else {
+ g.method_call(node)
+ }
+ } else {
+ g.fn_call(node)
+ }
+ if gen_or { // && !g.autofree {
+ // if !g.is_autofree {
+ g.or_block(tmp_opt, node.or_block, node.return_type)
+ //}
+ if is_gen_or_and_assign_rhs {
+ unwrapped_typ := node.return_type.clear_flag(.optional)
+ unwrapped_styp := g.typ(unwrapped_typ)
+ if unwrapped_typ == ast.void_type {
+ g.write('\n $cur_line')
+ } else if g.table.get_type_symbol(node.return_type).kind == .multi_return {
+ g.write('\n $cur_line $tmp_opt /*U*/')
+ } else {
+ if !g.inside_const {
+ g.write('\n $cur_line (*($unwrapped_styp*)${tmp_opt}.data)')
+ } else {
+ g.write('\n $cur_line $tmp_opt')
+ }
+ }
+ }
+ } else if gen_keep_alive {
+ if node.return_type == ast.void_type {
+ g.write('\n $cur_line')
+ } else {
+ g.write('\n $cur_line $tmp_opt')
+ }
+ }
+ if node.is_noreturn {
+ if g.inside_ternary == 0 {
+ g.writeln(';')
+ g.write('VUNREACHABLE()')
+ } else {
+ $if msvc {
+ // MSVC has no support for the statement expressions used below
+ } $else {
+ g.write(', ({VUNREACHABLE();})')
+ }
+ }
+ }
+}
+
+fn (mut g Gen) conversion_function_call(prefix string, postfix string, node ast.CallExpr) {
+ g.write('${prefix}( (')
+ g.expr(node.left)
+ dot := if node.left_type.is_ptr() { '->' } else { '.' }
+ g.write(')${dot}_typ )$postfix')
+}
+
+fn (mut g Gen) method_call(node ast.CallExpr) {
+ // TODO: there are still due to unchecked exprs (opt/some fn arg)
+ if node.left_type == 0 {
+ g.checker_bug('CallExpr.left_type is 0 in method_call', node.pos)
+ }
+ if node.receiver_type == 0 {
+ g.checker_bug('CallExpr.receiver_type is 0 in method_call', node.pos)
+ }
+ mut unwrapped_rec_type := node.receiver_type
+ if g.table.cur_fn.generic_names.len > 0 { // in generic fn
+ unwrapped_rec_type = g.unwrap_generic(node.receiver_type)
+ } else { // in non-generic fn
+ sym := g.table.get_type_symbol(node.receiver_type)
+ match sym.info {
+ ast.Struct, ast.Interface, ast.SumType {
+ generic_names := sym.info.generic_types.map(g.table.get_type_symbol(it).name)
+ if utyp := g.table.resolve_generic_to_concrete(node.receiver_type, generic_names,
+ sym.info.concrete_types)
+ {
+ unwrapped_rec_type = utyp
+ }
+ }
+ else {}
+ }
+ }
+ mut typ_sym := g.table.get_type_symbol(unwrapped_rec_type)
+ // alias type that undefined this method (not include `str`) need to use parent type
+ if typ_sym.kind == .alias && node.name != 'str' && !typ_sym.has_method(node.name) {
+ unwrapped_rec_type = (typ_sym.info as ast.Alias).parent_type
+ typ_sym = g.table.get_type_symbol(unwrapped_rec_type)
+ }
+ rec_cc_type := g.cc_type(unwrapped_rec_type, false)
+ mut receiver_type_name := util.no_dots(rec_cc_type)
+ if typ_sym.kind == .interface_ && (typ_sym.info as ast.Interface).defines_method(node.name) {
+ // Speaker_name_table[s._interface_idx].speak(s._object)
+ $if debug_interface_method_call ? {
+ eprintln('>>> interface typ_sym.name: $typ_sym.name | receiver_type_name: $receiver_type_name')
+ }
+ g.write('${c_name(receiver_type_name)}_name_table[')
+ g.expr(node.left)
+ dot := if node.left_type.is_ptr() { '->' } else { '.' }
+ mname := c_name(node.name)
+ g.write('${dot}_typ]._method_${mname}(')
+ g.expr(node.left)
+ g.write('${dot}_object')
+ if node.args.len > 0 {
+ g.write(', ')
+ g.call_args(node)
+ }
+ g.write(')')
+ return
+ }
+ left_sym := g.table.get_type_symbol(node.left_type)
+ final_left_sym := g.table.get_final_type_symbol(node.left_type)
+ if left_sym.kind == .array {
+ match node.name {
+ 'filter' {
+ g.gen_array_filter(node)
+ return
+ }
+ 'sort' {
+ g.gen_array_sort(node)
+ return
+ }
+ 'insert' {
+ g.gen_array_insert(node)
+ return
+ }
+ 'map' {
+ g.gen_array_map(node)
+ return
+ }
+ 'prepend' {
+ g.gen_array_prepend(node)
+ return
+ }
+ 'contains' {
+ g.gen_array_contains(node)
+ return
+ }
+ 'index' {
+ g.gen_array_index(node)
+ return
+ }
+ 'wait' {
+ g.gen_array_wait(node)
+ return
+ }
+ 'any' {
+ g.gen_array_any(node)
+ return
+ }
+ 'all' {
+ g.gen_array_all(node)
+ return
+ }
+ else {}
+ }
+ }
+
+ if left_sym.kind == .map && node.name == 'delete' {
+ left_info := left_sym.info as ast.Map
+ elem_type_str := g.typ(left_info.key_type)
+ g.write('map_delete(')
+ if node.left_type.is_ptr() {
+ g.expr(node.left)
+ } else {
+ g.write('&')
+ g.expr(node.left)
+ }
+ g.write(', &($elem_type_str[]){')
+ g.expr(node.args[0].expr)
+ g.write('})')
+ return
+ }
+
+ if left_sym.kind in [.sum_type, .interface_] {
+ if node.name == 'type_name' {
+ if left_sym.kind == .sum_type {
+ g.conversion_function_call('charptr_vstring_literal( /* $left_sym.name */ v_typeof_sumtype_$typ_sym.cname',
+ ')', node)
+ return
+ }
+ if left_sym.kind == .interface_ {
+ g.conversion_function_call('charptr_vstring_literal( /* $left_sym.name */ v_typeof_interface_$typ_sym.cname',
+ ')', node)
+ return
+ }
+ }
+ if node.name == 'type_idx' {
+ if left_sym.kind == .sum_type {
+ g.conversion_function_call('/* $left_sym.name */ v_typeof_sumtype_idx_$typ_sym.cname',
+ '', node)
+ return
+ }
+ if left_sym.kind == .interface_ {
+ g.conversion_function_call('/* $left_sym.name */ v_typeof_interface_idx_$typ_sym.cname',
+ '', node)
+ return
+ }
+ }
+ }
+
+ if node.name == 'str' {
+ mut rec_type := node.receiver_type
+ if rec_type.has_flag(.shared_f) {
+ rec_type = rec_type.clear_flag(.shared_f).set_nr_muls(0)
+ }
+ g.gen_str_for_type(rec_type)
+ }
+ mut has_cast := false
+ if left_sym.kind == .map && node.name in ['clone', 'move'] {
+ receiver_type_name = 'map'
+ }
+ if final_left_sym.kind == .array
+ && node.name in ['repeat', 'sort_with_compare', 'free', 'push_many', 'trim', 'first', 'last', 'pop', 'clone', 'reverse', 'slice', 'pointers'] {
+ if !(left_sym.info is ast.Alias && typ_sym.has_method(node.name)) {
+ // `array_Xyz_clone` => `array_clone`
+ receiver_type_name = 'array'
+ }
+ if node.name in ['last', 'first', 'pop'] {
+ return_type_str := g.typ(node.return_type)
+ has_cast = true
+ g.write('(*($return_type_str*)')
+ }
+ }
+ mut name := util.no_dots('${receiver_type_name}_$node.name')
+ mut array_depth := -1
+ mut noscan := ''
+ if left_sym.kind == .array {
+ needs_depth := node.name in ['clone', 'repeat']
+ if needs_depth {
+ elem_type := (left_sym.info as ast.Array).elem_type
+ array_depth = g.get_array_depth(elem_type)
+ }
+ maybe_noscan := needs_depth
+ || node.name in ['pop', 'push', 'push_many', 'reverse', 'grow_cap', 'grow_len']
+ if maybe_noscan {
+ info := left_sym.info as ast.Array
+ noscan = g.check_noscan(info.elem_type)
+ }
+ } else if left_sym.kind == .chan {
+ if node.name in ['close', 'try_pop', 'try_push'] {
+ name = 'sync__Channel_$node.name'
+ }
+ } else if left_sym.kind == .map {
+ if node.name == 'keys' {
+ name = 'map_keys'
+ }
+ }
+ if g.pref.obfuscate && g.cur_mod.name == 'main' && name.starts_with('main__')
+ && node.name != 'str' {
+ sym := g.table.get_type_symbol(node.receiver_type)
+ key := sym.name + '.' + node.name
+ g.write('/* obf method call: $key */')
+ name = g.obf_table[key] or {
+ panic('cgen: obf name "$key" not found, this should never happen')
+ }
+ }
+ // Check if expression is: arr[a..b].clone(), arr[a..].clone()
+ // if so, then instead of calling array_clone(&array_slice(...))
+ // call array_clone_static(array_slice(...))
+ mut is_range_slice := false
+ if node.receiver_type.is_ptr() && !node.left_type.is_ptr() {
+ if node.left is ast.IndexExpr {
+ idx := node.left.index
+ if idx is ast.RangeExpr {
+ // expr is arr[range].clone()
+ // use array_clone_static instead of array_clone
+ name = util.no_dots('${receiver_type_name}_${node.name}_static')
+ is_range_slice = true
+ }
+ }
+ }
+ name = g.generic_fn_name(node.concrete_types, name, false)
+ // TODO2
+ // g.generate_tmp_autofree_arg_vars(node, name)
+ //
+ // if node.receiver_type != 0 {
+ // g.write('/*${g.typ(node.receiver_type)}*/')
+ // g.write('/*expr_type=${g.typ(node.left_type)} rec type=${g.typ(node.receiver_type)}*/')
+ // }
+ if !node.receiver_type.is_ptr() && node.left_type.is_ptr() && node.name == 'str' {
+ g.write('ptr_str(')
+ } else {
+ if left_sym.kind == .array {
+ if array_depth >= 0 {
+ name = name + '_to_depth'
+ }
+ g.write('$name${noscan}(')
+ } else {
+ g.write('${name}(')
+ }
+ }
+ if node.receiver_type.is_ptr()
+ && (!node.left_type.is_ptr() || node.left_type.has_flag(.variadic)
+ || node.from_embed_type != 0
+ || (node.left_type.has_flag(.shared_f) && node.name != 'str')) {
+ // The receiver is a reference, but the caller provided a value
+ // Add `&` automatically.
+ // TODO same logic in call_args()
+ if !is_range_slice {
+ if !node.left.is_lvalue() {
+ g.write('ADDR($rec_cc_type, ')
+ has_cast = true
+ } else {
+ g.write('&')
+ }
+ }
+ } else if !node.receiver_type.is_ptr() && node.left_type.is_ptr() && node.name != 'str'
+ && node.from_embed_type == 0 {
+ if !node.left_type.has_flag(.shared_f) {
+ g.write('/*rec*/*')
+ }
+ } else if !is_range_slice && node.from_embed_type == 0 && node.name != 'str' {
+ diff := node.left_type.nr_muls() - node.receiver_type.nr_muls()
+ if diff < 0 {
+ // TODO
+ // g.write('&')
+ } else if diff > 0 {
+ g.write('/*diff=$diff*/')
+ g.write([]byte{len: diff, init: `*`}.bytestr())
+ }
+ }
+
+ // if node.left_type.idx() != node.receiver_type.idx() {
+ // println('${g.typ(node.left_type)} ${g.typ(node.receiver_type)}')
+ // }
+
+ if g.is_autofree && node.free_receiver && !g.inside_lambda && !g.is_builtin_mod {
+ // The receiver expression needs to be freed, use the temp var.
+ fn_name := node.name.replace('.', '_')
+ arg_name := '_arg_expr_${fn_name}_0_$node.pos.pos'
+ g.write('/*af receiver arg*/' + arg_name)
+ } else {
+ if left_sym.kind == .array && node.left.is_auto_deref_var()
+ && node.name in ['first', 'last', 'repeat'] {
+ g.write('*')
+ }
+ if node.left is ast.MapInit {
+ g.write('(map[]){')
+ g.expr(node.left)
+ g.write('}[0]')
+ } else {
+ g.expr(node.left)
+ }
+ if node.from_embed_type != 0 {
+ embed_name := typ_sym.embed_name()
+ if node.left_type.is_ptr() {
+ g.write('->')
+ } else {
+ g.write('.')
+ }
+ g.write(embed_name)
+ }
+ if node.left_type.has_flag(.shared_f) {
+ g.write('->val')
+ }
+ }
+ if has_cast {
+ g.write(')')
+ }
+ is_variadic := node.expected_arg_types.len > 0
+ && node.expected_arg_types[node.expected_arg_types.len - 1].has_flag(.variadic)
+ if node.args.len > 0 || is_variadic {
+ g.write(', ')
+ }
+ // /////////
+ /*
+ if name.contains('subkeys') {
+ println('call_args $name $node.arg_types.len')
+ for t in node.arg_types {
+ sym := g.table.get_type_symbol(t)
+ print('$sym.name ')
+ }
+ println('')
+}
+ */
+ // ///////
+ g.call_args(node)
+ if array_depth >= 0 {
+ g.write(', $array_depth')
+ }
+ g.write(')')
+}
+
+fn (mut g Gen) fn_call(node ast.CallExpr) {
+ // call struct field with fn type
+ // TODO: test node.left instead
+ // left & left_type will be `x` and `x type` in `x.fieldfn()`
+ // will be `0` for `foo()`
+ mut is_interface_call := false
+ mut is_selector_call := false
+ if node.left_type != 0 {
+ left_sym := g.table.get_type_symbol(node.left_type)
+ if left_sym.kind == .interface_ {
+ is_interface_call = true
+ g.write('(*')
+ }
+ g.expr(node.left)
+ if node.left_type.is_ptr() {
+ g.write('->')
+ } else {
+ g.write('.')
+ }
+ is_selector_call = true
+ }
+ mut name := node.name
+ is_print := name in ['print', 'println', 'eprint', 'eprintln', 'panic']
+ print_method := name
+ is_json_encode := name == 'json.encode'
+ is_json_encode_pretty := name == 'json.encode_pretty'
+ is_json_decode := name == 'json.decode'
+ g.is_json_fn = is_json_encode || is_json_encode_pretty || is_json_decode
+ mut json_type_str := ''
+ mut json_obj := ''
+ if g.is_json_fn {
+ json_obj = g.new_tmp_var()
+ mut tmp2 := ''
+ cur_line := g.go_before_stmt(0)
+ if is_json_encode || is_json_encode_pretty {
+ g.gen_json_for_type(node.args[0].typ)
+ json_type_str = g.typ(node.args[0].typ)
+ // `json__encode` => `json__encode_User`
+ // encode_name := c_name(name) + '_' + util.no_dots(json_type_str)
+ encode_name := js_enc_name(json_type_str)
+ g.writeln('// json.encode')
+ g.write('cJSON* $json_obj = ${encode_name}(')
+ if node.args[0].typ.is_ptr() {
+ g.write('*')
+ }
+ g.call_args(node)
+ g.writeln(');')
+ tmp2 = g.new_tmp_var()
+ if is_json_encode {
+ g.writeln('string $tmp2 = json__json_print($json_obj);')
+ } else {
+ g.writeln('string $tmp2 = json__json_print_pretty($json_obj);')
+ }
+ } else {
+ ast_type := node.args[0].expr as ast.TypeNode
+ // `json.decode(User, s)` => json.decode_User(s)
+ typ := c_name(g.typ(ast_type.typ))
+ fn_name := c_name(name) + '_' + typ
+ g.gen_json_for_type(ast_type.typ)
+ g.writeln('// json.decode')
+ g.write('cJSON* $json_obj = json__json_parse(')
+ // Skip the first argument in json.decode which is a type
+ // its name was already used to generate the function call
+ g.is_js_call = true
+ g.call_args(node)
+ g.is_js_call = false
+ g.writeln(');')
+ tmp2 = g.new_tmp_var()
+ g.writeln('Option_$typ $tmp2 = $fn_name ($json_obj);')
+ }
+ if !g.is_autofree {
+ g.write('cJSON_Delete($json_obj); //del')
+ }
+ g.write('\n$cur_line')
+ name = ''
+ json_obj = tmp2
+ }
+ if node.language == .c {
+ // Skip "C."
+ name = util.no_dots(name[2..])
+ } else {
+ name = c_name(name)
+ }
+ // Obfuscate only functions in the main module for now
+ if g.pref.obfuscate && g.cur_mod.name == 'main' && name.starts_with('main__') {
+ key := node.name
+ g.write('/* obf call: $key */')
+ name = g.obf_table[key] or {
+ panic('cgen: obf name "$key" not found, this should never happen')
+ }
+ }
+ if !is_selector_call {
+ name = g.generic_fn_name(node.concrete_types, name, false)
+ }
+ // TODO2
+ // cgen shouldn't modify ast nodes, this should be moved
+ // g.generate_tmp_autofree_arg_vars(node, name)
+ // Handle `print(x)`
+ mut print_auto_str := false
+ if is_print && node.args[0].typ != ast.string_type { // && !free_tmp_arg_vars {
+ mut typ := node.args[0].typ
+ if typ == 0 {
+ g.checker_bug('print arg.typ is 0', node.pos)
+ }
+ if typ != ast.string_type {
+ expr := node.args[0].expr
+ if g.is_autofree && !typ.has_flag(.optional) {
+ // Create a temporary variable so that the value can be freed
+ tmp := g.new_tmp_var()
+ // tmps << tmp
+ g.write('string $tmp = ')
+ g.gen_expr_to_string(expr, typ)
+ g.writeln('; ${c_name(print_method)}($tmp); string_free(&$tmp);')
+ } else {
+ g.write('${c_name(print_method)}(')
+ g.gen_expr_to_string(expr, typ)
+ g.write(')')
+ }
+ print_auto_str = true
+ }
+ }
+ if !print_auto_str {
+ if g.pref.is_debug && node.name == 'panic' {
+ paline, pafile, pamod, pafn := g.panic_debug_info(node.pos)
+ g.write('panic_debug($paline, tos3("$pafile"), tos3("$pamod"), tos3("$pafn"), ')
+ g.call_args(node)
+ g.write(')')
+ } else {
+ // Simple function call
+ // if free_tmp_arg_vars {
+ // g.writeln(';')
+ // g.write(cur_line + ' /* <== af cur line*/')
+ // }
+ g.write(g.get_ternary_name(name))
+ if is_interface_call {
+ g.write(')')
+ }
+ mut tmp_cnt_save := -1
+ g.write('(')
+ if g.is_json_fn {
+ g.write(json_obj)
+ } else {
+ if node.is_keep_alive
+ && g.pref.gc_mode in [.boehm_full, .boehm_incr, .boehm_full_opt, .boehm_incr_opt] {
+ cur_line := g.go_before_stmt(0)
+ tmp_cnt_save = g.keep_alive_call_pregen(node)
+ g.write(cur_line)
+ for i in 0 .. node.args.len {
+ if i > 0 {
+ g.write(', ')
+ }
+ g.write('__tmp_arg_${tmp_cnt_save + i}')
+ }
+ } else {
+ g.call_args(node)
+ }
+ }
+ g.write(')')
+ if tmp_cnt_save >= 0 {
+ g.writeln(';')
+ g.keep_alive_call_postgen(node, tmp_cnt_save)
+ }
+ }
+ }
+ g.is_json_fn = false
+}
+
+fn (mut g Gen) autofree_call_pregen(node ast.CallExpr) {
+ // g.writeln('// autofree_call_pregen()')
+ // Create a temporary var before fn call for each argument in order to free it (only if it's a complex expression,
+ // like `foo(get_string())` or `foo(a + b)`
+ mut free_tmp_arg_vars := g.is_autofree && !g.is_builtin_mod && node.args.len > 0
+ && !node.args[0].typ.has_flag(.optional) // TODO copy pasta checker.v
+ if !free_tmp_arg_vars {
+ return
+ }
+ if g.is_js_call {
+ return
+ }
+ if g.inside_const {
+ return
+ }
+ free_tmp_arg_vars = false // set the flag to true only if we have at least one arg to free
+ g.tmp_count2++
+ mut scope := g.file.scope.innermost(node.pos.pos)
+ // prepend the receiver for now (TODO turn the receiver into a CallArg everywhere?)
+ mut args := [ast.CallArg{
+ typ: node.receiver_type
+ expr: node.left
+ is_tmp_autofree: node.free_receiver
+ }]
+ args << node.args
+ // for i, arg in node.args {
+ for i, arg in args {
+ if !arg.is_tmp_autofree {
+ continue
+ }
+ if arg.expr is ast.CallExpr {
+ // Any argument can be an expression that has to be freed. Generate a tmp expression
+ // for each of those recursively.
+ g.autofree_call_pregen(arg.expr)
+ }
+ free_tmp_arg_vars = true
+ // t := g.new_tmp_var() + '_arg_expr_${name}_$i'
+ fn_name := node.name.replace('.', '_') // can't use name...
+ // t := '_tt${g.tmp_count2}_arg_expr_${fn_name}_$i'
+ t := '_arg_expr_${fn_name}_${i}_$node.pos.pos'
+ // g.called_fn_name = name
+ used := false // scope.known_var(t)
+ mut s := '$t = '
+ if used {
+ // This means this tmp var name was already used (the same function was called and
+ // `_arg_fnname_1` was already generated).
+ // We do not need to declare this variable again, so just generate `t = ...`
+ // instead of `string t = ...`, and we need to mark this variable as unused,
+ // so that it's freed after the call. (Used tmp arg vars are not freed to avoid double frees).
+ if x := scope.find(t) {
+ match mut x {
+ ast.Var { x.is_used = false }
+ else {}
+ }
+ }
+ s = '$t = '
+ } else {
+ scope.register(ast.Var{
+ name: t
+ typ: ast.string_type
+ is_autofree_tmp: true
+ pos: node.pos
+ })
+ s = 'string $t = '
+ }
+ // g.expr(arg.expr)
+ s += g.expr_string(arg.expr)
+ // g.writeln(';// new af pre')
+ s += ';// new af2 pre'
+ g.strs_to_free0 << s
+ // This tmp arg var will be freed with the rest of the vars at the end of the scope.
+ }
+}
+
+fn (mut g Gen) autofree_call_postgen(node_pos int) {
+ // if g.strs_to_free.len == 0 {
+ // return
+ // }
+ // g.writeln('\n/* strs_to_free3: $g.nr_vars_to_free */')
+ // if g.nr_vars_to_free <= 0 {
+ // return
+ // }
+ /*
+ for s in g.strs_to_free {
+ g.writeln('string_free(&$s);')
+ }
+ if !g.inside_or_block {
+ // we need to free the vars both inside the or block (in case of an error) and after it
+ // if we reset the array here, then the vars will not be freed after the block.
+ g.strs_to_free = []
+ }
+ */
+ if g.inside_vweb_tmpl {
+ return
+ }
+ // g.doing_autofree_tmp = true
+ // g.write('/* postgen */')
+ scope := g.file.scope.innermost(node_pos)
+ for _, obj in scope.objects {
+ match mut obj {
+ ast.Var {
+ // if var.typ == 0 {
+ // // TODO why 0?
+ // continue
+ // }
+ is_optional := obj.typ.has_flag(.optional)
+ if is_optional {
+ // TODO: free optionals
+ continue
+ }
+ if !obj.is_autofree_tmp {
+ continue
+ }
+ if obj.is_used {
+ // this means this tmp expr var has already been freed
+ continue
+ }
+ obj.is_used = true // TODO bug? sets all vars is_used to true
+ g.autofree_variable(obj)
+ // g.nr_vars_to_free--
+ }
+ else {}
+ }
+ }
+ // g.write('/* postgen end */')
+ // g.doing_autofree_tmp = false
+}
+
+fn (mut g Gen) call_args(node ast.CallExpr) {
+ args := if g.is_js_call { node.args[1..] } else { node.args }
+ expected_types := node.expected_arg_types
+ // only v variadic, C variadic args will be appeneded like normal args
+ is_variadic := expected_types.len > 0 && expected_types.last().has_flag(.variadic)
+ && node.language == .v
+ for i, arg in args {
+ if is_variadic && i == expected_types.len - 1 {
+ break
+ }
+ use_tmp_var_autofree := g.is_autofree && arg.typ == ast.string_type && arg.is_tmp_autofree
+ && !g.inside_const && !g.is_builtin_mod
+ // g.write('/* af=$arg.is_tmp_autofree */')
+ // some c fn definitions dont have args (cfns.v) or are not updated in checker
+ // when these are fixed we wont need this check
+ if i < expected_types.len {
+ if use_tmp_var_autofree {
+ if arg.is_tmp_autofree { // && !g.is_js_call {
+ // We saved expressions in temp variables so that they can be freed later.
+ // `foo(str + str2) => x := str + str2; foo(x); x.free()`
+ // g.write('_arg_expr_${g.called_fn_name}_$i')
+ // Use these variables here.
+ fn_name := node.name.replace('.', '_')
+ // name := '_tt${g.tmp_count2}_arg_expr_${fn_name}_$i'
+ name := '_arg_expr_${fn_name}_${i + 1}_$node.pos.pos'
+ g.write('/*af arg*/' + name)
+ }
+ } else {
+ if node.concrete_types.len > 0 && arg.expr.is_auto_deref_var() && !arg.is_mut
+ && !expected_types[i].is_ptr() {
+ g.write('*')
+ }
+ g.ref_or_deref_arg(arg, expected_types[i], node.language)
+ }
+ } else {
+ if use_tmp_var_autofree {
+ // TODO copypasta, move to an inline fn
+ fn_name := node.name.replace('.', '_')
+ // name := '_tt${g.tmp_count2}_arg_expr_${fn_name}_$i'
+ name := '_arg_expr_${fn_name}_${i + 1}_$node.pos.pos'
+ g.write('/*af arg2*/' + name)
+ } else {
+ g.expr(arg.expr)
+ }
+ }
+ if i < args.len - 1 || is_variadic {
+ g.write(', ')
+ }
+ }
+ arg_nr := expected_types.len - 1
+ if is_variadic {
+ varg_type := expected_types.last()
+ variadic_count := args.len - arg_nr
+ arr_sym := g.table.get_type_symbol(varg_type)
+ mut arr_info := arr_sym.info as ast.Array
+ if varg_type.has_flag(.generic) {
+ if fn_def := g.table.find_fn(node.name) {
+ if utyp := g.table.resolve_generic_to_concrete(arr_info.elem_type, fn_def.generic_names,
+ node.concrete_types)
+ {
+ arr_info.elem_type = utyp
+ }
+ } else {
+ g.error('unable to find function $node.name', node.pos)
+ }
+ }
+ elem_type := g.typ(arr_info.elem_type)
+ if g.pref.translated && args.len == 1 {
+ // Handle `foo(c'str')` for `fn foo(args ...&u8)`
+ // TODOC2V handle this in a better place
+ // println(g.table.type_to_str(args[0].typ))
+ g.expr(args[0].expr)
+ } else if args.len > 0 && args[args.len - 1].expr is ast.ArrayDecompose {
+ g.expr(args[args.len - 1].expr)
+ } else {
+ if variadic_count > 0 {
+ noscan := g.check_noscan(arr_info.elem_type)
+ g.write('new_array_from_c_array${noscan}($variadic_count, $variadic_count, sizeof($elem_type), _MOV(($elem_type[$variadic_count]){')
+ for j in arg_nr .. args.len {
+ g.ref_or_deref_arg(args[j], arr_info.elem_type, node.language)
+ if j < args.len - 1 {
+ g.write(', ')
+ }
+ }
+ g.write('}))')
+ } else {
+ g.write('__new_array(0, 0, sizeof($elem_type))')
+ }
+ }
+ }
+}
+
+// similar to `autofree_call_pregen()` but only to to handle [keep_args_alive] for C functions
+fn (mut g Gen) keep_alive_call_pregen(node ast.CallExpr) int {
+ g.empty_line = true
+ g.writeln('// keep_alive_call_pregen()')
+ // reserve the next tmp_vars for arguments
+ tmp_cnt_save := g.tmp_count + 1
+ g.tmp_count += node.args.len
+ for i, arg in node.args {
+ // save all arguments in temp vars (not only pointers) to make sure the
+ // evaluation order is preserved
+ expected_type := node.expected_arg_types[i]
+ typ := g.table.get_type_symbol(expected_type).cname
+ g.write('$typ __tmp_arg_${tmp_cnt_save + i} = ')
+ // g.expr(arg.expr)
+ g.ref_or_deref_arg(arg, expected_type, node.language)
+ g.writeln(';')
+ }
+ g.empty_line = false
+ return tmp_cnt_save
+}
+
+fn (mut g Gen) keep_alive_call_postgen(node ast.CallExpr, tmp_cnt_save int) {
+ g.writeln('// keep_alive_call_postgen()')
+ for i, expected_type in node.expected_arg_types {
+ if expected_type.is_ptr() || expected_type.is_pointer() {
+ g.writeln('GC_reachable_here(__tmp_arg_${tmp_cnt_save + i});')
+ }
+ }
+}
+
+[inline]
+fn (mut g Gen) ref_or_deref_arg(arg ast.CallArg, expected_type ast.Type, lang ast.Language) {
+ arg_is_ptr := expected_type.is_ptr() || expected_type.idx() in ast.pointer_type_idxs
+ expr_is_ptr := arg.typ.is_ptr() || arg.typ.idx() in ast.pointer_type_idxs
+ if expected_type == 0 {
+ g.checker_bug('ref_or_deref_arg expected_type is 0', arg.pos)
+ }
+ exp_sym := g.table.get_type_symbol(expected_type)
+ mut needs_closing := false
+ if arg.is_mut && !arg_is_ptr {
+ g.write('&/*mut*/')
+ } else if arg_is_ptr && !expr_is_ptr {
+ if arg.is_mut {
+ if exp_sym.kind == .array {
+ if (arg.expr is ast.Ident && (arg.expr as ast.Ident).kind == .variable)
+ || arg.expr is ast.SelectorExpr {
+ g.write('&/*arr*/')
+ g.expr(arg.expr)
+ } else {
+ // Special case for mutable arrays. We can't `&` function
+ // results, have to use `(array[]){ expr }[0]` hack.
+ g.write('&/*111*/(array[]){')
+ g.expr(arg.expr)
+ g.write('}[0]')
+ }
+ return
+ }
+ }
+ if !g.is_json_fn {
+ if arg.typ == 0 {
+ g.checker_bug('ref_or_deref_arg arg.typ is 0', arg.pos)
+ }
+ arg_typ_sym := g.table.get_type_symbol(arg.typ)
+ expected_deref_type := if expected_type.is_ptr() {
+ expected_type.deref()
+ } else {
+ expected_type
+ }
+ deref_sym := g.table.get_type_symbol(expected_deref_type)
+ if !((arg_typ_sym.kind == .function)
+ || deref_sym.kind in [.sum_type, .interface_]) && lang != .c {
+ if arg.expr.is_lvalue() {
+ g.write('(voidptr)&/*qq*/')
+ } else {
+ mut atype := expected_deref_type
+ if atype.has_flag(.generic) {
+ atype = g.unwrap_generic(atype)
+ }
+ if atype.has_flag(.generic) {
+ g.write('(voidptr)&/*qq*/')
+ } else {
+ needs_closing = true
+ g.write('ADDR(${g.typ(atype)}/*qq*/, ')
+ }
+ }
+ }
+ }
+ } else if arg.typ.has_flag(.shared_f) && !expected_type.has_flag(.shared_f) {
+ if expected_type.is_ptr() {
+ g.write('&')
+ }
+ g.expr(arg.expr)
+ g.write('->val')
+ return
+ }
+ g.expr_with_cast(arg.expr, arg.typ, expected_type)
+ if needs_closing {
+ g.write(')')
+ }
+}
+
+fn (mut g Gen) is_gui_app() bool {
+ $if windows {
+ if g.force_main_console {
+ return false
+ }
+ for cf in g.table.cflags {
+ if cf.value == 'gdi32' {
+ return true
+ }
+ }
+ }
+ return false
+}
+
+fn (g &Gen) fileis(s string) bool {
+ return g.file.path.contains(s)
+}
+
+fn (mut g Gen) write_fn_attrs(attrs []ast.Attr) string {
+ mut msvc_attrs := ''
+ for attr in attrs {
+ match attr.name {
+ 'inline' {
+ g.write('inline ')
+ }
+ 'noinline' {
+ // since these are supported by GCC, clang and MSVC, we can consider them officially supported.
+ g.write('__NOINLINE ')
+ }
+ 'noreturn' {
+ // a `[noreturn]` tag tells the compiler, that a function
+ // *DOES NOT RETURN* to its callsites.
+ // See: https://en.cppreference.com/w/c/language/_Noreturn
+ // Such functions should have no return type. They can be used
+ // in places where `panic(err)` or `exit(0)` can be used.
+ // panic/1 and exit/0 themselves will also be marked as
+ // `[noreturn]` soon.
+ // These functions should have busy `for{}` loops injected
+ // at their end, when they do not end by calling other fns
+ // marked by `[noreturn]`.
+ g.write('VNORETURN ')
+ }
+ 'irq_handler' {
+ g.write('__IRQHANDLER ')
+ }
+ '_cold' {
+ // GCC/clang attributes
+ // prefixed by _ to indicate they're for advanced users only and not really supported by V.
+ // source for descriptions: https://gcc.gnu.org/onlinedocs/gcc/Common-Function-Attributes.html#Common-Function-Attributes
+ // The cold attribute on functions is used to inform the compiler that the function is unlikely
+ // to be executed. The function is optimized for size rather than speed and on many targets it
+ // is placed into a special subsection of the text section so all cold functions appear close
+ // together, improving code locality of non-cold parts of program.
+ g.write('__attribute__((cold)) ')
+ }
+ '_constructor' {
+ // The constructor attribute causes the function to be called automatically before execution
+ // enters main ().
+ g.write('__attribute__((constructor)) ')
+ }
+ '_destructor' {
+ // The destructor attribute causes the function to be called automatically after main ()
+ // completes or exit () is called.
+ g.write('__attribute__((destructor)) ')
+ }
+ '_flatten' {
+ // Generally, inlining into a function is limited. For a function marked with this attribute,
+ // every call inside this function is inlined, if possible.
+ g.write('__attribute__((flatten)) ')
+ }
+ '_hot' {
+ // The hot attribute on a function is used to inform the compiler that the function is a hot
+ // spot of the compiled program.
+ g.write('__attribute__((hot)) ')
+ }
+ '_malloc' {
+ // This tells the compiler that a function is malloc-like, i.e., that the pointer P returned by
+ // the function cannot alias any other pointer valid when the function returns, and moreover no
+ // pointers to valid objects occur in any storage addressed by P.
+ g.write('__attribute__((malloc)) ')
+ }
+ '_pure' {
+ // Calls to functions whose return value is not affected by changes to the observable state
+ // of the program and that have no observable effects on such state other than to return a
+ // value may lend themselves to optimizations such as common subexpression elimination.
+ // Declaring such functions with the const attribute allows GCC to avoid emitting some calls in
+ // repeated invocations of the function with the same argument values.
+ g.write('__attribute__((const)) ')
+ }
+ '_naked' {
+ g.write('__attribute__((naked)) ')
+ }
+ 'windows_stdcall' {
+ // windows attributes (msvc/mingw)
+ // prefixed by windows to indicate they're for advanced users only and not really supported by V.
+ msvc_attrs += '__stdcall '
+ }
+ 'console' {
+ g.force_main_console = true
+ }
+ else {
+ // nothing but keep V happy
+ }
+ }
+ }
+ return msvc_attrs
+}
diff --git a/v_windows/v/vlib/v/gen/c/index.v b/v_windows/v/vlib/v/gen/c/index.v
new file mode 100644
index 0000000..26dc455
--- /dev/null
+++ b/v_windows/v/vlib/v/gen/c/index.v
@@ -0,0 +1,452 @@
+// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved.
+// Use of this source code is governed by an MIT license
+// that can be found in the LICENSE file.
+module c
+
+import v.ast
+import v.util
+
+fn (mut g Gen) index_expr(node ast.IndexExpr) {
+ if node.index is ast.RangeExpr {
+ g.range_expr(node, node.index)
+ } else {
+ sym := g.table.get_final_type_symbol(node.left_type)
+ if sym.kind == .array {
+ g.index_of_array(node, sym)
+ } else if sym.kind == .array_fixed {
+ g.index_of_fixed_array(node, sym)
+ } else if sym.kind == .map {
+ g.index_of_map(node, sym)
+ } else if sym.kind == .string && !node.left_type.is_ptr() {
+ is_direct_array_access := g.fn_decl != 0 && g.fn_decl.is_direct_arr
+ if is_direct_array_access {
+ g.expr(node.left)
+ g.write('.str[ ')
+ g.expr(node.index)
+ g.write(']')
+ } else {
+ gen_or := node.or_expr.kind != .absent || node.is_option
+ if gen_or {
+ tmp_opt := g.new_tmp_var()
+ cur_line := g.go_before_stmt(0)
+ g.out.write_string(util.tabs(g.indent))
+ opt_elem_type := g.typ(ast.byte_type.set_flag(.optional))
+ g.write('$opt_elem_type $tmp_opt = string_at_with_check(')
+ g.expr(node.left)
+ g.write(', ')
+ g.expr(node.index)
+ g.writeln(');')
+ if !node.is_option {
+ g.or_block(tmp_opt, node.or_expr, ast.byte_type)
+ }
+ g.write('\n$cur_line*(byte*)&${tmp_opt}.data')
+ } else {
+ g.write('string_at(')
+ g.expr(node.left)
+ g.write(', ')
+ g.expr(node.index)
+ g.write(')')
+ }
+ }
+ } else {
+ g.expr(node.left)
+ g.write('[')
+ g.expr(node.index)
+ g.write(']')
+ }
+ }
+}
+
+fn (mut g Gen) range_expr(node ast.IndexExpr, range ast.RangeExpr) {
+ sym := g.table.get_final_type_symbol(node.left_type)
+ if sym.kind == .string {
+ g.write('string_substr(')
+ g.expr(node.left)
+ } else if sym.kind == .array {
+ g.write('array_slice(')
+ if node.left_type.is_ptr() {
+ g.write('*')
+ }
+ g.expr(node.left)
+ } else if sym.kind == .array_fixed {
+ // Convert a fixed array to V array when doing `fixed_arr[start..end]`
+ info := sym.info as ast.ArrayFixed
+ noscan := g.check_noscan(info.elem_type)
+ g.write('array_slice(new_array_from_c_array${noscan}(')
+ g.write('$info.size')
+ g.write(', $info.size')
+ g.write(', sizeof(')
+ if node.left_type.is_ptr() {
+ g.write('(*')
+ }
+ g.expr(node.left)
+ if node.left_type.is_ptr() {
+ g.write(')')
+ }
+ g.write('[0]), ')
+ if node.left_type.is_ptr() {
+ g.write('*')
+ }
+ g.expr(node.left)
+ g.write(')')
+ } else {
+ g.expr(node.left)
+ }
+ g.write(', ')
+ if range.has_low {
+ g.expr(range.low)
+ } else {
+ g.write('0')
+ }
+ g.write(', ')
+ if range.has_high {
+ g.expr(range.high)
+ } else if sym.kind == .array_fixed {
+ info := sym.info as ast.ArrayFixed
+ g.write('$info.size')
+ } else if node.left_type.is_ptr() {
+ g.write('(')
+ g.write('*')
+ g.expr(node.left)
+ g.write(')')
+ g.write('.len')
+ } else {
+ g.expr(node.left)
+ g.write('.len')
+ }
+ g.write(')')
+}
+
+fn (mut g Gen) index_of_array(node ast.IndexExpr, sym ast.TypeSymbol) {
+ gen_or := node.or_expr.kind != .absent || node.is_option
+ left_is_ptr := node.left_type.is_ptr()
+ info := sym.info as ast.Array
+ elem_type_str := g.typ(info.elem_type)
+ elem_type := info.elem_type
+ elem_typ := g.table.get_type_symbol(elem_type)
+ // `vals[i].field = x` is an exception and requires `array_get`:
+ // `(*(Val*)array_get(vals, i)).field = x;`
+ is_selector := node.left is ast.SelectorExpr
+ if g.is_assign_lhs && !is_selector && node.is_setter {
+ is_direct_array_access := g.fn_decl != 0 && g.fn_decl.is_direct_arr
+ is_op_assign := g.assign_op != .assign && info.elem_type != ast.string_type
+ array_ptr_type_str := match elem_typ.kind {
+ .function { 'voidptr*' }
+ else { '$elem_type_str*' }
+ }
+ if is_direct_array_access {
+ g.write('(($array_ptr_type_str)')
+ } else if is_op_assign {
+ g.write('(*($array_ptr_type_str)array_get(')
+ if left_is_ptr && !node.left_type.has_flag(.shared_f) {
+ g.write('*')
+ }
+ } else {
+ g.is_arraymap_set = true // special handling of assign_op and closing with '})'
+ g.write('array_set(')
+ if !left_is_ptr || node.left_type.has_flag(.shared_f) {
+ g.write('&')
+ }
+ }
+ g.expr(node.left)
+ if node.left_type.has_flag(.shared_f) {
+ if left_is_ptr {
+ g.write('->val')
+ } else {
+ g.write('.val')
+ }
+ }
+ if is_direct_array_access {
+ if left_is_ptr && !node.left_type.has_flag(.shared_f) {
+ g.write('->')
+ } else {
+ g.write('.')
+ }
+ g.write('data)[')
+ g.expr(node.index)
+ g.write(']')
+ } else {
+ g.write(', ')
+ g.expr(node.index)
+ if !is_op_assign {
+ mut need_wrapper := true
+ /*
+ match node.right {
+ ast.EnumVal, ast.Ident {
+ // `&x` is enough for variables and enums
+ // `&(Foo[]){ ... }` is only needed for function calls and literals
+ need_wrapper = false
+ }
+ else {}
+ }
+ */
+ if need_wrapper {
+ if elem_typ.kind == .function {
+ g.write(', &(voidptr[]) { ')
+ } else {
+ g.write(', &($elem_type_str[]) { ')
+ }
+ } else {
+ g.write(', &')
+ }
+ } else {
+ // `x[0] *= y`
+ g.write('))')
+ }
+ }
+ } else {
+ is_direct_array_access := g.fn_decl != 0 && g.fn_decl.is_direct_arr
+ array_ptr_type_str := match elem_typ.kind {
+ .function { 'voidptr*' }
+ else { '$elem_type_str*' }
+ }
+ // do not clone inside `opt_ok(opt_ok(&(string[]) {..})` before returns
+ needs_clone := info.elem_type == ast.string_type_idx && g.is_autofree && !(g.inside_return
+ && g.fn_decl.return_type.has_flag(.optional)) && !g.is_assign_lhs
+ is_gen_or_and_assign_rhs := gen_or && !g.discard_or_result
+ cur_line := if is_gen_or_and_assign_rhs {
+ line := g.go_before_stmt(0)
+ g.out.write_string(util.tabs(g.indent))
+ line
+ } else {
+ ''
+ }
+ tmp_opt := if gen_or { g.new_tmp_var() } else { '' }
+ tmp_opt_ptr := if gen_or { g.new_tmp_var() } else { '' }
+ if gen_or {
+ g.write('$array_ptr_type_str $tmp_opt_ptr = ($array_ptr_type_str)/*ee elem_ptr_typ */(array_get_with_check(')
+ } else {
+ if needs_clone {
+ g.write('/*2*/string_clone(')
+ }
+ if g.is_fn_index_call {
+ if elem_typ.info is ast.FnType {
+ g.write('((')
+ g.write_fn_ptr_decl(&elem_typ.info, '')
+ g.write(')(*($array_ptr_type_str)/*ee elem_typ */array_get(')
+ }
+ if left_is_ptr && !node.left_type.has_flag(.shared_f) {
+ g.write('*')
+ }
+ } else if is_direct_array_access {
+ g.write('(($array_ptr_type_str)')
+ } else {
+ g.write('(*($array_ptr_type_str)/*ee elem_typ */array_get(')
+ if left_is_ptr && !node.left_type.has_flag(.shared_f) {
+ g.write('*')
+ }
+ }
+ }
+ g.expr(node.left)
+ // TODO: test direct_array_access when 'shared' is implemented
+ if node.left_type.has_flag(.shared_f) {
+ if left_is_ptr {
+ g.write('->val')
+ } else {
+ g.write('.val')
+ }
+ }
+ if is_direct_array_access && !gen_or {
+ if left_is_ptr && !node.left_type.has_flag(.shared_f) {
+ g.write('->')
+ } else {
+ g.write('.')
+ }
+ g.write('data)[')
+ g.expr(node.index)
+ g.write(']')
+ } else {
+ g.write(', ')
+ g.expr(node.index)
+ if g.is_fn_index_call {
+ g.write(')))')
+ } else {
+ g.write('))')
+ }
+ }
+ if needs_clone {
+ g.write(')')
+ }
+ if gen_or {
+ g.writeln(';')
+ opt_elem_type := g.typ(elem_type.set_flag(.optional))
+ g.writeln('$opt_elem_type $tmp_opt = {0};')
+ g.writeln('if ($tmp_opt_ptr) {')
+ g.writeln('\t*(($elem_type_str*)&${tmp_opt}.data) = *(($elem_type_str*)$tmp_opt_ptr);')
+ g.writeln('} else {')
+ g.writeln('\t${tmp_opt}.state = 2; ${tmp_opt}.err = _v_error(_SLIT("array index out of range"));')
+ g.writeln('}')
+ if !node.is_option {
+ g.or_block(tmp_opt, node.or_expr, elem_type)
+ }
+ g.write('\n$cur_line*($elem_type_str*)${tmp_opt}.data')
+ }
+ }
+}
+
+fn (mut g Gen) index_of_fixed_array(node ast.IndexExpr, sym ast.TypeSymbol) {
+ info := sym.info as ast.ArrayFixed
+ elem_type := info.elem_type
+ elem_sym := g.table.get_type_symbol(elem_type)
+ is_fn_index_call := g.is_fn_index_call && elem_sym.info is ast.FnType
+
+ if is_fn_index_call {
+ g.write('(*')
+ }
+ if node.left_type.is_ptr() {
+ g.write('(*')
+ g.expr(node.left)
+ g.write(')')
+ } else {
+ g.expr(node.left)
+ }
+ g.write('[')
+ direct := g.fn_decl != 0 && g.fn_decl.is_direct_arr
+ if direct || node.index is ast.IntegerLiteral {
+ g.expr(node.index)
+ } else {
+ // bounds check
+ g.write('v_fixed_index(')
+ g.expr(node.index)
+ g.write(', $info.size)')
+ }
+ g.write(']')
+ if is_fn_index_call {
+ g.write(')')
+ }
+}
+
+fn (mut g Gen) index_of_map(node ast.IndexExpr, sym ast.TypeSymbol) {
+ gen_or := node.or_expr.kind != .absent || node.is_option
+ left_is_ptr := node.left_type.is_ptr()
+ info := sym.info as ast.Map
+ key_type_str := g.typ(info.key_type)
+ elem_type := info.value_type
+ elem_type_str := g.typ(elem_type)
+ elem_typ := g.table.get_type_symbol(elem_type)
+ get_and_set_types := elem_typ.kind in [.struct_, .map]
+ if g.is_assign_lhs && !g.is_arraymap_set && !get_and_set_types {
+ if g.assign_op == .assign || info.value_type == ast.string_type {
+ g.is_arraymap_set = true
+ g.write('map_set(')
+ } else {
+ if node.is_setter {
+ g.write('(*(($elem_type_str*)map_get_and_set((map*)')
+ } else {
+ g.write('(*(($elem_type_str*)map_get((map*)')
+ }
+ }
+ if !left_is_ptr || node.left_type.has_flag(.shared_f) {
+ g.write('&')
+ }
+ if node.left is ast.IndexExpr {
+ g.inside_map_index = true
+ g.expr(node.left)
+ g.inside_map_index = false
+ } else {
+ g.expr(node.left)
+ }
+ if node.left_type.has_flag(.shared_f) {
+ if left_is_ptr {
+ g.write('->val')
+ } else {
+ g.write('.val')
+ }
+ }
+ g.write(', &($key_type_str[]){')
+ g.expr(node.index)
+ g.write('}')
+ if elem_typ.kind == .function {
+ g.write(', &(voidptr[]) { ')
+ } else {
+ g.arraymap_set_pos = g.out.len
+ g.write(', &($elem_type_str[]) { ')
+ }
+ if g.assign_op != .assign && info.value_type != ast.string_type {
+ zero := g.type_default(info.value_type)
+ g.write('$zero })))')
+ }
+ } else if g.inside_map_postfix || g.inside_map_infix || g.inside_map_index
+ || (g.is_assign_lhs && !g.is_arraymap_set && get_and_set_types) {
+ zero := g.type_default(info.value_type)
+ if node.is_setter {
+ g.write('(*($elem_type_str*)map_get_and_set((map*)')
+ } else {
+ g.write('(*($elem_type_str*)map_get((map*)')
+ }
+ if !left_is_ptr {
+ g.write('&')
+ }
+ g.expr(node.left)
+ g.write(', &($key_type_str[]){')
+ g.expr(node.index)
+ g.write('}, &($elem_type_str[]){ $zero }))')
+ } else {
+ zero := g.type_default(info.value_type)
+ is_gen_or_and_assign_rhs := gen_or && !g.discard_or_result
+ cur_line := if is_gen_or_and_assign_rhs {
+ line := g.go_before_stmt(0)
+ g.out.write_string(util.tabs(g.indent))
+ line
+ } else {
+ ''
+ }
+ tmp_opt := if gen_or { g.new_tmp_var() } else { '' }
+ tmp_opt_ptr := if gen_or { g.new_tmp_var() } else { '' }
+ if gen_or {
+ g.write('$elem_type_str* $tmp_opt_ptr = ($elem_type_str*)/*ee elem_ptr_typ */(map_get_check(')
+ } else {
+ if g.is_fn_index_call {
+ if elem_typ.info is ast.FnType {
+ g.write('((')
+ g.write_fn_ptr_decl(&elem_typ.info, '')
+ g.write(')(*(voidptr*)map_get(')
+ }
+ } else if elem_typ.kind == .function {
+ g.write('(*(voidptr*)map_get(')
+ } else {
+ g.write('(*($elem_type_str*)map_get(')
+ }
+ }
+ if !left_is_ptr || node.left_type.has_flag(.shared_f) {
+ g.write('ADDR(map, ')
+ g.expr(node.left)
+ } else {
+ g.write('(')
+ g.expr(node.left)
+ }
+ if node.left_type.has_flag(.shared_f) {
+ if left_is_ptr {
+ g.write('->val')
+ } else {
+ g.write('.val')
+ }
+ }
+ g.write('), &($key_type_str[]){')
+ g.expr(node.index)
+ g.write('}')
+ if gen_or {
+ g.write('))')
+ } else if g.is_fn_index_call {
+ g.write(', &(voidptr[]){ $zero })))')
+ } else if elem_typ.kind == .function {
+ g.write(', &(voidptr[]){ $zero }))')
+ } else {
+ g.write(', &($elem_type_str[]){ $zero }))')
+ }
+ if gen_or {
+ g.writeln(';')
+ opt_elem_type := g.typ(elem_type.set_flag(.optional))
+ g.writeln('$opt_elem_type $tmp_opt = {0};')
+ g.writeln('if ($tmp_opt_ptr) {')
+ g.writeln('\t*(($elem_type_str*)&${tmp_opt}.data) = *(($elem_type_str*)$tmp_opt_ptr);')
+ g.writeln('} else {')
+ g.writeln('\t${tmp_opt}.state = 2; ${tmp_opt}.err = _v_error(_SLIT("array index out of range"));')
+ g.writeln('}')
+ if !node.is_option {
+ g.or_block(tmp_opt, node.or_expr, elem_type)
+ }
+ g.write('\n$cur_line*($elem_type_str*)${tmp_opt}.data')
+ }
+ }
+}
diff --git a/v_windows/v/vlib/v/gen/c/infix_expr.v b/v_windows/v/vlib/v/gen/c/infix_expr.v
new file mode 100644
index 0000000..dc842c9
--- /dev/null
+++ b/v_windows/v/vlib/v/gen/c/infix_expr.v
@@ -0,0 +1,628 @@
+// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved.
+// Use of this source code is governed by an MIT license
+// that can be found in the LICENSE file.
+module c
+
+import v.ast
+import v.token
+import v.util
+
+fn (mut g Gen) infix_expr(node ast.InfixExpr) {
+ if node.auto_locked != '' {
+ g.writeln('sync__RwMutex_lock(&$node.auto_locked->mtx);')
+ }
+ match node.op {
+ .arrow {
+ g.infix_expr_arrow_op(node)
+ }
+ .eq, .ne {
+ g.infix_expr_eq_op(node)
+ }
+ .gt, .ge, .lt, .le {
+ g.infix_expr_cmp_op(node)
+ }
+ .key_in, .not_in {
+ g.infix_expr_in_op(node)
+ }
+ .key_is, .not_is {
+ g.infix_expr_is_op(node)
+ }
+ .plus, .minus, .mul, .div, .mod {
+ g.infix_expr_arithmetic_op(node)
+ }
+ .left_shift {
+ g.infix_expr_left_shift_op(node)
+ }
+ .and, .logical_or {
+ g.infix_expr_and_or_op(node)
+ }
+ else {
+ // `x & y == 0` => `(x & y) == 0` in C
+ need_par := node.op in [.amp, .pipe, .xor]
+ if need_par {
+ g.write('(')
+ }
+ g.gen_plain_infix_expr(node)
+ if need_par {
+ g.write(')')
+ }
+ }
+ }
+ if node.auto_locked != '' {
+ g.writeln(';')
+ g.write('sync__RwMutex_unlock(&$node.auto_locked->mtx)')
+ }
+}
+
+// infix_expr_arrow_op generates C code for pushing into channels (chan <- val)
+fn (mut g Gen) infix_expr_arrow_op(node ast.InfixExpr) {
+ left := g.unwrap(node.left_type)
+ styp := left.sym.cname
+ elem_type := (left.sym.info as ast.Chan).elem_type
+ gen_or := node.or_block.kind != .absent
+ tmp_opt := if gen_or { g.new_tmp_var() } else { '' }
+ if gen_or {
+ elem_styp := g.typ(elem_type)
+ g.register_chan_push_optional_call(elem_styp, styp)
+ g.write('Option_void $tmp_opt = __Option_${styp}_pushval(')
+ } else {
+ g.write('__${styp}_pushval(')
+ }
+ g.expr(node.left)
+ g.write(', ')
+ g.expr(node.right)
+ g.write(')')
+ if gen_or {
+ g.or_block(tmp_opt, node.or_block, ast.void_type)
+ }
+}
+
+// infix_expr_eq_op generates code for `==` and `!=`
+fn (mut g Gen) infix_expr_eq_op(node ast.InfixExpr) {
+ left := g.unwrap(node.left_type)
+ right := g.unwrap(node.right_type)
+ has_operator_overloading := g.table.type_has_method(left.sym, '==')
+ if (left.typ.is_ptr() && right.typ.is_int()) || (right.typ.is_ptr() && left.typ.is_int()) {
+ g.gen_plain_infix_expr(node)
+ } else if (left.typ.idx() == ast.string_type_idx || (!has_operator_overloading
+ && left.unaliased.idx() == ast.string_type_idx)) && node.right is ast.StringLiteral
+ && (node.right as ast.StringLiteral).val == '' {
+ // `str == ''` -> `str.len == 0` optimization
+ g.write('(')
+ g.expr(node.left)
+ g.write(')')
+ arrow := if left.typ.is_ptr() { '->' } else { '.' }
+ g.write('${arrow}len $node.op 0')
+ } else if has_operator_overloading {
+ if node.op == .ne {
+ g.write('!')
+ }
+ g.write(g.typ(left.unaliased.set_nr_muls(0)))
+ g.write('__eq(')
+ g.write('*'.repeat(left.typ.nr_muls()))
+ g.expr(node.left)
+ g.write(', ')
+ g.write('*'.repeat(right.typ.nr_muls()))
+ g.expr(node.right)
+ g.write(')')
+ } else if left.typ.idx() == right.typ.idx()
+ && left.sym.kind in [.array, .array_fixed, .alias, .map, .struct_, .sum_type] {
+ match left.sym.kind {
+ .alias {
+ ptr_typ := g.gen_alias_equality_fn(left.typ)
+ if node.op == .ne {
+ g.write('!')
+ }
+ g.write('${ptr_typ}_alias_eq(')
+ if left.typ.is_ptr() {
+ g.write('*')
+ }
+ g.expr(node.left)
+ g.write(', ')
+ if right.typ.is_ptr() {
+ g.write('*')
+ }
+ g.expr(node.right)
+ g.write(')')
+ }
+ .array {
+ ptr_typ := g.gen_array_equality_fn(left.unaliased.clear_flag(.shared_f))
+ if node.op == .ne {
+ g.write('!')
+ }
+ g.write('${ptr_typ}_arr_eq(')
+ if left.typ.is_ptr() && !left.typ.has_flag(.shared_f) {
+ g.write('*')
+ }
+ g.expr(node.left)
+ if left.typ.has_flag(.shared_f) {
+ if left.typ.is_ptr() {
+ g.write('->val')
+ } else {
+ g.write('.val')
+ }
+ }
+ g.write(', ')
+ if right.typ.is_ptr() && !right.typ.has_flag(.shared_f) {
+ g.write('*')
+ }
+ g.expr(node.right)
+ if right.typ.has_flag(.shared_f) {
+ if right.typ.is_ptr() {
+ g.write('->val')
+ } else {
+ g.write('.val')
+ }
+ }
+ g.write(')')
+ }
+ .array_fixed {
+ ptr_typ := g.gen_fixed_array_equality_fn(left.unaliased)
+ if node.op == .ne {
+ g.write('!')
+ }
+ g.write('${ptr_typ}_arr_eq(')
+ if left.typ.is_ptr() {
+ g.write('*')
+ }
+ if node.left is ast.ArrayInit {
+ s := g.typ(left.unaliased)
+ g.write('($s)')
+ }
+ g.expr(node.left)
+ g.write(', ')
+ if node.right is ast.ArrayInit {
+ s := g.typ(right.unaliased)
+ g.write('($s)')
+ }
+ g.expr(node.right)
+ g.write(')')
+ }
+ .map {
+ ptr_typ := g.gen_map_equality_fn(left.unaliased)
+ if node.op == .ne {
+ g.write('!')
+ }
+ g.write('${ptr_typ}_map_eq(')
+ if left.typ.is_ptr() {
+ g.write('*')
+ }
+ g.expr(node.left)
+ g.write(', ')
+ if right.typ.is_ptr() {
+ g.write('*')
+ }
+ g.expr(node.right)
+ g.write(')')
+ }
+ .struct_ {
+ ptr_typ := g.gen_struct_equality_fn(left.unaliased)
+ if node.op == .ne {
+ g.write('!')
+ }
+ g.write('${ptr_typ}_struct_eq(')
+ if left.typ.is_ptr() {
+ g.write('*')
+ }
+ g.expr(node.left)
+ g.write(', ')
+ if right.typ.is_ptr() {
+ g.write('*')
+ }
+ g.expr(node.right)
+ g.write(')')
+ }
+ .sum_type {
+ ptr_typ := g.gen_sumtype_equality_fn(left.unaliased)
+ if node.op == .ne {
+ g.write('!')
+ }
+ g.write('${ptr_typ}_sumtype_eq(')
+ if left.typ.is_ptr() {
+ g.write('*')
+ }
+ g.expr(node.left)
+ g.write(', ')
+ if right.typ.is_ptr() {
+ g.write('*')
+ }
+ g.expr(node.right)
+ g.write(')')
+ }
+ else {}
+ }
+ } else if left.unaliased.idx() in [ast.u32_type_idx, ast.u64_type_idx]
+ && right.unaliased.is_signed() {
+ g.gen_safe_integer_infix_expr(
+ op: node.op
+ unsigned_type: left.unaliased
+ unsigned_expr: node.left
+ signed_type: right.unaliased
+ signed_expr: node.right
+ )
+ } else if right.unaliased.idx() in [ast.u32_type_idx, ast.u64_type_idx]
+ && left.unaliased.is_signed() {
+ g.gen_safe_integer_infix_expr(
+ op: node.op
+ reverse: true
+ unsigned_type: right.unaliased
+ unsigned_expr: node.right
+ signed_type: left.unaliased
+ signed_expr: node.left
+ )
+ } else {
+ g.gen_plain_infix_expr(node)
+ }
+}
+
+// infix_expr_cmp_op generates code for `<`, `<=`, `>`, `>=`
+// It handles operator overloading when necessary
+fn (mut g Gen) infix_expr_cmp_op(node ast.InfixExpr) {
+ left := g.unwrap(node.left_type)
+ right := g.unwrap(node.right_type)
+ has_operator_overloading := g.table.type_has_method(left.sym, '<')
+ if left.sym.kind == right.sym.kind && has_operator_overloading {
+ if node.op in [.le, .ge] {
+ g.write('!')
+ }
+ g.write(g.typ(left.typ.set_nr_muls(0)))
+ g.write('__lt')
+ if node.op in [.lt, .ge] {
+ g.write('(')
+ g.write('*'.repeat(left.typ.nr_muls()))
+ g.expr(node.left)
+ g.write(', ')
+ g.write('*'.repeat(right.typ.nr_muls()))
+ g.expr(node.right)
+ g.write(')')
+ } else {
+ g.write('(')
+ g.write('*'.repeat(right.typ.nr_muls()))
+ g.expr(node.right)
+ g.write(', ')
+ g.write('*'.repeat(left.typ.nr_muls()))
+ g.expr(node.left)
+ g.write(')')
+ }
+ } else if left.unaliased.idx() in [ast.u32_type_idx, ast.u64_type_idx]
+ && right.unaliased.is_signed() {
+ g.gen_safe_integer_infix_expr(
+ op: node.op
+ unsigned_type: left.unaliased
+ unsigned_expr: node.left
+ signed_type: right.unaliased
+ signed_expr: node.right
+ )
+ } else if right.unaliased.idx() in [ast.u32_type_idx, ast.u64_type_idx]
+ && left.unaliased.is_signed() {
+ g.gen_safe_integer_infix_expr(
+ op: node.op
+ reverse: true
+ unsigned_type: right.unaliased
+ unsigned_expr: node.right
+ signed_type: left.unaliased
+ signed_expr: node.left
+ )
+ } else {
+ g.gen_plain_infix_expr(node)
+ }
+}
+
+// infix_expr_in_op generates code for `in` and `!in`
+fn (mut g Gen) infix_expr_in_op(node ast.InfixExpr) {
+ left := g.unwrap(node.left_type)
+ right := g.unwrap(node.right_type)
+ if node.op == .not_in {
+ g.write('!')
+ }
+ if right.unaliased_sym.kind == .array {
+ if mut node.right is ast.ArrayInit {
+ if node.right.exprs.len > 0 {
+ // `a in [1,2,3]` optimization => `a == 1 || a == 2 || a == 3`
+ // avoids an allocation
+ g.write('(')
+ g.infix_expr_in_optimization(node.left, node.right)
+ g.write(')')
+ return
+ }
+ }
+ fn_name := g.gen_array_contains_method(node.right_type)
+ g.write('(${fn_name}(')
+ if right.typ.is_ptr() && right.typ.share() != .shared_t {
+ g.write('*')
+ }
+ g.expr(node.right)
+ if right.typ.share() == .shared_t {
+ g.write('->val')
+ }
+ g.write(', ')
+ g.expr(node.left)
+ g.write('))')
+ return
+ } else if right.unaliased_sym.kind == .map {
+ g.write('_IN_MAP(')
+ if !left.typ.is_ptr() {
+ styp := g.typ(node.left_type)
+ g.write('ADDR($styp, ')
+ g.expr(node.left)
+ g.write(')')
+ } else {
+ g.expr(node.left)
+ }
+ g.write(', ')
+ if !right.typ.is_ptr() {
+ g.write('ADDR(map, ')
+ g.expr(node.right)
+ g.write(')')
+ } else {
+ g.expr(node.right)
+ }
+ g.write(')')
+ } else if right.unaliased_sym.kind == .string {
+ g.write('string_contains(')
+ g.expr(node.right)
+ g.write(', ')
+ g.expr(node.left)
+ g.write(')')
+ }
+}
+
+// infix_expr_in_optimization optimizes `<var> in <array>` expressions,
+// and transform them in a serie of equality comparison
+// i.e. `a in [1,2,3]` => `a == 1 || a == 2 || a == 3`
+fn (mut g Gen) infix_expr_in_optimization(left ast.Expr, right ast.ArrayInit) {
+ is_str := right.elem_type.idx() == ast.string_type_idx
+ elem_sym := g.table.get_type_symbol(right.elem_type)
+ is_array := elem_sym.kind == .array
+ for i, array_expr in right.exprs {
+ if is_str {
+ g.write('string__eq(')
+ } else if is_array {
+ ptr_typ := g.gen_array_equality_fn(right.elem_type)
+ g.write('${ptr_typ}_arr_eq(')
+ }
+ g.expr(left)
+ if is_str || is_array {
+ g.write(', ')
+ } else {
+ g.write(' == ')
+ }
+ g.expr(array_expr)
+ if is_str || is_array {
+ g.write(')')
+ }
+ if i < right.exprs.len - 1 {
+ g.write(' || ')
+ }
+ }
+}
+
+// infix_expr_is_op generates code for `is` and `!is`
+fn (mut g Gen) infix_expr_is_op(node ast.InfixExpr) {
+ sym := g.table.get_type_symbol(node.left_type)
+ right_sym := g.table.get_type_symbol(node.right_type)
+ if sym.kind == .interface_ && right_sym.kind == .interface_ {
+ g.gen_interface_is_op(node)
+ return
+ }
+
+ cmp_op := if node.op == .key_is { '==' } else { '!=' }
+ g.write('(')
+ g.expr(node.left)
+ g.write(')')
+ if node.left_type.is_ptr() {
+ g.write('->')
+ } else {
+ g.write('.')
+ }
+ if sym.kind == .interface_ {
+ g.write('_typ $cmp_op ')
+ // `_Animal_Dog_index`
+ sub_type := match mut node.right {
+ ast.TypeNode { node.right.typ }
+ ast.None { g.table.type_idxs['None__'] }
+ else { ast.Type(0) }
+ }
+ sub_sym := g.table.get_type_symbol(sub_type)
+ g.write('_${c_name(sym.name)}_${c_name(sub_sym.name)}_index')
+ return
+ } else if sym.kind == .sum_type {
+ g.write('_typ $cmp_op ')
+ }
+ g.expr(node.right)
+}
+
+fn (mut g Gen) gen_interface_is_op(node ast.InfixExpr) {
+ mut left_sym := g.table.get_type_symbol(node.left_type)
+ right_sym := g.table.get_type_symbol(node.right_type)
+
+ mut info := left_sym.info as ast.Interface
+
+ common_variants := info.conversions[node.right_type] or {
+ left_variants := g.table.iface_types[left_sym.name]
+ right_variants := g.table.iface_types[right_sym.name]
+ c := left_variants.filter(it in right_variants)
+ info.conversions[node.right_type] = c
+ c
+ }
+ left_sym.info = info
+ if common_variants.len == 0 {
+ g.write('false')
+ return
+ }
+ g.write('I_${left_sym.cname}_is_I_${right_sym.cname}(')
+ if node.left_type.is_ptr() {
+ g.write('*')
+ }
+ g.expr(node.left)
+ g.write(')')
+}
+
+// infix_expr_arithmetic_op generates code for `+`, `-`, `*`, `/`, and `%`
+// It handles operator overloading when necessary
+fn (mut g Gen) infix_expr_arithmetic_op(node ast.InfixExpr) {
+ left := g.unwrap(node.left_type)
+ right := g.unwrap(node.right_type)
+ method := g.table.type_find_method(left.sym, node.op.str()) or {
+ g.gen_plain_infix_expr(node)
+ return
+ }
+ left_styp := g.typ(left.typ.set_nr_muls(0))
+ g.write(left_styp)
+ g.write('_')
+ g.write(util.replace_op(node.op.str()))
+ g.write('(')
+ g.op_arg(node.left, method.params[0].typ, left.typ)
+ g.write(', ')
+ g.op_arg(node.right, method.params[1].typ, right.typ)
+ g.write(')')
+}
+
+// infix_expr_left_shift_op generates code for the `<<` operator
+// This can either be a value pushed into an array or a bit shift
+fn (mut g Gen) infix_expr_left_shift_op(node ast.InfixExpr) {
+ left := g.unwrap(node.left_type)
+ right := g.unwrap(node.right_type)
+ if left.unaliased_sym.kind == .array {
+ // arr << val
+ tmp_var := g.new_tmp_var()
+ array_info := left.unaliased_sym.info as ast.Array
+ noscan := g.check_noscan(array_info.elem_type)
+ //&& array_info.elem_type != g.unwrap_generic(node.right_type)
+ if right.unaliased_sym.kind == .array && array_info.elem_type != right.typ {
+ // push an array => PUSH_MANY, but not if pushing an array to 2d array (`[][]int << []int`)
+ g.write('_PUSH_MANY${noscan}(')
+ mut expected_push_many_atype := left.typ
+ if !expected_push_many_atype.is_ptr() {
+ // fn f(mut a []int) { a << [1,2,3] } -> type of `a` is `array_int*` -> no need for &
+ g.write('&')
+ } else {
+ expected_push_many_atype = expected_push_many_atype.deref()
+ }
+ g.expr(node.left)
+ g.write(', (')
+ g.expr_with_cast(node.right, node.right_type, left.unaliased)
+ styp := g.typ(expected_push_many_atype)
+ g.write('), $tmp_var, $styp)')
+ } else {
+ // push a single element
+ elem_type_str := g.typ(array_info.elem_type)
+ elem_sym := g.table.get_type_symbol(array_info.elem_type)
+ g.write('array_push${noscan}((array*)')
+ if !left.typ.is_ptr() {
+ g.write('&')
+ }
+ g.expr(node.left)
+ if elem_sym.kind == .function {
+ g.write(', _MOV((voidptr[]){ ')
+ } else {
+ g.write(', _MOV(($elem_type_str[]){ ')
+ }
+ // if g.autofree
+ needs_clone := array_info.elem_type.idx() == ast.string_type_idx && !g.is_builtin_mod
+ if needs_clone {
+ g.write('string_clone(')
+ }
+ if right.unaliased_sym.kind == .interface_ && node.right.is_auto_deref_var() {
+ g.write('*')
+ }
+ g.expr_with_cast(node.right, node.right_type, array_info.elem_type)
+ if needs_clone {
+ g.write(')')
+ }
+ g.write(' }))')
+ }
+ } else {
+ g.gen_plain_infix_expr(node)
+ }
+}
+
+// infix_expr_and_or_op generates code for `&&` and `||`
+fn (mut g Gen) infix_expr_and_or_op(node ast.InfixExpr) {
+ if node.right is ast.IfExpr {
+ // b := a && if true { a = false ...} else {...}
+ prev_inside_ternary := g.inside_ternary
+ g.inside_ternary = 0
+ if g.need_tmp_var_in_if(node.right) {
+ tmp := g.new_tmp_var()
+ cur_line := g.go_before_stmt(0).trim_space()
+ g.empty_line = true
+ g.write('bool $tmp = (')
+ g.expr(node.left)
+ g.writeln(');')
+ g.stmt_path_pos << g.out.len
+ g.write('$cur_line $tmp $node.op.str() ')
+ g.infix_left_var_name = if node.op == .and { tmp } else { '!$tmp' }
+ g.expr(node.right)
+ g.infix_left_var_name = ''
+ g.inside_ternary = prev_inside_ternary
+ return
+ }
+ g.inside_ternary = prev_inside_ternary
+ }
+ g.gen_plain_infix_expr(node)
+}
+
+// gen_plain_infix_expr generates basic code for infix expressions,
+// without any overloading of any kind
+// i.e. v`a + 1` => c`a + 1`
+// It handles auto dereferencing of variables, as well as automatic casting
+// (see Gen.expr_with_cast for more details)
+fn (mut g Gen) gen_plain_infix_expr(node ast.InfixExpr) {
+ if node.left_type.is_ptr() && node.left.is_auto_deref_var() {
+ g.write('*')
+ }
+ g.expr(node.left)
+ g.write(' $node.op.str() ')
+ g.expr_with_cast(node.right, node.right_type, node.left_type)
+}
+
+fn (mut g Gen) op_arg(expr ast.Expr, expected ast.Type, got ast.Type) {
+ mut needs_closing := false
+ mut nr_muls := got.nr_muls()
+ if expected.is_ptr() {
+ if nr_muls > 0 {
+ nr_muls--
+ } else {
+ if expr.is_lvalue() {
+ g.write('&')
+ } else {
+ styp := g.typ(got.set_nr_muls(0))
+ g.write('ADDR($styp, ')
+ needs_closing = true
+ }
+ }
+ }
+ g.write('*'.repeat(nr_muls))
+ g.expr(expr)
+ if needs_closing {
+ g.write(')')
+ }
+}
+
+struct GenSafeIntegerCfg {
+ op token.Kind
+ reverse bool
+ unsigned_type ast.Type
+ unsigned_expr ast.Expr
+ signed_type ast.Type
+ signed_expr ast.Expr
+}
+
+// gen_safe_integer_infix_expr generates code for comparison of
+// unsigned and signed integers
+fn (mut g Gen) gen_safe_integer_infix_expr(cfg GenSafeIntegerCfg) {
+ bitsize := if cfg.unsigned_type.idx() == ast.u32_type_idx
+ && cfg.signed_type.idx() != ast.i64_type_idx {
+ 32
+ } else {
+ 64
+ }
+ op_idx := int(cfg.op) - int(token.Kind.eq)
+ op_str := if cfg.reverse { cmp_rev[op_idx] } else { cmp_str[op_idx] }
+ g.write('_us${bitsize}_${op_str}(')
+ g.expr(cfg.unsigned_expr)
+ g.write(',')
+ g.expr(cfg.signed_expr)
+ g.write(')')
+}
diff --git a/v_windows/v/vlib/v/gen/c/json.v b/v_windows/v/vlib/v/gen/c/json.v
new file mode 100644
index 0000000..cfc480d
--- /dev/null
+++ b/v_windows/v/vlib/v/gen/c/json.v
@@ -0,0 +1,339 @@
+// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved.
+// Use of this source code is governed by an MIT license
+// that can be found in the LICENSE file.
+module c
+
+import v.ast
+import v.util
+import strings
+
+// TODO replace with comptime code generation.
+// TODO remove cJSON dependency.
+// OLD: User decode_User(string js) {
+// now it's
+// User decode_User(cJSON* root) {
+// User res;
+// res.name = decode_string(js_get(root, "name"));
+// res.profile = decode_Profile(js_get(root, "profile"));
+// return res;
+// }
+// Codegen json_decode/encode funcs
+fn (mut g Gen) gen_json_for_type(typ ast.Type) {
+ utyp := g.unwrap_generic(typ)
+ mut dec := strings.new_builder(100)
+ mut enc := strings.new_builder(100)
+ sym := g.table.get_type_symbol(utyp)
+ styp := g.typ(utyp)
+ if is_js_prim(sym.name) || sym.kind == .enum_ {
+ return
+ }
+ if sym.kind == .array {
+ // return
+ }
+ if sym.name in g.json_types {
+ return
+ }
+ g.json_types << sym.name
+ // println('gen_json_for_type($sym.name)')
+ // decode_TYPE funcs receive an actual cJSON* object to decode
+ // cJSON_Parse(str) call is added by the compiler
+ // Code gen decoder
+ dec_fn_name := js_dec_name(styp)
+ // Make sure that this optional type actually exists
+ g.register_optional(utyp)
+ dec_fn_dec := 'Option_$styp ${dec_fn_name}(cJSON* root)'
+ dec.writeln('
+$dec_fn_dec {
+ $styp res;
+ if (!root) {
+ const char *error_ptr = cJSON_GetErrorPtr();
+ if (error_ptr != NULL) {
+ // fprintf(stderr, "Error in decode() for $styp error_ptr=: %s\\n", error_ptr);
+ // printf("\\nbad js=%%s\\n", js.str);
+ return (Option_$styp){.state = 2,.err = _v_error(tos2((byteptr)error_ptr)),.data = {0}};
+ }
+ }
+')
+ g.json_forward_decls.writeln('$dec_fn_dec;')
+ // Code gen encoder
+ // encode_TYPE funcs receive an object to encode
+ enc_fn_name := js_enc_name(styp)
+ enc_fn_dec := 'cJSON* ${enc_fn_name}($styp val)'
+ g.json_forward_decls.writeln('$enc_fn_dec;\n')
+ enc.writeln('
+$enc_fn_dec {
+\tcJSON *o;')
+ if sym.kind == .array {
+ // Handle arrays
+ value_type := g.table.value_type(utyp)
+ // If we have `[]Profile`, have to register a Profile en(de)coder first
+ g.gen_json_for_type(value_type)
+ dec.writeln(g.decode_array(value_type))
+ enc.writeln(g.encode_array(value_type))
+ // enc += g.encode_array(t)
+ } else if sym.kind == .map {
+ // Handle maps
+ m := sym.info as ast.Map
+ g.gen_json_for_type(m.key_type)
+ g.gen_json_for_type(m.value_type)
+ dec.writeln(g.decode_map(m.key_type, m.value_type))
+ enc.writeln(g.encode_map(m.key_type, m.value_type))
+ } else if sym.kind == .alias {
+ a := sym.info as ast.Alias
+ parent_typ := a.parent_type
+ psym := g.table.get_type_symbol(parent_typ)
+ if is_js_prim(g.typ(parent_typ)) {
+ g.gen_json_for_type(parent_typ)
+ return
+ }
+ enc.writeln('\to = cJSON_CreateObject();')
+ if psym.info !is ast.Struct {
+ verror('json: $sym.name is not struct')
+ }
+ g.gen_struct_enc_dec(psym.info, styp, mut enc, mut dec)
+ } else {
+ enc.writeln('\to = cJSON_CreateObject();')
+ // Structs. Range through fields
+ if sym.info !is ast.Struct {
+ verror('json: $sym.name is not struct')
+ }
+ g.gen_struct_enc_dec(sym.info, styp, mut enc, mut dec)
+ }
+ // cJSON_delete
+ // p.cgen.fns << '$dec return opt_ok(res); \n}'
+ dec.writeln('\tOption_$styp ret;')
+ dec.writeln('\topt_ok(&res, (Option*)&ret, sizeof(res));')
+ dec.writeln('\treturn ret;\n}')
+ enc.writeln('\treturn o;\n}')
+ g.definitions.writeln(dec.str())
+ g.gowrappers.writeln(enc.str())
+}
+
+[inline]
+fn (mut g Gen) gen_struct_enc_dec(type_info ast.TypeInfo, styp string, mut enc strings.Builder, mut dec strings.Builder) {
+ info := type_info as ast.Struct
+ for field in info.fields {
+ mut name := field.name
+ mut is_raw := false
+ mut is_skip := false
+ mut is_required := false
+ for attr in field.attrs {
+ match attr.name {
+ 'json' {
+ name = attr.arg
+ }
+ 'skip' {
+ is_skip = true
+ }
+ 'raw' {
+ is_raw = true
+ }
+ 'required' {
+ is_required = true
+ }
+ else {}
+ }
+ }
+ if is_skip {
+ continue
+ }
+ field_type := g.typ(field.typ)
+ field_sym := g.table.get_type_symbol(field.typ)
+ // First generate decoding
+ if is_raw {
+ dec.writeln('\tres.${c_name(field.name)} = tos5(cJSON_PrintUnformatted(' +
+ 'js_get(root, "$name")));')
+ } else {
+ // Now generate decoders for all field types in this struct
+ // need to do it here so that these functions are generated first
+ g.gen_json_for_type(field.typ)
+ dec_name := js_dec_name(field_type)
+ if is_js_prim(field_type) {
+ tmp := g.new_tmp_var()
+ gen_js_get(styp, tmp, name, mut dec, is_required)
+ dec.writeln('\tres.${c_name(field.name)} = $dec_name (jsonroot_$tmp);')
+ } else if field_sym.kind == .enum_ {
+ tmp := g.new_tmp_var()
+ gen_js_get(styp, tmp, name, mut dec, is_required)
+ dec.writeln('\tres.${c_name(field.name)} = json__decode_u64(jsonroot_$tmp);')
+ } else if field_sym.name == 'time.Time' {
+ // time struct requires special treatment
+ // it has to be decoded from a unix timestamp number
+ tmp := g.new_tmp_var()
+ gen_js_get(styp, tmp, name, mut dec, is_required)
+ dec.writeln('\tres.${c_name(field.name)} = time__unix(json__decode_u64(jsonroot_$tmp));')
+ } else if field_sym.kind == .alias {
+ alias := field_sym.info as ast.Alias
+ parent_type := g.typ(alias.parent_type)
+ parent_dec_name := js_dec_name(parent_type)
+ if is_js_prim(parent_type) {
+ tmp := g.new_tmp_var()
+ gen_js_get(styp, tmp, name, mut dec, is_required)
+ dec.writeln('\tres.${c_name(field.name)} = $parent_dec_name (jsonroot_$tmp);')
+ } else {
+ g.gen_json_for_type(field.typ)
+ tmp := g.new_tmp_var()
+ gen_js_get_opt(dec_name, field_type, styp, tmp, name, mut dec, is_required)
+ dec.writeln('\tres.${c_name(field.name)} = *($field_type*) ${tmp}.data;')
+ }
+ } else {
+ tmp := g.new_tmp_var()
+ gen_js_get_opt(dec_name, field_type, styp, tmp, name, mut dec, is_required)
+ dec.writeln('\tres.${c_name(field.name)} = *($field_type*) ${tmp}.data;')
+ }
+ }
+ // Encoding
+ mut enc_name := js_enc_name(field_type)
+ if !is_js_prim(field_type) {
+ if field_sym.kind == .alias {
+ ainfo := field_sym.info as ast.Alias
+ enc_name = js_enc_name(g.typ(ainfo.parent_type))
+ }
+ }
+ if field_sym.kind == .enum_ {
+ enc.writeln('\tcJSON_AddItemToObject(o, "$name", json__encode_u64(val.${c_name(field.name)}));\n')
+ } else {
+ if field_sym.name == 'time.Time' {
+ // time struct requires special treatment
+ // it has to be encoded as a unix timestamp number
+ enc.writeln('\tcJSON_AddItemToObject(o, "$name", json__encode_u64(val.${c_name(field.name)}._v_unix));')
+ } else {
+ enc.writeln('\tcJSON_AddItemToObject(o, "$name", ${enc_name}(val.${c_name(field.name)}));\n')
+ }
+ }
+ }
+}
+
+fn gen_js_get(styp string, tmp string, name string, mut dec strings.Builder, is_required bool) {
+ dec.writeln('\tcJSON *jsonroot_$tmp = js_get(root,"$name");')
+ if is_required {
+ dec.writeln('\tif(jsonroot_$tmp == 0) {')
+ dec.writeln('\t\treturn (Option_$styp){ .state = 2, .err = _v_error(_SLIT("expected field \'$name\' is missing")), .data = {0} };')
+ dec.writeln('\t}')
+ }
+}
+
+fn gen_js_get_opt(dec_name string, field_type string, styp string, tmp string, name string, mut dec strings.Builder, is_required bool) {
+ gen_js_get(styp, tmp, name, mut dec, is_required)
+ dec.writeln('\tOption_$field_type $tmp = $dec_name (jsonroot_$tmp);')
+ dec.writeln('\tif(${tmp}.state != 0) {')
+ dec.writeln('\t\treturn (Option_$styp){ .state = ${tmp}.state, .err = ${tmp}.err, .data = {0} };')
+ dec.writeln('\t}')
+}
+
+fn js_enc_name(typ string) string {
+ suffix := if typ.ends_with('*') { typ.replace('*', '') } else { typ }
+ name := 'json__encode_$suffix'
+ return util.no_dots(name)
+}
+
+fn js_dec_name(typ string) string {
+ name := 'json__decode_$typ'
+ return util.no_dots(name)
+}
+
+fn is_js_prim(typ string) bool {
+ return typ in ['int', 'string', 'bool', 'f32', 'f64', 'i8', 'i16', 'i64', 'u16', 'u32', 'u64',
+ 'byte',
+ ]
+}
+
+fn (mut g Gen) decode_array(value_type ast.Type) string {
+ styp := g.typ(value_type)
+ fn_name := js_dec_name(styp)
+ mut s := ''
+ if is_js_prim(styp) {
+ s = '$styp val = ${fn_name}((cJSON *)jsval); '
+ } else {
+ s = '
+ Option_$styp val2 = $fn_name ((cJSON *)jsval);
+ if(val2.state != 0) {
+ array_free(&res);
+ return *(Option_Array_$styp*)&val2;
+ }
+ $styp val = *($styp*)val2.data;
+'
+ }
+ noscan := g.check_noscan(value_type)
+ return '
+ if(root && !cJSON_IsArray(root) && !cJSON_IsNull(root)) {
+ return (Option_Array_$styp){.state = 2, .err = _v_error(string__plus(_SLIT("Json element is not an array: "), tos2((byteptr)cJSON_PrintUnformatted(root)))), .data = {0}};
+ }
+ res = __new_array${noscan}(0, 0, sizeof($styp));
+ const cJSON *jsval = NULL;
+ cJSON_ArrayForEach(jsval, root)
+ {
+ $s
+ array_push${noscan}((array*)&res, &val);
+ }
+'
+}
+
+fn (mut g Gen) encode_array(value_type ast.Type) string {
+ styp := g.typ(value_type)
+ fn_name := js_enc_name(styp)
+ return '
+ o = cJSON_CreateArray();
+ for (int i = 0; i < val.len; i++){
+ cJSON_AddItemToArray(o, $fn_name ( (($styp*)val.data)[i] ));
+ }
+'
+}
+
+fn (mut g Gen) decode_map(key_type ast.Type, value_type ast.Type) string {
+ styp := g.typ(key_type)
+ styp_v := g.typ(value_type)
+ key_type_symbol := g.table.get_type_symbol(key_type)
+ hash_fn, key_eq_fn, clone_fn, free_fn := g.map_fn_ptrs(key_type_symbol)
+ fn_name_v := js_dec_name(styp_v)
+ mut s := ''
+ if is_js_prim(styp_v) {
+ s = '$styp_v val = $fn_name_v (js_get(root, jsval->string));'
+ } else {
+ s = '
+ Option_$styp_v val2 = $fn_name_v (js_get(root, jsval->string));
+ if(val2.state != 0) {
+ map_free(&res);
+ return *(Option_Map_${styp}_$styp_v*)&val2;
+ }
+ $styp_v val = *($styp_v*)val2.data;
+'
+ }
+ return '
+ if(!cJSON_IsObject(root) && !cJSON_IsNull(root)) {
+ return (Option_Map_${styp}_$styp_v){ .state = 2, .err = _v_error(string__plus(_SLIT("Json element is not an object: "), tos2((byteptr)cJSON_PrintUnformatted(root)))), .data = {0}};
+ }
+ res = new_map(sizeof($styp), sizeof($styp_v), $hash_fn, $key_eq_fn, $clone_fn, $free_fn);
+ cJSON *jsval = NULL;
+ cJSON_ArrayForEach(jsval, root)
+ {
+ $s
+ string key = tos2((byteptr)jsval->string);
+ map_set(&res, &key, &val);
+ }
+'
+}
+
+fn (mut g Gen) encode_map(key_type ast.Type, value_type ast.Type) string {
+ styp := g.typ(key_type)
+ styp_v := g.typ(value_type)
+ fn_name_v := js_enc_name(styp_v)
+ zero := g.type_default(value_type)
+ keys_tmp := g.new_tmp_var()
+ mut key := 'string key = '
+ if key_type.is_string() {
+ key += '(($styp*)${keys_tmp}.data)[i];'
+ } else {
+ // key += '${styp}_str((($styp*)${keys_tmp}.data)[i]);'
+ verror('json: encode only maps with string keys')
+ }
+ return '
+ o = cJSON_CreateObject();
+ Array_$styp $keys_tmp = map_keys(&val);
+ for (int i = 0; i < ${keys_tmp}.len; ++i) {
+ $key
+ cJSON_AddItemToObject(o, (char*) key.str, $fn_name_v ( *($styp_v*) map_get(&val, &key, &($styp_v[]) { $zero } ) ) );
+ }
+ array_free(&$keys_tmp);
+'
+}
diff --git a/v_windows/v/vlib/v/gen/c/live.v b/v_windows/v/vlib/v/gen/c/live.v
new file mode 100644
index 0000000..69709f9
--- /dev/null
+++ b/v_windows/v/vlib/v/gen/c/live.v
@@ -0,0 +1,104 @@
+module c
+
+import v.pref
+import v.util
+
+fn (mut g Gen) generate_hotcode_reloading_declarations() {
+ if g.pref.os == .windows {
+ if g.pref.is_livemain {
+ g.hotcode_definitions.writeln('HANDLE live_fn_mutex = 0;')
+ }
+ if g.pref.is_liveshared {
+ g.hotcode_definitions.writeln('HANDLE live_fn_mutex;')
+ }
+ g.hotcode_definitions.writeln('
+void pthread_mutex_lock(HANDLE *m) {
+ WaitForSingleObject(*m, INFINITE);
+}
+void pthread_mutex_unlock(HANDLE *m) {
+ ReleaseMutex(*m);
+}
+')
+ } else {
+ if g.pref.is_livemain {
+ g.hotcode_definitions.writeln('pthread_mutex_t live_fn_mutex = PTHREAD_MUTEX_INITIALIZER;')
+ }
+ if g.pref.is_liveshared {
+ g.hotcode_definitions.writeln('pthread_mutex_t live_fn_mutex;')
+ }
+ }
+}
+
+fn (mut g Gen) generate_hotcode_reloader_code() {
+ if g.pref.is_liveshared {
+ g.hotcode_definitions.writeln('')
+ return
+ }
+ // Hot code reloading
+ if g.pref.is_livemain {
+ mut phd := ''
+ mut load_code := []string{}
+ if g.pref.os != .windows {
+ for so_fn in g.hotcode_fn_names {
+ load_code << 'impl_live_$so_fn = dlsym(live_lib, "impl_live_$so_fn");'
+ }
+ phd = c.posix_hotcode_definitions_1
+ } else {
+ for so_fn in g.hotcode_fn_names {
+ load_code << 'impl_live_$so_fn = (void *)GetProcAddress(live_lib, "impl_live_$so_fn"); '
+ }
+ phd = c.windows_hotcode_definitions_1
+ }
+ g.hotcode_definitions.writeln(phd.replace('@LOAD_FNS@', load_code.join('\n')))
+ }
+}
+
+const (
+ posix_hotcode_definitions_1 = '
+void v_bind_live_symbols(void* live_lib){
+ @LOAD_FNS@
+}
+'
+ windows_hotcode_definitions_1 = '
+void v_bind_live_symbols(void* live_lib){
+ @LOAD_FNS@
+}
+'
+)
+
+fn (mut g Gen) generate_hotcode_reloading_main_caller() {
+ if !g.pref.is_livemain {
+ return
+ }
+ g.writeln('')
+ // We are in live code reload mode, so start the .so loader in the background
+ g.writeln('\t// live code initialization section:')
+ g.writeln('\t{')
+ g.writeln('\t\t// initialization of live function pointers')
+ for fname in g.hotcode_fn_names {
+ g.writeln('\t\timpl_live_$fname = 0;')
+ }
+ vexe := util.cescaped_path(pref.vexe_path())
+ file := util.cescaped_path(g.pref.path)
+ msvc := if g.pref.ccompiler == 'msvc' { '-cc msvc' } else { '' }
+ so_debug_flag := if g.pref.is_debug { '-cg' } else { '' }
+ vopts := '$msvc $so_debug_flag -sharedlive -shared'
+ //
+ g.writeln('\t\t// start background reloading thread')
+ if g.pref.os == .windows {
+ g.writeln('\t\tlive_fn_mutex = CreateMutexA(0, 0, 0);')
+ }
+ g.writeln('\t\tv__live__LiveReloadInfo* live_info = v__live__executable__new_live_reload_info(')
+ g.writeln('\t\t\t\t\t tos2("$file"),')
+ g.writeln('\t\t\t\t\t tos2("$vexe"),')
+ g.writeln('\t\t\t\t\t tos2("$vopts"),')
+ g.writeln('\t\t\t\t\t &live_fn_mutex,')
+ g.writeln('\t\t\t\t\t v_bind_live_symbols')
+ g.writeln('\t\t);')
+ // g_live_info gives access to the LiveReloadInfo methods,
+ // to the custom user code, through calling v_live_info()
+ g.writeln('\t\t g_live_info = (void*)live_info;')
+ g.writeln('\t\tv__live__executable__start_reloader(live_info);')
+ g.writeln('\t}\t// end of live code initialization section')
+ g.writeln('')
+}
diff --git a/v_windows/v/vlib/v/gen/c/profile.v b/v_windows/v/vlib/v/gen/c/profile.v
new file mode 100644
index 0000000..602c7b0
--- /dev/null
+++ b/v_windows/v/vlib/v/gen/c/profile.v
@@ -0,0 +1,52 @@
+module c
+
+import v.ast
+
+pub struct ProfileCounterMeta {
+ fn_name string
+ vpc_name string
+ vpc_calls string
+}
+
+fn (mut g Gen) profile_fn(fn_decl ast.FnDecl) {
+ if g.pref.profile_no_inline && fn_decl.attrs.contains('inline') {
+ g.defer_profile_code = ''
+ return
+ }
+ fn_name := fn_decl.name
+ if fn_name.starts_with('time.vpc_now') {
+ g.defer_profile_code = ''
+ } else {
+ measure_fn_name := if g.pref.os == .macos { 'time__vpc_now_darwin' } else { 'time__vpc_now' }
+ fn_profile_counter_name := 'vpc_$g.last_fn_c_name'
+ fn_profile_counter_name_calls := '${fn_profile_counter_name}_calls'
+ g.writeln('')
+ g.writeln('\tdouble _PROF_FN_START = ${measure_fn_name}(); $fn_profile_counter_name_calls++; // $fn_name')
+ g.writeln('')
+ g.defer_profile_code = '\t$fn_profile_counter_name += ${measure_fn_name}() - _PROF_FN_START;'
+ g.pcs_declarations.writeln('double $fn_profile_counter_name = 0.0; u64 $fn_profile_counter_name_calls = 0;')
+ g.pcs << ProfileCounterMeta{
+ fn_name: g.last_fn_c_name
+ vpc_name: fn_profile_counter_name
+ vpc_calls: fn_profile_counter_name_calls
+ }
+ }
+}
+
+pub fn (mut g Gen) gen_vprint_profile_stats() {
+ g.pcs_declarations.writeln('void vprint_profile_stats(){')
+ fstring := '"%14llu %14.3fms %14.0fns %s \\n"'
+ if g.pref.profile_file == '-' {
+ for pc_meta in g.pcs {
+ g.pcs_declarations.writeln('\tif ($pc_meta.vpc_calls) printf($fstring, $pc_meta.vpc_calls, $pc_meta.vpc_name/1000000.0, $pc_meta.vpc_name/$pc_meta.vpc_calls, "$pc_meta.fn_name" );')
+ }
+ } else {
+ g.pcs_declarations.writeln('\tFILE * fp;')
+ g.pcs_declarations.writeln('\tfp = fopen ("$g.pref.profile_file", "w+");')
+ for pc_meta in g.pcs {
+ g.pcs_declarations.writeln('\tif ($pc_meta.vpc_calls) fprintf(fp, $fstring, $pc_meta.vpc_calls, $pc_meta.vpc_name/1000000.0, $pc_meta.vpc_name/$pc_meta.vpc_calls, "$pc_meta.fn_name" );')
+ }
+ g.pcs_declarations.writeln('\tfclose(fp);')
+ }
+ g.pcs_declarations.writeln('}')
+}
diff --git a/v_windows/v/vlib/v/gen/c/sql.v b/v_windows/v/vlib/v/gen/c/sql.v
new file mode 100644
index 0000000..001f68c
--- /dev/null
+++ b/v_windows/v/vlib/v/gen/c/sql.v
@@ -0,0 +1,836 @@
+// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved.
+// Use of this source code is governed by an MIT license that can be found in the LICENSE file.
+module c
+
+import v.ast
+import v.util
+
+enum SqlExprSide {
+ left
+ right
+}
+
+enum SqlType {
+ sqlite3
+ mysql
+ psql
+ mssql
+ unknown
+}
+
+fn (mut g Gen) sql_stmt(node ast.SqlStmt) {
+ conn := g.new_tmp_var()
+ g.writeln('')
+ g.writeln('// orm')
+ g.write('orm__Connection $conn = (orm__Connection){._')
+ mut fn_prefix := ''
+ typ := g.parse_db_type(node.db_expr)
+ match typ {
+ .sqlite3 {
+ fn_prefix = 'sqlite__DB'
+ }
+ .mysql {
+ fn_prefix = 'mysql__Connection'
+ }
+ .psql {
+ fn_prefix = 'pg__DB'
+ }
+ else {
+ verror('This database type `$typ` is not implemented yet in orm') // TODO add better error
+ }
+ }
+ g.write('$fn_prefix = &')
+ g.expr(node.db_expr)
+ g.writeln(', ._typ = _orm__Connection_${fn_prefix}_index};')
+ for line in node.lines {
+ g.sql_stmt_line(line, conn)
+ }
+}
+
+fn (mut g Gen) sql_stmt_line(nd ast.SqlStmtLine, expr string) {
+ mut node := nd
+ table_name := g.get_table_name(node.table_expr)
+ g.sql_table_name = g.table.get_type_symbol(node.table_expr.typ).name
+ res := g.new_tmp_var()
+ mut subs := false
+ mut dcheck := false
+
+ if node.kind != .create {
+ mut fields := []ast.StructField{}
+ for f in node.fields {
+ mut skip := false
+ mut primary := false
+ for attr in f.attrs {
+ if attr.name == 'primary' {
+ primary = true
+ }
+ if attr.name == 'skip' {
+ skip = true
+ }
+ }
+ if !skip && !primary {
+ fields << f
+ }
+ }
+ node.fields = fields.clone()
+ unsafe { fields.free() }
+ }
+
+ if node.kind == .create {
+ g.write('Option_void $res = orm__Connection_name_table[${expr}._typ]._method_')
+ g.sql_create_table(node, expr, table_name)
+ subs = true
+ } else if node.kind == .drop {
+ g.write('Option_void $res = orm__Connection_name_table[${expr}._typ]._method_')
+ g.writeln('drop(${expr}._object, _SLIT("$table_name"));')
+ subs = true
+ } else if node.kind == .insert {
+ arr := g.new_tmp_var()
+ g.writeln('Array_orm__Primitive $arr = new_array_from_c_array(0, 0, sizeof(orm__Primitive), NULL);')
+ g.sql_insert(node, expr, table_name, arr, res, '', false, '')
+ dcheck = true
+ } else if node.kind == .update {
+ g.write('Option_void $res = orm__Connection_name_table[${expr}._typ]._method_')
+ g.sql_update(node, expr, table_name)
+ } else if node.kind == .delete {
+ g.write('Option_void $res = orm__Connection_name_table[${expr}._typ]._method_')
+ g.sql_delete(node, expr, table_name)
+ }
+ if !dcheck {
+ g.writeln('if (${res}.state != 0 && ${res}.err._typ != _IError_None___index) { _v_panic(IError_str(${res}.err)); }')
+ }
+ if subs {
+ for _, sub in node.sub_structs {
+ g.sql_stmt_line(sub, expr)
+ }
+ }
+}
+
+fn (mut g Gen) sql_create_table(node ast.SqlStmtLine, expr string, table_name string) {
+ g.write('create(${expr}._object, _SLIT("$table_name"), new_array_from_c_array($node.fields.len, $node.fields.len, sizeof(orm__TableField),')
+ if node.fields.len > 0 {
+ g.write(' _MOV((orm__TableField[$node.fields.len]){')
+ for field in node.fields {
+ sym := g.table.get_type_symbol(field.typ)
+ g.write('(orm__TableField){')
+ g.write('.name = _SLIT("$field.name"),')
+ mut typ := int(field.typ)
+ if sym.name == 'time.Time' {
+ typ = -2
+ }
+ g.write('.typ = $typ,')
+ g.write('.is_arr = ${sym.kind == .array}, ')
+ g.write('.is_time = ${int(g.table.get_type_name(field.typ) == 'time__Time')},')
+ g.write('.default_val = (string){.str = (byteptr) "$field.default_val", .is_lit = 1},')
+ g.write('.attrs = new_array_from_c_array($field.attrs.len, $field.attrs.len, sizeof(StructAttribute),')
+ if field.attrs.len > 0 {
+ g.write(' _MOV((StructAttribute[$field.attrs.len]){')
+ for attr in field.attrs {
+ g.write('(StructAttribute){')
+ g.write('.name = _SLIT("$attr.name"),')
+ g.write('.has_arg = ${int(attr.has_arg)},')
+ g.write('.arg = _SLIT("$attr.arg"),')
+ g.write('.kind = ${int(attr.kind)},')
+ g.write('},')
+ }
+ g.write('})')
+ } else {
+ g.write('NULL')
+ }
+ g.write(')')
+ g.write('},')
+ }
+ g.write('})')
+ } else {
+ g.write('NULL')
+ }
+ g.writeln('));')
+}
+
+fn (mut g Gen) sql_insert(node ast.SqlStmtLine, expr string, table_name string, last_ids_arr string, res string, pid string, is_array bool, fkey string) {
+ mut subs := []ast.SqlStmtLine{}
+ mut arrs := []ast.SqlStmtLine{}
+ mut fkeys := []string{}
+ mut field_names := []string{}
+
+ for f in node.fields {
+ sym := g.table.get_type_symbol(f.typ)
+ if sym.kind == .struct_ && sym.name != 'time.Time' {
+ subs << node.sub_structs[int(f.typ)]
+ } else if sym.kind == .array {
+ mut f_key := ''
+ for attr in f.attrs {
+ if attr.name == 'fkey' && attr.has_arg && attr.kind == .string {
+ f_key = attr.arg
+ }
+ }
+ if f_key == '' {
+ verror('An field which holds an array, needs a fkey defined')
+ }
+ fkeys << f_key
+ info := sym.array_info()
+ if info.nr_dims == 1 {
+ arrs << node.sub_structs[int(info.elem_type)]
+ field_names << f.name
+ } else {
+ verror('V ORM only supports 1 dimensional arrays')
+ }
+ }
+ }
+
+ fields := node.fields.filter(g.table.get_type_symbol(it.typ).kind != .array)
+
+ for sub in subs {
+ g.sql_stmt_line(sub, expr)
+ g.writeln('array_push(&$last_ids_arr, _MOV((orm__Primitive[]){orm__Connection_name_table[${expr}._typ]._method_last_id(${expr}._object)}));')
+ }
+
+ g.write('Option_void $res = orm__Connection_name_table[${expr}._typ]._method_')
+ g.write('insert(${expr}._object, _SLIT("$table_name"), (orm__QueryData){')
+
+ g.write('.fields = new_array_from_c_array($fields.len, $fields.len, sizeof(string),')
+ if fields.len > 0 {
+ g.write('_MOV((string[$fields.len]){')
+ for f in fields {
+ g.write('_SLIT("${g.get_field_name(f)}"),')
+ }
+ g.write('})')
+ } else {
+ g.write('NULL')
+ }
+ g.write('),')
+
+ g.write('.data = new_array_from_c_array($fields.len, $fields.len, sizeof(orm__Primitive),')
+ if fields.len > 0 {
+ g.write(' _MOV((orm__Primitive[$fields.len]){')
+ mut structs := 0
+ for f in fields {
+ if f.name == fkey {
+ g.write('$pid, ')
+ continue
+ }
+ mut sym := g.table.get_type_symbol(f.typ)
+ mut typ := sym.cname
+ if sym.kind == .struct_ && typ != 'time__Time' {
+ g.write('(*(orm__Primitive*) array_get($last_ids_arr, $structs)),')
+ structs++
+ continue
+ }
+ if typ == 'time__Time' {
+ typ = 'time'
+ }
+ g.write('orm__${typ}_to_primitive(${node.object_var_name}.$f.name),')
+ }
+ g.write('})')
+ } else {
+ g.write('NULL')
+ }
+ g.write('),')
+ g.write('.types = new_array_from_c_array(0, 0, sizeof(int), NULL),')
+ g.write('.kinds = new_array_from_c_array(0, 0, sizeof(orm__OperationKind), NULL),')
+ g.write('.is_and = new_array_from_c_array(0, 0, sizeof(bool), NULL),')
+ g.writeln('});')
+
+ g.writeln('if (${res}.state != 0 && ${res}.err._typ != _IError_None___index) { _v_panic(IError_str(${res}.err)); }')
+ if arrs.len > 0 {
+ mut id_name := g.new_tmp_var()
+ g.writeln('orm__Primitive $id_name = orm__Connection_name_table[${expr}._typ]._method_last_id(${expr}._object);')
+ for i, mut arr in arrs {
+ idx := g.new_tmp_var()
+ g.writeln('for (int $idx = 0; $idx < ${arr.object_var_name}.${field_names[i]}.len; $idx++) {')
+ last_ids := g.new_tmp_var()
+ res_ := g.new_tmp_var()
+ tmp_var := g.new_tmp_var()
+ ctyp := g.typ(arr.table_expr.typ)
+ g.writeln('$ctyp $tmp_var = (*($ctyp*)array_get(${arr.object_var_name}.${field_names[i]}, $idx));')
+ arr.object_var_name = tmp_var
+ mut fff := []ast.StructField{}
+ for f in arr.fields {
+ mut skip := false
+ mut primary := false
+ for attr in f.attrs {
+ if attr.name == 'primary' {
+ primary = true
+ }
+ if attr.name == 'skip' {
+ skip = true
+ }
+ }
+ if !skip && !primary {
+ fff << f
+ }
+ }
+ arr.fields = fff.clone()
+ unsafe { fff.free() }
+ g.sql_insert(arr, expr, g.get_table_name(arr.table_expr), last_ids, res_,
+ id_name, true, fkeys[i])
+ g.writeln('}')
+ }
+ }
+}
+
+fn (mut g Gen) sql_update(node ast.SqlStmtLine, expr string, table_name string) {
+ g.write('update(${expr}._object, _SLIT("$table_name"), (orm__QueryData){')
+ g.write('.kinds = new_array_from_c_array(0, 0, sizeof(orm__OperationKind), NULL),')
+ g.write('.is_and = new_array_from_c_array(0, 0, sizeof(bool), NULL),')
+ g.write('.types = new_array_from_c_array(0, 0, sizeof(int), NULL),')
+ g.write('.fields = new_array_from_c_array($node.updated_columns.len, $node.updated_columns.len, sizeof(string),')
+ if node.updated_columns.len > 0 {
+ g.write(' _MOV((string[$node.updated_columns.len]){')
+ for field in node.updated_columns {
+ g.write('_SLIT("$field"),')
+ }
+ g.write('})')
+ } else {
+ g.write('NULL')
+ }
+ g.write('),')
+ g.write('.data = new_array_from_c_array($node.update_exprs.len, $node.update_exprs.len, sizeof(orm__Primitive),')
+ if node.update_exprs.len > 0 {
+ g.write(' _MOV((orm__Primitive[$node.update_exprs.len]){')
+ for e in node.update_exprs {
+ g.sql_expr_to_orm_primitive(e)
+ }
+ g.write('})')
+ }
+ g.write('),},')
+ g.sql_gen_where_data(node.where_expr)
+ g.writeln(');')
+}
+
+fn (mut g Gen) sql_delete(node ast.SqlStmtLine, expr string, table_name string) {
+ g.write('_v_delete(${expr}._object, _SLIT("$table_name"),')
+ g.sql_gen_where_data(node.where_expr)
+ g.writeln(');')
+}
+
+fn (mut g Gen) sql_expr_to_orm_primitive(expr ast.Expr) {
+ match expr {
+ ast.InfixExpr {
+ g.sql_write_orm_primitive(g.table.find_type_idx('orm.InfixType'), expr)
+ }
+ ast.StringLiteral {
+ g.sql_write_orm_primitive(ast.string_type, expr)
+ }
+ ast.IntegerLiteral {
+ g.sql_write_orm_primitive(ast.int_type, expr)
+ }
+ ast.BoolLiteral {
+ g.sql_write_orm_primitive(ast.bool_type, expr)
+ }
+ ast.Ident {
+ info := expr.info as ast.IdentVar
+ g.sql_write_orm_primitive(info.typ, expr)
+ }
+ ast.SelectorExpr {
+ g.sql_write_orm_primitive(expr.typ, expr)
+ }
+ else {
+ eprintln(expr)
+ verror('Unknown expr')
+ }
+ }
+}
+
+fn (mut g Gen) sql_write_orm_primitive(t ast.Type, expr ast.Expr) {
+ mut sym := g.table.get_type_symbol(t)
+ mut typ := sym.cname
+ if typ == 'orm__Primitive' {
+ g.expr(expr)
+ g.write(',')
+ return
+ }
+ if typ == 'time__Time' {
+ typ = 'time'
+ }
+ if typ == 'orm__InfixType' {
+ typ = 'infix'
+ }
+ g.write('orm__${typ}_to_primitive(')
+ if expr is ast.InfixExpr {
+ g.write('(orm__InfixType){')
+ g.write('.name = _SLIT("$expr.left"),')
+ mut kind := match expr.op {
+ .plus {
+ 'orm__MathOperationKind__add'
+ }
+ .minus {
+ 'orm__MathOperationKind__sub'
+ }
+ .div {
+ 'orm__MathOperationKind__div'
+ }
+ .mul {
+ 'orm__MathOperationKind__mul'
+ }
+ else {
+ ''
+ }
+ }
+ g.write('.operator = $kind,')
+ g.write('.right = ')
+ g.sql_expr_to_orm_primitive(expr.right)
+ g.write('}')
+ } else {
+ g.expr(expr)
+ }
+ g.write('),')
+}
+
+fn (mut g Gen) sql_where_data(expr ast.Expr, mut fields []string, mut kinds []string, mut data []ast.Expr, mut is_and []bool) {
+ match expr {
+ ast.InfixExpr {
+ g.sql_side = .left
+ g.sql_where_data(expr.left, mut fields, mut kinds, mut data, mut is_and)
+ mut kind := match expr.op {
+ .ne {
+ 'orm__OperationKind__neq'
+ }
+ .eq {
+ 'orm__OperationKind__eq'
+ }
+ .lt {
+ 'orm__OperationKind__lt'
+ }
+ .gt {
+ 'orm__OperationKind__gt'
+ }
+ .ge {
+ 'orm__OperationKind__ge'
+ }
+ .le {
+ 'orm__OperationKind__le'
+ }
+ else {
+ ''
+ }
+ }
+ if kind == '' {
+ if expr.op == .logical_or {
+ is_and << false
+ } else if expr.op == .and {
+ is_and << true
+ } else {
+ kind = 'orm__OperationKind__eq'
+ }
+ }
+ if expr.left !is ast.InfixExpr && expr.right !is ast.InfixExpr {
+ kinds << kind
+ }
+ g.sql_side = .right
+ g.sql_where_data(expr.right, mut fields, mut kinds, mut data, mut is_and)
+ }
+ ast.Ident {
+ if g.sql_side == .left {
+ fields << g.get_field_name(g.get_struct_field(expr.name))
+ } else {
+ data << expr
+ }
+ }
+ ast.StringLiteral {
+ data << expr
+ }
+ ast.IntegerLiteral {
+ data << expr
+ }
+ ast.SelectorExpr {
+ data << expr
+ }
+ ast.BoolLiteral {
+ data << expr
+ }
+ else {}
+ }
+}
+
+fn (mut g Gen) sql_gen_where_data(where_expr ast.Expr) {
+ g.write('(orm__QueryData){')
+ mut fields := []string{}
+ mut kinds := []string{}
+ mut data := []ast.Expr{}
+ mut is_and := []bool{}
+ g.sql_where_data(where_expr, mut fields, mut kinds, mut data, mut is_and)
+ g.write('.types = new_array_from_c_array(0, 0, sizeof(int), NULL),')
+ g.write('.fields = new_array_from_c_array($fields.len, $fields.len, sizeof(string),')
+ if fields.len > 0 {
+ g.write(' _MOV((string[$fields.len]){')
+ for field in fields {
+ g.write('_SLIT("$field"),')
+ }
+ g.write('})')
+ } else {
+ g.write('NULL')
+ }
+ g.write('),')
+
+ g.write('.data = new_array_from_c_array($data.len, $data.len, sizeof(orm__Primitive),')
+ if data.len > 0 {
+ g.write(' _MOV((orm__Primitive[$data.len]){')
+ for e in data {
+ g.sql_expr_to_orm_primitive(e)
+ }
+ g.write('})')
+ }
+ g.write('),')
+
+ g.write('.kinds = new_array_from_c_array($kinds.len, $kinds.len, sizeof(orm__OperationKind),')
+ if kinds.len > 0 {
+ g.write(' _MOV((orm__OperationKind[$kinds.len]){')
+ for k in kinds {
+ g.write('$k,')
+ }
+ g.write('})')
+ } else {
+ g.write('NULL')
+ }
+ g.write('),')
+
+ g.write('.is_and = new_array_from_c_array($is_and.len, $is_and.len, sizeof(bool),')
+ if is_and.len > 0 {
+ g.write(' _MOV((bool[$is_and.len]){')
+ for b in is_and {
+ g.write('$b, ')
+ }
+ g.write('})')
+ } else {
+ g.write('NULL')
+ }
+ g.write('),}')
+}
+
+fn (mut g Gen) sql_select_expr(node ast.SqlExpr) {
+ left := g.go_before_stmt(0)
+ conn := g.new_tmp_var()
+ g.writeln('')
+ g.writeln('// orm')
+ g.write('orm__Connection $conn = (orm__Connection){._')
+ mut fn_prefix := ''
+ typ := g.parse_db_type(node.db_expr)
+ match typ {
+ .sqlite3 {
+ fn_prefix = 'sqlite__DB'
+ }
+ .mysql {
+ fn_prefix = 'mysql__Connection'
+ }
+ .psql {
+ fn_prefix = 'pg__DB'
+ }
+ else {
+ verror('This database type `$typ` is not implemented yet in orm') // TODO add better error
+ }
+ }
+
+ g.write('$fn_prefix = &')
+ g.expr(node.db_expr)
+ g.writeln(', ._typ = _orm__Connection_${fn_prefix}_index};')
+ g.sql_select(node, conn, left)
+}
+
+fn (mut g Gen) sql_select(node ast.SqlExpr, expr string, left string) {
+ mut fields := []ast.StructField{}
+ mut prim := ''
+ for f in node.fields {
+ mut skip := false
+ for attr in f.attrs {
+ if attr.name == 'primary' {
+ prim = f.name
+ }
+ if attr.name == 'skip' {
+ skip = true
+ }
+ }
+ if !skip {
+ fields << f
+ }
+ }
+
+ res := g.new_tmp_var()
+ table_name := g.get_table_name(node.table_expr)
+ g.sql_table_name = g.table.get_type_symbol(node.table_expr.typ).name
+ g.write('Option_Array_Array_orm__Primitive _o$res = orm__Connection_name_table[${expr}._typ]._method_select(${expr}._object, ')
+ g.write('(orm__SelectConfig){')
+ g.write('.table = _SLIT("$table_name"),')
+ g.write('.is_count = $node.is_count,')
+ g.write('.has_where = $node.has_where,')
+ g.write('.has_order = $node.has_order,')
+ if node.has_order {
+ g.write('.order = _SLIT("')
+ g.expr(node.order_expr)
+ g.write('"),')
+ if node.has_desc {
+ g.write('.order_type = orm__OrderType__desc,')
+ } else {
+ g.write('.order_type = orm__OrderType__asc,')
+ }
+ }
+ g.write('.has_limit = $node.has_limit,')
+ g.write('.has_offset = $node.has_offset,')
+ if prim != '' {
+ g.write('.primary = _SLIT("$prim"),')
+ }
+ select_fields := fields.filter(g.table.get_type_symbol(it.typ).kind != .array)
+ g.write('.fields = new_array_from_c_array($select_fields.len, $select_fields.len, sizeof(string),')
+ mut types := []int{}
+ if select_fields.len > 0 {
+ g.write(' _MOV((string[$select_fields.len]){')
+ for field in select_fields {
+ g.write('_SLIT("${g.get_field_name(field)}"),')
+ sym := g.table.get_type_symbol(field.typ)
+ if sym.name == 'time.Time' {
+ types << -2
+ continue
+ }
+ if sym.kind == .struct_ {
+ types << int(ast.int_type)
+ continue
+ }
+ types << int(field.typ)
+ }
+ g.write('})')
+ } else {
+ g.write('NULL')
+ }
+ g.write('),')
+ g.write('.types = new_array_from_c_array($types.len, $types.len, sizeof(int),')
+ if types.len > 0 {
+ g.write(' _MOV((int[$types.len]){')
+ for typ in types {
+ g.write('$typ,')
+ }
+ g.write('})')
+ } else {
+ g.write('NULL')
+ }
+ g.write('),},')
+
+ mut exprs := []ast.Expr{}
+ if node.has_limit {
+ exprs << node.limit_expr
+ }
+ if node.has_offset {
+ exprs << node.offset_expr
+ }
+ g.write('(orm__QueryData) {')
+ g.write('.types = new_array_from_c_array(0, 0, sizeof(int), NULL),')
+ g.write('.kinds = new_array_from_c_array(0, 0, sizeof(orm__OperationKind), NULL),')
+ g.write('.is_and = new_array_from_c_array(0, 0, sizeof(bool), NULL),')
+ g.write('.data = new_array_from_c_array($exprs.len, $exprs.len, sizeof(orm__Primitive),')
+ if exprs.len > 0 {
+ g.write(' _MOV((orm__Primitive[$exprs.len]){')
+ for e in exprs {
+ g.sql_expr_to_orm_primitive(e)
+ }
+ g.write('})')
+ } else {
+ g.write('NULL')
+ }
+ g.write(')},')
+
+ if node.has_where {
+ g.sql_gen_where_data(node.where_expr)
+ } else {
+ g.write('(orm__QueryData) {}')
+ }
+ g.writeln(');')
+ g.writeln('if (_o${res}.state != 0 && _o${res}.err._typ != _IError_None___index) { _v_panic(IError_str(_o${res}.err)); }')
+ g.writeln('Array_Array_orm__Primitive $res = (*(Array_Array_orm__Primitive*)_o${res}.data);')
+
+ if node.is_count {
+ g.writeln('$left *((*(orm__Primitive*) array_get((*(Array_orm__Primitive*)array_get($res, 0)), 0))._int);')
+ } else {
+ tmp := g.new_tmp_var()
+ styp := g.typ(node.typ)
+ idx := g.new_tmp_var()
+ g.writeln('int $idx = 0;')
+ mut typ_str := ''
+ if node.is_array {
+ info := g.table.get_type_symbol(node.typ).array_info()
+ typ_str = g.typ(info.elem_type)
+ g.writeln('$styp ${tmp}_array = __new_array(0, ${res}.len, sizeof($typ_str));')
+ g.writeln('for (; $idx < ${res}.len; $idx++) {')
+ g.write('\t$typ_str $tmp = ($typ_str) {')
+ inf := g.table.get_type_symbol(info.elem_type).struct_info()
+ for i, field in inf.fields {
+ g.zero_struct_field(field)
+ if i != inf.fields.len - 1 {
+ g.write(', ')
+ }
+ }
+ g.writeln('};')
+ } else {
+ g.write('$styp $tmp = ($styp){')
+ info := g.table.get_type_symbol(node.typ).struct_info()
+ for i, field in info.fields {
+ g.zero_struct_field(field)
+ if i != info.fields.len - 1 {
+ g.write(', ')
+ }
+ }
+ g.writeln('};')
+ }
+
+ g.writeln('if (${res}.len > 0) {')
+ for i, field in fields {
+ sel := '(*(orm__Primitive*) array_get((*(Array_orm__Primitive*) array_get($res, $idx)), $i))'
+ sym := g.table.get_type_symbol(field.typ)
+ if sym.kind == .struct_ && sym.name != 'time.Time' {
+ mut sub := node.sub_structs[int(field.typ)]
+ mut where_expr := sub.where_expr as ast.InfixExpr
+ mut ident := where_expr.right as ast.Ident
+ name := sel
+ s := g.table.find_type_idx('orm.Primitive')
+ if s != 0 {
+ if ident.info is ast.IdentVar {
+ mut info := ident.info as ast.IdentVar
+ info.typ = s
+ ident.info = info
+ }
+ }
+ ident.name = name
+ where_expr.right = ident
+ sub.where_expr = where_expr
+
+ g.sql_select(sub, expr, '${tmp}.$field.name = ')
+ } else if sym.kind == .array {
+ mut fkey := ''
+ for attr in field.attrs {
+ if attr.name == 'fkey' && attr.has_arg && attr.kind == .string {
+ fkey = attr.arg
+ }
+ }
+ if fkey == '' {
+ verror('An field which holds an array, needs a fkey defined')
+ }
+ info := sym.array_info()
+ arr_typ := info.elem_type
+ sub := node.sub_structs[int(arr_typ)]
+ mut where_expr := sub.where_expr as ast.InfixExpr
+ mut l := where_expr.left as ast.Ident
+ mut r := where_expr.right as ast.Ident
+ l.name = fkey
+ r.name = tmp
+ where_expr.left = l
+ where_expr.right = ast.SelectorExpr{
+ pos: r.pos
+ field_name: prim
+ is_mut: false
+ expr: r
+ expr_type: (r.info as ast.IdentVar).typ
+ typ: ast.int_type
+ scope: 0
+ }
+ mut arr := ast.SqlExpr{
+ typ: field.typ
+ is_count: sub.is_count
+ db_expr: sub.db_expr
+ has_where: sub.has_where
+ has_offset: sub.has_offset
+ offset_expr: sub.offset_expr
+ has_order: sub.has_order
+ order_expr: sub.order_expr
+ has_desc: sub.has_desc
+ is_array: true
+ pos: sub.pos
+ has_limit: sub.has_limit
+ limit_expr: sub.limit_expr
+ table_expr: sub.table_expr
+ fields: sub.fields
+ where_expr: where_expr
+ }
+
+ g.sql_select(arr, expr, '${tmp}.$field.name = ')
+ } else {
+ mut typ := sym.cname
+ g.writeln('${tmp}.$field.name = *(${sel}._$typ);')
+ }
+ }
+ g.writeln('}')
+
+ if node.is_array {
+ g.writeln('array_push(&${tmp}_array, _MOV(($typ_str[]){ $tmp }));')
+ g.writeln('}')
+ }
+
+ g.write('$left $tmp')
+ if node.is_array {
+ g.write('_array')
+ }
+ g.writeln(';')
+ }
+}
+
+fn (mut g Gen) parse_db_type(expr ast.Expr) SqlType {
+ match expr {
+ ast.Ident {
+ if expr.info is ast.IdentVar {
+ return g.parse_db_from_type_string(g.table.get_type_name(expr.info.typ))
+ }
+ }
+ ast.SelectorExpr {
+ return g.parse_db_from_type_string(g.table.get_type_name(expr.typ))
+ }
+ else {
+ return .unknown
+ }
+ }
+ return .unknown
+}
+
+fn (mut g Gen) parse_db_from_type_string(name string) SqlType {
+ match name {
+ 'sqlite.DB' {
+ return .sqlite3
+ }
+ 'mysql.Connection' {
+ return .mysql
+ }
+ 'pg.DB' {
+ return .psql
+ }
+ 'mssql.Connection' {
+ return .mssql
+ }
+ else {
+ return .unknown
+ }
+ }
+}
+
+fn (mut g Gen) get_table_name(table_expr ast.TypeNode) string {
+ info := g.table.get_type_symbol(table_expr.typ).struct_info()
+ mut tablename := util.strip_mod_name(g.table.get_type_symbol(table_expr.typ).name)
+ for attr in info.attrs {
+ if attr.kind == .string && attr.name == 'table' && attr.arg != '' {
+ tablename = attr.arg
+ break
+ }
+ }
+ return tablename
+}
+
+fn (mut g Gen) get_struct_field(name string) ast.StructField {
+ info := g.table.get_type_symbol(g.table.type_idxs[g.sql_table_name]).struct_info()
+ mut f := ast.StructField{}
+ for field in info.fields {
+ if field.name == name {
+ f = field
+ }
+ }
+ return f
+}
+
+fn (mut g Gen) get_field_name(field ast.StructField) string {
+ mut name := field.name
+ for attr in field.attrs {
+ if attr.kind == .string && attr.name == 'sql' && attr.arg != '' {
+ name = attr.arg
+ break
+ }
+ }
+ sym := g.table.get_type_symbol(field.typ)
+ if sym.kind == .struct_ && sym.name != 'time.Time' {
+ name = '${name}_id'
+ }
+ return name
+}
diff --git a/v_windows/v/vlib/v/gen/c/str.v b/v_windows/v/vlib/v/gen/c/str.v
new file mode 100644
index 0000000..788da2c
--- /dev/null
+++ b/v_windows/v/vlib/v/gen/c/str.v
@@ -0,0 +1,145 @@
+// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved.
+// Use of this source code is governed by an MIT license that can be found in the LICENSE file.
+module c
+
+import v.ast
+import v.util
+
+fn (mut g Gen) string_literal(node ast.StringLiteral) {
+ escaped_val := util.smart_quote(node.val, node.is_raw)
+ if node.language == .c {
+ g.write('"$escaped_val"')
+ } else {
+ g.write('_SLIT("$escaped_val")')
+ }
+}
+
+// optimize string interpolation in string builders:
+// `sb.writeln('a=$a')` =>
+// `sb.writeln('a='); sb.writeln(a.str())`
+fn (mut g Gen) string_inter_literal_sb_optimized(call_expr ast.CallExpr) {
+ node := call_expr.args[0].expr as ast.StringInterLiteral
+ // sb_name := g.cur_call_expr.left
+ // g.go_before_stmt(0)
+ g.writeln('// sb inter opt')
+ is_nl := call_expr.name == 'writeln'
+ // println('optimize sb $call_expr.name')
+ for i, val in node.vals {
+ escaped_val := util.smart_quote(val, false)
+ // if val == '' {
+ // break
+ // continue
+ // }
+ g.write('strings__Builder_write_string(&')
+ g.expr(call_expr.left)
+ g.write(', _SLIT("')
+ g.write(escaped_val)
+ g.writeln('"));')
+ //
+ if i >= node.exprs.len {
+ break
+ }
+ // if node.expr_types.len <= i || node.exprs.len <= i {
+ // continue
+ // }
+ if is_nl && i == node.exprs.len - 1 {
+ g.write('strings__Builder_writeln(&')
+ } else {
+ g.write('strings__Builder_write_string(&')
+ }
+ g.expr(call_expr.left)
+ g.write(', ')
+ typ := node.expr_types[i]
+ g.write(g.typ(typ))
+ g.write('_str(')
+ sym := g.table.get_type_symbol(typ)
+ if sym.kind != .function {
+ g.expr(node.exprs[i])
+ }
+ g.writeln('));')
+ }
+ g.writeln('')
+ // println(node.vals)
+ return
+}
+
+fn (mut g Gen) gen_expr_to_string(expr ast.Expr, etype ast.Type) {
+ is_shared := etype.has_flag(.shared_f)
+ mut typ := etype
+ if is_shared {
+ typ = typ.clear_flag(.shared_f).set_nr_muls(0)
+ }
+ mut sym := g.table.get_type_symbol(typ)
+ // when type is alias, print the aliased value
+ if mut sym.info is ast.Alias {
+ parent_sym := g.table.get_type_symbol(sym.info.parent_type)
+ if parent_sym.has_method('str') {
+ typ = sym.info.parent_type
+ sym = parent_sym
+ }
+ }
+ sym_has_str_method, str_method_expects_ptr, _ := sym.str_method_info()
+ if typ.has_flag(.variadic) {
+ str_fn_name := g.gen_str_for_type(typ)
+ g.write('${str_fn_name}(')
+ g.expr(expr)
+ g.write(')')
+ } else if typ == ast.string_type {
+ g.expr(expr)
+ } else if typ == ast.bool_type {
+ g.expr(expr)
+ g.write(' ? _SLIT("true") : _SLIT("false")')
+ } else if sym.kind == .none_ {
+ g.write('_SLIT("<none>")')
+ } else if sym.kind == .enum_ {
+ if expr !is ast.EnumVal {
+ str_fn_name := g.gen_str_for_type(typ)
+ g.write('${str_fn_name}(')
+ g.enum_expr(expr)
+ g.write(')')
+ } else {
+ g.write('_SLIT("')
+ g.enum_expr(expr)
+ g.write('")')
+ }
+ } else if sym_has_str_method
+ || sym.kind in [.array, .array_fixed, .map, .struct_, .multi_return, .sum_type, .interface_] {
+ is_ptr := typ.is_ptr()
+ is_var_mut := expr.is_auto_deref_var()
+ str_fn_name := g.gen_str_for_type(typ)
+ if is_ptr && !is_var_mut {
+ g.write('str_intp(1, _MOV((StrIntpData[]){{_SLIT("&"), $si_s_code ,{.d_s=')
+ }
+ g.write('${str_fn_name}(')
+ if str_method_expects_ptr && !is_ptr {
+ g.write('&')
+ } else if (!str_method_expects_ptr && is_ptr && !is_shared) || is_var_mut {
+ g.write('*')
+ }
+ if expr is ast.ArrayInit {
+ if expr.is_fixed {
+ s := g.typ(expr.typ)
+ g.write('($s)')
+ }
+ }
+ g.expr_with_cast(expr, typ, typ)
+ if is_shared {
+ g.write('->val')
+ }
+ g.write(')')
+ if is_ptr && !is_var_mut {
+ g.write('}}}))')
+ // g.write(')')
+ }
+ } else {
+ str_fn_name := g.gen_str_for_type(typ)
+ g.write('${str_fn_name}(')
+ if expr.is_auto_deref_var() {
+ g.write('*')
+ }
+ if sym.kind != .function {
+ g.expr_with_cast(expr, typ, typ)
+ }
+ g.write(')')
+ }
+}
diff --git a/v_windows/v/vlib/v/gen/c/str_intp.v b/v_windows/v/vlib/v/gen/c/str_intp.v
new file mode 100644
index 0000000..7ace443
--- /dev/null
+++ b/v_windows/v/vlib/v/gen/c/str_intp.v
@@ -0,0 +1,206 @@
+/*
+str_intp.v
+
+Copyright (c) 2019-2021 Dario Deledda. All rights reserved.
+Use of this source code is governed by an MIT license
+that can be found in the LICENSE file.
+
+This file contains string interpolation V functions
+*/
+module c
+
+import v.ast
+import v.util
+
+fn (mut g Gen) str_format(node ast.StringInterLiteral, i int) (u64, string) {
+ mut base := 0 // numeric base
+ mut upper_case := false // set upercase for the result string
+ mut typ := g.unwrap_generic(node.expr_types[i])
+ sym := g.table.get_type_symbol(typ)
+ if sym.kind == .alias {
+ typ = (sym.info as ast.Alias).parent_type
+ }
+ mut remove_tail_zeros := false
+ fspec := node.fmts[i]
+ mut fmt_type := StrIntpType{}
+
+ // upper cases
+ if (fspec - `A`) <= (`Z` - `A`) {
+ upper_case = true
+ }
+
+ if fspec in [`s`, `S`] {
+ /*
+ if node.fwidths[i] == 0 {
+ fmt_type = .si_s
+ } else {
+ fmt_type = .si_s
+ }
+ */
+ fmt_type = .si_s
+ } else if typ.is_float() {
+ if fspec in [`g`, `G`] {
+ match typ {
+ ast.f32_type { fmt_type = .si_g32 }
+ // ast.f64_type { fmt_type = .si_g64 }
+ else { fmt_type = .si_g64 }
+ }
+ remove_tail_zeros = true
+ } else if fspec in [`e`, `E`] {
+ match typ {
+ ast.f32_type { fmt_type = .si_e32 }
+ // ast.f64_type { fmt_type = .si_e64 }
+ else { fmt_type = .si_e64 }
+ }
+ } else if fspec in [`f`, `F`] {
+ match typ {
+ ast.f32_type { fmt_type = .si_f32 }
+ // ast.f64_type { fmt_type = .si_f64 }
+ else { fmt_type = .si_f64 }
+ }
+ }
+ } else if typ.is_pointer() {
+ if fspec in [`x`, `X`] {
+ base = 16 - 2 // our base start from 2
+ }
+ if fspec in [`p`, `x`, `X`] {
+ fmt_type = .si_p
+ } else {
+ fmt_type = .si_vp
+ }
+ } else if typ.is_int() {
+ if fspec in [`x`, `X`] {
+ base = 16 - 2 // our base start from 2
+ }
+ // if fspec in [`o`] {
+ if fspec == `o` {
+ base = 8 - 2 // our base start from 2
+ }
+ if fspec == `c` {
+ fmt_type = .si_c
+ } else {
+ match typ {
+ ast.i8_type { fmt_type = .si_i8 }
+ ast.byte_type { fmt_type = .si_u8 }
+ ast.i16_type { fmt_type = .si_i16 }
+ ast.u16_type { fmt_type = .si_u16 }
+ ast.i64_type { fmt_type = .si_i64 }
+ ast.u64_type { fmt_type = .si_u64 }
+ ast.u32_type { fmt_type = .si_u32 }
+ else { fmt_type = .si_i32 }
+ }
+ }
+ } else {
+ // TODO: better check this case
+ fmt_type = .si_p
+ }
+
+ /*
+ // pad filling 64bit format
+ mut pad_ch := u8(0)
+ if node.fills[i] {
+ pad_ch = u8(`0`)
+ }
+ res := get_str_intp_u64_format(fmt_type, node.fwidths[i], node.precisions[i], remove_tail_zeros, node.pluss[i], pad_ch, base, upper_case)
+ */
+
+ // pad filling 32bit format
+ mut pad_ch := 0
+ if node.fills[i] {
+ pad_ch = 1
+ }
+ res := get_str_intp_u32_format(fmt_type, node.fwidths[i], node.precisions[i], remove_tail_zeros,
+ node.pluss[i], byte(pad_ch), base, upper_case)
+ //
+ return res, fmt_type.str()
+}
+
+fn (mut g Gen) str_val(node ast.StringInterLiteral, i int) {
+ expr := node.exprs[i]
+
+ typ := g.unwrap_generic(node.expr_types[i])
+ if typ == ast.string_type {
+ if g.inside_vweb_tmpl {
+ g.write('vweb__filter(')
+ if expr.is_auto_deref_var() {
+ g.write('*')
+ }
+ g.expr(expr)
+ g.write(')')
+ } else {
+ if expr.is_auto_deref_var() {
+ g.write('*')
+ }
+ g.expr(expr)
+ }
+ } else if node.fmts[i] == `s` || typ.has_flag(.variadic) {
+ g.gen_expr_to_string(expr, typ)
+ } else if typ.is_number() || typ.is_pointer() || node.fmts[i] == `d` {
+ if typ.is_signed() && node.fmts[i] in [`x`, `X`, `o`] {
+ // convert to unsigned first befors C's integer propagation strikes
+ if typ == ast.i8_type {
+ g.write('(byte)(')
+ } else if typ == ast.i16_type {
+ g.write('(u16)(')
+ } else if typ == ast.int_type {
+ g.write('(u32)(')
+ } else {
+ g.write('(u64)(')
+ }
+ if expr.is_auto_deref_var() {
+ g.write('*')
+ }
+ g.expr(expr)
+ g.write(')')
+ } else {
+ if expr.is_auto_deref_var() {
+ g.write('*')
+ }
+ g.expr(expr)
+ }
+ } else {
+ if expr.is_auto_deref_var() {
+ g.write('*')
+ }
+ g.expr(expr)
+ }
+}
+
+fn (mut g Gen) string_inter_literal(node ast.StringInterLiteral) {
+ // fn (mut g Gen) str_int2(node ast.StringInterLiteral) {
+ g.write(' str_intp($node.vals.len, ')
+ g.write('_MOV((StrIntpData[]){')
+ for i, val in node.vals {
+ escaped_val := util.smart_quote(val, false)
+
+ if escaped_val.len > 0 {
+ g.write('{_SLIT("$escaped_val"), ')
+ } else {
+ g.write('{_SLIT0, ')
+ }
+
+ if i >= node.exprs.len {
+ // last part of the string
+ g.write('0, { .d_c = 0 }}')
+ break
+ }
+
+ ft_u64, ft_str := g.str_format(node, i)
+ g.write('0x$ft_u64.hex(), {.d_$ft_str = ')
+
+ // for pointers we need a void* cast
+ if unsafe { ft_str.str[0] } == `p` {
+ g.write('(void*)(')
+ g.str_val(node, i)
+ g.write(')')
+ } else {
+ g.str_val(node, i)
+ }
+
+ g.write('}}')
+ if i < (node.vals.len - 1) {
+ g.write(', ')
+ }
+ }
+ g.write('}))')
+}
diff --git a/v_windows/v/vlib/v/gen/c/testdata/addition.c.must_have b/v_windows/v/vlib/v/gen/c/testdata/addition.c.must_have
new file mode 100644
index 0000000..300bd6a
--- /dev/null
+++ b/v_windows/v/vlib/v/gen/c/testdata/addition.c.must_have
@@ -0,0 +1 @@
+int zzz = 579 \ No newline at end of file
diff --git a/v_windows/v/vlib/v/gen/c/testdata/addition.out b/v_windows/v/vlib/v/gen/c/testdata/addition.out
new file mode 100644
index 0000000..72b67e0
--- /dev/null
+++ b/v_windows/v/vlib/v/gen/c/testdata/addition.out
@@ -0,0 +1 @@
+579 \ No newline at end of file
diff --git a/v_windows/v/vlib/v/gen/c/testdata/addition.vv b/v_windows/v/vlib/v/gen/c/testdata/addition.vv
new file mode 100644
index 0000000..ff95896
--- /dev/null
+++ b/v_windows/v/vlib/v/gen/c/testdata/addition.vv
@@ -0,0 +1,4 @@
+fn main() {
+ zzz := 123 + 456
+ println(zzz)
+} \ No newline at end of file
diff --git a/v_windows/v/vlib/v/gen/c/testdata/const_references.c.must_have b/v_windows/v/vlib/v/gen/c/testdata/const_references.c.must_have
new file mode 100644
index 0000000..11a2cb8
--- /dev/null
+++ b/v_windows/v/vlib/v/gen/c/testdata/const_references.c.must_have
@@ -0,0 +1,3 @@
+VV_LOCAL_SYMBOL int main__a_const_accepting_fn(int* x, const int* const_x);
+VV_LOCAL_SYMBOL int main__a_const_accepting_fn(int* x, const int* const_x) {
+main__a_const_accepting_fn(&a, &b)
diff --git a/v_windows/v/vlib/v/gen/c/testdata/const_references.out b/v_windows/v/vlib/v/gen/c/testdata/const_references.out
new file mode 100644
index 0000000..01e79c3
--- /dev/null
+++ b/v_windows/v/vlib/v/gen/c/testdata/const_references.out
@@ -0,0 +1,3 @@
+1
+2
+3
diff --git a/v_windows/v/vlib/v/gen/c/testdata/const_references.vv b/v_windows/v/vlib/v/gen/c/testdata/const_references.vv
new file mode 100644
index 0000000..ae94cc7
--- /dev/null
+++ b/v_windows/v/vlib/v/gen/c/testdata/const_references.vv
@@ -0,0 +1,11 @@
+fn a_const_accepting_fn(x &int, const_x &int) int {
+ return *x + *const_x
+}
+
+fn main() {
+ a := 1
+ b := 2
+ println(a)
+ println(b)
+ println(a_const_accepting_fn(&a, &b))
+}
diff --git a/v_windows/v/vlib/v/gen/c/utils.v b/v_windows/v/vlib/v/gen/c/utils.v
new file mode 100644
index 0000000..a4768c8
--- /dev/null
+++ b/v_windows/v/vlib/v/gen/c/utils.v
@@ -0,0 +1,49 @@
+// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved.
+// Use of this source code is governed by an MIT license
+// that can be found in the LICENSE file.
+module c
+
+import v.ast
+
+fn (mut g Gen) unwrap_generic(typ ast.Type) ast.Type {
+ if typ.has_flag(.generic) {
+ if t_typ := g.table.resolve_generic_to_concrete(typ, g.table.cur_fn.generic_names,
+ g.table.cur_concrete_types)
+ {
+ return t_typ
+ }
+ }
+ return typ
+}
+
+struct Type {
+ // typ is the original type
+ typ ast.Type [required]
+ sym &ast.TypeSymbol [required]
+ // unaliased is `typ` once aliased have been resolved
+ // it may not contain informations such as flags and nr_muls
+ unaliased ast.Type [required]
+ unaliased_sym &ast.TypeSymbol [required]
+}
+
+// unwrap returns the following variants of a type:
+// * generics unwrapped
+// * alias unwrapped
+fn (mut g Gen) unwrap(typ ast.Type) Type {
+ no_generic := g.unwrap_generic(typ)
+ no_generic_sym := g.table.get_type_symbol(no_generic)
+ if no_generic_sym.kind != .alias {
+ return Type{
+ typ: no_generic
+ sym: no_generic_sym
+ unaliased: no_generic
+ unaliased_sym: no_generic_sym
+ }
+ }
+ return Type{
+ typ: no_generic
+ sym: no_generic_sym
+ unaliased: no_generic_sym.parent_idx
+ unaliased_sym: g.table.get_type_symbol(no_generic_sym.parent_idx)
+ }
+}