cgen: split up into for.v, match.v, struct.v (#13454)
parent
b5379255da
commit
5071a54b99
1297
vlib/v/gen/c/cgen.v
1297
vlib/v/gen/c/cgen.v
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,374 @@
|
||||||
|
// Copyright (c) 2019-2022 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) for_c_stmt(node ast.ForCStmt) {
|
||||||
|
g.loop_depth++
|
||||||
|
if node.is_multi {
|
||||||
|
g.is_vlines_enabled = false
|
||||||
|
g.inside_for_c_stmt = true
|
||||||
|
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.inside_for_c_stmt = false
|
||||||
|
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
|
||||||
|
g.inside_for_c_stmt = true
|
||||||
|
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.inside_for_c_stmt = false
|
||||||
|
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: {}')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
g.loop_depth--
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (mut g Gen) for_stmt(node ast.ForStmt) {
|
||||||
|
g.loop_depth++
|
||||||
|
g.is_vlines_enabled = false
|
||||||
|
if node.label.len > 0 {
|
||||||
|
g.writeln('$node.label:')
|
||||||
|
}
|
||||||
|
g.writeln('for (;;) {')
|
||||||
|
if !node.is_inf {
|
||||||
|
g.indent++
|
||||||
|
g.set_current_pos_as_last_stmt_pos()
|
||||||
|
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: {}')
|
||||||
|
}
|
||||||
|
g.loop_depth--
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (mut g Gen) for_in_stmt(node ast.ForInStmt) {
|
||||||
|
g.loop_depth++
|
||||||
|
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 := ast.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.sym(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.sym(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.sym(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.sym(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() {
|
||||||
|
if node.val_is_mut {
|
||||||
|
g.write('$val_styp ${c_name(node.val_var)} = &(*($val_styp)')
|
||||||
|
} else {
|
||||||
|
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.sym(node.cond_type)
|
||||||
|
next_fn := cond_type_sym.find_method_with_generic_parent('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(';')
|
||||||
|
if node.key_var in ['', '_'] {
|
||||||
|
g.writeln('while (1) {')
|
||||||
|
} else {
|
||||||
|
g.writeln('for (size_t $node.key_var = 0;; ++$node.key_var) {')
|
||||||
|
}
|
||||||
|
t_var := g.new_tmp_var()
|
||||||
|
receiver_typ := next_fn.params[0].typ
|
||||||
|
receiver_styp := g.typ(receiver_typ)
|
||||||
|
mut fn_name := receiver_styp.replace_each(['*', '', '.', '__']) + '_next'
|
||||||
|
receiver_sym := g.table.sym(receiver_typ)
|
||||||
|
if receiver_sym.info is ast.Struct {
|
||||||
|
if receiver_sym.info.concrete_types.len > 0 {
|
||||||
|
fn_name = g.generic_fn_name(receiver_sym.info.concrete_types, fn_name,
|
||||||
|
false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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)
|
||||||
|
if node.val_is_mut {
|
||||||
|
g.writeln('\t$val_styp $val = ($val_styp)${t_var}.data;')
|
||||||
|
} else {
|
||||||
|
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: {}')
|
||||||
|
}
|
||||||
|
g.loop_depth--
|
||||||
|
}
|
|
@ -0,0 +1,226 @@
|
||||||
|
// Copyright (c) 2019-2022 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) 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 is_noreturn_callexpr(stmt.expr) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if stmt.expr is ast.CallExpr {
|
||||||
|
if stmt.expr.is_method {
|
||||||
|
left_sym := g.table.sym(stmt.expr.receiver_type)
|
||||||
|
if left_sym.kind in [.array, .array_fixed, .map] {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
} else if stmt.expr.or_block.kind != .absent {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (mut g Gen) if_expr(node ast.IfExpr) {
|
||||||
|
if node.is_comptime {
|
||||||
|
g.comptime_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.vars[0].name != '_' {
|
||||||
|
base_type := g.base_type(branch.cond.expr_type)
|
||||||
|
if short_opt {
|
||||||
|
cond_var_name := if branch.cond.vars[0].name == '_' {
|
||||||
|
'_dummy_${g.tmp_count + 1}'
|
||||||
|
} else {
|
||||||
|
branch.cond.vars[0].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]).pos().pos)
|
||||||
|
if v := scope.find_var(branch.cond.vars[0].name) {
|
||||||
|
is_auto_heap = v.is_auto_heap
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if branch.cond.vars.len == 1 {
|
||||||
|
left_var_name := c_name(branch.cond.vars[0].name)
|
||||||
|
if is_auto_heap {
|
||||||
|
g.writeln('\t$base_type* $left_var_name = HEAP($base_type, *($base_type*)${var_name}.data);')
|
||||||
|
} else {
|
||||||
|
g.writeln('\t$base_type $left_var_name = *($base_type*)${var_name}.data;')
|
||||||
|
}
|
||||||
|
} else if branch.cond.vars.len > 1 {
|
||||||
|
for vi, var in branch.cond.vars {
|
||||||
|
left_var_name := c_name(var.name)
|
||||||
|
sym := g.table.sym(branch.cond.expr_type)
|
||||||
|
if sym.kind == .multi_return {
|
||||||
|
mr_info := sym.info as ast.MultiReturn
|
||||||
|
if mr_info.types.len == branch.cond.vars.len {
|
||||||
|
var_typ := g.typ(mr_info.types[vi])
|
||||||
|
if is_auto_heap {
|
||||||
|
g.writeln('\t$var_typ* $left_var_name = (HEAP($base_type, *($base_type*)${var_name}.data).arg$vi);')
|
||||||
|
} else {
|
||||||
|
g.writeln('\t$var_typ $left_var_name = (*($base_type*)${var_name}.data).arg$vi;')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
mut no_needs_par := false
|
||||||
|
if branch.cond is ast.InfixExpr {
|
||||||
|
if branch.cond.op == .key_in && branch.cond.left !is ast.InfixExpr
|
||||||
|
&& branch.cond.right is ast.ArrayInit {
|
||||||
|
no_needs_par = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if no_needs_par {
|
||||||
|
g.write('if ')
|
||||||
|
} else {
|
||||||
|
g.write('if (')
|
||||||
|
}
|
||||||
|
g.expr(branch.cond)
|
||||||
|
if no_needs_par {
|
||||||
|
g.writeln(' {')
|
||||||
|
} else {
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if node.branches.len > 0 {
|
||||||
|
g.writeln('}')
|
||||||
|
}
|
||||||
|
g.set_current_pos_as_last_stmt_pos()
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,419 @@
|
||||||
|
// Copyright (c) 2019-2022 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) need_tmp_var_in_match(node ast.MatchExpr) bool {
|
||||||
|
if node.is_expr && node.return_type != ast.void_type && node.return_type != 0 {
|
||||||
|
cond_sym := g.table.final_sym(node.cond_type)
|
||||||
|
sym := g.table.sym(node.return_type)
|
||||||
|
if g.table.type_kind(node.return_type) == .sum_type {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if node.return_type.has_flag(.optional) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if sym.kind == .multi_return {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if cond_sym.kind == .enum_ && node.branches.len > 5 {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
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 in [ast.CallExpr, ast.IfExpr, 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) {
|
||||||
|
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 is_expr && node.return_type.has_flag(.optional) {
|
||||||
|
old := g.inside_match_optional
|
||||||
|
defer {
|
||||||
|
g.inside_match_optional = old
|
||||||
|
}
|
||||||
|
g.inside_match_optional = true
|
||||||
|
}
|
||||||
|
if node.cond in [ast.Ident, ast.SelectorExpr, ast.IntegerLiteral, ast.StringLiteral,
|
||||||
|
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.set_current_pos_as_last_stmt_pos()
|
||||||
|
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('(')
|
||||||
|
}
|
||||||
|
typ := g.table.final_sym(node.cond_type)
|
||||||
|
if node.is_sum_type {
|
||||||
|
g.match_expr_sumtype(node, is_expr, cond_var, tmp_var)
|
||||||
|
} else if typ.kind == .enum_ && g.loop_depth == 0 && node.branches.len > 5 && g.fn_decl != 0 { // do not optimize while in top-level
|
||||||
|
g.match_expr_switch(node, is_expr, cond_var, tmp_var, typ)
|
||||||
|
} else {
|
||||||
|
g.match_expr_classic(node, is_expr, cond_var, tmp_var)
|
||||||
|
}
|
||||||
|
g.set_current_pos_as_last_stmt_pos()
|
||||||
|
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.sym(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.sym(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.sym(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.set_current_pos_as_last_stmt_pos()
|
||||||
|
}
|
||||||
|
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_switch(node ast.MatchExpr, is_expr bool, cond_var string, tmp_var string, enum_typ ast.TypeSymbol) {
|
||||||
|
cname := '${enum_typ.cname}__'
|
||||||
|
mut covered_enum := []string{cap: (enum_typ.info as ast.Enum).vals.len} // collects missing enum variant branches to avoid cstrict errors
|
||||||
|
mut range_branches := []ast.MatchBranch{cap: node.branches.len} // branches have RangeExpr cannot emit as switch case branch, we handle it in default branch
|
||||||
|
mut default_generated := false
|
||||||
|
g.empty_line = true
|
||||||
|
g.writeln('switch ($cond_var) {')
|
||||||
|
g.indent++
|
||||||
|
for branch in node.branches {
|
||||||
|
if branch.is_else {
|
||||||
|
for val in (enum_typ.info as ast.Enum).vals {
|
||||||
|
if val !in covered_enum {
|
||||||
|
g.writeln('case $cname$val:')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
g.writeln('default:')
|
||||||
|
default_generated = true
|
||||||
|
if range_branches.len > 0 {
|
||||||
|
g.indent++
|
||||||
|
for range_branch in range_branches {
|
||||||
|
g.write('if (')
|
||||||
|
for i, expr in range_branch.exprs {
|
||||||
|
if i > 0 {
|
||||||
|
g.write(' || ')
|
||||||
|
}
|
||||||
|
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(')')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
g.writeln(') {')
|
||||||
|
g.stmts_with_tmp_var(range_branch.stmts, tmp_var)
|
||||||
|
g.writeln('break;')
|
||||||
|
g.writeln('}')
|
||||||
|
}
|
||||||
|
g.indent--
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if branch.exprs.any(it is ast.RangeExpr) {
|
||||||
|
range_branches << branch
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
for expr in branch.exprs {
|
||||||
|
if expr is ast.EnumVal {
|
||||||
|
covered_enum << expr.val
|
||||||
|
g.write('case ')
|
||||||
|
g.expr(expr)
|
||||||
|
g.writeln(': ')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
g.indent++
|
||||||
|
g.writeln('{')
|
||||||
|
if is_expr && tmp_var.len > 0 && g.table.sym(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
|
||||||
|
g.writeln('} break;')
|
||||||
|
g.indent--
|
||||||
|
}
|
||||||
|
if range_branches.len > 0 && !default_generated {
|
||||||
|
g.writeln('default:')
|
||||||
|
g.indent++
|
||||||
|
for range_branch in range_branches {
|
||||||
|
g.write('if (')
|
||||||
|
for i, expr in range_branch.exprs {
|
||||||
|
if i > 0 {
|
||||||
|
g.write(' || ')
|
||||||
|
}
|
||||||
|
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(')')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
g.writeln(') {')
|
||||||
|
g.stmts_with_tmp_var(range_branch.stmts, tmp_var)
|
||||||
|
g.writeln('break;')
|
||||||
|
g.writeln('}')
|
||||||
|
}
|
||||||
|
g.indent--
|
||||||
|
}
|
||||||
|
g.indent--
|
||||||
|
g.writeln('}')
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (mut g Gen) match_expr_classic(node ast.MatchExpr, is_expr bool, cond_var string, tmp_var string) {
|
||||||
|
type_sym := g.table.sym(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.equality_fn(node.cond_type)
|
||||||
|
g.write('${ptr_typ}_arr_eq($cond_var, ')
|
||||||
|
g.expr(expr)
|
||||||
|
g.write(')')
|
||||||
|
}
|
||||||
|
.array_fixed {
|
||||||
|
ptr_typ := g.equality_fn(node.cond_type)
|
||||||
|
g.write('${ptr_typ}_arr_eq($cond_var, ')
|
||||||
|
g.expr(expr)
|
||||||
|
g.write(')')
|
||||||
|
}
|
||||||
|
.map {
|
||||||
|
ptr_typ := g.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.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(') {')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if is_expr && tmp_var.len > 0 && g.table.sym(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 && node.branches.len >= 1 {
|
||||||
|
g.write('}')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,299 @@
|
||||||
|
// Copyright (c) 2019-2022 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
|
||||||
|
|
||||||
|
const (
|
||||||
|
skip_struct_init = ['struct stat', 'struct addrinfo']
|
||||||
|
)
|
||||||
|
|
||||||
|
fn (mut g Gen) struct_init(node ast.StructInit) {
|
||||||
|
styp := g.typ(node.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
|
||||||
|
}
|
||||||
|
mut sym := g.table.final_sym(g.unwrap_generic(node.typ))
|
||||||
|
is_amp := g.is_amp
|
||||||
|
is_multiline := node.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 := node.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 node.typ.is_ptr() {
|
||||||
|
basetyp := g.typ(node.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 node.fields.len == 0 && node.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 = node.fields
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
if is_multiline {
|
||||||
|
g.indent++
|
||||||
|
}
|
||||||
|
// User set fields
|
||||||
|
mut initialized := false
|
||||||
|
mut old_is_shared := g.is_shared
|
||||||
|
for i, field in node.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.sym(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.typ, 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 != node.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_ {
|
||||||
|
mut info := sym.info as ast.Struct
|
||||||
|
nr_fields = info.fields.len
|
||||||
|
if info.is_union && node.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 := node.fields.filter(it.name !in init_field_names)
|
||||||
|
for embed in info.embeds {
|
||||||
|
embed_sym := g.table.sym(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{
|
||||||
|
...node
|
||||||
|
typ: embed
|
||||||
|
is_update_embed: true
|
||||||
|
fields: init_fields_to_embed
|
||||||
|
}
|
||||||
|
inside_cast_in_heap := g.inside_cast_in_heap
|
||||||
|
g.inside_cast_in_heap = 0 // prevent use of pointers in child structs
|
||||||
|
|
||||||
|
g.write('.$embed_name = ')
|
||||||
|
g.struct_init(default_init)
|
||||||
|
|
||||||
|
g.inside_cast_in_heap = inside_cast_in_heap // restore value for further struct inits
|
||||||
|
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 := node.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.sym(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, sfield.typ, 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 node.has_update_expr {
|
||||||
|
g.expr(node.update_expr)
|
||||||
|
if node.update_expr_type.is_ptr() {
|
||||||
|
g.write('->')
|
||||||
|
} else {
|
||||||
|
g.write('.')
|
||||||
|
}
|
||||||
|
if node.is_update_embed {
|
||||||
|
embed_sym := g.table.sym(node.typ)
|
||||||
|
embed_name := embed_sym.embed_name()
|
||||||
|
g.write(embed_name)
|
||||||
|
if node.typ.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.sym(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
|
||||||
|
}
|
Loading…
Reference in New Issue