mirror of
https://github.com/vlang/v.git
synced 2025-09-13 14:32:26 +03:00
This commit is contained in:
parent
7dd91ecef7
commit
a3f86eed1b
12 changed files with 605 additions and 461 deletions
|
@ -1538,7 +1538,6 @@ fn (t Tree) if_branch(node ast.IfBranch) &Node {
|
|||
obj.add_terse('cond', t.expr(node.cond))
|
||||
obj.add('pos', t.pos(node.pos))
|
||||
obj.add('body_pos', t.pos(node.body_pos))
|
||||
obj.add_terse('pkg_exist', t.bool_node(node.pkg_exist))
|
||||
obj.add_terse('stmts', t.array_node_stmt(node.stmts))
|
||||
obj.add('scope', t.number_node(int(node.scope)))
|
||||
obj.add('comments', t.array_node_comment(node.comments))
|
||||
|
|
|
@ -1234,10 +1234,9 @@ pub:
|
|||
body_pos token.Pos
|
||||
comments []Comment
|
||||
pub mut:
|
||||
cond Expr
|
||||
pkg_exist bool
|
||||
stmts []Stmt
|
||||
scope &Scope = unsafe { nil }
|
||||
cond Expr
|
||||
stmts []Stmt
|
||||
scope &Scope = unsafe { nil }
|
||||
}
|
||||
|
||||
pub struct UnsafeExpr {
|
||||
|
@ -2131,6 +2130,8 @@ pub fn (cc ComptimeCall) expr_str() string {
|
|||
if arg.expr.is_pure_literal() {
|
||||
str = "\$${cc.method_name}('${cc.args_var}', ${arg})"
|
||||
}
|
||||
} else if cc.kind == .pkgconfig {
|
||||
str = "\$${cc.method_name}('${cc.args_var}')"
|
||||
}
|
||||
return str
|
||||
}
|
||||
|
|
|
@ -94,6 +94,13 @@ pub mut:
|
|||
anon_struct_counter int
|
||||
anon_union_names map[string]int // anon union name -> union sym idx
|
||||
anon_union_counter int
|
||||
comptime_is_true map[string]ComptTimeCondResult // The evaluate cond results for different generic types combination, such as `comptime_is_true['T=int,X=string|main.v|pos ...'] = {true, '!DEFINED(WINDOWS)'}`
|
||||
}
|
||||
|
||||
pub struct ComptTimeCondResult {
|
||||
pub mut:
|
||||
val bool
|
||||
c_str string
|
||||
}
|
||||
|
||||
// used by vls to avoid leaks
|
||||
|
|
|
@ -256,7 +256,11 @@ fn (mut c Checker) comptime_for(mut node ast.ComptimeFor) {
|
|||
node.typ = c.expr(mut node.expr)
|
||||
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) {
|
||||
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)
|
||||
|
@ -655,7 +659,8 @@ fn (mut c Checker) eval_comptime_const_expr(expr ast.Expr, nlevel int) ?ast.Comp
|
|||
for i in 0 .. expr.branches.len {
|
||||
mut branch := expr.branches[i]
|
||||
if !expr.has_else || i < expr.branches.len - 1 {
|
||||
is_true, _ := c.comptime_if_cond(mut branch.cond)
|
||||
mut sb := strings.new_builder(256)
|
||||
is_true, _ := c.comptime_if_cond(mut branch.cond, mut sb)
|
||||
if is_true {
|
||||
last_stmt := branch.stmts.last()
|
||||
if last_stmt is ast.ExprStmt {
|
||||
|
@ -771,13 +776,199 @@ fn (mut c Checker) evaluate_once_comptime_if_attribute(mut node ast.Attr) bool {
|
|||
}
|
||||
}
|
||||
c.inside_ct_attr = true
|
||||
is_true, _ := c.comptime_if_cond(mut node.ct_expr)
|
||||
mut sb := strings.new_builder(256)
|
||||
is_true, _ := c.comptime_if_cond(mut node.ct_expr, mut sb)
|
||||
node.ct_skip = !is_true
|
||||
c.inside_ct_attr = false
|
||||
node.ct_evaled = true
|
||||
return node.ct_skip
|
||||
}
|
||||
|
||||
fn (mut c Checker) comptime_if_to_ifdef(name string) !string {
|
||||
match name {
|
||||
// platforms/os-es:
|
||||
'windows' {
|
||||
return '_WIN32'
|
||||
}
|
||||
'ios' {
|
||||
return '__TARGET_IOS__'
|
||||
}
|
||||
'macos' {
|
||||
return '__APPLE__'
|
||||
}
|
||||
'mach' {
|
||||
return '__MACH__'
|
||||
}
|
||||
'darwin' {
|
||||
return '__DARWIN__'
|
||||
}
|
||||
'hpux' {
|
||||
return '__HPUX__'
|
||||
}
|
||||
'gnu' {
|
||||
return '__GNU__'
|
||||
}
|
||||
'qnx' {
|
||||
return '__QNX__'
|
||||
}
|
||||
'linux' {
|
||||
return '__linux__'
|
||||
}
|
||||
'serenity' {
|
||||
return '__serenity__'
|
||||
}
|
||||
'plan9' {
|
||||
return '__plan9__'
|
||||
}
|
||||
'vinix' {
|
||||
return '__vinix__'
|
||||
}
|
||||
'freebsd' {
|
||||
return '__FreeBSD__'
|
||||
}
|
||||
'openbsd' {
|
||||
return '__OpenBSD__'
|
||||
}
|
||||
'netbsd' {
|
||||
return '__NetBSD__'
|
||||
}
|
||||
'bsd' {
|
||||
return '__BSD__'
|
||||
}
|
||||
'dragonfly' {
|
||||
return '__DragonFly__'
|
||||
}
|
||||
'android' {
|
||||
return '__ANDROID__'
|
||||
}
|
||||
'termux' {
|
||||
// Note: termux is running on Android natively so __ANDROID__ will also be defined
|
||||
return '__TERMUX__'
|
||||
}
|
||||
'solaris' {
|
||||
return '__sun'
|
||||
}
|
||||
'haiku' {
|
||||
return '__HAIKU__'
|
||||
}
|
||||
//
|
||||
'js' {
|
||||
return '_VJS'
|
||||
}
|
||||
'wasm32_emscripten' {
|
||||
return '__EMSCRIPTEN__'
|
||||
}
|
||||
'native' {
|
||||
return '_VNATIVE' // when using the native backend, cgen is inactive
|
||||
}
|
||||
// compilers:
|
||||
'gcc' {
|
||||
return '__V_GCC__'
|
||||
}
|
||||
'tinyc' {
|
||||
return '__TINYC__'
|
||||
}
|
||||
'clang' {
|
||||
return '__clang__'
|
||||
}
|
||||
'mingw' {
|
||||
return '__MINGW32__'
|
||||
}
|
||||
'msvc' {
|
||||
return '_MSC_VER'
|
||||
}
|
||||
'cplusplus' {
|
||||
return '__cplusplus'
|
||||
}
|
||||
// other:
|
||||
'threads' {
|
||||
return '__VTHREADS__'
|
||||
}
|
||||
'gcboehm' {
|
||||
return '_VGCBOEHM'
|
||||
}
|
||||
'debug' {
|
||||
return '_VDEBUG'
|
||||
}
|
||||
'prod' {
|
||||
return '_VPROD'
|
||||
}
|
||||
'profile' {
|
||||
return '_VPROFILE'
|
||||
}
|
||||
'test' {
|
||||
return '_VTEST'
|
||||
}
|
||||
'glibc' {
|
||||
return '__GLIBC__'
|
||||
}
|
||||
'prealloc' {
|
||||
return '_VPREALLOC'
|
||||
}
|
||||
'no_bounds_checking' {
|
||||
return 'CUSTOM_DEFINE_no_bounds_checking'
|
||||
}
|
||||
'freestanding' {
|
||||
return '_VFREESTANDING'
|
||||
}
|
||||
'autofree' {
|
||||
return '_VAUTOFREE'
|
||||
}
|
||||
// architectures:
|
||||
'amd64' {
|
||||
return '__V_amd64'
|
||||
}
|
||||
'aarch64', 'arm64' {
|
||||
return '__V_arm64'
|
||||
}
|
||||
'arm32' {
|
||||
return '__V_arm32'
|
||||
}
|
||||
'i386' {
|
||||
return '__V_x86'
|
||||
}
|
||||
'rv64', 'riscv64' {
|
||||
return '__V_rv64'
|
||||
}
|
||||
'rv32', 'riscv32' {
|
||||
return '__V_rv32'
|
||||
}
|
||||
's390x' {
|
||||
return '__V_s390x'
|
||||
}
|
||||
'ppc64le' {
|
||||
return '__V_ppc64le'
|
||||
}
|
||||
'loongarch64' {
|
||||
return '__V_loongarch64'
|
||||
}
|
||||
// bitness:
|
||||
'x64' {
|
||||
return 'TARGET_IS_64BIT'
|
||||
}
|
||||
'x32' {
|
||||
return 'TARGET_IS_32BIT'
|
||||
}
|
||||
// endianness:
|
||||
'little_endian' {
|
||||
return 'TARGET_ORDER_IS_LITTLE'
|
||||
}
|
||||
'big_endian' {
|
||||
return 'TARGET_ORDER_IS_BIG'
|
||||
}
|
||||
'fast_math' {
|
||||
if c.pref.ccompiler_type == .msvc {
|
||||
// turned on by: `-cflags /fp:fast`
|
||||
return '_M_FP_FAST'
|
||||
}
|
||||
// turned on by: `-cflags -ffast-math`
|
||||
return '__FAST_MATH__'
|
||||
}
|
||||
else {}
|
||||
}
|
||||
return error('bad os ifdef name "${name}"')
|
||||
}
|
||||
|
||||
fn (mut c Checker) get_expr_type(cond ast.Expr) ast.Type {
|
||||
match cond {
|
||||
ast.Ident {
|
||||
|
@ -854,7 +1045,7 @@ fn (mut c Checker) get_expr_type(cond ast.Expr) ast.Type {
|
|||
// comptime_if_cond evaluate the `cond` and return (`is_true`, `keep_stmts`)
|
||||
// `is_true` is the evaluate result of `cond`;
|
||||
// `keep_stmts` meaning the branch is a `multi pass branch`, we should keep the branch stmts even `is_true` is false, such as `$if T is int {`
|
||||
fn (mut c Checker) comptime_if_cond(mut cond ast.Expr) (bool, bool) {
|
||||
fn (mut c Checker) comptime_if_cond(mut cond ast.Expr, mut sb strings.Builder) (bool, bool) {
|
||||
mut should_record_ident := false
|
||||
mut is_user_ident := false
|
||||
mut ident_name := ''
|
||||
|
@ -873,17 +1064,25 @@ fn (mut c Checker) comptime_if_cond(mut cond ast.Expr) (bool, bool) {
|
|||
match mut cond {
|
||||
ast.BoolLiteral {
|
||||
c.expr(mut cond)
|
||||
return cond.val, false
|
||||
is_true = cond.val
|
||||
sb.write_string('${is_true}')
|
||||
return is_true, false
|
||||
}
|
||||
ast.ParExpr {
|
||||
return c.comptime_if_cond(mut cond.expr)
|
||||
sb.write_string('(')
|
||||
is_true_result, multi_pass_stmts := c.comptime_if_cond(mut cond.expr, mut
|
||||
sb)
|
||||
sb.write_string(')')
|
||||
return is_true_result, multi_pass_stmts
|
||||
}
|
||||
ast.PrefixExpr {
|
||||
if cond.op != .not {
|
||||
c.error('invalid \$if prefix operator, only allow `!`.', cond.pos)
|
||||
return false, false
|
||||
}
|
||||
is_true_result, multi_pass_stmts := c.comptime_if_cond(mut cond.right)
|
||||
sb.write_string(cond.op.str())
|
||||
is_true_result, multi_pass_stmts := c.comptime_if_cond(mut cond.right, mut
|
||||
sb)
|
||||
return !is_true_result, multi_pass_stmts
|
||||
}
|
||||
ast.PostfixExpr {
|
||||
|
@ -900,16 +1099,20 @@ fn (mut c Checker) comptime_if_cond(mut cond ast.Expr) (bool, bool) {
|
|||
should_record_ident = true
|
||||
is_user_ident = true
|
||||
ident_name = cname
|
||||
if cname in c.pref.compile_defines {
|
||||
return true, false
|
||||
}
|
||||
return false, false
|
||||
// ifdef := c.comptime_if_to_ifdef(cname, true) or {
|
||||
// c.error(err.msg(), cond.pos)
|
||||
// return false, false
|
||||
//}
|
||||
sb.write_string('defined(CUSTOM_DEFINE_${cname})')
|
||||
is_true = cname in c.pref.compile_defines
|
||||
return is_true, false
|
||||
}
|
||||
ast.InfixExpr {
|
||||
match cond.op {
|
||||
.and, .logical_or {
|
||||
l, d1 := c.comptime_if_cond(mut cond.left)
|
||||
r, d2 := c.comptime_if_cond(mut cond.right)
|
||||
l, d1 := c.comptime_if_cond(mut cond.left, mut sb)
|
||||
sb.write_string(' ${cond.op} ')
|
||||
r, d2 := c.comptime_if_cond(mut cond.right, mut sb)
|
||||
// if at least one of the cond has `keep_stmts`, we should keep stmts
|
||||
return if cond.op == .and { l && r } else { l || r }, d1 || d2
|
||||
}
|
||||
|
@ -979,11 +1182,9 @@ fn (mut c Checker) comptime_if_cond(mut cond ast.Expr) (bool, bool) {
|
|||
}
|
||||
}
|
||||
}
|
||||
return if cond.op in [.key_in, .key_is] {
|
||||
is_true, true
|
||||
} else {
|
||||
!is_true, true
|
||||
}
|
||||
is_true = if cond.op in [.key_in, .key_is] { is_true } else { !is_true }
|
||||
sb.write_string('${is_true}')
|
||||
return is_true, true
|
||||
}
|
||||
|
||||
if cond.left !in [ast.TypeNode, ast.Ident, ast.SelectorExpr] {
|
||||
|
@ -1078,6 +1279,7 @@ fn (mut c Checker) comptime_if_cond(mut cond ast.Expr) (bool, bool) {
|
|||
return false, false
|
||||
}
|
||||
}
|
||||
sb.write_string('${is_true}')
|
||||
return is_true, false
|
||||
}
|
||||
ast.SelectorExpr {
|
||||
|
@ -1106,9 +1308,11 @@ fn (mut c Checker) comptime_if_cond(mut cond ast.Expr) (bool, bool) {
|
|||
}
|
||||
match cond.op {
|
||||
.eq {
|
||||
sb.write_string('${is_true}')
|
||||
return is_true, true
|
||||
}
|
||||
.ne {
|
||||
sb.write_string('${!is_true}')
|
||||
return !is_true, true
|
||||
}
|
||||
else {
|
||||
|
@ -1153,6 +1357,7 @@ fn (mut c Checker) comptime_if_cond(mut cond ast.Expr) (bool, bool) {
|
|||
return false, false
|
||||
}
|
||||
}
|
||||
sb.write_string('${is_true}')
|
||||
return is_true, true
|
||||
} else if cond.left.field_name == 'return_type' {
|
||||
// method.return_type
|
||||
|
@ -1182,6 +1387,7 @@ fn (mut c Checker) comptime_if_cond(mut cond ast.Expr) (bool, bool) {
|
|||
return false, false
|
||||
}
|
||||
}
|
||||
sb.write_string('${is_true}')
|
||||
return is_true, false
|
||||
} else {
|
||||
c.error('only support .return_type compare for \$for method',
|
||||
|
@ -1196,8 +1402,10 @@ fn (mut c Checker) comptime_if_cond(mut cond ast.Expr) (bool, bool) {
|
|||
}
|
||||
ast.BoolLiteral {
|
||||
// field.is_pub == true
|
||||
l, _ := c.comptime_if_cond(mut cond.left)
|
||||
l, _ := c.comptime_if_cond(mut cond.left, mut sb)
|
||||
sb.write_string(' ${cond.op} ')
|
||||
r := (cond.right as ast.BoolLiteral).val
|
||||
sb.write_string('${r}')
|
||||
is_true = if cond.op == .eq { l == r } else { l != r }
|
||||
return is_true, true
|
||||
}
|
||||
|
@ -1239,6 +1447,7 @@ fn (mut c Checker) comptime_if_cond(mut cond ast.Expr) (bool, bool) {
|
|||
return false, false
|
||||
}
|
||||
}
|
||||
sb.write_string('${is_true}')
|
||||
return is_true, true
|
||||
}
|
||||
else {
|
||||
|
@ -1269,17 +1478,13 @@ fn (mut c Checker) comptime_if_cond(mut cond ast.Expr) (bool, bool) {
|
|||
is_user_ident = false
|
||||
ident_name = cname
|
||||
if cname in ast.valid_comptime_if_os {
|
||||
if !c.pref.output_cross_c {
|
||||
if cname_enum_val := pref.os_from_string(cname) {
|
||||
if cname_enum_val == c.pref.os {
|
||||
is_true = true
|
||||
}
|
||||
if cname_enum_val := pref.os_from_string(cname) {
|
||||
if cname_enum_val == c.pref.os {
|
||||
is_true = true
|
||||
}
|
||||
}
|
||||
return is_true, false
|
||||
} else if cname in ast.valid_comptime_if_compilers {
|
||||
is_true = pref.cc_from_string(cname) == c.pref.ccompiler_type
|
||||
return is_true, false
|
||||
} else if cname in ast.valid_comptime_if_platforms {
|
||||
if cname == 'aarch64' {
|
||||
c.note('use `arm64` instead of `aarch64`', cond.pos)
|
||||
|
@ -1321,7 +1526,6 @@ fn (mut c Checker) comptime_if_cond(mut cond ast.Expr) (bool, bool) {
|
|||
return false, false
|
||||
}
|
||||
}
|
||||
return is_true, false
|
||||
} else if cname in ast.valid_comptime_if_cpu_features {
|
||||
match cname {
|
||||
'x64' {
|
||||
|
@ -1342,7 +1546,6 @@ fn (mut c Checker) comptime_if_cond(mut cond ast.Expr) (bool, bool) {
|
|||
return false, false
|
||||
}
|
||||
}
|
||||
return is_true, false
|
||||
} else if cname in ast.valid_comptime_if_other {
|
||||
match cname {
|
||||
'apk' {
|
||||
|
@ -1414,7 +1617,6 @@ fn (mut c Checker) comptime_if_cond(mut cond ast.Expr) (bool, bool) {
|
|||
return false, false
|
||||
}
|
||||
}
|
||||
return is_true, false
|
||||
} else if cname !in c.pref.compile_defines_all {
|
||||
if cname == 'linux_or_macos' {
|
||||
c.error('linux_or_macos is deprecated, use `\$if linux || macos {` instead',
|
||||
|
@ -1441,21 +1643,34 @@ fn (mut c Checker) comptime_if_cond(mut cond ast.Expr) (bool, bool) {
|
|||
return false, false
|
||||
}
|
||||
is_true = (expr as ast.BoolLiteral).val
|
||||
return is_true, false
|
||||
} else if cname in c.pref.compile_defines {
|
||||
return true, false
|
||||
is_true = true
|
||||
} else {
|
||||
c.error('invalid \$if condition: unknown indent `${cname}`', cond.pos)
|
||||
return false, false
|
||||
}
|
||||
c.error('invalid \$if condition: unknown indent `${cname}`', cond.pos)
|
||||
return false, false
|
||||
if ifdef := c.comptime_if_to_ifdef(cname) {
|
||||
sb.write_string('defined(${ifdef})')
|
||||
} else {
|
||||
sb.write_string('${is_true}')
|
||||
}
|
||||
return is_true, false
|
||||
}
|
||||
ast.ComptimeCall {
|
||||
if cond.kind == .pkgconfig {
|
||||
mut m := pkgconfig.main([cond.args_var]) or {
|
||||
if mut m := pkgconfig.main([cond.args_var]) {
|
||||
if _ := m.run() {
|
||||
is_true = true
|
||||
} else {
|
||||
// pkgconfig not found, do not issue error, just set false
|
||||
is_true = false
|
||||
}
|
||||
} else {
|
||||
c.error(err.msg(), cond.pos)
|
||||
return false, true
|
||||
is_true = false
|
||||
}
|
||||
m.run() or { return false, true }
|
||||
return true, true
|
||||
sb.write_string('${is_true}')
|
||||
return is_true, true
|
||||
}
|
||||
if cond.kind == .d {
|
||||
t := c.expr(mut cond)
|
||||
|
@ -1464,7 +1679,9 @@ fn (mut c Checker) comptime_if_cond(mut cond ast.Expr) (bool, bool) {
|
|||
cond.pos)
|
||||
return false, false
|
||||
}
|
||||
return cond.compile_value.bool(), false
|
||||
is_true = cond.compile_value.bool()
|
||||
sb.write_string('${is_true}')
|
||||
return is_true, false
|
||||
}
|
||||
c.error('invalid \$if condition: unknown ComptimeCall', cond.pos)
|
||||
return false, false
|
||||
|
@ -1473,6 +1690,7 @@ fn (mut c Checker) comptime_if_cond(mut cond ast.Expr) (bool, bool) {
|
|||
if c.comptime.comptime_for_field_var != '' && cond.expr is ast.Ident {
|
||||
if (cond.expr as ast.Ident).name == c.comptime.comptime_for_field_var && cond.field_name in ['is_mut', 'is_pub', 'is_shared', 'is_atomic', 'is_option', 'is_array', 'is_map', 'is_chan', 'is_struct', 'is_alias', 'is_enum'] {
|
||||
is_true = c.type_resolver.get_comptime_selector_bool_field(cond.field_name)
|
||||
sb.write_string('${is_true}')
|
||||
return is_true, true
|
||||
}
|
||||
c.error('unknown field `${cond.field_name}` from ${c.comptime.comptime_for_field_var}',
|
||||
|
@ -1481,6 +1699,7 @@ fn (mut c Checker) comptime_if_cond(mut cond ast.Expr) (bool, bool) {
|
|||
if c.comptime.comptime_for_attr_var != '' && cond.expr is ast.Ident {
|
||||
if (cond.expr as ast.Ident).name == c.comptime.comptime_for_attr_var && cond.field_name == 'has_arg' {
|
||||
is_true = c.comptime.comptime_for_attr_value.has_arg
|
||||
sb.write_string('${is_true}')
|
||||
return is_true, true
|
||||
}
|
||||
c.error('unknown field `${cond.field_name}` from ${c.comptime.comptime_for_attr_var}',
|
||||
|
@ -1510,6 +1729,7 @@ fn (mut c Checker) comptime_if_cond(mut cond ast.Expr) (bool, bool) {
|
|||
'is_expand_simple_interpolation' { method.is_expand_simple_interpolation }
|
||||
else { false }
|
||||
}
|
||||
sb.write_string('${is_true}')
|
||||
return is_true, true
|
||||
}
|
||||
c.error('unknown field `${cond.field_name}` from ${c.comptime.comptime_for_method_var}',
|
||||
|
|
|
@ -4,6 +4,61 @@ module checker
|
|||
|
||||
import v.ast
|
||||
import v.token
|
||||
import v.util
|
||||
import strings
|
||||
|
||||
// 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 {
|
||||
if_kind := if node.is_comptime { '\$if' } else { 'if' }
|
||||
|
@ -40,8 +95,10 @@ fn (mut c Checker) if_expr(mut node ast.IfExpr) ast.Type {
|
|||
c.comptime.inside_comptime_if = last_in_comptime_if
|
||||
}
|
||||
|
||||
for i in 0 .. node.branches.len {
|
||||
mut branch := node.branches[i]
|
||||
comptime_branch_context_str := if node.is_comptime { c.gen_branch_context_string() } else { '' }
|
||||
|
||||
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 {
|
||||
c.warn('unnecessary `()` in `${if_kind}` condition, use `${if_kind} expr {` instead of `${if_kind} (expr) {`.',
|
||||
branch.pos)
|
||||
|
@ -49,17 +106,42 @@ fn (mut c Checker) if_expr(mut node ast.IfExpr) ast.Type {
|
|||
if !node.has_else || i < node.branches.len - 1 {
|
||||
// if branch
|
||||
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.cond.pos()}|'
|
||||
c.comptime.inside_comptime_if = true
|
||||
comptime_if_result, comptime_if_multi_pass_branch = c.comptime_if_cond(mut branch.cond)
|
||||
node.branches[i].pkg_exist = comptime_if_result
|
||||
mut sb := strings.new_builder(256)
|
||||
comptime_if_result, comptime_if_multi_pass_branch = c.comptime_if_cond(mut branch.cond, mut
|
||||
sb)
|
||||
if comptime_if_multi_pass_branch {
|
||||
comptime_if_has_multi_pass_branch = true
|
||||
}
|
||||
if !comptime_if_has_multi_pass_branch && comptime_if_found_branch {
|
||||
// when all prev branchs are single pass branchs, and already has a true branch:
|
||||
// remove following branchs' stmts by overwrite `comptime_if_result`
|
||||
|
||||
if comptime_if_found_branch {
|
||||
comptime_if_result = false
|
||||
}
|
||||
|
||||
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.val != comptime_if_result {
|
||||
c.error('checker erro1r : branch eval wrong', branch.cond.pos())
|
||||
}
|
||||
}
|
||||
|
||||
// set `comptime_is_true` which can be used by `cgen`
|
||||
c.table.comptime_is_true[idx_str] = ast.ComptTimeCondResult{
|
||||
val: comptime_if_result
|
||||
c_str: sb.str()
|
||||
}
|
||||
} else {
|
||||
// check condition type is boolean
|
||||
c.expected_type = ast.bool_type
|
||||
|
@ -78,6 +160,17 @@ fn (mut c Checker) if_expr(mut node ast.IfExpr) ast.Type {
|
|||
comptime_if_result = !comptime_if_found_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
|
||||
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
|
||||
}
|
||||
// hack: as a `else` has no `cond`, so we use `branch.pos` here
|
||||
idx_str := comptime_branch_context_str + '|${c.file.path}|${branch.pos}|'
|
||||
c.table.comptime_is_true[idx_str] = ast.ComptTimeCondResult{
|
||||
val: comptime_if_result
|
||||
c_str: ''
|
||||
}
|
||||
}
|
||||
}
|
||||
if mut branch.cond is ast.IfGuardExpr {
|
||||
|
@ -119,9 +212,17 @@ fn (mut c Checker) if_expr(mut node ast.IfExpr) ast.Type {
|
|||
if c.fn_level == 0 && c.pref.output_cross_c {
|
||||
// do not skip any of the branches for top level `$if OS {`
|
||||
// statements, in `-cross` mode
|
||||
comptime_if_multi_pass_branch = true
|
||||
comptime_remove_curr_branch_stmts = false
|
||||
c.skip_flags = false
|
||||
c.ct_cond_stack << branch.cond
|
||||
// hack: because `else` branch has no `cond`, so create an Ident, set the `pos`, for `hash_stmt()` work.
|
||||
if branch.cond is ast.NodeError {
|
||||
c.ct_cond_stack << ast.Ident{
|
||||
name: '__else_branch__'
|
||||
pos: branch.pos
|
||||
}
|
||||
} else {
|
||||
c.ct_cond_stack << branch.cond
|
||||
}
|
||||
}
|
||||
if !c.skip_flags {
|
||||
if node_is_expr {
|
||||
|
@ -147,9 +248,6 @@ fn (mut c Checker) if_expr(mut node ast.IfExpr) ast.Type {
|
|||
c.stmts(mut 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
|
||||
if c.fn_level == 0 && c.pref.output_cross_c && c.ct_cond_stack.len > 0 {
|
||||
|
@ -322,6 +420,11 @@ fn (mut c Checker) if_expr(mut node ast.IfExpr) ast.Type {
|
|||
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 == node.branches.len {
|
||||
|
|
|
@ -1030,16 +1030,16 @@ pub fn (mut g Gen) init() {
|
|||
if g.pref.gc_mode in [.boehm_full, .boehm_incr, .boehm_full_opt, .boehm_incr_opt, .boehm_leak] {
|
||||
g.comptime_definitions.writeln('#define _VGCBOEHM (1)')
|
||||
}
|
||||
if g.pref.is_debug || 'debug' in g.pref.compile_defines {
|
||||
if g.pref.is_debug {
|
||||
g.comptime_definitions.writeln('#define _VDEBUG (1)')
|
||||
}
|
||||
if g.pref.is_prod || 'prod' in g.pref.compile_defines {
|
||||
if g.pref.is_prod {
|
||||
g.comptime_definitions.writeln('#define _VPROD (1)')
|
||||
}
|
||||
if g.pref.is_test || 'test' in g.pref.compile_defines {
|
||||
if g.pref.is_test {
|
||||
g.comptime_definitions.writeln('#define _VTEST (1)')
|
||||
}
|
||||
if g.pref.is_prof || 'profile' in g.pref.compile_defines {
|
||||
if g.pref.is_prof {
|
||||
g.comptime_definitions.writeln('#define _VPROFILE (1)')
|
||||
}
|
||||
if g.pref.autofree {
|
||||
|
@ -5728,15 +5728,27 @@ fn (mut g Gen) hash_stmt_guarded_include(node ast.HashStmt) string {
|
|||
fn (mut g Gen) hash_stmt(node ast.HashStmt) {
|
||||
line_nr := node.pos.line_nr + 1
|
||||
mut ct_condition := ''
|
||||
|
||||
if node.ct_conds.len > 0 {
|
||||
ct_condition_start := g.out.len
|
||||
mut comptime_branch_context_str := g.gen_branch_context_string()
|
||||
mut is_true := ast.ComptTimeCondResult{}
|
||||
mut sb := strings.new_builder(256)
|
||||
for idx, ct_expr in node.ct_conds {
|
||||
g.comptime_if_cond(ct_expr, false)
|
||||
idx_str := comptime_branch_context_str + '|${g.file.path}|${ct_expr.pos()}|'
|
||||
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 {
|
||||
g.error('checker error: condition result idx string not found => [${idx_str}]',
|
||||
ct_expr.pos())
|
||||
return
|
||||
}
|
||||
sb.write_string(is_true.c_str)
|
||||
if idx < node.ct_conds.len - 1 {
|
||||
g.write(' && ')
|
||||
sb.write_string(' && ')
|
||||
}
|
||||
}
|
||||
ct_condition = g.out.cut_to(ct_condition_start).trim_space()
|
||||
ct_condition = sb.str()
|
||||
}
|
||||
// #include etc
|
||||
if node.kind == 'include' {
|
||||
|
|
|
@ -5,6 +5,7 @@ module c
|
|||
|
||||
import os
|
||||
import v.ast
|
||||
import v.token
|
||||
import v.util
|
||||
import v.pref
|
||||
import v.type_resolver
|
||||
|
@ -313,6 +314,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) {
|
||||
if !node.is_expr && !node.has_else && node.branches.len == 1 {
|
||||
if node.branches[0].stmts.len == 0 {
|
||||
|
@ -346,32 +400,42 @@ fn (mut g Gen) comptime_if(node ast.IfExpr) {
|
|||
} 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 := ast.ComptTimeCondResult{}
|
||||
for i, branch in node.branches {
|
||||
start_pos := g.out.len
|
||||
if comptime_may_skip_else {
|
||||
continue // if we already have a known true, ignore other branches
|
||||
}
|
||||
if i == node.branches.len - 1 && node.has_else {
|
||||
g.writeln('#else')
|
||||
comptime_if_stmts_skip = comptime_may_skip_else
|
||||
// `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 `checker`.
|
||||
idx_str := if branch.cond.pos() == token.Pos{} {
|
||||
comptime_branch_context_str + '|${g.file.path}|${branch.pos}|'
|
||||
} else {
|
||||
comptime_branch_context_str + '|${g.file.path}|${branch.cond.pos()}|'
|
||||
}
|
||||
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 {
|
||||
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 {
|
||||
g.write('#if ')
|
||||
} else {
|
||||
g.write('#elif ')
|
||||
}
|
||||
comptime_if_stmts_skip, comptime_may_skip_else = g.comptime_if_cond(branch.cond,
|
||||
branch.pkg_exist)
|
||||
if !comptime_if_stmts_skip && comptime_may_skip_else {
|
||||
comptime_may_skip_else = false // if the cond is false, not skip else branch
|
||||
// directly use `checker` evaluate results
|
||||
// for `cgen`, we can use `is_true.c_str` or `is_true.value` here
|
||||
g.writeln('${is_true.c_str}')
|
||||
$if debug_comptime_branch_context ? {
|
||||
g.writeln('/* ${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()
|
||||
if expr_str != '' {
|
||||
|
@ -430,7 +494,7 @@ fn (mut g Gen) comptime_if(node ast.IfExpr) {
|
|||
if should_create_scope {
|
||||
g.writeln('{')
|
||||
}
|
||||
if !comptime_if_stmts_skip {
|
||||
if is_true.val {
|
||||
g.stmts(branch.stmts)
|
||||
}
|
||||
if should_create_scope {
|
||||
|
@ -494,349 +558,6 @@ fn (mut g Gen) get_expr_type(cond ast.Expr) ast.Type {
|
|||
}
|
||||
}
|
||||
|
||||
// returns the value of the bool comptime expression and if next branches may be discarded
|
||||
// returning `false` means the statements inside the $if can be skipped
|
||||
fn (mut g Gen) comptime_if_cond(cond ast.Expr, pkg_exist bool) (bool, bool) {
|
||||
match cond {
|
||||
ast.BoolLiteral {
|
||||
g.expr(cond)
|
||||
return cond.val, true
|
||||
}
|
||||
ast.ParExpr {
|
||||
g.write('(')
|
||||
is_cond_true, may_discard := g.comptime_if_cond(cond.expr, pkg_exist)
|
||||
g.write(')')
|
||||
return is_cond_true, may_discard
|
||||
}
|
||||
ast.PrefixExpr {
|
||||
g.write(cond.op.str())
|
||||
is_cond_true, _ := g.comptime_if_cond(cond.right, pkg_exist)
|
||||
if cond.op == .not {
|
||||
if cond.right in [ast.BoolLiteral, ast.SelectorExpr] {
|
||||
return !is_cond_true, true
|
||||
}
|
||||
}
|
||||
return is_cond_true, false
|
||||
}
|
||||
ast.PostfixExpr {
|
||||
dname := (cond.expr as ast.Ident).name
|
||||
ifdef := g.comptime_if_to_ifdef(dname, true) or {
|
||||
verror(err.str())
|
||||
return false, true
|
||||
}
|
||||
g.write('defined(${ifdef})')
|
||||
if dname in g.pref.compile_defines_all && dname !in g.pref.compile_defines {
|
||||
return false, true
|
||||
} else {
|
||||
return true, false
|
||||
}
|
||||
}
|
||||
ast.InfixExpr {
|
||||
match cond.op {
|
||||
.and, .logical_or {
|
||||
l, d1 := g.comptime_if_cond(cond.left, pkg_exist)
|
||||
g.write(' ${cond.op} ')
|
||||
r, d2 := g.comptime_if_cond(cond.right, pkg_exist)
|
||||
return if cond.op == .and { l && r } else { l || r }, d1 && d1 == d2
|
||||
}
|
||||
.key_is, .not_is {
|
||||
if cond.left in [ast.TypeNode, ast.Ident, ast.SelectorExpr]
|
||||
&& cond.right in [ast.ComptimeType, ast.TypeNode] {
|
||||
exp_type := g.get_expr_type(cond.left)
|
||||
if cond.right is ast.ComptimeType {
|
||||
is_true := g.type_resolver.is_comptime_type(exp_type, cond.right)
|
||||
if cond.op == .key_is {
|
||||
if is_true {
|
||||
g.write('1')
|
||||
} else {
|
||||
g.write('0')
|
||||
}
|
||||
return is_true, true
|
||||
} else {
|
||||
if is_true {
|
||||
g.write('0')
|
||||
} else {
|
||||
g.write('1')
|
||||
}
|
||||
return !is_true, true
|
||||
}
|
||||
} else {
|
||||
got_type := g.unwrap_generic((cond.right as ast.TypeNode).typ)
|
||||
got_sym := g.table.sym(got_type)
|
||||
|
||||
if got_sym.kind == .interface && got_sym.info is ast.Interface {
|
||||
is_true := exp_type.has_flag(.option) == got_type.has_flag(.option)
|
||||
&& g.table.does_type_implement_interface(exp_type, got_type)
|
||||
if cond.op == .key_is {
|
||||
if is_true {
|
||||
g.write('1 && ${exp_type.has_flag(.option)} == ${got_type.has_flag(.option)}')
|
||||
} else {
|
||||
g.write('0')
|
||||
}
|
||||
return is_true, true
|
||||
} else if cond.op == .not_is {
|
||||
if is_true {
|
||||
g.write('0')
|
||||
} else {
|
||||
g.write('1')
|
||||
}
|
||||
return !is_true, true
|
||||
}
|
||||
}
|
||||
if got_sym.info is ast.FnType && cond.left is ast.Ident
|
||||
&& g.comptime.comptime_for_method_var == cond.left.name {
|
||||
is_compatible := g.table.fn_signature(got_sym.info.func,
|
||||
skip_receiver: true
|
||||
type_only: true
|
||||
) == g.table.fn_signature(g.comptime.comptime_for_method,
|
||||
skip_receiver: true
|
||||
type_only: true
|
||||
)
|
||||
if cond.op == .key_is {
|
||||
g.write(int(is_compatible).str())
|
||||
return is_compatible, true
|
||||
} else {
|
||||
g.write(int(!is_compatible).str())
|
||||
return !is_compatible, true
|
||||
}
|
||||
} else if cond.op == .key_is {
|
||||
g.write('${int(exp_type.idx())} == ${int(got_type.idx())} && ${exp_type.has_flag(.option)} == ${got_type.has_flag(.option)}')
|
||||
return exp_type == got_type, true
|
||||
} else {
|
||||
g.write('${int(exp_type.idx())} != ${int(got_type.idx())}')
|
||||
return exp_type != got_type, true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.eq, .ne {
|
||||
// TODO: Implement `$if method.args.len == 1`
|
||||
if cond.left is ast.SelectorExpr && (g.comptime.comptime_for_field_var.len > 0
|
||||
|| g.comptime.comptime_for_method != unsafe { nil }
|
||||
|| cond.left.name_type != 0) {
|
||||
if cond.right is ast.StringLiteral {
|
||||
if cond.left.expr is ast.Ident && cond.left.field_name == 'name' {
|
||||
if g.comptime.comptime_for_method_var.len > 0
|
||||
&& cond.left.expr.name == g.comptime.comptime_for_method_var {
|
||||
is_true := if cond.op == .eq {
|
||||
g.comptime.comptime_for_method.name == cond.right.val
|
||||
} else {
|
||||
g.comptime.comptime_for_method.name != cond.right.val
|
||||
}
|
||||
if is_true {
|
||||
g.write('1')
|
||||
} else {
|
||||
g.write('0')
|
||||
}
|
||||
return is_true, true
|
||||
} else if g.comptime.comptime_for_field_var.len > 0
|
||||
&& cond.left.expr.name == g.comptime.comptime_for_field_var {
|
||||
is_true := if cond.op == .eq {
|
||||
g.comptime.comptime_for_field_value.name == cond.right.val
|
||||
} else {
|
||||
g.comptime.comptime_for_field_value.name != cond.right.val
|
||||
}
|
||||
if is_true {
|
||||
g.write('1')
|
||||
} else {
|
||||
g.write('0')
|
||||
}
|
||||
return is_true, true
|
||||
}
|
||||
}
|
||||
} else if cond.right is ast.IntegerLiteral {
|
||||
if g.comptime.is_comptime_selector_field_name(cond.left, 'indirections') {
|
||||
left_muls := if cond.left.name_type != 0 {
|
||||
g.unwrap_generic(cond.left.name_type).nr_muls()
|
||||
} else {
|
||||
g.comptime.comptime_for_field_type.nr_muls()
|
||||
}
|
||||
is_true := match cond.op {
|
||||
.eq { left_muls == cond.right.val.i64() }
|
||||
.ne { left_muls != cond.right.val.i64() }
|
||||
else { false }
|
||||
}
|
||||
if is_true {
|
||||
g.write('1')
|
||||
} else {
|
||||
g.write('0')
|
||||
}
|
||||
return is_true, true
|
||||
} else if g.comptime.comptime_for_method_var != ''
|
||||
&& cond.left.expr is ast.Ident
|
||||
&& cond.left.expr.name == g.comptime.comptime_for_method_var
|
||||
&& cond.left.field_name == 'return_type' {
|
||||
is_true := match cond.op {
|
||||
.eq { g.comptime.comptime_for_method_ret_type.idx() == cond.right.val.i64() }
|
||||
.ne { g.comptime.comptime_for_method_ret_type.idx() != cond.right.val.i64() }
|
||||
else { false }
|
||||
}
|
||||
if is_true {
|
||||
g.write('1')
|
||||
} else {
|
||||
g.write('0')
|
||||
}
|
||||
return is_true, true
|
||||
}
|
||||
}
|
||||
}
|
||||
if cond.left is ast.SelectorExpr || cond.right is ast.SelectorExpr {
|
||||
l, d1 := g.comptime_if_cond(cond.left, pkg_exist)
|
||||
g.write(' ${cond.op} ')
|
||||
r, d2 := g.comptime_if_cond(cond.right, pkg_exist)
|
||||
return if cond.op == .eq { l == r } else { l != r }, d1 && d1 == d2
|
||||
}
|
||||
if cond.left is ast.SizeOf && cond.left.typ != 0
|
||||
&& cond.right is ast.IntegerLiteral {
|
||||
// TODO: support struct.fieldname
|
||||
s, _ := g.table.type_size(g.unwrap_generic(cond.left.typ))
|
||||
right := cond.right as ast.IntegerLiteral
|
||||
is_true := match cond.op {
|
||||
.eq { s == right.val.i64() }
|
||||
.ne { s != right.val.i64() }
|
||||
else { false }
|
||||
}
|
||||
if is_true {
|
||||
g.write('1')
|
||||
} else {
|
||||
g.write('0')
|
||||
}
|
||||
return is_true, true
|
||||
} else {
|
||||
g.write('1')
|
||||
return true, true
|
||||
}
|
||||
}
|
||||
.key_in, .not_in {
|
||||
if cond.left in [ast.TypeNode, ast.SelectorExpr, ast.Ident]
|
||||
&& cond.right is ast.ArrayInit {
|
||||
checked_type := g.get_expr_type(cond.left)
|
||||
|
||||
for expr in cond.right.exprs {
|
||||
if expr is ast.ComptimeType {
|
||||
if g.type_resolver.is_comptime_type(checked_type, expr as ast.ComptimeType) {
|
||||
if cond.op == .key_in {
|
||||
g.write('1')
|
||||
} else {
|
||||
g.write('0')
|
||||
}
|
||||
return cond.op == .key_in, true
|
||||
}
|
||||
} else if expr is ast.TypeNode {
|
||||
got_type := g.unwrap_generic(expr.typ)
|
||||
if checked_type.idx() == got_type.idx()
|
||||
&& checked_type.has_flag(.option) == got_type.has_flag(.option) {
|
||||
if cond.op == .key_in {
|
||||
g.write('1')
|
||||
} else {
|
||||
g.write('0')
|
||||
}
|
||||
return cond.op == .key_in, true
|
||||
}
|
||||
}
|
||||
}
|
||||
if cond.op == .not_in {
|
||||
g.write('1')
|
||||
} else {
|
||||
g.write('0')
|
||||
}
|
||||
return cond.op == .not_in, true
|
||||
}
|
||||
}
|
||||
.gt, .lt, .ge, .le {
|
||||
if cond.left is ast.SelectorExpr && cond.right is ast.IntegerLiteral
|
||||
&& g.comptime.is_comptime_selector_field_name(cond.left, 'indirections') {
|
||||
left := cond.left as ast.SelectorExpr
|
||||
left_muls := if left.name_type != 0 {
|
||||
g.unwrap_generic(left.name_type).nr_muls()
|
||||
} else {
|
||||
g.comptime.comptime_for_field_type.nr_muls()
|
||||
}
|
||||
is_true := match cond.op {
|
||||
.gt { left_muls > cond.right.val.i64() }
|
||||
.lt { left_muls < cond.right.val.i64() }
|
||||
.ge { left_muls >= cond.right.val.i64() }
|
||||
.le { left_muls <= cond.right.val.i64() }
|
||||
else { false }
|
||||
}
|
||||
if is_true {
|
||||
g.write('1')
|
||||
} else {
|
||||
g.write('0')
|
||||
}
|
||||
return is_true, true
|
||||
}
|
||||
if cond.left is ast.SizeOf && cond.left.typ != 0
|
||||
&& cond.right is ast.IntegerLiteral {
|
||||
// TODO: support struct.fieldname
|
||||
s, _ := g.table.type_size(g.unwrap_generic(cond.left.typ))
|
||||
right := cond.right as ast.IntegerLiteral
|
||||
is_true := match cond.op {
|
||||
.gt { s > right.val.i64() }
|
||||
.lt { s < right.val.i64() }
|
||||
.ge { s >= right.val.i64() }
|
||||
.le { s <= right.val.i64() }
|
||||
else { false }
|
||||
}
|
||||
if is_true {
|
||||
g.write('1')
|
||||
} else {
|
||||
g.write('0')
|
||||
}
|
||||
return is_true, true
|
||||
} else {
|
||||
return true, false
|
||||
}
|
||||
}
|
||||
else {
|
||||
return true, false
|
||||
}
|
||||
}
|
||||
}
|
||||
ast.Ident {
|
||||
ifdef := g.comptime_if_to_ifdef(cond.name, false) or { 'true' } // handled in checker
|
||||
g.write('defined(${ifdef})')
|
||||
return true, false
|
||||
}
|
||||
ast.ComptimeCall {
|
||||
if cond.kind == .pkgconfig {
|
||||
g.write('${pkg_exist}')
|
||||
return true, false
|
||||
}
|
||||
if cond.kind == .d {
|
||||
if cond.result_type == ast.bool_type {
|
||||
if cond.compile_value == 'true' {
|
||||
g.write('1')
|
||||
} else {
|
||||
g.write('0')
|
||||
}
|
||||
} else {
|
||||
g.write('defined(CUSTOM_DEFINE_${cond.args_var})')
|
||||
}
|
||||
return true, false
|
||||
}
|
||||
return true, false
|
||||
}
|
||||
ast.SelectorExpr {
|
||||
if g.comptime.comptime_for_field_var != '' && cond.expr is ast.Ident
|
||||
&& cond.expr.name == g.comptime.comptime_for_field_var
|
||||
&& cond.field_name in ['is_mut', 'is_pub', 'is_shared', 'is_atomic', 'is_option', 'is_array', 'is_map', 'is_chan', 'is_struct', 'is_alias', 'is_enum'] {
|
||||
ret_bool := g.type_resolver.get_comptime_selector_bool_field(cond.field_name)
|
||||
g.write(ret_bool.str())
|
||||
return ret_bool, true
|
||||
} else {
|
||||
g.write('1')
|
||||
return true, true
|
||||
}
|
||||
}
|
||||
else {
|
||||
// should be unreachable, but just in case
|
||||
g.write('1')
|
||||
return true, true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// push_new_comptime_info saves the current comptime information
|
||||
fn (mut g Gen) push_new_comptime_info() {
|
||||
g.type_resolver.info_stack << type_resolver.ResolverInfo{
|
||||
|
@ -847,6 +568,8 @@ fn (mut g Gen) push_new_comptime_info() {
|
|||
comptime_for_field_type: g.comptime.comptime_for_field_type
|
||||
comptime_for_field_value: g.comptime.comptime_for_field_value
|
||||
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: g.comptime.comptime_for_method
|
||||
comptime_for_method_ret_type: g.comptime.comptime_for_method_ret_type
|
||||
|
@ -864,6 +587,8 @@ fn (mut g Gen) pop_comptime_info() {
|
|||
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_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 = old.comptime_for_method
|
||||
g.comptime.comptime_for_method_ret_type = old.comptime_for_method_ret_type
|
||||
|
@ -887,6 +612,7 @@ fn (mut g Gen) comptime_for(node ast.ComptimeFor) {
|
|||
typ_vweb_result := g.table.find_type('vweb.Result')
|
||||
for method in methods {
|
||||
g.push_new_comptime_info()
|
||||
g.comptime.inside_comptime_for = true
|
||||
// filter vweb route methods (non-generic method)
|
||||
if method.receiver_type != 0 && method.return_type == typ_vweb_result {
|
||||
rec_sym := g.table.sym(method.receiver_type)
|
||||
|
@ -902,7 +628,7 @@ fn (mut g Gen) comptime_for(node ast.ComptimeFor) {
|
|||
}
|
||||
g.comptime.comptime_for_method = unsafe { &method }
|
||||
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}");')
|
||||
if method.attrs.len == 0 {
|
||||
g.writeln('\t${node.val_var}.attrs = __new_array_with_default(0, 0, sizeof(string), 0);')
|
||||
|
@ -976,13 +702,13 @@ fn (mut g Gen) comptime_for(node ast.ComptimeFor) {
|
|||
if fields.len > 0 {
|
||||
g.writeln('\tFieldData ${node.val_var} = {0};')
|
||||
}
|
||||
g.push_new_comptime_info()
|
||||
for field in fields {
|
||||
g.push_new_comptime_info()
|
||||
g.comptime.inside_comptime_for = true
|
||||
g.comptime.comptime_for_field_var = node.val_var
|
||||
g.comptime.comptime_for_field_value = field
|
||||
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}");')
|
||||
if field.attrs.len == 0 {
|
||||
g.writeln('\t${node.val_var}.attrs = __new_array_with_default(0, 0, sizeof(string), 0);')
|
||||
|
@ -996,8 +722,8 @@ fn (mut g Gen) comptime_for(node ast.ComptimeFor) {
|
|||
styp := field.typ
|
||||
unaliased_styp := g.table.unaliased_type(styp)
|
||||
|
||||
g.writeln('\t${node.val_var}.typ = ${int(styp.idx())};')
|
||||
g.writeln('\t${node.val_var}.unaliased_typ = ${int(unaliased_styp.idx())};')
|
||||
g.writeln('\t${node.val_var}.typ = ${int(styp.idx())};\t// ${g.table.type_to_str(styp)}')
|
||||
g.writeln('\t${node.val_var}.unaliased_typ = ${int(unaliased_styp.idx())};\t// ${g.table.type_to_str(unaliased_styp)}')
|
||||
g.writeln('\t${node.val_var}.is_pub = ${field.is_pub};')
|
||||
g.writeln('\t${node.val_var}.is_mut = ${field.is_mut};')
|
||||
|
||||
|
@ -1019,8 +745,8 @@ fn (mut g Gen) comptime_for(node ast.ComptimeFor) {
|
|||
g.stmts(node.stmts)
|
||||
i++
|
||||
g.writeln('}')
|
||||
g.pop_comptime_info()
|
||||
}
|
||||
g.pop_comptime_info()
|
||||
}
|
||||
} else if node.kind == .values {
|
||||
if sym.kind == .enum {
|
||||
|
@ -1028,8 +754,9 @@ fn (mut g Gen) comptime_for(node ast.ComptimeFor) {
|
|||
if sym.info.vals.len > 0 {
|
||||
g.writeln('\tEnumData ${node.val_var} = {0};')
|
||||
}
|
||||
g.push_new_comptime_info()
|
||||
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.type_resolver.update_ct_type('${node.val_var}.typ', node.typ)
|
||||
|
||||
|
@ -1053,8 +780,8 @@ fn (mut g Gen) comptime_for(node ast.ComptimeFor) {
|
|||
g.stmts(node.stmts)
|
||||
g.writeln('}')
|
||||
i++
|
||||
g.pop_comptime_info()
|
||||
}
|
||||
g.pop_comptime_info()
|
||||
}
|
||||
}
|
||||
} else if node.kind == .attributes {
|
||||
|
@ -1063,7 +790,11 @@ fn (mut g Gen) comptime_for(node ast.ComptimeFor) {
|
|||
g.writeln('\tVAttribute ${node.val_var} = {0};')
|
||||
|
||||
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}.has_arg = ${attr.has_arg};')
|
||||
g.writeln('\t${node.val_var}.arg = _S("${util.smart_quote(attr.arg, false)}");')
|
||||
|
@ -1071,6 +802,7 @@ fn (mut g Gen) comptime_for(node ast.ComptimeFor) {
|
|||
g.stmts(node.stmts)
|
||||
g.writeln('}')
|
||||
i++
|
||||
g.pop_comptime_info()
|
||||
}
|
||||
}
|
||||
} else if node.kind == .variants {
|
||||
|
@ -1079,18 +811,19 @@ fn (mut g Gen) comptime_for(node ast.ComptimeFor) {
|
|||
g.writeln('\tVariantData ${node.val_var} = {0};')
|
||||
}
|
||||
g.comptime.inside_comptime_for = true
|
||||
g.push_new_comptime_info()
|
||||
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.type_resolver.update_ct_type('${node.val_var}.typ', variant)
|
||||
|
||||
g.writeln('/* variant ${i} */ {')
|
||||
g.writeln('\t${node.val_var}.typ = ${int(variant)};')
|
||||
g.writeln('/* variant ${i} : ${g.table.type_to_str(variant)} */ {')
|
||||
g.writeln('\t${node.val_var}.typ = ${int(variant)};\t// ')
|
||||
g.stmts(node.stmts)
|
||||
g.writeln('}')
|
||||
i++
|
||||
g.pop_comptime_info()
|
||||
}
|
||||
g.pop_comptime_info()
|
||||
}
|
||||
} else if node.kind == .params {
|
||||
method := g.comptime.comptime_for_method
|
||||
|
@ -1098,20 +831,20 @@ fn (mut g Gen) comptime_for(node ast.ComptimeFor) {
|
|||
if method.params.len > 0 {
|
||||
g.writeln('\tMethodParam ${node.val_var} = {0};')
|
||||
}
|
||||
g.push_new_comptime_info()
|
||||
g.comptime.inside_comptime_for = true
|
||||
g.comptime.comptime_for_method_param_var = node.val_var
|
||||
for param in method.params[1..] {
|
||||
g.push_new_comptime_info()
|
||||
g.comptime.inside_comptime_for = true
|
||||
g.comptime.comptime_for_method_param_var = node.val_var
|
||||
g.type_resolver.update_ct_type('${node.val_var}.typ', param.typ)
|
||||
|
||||
g.writeln('/* method param ${i} */ {')
|
||||
g.writeln('\t${node.val_var}.typ = ${int(param.typ)};')
|
||||
g.writeln('/* method param ${i} : ${param.name} */ {')
|
||||
g.writeln('\t${node.val_var}.typ = ${int(param.typ)};\t// ${g.table.type_to_str(param.typ)}')
|
||||
g.writeln('\t${node.val_var}.name = _S("${param.name}");')
|
||||
g.stmts(node.stmts)
|
||||
g.writeln('}')
|
||||
i++
|
||||
g.pop_comptime_info()
|
||||
}
|
||||
g.pop_comptime_info()
|
||||
}
|
||||
g.indent--
|
||||
g.writeln('}// \$for')
|
||||
|
|
|
@ -1,7 +1,24 @@
|
|||
module js
|
||||
|
||||
import v.token
|
||||
import v.ast
|
||||
|
||||
fn (mut g JsGen) gen_branch_context_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) {
|
||||
if !node.is_expr && !node.has_else && node.branches.len == 1 {
|
||||
if node.branches[0].stmts.len == 0 {
|
||||
|
@ -10,17 +27,32 @@ fn (mut g JsGen) comptime_if(node ast.IfExpr) {
|
|||
}
|
||||
}
|
||||
|
||||
mut comptime_branch_context_str := g.gen_branch_context_string()
|
||||
mut is_true := ast.ComptTimeCondResult{}
|
||||
for i, branch in node.branches {
|
||||
idx_str := if branch.cond.pos() == token.Pos{} {
|
||||
comptime_branch_context_str + '|${g.file.path}|${branch.pos}|'
|
||||
} else {
|
||||
comptime_branch_context_str + '|${g.file.path}|${branch.cond.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 {
|
||||
g.writeln('else')
|
||||
} else {
|
||||
result := if is_true.val { '1' } else { '0' }
|
||||
if i == 0 {
|
||||
g.write('if (')
|
||||
g.writeln('if (${result})')
|
||||
} else {
|
||||
g.write('else if (')
|
||||
g.writeln('else if (${result})')
|
||||
}
|
||||
$if debug_comptime_branch_context ? {
|
||||
g.writeln('// ${node.branches[i].cond} generic=[${comptime_branch_context_str}]')
|
||||
}
|
||||
g.comptime_if_cond(branch.cond, branch.pkg_exist)
|
||||
g.writeln(')')
|
||||
}
|
||||
|
||||
if node.is_expr {
|
||||
|
@ -45,7 +77,9 @@ fn (mut g JsGen) comptime_if(node ast.IfExpr) {
|
|||
}
|
||||
} else {
|
||||
g.writeln('{')
|
||||
g.stmts(branch.stmts)
|
||||
if is_true.val {
|
||||
g.stmts(branch.stmts)
|
||||
}
|
||||
g.writeln('}')
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,36 +5,32 @@ module wasm
|
|||
|
||||
import v.ast
|
||||
|
||||
pub fn (mut g Gen) comptime_cond(cond ast.Expr, pkg_exists bool) bool {
|
||||
pub fn (mut g Gen) comptime_cond(cond ast.Expr) bool {
|
||||
match cond {
|
||||
ast.BoolLiteral {
|
||||
return cond.val
|
||||
}
|
||||
ast.ParExpr {
|
||||
g.comptime_cond(cond.expr, pkg_exists)
|
||||
g.comptime_cond(cond.expr)
|
||||
}
|
||||
ast.PrefixExpr {
|
||||
if cond.op == .not {
|
||||
return !g.comptime_cond(cond.right, pkg_exists)
|
||||
return !g.comptime_cond(cond.right)
|
||||
}
|
||||
}
|
||||
ast.InfixExpr {
|
||||
match cond.op {
|
||||
.and {
|
||||
return g.comptime_cond(cond.left, pkg_exists)
|
||||
&& g.comptime_cond(cond.right, pkg_exists)
|
||||
return g.comptime_cond(cond.left) && g.comptime_cond(cond.right)
|
||||
}
|
||||
.logical_or {
|
||||
return g.comptime_cond(cond.left, pkg_exists)
|
||||
|| g.comptime_cond(cond.right, pkg_exists)
|
||||
return g.comptime_cond(cond.left) || g.comptime_cond(cond.right)
|
||||
}
|
||||
.eq {
|
||||
return g.comptime_cond(cond.left, pkg_exists) == g.comptime_cond(cond.right,
|
||||
pkg_exists)
|
||||
return g.comptime_cond(cond.left) == g.comptime_cond(cond.right)
|
||||
}
|
||||
.ne {
|
||||
return g.comptime_cond(cond.left, pkg_exists) != g.comptime_cond(cond.right,
|
||||
pkg_exists)
|
||||
return g.comptime_cond(cond.left) != g.comptime_cond(cond.right)
|
||||
}
|
||||
// wasm doesn't support generics
|
||||
// .key_is, .not_is
|
||||
|
@ -45,7 +41,7 @@ pub fn (mut g Gen) comptime_cond(cond ast.Expr, pkg_exists bool) bool {
|
|||
return g.comptime_if_to_ifdef(cond.name, false)
|
||||
}
|
||||
ast.ComptimeCall {
|
||||
return pkg_exists // more documentation needed here...
|
||||
return false // pkg_exists, more documentation needed here...
|
||||
}
|
||||
ast.PostfixExpr {
|
||||
return g.comptime_if_to_ifdef((cond.expr as ast.Ident).name, true)
|
||||
|
@ -66,7 +62,7 @@ pub fn (mut g Gen) comptime_if_expr(node ast.IfExpr, expected ast.Type, existing
|
|||
for i, branch in node.branches {
|
||||
has_expr := !(node.has_else && i + 1 >= node.branches.len)
|
||||
|
||||
if has_expr && !g.comptime_cond(branch.cond, branch.pkg_exist) {
|
||||
if has_expr && !g.comptime_cond(branch.cond) {
|
||||
continue
|
||||
}
|
||||
// !node.is_expr || cond
|
||||
|
|
|
@ -4,9 +4,11 @@ const disable_opt_features = true
|
|||
// Note: the `unknown_fn()` calls are here on purpose, to make sure that anything
|
||||
// that doesn't match a compile-time condition is not even parsed.
|
||||
fn test_ct_expressions() {
|
||||
mut result := ''
|
||||
foo := version
|
||||
bar := foo
|
||||
$if bar == 123 {
|
||||
result += 'a'
|
||||
assert true
|
||||
} $else {
|
||||
unknown_fn()
|
||||
|
@ -15,6 +17,7 @@ fn test_ct_expressions() {
|
|||
$if bar != 123 {
|
||||
unknown_fn()
|
||||
} $else $if bar != 124 {
|
||||
result += 'b'
|
||||
assert true
|
||||
} $else {
|
||||
unknown_fn()
|
||||
|
@ -23,8 +26,10 @@ fn test_ct_expressions() {
|
|||
$if !disable_opt_features {
|
||||
unknown_fn()
|
||||
} $else {
|
||||
result += 'c'
|
||||
assert true
|
||||
}
|
||||
assert result == 'abc'
|
||||
}
|
||||
|
||||
fn generic_t_is[O]() O {
|
||||
|
|
|
@ -30,7 +30,7 @@ fn test[T](val T) string {
|
|||
|
||||
fn test_main() {
|
||||
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(U32Struct{7}) == 'struct field u32'
|
||||
}
|
||||
|
|
34
vlib/v/tests/comptime/comptime_test_ident_test.v
Normal file
34
vlib/v/tests/comptime/comptime_test_ident_test.v
Normal file
|
@ -0,0 +1,34 @@
|
|||
fn test_test_ident() {
|
||||
mut result := ''
|
||||
$if test ? {
|
||||
result += '1'
|
||||
} $else {
|
||||
result += '2'
|
||||
}
|
||||
|
||||
$if test {
|
||||
result += '3'
|
||||
} $else {
|
||||
result += '4'
|
||||
}
|
||||
|
||||
$if !test {
|
||||
result += '5'
|
||||
} $else {
|
||||
result += '6'
|
||||
}
|
||||
|
||||
$if $d('test', false) {
|
||||
result += '7'
|
||||
} $else {
|
||||
result += '8'
|
||||
}
|
||||
|
||||
$if $d('test', true) {
|
||||
result += '9'
|
||||
} $else {
|
||||
result += '0'
|
||||
}
|
||||
|
||||
assert result == '23689'
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue