mirror of
https://github.com/vlang/v.git
synced 2023-08-10 21:13:21 +03:00
This commit is contained in:
parent
a609d6c9d1
commit
9be80198fc
@ -173,7 +173,7 @@ fn (mut c Checker) check_types(got ast.Type, expected ast.Type) bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if expected.has_flag(.generic) {
|
if expected.has_flag(.generic) && !got.has_flag(.generic) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
|
@ -39,46 +39,49 @@ pub const (
|
|||||||
pub struct Checker {
|
pub struct Checker {
|
||||||
pref &pref.Preferences = unsafe { nil } // Preferences shared from V struct
|
pref &pref.Preferences = unsafe { nil } // Preferences shared from V struct
|
||||||
pub mut:
|
pub mut:
|
||||||
table &ast.Table = unsafe { nil }
|
table &ast.Table = unsafe { nil }
|
||||||
file &ast.File = unsafe { nil }
|
file &ast.File = unsafe { nil }
|
||||||
nr_errors int
|
nr_errors int
|
||||||
nr_warnings int
|
nr_warnings int
|
||||||
nr_notices int
|
nr_notices int
|
||||||
errors []errors.Error
|
errors []errors.Error
|
||||||
warnings []errors.Warning
|
warnings []errors.Warning
|
||||||
notices []errors.Notice
|
notices []errors.Notice
|
||||||
error_lines []int // to avoid printing multiple errors for the same line
|
error_lines []int // to avoid printing multiple errors for the same line
|
||||||
expected_type ast.Type
|
expected_type ast.Type
|
||||||
expected_or_type ast.Type // fn() or { 'this type' } eg. string. expected or block type
|
expected_or_type ast.Type // fn() or { 'this type' } eg. string. expected or block type
|
||||||
expected_expr_type ast.Type // if/match is_expr: expected_type
|
expected_expr_type ast.Type // if/match is_expr: expected_type
|
||||||
mod string // current module name
|
mod string // current module name
|
||||||
const_var &ast.ConstField = unsafe { nil } // the current constant, when checking const declarations
|
const_var &ast.ConstField = unsafe { nil } // the current constant, when checking const declarations
|
||||||
const_deps []string
|
const_deps []string
|
||||||
const_names []string
|
const_names []string
|
||||||
global_names []string
|
global_names []string
|
||||||
locked_names []string // vars that are currently locked
|
locked_names []string // vars that are currently locked
|
||||||
rlocked_names []string // vars that are currently read-locked
|
rlocked_names []string // vars that are currently read-locked
|
||||||
in_for_count int // if checker is currently in a for loop
|
in_for_count int // if checker is currently in a for loop
|
||||||
should_abort bool // when too many errors/warnings/notices are accumulated, .should_abort becomes true. It is checked in statement/expression loops, so the checker can return early, instead of wasting time.
|
should_abort bool // when too many errors/warnings/notices are accumulated, .should_abort becomes true. It is checked in statement/expression loops, so the checker can return early, instead of wasting time.
|
||||||
returns bool
|
returns bool
|
||||||
scope_returns bool
|
scope_returns bool
|
||||||
is_builtin_mod bool // true inside the 'builtin', 'os' or 'strconv' modules; TODO: remove the need for special casing this
|
is_builtin_mod bool // true inside the 'builtin', 'os' or 'strconv' modules; TODO: remove the need for special casing this
|
||||||
is_just_builtin_mod bool // true only inside 'builtin'
|
is_just_builtin_mod bool // true only inside 'builtin'
|
||||||
is_generated bool // true for `[generated] module xyz` .v files
|
is_generated bool // true for `[generated] module xyz` .v files
|
||||||
inside_unsafe bool // true inside `unsafe {}` blocks
|
inside_unsafe bool // true inside `unsafe {}` blocks
|
||||||
inside_const bool // true inside `const ( ... )` blocks
|
inside_const bool // true inside `const ( ... )` blocks
|
||||||
inside_anon_fn bool // true inside `fn() { ... }()`
|
inside_anon_fn bool // true inside `fn() { ... }()`
|
||||||
inside_ref_lit bool // true inside `a := &something`
|
inside_ref_lit bool // true inside `a := &something`
|
||||||
inside_defer bool // true inside `defer {}` blocks
|
inside_defer bool // true inside `defer {}` blocks
|
||||||
inside_fn_arg bool // `a`, `b` in `a.f(b)`
|
inside_fn_arg bool // `a`, `b` in `a.f(b)`
|
||||||
inside_ct_attr bool // true inside `[if expr]`
|
inside_ct_attr bool // true inside `[if expr]`
|
||||||
inside_x_is_type bool // true inside the Type expression of `if x is Type {`
|
inside_x_is_type bool // true inside the Type expression of `if x is Type {`
|
||||||
inside_comptime_for_field bool
|
inside_comptime_for_field bool
|
||||||
skip_flags bool // should `#flag` and `#include` be skipped
|
inside_generic_struct_init bool
|
||||||
fn_level int // 0 for the top level, 1 for `fn abc() {}`, 2 for a nested fn, etc
|
cur_struct_generic_types []ast.Type
|
||||||
smartcast_mut_pos token.Pos // match mut foo, if mut foo is Foo
|
cur_struct_concrete_types []ast.Type
|
||||||
smartcast_cond_pos token.Pos // match cond
|
skip_flags bool // should `#flag` and `#include` be skipped
|
||||||
ct_cond_stack []ast.Expr
|
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
|
||||||
|
smartcast_cond_pos token.Pos // match cond
|
||||||
|
ct_cond_stack []ast.Expr
|
||||||
mut:
|
mut:
|
||||||
stmt_level int // the nesting level inside each stmts list;
|
stmt_level int // the nesting level inside each stmts list;
|
||||||
// .stmt_level is used to check for `evaluated but not used` ExprStmts like `1 << 1`
|
// .stmt_level is used to check for `evaluated but not used` ExprStmts like `1 << 1`
|
||||||
@ -2438,11 +2441,19 @@ fn (mut c Checker) stmts_ending_with_expression(mut stmts []ast.Stmt) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn (mut c Checker) unwrap_generic(typ ast.Type) ast.Type {
|
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 t_typ := c.table.resolve_generic_to_concrete(typ, c.table.cur_fn.generic_names,
|
if c.inside_generic_struct_init {
|
||||||
c.table.cur_concrete_types)
|
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
|
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
|
return typ
|
||||||
@ -3329,7 +3340,9 @@ fn (mut c Checker) ident(mut node ast.Ident) ast.Type {
|
|||||||
if func := c.table.find_fn(node.name) {
|
if func := c.table.find_fn(node.name) {
|
||||||
if func.generic_names.len > 0 {
|
if func.generic_names.len > 0 {
|
||||||
concrete_types := node.concrete_types.map(c.unwrap_generic(it))
|
concrete_types := node.concrete_types.map(c.unwrap_generic(it))
|
||||||
c.table.register_fn_concrete_types(func.fkey(), concrete_types)
|
if concrete_types.all(!it.has_flag(.generic)) {
|
||||||
|
c.table.register_fn_concrete_types(func.fkey(), concrete_types)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return info.typ
|
return info.typ
|
||||||
@ -3493,7 +3506,9 @@ fn (mut c Checker) ident(mut node ast.Ident) ast.Type {
|
|||||||
concrete_types)
|
concrete_types)
|
||||||
{
|
{
|
||||||
fn_type = typ_
|
fn_type = typ_
|
||||||
c.table.register_fn_concrete_types(func.fkey(), concrete_types)
|
if concrete_types.all(!it.has_flag(.generic)) {
|
||||||
|
c.table.register_fn_concrete_types(func.fkey(), concrete_types)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
node.name = name
|
node.name = name
|
||||||
|
@ -155,6 +155,9 @@ fn (mut c Checker) struct_decl(mut node ast.StructDecl) {
|
|||||||
true)
|
true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else if c.table.final_sym(field.typ).kind == .function
|
||||||
|
&& field.default_expr_typ.is_pointer() {
|
||||||
|
continue
|
||||||
} else {
|
} else {
|
||||||
c.error('incompatible initializer for field `${field.name}`: ${err.msg()}',
|
c.error('incompatible initializer for field `${field.name}`: ${err.msg()}',
|
||||||
field.default_expr.pos())
|
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',
|
c.error('a non generic struct `${node.typ_str}` used like a generic struct',
|
||||||
node.name_pos)
|
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 {
|
} else if struct_sym.info is ast.Alias {
|
||||||
parent_sym := c.table.sym(struct_sym.info.parent_type)
|
parent_sym := c.table.sym(struct_sym.info.parent_type)
|
||||||
// e.g. ´x := MyMapAlias{}´, should be a cast to alias type ´x := MyMapAlias(map[...]...)´
|
// 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
|
// 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,
|
// If the return value data composition form in `c.table.struct_fields()` is modified,
|
||||||
// need to modify here accordingly.
|
// 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{}
|
mut checked_types := []ast.Type{}
|
||||||
|
|
||||||
for i, field in fields {
|
for i, mut field in fields {
|
||||||
if field.name in inited_fields {
|
if field.name in inited_fields {
|
||||||
continue
|
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 field.has_default_expr {
|
||||||
if i < info.fields.len && field.default_expr_typ == 0 {
|
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)
|
idx := c.table.find_type_idx(field.default_expr.typ_str)
|
||||||
if idx != 0 {
|
if idx != 0 {
|
||||||
info.fields[i].default_expr_typ = ast.new_type(idx)
|
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() {
|
if field.typ.is_any_kind_of_pointer() {
|
||||||
info.fields[i].default_expr_typ = field.typ
|
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 {
|
} else {
|
||||||
if const_field := c.table.global_scope.find_const('${field.default_expr}') {
|
if const_field := c.table.global_scope.find_const('${field.default_expr}') {
|
||||||
info.fields[i].default_expr_typ = const_field.typ
|
info.fields[i].default_expr_typ = const_field.typ
|
||||||
|
@ -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
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user