diff --git a/vlib/math/stats/stats.v b/vlib/math/stats/stats.v index 98d970d2eb5250..402a6b58d3e860 100644 --- a/vlib/math/stats/stats.v +++ b/vlib/math/stats/stats.v @@ -50,7 +50,7 @@ pub fn geometric_mean[T](data []T) T { return math.pow(sum, f64(1.0) / data.len) } $else { // use f32 for f32/int/... - return T(math.powf(sum, f32(1.0) / data.len)) + return T(math.powf(f32(sum), f32(1.0) / data.len)) } } @@ -131,7 +131,7 @@ pub fn rms[T](data []T) T { // use f32 for f32/int/... mut sum := f32(0) for v in data { - sum += math.powf(v, 2) + sum += math.powf(f32(v), 2) } return T(math.sqrtf(sum / data.len)) } @@ -203,7 +203,7 @@ pub fn population_stddev[T](data []T) T { $if T is f64 { return math.sqrt(population_variance[T](data)) } $else { - return T(math.sqrtf(population_variance[T](data))) + return T(math.sqrtf(f32(population_variance[T](data)))) } } @@ -218,7 +218,7 @@ pub fn population_stddev_mean[T](data []T, mean T) T { $if T is f64 { return math.sqrt(population_variance_mean[T](data, mean)) } $else { - return T(math.sqrtf(population_variance_mean[T](data, mean))) + return T(math.sqrtf(f32(population_variance_mean[T](data, mean)))) } } @@ -234,7 +234,7 @@ pub fn sample_stddev[T](data []T) T { $if T is f64 { return math.sqrt(sample_variance[T](data)) } $else { - return T(math.sqrtf(sample_variance[T](data))) + return T(math.sqrtf(f32(sample_variance[T](data)))) } } diff --git a/vlib/v/ast/table.v b/vlib/v/ast/table.v index c3b7a25e5ba239..0c6ef498a66ee0 100644 --- a/vlib/v/ast/table.v +++ b/vlib/v/ast/table.v @@ -2181,6 +2181,28 @@ pub fn (mut t Table) unwrap_generic_type_ex(typ Type, generic_names []string, co idx := t.find_or_register_map(unwrap_key_type, unwrap_value_type) return new_type(idx).derive_add_muls(typ).clear_flag(.generic) } + FnType { + mut unwrapped_fn := ts.info.func + unwrapped_fn.params = unwrapped_fn.params.clone() + mut has_generic := false + for i, param in unwrapped_fn.params { + if param.typ.has_flag(.generic) { + unwrapped_fn.params[i].typ = t.unwrap_generic_type_ex(param.typ, generic_names, + concrete_types, recheck_concrete_types) + has_generic = true + } + } + if unwrapped_fn.return_type.has_flag(.generic) { + unwrapped_fn.return_type = t.unwrap_generic_type_ex(unwrapped_fn.return_type, + generic_names, concrete_types, recheck_concrete_types) + has_generic = true + } + if has_generic { + idx := t.find_or_register_fn_type(unwrapped_fn, true, false) + return new_type(idx).derive_add_muls(typ).clear_flag(.generic) + } + return typ + } Struct, Interface, SumType { if !ts.info.is_generic { return typ @@ -2423,7 +2445,13 @@ pub fn (mut t Table) unwrap_generic_type_ex(typ Type, generic_names []string, co } return new_type(new_idx).derive(typ).clear_flag(.generic) } - else {} + else { + if typ.has_flag(.generic) { + if converted := t.convert_generic_type(typ, generic_names, concrete_types) { + return converted + } + } + } } return typ } diff --git a/vlib/v/checker/containers.v b/vlib/v/checker/containers.v index 2ac098543b091d..b58ecde20134f5 100644 --- a/vlib/v/checker/containers.v +++ b/vlib/v/checker/containers.v @@ -314,21 +314,36 @@ fn (mut c Checker) array_init(mut node ast.ArrayInit) ast.Type { fn (mut c Checker) check_array_init_default_expr(mut node ast.ArrayInit) { mut init_expr := node.init_expr - c.expected_type = node.elem_type + mut expected_elem_type := node.elem_type + if node.elem_type.has_flag(.generic) && c.table.cur_fn != unsafe { nil } { + if c.table.cur_fn.generic_names.len > 0 + && c.table.cur_concrete_types.len == c.table.cur_fn.generic_names.len { + expr := node.init_expr + if expr is ast.CallExpr { + if func := c.table.find_fn(expr.name) { + if !func.return_type.has_flag(.generic) { + expected_elem_type = c.table.unwrap_generic_type(node.elem_type, + c.table.cur_fn.generic_names, c.table.cur_concrete_types) + } + } + } + } + } + c.expected_type = expected_elem_type init_typ := c.check_expr_option_or_result_call(init_expr, c.expr(mut init_expr)) node.init_type = init_typ - if !node.elem_type.has_flag(.option) && init_typ.has_flag(.option) { + if !expected_elem_type.has_flag(.option) && init_typ.has_flag(.option) { c.error('cannot use unwrapped Option as initializer', init_expr.pos()) } - if node.elem_type.is_number() && init_typ.is_number() { + if expected_elem_type.is_number() && init_typ.is_number() { return } - if c.table.type_kind(node.elem_type) == .interface { - if c.type_implements(init_typ, node.elem_type, init_expr.pos()) { + if c.table.type_kind(expected_elem_type) == .interface { + if c.type_implements(init_typ, expected_elem_type, init_expr.pos()) { return } } - c.check_expected(init_typ, node.elem_type) or { c.error(err.msg(), init_expr.pos()) } + c.check_expected(init_typ, expected_elem_type) or { c.error(err.msg(), init_expr.pos()) } } fn (mut c Checker) check_array_init_para_type(para string, mut expr ast.Expr, pos token.Pos) { diff --git a/vlib/v/checker/fn.v b/vlib/v/checker/fn.v index 3d25cdd2b22235..234619944cf404 100644 --- a/vlib/v/checker/fn.v +++ b/vlib/v/checker/fn.v @@ -1553,11 +1553,19 @@ fn (mut c Checker) fn_call(mut node ast.CallExpr, mut continue_check &bool) ast. c.error('cannot have parameter after array decompose', node.pos) } param_i := i + nr_multi_values - param := if func.is_variadic && i >= func.params.len - 1 { + mut param := if func.is_variadic && i >= func.params.len - 1 { func.params.last() } else { func.params[param_i] } + if node.is_fn_var && param.typ.has_flag(.generic) && c.table.cur_fn != unsafe { nil } + && c.table.cur_fn.generic_names.len > 0 + && c.table.cur_fn.generic_names.len == c.table.cur_concrete_types.len { + mut unwrapped := param + unwrapped.typ = c.table.unwrap_generic_type(param.typ, c.table.cur_fn.generic_names, + c.table.cur_concrete_types) + param = unwrapped + } // registers if the arg must be passed by ref to disable auto deref args call_arg.should_be_ptr = param.typ.is_ptr() && !param.is_mut if func.is_variadic && call_arg.expr is ast.ArrayDecompose { diff --git a/vlib/v/checker/tests/generics_fn_param_wrong_arg_type_err.out b/vlib/v/checker/tests/generics_fn_param_wrong_arg_type_err.out new file mode 100644 index 00000000000000..873ebbf230308b --- /dev/null +++ b/vlib/v/checker/tests/generics_fn_param_wrong_arg_type_err.out @@ -0,0 +1,6 @@ +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` + 1 | fn test[T, U](f fn (T) U) U { + 2 | return f(1) + | ^ + 3 | } + 4 | diff --git a/vlib/v/checker/tests/generics_fn_param_wrong_arg_type_err.vv b/vlib/v/checker/tests/generics_fn_param_wrong_arg_type_err.vv new file mode 100644 index 00000000000000..e0481fe92c5125 --- /dev/null +++ b/vlib/v/checker/tests/generics_fn_param_wrong_arg_type_err.vv @@ -0,0 +1,9 @@ +fn test[T, U](f fn (T) U) U { + return f(1) +} + +fn main() { + println(test(fn (x string) int { + return x.len + })) +} diff --git a/vlib/v/gen/c/array.v b/vlib/v/gen/c/array.v index e6aa4676e83031..b2d0f74e639546 100644 --- a/vlib/v/gen/c/array.v +++ b/vlib/v/gen/c/array.v @@ -1491,7 +1491,8 @@ fn (mut g Gen) gen_array_wait(node ast.CallExpr) { thread_type := arr.array_info().elem_type thread_sym := g.table.sym(thread_type) thread_ret_type := thread_sym.thread_info().return_type - elsymcname := g.table.sym(thread_ret_type).cname + unwrapped_ret_type := g.unwrap_generic(thread_ret_type) + elsymcname := g.table.sym(unwrapped_ret_type).cname fn_name := g.register_thread_array_wait_call(elsymcname) g.write('${fn_name}(') if node.left_type.is_ptr() { @@ -1506,7 +1507,8 @@ fn (mut g Gen) gen_fixed_array_wait(node ast.CallExpr) { thread_type := arr.array_fixed_info().elem_type thread_sym := g.table.sym(thread_type) thread_ret_type := thread_sym.thread_info().return_type - elsymcname := g.table.sym(thread_ret_type).cname + unwrapped_ret_type := g.unwrap_generic(thread_ret_type) + elsymcname := g.table.sym(unwrapped_ret_type).cname fn_name := g.register_thread_fixed_array_wait_call(node, elsymcname) g.write('${fn_name}(') g.expr(node.left) diff --git a/vlib/v/gen/c/cgen.v b/vlib/v/gen/c/cgen.v index 2aec702999f220..e50b0316699429 100644 --- a/vlib/v/gen/c/cgen.v +++ b/vlib/v/gen/c/cgen.v @@ -1542,13 +1542,40 @@ fn (mut g Gen) register_thread_void_wait_call() { g.gowrappers.writeln('void __v_thread_wait(__v_thread thread) {') if g.pref.os == .windows { g.gowrappers.writeln('\tu32 stat = WaitForSingleObject(thread, INFINITE);') + g.gowrappers.writeln('\tif (stat != WAIT_OBJECT_0) { builtin___v_panic(_S("error waiting thread")); }') } else { g.gowrappers.writeln('\tint stat = pthread_join(thread, (void **)NULL);') + g.gowrappers.writeln('\tif (stat != 0) { builtin___v_panic(_S("error waiting thread")); }') } - g.gowrappers.writeln('\tif (stat != 0) { builtin___v_panic(_S("unable to join thread")); }') + g.gowrappers.writeln('}') +} + +fn (mut g Gen) register_thread_wait_call(eltyp string) { + thread_typ := '__v_thread_${eltyp}' + fn_name := '${thread_typ}_wait' + lock g.waiter_fns { + if fn_name in g.waiter_fns { + return + } + g.waiter_fns << fn_name + g.waiter_fn_definitions.writeln('${eltyp} ${fn_name}(${thread_typ} thread);') + } + g.gowrappers.writeln('${eltyp} ${fn_name}(${thread_typ} thread) {') + g.gowrappers.writeln('\t${eltyp} res;') if g.pref.os == .windows { - g.gowrappers.writeln('\tCloseHandle(thread);') + g.gowrappers.writeln('\tu32 stat = WaitForSingleObject(thread.handle, INFINITE);') + g.gowrappers.writeln('\tif (stat != WAIT_OBJECT_0) { builtin___v_panic(_S("error waiting thread")); }') + g.gowrappers.writeln('\tCloseHandle(thread.handle);') + g.gowrappers.writeln('\tres = *(${eltyp}*)(thread.ret_ptr);') + g.gowrappers.writeln('\tbuiltin___v_free(thread.ret_ptr);') + } else { + g.gowrappers.writeln('\tvoid* ret_val;') + g.gowrappers.writeln('\tint stat = pthread_join(thread, &ret_val);') + g.gowrappers.writeln('\tif (stat != 0) { builtin___v_panic(_S("error waiting thread")); }') + g.gowrappers.writeln('\tres = *(${eltyp}*)ret_val;') + g.gowrappers.writeln('\tbuiltin___v_free(ret_val);') } + g.gowrappers.writeln('\treturn res;') g.gowrappers.writeln('}') } @@ -1578,6 +1605,7 @@ void ${fn_name}(${thread_arr_typ} a) { } }') } else { + g.register_thread_wait_call(eltyp) g.waiter_fn_definitions.writeln('${ret_typ} ${fn_name}(${thread_arr_typ} a);') g.gowrappers.writeln(' ${ret_typ} ${fn_name}(${thread_arr_typ} a) { @@ -1620,12 +1648,13 @@ fn (mut g Gen) register_thread_fixed_array_wait_call(node ast.CallExpr, eltyp st g.gowrappers.writeln(' void ${fn_name}(${thread_arr_typ} a) { for (${ast.int_type_name} i = 0; i < ${len}; ++i) { - ${thread_typ} t = ((${thread_typ}*)a)[i]; + ${thread_typ} t = a[i]; if (t == 0) continue; __v_thread_wait(t); } }') } else { + g.register_thread_wait_call(eltyp) g.waiter_fn_definitions.writeln('${ret_typ} ${fn_name}(${thread_arr_typ} a);') g.gowrappers.writeln(' ${ret_typ} ${fn_name}(${thread_arr_typ} a) { diff --git a/vlib/v/gen/c/spawn_and_go.v b/vlib/v/gen/c/spawn_and_go.v index c55f68559a832f..bbafb2178f4b1b 100644 --- a/vlib/v/gen/c/spawn_and_go.v +++ b/vlib/v/gen/c/spawn_and_go.v @@ -119,8 +119,8 @@ fn (mut g Gen) spawn_and_go_expr(node ast.SpawnExpr, mode SpawnGoMode) { g.expr(arg.expr) g.writeln(';') } - call_ret_type := node.call_expr.return_type - s_ret_typ := g.styp(call_ret_type) + call_ret_type := g.unwrap_generic(node.call_expr.return_type) + s_ret_typ := g.styp(g.unwrap_generic(call_ret_type)) if g.pref.os == .windows && call_ret_type != ast.void_type { g.writeln('${arg_tmp_var}->ret_ptr = (void *) builtin___v_malloc(sizeof(${s_ret_typ}));') } @@ -182,14 +182,17 @@ fn (mut g Gen) spawn_and_go_expr(node ast.SpawnExpr, mode SpawnGoMode) { if should_register { g.type_definitions.writeln('\ntypedef struct ${wrapper_struct_name} {') mut fn_var := '' + mut wrapper_return_type := call_ret_type if node.call_expr.is_fn_var { fn_sym := g.table.sym(node.call_expr.fn_var_type) info := fn_sym.info as ast.FnType - fn_var = g.fn_var_signature(ast.void_type, info.func.return_type, info.func.params.map(it.typ), + wrapper_return_type = info.func.return_type + fn_var = g.fn_var_signature(ast.void_type, wrapper_return_type, info.func.params.map(it.typ), 'fn') } else if node.call_expr.left is ast.AnonFn { f := node.call_expr.left.decl - fn_var = g.fn_var_signature(ast.void_type, f.return_type, f.params.map(it.typ), + wrapper_return_type = f.return_type + fn_var = g.fn_var_signature(ast.void_type, wrapper_return_type, f.params.map(it.typ), 'fn') } else { if node.call_expr.is_method { @@ -198,6 +201,7 @@ fn (mut g Gen) spawn_and_go_expr(node ast.SpawnExpr, mode SpawnGoMode) { mut muttable := unsafe { &ast.Table(g.table) } return_type := muttable.convert_generic_type(f.return_type, f.generic_names, node.call_expr.concrete_types) or { f.return_type } + wrapper_return_type = return_type mut arg_types := f.params.map(it.typ) arg_types = arg_types.map(muttable.convert_generic_type(it, f.generic_names, node.call_expr.concrete_types) or { it }) @@ -209,6 +213,7 @@ fn (mut g Gen) spawn_and_go_expr(node ast.SpawnExpr, mode SpawnGoMode) { concrete_types := node.call_expr.concrete_types.map(g.unwrap_generic(it)) return_type := g.table.convert_generic_type(f.return_type, f.generic_names, concrete_types) or { f.return_type } + wrapper_return_type = return_type mut arg_types := f.params.map(it.typ) arg_types = arg_types.map(g.table.convert_generic_type(it, f.generic_names, concrete_types) or { it }) @@ -230,6 +235,8 @@ fn (mut g Gen) spawn_and_go_expr(node ast.SpawnExpr, mode SpawnGoMode) { } } } + wrapper_return_type = g.unwrap_generic(wrapper_return_type) + wrapper_s_ret_typ := g.styp(wrapper_return_type) if fn_var != '' { g.type_definitions.writeln('\t${fn_var};') } @@ -237,7 +244,7 @@ fn (mut g Gen) spawn_and_go_expr(node ast.SpawnExpr, mode SpawnGoMode) { styp := g.styp(expr.receiver_type) g.type_definitions.writeln('\t${styp} arg0;') } - need_return_ptr := g.pref.os == .windows && call_ret_type != ast.void_type + need_return_ptr := g.pref.os == .windows && wrapper_return_type != ast.void_type for i, arg in expr.args { arg_sym := g.table.sym(arg.typ) if arg_sym.info is ast.FnType { @@ -256,13 +263,13 @@ fn (mut g Gen) spawn_and_go_expr(node ast.SpawnExpr, mode SpawnGoMode) { thread_ret_type := if g.pref.os == .windows { 'u32' } else { 'void*' } g.waiter_fn_definitions.writeln('${g.static_non_parallel}${thread_ret_type} ${wrapper_fn_name}(${wrapper_struct_name} *arg);') g.gowrappers.writeln('${thread_ret_type} ${wrapper_fn_name}(${wrapper_struct_name} *arg) {') - if call_ret_type != ast.void_type { + if wrapper_return_type != ast.void_type { if g.pref.os == .windows { - g.gowrappers.write_string('\t*((${s_ret_typ}*)(arg->ret_ptr)) = ') + g.gowrappers.write_string('\t*((${wrapper_s_ret_typ}*)(arg->ret_ptr)) = ') } else { - g.gowrappers.writeln('\t${s_ret_typ}* ret_ptr = (${s_ret_typ}*) builtin___v_malloc(sizeof(${s_ret_typ}));') + g.gowrappers.writeln('\t${wrapper_s_ret_typ}* ret_ptr = (${wrapper_s_ret_typ}*) builtin___v_malloc(sizeof(${wrapper_s_ret_typ}));') $if tinyc && arm64 { - g.gowrappers.write_string('\t${s_ret_typ} tcc_bug_tmp_var = ') + g.gowrappers.write_string('\t${wrapper_s_ret_typ} tcc_bug_tmp_var = ') } $else { g.gowrappers.write_string('\t*ret_ptr = ') } @@ -355,14 +362,14 @@ fn (mut g Gen) spawn_and_go_expr(node ast.SpawnExpr, mode SpawnGoMode) { } g.gowrappers.writeln(');') $if tinyc && arm64 { - if g.pref.os != .windows && call_ret_type != ast.void_type { + if g.pref.os != .windows && wrapper_return_type != ast.void_type { g.gowrappers.writeln('\t*ret_ptr = tcc_bug_tmp_var;') } } if is_spawn { g.gowrappers.writeln('\tbuiltin___v_free(arg);') } - if g.pref.os != .windows && call_ret_type != ast.void_type { + if g.pref.os != .windows && wrapper_return_type != ast.void_type { g.gowrappers.writeln('\treturn ret_ptr;') } else { g.gowrappers.writeln('\treturn 0;') diff --git a/vlib/v/gen/c/struct.v b/vlib/v/gen/c/struct.v index 1b4c9bb7e570c5..6515bedcf46601 100644 --- a/vlib/v/gen/c/struct.v +++ b/vlib/v/gen/c/struct.v @@ -159,7 +159,12 @@ fn (mut g Gen) struct_init(node ast.StructInit) { } else if is_multiline { g.writeln('(${styp}){') } else if is_generic_default { - g.write(g.type_default(node.typ)) + default_val := g.type_default(node.typ) + if default_val == '{0}' { + g.write('(${styp}){0}') + } else { + g.write(default_val) + } } else { g.write('(${styp}){') } diff --git a/vlib/v/tests/comptime/comptime_infix_assign_test.v b/vlib/v/tests/comptime/comptime_infix_assign_test.v index eb8c08df435d96..b763b7fc44b4e5 100644 --- a/vlib/v/tests/comptime/comptime_infix_assign_test.v +++ b/vlib/v/tests/comptime/comptime_infix_assign_test.v @@ -17,7 +17,7 @@ fn enc[T](item T) string { 'i64', 'u64' { 16 } else { return '' } } - return u64_to_hex(item, len) + return u64_to_hex(u64(item), len) } $else $if T is $array { mut hex := '' for val in item {