From 3e697fe95dd3669a2484fed4709e0773f3fcf4a1 Mon Sep 17 00:00:00 2001 From: Felipe Pena Date: Wed, 16 Oct 2024 01:02:02 -0300 Subject: [PATCH] cgen: fix comptime for method with `$method` as inherited var (fix #12657) (#22536) --- vlib/v/ast/scope.v | 11 ++++ vlib/v/comptime/comptimeinfo.v | 2 + vlib/v/gen/c/cgen.v | 1 + vlib/v/gen/c/comptime.v | 1 + vlib/v/gen/c/fn.v | 53 +++++++++++-------- .../comptime_call_method_closure_test.v | 22 ++++++++ 6 files changed, 69 insertions(+), 21 deletions(-) create mode 100644 vlib/v/tests/comptime/comptime_call_method_closure_test.v diff --git a/vlib/v/ast/scope.v b/vlib/v/ast/scope.v index 549391ea09a9fe..fb2ecc48d1640b 100644 --- a/vlib/v/ast/scope.v +++ b/vlib/v/ast/scope.v @@ -225,6 +225,17 @@ pub fn (s &Scope) has_inherited_vars() bool { return false } +pub fn (s &Scope) is_inherited_var(var_name string) bool { + for _, obj in s.objects { + if obj is Var { + if obj.is_inherited && obj.name == var_name { + return true + } + } + } + return false +} + pub fn (sc &Scope) show(depth int, max_depth int) string { mut out := '' mut indent := '' diff --git a/vlib/v/comptime/comptimeinfo.v b/vlib/v/comptime/comptimeinfo.v index 9a029bbb433fd6..58d72383fbadc2 100644 --- a/vlib/v/comptime/comptimeinfo.v +++ b/vlib/v/comptime/comptimeinfo.v @@ -333,6 +333,8 @@ pub mut: resolver IResolverType = DummyResolver{} // symbol table resolver table &ast.Table = unsafe { nil } + // loop id for loop distinction + comptime_loop_id int // $for inside_comptime_for bool type_map map[string]ast.Type diff --git a/vlib/v/gen/c/cgen.v b/vlib/v/gen/c/cgen.v index f692af6f5171bb..79f2f07a1178ed 100644 --- a/vlib/v/gen/c/cgen.v +++ b/vlib/v/gen/c/cgen.v @@ -174,6 +174,7 @@ mut: defer_ifdef string defer_profile_code string defer_vars []string + closure_structs []string str_types []StrType // types that need automatic str() generation generated_str_fns []StrType // types that already have a str() function str_fn_names shared []string // remove duplicate function names diff --git a/vlib/v/gen/c/comptime.v b/vlib/v/gen/c/comptime.v index 13b2cd480add45..84b54ac8845026 100644 --- a/vlib/v/gen/c/comptime.v +++ b/vlib/v/gen/c/comptime.v @@ -762,6 +762,7 @@ fn (mut g Gen) push_new_comptime_info() { comptime_for_method_var: g.comptime.comptime_for_method_var comptime_for_method: g.comptime.comptime_for_method comptime_for_method_ret_type: g.comptime.comptime_for_method_ret_type + comptime_loop_id: g.comptime.comptime_loop_id++ } } diff --git a/vlib/v/gen/c/fn.v b/vlib/v/gen/c/fn.v index 9d9c01c4f27dff..70511b36d23bf0 100644 --- a/vlib/v/gen/c/fn.v +++ b/vlib/v/gen/c/fn.v @@ -545,6 +545,10 @@ fn (mut g Gen) c_fn_name(node &ast.FnDecl) string { name = name.replace_each(c_fn_name_escape_seq) } } + if node.is_anon && g.comptime.comptime_for_method_var != '' + && node.scope.is_inherited_var('method') { + name = '${name}_${g.comptime.comptime_loop_id}' + } if node.language == .c { name = util.no_dots(name) } else { @@ -571,6 +575,18 @@ fn (mut g Gen) c_fn_name(node &ast.FnDecl) string { const closure_ctx = '_V_closure_ctx' +fn (mut g Gen) gen_closure_fn_name(node ast.AnonFn) string { + mut fn_name := node.decl.name + if node.decl.generic_names.len > 0 { + fn_name = g.generic_fn_name(g.cur_concrete_types, fn_name) + } + if node.inherited_vars.len > 0 && g.comptime.comptime_for_method_var != '' + && node.inherited_vars.any(it.name == 'method') { + fn_name += '_${g.comptime.comptime_loop_id}' + } + return fn_name +} + fn (mut g Gen) closure_ctx(node ast.FnDecl) string { mut fn_name := node.name if node.generic_names.len > 0 { @@ -581,12 +597,7 @@ fn (mut g Gen) closure_ctx(node ast.FnDecl) string { fn (mut g Gen) gen_anon_fn(mut node ast.AnonFn) { g.gen_anon_fn_decl(mut node) - mut fn_name := node.decl.name - - if node.decl.generic_names.len > 0 { - fn_name = g.generic_fn_name(g.cur_concrete_types, fn_name) - } - + fn_name := g.gen_closure_fn_name(node) if !node.decl.scope.has_inherited_vars() { g.write(fn_name) return @@ -652,10 +663,7 @@ fn (mut g Gen) gen_anon_fn(mut node ast.AnonFn) { } fn (mut g Gen) gen_anon_fn_decl(mut node ast.AnonFn) { - mut fn_name := node.decl.name - if node.decl.generic_names.len > 0 { - fn_name = g.generic_fn_name(g.cur_concrete_types, fn_name) - } + mut fn_name := g.gen_closure_fn_name(node) if node.has_gen[fn_name] { return } @@ -664,19 +672,22 @@ fn (mut g Gen) gen_anon_fn_decl(mut node ast.AnonFn) { builder.writeln('/*F*/') if node.inherited_vars.len > 0 { ctx_struct := g.closure_ctx(node.decl) - builder.writeln('${ctx_struct} {') - for var in node.inherited_vars { - var_sym := g.table.sym(var.typ) - if var_sym.info is ast.FnType { - sig := g.fn_var_signature(var_sym.info.func.return_type, var_sym.info.func.params.map(it.typ), - c_name(var.name)) - builder.writeln('\t' + sig + ';') - } else { - styp := g.typ(var.typ) - builder.writeln('\t${styp} ${c_name(var.name)};') + if ctx_struct !in g.closure_structs { + g.closure_structs << ctx_struct + builder.writeln('${ctx_struct} {') + for var in node.inherited_vars { + var_sym := g.table.sym(var.typ) + if var_sym.info is ast.FnType { + sig := g.fn_var_signature(var_sym.info.func.return_type, var_sym.info.func.params.map(it.typ), + c_name(var.name)) + builder.writeln('\t' + sig + ';') + } else { + styp := g.typ(var.typ) + builder.writeln('\t${styp} ${c_name(var.name)};') + } } + builder.writeln('};\n') } - builder.writeln('};\n') } pos := g.out.len was_anon_fn := g.anon_fn diff --git a/vlib/v/tests/comptime/comptime_call_method_closure_test.v b/vlib/v/tests/comptime/comptime_call_method_closure_test.v new file mode 100644 index 00000000000000..c711a355ffcdf4 --- /dev/null +++ b/vlib/v/tests/comptime/comptime_call_method_closure_test.v @@ -0,0 +1,22 @@ +struct Foo {} + +fn (f Foo) foo() string { + return 'foo' +} + +fn (f Foo) bar() string { + return 'bar' +} + +fn test_main() { + f := Foo{} + mut results := []string{} + $for method in Foo.methods { + x := fn [method, f] () string { + return f.$method() + } + results << x() + } + assert results[0] == 'foo' + assert results[1] == 'bar' +}