cgen & jsgen: generate when possible a `switch` from `match x {` (#12216)
parent
a3de67de28
commit
53c2e262f1
|
@ -104,6 +104,7 @@ mut:
|
|||
inside_map_index bool
|
||||
inside_opt_data bool
|
||||
inside_if_optional bool
|
||||
loop_depth int
|
||||
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
|
||||
|
@ -1780,6 +1781,7 @@ fn (mut g Gen) write_defer_stmts() {
|
|||
}
|
||||
|
||||
fn (mut g Gen) for_c_stmt(node ast.ForCStmt) {
|
||||
g.loop_depth++
|
||||
if node.is_multi {
|
||||
g.is_vlines_enabled = false
|
||||
if node.label.len > 0 {
|
||||
|
@ -1853,9 +1855,11 @@ fn (mut g Gen) for_c_stmt(node ast.ForCStmt) {
|
|||
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:')
|
||||
|
@ -1878,9 +1882,11 @@ fn (mut g Gen) for_stmt(node ast.ForStmt) {
|
|||
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: {}')
|
||||
}
|
||||
|
@ -2120,6 +2126,7 @@ fn (mut g Gen) for_in_stmt(node ast.ForInStmt) {
|
|||
if node.label.len > 0 {
|
||||
g.writeln('\t${node.label}__break: {}')
|
||||
}
|
||||
g.loop_depth--
|
||||
}
|
||||
|
||||
struct SumtypeCastingFn {
|
||||
|
@ -4255,10 +4262,14 @@ fn (mut g Gen) unlock_locks() {
|
|||
|
||||
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.get_final_type_symbol(node.cond_type)
|
||||
sym := g.table.get_type_symbol(node.return_type)
|
||||
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
|
||||
|
@ -4278,6 +4289,27 @@ fn (mut g Gen) need_tmp_var_in_match(node ast.MatchExpr) bool {
|
|||
return false
|
||||
}
|
||||
|
||||
fn (mut g Gen) branches_all_resolvable_in_runtime(node ast.MatchExpr, typ ast.TypeSymbol) bool {
|
||||
for branch in node.branches {
|
||||
for expr in branch.exprs {
|
||||
if expr is ast.EnumVal {
|
||||
continue
|
||||
} else if expr is ast.RangeExpr {
|
||||
return false
|
||||
// we must implement constant folding on enum fields and make it accessible
|
||||
// anywhere to prove that range expr's actual branches are resolvale
|
||||
|
||||
// if expr.high !is ast.IntegerLiteral || expr.low !is ast.IntegerLiteral {
|
||||
// return false
|
||||
// }
|
||||
// continue
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
fn (mut g Gen) match_expr(node ast.MatchExpr) {
|
||||
// println('match expr typ=$it.expr_type')
|
||||
// TODO
|
||||
|
@ -4321,8 +4353,13 @@ fn (mut g Gen) match_expr(node ast.MatchExpr) {
|
|||
// brackets needed otherwise '?' will apply to everything on the left
|
||||
g.write('(')
|
||||
}
|
||||
typ := g.table.get_final_type_symbol(node.cond_type)
|
||||
all_resolvable := g.branches_all_resolvable_in_runtime(node, typ)
|
||||
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
|
||||
&& all_resolvable { // 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)
|
||||
}
|
||||
|
@ -4411,6 +4448,46 @@ fn (mut g Gen) match_expr_sumtype(node ast.MatchExpr, is_expr bool, cond_var str
|
|||
}
|
||||
}
|
||||
|
||||
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}
|
||||
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:')
|
||||
} else {
|
||||
for expr in branch.exprs {
|
||||
if expr is ast.EnumVal {
|
||||
covered_enum << (expr as ast.EnumVal).val
|
||||
g.write('case ')
|
||||
g.expr(expr)
|
||||
g.writeln(': ')
|
||||
} else if expr is ast.RangeExpr {
|
||||
// low, high := (expr.low as ast.IntegerLiteral).val.int(), (expr.high as ast.IntegerLiteral).val.int()
|
||||
// for val in (enum_typ.info as ast.Enum).vals[low..high + 1] {
|
||||
// covered_enum << val
|
||||
// g.writeln('case $cname$val:')
|
||||
// }
|
||||
}
|
||||
}
|
||||
}
|
||||
g.indent++
|
||||
g.writeln('{')
|
||||
g.stmts_with_tmp_var(branch.stmts, tmp_var)
|
||||
g.writeln('} break;')
|
||||
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.get_type_symbol(node.cond_type)
|
||||
for j, branch in node.branches {
|
||||
|
|
|
@ -1956,10 +1956,14 @@ fn (mut g JsGen) gen_lock_expr(node ast.LockExpr) {
|
|||
|
||||
fn (mut g JsGen) 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.get_final_type_symbol(node.cond_type)
|
||||
sym := g.table.get_type_symbol(node.return_type)
|
||||
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
|
||||
|
@ -2165,6 +2169,7 @@ fn (mut g JsGen) match_expr(node ast.MatchExpr) {
|
|||
is_expr := (node.is_expr && node.return_type != ast.void_type) || g.inside_ternary
|
||||
mut cond_var := MatchCond(CondString{''})
|
||||
mut tmp_var := ''
|
||||
mut cur_line := ''
|
||||
if is_expr && !need_tmp_var {
|
||||
g.inside_ternary = true
|
||||
}
|
||||
|
@ -2180,17 +2185,23 @@ fn (mut g JsGen) match_expr(node ast.MatchExpr) {
|
|||
g.writeln(';')
|
||||
}
|
||||
if need_tmp_var {
|
||||
g.empty_line = true
|
||||
cur_line = g.out.cut_to(g.stmt_start_pos).trim_left(' \t')
|
||||
tmp_var = g.new_tmp_var()
|
||||
g.writeln('let $tmp_var = undefined;')
|
||||
}
|
||||
if is_expr && !need_tmp_var {
|
||||
g.write('(')
|
||||
}
|
||||
typ := g.table.get_final_type_symbol(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.inside_loop && 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)
|
||||
} else {
|
||||
g.match_expr_classic(node, is_expr, cond_var, tmp_var)
|
||||
}
|
||||
g.write(cur_line)
|
||||
if need_tmp_var {
|
||||
g.write('$tmp_var')
|
||||
}
|
||||
|
@ -2302,6 +2313,32 @@ fn (mut g JsGen) match_expr_sumtype(node ast.MatchExpr, is_expr bool, cond_var M
|
|||
}
|
||||
}
|
||||
|
||||
fn (mut g JsGen) match_expr_switch(node ast.MatchExpr, is_expr bool, cond_var MatchCond, tmp_var string) {
|
||||
g.empty_line = true
|
||||
g.write('switch (')
|
||||
g.match_cond(cond_var)
|
||||
g.writeln(') {')
|
||||
g.inc_indent()
|
||||
for branch in node.branches {
|
||||
if branch.is_else {
|
||||
g.writeln('default:')
|
||||
} else {
|
||||
for expr in branch.exprs {
|
||||
g.write('case ')
|
||||
g.expr(expr)
|
||||
g.writeln(': ')
|
||||
}
|
||||
}
|
||||
g.inc_indent()
|
||||
g.writeln('{')
|
||||
g.stmts_with_tmp_var(branch.stmts, tmp_var)
|
||||
g.writeln('} break;')
|
||||
g.dec_indent()
|
||||
}
|
||||
g.dec_indent()
|
||||
g.writeln('}')
|
||||
}
|
||||
|
||||
fn (mut g JsGen) need_tmp_var_in_if(node ast.IfExpr) bool {
|
||||
if node.is_expr && g.inside_ternary {
|
||||
if node.typ.has_flag(.optional) {
|
||||
|
|
Loading…
Reference in New Issue