From e256f1b2aa7db0c2bd7d0ca54f4175f7e4d5718f Mon Sep 17 00:00:00 2001 From: Delyan Angelov Date: Wed, 22 Dec 2021 16:16:10 +0200 Subject: [PATCH] checker,cgen: stabilise generic method handling (fix a source code ordering issue) --- vlib/v/ast/ast.v | 1 + vlib/v/checker/checker.v | 13 ++++++++-- vlib/v/checker/fn.v | 2 ++ vlib/v/gen/c/cgen.v | 4 +++ vlib/v/gen/c/fn.v | 9 ++++++- vlib/v/tests/generics_method_ordering_test.v | 27 ++++++++++++++++++++ 6 files changed, 53 insertions(+), 3 deletions(-) create mode 100644 vlib/v/tests/generics_method_ordering_test.v diff --git a/vlib/v/ast/ast.v b/vlib/v/ast/ast.v index 52ffb87f9b..3d82b6ea1c 100644 --- a/vlib/v/ast/ast.v +++ b/vlib/v/ast/ast.v @@ -488,6 +488,7 @@ pub mut: return_type_pos token.Position // `string` in `fn (u User) name() string` position has_return bool should_be_skipped bool // true, when -skip-unused could not find any usages of that function, starting from main + other known used functions + ninstances int // 0 for generic functions with no concrete instances has_await bool // 'true' if this function uses JS.await // comments []Comment // comments *after* the header, but *before* `{`; used for InterfaceDecl diff --git a/vlib/v/checker/checker.v b/vlib/v/checker/checker.v index e80a05dce5..6e1ecfbfd1 100644 --- a/vlib/v/checker/checker.v +++ b/vlib/v/checker/checker.v @@ -291,7 +291,7 @@ pub fn (mut c Checker) check_files(ast_files []&ast.File) { c.timers.start('checker_post_process_generic_fns') last_file := c.file // post process generic functions. must be done after all files have been - // checked, to eunsure all generic calls are processed as this information + // checked, to ensure all generic calls are processed as this information // is needed when the generic type is auto inferred from the call argument // Check more times if there are more new registered fn concrete types for { @@ -4831,7 +4831,11 @@ fn (mut c Checker) post_process_generic_fns() { for i in 0 .. c.file.generic_fns.len { mut node := c.file.generic_fns[i] c.mod = node.mod - for concrete_types in c.table.fn_generic_types[node.name] { + gtypes := c.table.fn_generic_types[node.name] + $if trace_post_process_generic_fns ? { + eprintln('> post_process_generic_fns $node.mod | $node.name | $gtypes') + } + for concrete_types in gtypes { c.table.cur_concrete_types = concrete_types c.fn_decl(mut node) if node.name == 'vweb.run' { @@ -4843,6 +4847,11 @@ fn (mut c Checker) post_process_generic_fns() { } } c.table.cur_concrete_types = [] + $if trace_post_process_generic_fns ? { + if node.generic_names.len > 0 { + eprintln(' > fn_decl node.name: $node.name | generic_names: $node.generic_names | ninstances: $node.ninstances') + } + } } } diff --git a/vlib/v/checker/fn.v b/vlib/v/checker/fn.v index 6a8fff321f..295b89c518 100644 --- a/vlib/v/checker/fn.v +++ b/vlib/v/checker/fn.v @@ -14,8 +14,10 @@ fn (mut c Checker) fn_decl(mut node ast.FnDecl) { // have a chance to populate c.table.fn_generic_types with // the correct concrete types. c.file.generic_fns << node + c.need_recheck_generic_fns = true return } + node.ninstances++ // save all the state that fn_decl or inner statements/expressions // could potentially modify, since functions can be nested, due to // anonymous function support, and ensure that it is restored, when diff --git a/vlib/v/gen/c/cgen.v b/vlib/v/gen/c/cgen.v index 46580faa64..8a3b571dfc 100644 --- a/vlib/v/gen/c/cgen.v +++ b/vlib/v/gen/c/cgen.v @@ -4855,6 +4855,10 @@ fn (mut g Gen) return_stmt(node ast.Return) { has_semicolon = true } } else if node.exprs.len >= 1 { + if node.types.len == 0 { + g.checker_bug('node.exprs.len == $node.exprs.len && node.types.len == 0', + node.pos) + } // normal return return_sym := g.table.sym(node.types[0]) expr0 := node.exprs[0] diff --git a/vlib/v/gen/c/fn.v b/vlib/v/gen/c/fn.v index 2de86347b6..b4384b7c31 100644 --- a/vlib/v/gen/c/fn.v +++ b/vlib/v/gen/c/fn.v @@ -32,6 +32,12 @@ fn (mut g Gen) fn_decl(node ast.FnDecl) { if node.should_be_skipped { return } + if node.ninstances == 0 && node.generic_names.len > 0 { + $if trace_generics ? { + eprintln('skipping generic fn with no concrete instances: $node.mod $node.name') + } + return + } if !g.is_used_by_main(node) { return } @@ -82,7 +88,8 @@ fn (mut g Gen) fn_decl(node ast.FnDecl) { if node.is_main { g.has_main = true } - is_backtrace := node.name.starts_with('backtrace') // TODO PERF remove this from here + // TODO PERF remove this from here + is_backtrace := node.name.starts_with('backtrace') && node.name in ['backtrace_symbols', 'backtrace', 'backtrace_symbols_fd'] if is_backtrace { g.write('\n#ifndef __cplusplus\n') diff --git a/vlib/v/tests/generics_method_ordering_test.v b/vlib/v/tests/generics_method_ordering_test.v new file mode 100644 index 0000000000..c3c1d678ca --- /dev/null +++ b/vlib/v/tests/generics_method_ordering_test.v @@ -0,0 +1,27 @@ +struct Foo { + x int +} + +fn (f Foo) pop() { + println('hey') +} + +struct Bar { + y int +} + +// NB: Bar.foo before Bar.pop, should not cause a V compiler panic +fn (b Bar) foo() bool { + return true +} + +fn (b Bar) pop() { + println(b.foo()) +} + +// fn dummy() { println(Bar{}) } + +fn test_foo() { + dump(Foo{}) + assert true +}