From ca3da8b630c3f1307707ac9573c6fdbee919f534 Mon Sep 17 00:00:00 2001 From: shove Date: Mon, 15 Jan 2024 02:01:48 +0800 Subject: [PATCH] ast, markused, checker: fix mark methods into used-list, when generics as receivers (fix #20509) (#20527) --- vlib/v/ast/ast.v | 57 ++++++++++--------- vlib/v/checker/fn.v | 6 ++ vlib/v/markused/walker.v | 7 +++ .../skip_unused/generics_as_receiver.run.out | 1 + .../generics_as_receiver.skip_unused.run.out | 1 + .../tests/skip_unused/generics_as_receiver.vv | 9 +++ 6 files changed, 53 insertions(+), 28 deletions(-) create mode 100644 vlib/v/tests/skip_unused/generics_as_receiver.run.out create mode 100644 vlib/v/tests/skip_unused/generics_as_receiver.skip_unused.run.out create mode 100644 vlib/v/tests/skip_unused/generics_as_receiver.vv diff --git a/vlib/v/ast/ast.v b/vlib/v/ast/ast.v index d36c436c7..1f08c58ab 100644 --- a/vlib/v/ast/ast.v +++ b/vlib/v/ast/ast.v @@ -736,34 +736,35 @@ pub: name_pos token.Pos mod string pub mut: - name string // left.name() - is_method bool - is_field bool // temp hack, remove ASAP when re-impl CallExpr / Selector (joe) - is_fn_var bool // fn variable, `a := fn() {}`, then: `a()` - is_fn_a_const bool // fn const, `const c = abc`, where `abc` is a function, then: `c()` - is_keep_alive bool // GC must not free arguments before fn returns - is_noreturn bool // whether the function/method is marked as [noreturn] - is_ctor_new bool // if JS ctor calls requires `new` before call, marked as `[use_new]` in V - is_file_translated bool // true, when the file it resides in is `[translated]` - args []CallArg - expected_arg_types []Type - comptime_ret_val bool - language Language - or_block OrExpr - left Expr // `user` in `user.register()` - left_type Type // type of `user` - receiver_type Type // User - return_type Type - fn_var_type Type // the fn type, when `is_fn_a_const` or `is_fn_var` is true - const_name string // the fully qualified name of the const, i.e. `main.c`, given `const c = abc`, and callexpr: `c()` - should_be_skipped bool // true for calls to `[if someflag?]` functions, when there is no `-d someflag` - concrete_types []Type // concrete types, e.g. - concrete_list_pos token.Pos - raw_concrete_types []Type - free_receiver bool // true if the receiver expression needs to be freed - scope &Scope = unsafe { nil } - from_embed_types []Type // holds the type of the embed that the method is called from - comments []Comment + name string // left.name() + is_method bool + is_field bool // temp hack, remove ASAP when re-impl CallExpr / Selector (joe) + is_fn_var bool // fn variable, `a := fn() {}`, then: `a()` + is_fn_a_const bool // fn const, `const c = abc`, where `abc` is a function, then: `c()` + is_keep_alive bool // GC must not free arguments before fn returns + is_noreturn bool // whether the function/method is marked as [noreturn] + is_ctor_new bool // if JS ctor calls requires `new` before call, marked as `[use_new]` in V + is_file_translated bool // true, when the file it resides in is `[translated]` + args []CallArg + expected_arg_types []Type + comptime_ret_val bool + language Language + or_block OrExpr + left Expr // `user` in `user.register()` + left_type Type // type of `user` + receiver_type Type // User + receiver_concrete_type Type // We need the receiver to be T in cgen, so save the concrete type to node.receiver_concrete_type + return_type Type + fn_var_type Type // the fn type, when `is_fn_a_const` or `is_fn_var` is true + const_name string // the fully qualified name of the const, i.e. `main.c`, given `const c = abc`, and callexpr: `c()` + should_be_skipped bool // true for calls to `[if someflag?]` functions, when there is no `-d someflag` + concrete_types []Type // concrete types, e.g. + concrete_list_pos token.Pos + raw_concrete_types []Type + free_receiver bool // true if the receiver expression needs to be freed + scope &Scope = unsafe { nil } + from_embed_types []Type // holds the type of the embed that the method is called from + comments []Comment } /* diff --git a/vlib/v/checker/fn.v b/vlib/v/checker/fn.v index b6549a735..28c04b517 100644 --- a/vlib/v/checker/fn.v +++ b/vlib/v/checker/fn.v @@ -2360,11 +2360,17 @@ fn (mut c Checker) method_call(mut node ast.CallExpr) ast.Type { node.receiver_type = node.from_embed_types.last().derive(method.params[0].typ) } else if is_generic { // We need the receiver to be T in cgen. + // so save the concrete type to node.receiver_concrete_type // TODO: cant we just set all these to the concrete type in checker? then no need in gen node.receiver_type = left_type.derive(method.params[0].typ).set_flag(.generic) } else { node.receiver_type = method.params[0].typ } + node.receiver_concrete_type = if is_method_from_embed { + node.from_embed_types.last().derive(method.params[0].typ) + } else { + method.params[0].typ + } if left_sym.kind == .interface_ && is_method_from_embed && method.return_type.has_flag(.generic) && method.generic_names.len == 0 { method.generic_names = c.table.get_generic_names((rec_sym.info as ast.Interface).generic_types) diff --git a/vlib/v/markused/walker.v b/vlib/v/markused/walker.v index 5f6d1cf9f..16db5159f 100644 --- a/vlib/v/markused/walker.v +++ b/vlib/v/markused/walker.v @@ -516,6 +516,13 @@ pub fn (mut w Walker) call_expr(mut node ast.CallExpr) { return } w.mark_fn_as_used(fn_name) + if node.is_method && node.receiver_type.has_flag(.generic) && node.receiver_concrete_type != 0 + && !node.receiver_concrete_type.has_flag(.generic) { + // We need the receiver to be T in cgen. + // so save the concrete type to node.receiver_concrete_type + fkey := '${int(node.receiver_concrete_type)}.${node.name}' + w.used_fns[fkey] = true + } stmt := w.all_fns[fn_name] or { return } if stmt.name == node.name { if !node.is_method || node.receiver_type == stmt.receiver.typ { diff --git a/vlib/v/tests/skip_unused/generics_as_receiver.run.out b/vlib/v/tests/skip_unused/generics_as_receiver.run.out new file mode 100644 index 000000000..ddc2d74b1 --- /dev/null +++ b/vlib/v/tests/skip_unused/generics_as_receiver.run.out @@ -0,0 +1 @@ +0000-00-00T00:00:00.000000000Z diff --git a/vlib/v/tests/skip_unused/generics_as_receiver.skip_unused.run.out b/vlib/v/tests/skip_unused/generics_as_receiver.skip_unused.run.out new file mode 100644 index 000000000..ddc2d74b1 --- /dev/null +++ b/vlib/v/tests/skip_unused/generics_as_receiver.skip_unused.run.out @@ -0,0 +1 @@ +0000-00-00T00:00:00.000000000Z diff --git a/vlib/v/tests/skip_unused/generics_as_receiver.vv b/vlib/v/tests/skip_unused/generics_as_receiver.vv new file mode 100644 index 000000000..6639f6c3d --- /dev/null +++ b/vlib/v/tests/skip_unused/generics_as_receiver.vv @@ -0,0 +1,9 @@ +import time + +fn foo[T](val T) string { + return val.format_rfc3339_nano() +} + +fn main() { + println(foo(time.Time{})) +} -- 2.39.5