From 57c4188d98efe40b5dc4707b53f37ed0a1abe04f Mon Sep 17 00:00:00 2001 From: yuyi Date: Fri, 15 Jul 2022 19:18:06 +0800 Subject: [PATCH] ast, checker: check generic fn declaration error (#15079) --- vlib/v/ast/table.v | 64 +++++++++++++++++++ vlib/v/checker/fn.v | 11 +++- .../v/checker/tests/generic_fn_decl_err_a.out | 7 ++ vlib/v/checker/tests/generic_fn_decl_err_a.vv | 19 ++++++ 4 files changed, 100 insertions(+), 1 deletion(-) create mode 100644 vlib/v/checker/tests/generic_fn_decl_err_a.out create mode 100644 vlib/v/checker/tests/generic_fn_decl_err_a.vv diff --git a/vlib/v/ast/table.v b/vlib/v/ast/table.v index 23781c1d2e..cc56ee541b 100644 --- a/vlib/v/ast/table.v +++ b/vlib/v/ast/table.v @@ -1686,6 +1686,70 @@ pub fn (mut t Table) resolve_generic_to_concrete(generic_type Type, generic_name return none } +fn generic_names_push_with_filter(mut to_names []string, from_names []string) { + for name in from_names { + if name !in to_names { + to_names << name + } + } +} + +pub fn (mut t Table) generic_type_names(generic_type Type) []string { + mut names := []string{} + mut sym := t.sym(generic_type) + if sym.name.len == 1 && sym.name[0].is_capital() { + names << sym.name + return names + } + match mut sym.info { + Array { + mut elem_type := sym.info.elem_type + mut elem_sym := t.sym(elem_type) + mut dims := 1 + for mut elem_sym.info is Array { + elem_type = elem_sym.info.elem_type + elem_sym = t.sym(elem_type) + dims++ + } + names << t.generic_type_names(elem_type) + } + ArrayFixed { + names << t.generic_type_names(sym.info.elem_type) + } + Chan { + names << t.generic_type_names(sym.info.elem_type) + } + FnType { + mut func := sym.info.func + if func.return_type.has_flag(.generic) { + names << t.generic_type_names(func.return_type) + } + func.params = func.params.clone() + for mut param in func.params { + if param.typ.has_flag(.generic) { + generic_names_push_with_filter(mut names, t.generic_type_names(param.typ)) + } + } + } + MultiReturn { + for ret_type in sym.info.types { + generic_names_push_with_filter(mut names, t.generic_type_names(ret_type)) + } + } + Map { + names << t.generic_type_names(sym.info.key_type) + generic_names_push_with_filter(mut names, t.generic_type_names(sym.info.value_type)) + } + Struct, Interface, SumType { + if sym.info.is_generic { + names << sym.info.generic_types.map(t.sym(it).name) + } + } + else {} + } + return names +} + pub fn (mut t Table) unwrap_generic_type(typ Type, generic_names []string, concrete_types []Type) Type { mut final_concrete_types := []Type{} mut fields := []StructField{} diff --git a/vlib/v/checker/fn.v b/vlib/v/checker/fn.v index c65edbdcd4..3ee1de6922 100644 --- a/vlib/v/checker/fn.v +++ b/vlib/v/checker/fn.v @@ -207,7 +207,16 @@ fn (mut c Checker) fn_decl(mut node ast.FnDecl) { } } } - //&& node.params.len == 1 && param.typ.is_ptr() { + if param.typ.has_flag(.generic) { + generic_names := c.table.generic_type_names(param.typ) + for name in generic_names { + if name !in node.generic_names { + fn_generic_names := node.generic_names.join(', ') + c.error('generic type name `$name` is not mentioned in fn `$node.name<$fn_generic_names>`', + param.type_pos) + } + } + } if (c.pref.translated || c.file.is_translated) && node.is_variadic && param.typ.is_ptr() { // TODO c2v hack to fix `(const char *s, ...)` param.typ = ast.int_type.ref() diff --git a/vlib/v/checker/tests/generic_fn_decl_err_a.out b/vlib/v/checker/tests/generic_fn_decl_err_a.out new file mode 100644 index 0000000000..b379de2714 --- /dev/null +++ b/vlib/v/checker/tests/generic_fn_decl_err_a.out @@ -0,0 +1,7 @@ +vlib/v/checker/tests/generic_fn_decl_err_a.vv:17:28: error: generic type name `P` is not mentioned in fn `create` + 15 | } + 16 | + 17 | fn (r Db) create(u U, p P) { + | ^ + 18 | println('Yo') + 19 | } diff --git a/vlib/v/checker/tests/generic_fn_decl_err_a.vv b/vlib/v/checker/tests/generic_fn_decl_err_a.vv new file mode 100644 index 0000000000..98be9c4413 --- /dev/null +++ b/vlib/v/checker/tests/generic_fn_decl_err_a.vv @@ -0,0 +1,19 @@ +module main + +struct Db {} + +struct User {} + +struct Post {} + +fn main() { + r := Db{} + u := User{} + p := Post{} + + r.create(u, p) +} + +fn (r Db) create(u U, p P) { + println('Yo') +}