Skip to content

Commit

Permalink
checker, cgen: fix generic static method call return type resolution (v…
Browse files Browse the repository at this point in the history
  • Loading branch information
felipensp authored Nov 16, 2024
1 parent 7d6a301 commit 4c557cf
Show file tree
Hide file tree
Showing 7 changed files with 110 additions and 14 deletions.
6 changes: 3 additions & 3 deletions vlib/v/ast/table.v
Original file line number Diff line number Diff line change
Expand Up @@ -1630,7 +1630,7 @@ pub fn (t &Table) does_type_implement_interface(typ Type, inter_typ Type) bool {
return false
}

pub fn (mut t Table) convert_generic_static_type_name(fn_name string, generic_names []string, concrete_types []Type) string {
pub fn (mut t Table) convert_generic_static_type_name(fn_name string, generic_names []string, concrete_types []Type) (Type, string) {
if index := fn_name.index('__static__') {
if index > 0 {
generic_name := fn_name[0..index]
Expand All @@ -1639,12 +1639,12 @@ pub fn (mut t Table) convert_generic_static_type_name(fn_name string, generic_na
if valid_generic {
name_type := t.find_type(generic_name).set_flag(.generic)
if typ := t.convert_generic_type(name_type, generic_names, concrete_types) {
return '${t.type_to_str(typ)}${fn_name[index..]}'
return name_type, '${t.type_to_str(typ)}${fn_name[index..]}'
}
}
}
}
return fn_name
return void_type, fn_name
}

// convert_generic_type convert generics to real types (T => int) or other generics type.
Expand Down
21 changes: 15 additions & 6 deletions vlib/v/checker/assign.v
Original file line number Diff line number Diff line change
Expand Up @@ -414,14 +414,23 @@ fn (mut c Checker) assign_stmt(mut node ast.AssignStmt) {
if left.obj.ct_type_var in [.generic_var, .no_comptime]
&& c.table.cur_fn != unsafe { nil }
&& c.table.cur_fn.generic_names.len != 0
&& !right.comptime_ret_val
&& right.return_type_generic.has_flag(.generic)
&& c.is_generic_expr(right) {
&& !right.comptime_ret_val && c.is_generic_expr(right) {
// mark variable as generic var because its type changes according to fn return generic resolution type
left.obj.ct_type_var = .generic_var
fn_ret_type := c.resolve_return_type(right)
if fn_ret_type != ast.void_type
&& c.table.final_sym(fn_ret_type).kind != .multi_return {
if right.return_type_generic.has_flag(.generic) {
fn_ret_type := c.resolve_return_type(right)
if fn_ret_type != ast.void_type
&& c.table.final_sym(fn_ret_type).kind != .multi_return {
var_type := if right.or_block.kind == .absent {
fn_ret_type
} else {
fn_ret_type.clear_option_and_result()
}
c.comptime.type_map['g.${left.name}.${left.obj.pos.pos}'] = var_type
}
} else if right.is_static_method
&& right.left_type.has_flag(.generic) {
fn_ret_type := c.unwrap_generic(c.resolve_return_type(right))
var_type := if right.or_block.kind == .absent {
fn_ret_type
} else {
Expand Down
16 changes: 14 additions & 2 deletions vlib/v/checker/fn.v
Original file line number Diff line number Diff line change
Expand Up @@ -848,8 +848,8 @@ fn (mut c Checker) fn_call(mut node ast.CallExpr, mut continue_check &bool) ast.
if node.is_static_method {
// resolve static call T.name()
if c.table.cur_fn != unsafe { nil } {
fn_name = c.table.convert_generic_static_type_name(fn_name, c.table.cur_fn.generic_names,
c.table.cur_concrete_types)
node.left_type, fn_name = c.table.convert_generic_static_type_name(fn_name,
c.table.cur_fn.generic_names, c.table.cur_concrete_types)
}
}
if fn_name == 'main' {
Expand Down Expand Up @@ -1096,6 +1096,7 @@ fn (mut c Checker) fn_call(mut node ast.CallExpr, mut continue_check &bool) ast.
func = f
unsafe { c.table.fns[orig_name].usages++ }
node.name = orig_name
node.left_type = typ
}
}
}
Expand Down Expand Up @@ -1875,6 +1876,9 @@ fn (mut c Checker) is_generic_expr(node ast.Expr) bool {
if node.args.any(c.comptime.is_generic_param_var(it.expr)) {
return true
}
if node.is_static_method && node.left_type.has_flag(.generic) {
return true
}
// fn[T]() or generic_var.fn[T]()
node.concrete_types.any(it.has_flag(.generic))
}
Expand Down Expand Up @@ -3916,6 +3920,14 @@ fn (mut c Checker) resolve_return_type(node ast.CallExpr) ast.Type {
if method := c.table.find_method(left_sym, node.name) {
return c.resolve_fn_return_type(method, node)
}
} else if node.is_static_method {
if c.table.cur_fn != unsafe { nil } {
_, name := c.table.convert_generic_static_type_name(node.name, c.table.cur_fn.generic_names,
c.table.cur_concrete_types)
if func := c.table.find_fn(name) {
return c.resolve_fn_return_type(func, node)
}
}
} else {
if func := c.table.find_fn(node.name) {
return c.resolve_fn_return_type(func, node)
Expand Down
3 changes: 3 additions & 0 deletions vlib/v/checker/return.v
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,9 @@ fn (mut c Checker) return_stmt(mut node ast.Return) {
if expr.obj.smartcasts.len > 0 {
typ = c.unwrap_generic(expr.obj.smartcasts.last())
}
if expr.obj.ct_type_var != .no_comptime {
typ = c.comptime.get_type_or_default(expr, typ)
}
}
}
got_types << typ
Expand Down
7 changes: 7 additions & 0 deletions vlib/v/gen/c/assign.v
Original file line number Diff line number Diff line change
Expand Up @@ -322,6 +322,13 @@ fn (mut g Gen) assign_stmt(node_ ast.AssignStmt) {
g.comptime.type_map['g.${left.name}.${left.obj.pos.pos}'] = var_type
// eprintln('>> ${func.name} > resolve ${left.name}.${left.obj.pos.pos}.generic to ${g.table.type_to_str(var_type)}')
}
} else if val.is_static_method && val.left_type.has_flag(.generic) {
fn_ret_type := g.resolve_return_type(val)
var_type = fn_ret_type
val_type = var_type
left.obj.typ = var_type
g.comptime.type_map['g.${left.name}.${left.obj.pos.pos}'] = var_type
g.assign_ct_type = var_type
}
}
is_auto_heap = left.obj.is_auto_heap
Expand Down
23 changes: 20 additions & 3 deletions vlib/v/gen/c/fn.v
Original file line number Diff line number Diff line change
Expand Up @@ -1386,6 +1386,23 @@ fn (mut g Gen) resolve_return_type(node ast.CallExpr) ast.Type {
}
}
}
} else if node.is_static_method {
if g.cur_fn != unsafe { nil } {
_, name := g.table.convert_generic_static_type_name(node.name, g.cur_fn.generic_names,
g.cur_concrete_types)
if func := g.table.find_fn(name) {
return if node.or_block.kind == .absent {
func.return_type
} else {
func.return_type.clear_option_and_result()
}
}
}
return if node.or_block.kind == .absent {
node.return_type
} else {
node.return_type.clear_option_and_result()
}
} else {
if func := g.table.find_fn(node.name) {
if func.generic_names.len > 0 {
Expand Down Expand Up @@ -2014,7 +2031,7 @@ fn (mut g Gen) fn_call(node ast.CallExpr) {
// will be `0` for `foo()`
mut is_interface_call := false
mut is_selector_call := false
if node.left_type != 0 {
if node.is_method && node.left_type != 0 {
mut fn_typ := ast.no_type
left_sym := g.table.sym(node.left_type)
if node.is_field {
Expand Down Expand Up @@ -2048,7 +2065,7 @@ fn (mut g Gen) fn_call(node ast.CallExpr) {
if node.is_static_method {
// resolve static call T.name()
if g.cur_fn != unsafe { nil } {
name = g.table.convert_generic_static_type_name(node.name, g.cur_fn.generic_names,
_, name = g.table.convert_generic_static_type_name(node.name, g.cur_fn.generic_names,
g.cur_concrete_types)
}
}
Expand Down Expand Up @@ -2276,7 +2293,7 @@ fn (mut g Gen) fn_call(node ast.CallExpr) {
// Temp fix generate call fn error when the struct type of sumtype
// has the fn field and is same to the struct name.
mut is_cast_needed := true
if node.left_type != 0 {
if node.is_method && node.left_type != 0 {
left_sym := g.table.sym(node.left_type)
if left_sym.kind == .struct && node.name == obj.name {
is_cast_needed = false
Expand Down
48 changes: 48 additions & 0 deletions vlib/v/tests/fns/generic_static_method_call_test.v
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
struct Parser {
mut:
data []u8
}

fn (mut p Parser) read_element[T]() !T {
t := T.parse(mut p)!
return t
}

struct TestA {
data []u8
}

fn TestA.parse(mut p Parser) !TestA {
return TestA{
data: p.data
}
}

struct TestB {
st string
}

fn TestB.parse(mut p Parser) !TestB {
return TestB{
st: p.data.hex()
}
}

fn test_main() {
data := []u8{len: 5, init: u8(5)}
mut p := Parser{
data: data
}

ta := p.read_element[TestA]()!
dump(ta)
assert ta == TestA{
data: [u8(5), 5, 5, 5, 5]
}

tb := p.read_element[TestB]()!
dump(tb)
assert tb == TestB{
st: '0505050505'
}
}

0 comments on commit 4c557cf

Please sign in to comment.