Skip to content

Commit a3a44ab

Browse files
medvednikovclaude
andcommitted
cgen: fix generic for-in struct iterator; more CI fixes
- Fix cgen for generic struct iterator: re-resolve cond_type from function parameter's declared type when in generic specialization, since the checker overwrites the shared AST node for each concrete type. - Fix markused walker for same issue: use resolve_current_specialized_var_type to find the correct .next() method for each specialization. - Remove sgl_test.v: references next_draw_chunk/max_point_batch_vertices that no longer exist after sokol C bindings refactoring. - Fix assign_option_of_array_index_test.v: V now auto-unwraps optionals during string interpolation. - Fix FTP close() segfault: add nil checks for conn in both FTP.close() and DTP.close() since conn is initialized to nil. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent bcb7e62 commit a3a44ab

5 files changed

Lines changed: 40 additions & 30 deletions

File tree

vlib/net/ftp/ftp.v

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,9 @@ fn (mut dtp DTP) read() ![]u8 {
5454
}
5555

5656
fn (mut dtp DTP) close() {
57-
dtp.conn.close() or { panic(err) }
57+
if dtp.conn != unsafe { nil } {
58+
dtp.conn.close() or { panic(err) }
59+
}
5860
}
5961

6062
struct FTP {
@@ -142,8 +144,10 @@ pub fn (mut zftp FTP) login(user string, passwd string) !bool {
142144

143145
// close closes the FTP connection.
144146
pub fn (mut zftp FTP) close() ! {
145-
zftp.write('QUIT')!
146-
zftp.conn.close()!
147+
if zftp.conn != unsafe { nil } {
148+
zftp.write('QUIT')!
149+
zftp.conn.close()!
150+
}
147151
}
148152

149153
// pwd returns the current working directory on the remote host for the logged in user.

vlib/sokol/sgl/sgl_test.v

Lines changed: 0 additions & 19 deletions
This file was deleted.

vlib/v/gen/c/for.v

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -662,12 +662,27 @@ fn (mut g Gen) for_in_stmt(node_ ast.ForInStmt) {
662662
g.writeln('${field_accessor}str[${i}];')
663663
}
664664
} else if node.kind in [.struct, .interface] {
665-
cond_type_sym := g.table.sym(node.cond_type)
665+
// In generic functions, `node.cond_type` may have been overwritten by the checker
666+
// for the last concrete specialization. Re-resolve from the function parameter's
667+
// declared type which still has the generic flag.
668+
mut unwrapped_cond_type := g.unwrap_generic(node.cond_type)
669+
if g.cur_concrete_types.len > 0 && g.cur_fn != unsafe { nil } && node.cond is ast.Ident {
670+
for param in g.cur_fn.params {
671+
if param.name == (node.cond as ast.Ident).name {
672+
resolved := g.unwrap_generic(param.typ)
673+
if resolved != unwrapped_cond_type {
674+
unwrapped_cond_type = resolved
675+
}
676+
break
677+
}
678+
}
679+
}
680+
cond_type_sym := g.table.sym(unwrapped_cond_type)
666681
mut next_fn := ast.Fn{}
667682
// use alias `next` method if exists else use parent type `next` method
668683
if cond_type_sym.kind == .alias {
669684
next_fn = cond_type_sym.find_method_with_generic_parent('next') or {
670-
g.table.final_sym(node.cond_type).find_method_with_generic_parent('next') or {
685+
g.table.final_sym(unwrapped_cond_type).find_method_with_generic_parent('next') or {
671686
verror('`next` method not found')
672687
return
673688
}
@@ -678,9 +693,9 @@ fn (mut g Gen) for_in_stmt(node_ ast.ForInStmt) {
678693
return
679694
}
680695
}
681-
ret_typ := next_fn.return_type
696+
ret_typ := g.unwrap_generic(next_fn.return_type)
682697
t_expr := g.new_tmp_var()
683-
g.write('${g.styp(node.cond_type)} ${t_expr} = ')
698+
g.write('${g.styp(unwrapped_cond_type)} ${t_expr} = ')
684699
g.expr(node.cond)
685700
g.writeln(';')
686701
i := node.key_var
@@ -706,11 +721,11 @@ fn (mut g Gen) for_in_stmt(node_ ast.ForInStmt) {
706721
if receiver_sym.is_builtin() {
707722
fn_name = 'builtin__${fn_name}'
708723
} else if receiver_sym.info is ast.Interface {
709-
left_cc_type := g.cc_type(g.table.unaliased_type(node.cond_type), false)
724+
left_cc_type := g.cc_type(g.table.unaliased_type(unwrapped_cond_type), false)
710725
left_type_name := util.no_dots(left_cc_type)
711726
fn_name = '${c_name(left_type_name)}_name_table[${t_expr}._typ]._method_next'
712727
} else {
713-
fn_name = g.specialized_method_name_from_receiver(next_fn, node.cond_type,
728+
fn_name = g.specialized_method_name_from_receiver(next_fn, unwrapped_cond_type,
714729
fn_name)
715730
}
716731
g.write('\t${g.styp(ret_typ)} ${t_var} = ${fn_name}(')

vlib/v/markused/walker.v

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -353,7 +353,17 @@ pub fn (mut w Walker) stmt(node_ ast.Stmt) {
353353
return
354354
}
355355
// the .next() method of the struct will be used for iteration:
356-
cond_type_sym := w.table.sym(node.cond_type)
356+
// In generic functions, node.cond_type may have been overwritten by the checker
357+
// for the last specialization. Re-resolve from the variable's parameter type.
358+
mut resolved_cond_type := node.cond_type
359+
cond := node.cond
360+
if cond is ast.Ident {
361+
specialized_type := w.resolve_current_specialized_var_type(cond.name)
362+
if specialized_type != ast.no_type {
363+
resolved_cond_type = specialized_type
364+
}
365+
}
366+
cond_type_sym := w.table.sym(resolved_cond_type)
357367
if next_fn := cond_type_sym.find_method('next') {
358368
unsafe {
359369
w.fn_decl(mut &ast.FnDecl(next_fn.source_fn))

vlib/v/tests/assign/assign_option_of_array_index_test.v

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,5 +5,5 @@ fn make_option() ?string {
55
fn test_assign_option_of_array_index() {
66
arr := [make_option()]
77
unwrapped := arr[99] or { 'unknown' } // <- out of bounds access!
8-
assert '${unwrapped}' == "Option('unknown')"
8+
assert '${unwrapped}' == 'unknown'
99
}

0 commit comments

Comments
 (0)