Skip to content

Commit

Permalink
ast, markused, checker: fix mark methods into used-list, when generic…
Browse files Browse the repository at this point in the history
…s as receivers (fix vlang#20509) (vlang#20527)
  • Loading branch information
shove70 authored Jan 14, 2024
1 parent 43b8cc8 commit ca3da8b
Show file tree
Hide file tree
Showing 6 changed files with 53 additions and 28 deletions.
57 changes: 29 additions & 28 deletions vlib/v/ast/ast.v
Original file line number Diff line number Diff line change
Expand Up @@ -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. <int, string>
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. <int, string>
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
}

/*
Expand Down
6 changes: 6 additions & 0 deletions vlib/v/checker/fn.v
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
7 changes: 7 additions & 0 deletions vlib/v/markused/walker.v
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
1 change: 1 addition & 0 deletions vlib/v/tests/skip_unused/generics_as_receiver.run.out
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
0000-00-00T00:00:00.000000000Z
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
0000-00-00T00:00:00.000000000Z
9 changes: 9 additions & 0 deletions vlib/v/tests/skip_unused/generics_as_receiver.vv
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import time

fn foo[T](val T) string {
return val.format_rfc3339_nano()
}

fn main() {
println(foo(time.Time{}))
}

0 comments on commit ca3da8b

Please sign in to comment.