Skip to content

Commit 6ffdbfd

Browse files
committed
checker: hopefully fix this
1 parent 736abd4 commit 6ffdbfd

File tree

3 files changed

+67
-30
lines changed

3 files changed

+67
-30
lines changed

vlib/v/ast/table.v

Lines changed: 4 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -2177,28 +2177,6 @@ pub fn (mut t Table) unwrap_generic_type_ex(typ Type, generic_names []string, co
21772177
idx := t.find_or_register_map(unwrap_key_type, unwrap_value_type)
21782178
return new_type(idx).derive_add_muls(typ).clear_flag(.generic)
21792179
}
2180-
FnType {
2181-
mut unwrapped_fn := ts.info.func
2182-
unwrapped_fn.params = unwrapped_fn.params.clone()
2183-
mut has_generic := false
2184-
for i, param in unwrapped_fn.params {
2185-
if param.typ.has_flag(.generic) {
2186-
unwrapped_fn.params[i].typ = t.unwrap_generic_type_ex(param.typ, generic_names,
2187-
concrete_types, recheck_concrete_types)
2188-
has_generic = true
2189-
}
2190-
}
2191-
if unwrapped_fn.return_type.has_flag(.generic) {
2192-
unwrapped_fn.return_type = t.unwrap_generic_type_ex(unwrapped_fn.return_type,
2193-
generic_names, concrete_types, recheck_concrete_types)
2194-
has_generic = true
2195-
}
2196-
if has_generic {
2197-
idx := t.find_or_register_fn_type(unwrapped_fn, true, false)
2198-
return new_type(idx).derive_add_muls(typ).clear_flag(.generic)
2199-
}
2200-
return typ
2201-
}
22022180
Struct, Interface, SumType {
22032181
if !ts.info.is_generic {
22042182
return typ
@@ -2442,7 +2420,10 @@ pub fn (mut t Table) unwrap_generic_type_ex(typ Type, generic_names []string, co
24422420
return new_type(new_idx).derive(typ).clear_flag(.generic)
24432421
}
24442422
else {
2445-
if typ.has_flag(.generic) && ts.kind == .any {
2423+
// Only convert generic placeholders when recheck_concrete_types is true
2424+
// This ensures conversion happens during the recheck phase (code generation)
2425+
// and not during initial type checking where it causes false positives
2426+
if recheck_concrete_types && typ.has_flag(.generic) && ts.kind == .any {
24462427
if converted := t.convert_generic_type(typ, generic_names, concrete_types) {
24472428
converted_sym := t.sym(converted)
24482429
// TODO: find out why cgen fails on interfaces

vlib/v/checker/check_types.v

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -537,7 +537,10 @@ fn (mut c Checker) check_matching_function_symbols(got_type_sym &ast.TypeSymbol,
537537
if got_fn.return_type.has_flag(.result) != exp_fn.return_type.has_flag(.result) {
538538
return false
539539
}
540-
if !c.check_basic(got_fn.return_type, exp_fn.return_type) {
540+
// If expected return type has .generic flag, it's a generic placeholder (T, U, R, etc.)
541+
// and should match any type
542+
if !exp_fn.return_type.has_flag(.generic)
543+
&& !c.check_basic(got_fn.return_type, exp_fn.return_type) {
541544
return false
542545
}
543546
// The check for sumtype in c.check_basic() in the previous step is only for its variant to be subsumed
@@ -548,6 +551,11 @@ fn (mut c Checker) check_matching_function_symbols(got_type_sym &ast.TypeSymbol,
548551
}
549552
for i, got_arg in got_fn.params {
550553
exp_arg := exp_fn.params[i]
554+
// If expected parameter type has .generic flag, it's a generic placeholder
555+
// and should match any type, so skip the check
556+
if exp_arg.typ.has_flag(.generic) {
557+
continue
558+
}
551559
exp_arg_typ := c.unwrap_generic(exp_arg.typ)
552560
got_arg_typ := c.unwrap_generic(got_arg.typ)
553561
exp_arg_is_ptr := exp_arg_typ.is_any_kind_of_pointer()
@@ -1141,8 +1149,7 @@ fn (mut c Checker) infer_fn_generic_types(func &ast.Fn, mut node ast.CallExpr) {
11411149
for {
11421150
if mut arg_elem_sym.info is ast.Array
11431151
&& mut param_elem_sym.info is ast.Array
1144-
&& c.table.cur_fn != unsafe { nil }
1145-
&& param_elem_sym.name !in c.table.cur_fn.generic_names {
1152+
&& param_elem_sym.name !in func.generic_names {
11461153
arg_elem_typ, param_elem_typ = arg_elem_sym.info.elem_type, param_elem_sym.info.elem_type
11471154
arg_elem_sym, param_elem_sym = c.table.sym(arg_elem_typ), c.table.sym(param_elem_typ)
11481155
} else {
@@ -1161,8 +1168,7 @@ fn (mut c Checker) infer_fn_generic_types(func &ast.Fn, mut node ast.CallExpr) {
11611168
for {
11621169
if mut arg_elem_sym.info is ast.ArrayFixed
11631170
&& mut param_elem_sym.info is ast.ArrayFixed
1164-
&& c.table.cur_fn != unsafe { nil }
1165-
&& param_elem_sym.name !in c.table.cur_fn.generic_names {
1171+
&& param_elem_sym.name !in func.generic_names {
11661172
arg_elem_typ, param_elem_typ = arg_elem_sym.info.elem_type, param_elem_sym.info.elem_type
11671173
arg_elem_sym, param_elem_sym = c.table.sym(arg_elem_typ), c.table.sym(param_elem_typ)
11681174
} else {

vlib/v/checker/fn.v

Lines changed: 52 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1347,6 +1347,7 @@ fn (mut c Checker) fn_call(mut node ast.CallExpr, mut continue_check &bool) ast.
13471347

13481348
// XTODO document
13491349
if typ != 0 {
1350+
// Only unwrap FnType if we have concrete types (during recheck/instantiation)
13501351
unwrapped_typ := if typ.has_flag(.generic) && c.table.cur_fn != unsafe { nil }
13511352
&& c.table.cur_fn.generic_names.len > 0 {
13521353
c.table.unwrap_generic_type(typ, c.table.cur_fn.generic_names, c.table.cur_concrete_types)
@@ -1356,6 +1357,26 @@ fn (mut c Checker) fn_call(mut node ast.CallExpr, mut continue_check &bool) ast.
13561357
generic_vts := c.table.final_sym(unwrapped_typ)
13571358
if generic_vts.info is ast.FnType {
13581359
func = generic_vts.info.func
1360+
// If we're inside a generic function with concrete types, unwrap the function's parameter types
1361+
// This is needed to properly type-check calls like f(1) where f: fn(T) U and T=string
1362+
if c.table.cur_fn != unsafe { nil } && c.table.cur_fn.generic_names.len > 0
1363+
&& c.table.cur_concrete_types.len == c.table.cur_fn.generic_names.len {
1364+
mut unwrapped_func := func
1365+
unwrapped_func.params = unwrapped_func.params.clone()
1366+
for i, param in func.params {
1367+
if param.typ.has_flag(.generic) {
1368+
unwrapped_func.params[i].typ = c.table.unwrap_generic_type_ex(param.typ,
1369+
c.table.cur_fn.generic_names, c.table.cur_concrete_types,
1370+
true)
1371+
}
1372+
}
1373+
if func.return_type.has_flag(.generic) {
1374+
unwrapped_func.return_type = c.table.unwrap_generic_type_ex(func.return_type,
1375+
c.table.cur_fn.generic_names, c.table.cur_concrete_types,
1376+
true)
1377+
}
1378+
func = unwrapped_func
1379+
}
13591380
found = true
13601381
found_in_args = true
13611382
} else {
@@ -1758,9 +1779,28 @@ fn (mut c Checker) fn_call(mut node ast.CallExpr, mut continue_check &bool) ast.
17581779
c.error('literal argument cannot be passed as reference parameter `${c.table.type_to_str(param.typ)}`',
17591780
call_arg.pos)
17601781
}
1761-
c.check_expected_call_arg(arg_typ, c.unwrap_generic(param.typ), node.language,
1782+
// checked by second pass
1783+
if param.typ.has_flag(.generic) && func.generic_names.len > 0 && concrete_types.len == 0 {
1784+
continue
1785+
}
1786+
unwrapped_param_typ := if param.typ.has_flag(.generic) && func.generic_names.len > 0
1787+
&& concrete_types.len == func.generic_names.len {
1788+
c.table.unwrap_generic_type_ex(param.typ, func.generic_names, concrete_types,
1789+
true)
1790+
} else {
1791+
c.unwrap_generic(param.typ)
1792+
}
1793+
unwrapped_arg_typ := if arg_typ.has_flag(.generic) && c.table.cur_fn != unsafe { nil }
1794+
&& c.table.cur_fn.generic_names.len > 0
1795+
&& c.table.cur_concrete_types.len == c.table.cur_fn.generic_names.len {
1796+
c.table.unwrap_generic_type_ex(arg_typ, c.table.cur_fn.generic_names, c.table.cur_concrete_types,
1797+
true)
1798+
} else {
1799+
arg_typ
1800+
}
1801+
c.check_expected_call_arg(unwrapped_arg_typ, unwrapped_param_typ, node.language,
17621802
call_arg) or {
1763-
if param.typ.has_flag(.generic) {
1803+
if unwrapped_param_typ.has_flag(.generic) {
17641804
continue
17651805
}
17661806
if param_typ_sym.info is ast.Array && arg_typ_sym.info is ast.Array {
@@ -1939,6 +1979,16 @@ fn (mut c Checker) fn_call(mut node ast.CallExpr, mut continue_check &bool) ast.
19391979
// json.encode param is set voidptr, we should bound the proper type here
19401980
node.expected_arg_types = [node.args[0].typ]
19411981
}
1982+
// unwrap before type inference matching
1983+
if c.table.cur_fn != unsafe { nil } && c.table.cur_fn.generic_names.len > 0
1984+
&& c.table.cur_concrete_types.len == c.table.cur_fn.generic_names.len {
1985+
for i, arg in node.args {
1986+
if arg.typ.has_flag(.generic) {
1987+
node.args[i].typ = c.table.unwrap_generic_type_ex(arg.typ, c.table.cur_fn.generic_names,
1988+
c.table.cur_concrete_types, true)
1989+
}
1990+
}
1991+
}
19421992
if func.generic_names.len != node.concrete_types.len {
19431993
// no type arguments given in call, attempt implicit instantiation
19441994
c.infer_fn_generic_types(func, mut node)

0 commit comments

Comments
 (0)