From ae04167c321fa30120f6cf6a8e78cb4540601cd5 Mon Sep 17 00:00:00 2001 From: Felipe Pena Date: Thu, 17 Oct 2024 15:48:44 -0300 Subject: [PATCH] v: allow checking of method comptime signature with `is` operator (fix #8843) (#22549) --- vlib/v/ast/types.v | 2 +- vlib/v/checker/if.v | 17 +++++++++- vlib/v/gen/c/comptime.v | 18 ++++++++++- .../comptime/comptime_call_is_check_test.v | 31 +++++++++++++++++++ 4 files changed, 65 insertions(+), 3 deletions(-) create mode 100644 vlib/v/tests/comptime/comptime_call_is_check_test.v diff --git a/vlib/v/ast/types.v b/vlib/v/ast/types.v index fb7e8fa23..2c5148df3 100644 --- a/vlib/v/ast/types.v +++ b/vlib/v/ast/types.v @@ -1604,7 +1604,7 @@ pub fn (t &Table) fn_signature_using_aliases(func &Fn, import_aliases map[string sb.write_string(func.name) } sb.write_string('(') - start := int(opts.skip_receiver) + start := int(func.is_method && opts.skip_receiver) for i in start .. func.params.len { if i != start { sb.write_string(', ') diff --git a/vlib/v/checker/if.v b/vlib/v/checker/if.v index 13b3aa029..e267d6728 100644 --- a/vlib/v/checker/if.v +++ b/vlib/v/checker/if.v @@ -191,7 +191,22 @@ fn (mut c Checker) if_expr(mut node ast.IfExpr) ast.Type { checked_type = c.unwrap_generic(var.smartcasts.last()) } } - skip_state = c.check_compatible_types(checked_type, right as ast.TypeNode) + if sym.info is ast.FnType + && c.comptime.comptime_for_method_var == left.name { + skip_state = if c.table.fn_signature(sym.info.func, + skip_receiver: true + type_only: true + ) == c.table.fn_signature(c.comptime.comptime_for_method, + skip_receiver: true + type_only: true + ) { + .eval + } else { + .skip + } + } else { + skip_state = c.check_compatible_types(checked_type, right as ast.TypeNode) + } } } if branch.cond.op == .not_is && skip_state != .unknown { diff --git a/vlib/v/gen/c/comptime.v b/vlib/v/gen/c/comptime.v index 579c7cac6..9cf381769 100644 --- a/vlib/v/gen/c/comptime.v +++ b/vlib/v/gen/c/comptime.v @@ -574,7 +574,23 @@ fn (mut g Gen) comptime_if_cond(cond ast.Expr, pkg_exist bool) (bool, bool) { return !is_true, true } } - if cond.op == .key_is { + 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('${exp_type.idx()} == ${got_type.idx()} && ${exp_type.has_flag(.option)} == ${got_type.has_flag(.option)}') return exp_type == got_type, true } else { diff --git a/vlib/v/tests/comptime/comptime_call_is_check_test.v b/vlib/v/tests/comptime/comptime_call_is_check_test.v new file mode 100644 index 000000000..e4d62eaae --- /dev/null +++ b/vlib/v/tests/comptime/comptime_call_is_check_test.v @@ -0,0 +1,31 @@ +module main + +struct Struct1 { + num f64 +} + +pub type DoThingFunc = fn (f32) bool + +fn (mut s Struct1) do_thing(a f32) bool { + return false +} + +fn register[T]() { + obj := T{} + mut called := false + $for method in T.methods { + $if method !is DoThingFunc { + println('method ${method.name} is not do thing') + assert false + } $else { + println('method ${method.name} is do thing') + assert obj.$method(1.23) == false + called = true + } + } + assert called +} + +fn test_main() { + register[Struct1]() +} -- 2.39.5