checker,cgen: propagate comptime computation results from the checker phase to the code generation (cgen) phase

This commit is contained in:
kbkpbot 2025-08-21 21:53:43 +08:00
parent 2e23f2a18b
commit 93777396eb
7 changed files with 250 additions and 52 deletions

View file

@ -1235,7 +1235,6 @@ pub:
comments []Comment comments []Comment
pub mut: pub mut:
cond Expr cond Expr
pkg_exist bool
stmts []Stmt stmts []Stmt
scope &Scope = unsafe { nil } scope &Scope = unsafe { nil }
} }

View file

@ -94,6 +94,7 @@ pub mut:
anon_struct_counter int anon_struct_counter int
anon_union_names map[string]int // anon union name -> union sym idx anon_union_names map[string]int // anon union name -> union sym idx
anon_union_counter int anon_union_counter int
comptime_is_true map[string]bool // The evaluate cond results for different generic types combination, such as `comptime_is_true['T=int,X=string|main.v|pos ...'] = true`
} }
// used by vls to avoid leaks // used by vls to avoid leaks

View file

@ -256,7 +256,11 @@ fn (mut c Checker) comptime_for(mut node ast.ComptimeFor) {
node.typ = c.expr(mut node.expr) node.typ = c.expr(mut node.expr)
c.unwrap_generic(node.typ) c.unwrap_generic(node.typ)
} }
sym := c.table.final_sym(typ) sym := if node.typ != c.field_data_type {
c.table.final_sym(typ)
} else {
c.table.final_sym(c.comptime.comptime_for_field_type)
}
if sym.kind == .placeholder || typ.has_flag(.generic) { if sym.kind == .placeholder || typ.has_flag(.generic) {
c.error('\$for expects a type name or variable name to be used here, but ${sym.name} is not a type or variable name', c.error('\$for expects a type name or variable name to be used here, but ${sym.name} is not a type or variable name',
node.typ_pos) node.typ_pos)

View file

@ -4,6 +4,60 @@ module checker
import v.ast import v.ast
import v.token import v.token
import v.util
// gen_branch_context_string generate current branches context string.
// context include generic types, `$for`.
fn (mut c Checker) gen_branch_context_string() string {
mut arr := []string{}
// gen `T=int,X=string`
if c.table.cur_fn.generic_names.len > 0
&& c.table.cur_fn.generic_names.len == c.table.cur_concrete_types.len {
for i in 0 .. c.table.cur_fn.generic_names.len {
arr << c.table.cur_fn.generic_names[i] + '=' +
util.strip_main_name(c.table.type_to_str(c.table.cur_concrete_types[i]))
}
}
// gen comptime `$for`
if c.comptime.inside_comptime_for {
// variants
if c.comptime.comptime_for_variant_var.len > 0 {
variant := c.table.type_to_str(c.type_resolver.get_ct_type_or_default('${c.comptime.comptime_for_variant_var}.typ',
ast.no_type))
arr << c.comptime.comptime_for_variant_var + '.typ=' + variant
}
// fields
if c.comptime.comptime_for_field_var.len > 0 {
arr << c.comptime.comptime_for_field_var + '.name=' +
c.comptime.comptime_for_field_value.name
}
// values
if c.comptime.comptime_for_enum_var.len > 0 {
enum_var := c.table.type_to_str(c.type_resolver.get_ct_type_or_default('${c.comptime.comptime_for_enum_var}.typ',
ast.void_type))
arr << c.comptime.comptime_for_enum_var + '.typ=' + enum_var
}
// attributes
if c.comptime.comptime_for_attr_var.len > 0 {
arr << c.comptime.comptime_for_attr_var + '.name=' +
c.comptime.comptime_for_attr_value.name
}
// methods
if c.comptime.comptime_for_method_var.len > 0 {
arr << c.comptime.comptime_for_method_var + '.name=' +
c.comptime.comptime_for_method.name
}
// args
if c.comptime.comptime_for_method_param_var.len > 0 {
arg_var := c.table.type_to_str(c.type_resolver.get_ct_type_or_default('${c.comptime.comptime_for_method_param_var}.typ',
ast.void_type))
arr << c.comptime.comptime_for_method_param_var + '.typ=' + arg_var
}
}
return arr.join(',')
}
fn (mut c Checker) if_expr(mut node ast.IfExpr) ast.Type { fn (mut c Checker) if_expr(mut node ast.IfExpr) ast.Type {
if_kind := if node.is_comptime { '\$if' } else { 'if' } if_kind := if node.is_comptime { '\$if' } else { 'if' }
@ -40,8 +94,10 @@ fn (mut c Checker) if_expr(mut node ast.IfExpr) ast.Type {
c.comptime.inside_comptime_if = last_in_comptime_if c.comptime.inside_comptime_if = last_in_comptime_if
} }
for i in 0 .. node.branches.len { comptime_branch_context_str := if node.is_comptime { c.gen_branch_context_string() } else { '' }
mut branch := node.branches[i]
for i, mut branch in node.branches {
mut comptime_remove_curr_branch_stmts := false
if branch.cond is ast.ParExpr && !c.pref.translated && !c.file.is_translated { if branch.cond is ast.ParExpr && !c.pref.translated && !c.file.is_translated {
c.warn('unnecessary `()` in `${if_kind}` condition, use `${if_kind} expr {` instead of `${if_kind} (expr) {`.', c.warn('unnecessary `()` in `${if_kind}` condition, use `${if_kind} expr {` instead of `${if_kind} (expr) {`.',
branch.pos) branch.pos)
@ -49,17 +105,37 @@ fn (mut c Checker) if_expr(mut node ast.IfExpr) ast.Type {
if !node.has_else || i < node.branches.len - 1 { if !node.has_else || i < node.branches.len - 1 {
// if branch // if branch
if node.is_comptime { if node.is_comptime {
// `idx_str` is composed of two parts:
// The first part represents the current context of the branch statement, `comptime_branch_context_str`, formatted like `T=int,X=string,method.name=json`
// The second part indicates the branch's location in the source file.
// This format must match what is in `cgen`.
idx_str := comptime_branch_context_str + '|${c.file.path}|${branch.pos}|'
c.comptime.inside_comptime_if = true c.comptime.inside_comptime_if = true
comptime_if_result, comptime_if_multi_pass_branch = c.comptime_if_cond(mut branch.cond) comptime_if_result, comptime_if_multi_pass_branch = c.comptime_if_cond(mut branch.cond)
node.branches[i].pkg_exist = comptime_if_result
if comptime_if_multi_pass_branch { if comptime_if_multi_pass_branch {
comptime_if_has_multi_pass_branch = true comptime_if_has_multi_pass_branch = true
} }
if !comptime_if_has_multi_pass_branch && comptime_if_found_branch { comptime_if_result = if comptime_if_found_branch {
// when all prev branchs are single pass branchs, and already has a true branch: false
// remove following branchs' stmts by overwrite `comptime_if_result` } else {
comptime_if_result = false comptime_if_result
} }
if !comptime_if_has_multi_pass_branch
&& (comptime_if_found_branch || !comptime_if_result) {
// when all prev branchs are single pass branchs,
// 1. already has a true branch or
// 2. `comptime_if_result is` false
// remove current branchs' stmts
comptime_remove_curr_branch_stmts = true
}
if old_val := c.table.comptime_is_true[idx_str] {
if old_val != comptime_if_result {
c.error('checker error : branch eval wrong', branch.pos)
}
}
// set `comptime_is_true` which can be used by `cgen`
c.table.comptime_is_true[idx_str] = comptime_if_result
} else { } else {
// check condition type is boolean // check condition type is boolean
c.expected_type = ast.bool_type c.expected_type = ast.bool_type
@ -78,6 +154,13 @@ fn (mut c Checker) if_expr(mut node ast.IfExpr) ast.Type {
comptime_if_result = !comptime_if_found_branch comptime_if_result = !comptime_if_found_branch
// if all other branchs has at least one multi pass branch, we should keep this else branch // if all other branchs has at least one multi pass branch, we should keep this else branch
comptime_if_multi_pass_branch = comptime_if_has_multi_pass_branch comptime_if_multi_pass_branch = comptime_if_has_multi_pass_branch
if !comptime_if_has_multi_pass_branch && comptime_if_found_branch {
// when all prev branchs are single pass branchs, already has a true branch
// remove current branchs' stmts
comptime_remove_curr_branch_stmts = true
}
idx_str := comptime_branch_context_str + '|${c.file.path}|${branch.pos}|'
c.table.comptime_is_true[idx_str] = comptime_if_result
} }
} }
if mut branch.cond is ast.IfGuardExpr { if mut branch.cond is ast.IfGuardExpr {
@ -119,7 +202,7 @@ fn (mut c Checker) if_expr(mut node ast.IfExpr) ast.Type {
if c.fn_level == 0 && c.pref.output_cross_c { if c.fn_level == 0 && c.pref.output_cross_c {
// do not skip any of the branches for top level `$if OS {` // do not skip any of the branches for top level `$if OS {`
// statements, in `-cross` mode // statements, in `-cross` mode
comptime_if_multi_pass_branch = true comptime_remove_curr_branch_stmts = false
c.skip_flags = false c.skip_flags = false
c.ct_cond_stack << branch.cond c.ct_cond_stack << branch.cond
} }
@ -147,9 +230,6 @@ fn (mut c Checker) if_expr(mut node ast.IfExpr) ast.Type {
c.stmts(mut branch.stmts) c.stmts(mut branch.stmts)
c.check_non_expr_branch_last_stmt(branch.stmts) c.check_non_expr_branch_last_stmt(branch.stmts)
} }
} else if !comptime_if_multi_pass_branch && !comptime_if_result {
// this branch is not a multi pass branch, and current cond result is false, remove branch stmts
node.branches[i].stmts = []
} }
c.skip_flags = cur_skip_flags c.skip_flags = cur_skip_flags
if c.fn_level == 0 && c.pref.output_cross_c && c.ct_cond_stack.len > 0 { if c.fn_level == 0 && c.pref.output_cross_c && c.ct_cond_stack.len > 0 {
@ -322,6 +402,11 @@ fn (mut c Checker) if_expr(mut node ast.IfExpr) ast.Type {
nbranches_without_return++ nbranches_without_return++
} }
} }
if comptime_remove_curr_branch_stmts {
// remove the branch statements since they may contain OS-specific code.
branch.stmts = []
}
} }
if nbranches_with_return > 0 { if nbranches_with_return > 0 {
if nbranches_with_return == node.branches.len { if nbranches_with_return == node.branches.len {

View file

@ -313,6 +313,59 @@ fn (mut g Gen) comptime_at(node ast.AtExpr) {
} }
} }
// gen_branch_context_string generate current branches context string.
// context include generic types, `$for`.
fn (mut g Gen) gen_branch_context_string() string {
mut arr := []string{}
// gen `T=int,X=string`
if g.cur_fn != unsafe { nil } && g.cur_fn.generic_names.len > 0
&& g.cur_fn.generic_names.len == g.cur_concrete_types.len {
for i in 0 .. g.cur_fn.generic_names.len {
arr << g.cur_fn.generic_names[i] + '=' +
util.strip_main_name(g.table.type_to_str(g.cur_concrete_types[i]))
}
}
// gen comptime `$for`
if g.comptime.inside_comptime_for {
// variants
if g.comptime.comptime_for_variant_var.len > 0 {
variant := g.table.type_to_str(g.type_resolver.get_ct_type_or_default('${g.comptime.comptime_for_variant_var}.typ',
ast.no_type))
arr << g.comptime.comptime_for_variant_var + '.typ=' + variant
}
// fields
if g.comptime.comptime_for_field_var.len > 0 {
arr << g.comptime.comptime_for_field_var + '.name=' +
g.comptime.comptime_for_field_value.name
}
// values
if g.comptime.comptime_for_enum_var.len > 0 {
enum_var := g.table.type_to_str(g.type_resolver.get_ct_type_or_default('${g.comptime.comptime_for_enum_var}.typ',
ast.void_type))
arr << g.comptime.comptime_for_enum_var + '.typ=' + enum_var
}
// attributes
if g.comptime.comptime_for_attr_var.len > 0 {
arr << g.comptime.comptime_for_attr_var + '.name=' +
g.comptime.comptime_for_attr_value.name
}
// methods
if g.comptime.comptime_for_method_var.len > 0 {
arr << g.comptime.comptime_for_method_var + '.name=' +
g.comptime.comptime_for_method.name
}
// args
if g.comptime.comptime_for_method_param_var.len > 0 {
arg_var := g.table.type_to_str(g.type_resolver.get_ct_type_or_default('${g.comptime.comptime_for_method_param_var}.typ',
ast.void_type))
arr << g.comptime.comptime_for_method_param_var + '.typ=' + arg_var
}
}
return arr.join(',')
}
fn (mut g Gen) comptime_if(node ast.IfExpr) { fn (mut g Gen) comptime_if(node ast.IfExpr) {
if !node.is_expr && !node.has_else && node.branches.len == 1 { if !node.is_expr && !node.has_else && node.branches.len == 1 {
if node.branches[0].stmts.len == 0 { if node.branches[0].stmts.len == 0 {
@ -346,32 +399,46 @@ fn (mut g Gen) comptime_if(node ast.IfExpr) {
} else { } else {
'' ''
} }
mut comptime_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)
mut comptime_may_skip_else := false
mut comptime_branch_context_str := g.gen_branch_context_string()
mut is_true := false
for i, branch in node.branches { for i, branch in node.branches {
start_pos := g.out.len start_pos := g.out.len
if comptime_may_skip_else { // `idx_str` is composed of two parts:
continue // if we already have a known true, ignore other branches // The first part represents the current context of the branch statement, `comptime_branch_context_str`, formatted like `T=int,X=string,method.name=json`
} // The second part indicates the branch's location in the source file.
if i == node.branches.len - 1 && node.has_else { // This format must match what is in `checker`.
g.writeln('#else') idx_str := comptime_branch_context_str + '|${g.file.path}|${branch.pos}|'
comptime_if_stmts_skip = comptime_may_skip_else if comptime_is_true := g.table.comptime_is_true[idx_str] {
// `g.table.comptime_is_true` are the branch condition results set by `checker`
is_true = comptime_is_true
} else { } else {
g.error('checker error: condition result idx string not found => [${idx_str}]',
node.branches[i].cond.pos())
return
}
if !node.has_else || i < node.branches.len - 1 {
if i == 0 { if i == 0 {
g.write('#if ') g.write('#if ')
} else { } else {
g.write('#elif ') g.write('#elif ')
} }
comptime_if_stmts_skip, comptime_may_skip_else = g.comptime_if_cond(branch.cond, if g.pref.output_cross_c {
branch.pkg_exist) // generate full `if defined` in cross mode
if !comptime_if_stmts_skip && comptime_may_skip_else { pos := g.out.len
comptime_may_skip_else = false // if the cond is false, not skip else branch g.comptime_if_cond(branch.cond, true)
cond_str := g.out.cut_to(pos).trim_space()
g.writeln(cond_str)
} else {
// directly use `checker` evaluate results
if is_true {
g.writeln('1\t/* ${node.branches[i].cond} | generic=[${comptime_branch_context_str}] */')
} else {
g.writeln('0\t/* ${node.branches[i].cond} | generic=[${comptime_branch_context_str}] */')
} }
comptime_if_stmts_skip = !comptime_if_stmts_skip }
g.writeln('') } else {
g.writeln('#else')
} }
expr_str := g.out.last_n(g.out.len - start_pos).trim_space() expr_str := g.out.last_n(g.out.len - start_pos).trim_space()
if expr_str != '' { if expr_str != '' {
@ -430,7 +497,7 @@ fn (mut g Gen) comptime_if(node ast.IfExpr) {
if should_create_scope { if should_create_scope {
g.writeln('{') g.writeln('{')
} }
if !comptime_if_stmts_skip { if is_true {
g.stmts(branch.stmts) g.stmts(branch.stmts)
} }
if should_create_scope { if should_create_scope {
@ -847,6 +914,8 @@ fn (mut g Gen) push_new_comptime_info() {
comptime_for_field_type: g.comptime.comptime_for_field_type comptime_for_field_type: g.comptime.comptime_for_field_type
comptime_for_field_value: g.comptime.comptime_for_field_value comptime_for_field_value: g.comptime.comptime_for_field_value
comptime_for_enum_var: g.comptime.comptime_for_enum_var comptime_for_enum_var: g.comptime.comptime_for_enum_var
comptime_for_attr_var: g.comptime.comptime_for_attr_var
comptime_for_attr_value: g.comptime.comptime_for_attr_value
comptime_for_method_var: g.comptime.comptime_for_method_var comptime_for_method_var: g.comptime.comptime_for_method_var
comptime_for_method: g.comptime.comptime_for_method comptime_for_method: g.comptime.comptime_for_method
comptime_for_method_ret_type: g.comptime.comptime_for_method_ret_type comptime_for_method_ret_type: g.comptime.comptime_for_method_ret_type
@ -864,6 +933,8 @@ fn (mut g Gen) pop_comptime_info() {
g.comptime.comptime_for_field_type = old.comptime_for_field_type g.comptime.comptime_for_field_type = old.comptime_for_field_type
g.comptime.comptime_for_field_value = old.comptime_for_field_value g.comptime.comptime_for_field_value = old.comptime_for_field_value
g.comptime.comptime_for_enum_var = old.comptime_for_enum_var g.comptime.comptime_for_enum_var = old.comptime_for_enum_var
g.comptime.comptime_for_attr_var = old.comptime_for_attr_var
g.comptime.comptime_for_attr_value = old.comptime_for_attr_value
g.comptime.comptime_for_method_var = old.comptime_for_method_var g.comptime.comptime_for_method_var = old.comptime_for_method_var
g.comptime.comptime_for_method = old.comptime_for_method g.comptime.comptime_for_method = old.comptime_for_method
g.comptime.comptime_for_method_ret_type = old.comptime_for_method_ret_type g.comptime.comptime_for_method_ret_type = old.comptime_for_method_ret_type
@ -887,6 +958,7 @@ fn (mut g Gen) comptime_for(node ast.ComptimeFor) {
typ_vweb_result := g.table.find_type('vweb.Result') typ_vweb_result := g.table.find_type('vweb.Result')
for method in methods { for method in methods {
g.push_new_comptime_info() g.push_new_comptime_info()
g.comptime.inside_comptime_for = true
// filter vweb route methods (non-generic method) // filter vweb route methods (non-generic method)
if method.receiver_type != 0 && method.return_type == typ_vweb_result { if method.receiver_type != 0 && method.return_type == typ_vweb_result {
rec_sym := g.table.sym(method.receiver_type) rec_sym := g.table.sym(method.receiver_type)
@ -902,7 +974,7 @@ fn (mut g Gen) comptime_for(node ast.ComptimeFor) {
} }
g.comptime.comptime_for_method = unsafe { &method } g.comptime.comptime_for_method = unsafe { &method }
g.comptime.comptime_for_method_var = node.val_var g.comptime.comptime_for_method_var = node.val_var
g.writeln('/* method ${i} */ {') g.writeln('/* method ${i} : ${method.name} */ {')
g.writeln('\t${node.val_var}.name = _S("${method.name}");') g.writeln('\t${node.val_var}.name = _S("${method.name}");')
if method.attrs.len == 0 { if method.attrs.len == 0 {
g.writeln('\t${node.val_var}.attrs = __new_array_with_default(0, 0, sizeof(string), 0);') g.writeln('\t${node.val_var}.attrs = __new_array_with_default(0, 0, sizeof(string), 0);')
@ -976,13 +1048,13 @@ fn (mut g Gen) comptime_for(node ast.ComptimeFor) {
if fields.len > 0 { if fields.len > 0 {
g.writeln('\tFieldData ${node.val_var} = {0};') g.writeln('\tFieldData ${node.val_var} = {0};')
} }
g.push_new_comptime_info()
for field in fields { for field in fields {
g.push_new_comptime_info()
g.comptime.inside_comptime_for = true g.comptime.inside_comptime_for = true
g.comptime.comptime_for_field_var = node.val_var g.comptime.comptime_for_field_var = node.val_var
g.comptime.comptime_for_field_value = field g.comptime.comptime_for_field_value = field
g.comptime.comptime_for_field_type = field.typ g.comptime.comptime_for_field_type = field.typ
g.writeln('/* field ${i} */ {') g.writeln('/* field ${i} : ${field.name} */ {')
g.writeln('\t${node.val_var}.name = _S("${field.name}");') g.writeln('\t${node.val_var}.name = _S("${field.name}");')
if field.attrs.len == 0 { if field.attrs.len == 0 {
g.writeln('\t${node.val_var}.attrs = __new_array_with_default(0, 0, sizeof(string), 0);') g.writeln('\t${node.val_var}.attrs = __new_array_with_default(0, 0, sizeof(string), 0);')
@ -1019,17 +1091,18 @@ fn (mut g Gen) comptime_for(node ast.ComptimeFor) {
g.stmts(node.stmts) g.stmts(node.stmts)
i++ i++
g.writeln('}') g.writeln('}')
}
g.pop_comptime_info() g.pop_comptime_info()
} }
}
} else if node.kind == .values { } else if node.kind == .values {
if sym.kind == .enum { if sym.kind == .enum {
if sym.info is ast.Enum { if sym.info is ast.Enum {
if sym.info.vals.len > 0 { if sym.info.vals.len > 0 {
g.writeln('\tEnumData ${node.val_var} = {0};') g.writeln('\tEnumData ${node.val_var} = {0};')
} }
g.push_new_comptime_info()
for val in sym.info.vals { for val in sym.info.vals {
g.push_new_comptime_info()
g.comptime.inside_comptime_for = true
g.comptime.comptime_for_enum_var = node.val_var g.comptime.comptime_for_enum_var = node.val_var
g.type_resolver.update_ct_type('${node.val_var}.typ', node.typ) g.type_resolver.update_ct_type('${node.val_var}.typ', node.typ)
@ -1053,17 +1126,21 @@ fn (mut g Gen) comptime_for(node ast.ComptimeFor) {
g.stmts(node.stmts) g.stmts(node.stmts)
g.writeln('}') g.writeln('}')
i++ i++
}
g.pop_comptime_info() g.pop_comptime_info()
} }
} }
}
} else if node.kind == .attributes { } else if node.kind == .attributes {
attrs := g.table.get_attrs(sym) attrs := g.table.get_attrs(sym)
if attrs.len > 0 { if attrs.len > 0 {
g.writeln('\tVAttribute ${node.val_var} = {0};') g.writeln('\tVAttribute ${node.val_var} = {0};')
for attr in attrs { for attr in attrs {
g.writeln('/* attribute ${i} */ {') g.push_new_comptime_info()
g.comptime.inside_comptime_for = true
g.comptime.comptime_for_attr_var = node.val_var
g.comptime.comptime_for_attr_value = attr
g.writeln('/* attribute ${i} : ${attr.name} */ {')
g.writeln('\t${node.val_var}.name = _S("${attr.name}");') g.writeln('\t${node.val_var}.name = _S("${attr.name}");')
g.writeln('\t${node.val_var}.has_arg = ${attr.has_arg};') g.writeln('\t${node.val_var}.has_arg = ${attr.has_arg};')
g.writeln('\t${node.val_var}.arg = _S("${util.smart_quote(attr.arg, false)}");') g.writeln('\t${node.val_var}.arg = _S("${util.smart_quote(attr.arg, false)}");')
@ -1071,6 +1148,7 @@ fn (mut g Gen) comptime_for(node ast.ComptimeFor) {
g.stmts(node.stmts) g.stmts(node.stmts)
g.writeln('}') g.writeln('}')
i++ i++
g.pop_comptime_info()
} }
} }
} else if node.kind == .variants { } else if node.kind == .variants {
@ -1079,40 +1157,41 @@ fn (mut g Gen) comptime_for(node ast.ComptimeFor) {
g.writeln('\tVariantData ${node.val_var} = {0};') g.writeln('\tVariantData ${node.val_var} = {0};')
} }
g.comptime.inside_comptime_for = true g.comptime.inside_comptime_for = true
g.push_new_comptime_info()
for variant in sym.info.variants { for variant in sym.info.variants {
g.push_new_comptime_info()
g.comptime.inside_comptime_for = true
g.comptime.comptime_for_variant_var = node.val_var g.comptime.comptime_for_variant_var = node.val_var
g.type_resolver.update_ct_type('${node.val_var}.typ', variant) g.type_resolver.update_ct_type('${node.val_var}.typ', variant)
g.writeln('/* variant ${i} */ {') g.writeln('/* variant ${i} : ${g.table.type_to_str(variant)} */ {')
g.writeln('\t${node.val_var}.typ = ${int(variant)};') g.writeln('\t${node.val_var}.typ = ${int(variant)};')
g.stmts(node.stmts) g.stmts(node.stmts)
g.writeln('}') g.writeln('}')
i++ i++
}
g.pop_comptime_info() g.pop_comptime_info()
} }
}
} else if node.kind == .params { } else if node.kind == .params {
method := g.comptime.comptime_for_method method := g.comptime.comptime_for_method
if method.params.len > 0 { if method.params.len > 0 {
g.writeln('\tMethodParam ${node.val_var} = {0};') g.writeln('\tMethodParam ${node.val_var} = {0};')
} }
for param in method.params[1..] {
g.push_new_comptime_info() g.push_new_comptime_info()
g.comptime.inside_comptime_for = true g.comptime.inside_comptime_for = true
g.comptime.comptime_for_method_param_var = node.val_var g.comptime.comptime_for_method_param_var = node.val_var
for param in method.params[1..] {
g.type_resolver.update_ct_type('${node.val_var}.typ', param.typ) g.type_resolver.update_ct_type('${node.val_var}.typ', param.typ)
g.writeln('/* method param ${i} */ {') g.writeln('/* method param ${i} : ${param.name} */ {')
g.writeln('\t${node.val_var}.typ = ${int(param.typ)};') g.writeln('\t${node.val_var}.typ = ${int(param.typ)};')
g.writeln('\t${node.val_var}.name = _S("${param.name}");') g.writeln('\t${node.val_var}.name = _S("${param.name}");')
g.stmts(node.stmts) g.stmts(node.stmts)
g.writeln('}') g.writeln('}')
i++ i++
}
g.pop_comptime_info() g.pop_comptime_info()
} }
}
g.indent-- g.indent--
g.writeln('}// \$for') g.writeln('}// \$for')
} }

View file

@ -2,6 +2,22 @@ module js
import v.ast import v.ast
fn (mut g JsGen) gen_cond_generic_string() string {
mut arr := []string{}
// gen `T=int,X=string`
if g.fn_decl != unsafe { nil } && g.fn_decl.generic_names.len > 0
&& g.fn_decl.generic_names.len == g.cur_concrete_types.len {
for i in 0 .. g.fn_decl.generic_names.len {
arr << g.fn_decl.generic_names[i] + '=' +
g.table.type_to_str(g.cur_concrete_types[i]).replace('main.', '')
}
}
// TODO: support comptime `$for`
return arr.join(',')
}
fn (mut g JsGen) comptime_if(node ast.IfExpr) { fn (mut g JsGen) comptime_if(node ast.IfExpr) {
if !node.is_expr && !node.has_else && node.branches.len == 1 { if !node.is_expr && !node.has_else && node.branches.len == 1 {
if node.branches[0].stmts.len == 0 { if node.branches[0].stmts.len == 0 {
@ -10,7 +26,16 @@ fn (mut g JsGen) comptime_if(node ast.IfExpr) {
} }
} }
mut comptime_generic_str := g.gen_cond_generic_string()
mut is_true := false
for i, branch in node.branches { for i, branch in node.branches {
idx_str := comptime_generic_str + '|${g.file.path}|${branch.pos}|'
if comptime_is_true := g.table.comptime_is_true[idx_str] {
is_true = comptime_is_true
} else {
panic('checker error: cond result idx string not found => [${idx_str}]')
return
}
if i == node.branches.len - 1 && node.has_else { if i == node.branches.len - 1 && node.has_else {
g.writeln('else') g.writeln('else')
} else { } else {
@ -19,8 +44,11 @@ fn (mut g JsGen) comptime_if(node ast.IfExpr) {
} else { } else {
g.write('else if (') g.write('else if (')
} }
g.comptime_if_cond(branch.cond, branch.pkg_exist) if is_true {
g.writeln(')') g.writeln('1)\t// ${node.branches[i].cond} generic=[${comptime_generic_str}]')
} else {
g.writeln('0)\t// ${node.branches[i].cond} generic=[${comptime_generic_str}]')
}
} }
if node.is_expr { if node.is_expr {
@ -45,7 +73,9 @@ fn (mut g JsGen) comptime_if(node ast.IfExpr) {
} }
} else { } else {
g.writeln('{') g.writeln('{')
if is_true {
g.stmts(branch.stmts) g.stmts(branch.stmts)
}
g.writeln('}') g.writeln('}')
} }
} }

View file

@ -30,7 +30,7 @@ fn test[T](val T) string {
fn test_main() { fn test_main() {
assert test(u32(7)) == 'else block' assert test(u32(7)) == 'else block'
assert test(OtherStruct{'7'}) == 'struct field string' assert test(OtherStruct{'7'}) == 'not u32 or i32 struct field, got type: string'
assert test(I32Struct{-7}) == 'struct field i32' assert test(I32Struct{-7}) == 'struct field i32'
assert test(U32Struct{7}) == 'struct field u32' assert test(U32Struct{7}) == 'struct field u32'
} }