Skip to content

Commit 844c332

Browse files
authored
ast,checker: add type checking for param of fn passed as generic arg (fix #26195) (#26257)
1 parent e20a432 commit 844c332

11 files changed

Lines changed: 140 additions & 31 deletions

File tree

vlib/math/stats/stats.v

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ pub fn geometric_mean[T](data []T) T {
5050
return math.pow(sum, f64(1.0) / data.len)
5151
} $else {
5252
// use f32 for f32/int/...
53-
return T(math.powf(sum, f32(1.0) / data.len))
53+
return T(math.powf(f32(sum), f32(1.0) / data.len))
5454
}
5555
}
5656

@@ -131,7 +131,7 @@ pub fn rms[T](data []T) T {
131131
// use f32 for f32/int/...
132132
mut sum := f32(0)
133133
for v in data {
134-
sum += math.powf(v, 2)
134+
sum += math.powf(f32(v), 2)
135135
}
136136
return T(math.sqrtf(sum / data.len))
137137
}
@@ -203,7 +203,7 @@ pub fn population_stddev[T](data []T) T {
203203
$if T is f64 {
204204
return math.sqrt(population_variance[T](data))
205205
} $else {
206-
return T(math.sqrtf(population_variance[T](data)))
206+
return T(math.sqrtf(f32(population_variance[T](data))))
207207
}
208208
}
209209

@@ -218,7 +218,7 @@ pub fn population_stddev_mean[T](data []T, mean T) T {
218218
$if T is f64 {
219219
return math.sqrt(population_variance_mean[T](data, mean))
220220
} $else {
221-
return T(math.sqrtf(population_variance_mean[T](data, mean)))
221+
return T(math.sqrtf(f32(population_variance_mean[T](data, mean))))
222222
}
223223
}
224224

@@ -234,7 +234,7 @@ pub fn sample_stddev[T](data []T) T {
234234
$if T is f64 {
235235
return math.sqrt(sample_variance[T](data))
236236
} $else {
237-
return T(math.sqrtf(sample_variance[T](data)))
237+
return T(math.sqrtf(f32(sample_variance[T](data))))
238238
}
239239
}
240240

vlib/v/ast/table.v

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2181,6 +2181,28 @@ pub fn (mut t Table) unwrap_generic_type_ex(typ Type, generic_names []string, co
21812181
idx := t.find_or_register_map(unwrap_key_type, unwrap_value_type)
21822182
return new_type(idx).derive_add_muls(typ).clear_flag(.generic)
21832183
}
2184+
FnType {
2185+
mut unwrapped_fn := ts.info.func
2186+
unwrapped_fn.params = unwrapped_fn.params.clone()
2187+
mut has_generic := false
2188+
for i, param in unwrapped_fn.params {
2189+
if param.typ.has_flag(.generic) {
2190+
unwrapped_fn.params[i].typ = t.unwrap_generic_type_ex(param.typ, generic_names,
2191+
concrete_types, recheck_concrete_types)
2192+
has_generic = true
2193+
}
2194+
}
2195+
if unwrapped_fn.return_type.has_flag(.generic) {
2196+
unwrapped_fn.return_type = t.unwrap_generic_type_ex(unwrapped_fn.return_type,
2197+
generic_names, concrete_types, recheck_concrete_types)
2198+
has_generic = true
2199+
}
2200+
if has_generic {
2201+
idx := t.find_or_register_fn_type(unwrapped_fn, true, false)
2202+
return new_type(idx).derive_add_muls(typ).clear_flag(.generic)
2203+
}
2204+
return typ
2205+
}
21842206
Struct, Interface, SumType {
21852207
if !ts.info.is_generic {
21862208
return typ
@@ -2423,7 +2445,13 @@ pub fn (mut t Table) unwrap_generic_type_ex(typ Type, generic_names []string, co
24232445
}
24242446
return new_type(new_idx).derive(typ).clear_flag(.generic)
24252447
}
2426-
else {}
2448+
else {
2449+
if typ.has_flag(.generic) {
2450+
if converted := t.convert_generic_type(typ, generic_names, concrete_types) {
2451+
return converted
2452+
}
2453+
}
2454+
}
24272455
}
24282456
return typ
24292457
}

