mirror of
https://github.com/vlang/v.git
synced 2023-08-10 21:13:21 +03:00
checker: infer generic type T from matching fn call argument (#6298)
This commit is contained in:
parent
580fefe63b
commit
f7decfe399
@ -372,3 +372,20 @@ pub fn (mut c Checker) string_inter_lit(mut node ast.StringInterLiteral) table.T
|
|||||||
pub fn (c &Checker) check_sumtype_compatibility(a, b table.Type) bool {
|
pub fn (c &Checker) check_sumtype_compatibility(a, b table.Type) bool {
|
||||||
return c.table.sumtype_has_variant(a, b) || c.table.sumtype_has_variant(b, a)
|
return c.table.sumtype_has_variant(a, b) || c.table.sumtype_has_variant(b, a)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn (mut c Checker) infer_fn_types(f table.Fn, mut call_expr ast.CallExpr) {
|
||||||
|
gt_name := 'T'
|
||||||
|
mut typ := table.void_type
|
||||||
|
for i, arg in f.params {
|
||||||
|
if arg.type_source_name == gt_name {
|
||||||
|
typ = call_expr.args[i].typ
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if typ == table.void_type {
|
||||||
|
c.error('could not infer generic type `$gt_name` in call to `$f.name`', call_expr.pos)
|
||||||
|
} else {
|
||||||
|
c.table.register_fn_gen_type(f.name, typ)
|
||||||
|
call_expr.generic_type = typ
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -56,6 +56,7 @@ mut:
|
|||||||
inside_sql bool // to handle sql table fields pseudo variables
|
inside_sql bool // to handle sql table fields pseudo variables
|
||||||
cur_orm_ts table.TypeSymbol
|
cur_orm_ts table.TypeSymbol
|
||||||
error_details []string
|
error_details []string
|
||||||
|
generic_funcs []&ast.FnDecl
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new_checker(table &table.Table, pref &pref.Preferences) Checker {
|
pub fn new_checker(table &table.Table, pref &pref.Preferences) Checker {
|
||||||
@ -80,6 +81,7 @@ pub fn (mut c Checker) check(ast_file ast.File) {
|
|||||||
c.stmt(stmt)
|
c.stmt(stmt)
|
||||||
}
|
}
|
||||||
c.check_scope_vars(c.file.scope)
|
c.check_scope_vars(c.file.scope)
|
||||||
|
c.post_process_generic_fns()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn (mut c Checker) check_scope_vars(sc &ast.Scope) {
|
pub fn (mut c Checker) check_scope_vars(sc &ast.Scope) {
|
||||||
@ -1505,6 +1507,10 @@ pub fn (mut c Checker) call_fn(mut call_expr ast.CallExpr) table.Type {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if f.is_generic && call_expr.generic_type == table.void_type {
|
||||||
|
// no type arguments given in call, attempt implicit instantiation
|
||||||
|
c.infer_fn_types(f, mut call_expr)
|
||||||
|
}
|
||||||
if call_expr.generic_type != table.void_type && f.return_type != 0 { // table.t_type {
|
if call_expr.generic_type != table.void_type && f.return_type != 0 { // table.t_type {
|
||||||
// Handle `foo<T>() T` => `foo<int>() int` => return int
|
// Handle `foo<T>() T` => `foo<int>() int` => return int
|
||||||
return_sym := c.table.get_type_symbol(f.return_type)
|
return_sym := c.table.get_type_symbol(f.return_type)
|
||||||
@ -3962,10 +3968,16 @@ fn (mut c Checker) fetch_and_verify_orm_fields(info table.Struct, pos token.Posi
|
|||||||
return fields
|
return fields
|
||||||
}
|
}
|
||||||
|
|
||||||
fn (mut c Checker) fn_decl(mut node ast.FnDecl) {
|
fn (mut c Checker) post_process_generic_fns() {
|
||||||
c.returns = false
|
// Loop thru each generic function concrete type.
|
||||||
if node.is_generic && c.cur_generic_type == 0 { // need the cur_generic_type check to avoid inf. recursion
|
// Check each specific fn instantiation.
|
||||||
// loop thru each generic type and generate a function
|
for i in 0 .. c.generic_funcs.len {
|
||||||
|
mut node := c.generic_funcs[i]
|
||||||
|
if c.table.fn_gen_types.len == 0 {
|
||||||
|
// no concrete types, so just skip:
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
// eprintln('>> post_process_generic_fns $c.file.path | $node.name , c.table.fn_gen_types.len: $c.table.fn_gen_types.len')
|
||||||
for gen_type in c.table.fn_gen_types[node.name] {
|
for gen_type in c.table.fn_gen_types[node.name] {
|
||||||
c.cur_generic_type = gen_type
|
c.cur_generic_type = gen_type
|
||||||
// sym:=c.table.get_type_symbol(gen_type)
|
// sym:=c.table.get_type_symbol(gen_type)
|
||||||
@ -3973,6 +3985,24 @@ fn (mut c Checker) fn_decl(mut node ast.FnDecl) {
|
|||||||
c.fn_decl(mut node)
|
c.fn_decl(mut node)
|
||||||
}
|
}
|
||||||
c.cur_generic_type = 0
|
c.cur_generic_type = 0
|
||||||
|
c.generic_funcs[i] = 0
|
||||||
|
}
|
||||||
|
// The generic funtions for each file/mod should be
|
||||||
|
// postprocessed just once in the checker, while the file/mod
|
||||||
|
// context is still the same.
|
||||||
|
c.generic_funcs = []
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (mut c Checker) fn_decl(mut node ast.FnDecl) {
|
||||||
|
c.returns = false
|
||||||
|
if node.is_generic && c.cur_generic_type == 0 {
|
||||||
|
// Just remember the generic function for now.
|
||||||
|
// It will be processed later in c.post_process_generic_fns,
|
||||||
|
// after all other normal functions are processed.
|
||||||
|
// This is done so that all generic function calls can
|
||||||
|
// have a chance to populate c.table.fn_gen_types with
|
||||||
|
// the correct concrete types.
|
||||||
|
c.generic_funcs << node
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if node.language == .v && !c.is_builtin_mod {
|
if node.language == .v && !c.is_builtin_mod {
|
||||||
|
33
vlib/v/tests/generic_fn_infer_test.v
Normal file
33
vlib/v/tests/generic_fn_infer_test.v
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
fn call<T>(v T) {
|
||||||
|
}
|
||||||
|
|
||||||
|
fn simple<T>(p T) T {
|
||||||
|
return p
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_infer() {
|
||||||
|
call(3)
|
||||||
|
i := 4
|
||||||
|
r := simple(i)
|
||||||
|
assert r == 4
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_explicit_calls_should_also_work() {
|
||||||
|
call<int>(2)
|
||||||
|
assert true
|
||||||
|
simple<int>(5)
|
||||||
|
assert true
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
fn choose4<T>(a, b, c, d T) T {
|
||||||
|
// NB: a similar construct is used in prime31's via engine
|
||||||
|
return a
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_calling_generic_fn_with_many_params() {
|
||||||
|
x := choose4(1, 2, 3, 4)
|
||||||
|
assert x == 1
|
||||||
|
y := choose4<string>('abc', 'xyz', 'def', 'ghi')
|
||||||
|
assert y == 'abc'
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user