From f3addb5b5095d549dbc7ec0f1847186350e866d7 Mon Sep 17 00:00:00 2001 From: Alexander Medvednikov Date: Thu, 26 Feb 2026 10:03:27 +0300 Subject: [PATCH] comptime: include parent methods in $for on alias types (fixes #25405) --- vlib/v/ast/table.v | 24 ++++++++++++++++++ vlib/v/checker/comptime.v | 2 +- vlib/v/gen/c/comptime.v | 12 +++++---- .../comptime_for_alias_methods_test.v | 25 +++++++++++++++++++ .../tests/veb_aliased_app_and_context_test.v | 9 +++++-- 5 files changed, 64 insertions(+), 8 deletions(-) create mode 100644 vlib/v/tests/comptime/comptime_for_alias_methods_test.v diff --git a/vlib/v/ast/table.v b/vlib/v/ast/table.v index 9ae3ef440..415ef3a0e 100644 --- a/vlib/v/ast/table.v +++ b/vlib/v/ast/table.v @@ -342,6 +342,30 @@ pub fn (t &Table) has_method(s &TypeSymbol, name string) bool { return true } +// get_type_methods returns methods available on `typ`. +// For aliases, it includes alias-defined methods first, then inherited parent methods. +pub fn (t &Table) get_type_methods(typ Type) []Fn { + mut ts := t.sym(typ) + mut methods := ts.get_methods() + if ts.kind != .alias { + return methods + } + mut seen_method_names := map[string]bool{} + for method in methods { + seen_method_names[method.name] = true + } + for ts.parent_idx != 0 { + ts = t.type_symbols[ts.parent_idx] + for method in ts.get_methods() { + if method.name !in seen_method_names { + methods << method + seen_method_names[method.name] = true + } + } + } + return methods +} + // find_method searches from current type up through each parent looking for method pub fn (t &Table) find_method(s &TypeSymbol, name string) !Fn { mut ts := unsafe { s } diff --git a/vlib/v/checker/comptime.v b/vlib/v/checker/comptime.v index 2ab4d321f..7de2f7fc5 100644 --- a/vlib/v/checker/comptime.v +++ b/vlib/v/checker/comptime.v @@ -434,7 +434,7 @@ fn (mut c Checker) comptime_for(mut node ast.ComptimeFor) { return } } else if node.kind == .methods { - mut methods := sym.get_methods() + mut methods := c.table.get_type_methods(typ) if methods.len == 0 { // force eval `node.stmts` to set their types methods << ast.Fn{} diff --git a/vlib/v/gen/c/comptime.v b/vlib/v/gen/c/comptime.v index d2c93c820..c24d7ce05 100644 --- a/vlib/v/gen/c/comptime.v +++ b/vlib/v/gen/c/comptime.v @@ -621,17 +621,19 @@ fn (mut g Gen) pop_comptime_info() { } fn (mut g Gen) comptime_for(node ast.ComptimeFor) { - sym := if node.typ != g.field_data_type { - g.table.final_sym(g.unwrap_generic(node.typ)) + for_typ := if node.typ != g.field_data_type { + g.unwrap_generic(node.typ) } else { - g.table.final_sym(g.comptime.comptime_for_field_type) + g.comptime.comptime_for_field_type } - g.writeln('/* \$for ${node.val_var} in ${sym.name}.${node.kind.str()} */ {') + sym := g.table.final_sym(for_typ) + iter_sym_name := if node.kind == .methods { g.table.sym(for_typ).name } else { sym.name } + g.writeln('/* \$for ${node.val_var} in ${iter_sym_name}.${node.kind.str()} */ {') g.indent++ mut i := 0 old_defer_stmts := g.defer_stmts if node.kind == .methods { - methods := sym.get_methods() + methods := g.table.get_type_methods(for_typ) if methods.len > 0 { g.writeln('FunctionData ${node.val_var} = {0};') } diff --git a/vlib/v/tests/comptime/comptime_for_alias_methods_test.v b/vlib/v/tests/comptime/comptime_for_alias_methods_test.v new file mode 100644 index 000000000..098d352f9 --- /dev/null +++ b/vlib/v/tests/comptime/comptime_for_alias_methods_test.v @@ -0,0 +1,25 @@ +struct App {} + +type AliasApp = App + +fn (app AliasApp) alias_method() int { + return 1 +} + +fn (app App) parent_method() int { + return 2 +} + +fn collect_method_names[T]() []string { + mut names := []string{} + $for method in T.methods { + names << method.name + } + return names +} + +fn test_comptime_for_alias_methods_includes_alias_and_parent_methods() { + names := collect_method_names[AliasApp]() + assert 'alias_method' in names + assert 'parent_method' in names +} diff --git a/vlib/veb/tests/veb_aliased_app_and_context_test.v b/vlib/veb/tests/veb_aliased_app_and_context_test.v index c4c999932..476aa9a59 100644 --- a/vlib/veb/tests/veb_aliased_app_and_context_test.v +++ b/vlib/veb/tests/veb_aliased_app_and_context_test.v @@ -48,6 +48,11 @@ fn test_aliased_app_compiles_and_starts() { fn test_static_root() { x := http.get('http://127.0.0.1:${app_port}/')! eprintln('>>>> http request was sent and received') - assert x.status() == .not_found - assert x.body == '404 Not Found' + assert x.status() == .ok + assert x.body == 'index success' +} + +@['/'; get] +fn (mut app AliasApp) index(mut ctx AliasContext) veb.Result { + return ctx.text('index success') } -- 2.39.5