vlib/v/checker/containers.v

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -314,21 +314,36 @@ fn (mut c Checker) array_init(mut node ast.ArrayInit) ast.Type {
314314

315315
fn (mut c Checker) check_array_init_default_expr(mut node ast.ArrayInit) {
316316
mut init_expr := node.init_expr
317-
c.expected_type = node.elem_type
317+
mut expected_elem_type := node.elem_type
318+
if node.elem_type.has_flag(.generic) && c.table.cur_fn != unsafe { nil } {
319+
if c.table.cur_fn.generic_names.len > 0
320+
&& c.table.cur_concrete_types.len == c.table.cur_fn.generic_names.len {
321+
expr := node.init_expr
322+
if expr is ast.CallExpr {
323+
if func := c.table.find_fn(expr.name) {
324+
if !func.return_type.has_flag(.generic) {
325+
expected_elem_type = c.table.unwrap_generic_type(node.elem_type,
326+
c.table.cur_fn.generic_names, c.table.cur_concrete_types)
327+
}
328+
}
329+
}
330+
}
331+
}
332+
c.expected_type = expected_elem_type
318333
init_typ := c.check_expr_option_or_result_call(init_expr, c.expr(mut init_expr))
319334
node.init_type = init_typ
320-
if !node.elem_type.has_flag(.option) && init_typ.has_flag(.option) {
335+
if !expected_elem_type.has_flag(.option) && init_typ.has_flag(.option) {
321336
c.error('cannot use unwrapped Option as initializer', init_expr.pos())
322337
}
323-
if node.elem_type.is_number() && init_typ.is_number() {
338+
if expected_elem_type.is_number() && init_typ.is_number() {
324339
return
325340
}
326-
if c.table.type_kind(node.elem_type) == .interface {
327-
if c.type_implements(init_typ, node.elem_type, init_expr.pos()) {
341+
if c.table.type_kind(expected_elem_type) == .interface {
342+
if c.type_implements(init_typ, expected_elem_type, init_expr.pos()) {
328343
return
329344
}
330345
}
331-
c.check_expected(init_typ, node.elem_type) or { c.error(err.msg(), init_expr.pos()) }
346+
c.check_expected(init_typ, expected_elem_type) or { c.error(err.msg(), init_expr.pos()) }
332347
}
333348

334349
fn (mut c Checker) check_array_init_para_type(para string, mut expr ast.Expr, pos token.Pos) {

vlib/v/checker/fn.v

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1553,11 +1553,19 @@ fn (mut c Checker) fn_call(mut node ast.CallExpr, mut continue_check &bool) ast.
15531553
c.error('cannot have parameter after array decompose', node.pos)
15541554
}
15551555
param_i := i + nr_multi_values
1556-
param := if func.is_variadic && i >= func.params.len - 1 {
1556+
mut param := if func.is_variadic && i >= func.params.len - 1 {
15571557
func.params.last()
15581558
} else {
15591559
func.params[param_i]
15601560
}
1561+
if node.is_fn_var && param.typ.has_flag(.generic) && c.table.cur_fn != unsafe { nil }
1562+
&& c.table.cur_fn.generic_names.len > 0
1563+
&& c.table.cur_fn.generic_names.len == c.table.cur_concrete_types.len {
1564+
mut unwrapped := param
1565+
unwrapped.typ = c.table.unwrap_generic_type(param.typ, c.table.cur_fn.generic_names,
1566+
c.table.cur_concrete_types)
1567+
param = unwrapped
1568+
}
15611569
// registers if the arg must be passed by ref to disable auto deref args
15621570
call_arg.should_be_ptr = param.typ.is_ptr() && !param.is_mut
15631571
if func.is_variadic && call_arg.expr is ast.ArrayDecompose {
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
vlib/v/checker/tests/generics_fn_param_wrong_arg_type_err.vv:2:11: error: cannot use `int literal` as `string` in argument 1 to `f`
2+
1 | fn test[T, U](f fn (T) U) U {
3+
2 | return f(1)
4+
| ^
5+
3 | }
6+
4 |
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
fn test[T, U](f fn (T) U) U {
2+
return f(1)
3+
}
4+
5+
fn main() {
6+
println(test(fn (x string) int {
7+
return x.len
8+
}))
9+
}

vlib/v/gen/c/array.v

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1491,7 +1491,8 @@ fn (mut g Gen) gen_array_wait(node ast.CallExpr) {
14911491
thread_type := arr.array_info().elem_type
14921492
thread_sym := g.table.sym(thread_type)
14931493
thread_ret_type := thread_sym.thread_info().return_type
1494-
elsymcname := g.table.sym(thread_ret_type).cname
1494+
unwrapped_ret_type := g.unwrap_generic(thread_ret_type)
1495+
elsymcname := g.table.sym(unwrapped_ret_type).cname
14951496
fn_name := g.register_thread_array_wait_call(elsymcname)
14961497
g.write('${fn_name}(')
14971498
if node.left_type.is_ptr() {
@@ -1506,7 +1507,8 @@ fn (mut g Gen) gen_fixed_array_wait(node ast.CallExpr) {
15061507
thread_type := arr.array_fixed_info().elem_type
15071508
thread_sym := g.table.sym(thread_type)
15081509
thread_ret_type := thread_sym.thread_info().return_type
1509-
elsymcname := g.table.sym(thread_ret_type).cname
1510+
unwrapped_ret_type := g.unwrap_generic(thread_ret_type)
1511+
elsymcname := g.table.sym(unwrapped_ret_type).cname
15101512
fn_name := g.register_thread_fixed_array_wait_call(node, elsymcname)
15111513
g.write('${fn_name}(')
15121514
g.expr(node.left)

vlib/v/gen/c/cgen.v

Lines changed: 32 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1563,13 +1563,40 @@ fn (mut g Gen) register_thread_void_wait_call() {
15631563
g.gowrappers.writeln('void __v_thread_wait(__v_thread thread) {')
15641564
if g.pref.os == .windows {
15651565
g.gowrappers.writeln('\tu32 stat = WaitForSingleObject(thread, INFINITE);')
1566+
g.gowrappers.writeln('\tif (stat != WAIT_OBJECT_0) { builtin___v_panic(_S("error waiting thread")); }')
15661567
} else {
15671568
g.gowrappers.writeln('\tint stat = pthread_join(thread, (void **)NULL);')
1569+
g.gowrappers.writeln('\tif (stat != 0) { builtin___v_panic(_S("error waiting thread")); }')
15681570
}
1569-
g.gowrappers.writeln('\tif (stat != 0) { builtin___v_panic(_S("unable to join thread")); }')
1571+
g.gowrappers.writeln('}')
1572+
}
1573+
1574+
fn (mut g Gen) register_thread_wait_call(eltyp string) {
1575+
thread_typ := '__v_thread_${eltyp}'
1576+
fn_name := '${thread_typ}_wait'
1577+
lock g.waiter_fns {
1578+
if fn_name in g.waiter_fns {
1579+
return
1580+
}
1581+
g.waiter_fns << fn_name
1582+
g.waiter_fn_definitions.writeln('${eltyp} ${fn_name}(${thread_typ} thread);')
1583+
}
1584+
g.gowrappers.writeln('${eltyp} ${fn_name}(${thread_typ} thread) {')
1585+
g.gowrappers.writeln('\t${eltyp} res;')
15701586
if g.pref.os == .windows {
1571-
g.gowrappers.writeln('\tCloseHandle(thread);')
1587+
g.gowrappers.writeln('\tu32 stat = WaitForSingleObject(thread.handle, INFINITE);')
1588+
g.gowrappers.writeln('\tif (stat != WAIT_OBJECT_0) { builtin___v_panic(_S("error waiting thread")); }')
1589+
g.gowrappers.writeln('\tCloseHandle(thread.handle);')
1590+
g.gowrappers.writeln('\tres = *(${eltyp}*)(thread.ret_ptr);')
1591+
g.gowrappers.writeln('\tbuiltin___v_free(thread.ret_ptr);')
1592+
} else {
1593+
g.gowrappers.writeln('\tvoid* ret_val;')
1594+
g.gowrappers.writeln('\tint stat = pthread_join(thread, &ret_val);')
1595+
g.gowrappers.writeln('\tif (stat != 0) { builtin___v_panic(_S("error waiting thread")); }')
1596+
g.gowrappers.writeln('\tres = *(${eltyp}*)ret_val;')
1597+
g.gowrappers.writeln('\tbuiltin___v_free(ret_val);')
15721598
}
1599+
g.gowrappers.writeln('\treturn res;')
15731600
g.gowrappers.writeln('}')
15741601
}
15751602

@@ -1599,6 +1626,7 @@ void ${fn_name}(${thread_arr_typ} a) {
15991626
}
16001627
}')
16011628
} else {
1629+
g.register_thread_wait_call(eltyp)
16021630
g.waiter_fn_definitions.writeln('${ret_typ} ${fn_name}(${thread_arr_typ} a);')
16031631
g.gowrappers.writeln('
16041632
${ret_typ} ${fn_name}(${thread_arr_typ} a) {
@@ -1641,12 +1669,13 @@ fn (mut g Gen) register_thread_fixed_array_wait_call(node ast.CallExpr, eltyp st
16411669
g.gowrappers.writeln('
16421670
void ${fn_name}(${thread_arr_typ} a) {
16431671
for (${ast.int_type_name} i = 0; i < ${len}; ++i) {
1644-
${thread_typ} t = ((${thread_typ}*)a)[i];
1672+
${thread_typ} t = a[i];
16451673
if (t == 0) continue;
16461674
__v_thread_wait(t);
16471675
}
16481676
}')
16491677
} else {
1678+
g.register_thread_wait_call(eltyp)
16501679
g.waiter_fn_definitions.writeln('${ret_typ} ${fn_name}(${thread_arr_typ} a);')
16511680
g.gowrappers.writeln('
16521681
${ret_typ} ${fn_name}(${thread_arr_typ} a) {

vlib/v/gen/c/spawn_and_go.v

Lines changed: 18 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -119,8 +119,8 @@ fn (mut g Gen) spawn_and_go_expr(node ast.SpawnExpr, mode SpawnGoMode) {
119119
g.expr(arg.expr)
120120
g.writeln(';')
121121
}
122-
call_ret_type := node.call_expr.return_type
123-
s_ret_typ := g.styp(call_ret_type)
122+
call_ret_type := g.unwrap_generic(node.call_expr.return_type)
123+
s_ret_typ := g.styp(g.unwrap_generic(call_ret_type))
124124
if g.pref.os == .windows && call_ret_type != ast.void_type {
125125
g.writeln('${arg_tmp_var}->ret_ptr = (void *) builtin___v_malloc(sizeof(${s_ret_typ}));')
126126
}
@@ -182,14 +182,17 @@ fn (mut g Gen) spawn_and_go_expr(node ast.SpawnExpr, mode SpawnGoMode) {
182182
if should_register {
183183
g.type_definitions.writeln('\ntypedef struct ${wrapper_struct_name} {')
184184
mut fn_var := ''
185+
mut wrapper_return_type := call_ret_type
185186
if node.call_expr.is_fn_var {
186187
fn_sym := g.table.sym(node.call_expr.fn_var_type)
187188
info := fn_sym.info as ast.FnType
188-
fn_var = g.fn_var_signature(ast.void_type, info.func.return_type, info.func.params.map(it.typ),
189+
wrapper_return_type = info.func.return_type
190+
fn_var = g.fn_var_signature(ast.void_type, wrapper_return_type, info.func.params.map(it.typ),
189191
'fn')
190192
} else if node.call_expr.left is ast.AnonFn {
191193
f := node.call_expr.left.decl
192-
fn_var = g.fn_var_signature(ast.void_type, f.return_type, f.params.map(it.typ),
194+
wrapper_return_type = f.return_type
195+
fn_var = g.fn_var_signature(ast.void_type, wrapper_return_type, f.params.map(it.typ),
193196
'fn')
194197
} else {
195198
if node.call_expr.is_method {
@@ -198,6 +201,7 @@ fn (mut g Gen) spawn_and_go_expr(node ast.SpawnExpr, mode SpawnGoMode) {
198201
mut muttable := unsafe { &ast.Table(g.table) }
199202
return_type := muttable.convert_generic_type(f.return_type, f.generic_names,
200203
node.call_expr.concrete_types) or { f.return_type }
204+
wrapper_return_type = return_type
201205
mut arg_types := f.params.map(it.typ)
202206
arg_types = arg_types.map(muttable.convert_generic_type(it, f.generic_names,
203207
node.call_expr.concrete_types) or { it })
@@ -209,6 +213,7 @@ fn (mut g Gen) spawn_and_go_expr(node ast.SpawnExpr, mode SpawnGoMode) {
209213
concrete_types := node.call_expr.concrete_types.map(g.unwrap_generic(it))
210214
return_type := g.table.convert_generic_type(f.return_type, f.generic_names,
211215
concrete_types) or { f.return_type }
216+
wrapper_return_type = return_type
212217
mut arg_types := f.params.map(it.typ)
213218
arg_types = arg_types.map(g.table.convert_generic_type(it, f.generic_names,
214219
concrete_types) or { it })
@@ -230,14 +235,16 @@ fn (mut g Gen) spawn_and_go_expr(node ast.SpawnExpr, mode SpawnGoMode) {
230235
}
231236
}
232237
}
238+
wrapper_return_type = g.unwrap_generic(wrapper_return_type)
239+
wrapper_s_ret_typ := g.styp(wrapper_return_type)
233240
if fn_var != '' {
234241
g.type_definitions.writeln('\t${fn_var};')
235242
}
236243
if expr.is_method {
237244
styp := g.styp(expr.receiver_type)
238245
g.type_definitions.writeln('\t${styp} arg0;')
239246
}
240-
need_return_ptr := g.pref.os == .windows && call_ret_type != ast.void_type
247+
need_return_ptr := g.pref.os == .windows && wrapper_return_type != ast.void_type
241248
for i, arg in expr.args {
242249
arg_sym := g.table.sym(arg.typ)
243250
if arg_sym.info is ast.FnType {
@@ -256,13 +263,13 @@ fn (mut g Gen) spawn_and_go_expr(node ast.SpawnExpr, mode SpawnGoMode) {
256263
thread_ret_type := if g.pref.os == .windows { 'u32' } else { 'void*' }
257264
g.waiter_fn_definitions.writeln('${g.static_non_parallel}${thread_ret_type} ${wrapper_fn_name}(${wrapper_struct_name} *arg);')
258265
g.gowrappers.writeln('${thread_ret_type} ${wrapper_fn_name}(${wrapper_struct_name} *arg) {')
259-
if call_ret_type != ast.void_type {
266+
if wrapper_return_type != ast.void_type {
260267
if g.pref.os == .windows {
261-
g.gowrappers.write_string('\t*((${s_ret_typ}*)(arg->ret_ptr)) = ')
268+
g.gowrappers.write_string('\t*((${wrapper_s_ret_typ}*)(arg->ret_ptr)) = ')
262269
} else {
263-
g.gowrappers.writeln('\t${s_ret_typ}* ret_ptr = (${s_ret_typ}*) builtin___v_malloc(sizeof(${s_ret_typ}));')
270+
g.gowrappers.writeln('\t${wrapper_s_ret_typ}* ret_ptr = (${wrapper_s_ret_typ}*) builtin___v_malloc(sizeof(${wrapper_s_ret_typ}));')
264271
$if tinyc && arm64 {
265-
g.gowrappers.write_string('\t${s_ret_typ} tcc_bug_tmp_var = ')
272+
g.gowrappers.write_string('\t${wrapper_s_ret_typ} tcc_bug_tmp_var = ')
266273
} $else {
267274
g.gowrappers.write_string('\t*ret_ptr = ')
268275
}
@@ -355,14 +362,14 @@ fn (mut g Gen) spawn_and_go_expr(node ast.SpawnExpr, mode SpawnGoMode) {
355362
}
356363
g.gowrappers.writeln(');')
357364
$if tinyc && arm64 {
358-
if g.pref.os != .windows && call_ret_type != ast.void_type {
365+
if g.pref.os != .windows && wrapper_return_type != ast.void_type {
359366
g.gowrappers.writeln('\t*ret_ptr = tcc_bug_tmp_var;')
360367
}
361368
}
362369
if is_spawn {
363370
g.gowrappers.writeln('\tbuiltin___v_free(arg);')
364371
}
365-
if g.pref.os != .windows && call_ret_type != ast.void_type {
372+
if g.pref.os != .windows && wrapper_return_type != ast.void_type {
366373
g.gowrappers.writeln('\treturn ret_ptr;')
367374
} else {
368375
g.gowrappers.writeln('\treturn 0;')

vlib/v/gen/c/struct.v

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -159,7 +159,12 @@ fn (mut g Gen) struct_init(node ast.StructInit) {
159159
} else if is_multiline {
160160
g.writeln('(${styp}){')
161161
} else if is_generic_default {
162-
g.write(g.type_default(node.typ))
162+
default_val := g.type_default(node.typ)
163+
if default_val == '{0}' {
164+
g.write('(${styp}){0}')
165+
} else {
166+
g.write(default_val)
167+
}
163168
} else {
164169
g.write('(${styp}){')
165170
}

0 commit comments

Comments
 (0)