v.gen.js: simplify comptime if; catch up with cgen (fix #25295) (#25313)

This commit is contained in:
kbkpbot 2025-09-16 00:24:25 +08:00 committed by GitHub
parent 89a24958a1
commit d2174e47a5
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 96 additions and 348 deletions

View File

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

View File

@ -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')
}

View File

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