From dafa6fd0aaaa329a72c724297e895d6097303265 Mon Sep 17 00:00:00 2001 From: Delyan Angelov Date: Thu, 4 Dec 2025 15:00:05 +0200 Subject: [PATCH] checker: force all fn declarations to follow after all other top level statements (fix #25889) (#25890) --- vlib/v/checker/checker.v | 26 ++++++++++++++ .../c_js_struct_and_fn_as_generics_error.out | 14 ++++---- .../tests/duplicate_field_method_err.out | 12 +++---- .../tests/invalid_parameter_name_err.out | 14 ++++---- .../fns/generic_fn_order_issue_25889_test.v | 35 +++++++++++++++++++ 5 files changed, 81 insertions(+), 20 deletions(-) create mode 100644 vlib/v/tests/fns/generic_fn_order_issue_25889_test.v diff --git a/vlib/v/checker/checker.v b/vlib/v/checker/checker.v index 214358684..3f2e98448 100644 --- a/vlib/v/checker/checker.v +++ b/vlib/v/checker/checker.v @@ -266,6 +266,7 @@ pub fn (mut c Checker) check(mut ast_file ast.File) { } } } + c.reorder_fns_at_the_end(mut ast_file) c.stmt_level = 0 for mut stmt in ast_file.stmts { if stmt in [ast.ConstDecl, ast.ExprStmt] { @@ -303,6 +304,31 @@ pub fn (mut c Checker) check(mut ast_file ast.File) { c.check_unused_labels() } +pub fn (mut c Checker) reorder_fns_at_the_end(mut ast_file ast.File) { + mut fdeclarations := 0 + for mut stmt in ast_file.stmts { + if stmt is ast.FnDecl { + fdeclarations++ + } + } + if fdeclarations == 0 { + return + } + // eprintln('>>> ast_file: ${ast_file.path:-60s} | fdeclarations: ${fdeclarations}') + mut stmts := []ast.Stmt{cap: ast_file.stmts.len} + for stmt in ast_file.stmts { + if stmt !is ast.FnDecl { + stmts << stmt + } + } + for stmt in ast_file.stmts { + if stmt is ast.FnDecl { + stmts << stmt + } + } + ast_file.stmts = stmts +} + pub fn (mut c Checker) check_scope_vars(sc &ast.Scope) { if !c.pref.is_repl && !c.file.is_test { for _, obj in sc.objects { diff --git a/vlib/v/checker/tests/c_js_struct_and_fn_as_generics_error.out b/vlib/v/checker/tests/c_js_struct_and_fn_as_generics_error.out index 510aa255f..a63b1b866 100644 --- a/vlib/v/checker/tests/c_js_struct_and_fn_as_generics_error.out +++ b/vlib/v/checker/tests/c_js_struct_and_fn_as_generics_error.out @@ -3,6 +3,13 @@ vlib/v/checker/tests/c_js_struct_and_fn_as_generics_error.vv:1:1: error: C struc | ~~~~~~~~~~~~ 2 | ptr &T 3 | } +vlib/v/checker/tests/c_js_struct_and_fn_as_generics_error.vv:9:1: error: JS structs cannot be declared as generic + 7 | fn JS.encode[T](data string) T + 8 | + 9 | struct JS.Some[T] { + | ~~~~~~~~~~~~~~ + 10 | i T + 11 | } vlib/v/checker/tests/c_js_struct_and_fn_as_generics_error.vv:5:1: error: C functions cannot be declared as generic 3 | } 4 | @@ -17,10 +24,3 @@ vlib/v/checker/tests/c_js_struct_and_fn_as_generics_error.vv:7:1: error: JS func | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 8 | 9 | struct JS.Some[T] { -vlib/v/checker/tests/c_js_struct_and_fn_as_generics_error.vv:9:1: error: JS structs cannot be declared as generic - 7 | fn JS.encode[T](data string) T - 8 | - 9 | struct JS.Some[T] { - | ~~~~~~~~~~~~~~ - 10 | i T - 11 | } diff --git a/vlib/v/checker/tests/duplicate_field_method_err.out b/vlib/v/checker/tests/duplicate_field_method_err.out index b4befdd8e..9682667ca 100644 --- a/vlib/v/checker/tests/duplicate_field_method_err.out +++ b/vlib/v/checker/tests/duplicate_field_method_err.out @@ -1,3 +1,9 @@ +vlib/v/checker/tests/duplicate_field_method_err.vv:9:2: error: type `Foo` has both field and method named `bar` + 7 | interface Foo { + 8 | bar fn () + 9 | bar() + | ~~~~~ + 10 | } vlib/v/checker/tests/duplicate_field_method_err.vv:5:1: error: type `St` has both field and method named `attr` 3 | } 4 | @@ -5,9 +11,3 @@ vlib/v/checker/tests/duplicate_field_method_err.vv:5:1: error: type `St` has bot | ~~~~~~~~~~~~~~~~ 6 | 7 | interface Foo { -vlib/v/checker/tests/duplicate_field_method_err.vv:9:2: error: type `Foo` has both field and method named `bar` - 7 | interface Foo { - 8 | bar fn () - 9 | bar() - | ~~~~~ - 10 | } diff --git a/vlib/v/checker/tests/invalid_parameter_name_err.out b/vlib/v/checker/tests/invalid_parameter_name_err.out index db6ec6fa0..22a682f41 100644 --- a/vlib/v/checker/tests/invalid_parameter_name_err.out +++ b/vlib/v/checker/tests/invalid_parameter_name_err.out @@ -1,10 +1,3 @@ -vlib/v/checker/tests/invalid_parameter_name_err.vv:4:24: error: invalid use of reserved type `char` as a parameter name - 2 | } - 3 | - 4 | fn (mut b Buffer) put8(char u8) ! { - | ~~~~ - 5 | return error('-') - 6 | } vlib/v/checker/tests/invalid_parameter_name_err.vv:10:6: error: invalid use of reserved type `char` as a parameter name 8 | interface Writer { 9 | mut: @@ -12,3 +5,10 @@ vlib/v/checker/tests/invalid_parameter_name_err.vv:10:6: error: invalid use of r | ~~~~ 11 | } 12 | +vlib/v/checker/tests/invalid_parameter_name_err.vv:4:24: error: invalid use of reserved type `char` as a parameter name + 2 | } + 3 | + 4 | fn (mut b Buffer) put8(char u8) ! { + | ~~~~ + 5 | return error('-') + 6 | } diff --git a/vlib/v/tests/fns/generic_fn_order_issue_25889_test.v b/vlib/v/tests/fns/generic_fn_order_issue_25889_test.v new file mode 100644 index 000000000..cbe00ec36 --- /dev/null +++ b/vlib/v/tests/fns/generic_fn_order_issue_25889_test.v @@ -0,0 +1,35 @@ +interface Speaker { + speak() +} + +struct Cat {} + +fn (c &Cat) speak() {} + +struct EventA { + a u32 +} + +struct EventB { + b u32 +} + +type Event = EventA | EventB + +fn some_func() { + queue := Queue.new[Speaker]() +} + +struct Queue[T] { +mut: + data &T = unsafe { nil } +} + +fn Queue.new[T]() Queue[T] { + return Queue[T]{} +} + +fn test_main() { + some_func() + assert true +} -- 2.39.5