diff --git a/vlib/v/checker/checker.v b/vlib/v/checker/checker.v index 0a397f140a..237d799e80 100644 --- a/vlib/v/checker/checker.v +++ b/vlib/v/checker/checker.v @@ -1183,7 +1183,13 @@ fn (mut c Checker) selector_expr(mut node ast.SelectorExpr) ast.Type { } } } - + // evaluates comptime field. (from T.fields) + if c.check_comptime_is_field_selector(node) { + if c.check_comptime_is_field_selector_bool(node) { + node.expr_type = ast.bool_type + return node.expr_type + } + } old_selector_expr := c.inside_selector_expr c.inside_selector_expr = true mut typ := c.expr(node.expr) diff --git a/vlib/v/checker/comptime.v b/vlib/v/checker/comptime.v index 8c5da31649..e023d65724 100644 --- a/vlib/v/checker/comptime.v +++ b/vlib/v/checker/comptime.v @@ -562,6 +562,12 @@ fn (mut c Checker) comptime_if_branch(cond ast.Expr, pos token.Pos) ComptimeBran .eq, .ne { if cond.left is ast.SelectorExpr && cond.right is ast.IntegerLiteral { // $if method.args.len == 1 + } else if cond.left is ast.SelectorExpr + && c.check_comptime_is_field_selector_bool(cond.left as ast.SelectorExpr) { + // field.is_public (from T.fields) + } else if cond.right is ast.SelectorExpr + && c.check_comptime_is_field_selector_bool(cond.right as ast.SelectorExpr) { + // field.is_public (from T.fields) } else if cond.left is ast.Ident { // $if version == 2 left_type := c.expr(cond.left) @@ -716,9 +722,37 @@ fn (mut c Checker) comptime_if_branch(cond ast.Expr, pos token.Pos) ComptimeBran } return .eval } + ast.SelectorExpr { + if c.check_comptime_is_field_selector(cond) { + if c.check_comptime_is_field_selector_bool(cond) { + return .eval + } + c.error('unknown field `${cond.field_name}` from ${c.comptime_for_field_var}', + cond.pos) + } + return .unknown + } else { c.error('invalid `\$if` condition', pos) } } return .unknown } + +[inline] +fn (mut c Checker) check_comptime_is_field_selector(node ast.SelectorExpr) bool { + if c.inside_comptime_for_field && node.expr is ast.Ident { + return (node.expr as ast.Ident).name == c.comptime_for_field_var + } + return false +} + +[inline] +fn (mut c Checker) check_comptime_is_field_selector_bool(node ast.SelectorExpr) bool { + if c.check_comptime_is_field_selector(node) { + bool_fields := ['is_mut', 'is_pub', 'is_shared', 'is_atomic', 'is_optional', 'is_array', + 'is_map', 'is_chan', 'is_struct'] + return node.field_name in bool_fields + } + return false +} diff --git a/vlib/v/gen/c/comptime.v b/vlib/v/gen/c/comptime.v index 3508d2884a..558bdd18e3 100644 --- a/vlib/v/gen/c/comptime.v +++ b/vlib/v/gen/c/comptime.v @@ -17,6 +17,25 @@ fn (mut g Gen) get_comptime_selector_var_type(node ast.ComptimeSelector) (ast.St return field, field_name } +fn (mut g Gen) get_comptime_selector_bool_field(field_name string) bool { + field := g.comptime_for_field_value + field_typ := g.comptime_for_field_type + field_sym := g.table.sym(g.unwrap_generic(g.comptime_for_field_type)) + + match field_name { + 'is_pub' { return field.is_pub } + 'is_mut' { return field.is_mut } + 'is_shared' { return field_typ.has_flag(.shared_f) } + 'is_atomic' { return field_typ.has_flag(.atomic_f) } + 'is_optional' { return field.typ.has_flag(.optional) } + 'is_array' { return field_sym.kind in [.array, .array_fixed] } + 'is_map' { return field_sym.kind == .map } + 'is_chan' { return field_sym.kind == .chan } + 'is_struct' { return field_sym.kind == .struct_ } + else { return false } + } +} + fn (mut g Gen) comptime_selector(node ast.ComptimeSelector) { g.expr(node.left) if node.left_type.is_ptr() { @@ -443,8 +462,15 @@ fn (mut g Gen) comptime_if_cond(cond ast.Expr, pkg_exist bool) bool { } .eq, .ne { // TODO Implement `$if method.args.len == 1` - g.write('1') - return true + if cond.left is ast.SelectorExpr || cond.right is ast.SelectorExpr { + 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 == .eq { l == r } else { l != r } + } else { + g.write('1') + return true + } } else { return true @@ -460,6 +486,17 @@ fn (mut g Gen) comptime_if_cond(cond ast.Expr, pkg_exist bool) bool { g.write('${pkg_exist}') return true } + ast.SelectorExpr { + if g.inside_comptime_for_field && cond.expr is ast.Ident + && (cond.expr as ast.Ident).name == g.comptime_for_field_var && cond.field_name in ['is_mut', 'is_pub', 'is_shared', 'is_atomic', 'is_optional', 'is_array', 'is_map', 'is_chan', 'is_struct'] { + ret_bool := g.get_comptime_selector_bool_field(cond.field_name) + g.write(ret_bool.str()) + return true + } else { + g.write('1') + return true + } + } else { // should be unreachable, but just in case g.write('1') diff --git a/vlib/v/tests/inout/comptime_bool_fields.out b/vlib/v/tests/inout/comptime_bool_fields.out new file mode 100644 index 0000000000..41d19004f5 --- /dev/null +++ b/vlib/v/tests/inout/comptime_bool_fields.out @@ -0,0 +1,18 @@ +field: a +a is mut?: true +field: b +b is mut?: true +b is struct?: true +field: c +c is pub?: true +c is optional?: true +field: d +d is pub?: true +d is map?: true +field: e +e is pub?: true +e is atomic?: true +field: f +f is pub?: true +f is mut?: true +f is array?: true diff --git a/vlib/v/tests/inout/comptime_bool_fields.vv b/vlib/v/tests/inout/comptime_bool_fields.vv new file mode 100644 index 0000000000..ceadff1e88 --- /dev/null +++ b/vlib/v/tests/inout/comptime_bool_fields.vv @@ -0,0 +1,51 @@ +struct Bb { +mut: + a int + b struct {} + +pub: + c ?string + d map[string]int + e atomic int +pub mut: + f []f64 +} + +fn foo[U](val U) { + $for field in U.fields { + println('field: ${field.name}') + + $if field.is_pub == true { + println(field.name + ' is pub?: ${field.is_pub}') + } + $if field.is_mut { + println(field.name + ' is mut?: ${field.is_mut}') + } + $if field.is_struct { + println(field.name + ' is struct?: ${field.is_struct}') + } + $if field.is_chan { + println(field.name + ' is chan?: ${field.is_chan}') + } + $if field.is_array { + println(field.name + ' is array?: ${field.is_array}') + } + $if field.is_map { + println(field.name + ' is map?: ${field.is_map}') + } + $if field.is_shared { + println(field.name + ' is shared?: ${field.is_shared}') + } + $if field.is_optional { + println(field.name + ' is optional?: ${field.is_optional}') + } + $if field.is_atomic { + println(field.name + ' is atomic?: ${field.is_atomic}') + } + } +} + +fn main() { + bb := Bb{} + foo(bb) +}