From e5d88def78067733949f3427ec5038907bc13da7 Mon Sep 17 00:00:00 2001 From: Alexander Medvednikov Date: Sun, 5 Apr 2026 03:29:02 +0300 Subject: [PATCH] all: fix failing tests --- vlib/arrays/arrays.v | 19 ++++---- vlib/arrays/arrays_fold_test.v | 20 ++++++++ vlib/crypto/scrypt/scrypt.v | 15 +++--- vlib/crypto/sha256/sha256block_generic.v | 3 +- vlib/db/mssql/mssql_test.v | 8 ++- vlib/v/ast/table.v | 17 +++---- vlib/v/checker/check_types.v | 10 ++-- vlib/v/checker/checker.v | 47 +++++++++++++----- vlib/v/checker/fn.v | 15 ++++-- vlib/v/checker/interface.v | 9 +++- vlib/v/gen/c/cgen.v | 62 +++++++++++++++++++----- vlib/v/gen/native/blacklist.v | 2 + vlib/v/type_resolver/generic_resolver.v | 9 ++-- 13 files changed, 173 insertions(+), 63 deletions(-) diff --git a/vlib/arrays/arrays.v b/vlib/arrays/arrays.v index 71f73e008..48283fdfb 100644 --- a/vlib/arrays/arrays.v +++ b/vlib/arrays/arrays.v @@ -322,18 +322,19 @@ pub fn filter_indexed[T](array []T, predicate fn (idx int, elem T) bool) []T { // assert r == 5 // ``` pub fn fold[T, R](array []T, init R, fold_op fn (acc R, elem T) R) R { - mut value := R{} $if R is $array { - value = init.clone() + mut value := init.clone() + for e in array { + value = fold_op(value, e) + } + return value } $else { - value = init - } - - for e in array { - value = fold_op(value, e) + mut value := init + for e in array { + value = fold_op(value, e) + } + return value } - - return value } // fold_indexed sets `acc = init`, then successively calls `acc = fold_op(idx, acc, elem)` for each element in `array`. diff --git a/vlib/arrays/arrays_fold_test.v b/vlib/arrays/arrays_fold_test.v index c854587d4..3d8dd8aee 100644 --- a/vlib/arrays/arrays_fold_test.v +++ b/vlib/arrays/arrays_fold_test.v @@ -2,6 +2,18 @@ import arrays type MyInt = int +interface FoldCyclable { + id() int +} + +struct FoldWheel { + v int +} + +fn (w FoldWheel) id() int { + return w.v +} + fn test_main() { assert arrays.fold[int, []int]([1, 2, 3, 4], []int{}, fn (r []int, t int) []int { return arrays.merge(r, [t]) @@ -19,3 +31,11 @@ fn test_main() { return r.map(it * t) }) == [120, 144, 168] } + +fn test_fold_with_interface_accumulator() { + items := [FoldWheel{1}, FoldWheel{2}] + result := arrays.fold[FoldWheel, FoldCyclable](items, FoldCyclable(FoldWheel{}), fn (_ FoldCyclable, elem FoldWheel) FoldCyclable { + return elem + }) + assert result.id() == 2 +} diff --git a/vlib/crypto/scrypt/scrypt.v b/vlib/crypto/scrypt/scrypt.v index ad0c6e3b8..0f621f26a 100644 --- a/vlib/crypto/scrypt/scrypt.v +++ b/vlib/crypto/scrypt/scrypt.v @@ -17,7 +17,8 @@ pub const max_blocksize_parallal_product = u64(1 << 30) // salsa20_8 applies the salsa20/8 core transformation to a block // of 64 u8 bytes. The block is modified in place. fn salsa20_8(mut block []u8) { - mut block_words := []u32{len: 16} + // Keep the temporary state on the stack. + mut block_words := [16]u32{} mut scratch := [16]u32{} for i in 0 .. 16 { @@ -95,18 +96,20 @@ fn blkxor(mut dest []u8, src []u8, len u32) { // has to be the same size, 128 * r. r is a positive integer // value > 0. The block is modified in place. fn block_mix(mut block []u8, mut temp []u8, r u32) { - mut scratch := []u8{len: 64, cap: 64} + mut scratch := [64]u8{} + // Reuse the fixed buffer directly instead of cloning it into a heap-backed slice. + mut scratch_buf := unsafe { scratch[..] } - blkcpy(mut scratch, block[(((2 * r) - 1) * 64)..], 64) + blkcpy(mut scratch_buf, block[(((2 * r) - 1) * 64)..], 64) for i in 0 .. 2 * r { start := i * 64 stop := start + 64 - blkxor(mut scratch, block[start..stop], 64) - salsa20_8(mut scratch) + blkxor(mut scratch_buf, block[start..stop], 64) + salsa20_8(mut scratch_buf) - blkcpy(mut temp[start..stop], scratch, 64) + blkcpy(mut temp[start..stop], scratch_buf, 64) } for i in 0 .. r { diff --git a/vlib/crypto/sha256/sha256block_generic.v b/vlib/crypto/sha256/sha256block_generic.v index 13c855361..0c5346158 100644 --- a/vlib/crypto/sha256/sha256block_generic.v +++ b/vlib/crypto/sha256/sha256block_generic.v @@ -80,7 +80,8 @@ const _k = [ fn block_generic(mut dig Digest, p_ []u8) { unsafe { mut p := p_ - mut w := []u32{len: (64)} + // Keep the message schedule on the stack. + mut w := [64]u32{} mut h0 := dig.h[0] mut h1 := dig.h[1] mut h2 := dig.h[2] diff --git a/vlib/db/mssql/mssql_test.v b/vlib/db/mssql/mssql_test.v index ecfe015b2..4ace13bf0 100644 --- a/vlib/db/mssql/mssql_test.v +++ b/vlib/db/mssql/mssql_test.v @@ -71,7 +71,13 @@ fn connect_with_retry() !mssql.Connection { for _ in 0 .. 30 { mut conn := mssql.Connection{} if _ := conn.connect(conn_str) { - return conn + // SQL Server can accept a connection before it is ready to execute statements. + if _ := conn.query('select 1') { + return conn + } else { + last_err = err.msg() + conn.close() + } } else { last_err = err.msg() } diff --git a/vlib/v/ast/table.v b/vlib/v/ast/table.v index f02ebd80a..b985b5db9 100644 --- a/vlib/v/ast/table.v +++ b/vlib/v/ast/table.v @@ -3231,12 +3231,9 @@ fn (mut t Table) unwrap_generic_type_ex_with_depth(typ Type, generic_names []str } } if fields[i].has_default_expr { - if fields[i].default_expr_typ.has_flag(.generic) { - if t_typ := t.convert_generic_type(fields[i].default_expr_typ, + if fields[i].default_expr_typ != 0 && fields[i].default_expr_typ != nil_type { + fields[i].default_expr_typ = t.convert_generic_expr_type(fields[i].default_expr_typ, t_generic_names, t_concrete_types) - { - fields[i].default_expr_typ = t_typ - } fields[i].default_expr = t.convert_generic_default_expr(fields[i].default_expr, t_generic_names, t_concrete_types) } else if fields[i].default_expr_typ == 0 @@ -3456,10 +3453,12 @@ pub fn (mut t Table) generic_insts_to_concrete() { } } } - if fields[i].has_default_expr - && fields[i].default_expr_typ.has_flag(.generic) { - fields[i].default_expr_typ = t.convert_generic_expr_type(fields[i].default_expr_typ, - generic_names, info.concrete_types) + if fields[i].has_default_expr { + if fields[i].default_expr_typ != 0 + && fields[i].default_expr_typ != nil_type { + fields[i].default_expr_typ = t.convert_generic_expr_type(fields[i].default_expr_typ, + generic_names, info.concrete_types) + } fields[i].default_expr = t.convert_generic_default_expr(fields[i].default_expr, generic_names, info.concrete_types) } diff --git a/vlib/v/checker/check_types.v b/vlib/v/checker/check_types.v index ce417379d..d4b99f837 100644 --- a/vlib/v/checker/check_types.v +++ b/vlib/v/checker/check_types.v @@ -1256,14 +1256,17 @@ fn (mut c Checker) infer_fn_generic_types(func &ast.Fn, mut node ast.CallExpr) { } } } - if arg.expr.is_auto_deref_var() && typ.is_ptr() { + if arg.expr.is_auto_deref_var() && typ.is_ptr() && !arg.is_mut { typ = typ.deref() } // resolve &T &&T ... // Use param.typ (not param_infer_typ) to get the actual pointer // count including mut lowering, so that e.g. `mut val T` with // param.typ=&T correctly strips the pointer from the arg type. - if param.typ.nr_muls() > 0 && typ.nr_muls() > 0 { + // Explicit `mut arg` calls should preserve the source-level + // reference type of the argument, even when the current scope + // variable is auto-dereferenced. + if param.typ.nr_muls() > 0 && typ.nr_muls() > 0 && !arg.is_mut { param_muls := param.typ.nr_muls() arg_muls := typ.nr_muls() typ = if arg_muls >= param_muls { @@ -1272,7 +1275,8 @@ fn (mut c Checker) infer_fn_generic_types(func &ast.Fn, mut node ast.CallExpr) { typ.set_nr_muls(0) } } - } else if param_infer_typ.has_flag(.generic) { + } else if param_infer_typ.has_flag(.generic) + || c.type_has_unresolved_generic_parts(param_infer_typ) { arg_typ := if c.table.sym(arg.typ).kind == .any { c.unwrap_generic(arg.typ) } else { diff --git a/vlib/v/checker/checker.v b/vlib/v/checker/checker.v index 923af40f4..11fa5b836 100644 --- a/vlib/v/checker/checker.v +++ b/vlib/v/checker/checker.v @@ -1708,16 +1708,17 @@ fn (mut c Checker) fail_if_immutable(mut expr ast.Expr) (string, token.Pos) { } fn (mut c Checker) type_implements(typ ast.Type, interface_type ast.Type, pos token.Pos) bool { - if typ == interface_type { + mut resolved_interface_type := c.unwrap_generic(interface_type) + if typ == resolved_interface_type { return true } $if debug_interface_type_implements ? { - eprintln('> type_implements typ: ${typ.debug()} (`${c.table.type_to_str(typ)}`) | inter_typ: ${interface_type.debug()} (`${c.table.type_to_str(interface_type)}`)') + eprintln('> type_implements typ: ${typ.debug()} (`${c.table.type_to_str(typ)}`) | inter_typ: ${resolved_interface_type.debug()} (`${c.table.type_to_str(resolved_interface_type)}`)') } utyp := c.unwrap_generic(typ) styp := c.table.type_to_str(utyp) typ_sym := c.table.sym(utyp) - mut inter_sym := c.table.final_sym(interface_type) + mut inter_sym := c.table.final_sym(resolved_interface_type) if !inter_sym.is_pub && inter_sym.mod !in [typ_sym.mod, c.mod] && typ_sym.mod != 'builtin' { c.error('`${styp}` cannot implement private interface `${inter_sym.name}` of other module', pos) @@ -1740,7 +1741,7 @@ fn (mut c Checker) type_implements(typ ast.Type, interface_type ast.Type, pos to return false } if mut inter_sym.info is ast.Interface { - mut generic_type := interface_type + mut generic_type := resolved_interface_type mut generic_info := inter_sym.info if inter_sym.info.parent_type.has_flag(.generic) && (inter_sym.info.concrete_types.len == 0 || inter_sym.info.concrete_types.any(it.has_flag(.generic))) { @@ -1750,7 +1751,7 @@ fn (mut c Checker) type_implements(typ ast.Type, interface_type ast.Type, pos to generic_info = parent_sym.info } } - mut inferred_type := interface_type + mut inferred_type := resolved_interface_type is_fully_concrete := inter_sym.info.concrete_types.len > 0 && !inter_sym.info.concrete_types.any(it.has_flag(.generic)) if generic_info.is_generic && !is_fully_concrete { @@ -1760,7 +1761,7 @@ fn (mut c Checker) type_implements(typ ast.Type, interface_type ast.Type, pos to } } if inter_sym.info.is_generic && !is_fully_concrete { - if inferred_type == interface_type { + if inferred_type == resolved_interface_type { // terminate early, since otherwise we get an infinite recursion/segfault: return false } @@ -1771,7 +1772,7 @@ fn (mut c Checker) type_implements(typ ast.Type, interface_type ast.Type, pos to generic_inst_info := inter_sym.info as ast.GenericInst if !generic_inst_info.concrete_types.any(it.has_flag(.generic)) { c.table.generic_insts_to_concrete() - inter_sym = c.table.final_sym(interface_type) + inter_sym = c.table.final_sym(resolved_interface_type) } } // do not check the same type more than once @@ -1782,22 +1783,22 @@ fn (mut c Checker) type_implements(typ ast.Type, interface_type ast.Type, pos to } } } - if utyp.idx() == interface_type.idx() { + if utyp.idx() == resolved_interface_type.idx() { // same type -> already casted to the interface return true } - if interface_type.idx() == ast.error_type_idx && utyp.idx() == ast.none_type_idx { + if resolved_interface_type.idx() == ast.error_type_idx && utyp.idx() == ast.none_type_idx { // `none` "implements" the Error interface return true } is_interface_upcast := typ_sym.kind == .interface && inter_sym.kind == .interface && !styp.starts_with('JS.') && !inter_sym.name.starts_with('JS.') - if is_interface_upcast && !c.table.interface_inherits_interface(utyp, interface_type) { + if is_interface_upcast && !c.table.interface_inherits_interface(utyp, resolved_interface_type) { c.error('cannot implement interface `${inter_sym.name}` with a different interface `${styp}`', pos) return false } - interface_sym := c.table.sym(interface_type) + interface_sym := c.table.sym(resolved_interface_type) mut interface_generic_names := []string{} mut interface_concrete_types := []ast.Type{} mut interface_info := ast.Interface{} @@ -4540,6 +4541,11 @@ fn (mut c Checker) cast_expr(mut node ast.CastExpr) ast.Type { mut to_sym := c.table.sym(to_type) // type to be used as cast mut final_to_sym := c.table.final_sym(to_type) final_to_type := if mut to_sym.info is ast.Alias { to_sym.info.parent_type } else { to_type } + mut to_is_interface := to_sym.kind == .interface + if !to_is_interface && to_sym.kind == .generic_inst { + gi := to_sym.info as ast.GenericInst + to_is_interface = c.table.type_symbols[gi.parent_idx].kind == .interface + } if final_to_sym == final_from_sym && final_to_type.flags() == from_type.flags() && to_type.flags() == from_type.flags() { @@ -4690,13 +4696,24 @@ fn (mut c Checker) cast_expr(mut node ast.CastExpr) ast.Type { tt := c.table.type_to_str(to_type) c.error('cannot cast `${ft}` to `${tt}`', node.pos) } - } else if !from_type.has_option_or_result() && mut to_sym.info is ast.Interface { + } else if !from_type.has_option_or_result() && to_is_interface { if c.type_implements(from_type, to_type, node.pos) { if !from_type.is_any_kind_of_pointer() && from_sym.kind != .interface && !c.inside_unsafe && !from_type.is_number() { c.mark_as_referenced(mut &node.expr, true) } - if to_sym.info.is_generic { + if mut to_sym.info is ast.Interface { + if !to_sym.info.is_generic { + // already concrete + } else { + inferred_type := c.unwrap_generic_interface(from_type, to_type, node.pos) + if inferred_type != 0 { + to_type = inferred_type + to_sym = c.table.sym(to_type) + final_to_sym = c.table.final_sym(to_type) + } + } + } else if to_type.has_flag(.generic) || c.type_has_unresolved_generic_parts(to_type) { inferred_type := c.unwrap_generic_interface(from_type, to_type, node.pos) if inferred_type != 0 { to_type = inferred_type @@ -4705,6 +4722,10 @@ fn (mut c Checker) cast_expr(mut node ast.CastExpr) ast.Type { } } } else { + if to_type.has_flag(.generic) || c.type_has_unresolved_generic_parts(to_type) { + node.typname = c.table.sym(node.typ).name + return node.typ + } if from_sym.kind == .interface && to_sym.kind == .interface { return to_type } diff --git a/vlib/v/checker/fn.v b/vlib/v/checker/fn.v index e59a68f4c..c7eee4fbe 100644 --- a/vlib/v/checker/fn.v +++ b/vlib/v/checker/fn.v @@ -47,11 +47,18 @@ fn (mut c Checker) refresh_generic_fn_scope_vars(node &ast.FnDecl) { } } for param in node.params { - param_type := c.recheck_concrete_type(param.typ) + param_source_type := if param.is_mut && param.orig_typ != 0 + && (param.orig_typ.has_flag(.generic) + || c.type_has_unresolved_generic_parts(param.orig_typ)) { + param.orig_typ + } else { + param.typ + } + param_type := c.recheck_concrete_type(param_source_type) if mut param_var := c.fn_scope.find_var(param.name) { - if param_var.generic_typ == 0 - && (param.typ.has_flag(.generic) || c.type_has_unresolved_generic_parts(param.typ)) { - param_var.generic_typ = param.typ + if param_var.generic_typ == 0 && (param_source_type.has_flag(.generic) + || c.type_has_unresolved_generic_parts(param_source_type)) { + param_var.generic_typ = param_source_type } param_var.typ = param_type param_var.orig_type = ast.no_type diff --git a/vlib/v/checker/interface.v b/vlib/v/checker/interface.v index ef14b084f..af5dc2876 100644 --- a/vlib/v/checker/interface.v +++ b/vlib/v/checker/interface.v @@ -414,7 +414,14 @@ fn (mut c Checker) unwrap_generic_interface(typ ast.Type, interface_type ast.Typ // add concrete types to method for imethod in inter_sym.info.methods { im_fkey := imethod.fkey() - c.table.register_fn_concrete_types(im_fkey, inferred_types) + if c.table.register_fn_concrete_types(im_fkey, inferred_types) { + c.need_recheck_generic_fns = true + } + if method := typ_sym.find_method_with_generic_parent(imethod.name) { + if c.table.register_fn_concrete_types(method.fkey(), inferred_types) { + c.need_recheck_generic_fns = true + } + } } result_type := c.table.unwrap_generic_type(interface_type, generic_names, inferred_types) diff --git a/vlib/v/gen/c/cgen.v b/vlib/v/gen/c/cgen.v index 1e5ee4375..d600c7215 100644 --- a/vlib/v/gen/c/cgen.v +++ b/vlib/v/gen/c/cgen.v @@ -5268,12 +5268,27 @@ fn (mut g Gen) selector_expr(node ast.SelectorExpr) { } else if node.field_name in ['idx', 'unaliased_typ'] { // `T.idx`, `T.unaliased_typ`, `typeof(expr).idx`, `typeof(expr).unalised_typ` mut name_type := node.name_type + mut resolved_via_generic := false if node.expr is ast.TypeOf { - name_type = g.type_resolver.typeof_field_type(g.type_resolver.typeof_type(node.expr.expr, - g.resolve_typeof_expr_type(node.expr.expr, name_type)), node.field_name) + if g.cur_fn != unsafe { nil } && g.cur_concrete_types.len > 0 { + resolved := g.resolve_typeof_in_generic(node.expr) + if resolved != 0 { + name_type = g.type_resolver.typeof_field_type(resolved, + node.field_name) + resolved_via_generic = true + } else { + name_type = g.type_resolver.typeof_field_type(g.type_resolver.typeof_type(node.expr.expr, + g.resolve_typeof_expr_type(node.expr.expr, name_type)), + node.field_name) + } + } else { + name_type = g.type_resolver.typeof_field_type(g.type_resolver.typeof_type(node.expr.expr, + g.resolve_typeof_expr_type(node.expr.expr, name_type)), + node.field_name) + } // For mut params (auto_deref), strip pointer so that // typeof(mut_param).idx == typeof(val_param).idx - if node.expr.expr is ast.Ident { + if !resolved_via_generic && node.expr.expr is ast.Ident { if node.expr.expr.obj is ast.Var { if node.expr.expr.obj.is_auto_deref && name_type.is_ptr() { name_type = name_type.deref() @@ -5295,8 +5310,18 @@ fn (mut g Gen) selector_expr(node ast.SelectorExpr) { } else if node.field_name == 'indirections' { mut name_type := node.name_type if node.expr is ast.TypeOf { - name_type = g.type_resolver.typeof_type(node.expr.expr, g.resolve_typeof_expr_type(node.expr.expr, - name_type)) + if g.cur_fn != unsafe { nil } && g.cur_concrete_types.len > 0 { + resolved := g.resolve_typeof_in_generic(node.expr) + if resolved != 0 { + name_type = resolved + } else { + name_type = g.type_resolver.typeof_type(node.expr.expr, + g.resolve_typeof_expr_type(node.expr.expr, name_type)) + } + } else { + name_type = g.type_resolver.typeof_type(node.expr.expr, g.resolve_typeof_expr_type(node.expr.expr, + name_type)) + } } // `typeof(expr).indirections` g.write(int(g.unwrap_generic(name_type).nr_muls()).str()) @@ -5323,7 +5348,15 @@ fn (mut g Gen) selector_expr(node ast.SelectorExpr) { } } } - g.checker_bug('unexpected SelectorExpr.expr_type = 0', node.pos) + resolved_expr_type := g.resolved_expr_type(node.expr, g.type_resolver.get_type_or_default(node.expr, + ast.void_type)) + if resolved_expr_type == 0 || resolved_expr_type == ast.void_type { + g.checker_bug('unexpected SelectorExpr.expr_type = 0', node.pos) + } + unsafe { + mut p := &ast.SelectorExpr(&node) + p.expr_type = resolved_expr_type + } } mut selector_scope := node.scope @@ -7906,6 +7939,11 @@ fn (mut g Gen) return_stmt(node ast.Return) { return } } + ret_expr_types := if node.types.len == 0 && exprs_len == 1 && type0 != ast.void_type { + [type0] + } else { + node.types + } ret_type := g.unwrap_generic(g.fn_decl.return_type) // got to do a correct check for multireturn @@ -7973,7 +8011,7 @@ fn (mut g Gen) return_stmt(node ast.Return) { || g.cur_lock.lockeds.len > 0 || (fn_return_is_multi && exprs_len >= 1 && fn_return_is_option) || fn_return_is_fixed_array_non_result - || (fn_return_is_multi && node.types.any(g.table.final_sym(it).kind == .array_fixed)) + || (fn_return_is_multi && ret_expr_types.any(g.table.final_sym(it).kind == .array_fixed)) // handle promoting none/error/function returning _option' if fn_return_is_option { option_none := expr0 is ast.None @@ -8110,9 +8148,9 @@ fn (mut g Gen) return_stmt(node ast.Return) { continue } g.write('.arg${arg_idx}=') - if expr !is ast.ArrayInit && g.table.final_sym(node.types[i]).kind == .array_fixed { + if expr !is ast.ArrayInit && g.table.final_sym(ret_expr_types[i]).kind == .array_fixed { line := g.go_before_last_stmt().trim_space() - expr_styp := g.styp(node.types[i]) + expr_styp := g.styp(ret_expr_types[i]) g.write('memcpy(&') if fn_return_is_result || fn_return_is_option { g.write('((${styp}*)') @@ -8136,9 +8174,9 @@ fn (mut g Gen) return_stmt(node ast.Return) { g.write('*') } if mr_info.types[i].has_flag(.option) { - g.expr_with_opt(expr, node.types[i], mr_info.types[i]) + g.expr_with_opt(expr, ret_expr_types[i], mr_info.types[i]) } else if g.table.sym(mr_info.types[i]).kind in [.sum_type, .interface] { - g.expr_with_cast(expr, node.types[i], mr_info.types[i]) + g.expr_with_cast(expr, ret_expr_types[i], mr_info.types[i]) } else { g.expr(expr) } @@ -8175,7 +8213,7 @@ fn (mut g Gen) return_stmt(node ast.Return) { g.write('return ${tmpvar}') } } else if exprs_len >= 1 { - if node.types.len == 0 { + if ret_expr_types.len == 0 { g.checker_bug('node.exprs.len == ${node.exprs.len} && node.types.len == 0', node.pos) } diff --git a/vlib/v/gen/native/blacklist.v b/vlib/v/gen/native/blacklist.v index 4d4f3fa4a..776a3b902 100644 --- a/vlib/v/gen/native/blacklist.v +++ b/vlib/v/gen/native/blacklist.v @@ -68,6 +68,7 @@ const blacklist = { '_memory_panic': false 'panic': false 'vcurrent_hash': false + '__at_least_one': false '__new_array': false 'panic_on_negative_len': false 'panic_on_negative_cap': false @@ -103,6 +104,7 @@ const windows_blacklist = { 'string.last_index': true 'string.last_index_u8': false 'string.contains_u8': false + '__at_least_one': false } fn (g &Gen) is_blacklisted(name string, is_builtin bool) bool { diff --git a/vlib/v/type_resolver/generic_resolver.v b/vlib/v/type_resolver/generic_resolver.v index 53e7e57b6..7dcc71179 100644 --- a/vlib/v/type_resolver/generic_resolver.v +++ b/vlib/v/type_resolver/generic_resolver.v @@ -237,10 +237,11 @@ pub fn (mut t TypeResolver) resolve_args(cur_fn &ast.FnDecl, func &ast.Fn, mut n comptime_args[k] = cparam_type_sym.info.value_type } } else { - if node_.args[i].expr.is_auto_deref_var() { + if node_.args[i].expr.is_auto_deref_var() && !node_.args[i].is_mut { ctyp = ctyp.deref() } - if ctyp.nr_muls() > 0 && param_typ.nr_muls() > 0 { + if ctyp.nr_muls() > 0 && param_typ.nr_muls() > 0 + && !node_.args[i].is_mut { ctyp = ctyp.set_nr_muls(0) } comptime_args[k] = ctyp @@ -256,10 +257,10 @@ pub fn (mut t TypeResolver) resolve_args(cur_fn &ast.FnDecl, func &ast.Fn, mut n if param_typ_sym.kind == .array && cparam_type_sym.info is ast.Array { ctyp = cparam_type_sym.info.elem_type } - if node_.args[i].expr.is_auto_deref_var() { + if node_.args[i].expr.is_auto_deref_var() && !node_.args[i].is_mut { ctyp = ctyp.deref() } - if ctyp.nr_muls() > 0 && param_typ.nr_muls() > 0 { + if ctyp.nr_muls() > 0 && param_typ.nr_muls() > 0 && !node_.args[i].is_mut { ctyp = ctyp.set_nr_muls(0) } comptime_args[k] = ctyp -- 2.39.5