1
0
mirror of https://github.com/vlang/v.git synced 2023-08-10 21:13:21 +03:00

checker: fix generic struct field with default fn_type value (fix #19011) (#19014)

This commit is contained in:
yuyi 2023-08-01 02:30:12 +08:00 committed by GitHub
parent a609d6c9d1
commit 9be80198fc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 113 additions and 51 deletions

View File

@ -173,7 +173,7 @@ fn (mut c Checker) check_types(got ast.Type, expected ast.Type) bool {
return false
}
}
if expected.has_flag(.generic) {
if expected.has_flag(.generic) && !got.has_flag(.generic) {
return false
}
return true

View File

@ -74,6 +74,9 @@ pub mut:
inside_ct_attr bool // true inside `[if expr]`
inside_x_is_type bool // true inside the Type expression of `if x is Type {`
inside_comptime_for_field bool
inside_generic_struct_init bool
cur_struct_generic_types []ast.Type
cur_struct_concrete_types []ast.Type
skip_flags bool // should `#flag` and `#include` be skipped
fn_level int // 0 for the top level, 1 for `fn abc() {}`, 2 for a nested fn, etc
smartcast_mut_pos token.Pos // match mut foo, if mut foo is Foo
@ -2438,13 +2441,21 @@ fn (mut c Checker) stmts_ending_with_expression(mut stmts []ast.Stmt) {
}
fn (mut c Checker) unwrap_generic(typ ast.Type) ast.Type {
if typ.has_flag(.generic) && c.table.cur_fn != unsafe { nil } {
if typ.has_flag(.generic) {
if c.inside_generic_struct_init {
generic_names := c.cur_struct_generic_types.map(c.table.sym(it).name)
if t_typ := c.table.resolve_generic_to_concrete(typ, generic_names, c.cur_struct_concrete_types) {
return t_typ
}
}
if c.table.cur_fn != unsafe { nil } {
if t_typ := c.table.resolve_generic_to_concrete(typ, c.table.cur_fn.generic_names,
c.table.cur_concrete_types)
{
return t_typ
}
}
}
return typ
}
@ -3329,9 +3340,11 @@ fn (mut c Checker) ident(mut node ast.Ident) ast.Type {
if func := c.table.find_fn(node.name) {
if func.generic_names.len > 0 {
concrete_types := node.concrete_types.map(c.unwrap_generic(it))
if concrete_types.all(!it.has_flag(.generic)) {
c.table.register_fn_concrete_types(func.fkey(), concrete_types)
}
}
}
return info.typ
} else if node.kind == .unresolved {
// first use
@ -3493,9 +3506,11 @@ fn (mut c Checker) ident(mut node ast.Ident) ast.Type {
concrete_types)
{
fn_type = typ_
if concrete_types.all(!it.has_flag(.generic)) {
c.table.register_fn_concrete_types(func.fkey(), concrete_types)
}
}
}
node.name = name
node.kind = .function
node.info = ast.IdentFn{

View File

@ -155,6 +155,9 @@ fn (mut c Checker) struct_decl(mut node ast.StructDecl) {
true)
}
}
} else if c.table.final_sym(field.typ).kind == .function
&& field.default_expr_typ.is_pointer() {
continue
} else {
c.error('incompatible initializer for field `${field.name}`: ${err.msg()}',
field.default_expr.pos())
@ -352,6 +355,17 @@ fn (mut c Checker) struct_init(mut node ast.StructInit, is_field_zero_struct_ini
c.error('a non generic struct `${node.typ_str}` used like a generic struct',
node.name_pos)
}
if struct_sym.info.generic_types.len > 0
&& struct_sym.info.generic_types.len == struct_sym.info.concrete_types.len {
c.inside_generic_struct_init = true
c.cur_struct_generic_types = struct_sym.info.generic_types.clone()
c.cur_struct_concrete_types = struct_sym.info.concrete_types.clone()
defer {
c.inside_generic_struct_init = false
c.cur_struct_generic_types = []
c.cur_struct_concrete_types = []
}
}
} else if struct_sym.info is ast.Alias {
parent_sym := c.table.sym(struct_sym.info.parent_type)
// e.g. ´x := MyMapAlias{}´, should be a cast to alias type ´x := MyMapAlias(map[...]...)´
@ -613,10 +627,10 @@ fn (mut c Checker) struct_init(mut node ast.StructInit, is_field_zero_struct_ini
// and the second part is all fields embedded in the structure
// If the return value data composition form in `c.table.struct_fields()` is modified,
// need to modify here accordingly.
fields := c.table.struct_fields(type_sym)
mut fields := c.table.struct_fields(type_sym)
mut checked_types := []ast.Type{}
for i, field in fields {
for i, mut field in fields {
if field.name in inited_fields {
continue
}
@ -628,7 +642,7 @@ fn (mut c Checker) struct_init(mut node ast.StructInit, is_field_zero_struct_ini
}
if field.has_default_expr {
if i < info.fields.len && field.default_expr_typ == 0 {
if field.default_expr is ast.StructInit {
if mut field.default_expr is ast.StructInit {
idx := c.table.find_type_idx(field.default_expr.typ_str)
if idx != 0 {
info.fields[i].default_expr_typ = ast.new_type(idx)
@ -637,6 +651,9 @@ fn (mut c Checker) struct_init(mut node ast.StructInit, is_field_zero_struct_ini
if field.typ.is_any_kind_of_pointer() {
info.fields[i].default_expr_typ = field.typ
}
} else if field.default_expr is ast.Ident
&& field.default_expr.info is ast.IdentFn {
c.expr(mut field.default_expr)
} else {
if const_field := c.table.global_scope.find_const('${field.default_expr}') {
info.fields[i].default_expr_typ = const_field.typ

View File

@ -0,0 +1,30 @@
[heap]
struct BloomFilter1[T] {
hash_func fn (T) u32 = unsafe { nil } // hash function, input [T] , output u32
table_size int // every entry is one-bit, packed into `table`
num_functions int // 1~16
}
[heap]
struct BloomFilter2[T] {
// TODO V bug
hash_func fn (T) u32 = default_cb[T] // hash function, input [T] , output u32
table_size int // every entry is one-bit, packed into `table`
num_functions int // 1~16
mut:
table []u8
}
fn default_cb[T](x T) u32 {
return 22
}
fn test_generic_struct_field_with_default_fn_type() {
filter1 := BloomFilter1[int]{}
println(filter1)
assert true
filter2 := BloomFilter2[int]{}
println(filter2.hash_func(11))
assert filter2.hash_func(11) == 22
}