Skip to content
10 changes: 5 additions & 5 deletions vlib/math/stats/stats.v
Original file line number Diff line number Diff line change
Expand Up @@ -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))
}
}

Expand Down Expand Up @@ -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))
}
Expand Down Expand Up @@ -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))))
}
}

Expand All @@ -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))))
}
}

Expand All @@ -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))))
}
}

Expand Down
30 changes: 29 additions & 1 deletion vlib/v/ast/table.v
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
}
Expand Down
27 changes: 21 additions & 6 deletions vlib/v/checker/containers.v
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
10 changes: 9 additions & 1 deletion vlib/v/checker/fn.v
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
6 changes: 6 additions & 0 deletions vlib/v/checker/tests/generics_fn_param_wrong_arg_type_err.out
Original file line number Diff line number Diff line change
@@ -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 |
9 changes: 9 additions & 0 deletions vlib/v/checker/tests/generics_fn_param_wrong_arg_type_err.vv
Original file line number Diff line number Diff line change
@@ -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
}))
}
6 changes: 4 additions & 2 deletions vlib/v/gen/c/array.v
Original file line number Diff line number Diff line change
Expand Up @@ -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() {
Expand All @@ -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)
Expand Down
35 changes: 32 additions & 3 deletions vlib/v/gen/c/cgen.v
Original file line number Diff line number Diff line change
Expand Up @@ -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('}')
}

Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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) {
Expand Down
29 changes: 18 additions & 11 deletions vlib/v/gen/c/spawn_and_go.v
Original file line number Diff line number Diff line change
Expand Up @@ -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}));')
}
Expand Down Expand Up @@ -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 {
Expand All @@ -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 })
Expand All @@ -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 })
Expand All @@ -230,14 +235,16 @@ 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};')
}
if expr.is_method {
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 {
Expand All @@ -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 = ')
}
Expand Down Expand Up @@ -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;')
Expand Down
7 changes: 6 additions & 1 deletion vlib/v/gen/c/struct.v
Original file line number Diff line number Diff line change
Expand Up @@ -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}){')
}
Expand Down
2 changes: 1 addition & 1 deletion vlib/v/tests/comptime/comptime_infix_assign_test.v
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
Loading