cgen & jsgen: generate when possible a `switch` from `match x {` (#12216)

pull/12226/head
ChAoS_UnItY 2021-10-18 15:59:52 +08:00 committed by GitHub
parent a3de67de28
commit 53c2e262f1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 114 additions and 0 deletions

View File

@ -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 {

View File

@ -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) {