From bfc6d5469f219131c293662e7172bdfe1e4bdf96 Mon Sep 17 00:00:00 2001 From: Felipe Pena Date: Mon, 2 Jun 2025 04:17:24 -0300 Subject: [PATCH] comptime: fix `T.indirections` comparison (fix #24630) (#24636) --- cmd/tools/vvet/vvet.v | 4 +-- vlib/v/checker/comptime.v | 11 +++++++- vlib/v/gen/c/comptime.v | 26 ++++++++++++++----- .../comptime/comptime_indirections_test.v | 23 ++++++++++++++++ vlib/v/type_resolver/comptime_resolver.v | 7 ++--- 5 files changed, 58 insertions(+), 13 deletions(-) create mode 100644 vlib/v/tests/comptime/comptime_indirections_test.v diff --git a/cmd/tools/vvet/vvet.v b/cmd/tools/vvet/vvet.v index 168e992316..72047b1691 100644 --- a/cmd/tools/vvet/vvet.v +++ b/cmd/tools/vvet/vvet.v @@ -404,8 +404,8 @@ fn (mut vt Vet) const_decl(stmt ast.ConstDecl) { fn (mut vt Vet) vet_empty_str(expr ast.InfixExpr) { if expr.left is ast.SelectorExpr && expr.right is ast.IntegerLiteral { operand := (expr.left as ast.SelectorExpr) // TODO: remove as-casts when multiple conds can be smart-casted. - if operand.expr is ast.Ident && operand.expr.info.typ == ast.string_type_idx - && operand.field_name == 'len' { + if operand.expr is ast.Ident && operand.field_name == 'len' + && operand.expr.info.typ == ast.string_type_idx { if expr.op != .lt && expr.right.val == '0' { // Case: `var.len > 0`, `var.len == 0`, `var.len != 0` op := if expr.op == .gt { '!=' } else { expr.op.str() } diff --git a/vlib/v/checker/comptime.v b/vlib/v/checker/comptime.v index 4a8d110aee..ca5a1b1551 100644 --- a/vlib/v/checker/comptime.v +++ b/vlib/v/checker/comptime.v @@ -846,10 +846,14 @@ fn (mut c Checker) comptime_if_cond(mut cond ast.Expr, pos token.Pos) ComptimeBr } } .eq, .ne { - if cond.left is ast.SelectorExpr + if mut cond.left is ast.SelectorExpr && cond.right in [ast.IntegerLiteral, ast.StringLiteral] { // $if field.indirections == 1 // $if method.args.len == 1 + if cond.left.typ == 0 && cond.left.name_type == 0 + && (cond.left.expr is ast.Ident && cond.left.expr.name.len == 1) { + c.expr(mut cond.left) + } return .unknown } else if cond.left is ast.SelectorExpr && c.comptime.check_comptime_is_field_selector_bool(cond.left) { @@ -908,6 +912,11 @@ fn (mut c Checker) comptime_if_cond(mut cond ast.Expr, pos token.Pos) ComptimeBr } } .gt, .lt, .ge, .le { + if cond.left is ast.SelectorExpr && cond.left.typ == 0 + && cond.left.name_type == 0 + && (cond.left.expr is ast.Ident && cond.left.expr.name.len == 1) { + c.expr(mut cond.left) + } if cond.left is ast.SelectorExpr && cond.right is ast.IntegerLiteral && c.comptime.is_comptime_selector_field_name(cond.left, 'indirections') { return .unknown diff --git a/vlib/v/gen/c/comptime.v b/vlib/v/gen/c/comptime.v index 623130f60c..51bd61bd22 100644 --- a/vlib/v/gen/c/comptime.v +++ b/vlib/v/gen/c/comptime.v @@ -612,7 +612,8 @@ fn (mut g Gen) comptime_if_cond(cond ast.Expr, pkg_exist bool) (bool, bool) { .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 }) { + || 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 @@ -645,9 +646,14 @@ fn (mut g Gen) comptime_if_cond(cond ast.Expr, pkg_exist bool) (bool, bool) { } } 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 { g.comptime.comptime_for_field_type.nr_muls() == cond.right.val.i64() } - .ne { g.comptime.comptime_for_field_type.nr_muls() != cond.right.val.i64() } + .eq { left_muls == cond.right.val.i64() } + .ne { left_muls != cond.right.val.i64() } else { false } } if is_true { @@ -723,11 +729,17 @@ fn (mut g Gen) comptime_if_cond(cond ast.Expr, pkg_exist bool) (bool, bool) { .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 { g.comptime.comptime_for_field_type.nr_muls() > cond.right.val.i64() } - .lt { g.comptime.comptime_for_field_type.nr_muls() < cond.right.val.i64() } - .ge { g.comptime.comptime_for_field_type.nr_muls() >= cond.right.val.i64() } - .le { g.comptime.comptime_for_field_type.nr_muls() <= cond.right.val.i64() } + .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 { diff --git a/vlib/v/tests/comptime/comptime_indirections_test.v b/vlib/v/tests/comptime/comptime_indirections_test.v new file mode 100644 index 0000000000..1780c3a633 --- /dev/null +++ b/vlib/v/tests/comptime/comptime_indirections_test.v @@ -0,0 +1,23 @@ +fn count_muls[T](val T) int { + mut nr_muls := 0 + dump(T.indirections) + dump(typeof(val).indirections) + $if T.indirections > 0 { + nr_muls = (int(typeof(val).idx) >> 16) & 0xff + } + assert nr_muls == T.indirections + $if T.indirections != 0 { + nr_muls = (int(typeof(val).idx) >> 16) & 0xff + } + assert nr_muls == T.indirections + return nr_muls +} + +fn test_main() { + val := '' + pval := &val + ppval := &pval + assert count_muls(val) == 0 + assert count_muls(pval) == 1 + assert count_muls(ppval) == 2 +} diff --git a/vlib/v/type_resolver/comptime_resolver.v b/vlib/v/type_resolver/comptime_resolver.v index de3c1ca684..80993be0e7 100644 --- a/vlib/v/type_resolver/comptime_resolver.v +++ b/vlib/v/type_resolver/comptime_resolver.v @@ -177,11 +177,12 @@ pub fn (mut t TypeResolver) get_comptime_selector_type(node ast.ComptimeSelector return default_type } -// is_comptime_selector_field_name checks if the SelectorExpr is related to $for variable accessing specific field name provided by `field_name` +// is_comptime_selector_field_name checks if the SelectorExpr is related to $for variable or generic letter accessing specific field name provided by `field_name` @[inline] pub fn (t &ResolverInfo) is_comptime_selector_field_name(node ast.SelectorExpr, field_name string) bool { - return t.comptime_for_field_var != '' && node.expr is ast.Ident - && node.expr.name == t.comptime_for_field_var && node.field_name == field_name + return ((t.comptime_for_field_var != '' && node.expr is ast.Ident + && node.expr.name == t.comptime_for_field_var) || node.name_type != 0) + && node.field_name == field_name } // is_comptime_selector_type checks if the SelectorExpr is related to $for variable accessing .typ field