diff --git a/vlib/v/ast/str.v b/vlib/v/ast/str.v index 266547a9ad..fd4edf01aa 100644 --- a/vlib/v/ast/str.v +++ b/vlib/v/ast/str.v @@ -312,6 +312,9 @@ pub fn (x Expr) str() string { StringLiteral { return '"$x.val"' } + Type { + return 'Type($x.typ)' + } TypeOf { return 'typeof($x.expr.str())' } diff --git a/vlib/v/checker/checker.v b/vlib/v/checker/checker.v index b36815f7fc..be2077d8a0 100644 --- a/vlib/v/checker/checker.v +++ b/vlib/v/checker/checker.v @@ -4361,7 +4361,7 @@ pub fn (mut c Checker) if_expr(mut node ast.IfExpr) table.Type { mut nbranches_without_return := 0 mut should_skip := false // Whether the current branch should be skipped mut found_branch := false // Whether a matching branch was found- skip the rest - mut is_comptime_t_is_expr := false // if `$if T is string` + mut is_comptime_type_is_expr := false // if `$if T is string` for i in 0 .. node.branches.len { mut branch := node.branches[i] if branch.cond is ast.ParExpr { @@ -4443,8 +4443,9 @@ pub fn (mut c Checker) if_expr(mut node ast.IfExpr) table.Type { got_type := (branch.cond.right as ast.Type).typ if left is ast.SelectorExpr { c.comptime_fields_type[left.expr.str()] = got_type + is_comptime_type_is_expr = true } else if left is ast.Type { - is_comptime_t_is_expr = true + is_comptime_type_is_expr = true left_type := c.unwrap_generic(left.typ) if left_type != got_type { should_skip = true @@ -4458,12 +4459,12 @@ pub fn (mut c Checker) if_expr(mut node ast.IfExpr) table.Type { } else if should_skip { c.skip_flags = true should_skip = false // Reset the value of `should_skip` for the next branch - } else { + } else if !is_comptime_type_is_expr { found_branch = true // If a branch wasn't skipped, the rest must be } if !c.skip_flags || c.pref.output_cross_c { c.stmts(branch.stmts) - } else if !is_comptime_t_is_expr { + } else if !is_comptime_type_is_expr { node.branches[i].stmts = [] } c.skip_flags = cur_skip_flags diff --git a/vlib/v/gen/comptime.v b/vlib/v/gen/comptime.v index a75fe2d4a4..f4bb93f7d2 100644 --- a/vlib/v/gen/comptime.v +++ b/vlib/v/gen/comptime.v @@ -155,13 +155,14 @@ fn (mut g Gen) comp_if(node ast.IfExpr) { start_pos := g.out.len if i == node.branches.len - 1 && node.has_else { g.writeln('#else') + comp_if_stmts_skip = false } else { if i == 0 { g.write('#if ') } else { g.write('#elif ') } - g.comp_if_expr(branch.cond) + comp_if_stmts_skip = !g.comp_if_cond(branch.cond) g.writeln('') } expr_str := g.out.last_n(g.out.len - start_pos).trim_space() @@ -193,30 +194,12 @@ fn (mut g Gen) comp_if(node ast.IfExpr) { if should_create_scope { g.writeln('{') } - if branch.cond is ast.InfixExpr { - if branch.cond.op == .key_is { - left := branch.cond.left - got_type := (branch.cond.right as ast.Type).typ - if left is ast.Type { - left_type := g.unwrap_generic(left.typ) - if left_type != got_type { - comp_if_stmts_skip = true - } - } - } - } - is_else := node.has_else && i == node.branches.len - 1 - if !comp_if_stmts_skip || (comp_if_stmts_skip && is_else) { + if !comp_if_stmts_skip { g.stmts(branch.stmts) } if should_create_scope { g.writeln('}') } - if !comp_if_stmts_skip && branch.cond is ast.InfixExpr { - if (branch.cond as ast.InfixExpr).op == .key_is { - break - } - } } g.defer_ifdef = '' } @@ -227,33 +210,37 @@ fn (mut g Gen) comp_if(node ast.IfExpr) { } } -fn (mut g Gen) comp_if_expr(cond ast.Expr) { +fn (mut g Gen) comp_if_cond(cond ast.Expr) bool { match cond { ast.BoolLiteral { g.expr(cond) + return true } ast.ParExpr { g.write('(') - g.comp_if_expr(cond.expr) + is_cond_true := g.comp_if_cond(cond.expr) g.write(')') + return is_cond_true } ast.PrefixExpr { g.write(cond.op.str()) - g.comp_if_expr(cond.right) + return g.comp_if_cond(cond.right) } ast.PostfixExpr { ifdef := g.comp_if_to_ifdef((cond.expr as ast.Ident).name, true) or { verror(err) - return + return false } g.write('defined($ifdef)') + return true } ast.InfixExpr { match cond.op { .and, .logical_or { - g.comp_if_expr(cond.left) + l := g.comp_if_cond(cond.left) g.write(' $cond.op ') - g.comp_if_expr(cond.right) + r := g.comp_if_cond(cond.right) + return l && r } .key_is, .not_is { left := cond.left @@ -268,23 +255,34 @@ fn (mut g Gen) comp_if_expr(cond ast.Expr) { // this is only allowed for generics currently, otherwise blocked by checker exp_type = g.unwrap_generic(left.typ) } - op := if cond.op == .key_is { '==' } else { '!=' } - g.write('$exp_type $op $got_type') + + 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 } - else {} } } ast.Ident { ifdef := g.comp_if_to_ifdef(cond.name, false) or { 'true' } // handled in checker g.write('defined($ifdef)') + return true } else { // should be unreachable, but just in case g.write('1') + return true } } } diff --git a/vlib/v/tests/comptime_if_expr_test.v b/vlib/v/tests/comptime_if_expr_test.v index 3f739ef1a9..9e65f994a8 100644 --- a/vlib/v/tests/comptime_if_expr_test.v +++ b/vlib/v/tests/comptime_if_expr_test.v @@ -81,6 +81,14 @@ fn test_generic_t_is3() { assert res == GenericTIsTest{} } +fn generic_t_is_with_else(raw_data string) ?T { + $if T is string { + return raw_data + } $else { + return T{} + } +} + fn test_generic_t_is_with_else() { res := generic_t_is_with_else('') or { assert false @@ -94,10 +102,25 @@ fn test_generic_t_is_with_else() { assert str == 'test' } -fn generic_t_is_with_else(raw_data string) ?T { - $if T is string { - return raw_data - } $else { - return T{} +fn generic_t_is_with_else_if() []string { + mut fields := []string{} + $for field in T.fields { + $if field.typ is string { + fields << field.name + } $else $if field.typ is int { + fields << field.name + } } + return fields } + +struct User { + name string + age int +} + +fn test_generic_t_is_with_else_if() { + x := generic_t_is_with_else_if() + assert x == ['name', 'age'] +} +