From d2174e47a5aaf0a61645ef1413f6191f0fc3dcf6 Mon Sep 17 00:00:00 2001 From: kbkpbot Date: Tue, 16 Sep 2025 00:24:25 +0800 Subject: [PATCH] v.gen.js: simplify comptime if; catch up with cgen (fix #25295) (#25313) --- vlib/builtin/js/int.js.v | 12 +- vlib/v/gen/js/comptime.v | 337 ++------------------------------------- vlib/v/gen/js/js.v | 95 ++++++++--- 3 files changed, 96 insertions(+), 348 deletions(-) diff --git a/vlib/builtin/js/int.js.v b/vlib/builtin/js/int.js.v index 1e31ee5e3a..ef5b73c363 100644 --- a/vlib/builtin/js/int.js.v +++ b/vlib/builtin/js/int.js.v @@ -17,8 +17,16 @@ pub const max_i32 = i32(2147483647) pub const min_i64 = i64(-9223372036854775807 - 1) pub const max_i64 = i64(9223372036854775807) -pub const min_int = int(min_i32) -pub const max_int = int(max_i32) +pub const min_int = $if new_int ? && (arm64 || amd64 || rv64 || s390x || ppc64le || loongarch64) { + int(min_i64) +} $else { + int(min_i32) +} +pub const max_int = $if new_int ? && (arm64 || amd64 || rv64 || s390x || ppc64le || loongarch64) { + int(max_i64) +} $else { + int(max_i32) +} pub const min_u8 = u8(0) pub const max_u8 = u8(255) diff --git a/vlib/v/gen/js/comptime.v b/vlib/v/gen/js/comptime.v index 801304c5f5..2d88d06860 100644 --- a/vlib/v/gen/js/comptime.v +++ b/vlib/v/gen/js/comptime.v @@ -18,334 +18,15 @@ fn (mut g JsGen) gen_branch_context_string() string { return arr.join(',') } -fn (mut g JsGen) comptime_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 - } +fn (mut g JsGen) comptime_if_result(branch ast.IfBranch) bool { + idx_str := g.gen_branch_context_string() + '|id=${branch.id}|' + $if debug_comptime_branch_context ? { + g.write('/* ${idx_str} */') } - - mut comptime_branch_context_str := g.gen_branch_context_string() - mut is_true := ast.ComptTimeCondResult{} - for i, branch in node.branches { - idx_str := comptime_branch_context_str + '|id=${branch.id}|' - 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 { - g.writeln('else') - } else { - result := if is_true.val { '1' } else { '0' } - if i == 0 { - g.writeln('if (${result})') - } else { - g.writeln('else if (${result})') - } - $if debug_comptime_branch_context ? { - g.writeln('// ${node.branches[i].cond} generic=[${comptime_branch_context_str}]') - } - } - - if node.is_expr { - print('${branch.stmts}') - len := branch.stmts.len - if len > 0 { - last := branch.stmts.last() as ast.ExprStmt - if len > 1 { - tmp := g.new_tmp_var() - g.inc_indent() - g.writeln('let ${tmp};') - g.writeln('{') - g.stmts(branch.stmts[..len - 1]) - g.write('\t${tmp} = ') - g.stmt(last) - g.writeln('}') - g.dec_indent() - g.writeln('${tmp};') - } else { - g.stmt(last) - } - } - } else { - g.writeln('{') - if is_true.val { - g.stmts(branch.stmts) - } - g.writeln('}') - } + if comptime_is_true := g.table.comptime_is_true[idx_str] { + return comptime_is_true.val + } else { + panic('checker error: cond result idx string not found => [${idx_str}]') + return false } } - -/* -// returning `false` means the statements inside the $if can be skipped -*/ -// returns the value of the bool comptime expression -fn (mut g JsGen) comptime_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.comptime_if_cond(cond.expr, pkg_exist) - g.write(')') - return is_cond_true - } - ast.PrefixExpr { - g.write(cond.op.str()) - return g.comptime_if_cond(cond.right, pkg_exist) - } - ast.PostfixExpr { - ifdef := g.comptime_if_to_ifdef((cond.expr as ast.Ident).name, true) or { - verror(err.msg()) - return false - } - g.write('${ifdef}') - return true - } - ast.InfixExpr { - match cond.op { - .and, .logical_or { - l := g.comptime_if_cond(cond.left, pkg_exist) - g.write(' ${cond.op} ') - r := g.comptime_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.no_type - 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.sym(got_type).kind == .interface { - // `$if Foo is Interface {` - interface_sym := g.table.sym(got_type) - if interface_sym.info is ast.Interface { - // q := g.table.sym(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 { - // 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.comptime_if_to_ifdef(cond.name, false) or { 'true' } // handled in checker - g.write('${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 JsGen) comptime_if_to_ifdef(name string, is_comptime_option bool) !string { - match name { - // platforms/os-es: - 'windows' { - return '(\$process.platform == "windows")' - } - 'ios' { - return '(\$process.platform == "darwin")' - } - 'macos' { - return '(\$process.platform == "darwin")' - } - 'mach' { - return '(\$process.platform == "darwin")' - } - 'darwin' { - return '(\$process.platform == "darwin")' - } - 'linux' { - return '(\$process.platform == "linux")' - } - 'freebsd' { - return '(\$process.platform == "freebsd")' - } - 'openbsd' { - return '(\$process.platform == "openbsd")' - } - 'bsd' { - return '(\$process.platform == "freebsd" || (\$process.platform == "openbsd"))' - } - 'android' { - return '(\$process.platform == "android")' - } - 'solaris' { - return '(\$process.platform == "sunos")' - } - 'js_node' { - if g.pref.backend == .js_node { - return 'true' - } else { - return 'false' - } - } - 'js_freestanding' { - if g.pref.backend == .js_freestanding { - return 'true' - } else { - return 'false' - } - } - 'js_browser' { - if g.pref.backend == .js_browser { - return 'true' - } else { - return 'false' - } - } - 'es5' { - if g.pref.output_es5 { - return 'true' - } else { - return 'false' - } - } - // - 'js' { - return 'true' - } - 'native' { - return 'false' - } - // compilers: - 'gcc' { - return 'false' - } - 'tinyc' { - return 'false' - } - 'clang' { - return 'false' - } - 'mingw' { - return 'false' - } - 'msvc' { - return 'false' - } - 'cplusplus' { - return 'false' - } - // other: - 'threads' { - return 'false' - } - 'gcboehm' { - return 'false' - } - // todo(playX): these should return true or false depending on CLI options - 'debug' { - return 'false' - } - 'prod' { - return 'false' - } - 'test' { - return 'false' - } - 'glibc' { - return 'false' - } - 'prealloc' { - return 'false' - } - 'no_bounds_checking' { - return 'checkDefine("CUSTOM_DEFINE_no_bounds_checking")' - } - 'freestanding' { - return '_VFREESTANDING' - } - 'autofree' { - return '_VAUTOFREE' - } - // architectures: - 'amd64' { - return '(\$process.arch == "x64")' - } - 'aarch64', 'arm64' { - return '(\$process.arch == "arm64)' - } - // bitness: - 'x64' { - return '(\$process.arch == "x64")' - } - 'x32' { - return '(\$process.arch == "x32")' - } - // endianness: - 'little_endian' { - return '(\$os.endianness == "LE")' - } - 'big_endian' { - return '(\$os.endianness == "BE")' - } - else { - if is_comptime_option - || (g.pref.compile_defines_all.len > 0 && name in g.pref.compile_defines_all) { - return 'checkDefine("CUSTOM_DEFINE_${name}")' - } - return error('bad os ifdef name "${name}"') // should never happen, caught in the checker - } - } - return error('none') -} diff --git a/vlib/v/gen/js/js.v b/vlib/v/gen/js/js.v index 6ec499619c..b47e484c98 100644 --- a/vlib/v/gen/js/js.v +++ b/vlib/v/gen/js/js.v @@ -2722,10 +2722,6 @@ fn (mut g JsGen) need_tmp_var_in_if(node ast.IfExpr) bool { } fn (mut g JsGen) 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 @@ -2734,6 +2730,8 @@ fn (mut g JsGen) gen_if_expr(node ast.IfExpr) { // 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 is_true := false + mut comptime_has_true_branch := false if needs_tmp_var { if node.typ.has_flag(.option) { @@ -2746,14 +2744,32 @@ fn (mut g JsGen) gen_if_expr(node ast.IfExpr) { prev := g.inside_ternary g.inside_ternary = true for i, branch in node.branches { - if i > 0 { - g.write(' : ') - } - if i < node.branches.len - 1 || !node.has_else { - g.write('(') - g.expr(branch.cond) - g.write(').valueOf()') - g.write(' ? ') + if node.is_comptime { + $if debug_comptime_branch_context ? { + g.write('/* ${branch.cond} */') + } + // comptime $if, only generate the true branch + if i < node.branches.len - 1 || !node.has_else { + if !g.comptime_if_result(branch) { + continue + } + comptime_has_true_branch = true + } else { + // else branch + if comptime_has_true_branch { + continue + } + } + } else { + if i > 0 { + g.write(' : ') + } + if i < node.branches.len - 1 || !node.has_else { + g.write('(') + g.expr(branch.cond) + g.write(').valueOf()') + g.write(' ? ') + } } g.stmts(branch.stmts) } @@ -2784,6 +2800,8 @@ fn (mut g JsGen) gen_if_expr(node ast.IfExpr) { } } + is_true = false + comptime_has_true_branch = false for i, branch in node.branches { if i > 0 { g.write('} else ') @@ -2796,6 +2814,9 @@ fn (mut g JsGen) gen_if_expr(node ast.IfExpr) { cvar_name := guard_vars[guard_idx] g.writeln('\tlet err = ${cvar_name}.err;') } + if node.is_comptime && !comptime_has_true_branch { + is_true = true + } } else { match branch.cond { ast.IfGuardExpr { @@ -2809,7 +2830,20 @@ fn (mut g JsGen) gen_if_expr(node ast.IfExpr) { g.writeln('if (${var_name}.state == 0) {') } else { g.write('if (${var_name} = ') - g.expr(branch.cond.expr) + if node.is_comptime { + $if debug_comptime_branch_context ? { + g.write('/* ${branch.cond} */') + } + is_true = g.comptime_if_result(branch) + if is_true { + g.write('1') + comptime_has_true_branch = true + } else { + g.write('0') + } + } else { + g.expr(branch.cond.expr) + } g.writeln(', ${var_name}.state == 0) {') } if short_opt || branch.cond.vars[0].name != '_' { @@ -2820,7 +2854,17 @@ fn (mut g JsGen) gen_if_expr(node ast.IfExpr) { branch.cond.vars[0].name } g.write('\tlet ${cond_var_name} = ') - g.expr(branch.cond.expr) + if node.is_comptime { + is_true = g.comptime_if_result(branch) + if is_true { + g.write('1') + comptime_has_true_branch = true + } else { + g.write('0') + } + } else { + g.expr(branch.cond.expr) + } g.writeln(';') } else { g.writeln('\tlet ${branch.cond.vars}[0].name = ${var_name}.data;') @@ -2828,16 +2872,31 @@ fn (mut g JsGen) gen_if_expr(node ast.IfExpr) { } } else { - g.write('if ((') - g.expr(branch.cond) - g.writeln(').valueOf()) {') + if node.is_comptime { + g.write('if (') + $if debug_comptime_branch_context ? { + g.write('/* ${branch.cond} */') + } + is_true = g.comptime_if_result(branch) + if is_true { + g.write('1') + comptime_has_true_branch = true + } else { + g.write('0') + } + g.writeln(') {') + } else { + g.write('if ((') + g.expr(branch.cond) + g.writeln(').valueOf()) {') + } } } } g.inc_indent() if needs_tmp_var { g.stmts_with_tmp_var(branch.stmts, tmp) - } else { + } else if (node.is_comptime && is_true) || !node.is_comptime { g.stmts(branch.stmts) } g.dec_indent()