Skip to content

Commit 8b4ff32

Browse files
parser: fix anonymous function name collisions across files (#26642)
1 parent 1c4bb91 commit 8b4ff32

7 files changed

Lines changed: 37 additions & 4 deletions

File tree

vlib/v/ast/str.v

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
@[has_globals]
55
module ast
66

7+
import v.token
78
import v.util
89
import strings
910
import sync.stdatomic
@@ -18,8 +19,8 @@ pub fn (f &FnDecl) get_name() string {
1819
}
1920

2021
// get_anon_fn_name returns the unique anonymous function name, based on the prefix, the func signature and its position in the source code
21-
pub fn (table &Table) get_anon_fn_name(prefix string, func &Fn, pos int) string {
22-
return 'anon_fn_${prefix}_${table.fn_type_signature(func)}_${pos}'
22+
pub fn (table &Table) get_anon_fn_name(prefix string, func &Fn, pos token.Pos) string {
23+
return 'anon_fn_${prefix}_${pos.file_idx}_${table.fn_type_signature(func)}_${pos.pos}'
2324
}
2425

2526
// get_name returns the real name for the function calling

vlib/v/checker/lambda_expr.v

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ pub fn (mut c Checker) lambda_expr(mut node ast.LambdaExpr, exp_typ ast.Type) as
8383
return_type: return_type
8484
is_method: false
8585
}
86-
name := c.table.get_anon_fn_name(c.file.unique_prefix, func, node.pos.pos)
86+
name := c.table.get_anon_fn_name(c.file.unique_prefix, func, node.pos)
8787
func.name = name
8888
idx := c.table.find_or_register_fn_type(func, true, false)
8989
typ := ast.new_type(idx)
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
1
2+
2
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
module main
2+
3+
// Both a.v and b.v define an anonymous function with the same signature (fn() int)
4+
// at the same byte offset from the start of their respective files.
5+
// Before the fix in get_anon_fn_name(), both would produce the same C symbol name,
6+
// causing a linker collision or silent wrong behaviour.
7+
fn get_fn_a() fn () int {
8+
return fn () int {
9+
return 1
10+
}
11+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
module main
2+
3+
// This file intentionally pads its content so that the anonymous function below
4+
// starts at the same byte offset as the one in a.v. Before the fix in
5+
// get_anon_fn_name() (which now includes pos.file_idx in the C symbol name),
6+
// both would produce the same name and trigger a linker error.
7+
fn get_fn_b() fn () int {
8+
return fn () int {
9+
return 2
10+
}
11+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
module main
2+
3+
fn main() {
4+
fa := get_fn_a()
5+
fb := get_fn_b()
6+
println(fa())
7+
println(fb())
8+
}

vlib/v/parser/fn.v

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1218,7 +1218,7 @@ fn (mut p Parser) anon_fn() ast.AnonFn {
12181218
return_type: return_type
12191219
is_method: false
12201220
}
1221-
name := p.table.get_anon_fn_name(p.unique_prefix, func, p.tok.pos)
1221+
name := p.table.get_anon_fn_name(p.unique_prefix, func, p.tok.pos())
12221222
keep_fn_name := p.cur_fn_name
12231223
p.cur_fn_name = name
12241224
if p.tok.kind == .lcbr {

0 commit comments

Comments
 (0)