From de46d9d39520539e4f14f70619beead78df04608 Mon Sep 17 00:00:00 2001 From: Felipe Pena Date: Mon, 21 Oct 2024 05:09:33 -0300 Subject: [PATCH] cgen: add codegen for auto free methods for interface type (#22555) --- vlib/v/gen/c/assign.v | 11 ++-- vlib/v/gen/c/auto_free_methods.v | 59 ++++++++++++++++--- vlib/v/gen/c/cgen.v | 2 +- vlib/v/gen/c/fn.v | 6 +- .../testdata/interface_auto_free.c.must_have | 7 +++ vlib/v/gen/c/testdata/interface_auto_free.vv | 20 +++++++ 6 files changed, 86 insertions(+), 19 deletions(-) create mode 100644 vlib/v/gen/c/testdata/interface_auto_free.c.must_have create mode 100644 vlib/v/gen/c/testdata/interface_auto_free.vv diff --git a/vlib/v/gen/c/assign.v b/vlib/v/gen/c/assign.v index 18e3694ce..557f20641 100644 --- a/vlib/v/gen/c/assign.v +++ b/vlib/v/gen/c/assign.v @@ -723,10 +723,13 @@ fn (mut g Gen) assign_stmt(node_ ast.AssignStmt) { g.write(', ') } mut cloned := false - if g.is_autofree && right_sym.kind in [.array, .string] - && !unwrapped_val_type.has_flag(.shared_f) { - if g.gen_clone_assignment(var_type, val, unwrapped_val_type, false) { - cloned = true + if g.is_autofree { + if right_sym.kind in [.array, .string] && !unwrapped_val_type.has_flag(.shared_f) { + if g.gen_clone_assignment(var_type, val, unwrapped_val_type, false) { + cloned = true + } + } else if right_sym.info is ast.Interface && var_type != ast.error_type { + g.register_free_method(var_type) } } if !cloned { diff --git a/vlib/v/gen/c/auto_free_methods.v b/vlib/v/gen/c/auto_free_methods.v index 18606ce10..24fcea577 100644 --- a/vlib/v/gen/c/auto_free_methods.v +++ b/vlib/v/gen/c/auto_free_methods.v @@ -5,8 +5,19 @@ module c import v.ast import strings +@[inline] +fn (mut g Gen) register_free_method(typ ast.Type) { + if typ.has_flag(.shared_f) { + g.get_free_method(typ.clear_flag(.shared_f).set_nr_muls(0)) + } else { + g.get_free_method(typ) + } +} + fn (mut g Gen) get_free_method(typ ast.Type) string { - g.autofree_methods[typ] = true + if typ in g.autofree_methods { + return g.autofree_methods[typ] + } mut sym := g.table.sym(g.unwrap_generic(typ)) if mut sym.info is ast.Alias { if sym.info.is_import { @@ -16,8 +27,10 @@ fn (mut g Gen) get_free_method(typ ast.Type) string { styp := g.typ(typ).replace('*', '') fn_name := styp_to_free_fn_name(styp) if sym.has_method_with_generic_parent('free') { + g.autofree_methods[typ] = fn_name return fn_name } + g.autofree_methods[typ] = fn_name return fn_name } @@ -43,7 +56,7 @@ fn (mut g Gen) gen_free_method(typ ast.Type) string { sym = g.table.sym(sym.info.parent_type) } } - if sym.has_method_with_generic_parent('free') { + if sym.kind != .interface && sym.has_method_with_generic_parent('free') { return fn_name } @@ -57,6 +70,9 @@ fn (mut g Gen) gen_free_method(typ ast.Type) string { ast.Map { g.gen_free_for_map(objtyp, sym.info, styp, fn_name) } + ast.Interface { + g.gen_free_for_interface(sym, sym.info, styp, fn_name) + } else { println(g.table.type_str(typ)) // print_backtrace() @@ -67,6 +83,28 @@ fn (mut g Gen) gen_free_method(typ ast.Type) string { return fn_name } +fn (mut g Gen) gen_free_for_interface(sym ast.TypeSymbol, info ast.Interface, styp string, fn_name string) { + g.definitions.writeln('${g.static_modifier} void ${fn_name}(${styp}* it); // auto') + mut fn_builder := strings.new_builder(128) + defer { + g.auto_fn_definitions << fn_builder.str() + } + fn_builder.writeln('${g.static_modifier} void ${fn_name}(${styp}* it) {') + for t in info.types { + typ_ := g.unwrap_generic(t) + sub_sym := g.table.sym(typ_) + if sub_sym.kind !in [.string, .array, .map, .struct] { + continue + } + if !sub_sym.has_method_with_generic_parent('free') { + continue + } + type_styp := g.gen_type_name_for_free_call(typ_) + fn_builder.writeln('\tif (it->_typ == _${sym.cname}_${sub_sym.cname}_index) { ${type_styp}_free(it->_${sub_sym.cname}); return; }') + } + fn_builder.writeln('}') +} + fn (mut g Gen) gen_free_for_struct(typ ast.Type, info ast.Struct, styp string, fn_name string) { g.definitions.writeln('${g.static_modifier} void ${fn_name}(${styp}* it); // auto') mut fn_builder := strings.new_builder(128) @@ -81,13 +119,8 @@ fn (mut g Gen) gen_free_for_struct(typ ast.Type, info ast.Struct, styp string, f if sym.kind !in [.string, .array, .map, .struct] { continue } - mut field_styp := g.typ(field.typ.set_nr_muls(0).clear_flag(.option)).replace('*', - '') - is_shared := field_styp.starts_with('__shared') + field_styp := g.gen_type_name_for_free_call(field.typ) is_struct_option := typ.has_flag(.option) - if is_shared { - field_styp = field_styp.all_after('__shared__') - } field_styp_fn_name := if sym.has_method('free') { '${field_styp}_free' } else { @@ -95,7 +128,7 @@ fn (mut g Gen) gen_free_for_struct(typ ast.Type, info ast.Struct, styp string, f } is_field_option := field.typ.has_flag(.option) expects_opt := field_styp_fn_name.starts_with('_option_') - if is_shared { + if field.typ.has_flag(.shared_f) { fn_builder.writeln('\t${field_styp_fn_name}(&(it->${field_name}->val));') } else if is_struct_option { opt_styp := g.base_type(typ) @@ -126,6 +159,14 @@ fn (mut g Gen) gen_free_for_struct(typ ast.Type, info ast.Struct, styp string, f fn_builder.writeln('}') } +fn (mut g Gen) gen_type_name_for_free_call(typ ast.Type) string { + mut styp := g.typ(typ.set_nr_muls(0).clear_flag(.option)).replace('*', '') + if styp.starts_with('__shared') { + styp = styp.all_after('__shared__') + } + return styp +} + fn (mut g Gen) gen_free_for_array(info ast.Array, styp string, fn_name string) { g.definitions.writeln('${g.static_modifier} void ${fn_name}(${styp}* it); // auto') mut fn_builder := strings.new_builder(128) diff --git a/vlib/v/gen/c/cgen.v b/vlib/v/gen/c/cgen.v index e3d7c0b2e..e94afb358 100644 --- a/vlib/v/gen/c/cgen.v +++ b/vlib/v/gen/c/cgen.v @@ -245,7 +245,7 @@ mut: cur_fn &ast.FnDecl = unsafe { nil } // same here cur_lock ast.LockExpr cur_struct_init_typ ast.Type - autofree_methods map[ast.Type]bool + autofree_methods map[ast.Type]string generated_free_methods map[ast.Type]bool autofree_scope_stmts []string use_segfault_handler bool = true diff --git a/vlib/v/gen/c/fn.v b/vlib/v/gen/c/fn.v index fcdbf737f..c8269e160 100644 --- a/vlib/v/gen/c/fn.v +++ b/vlib/v/gen/c/fn.v @@ -1734,11 +1734,7 @@ fn (mut g Gen) method_call(node ast.CallExpr) { return } } else if node.name == 'free' { - mut rec_type := node.receiver_type - if rec_type.has_flag(.shared_f) { - rec_type = rec_type.clear_flag(.shared_f).set_nr_muls(0) - } - g.get_free_method(rec_type) + g.register_free_method(node.receiver_type) is_free_method = true } mut cast_n := 0 diff --git a/vlib/v/gen/c/testdata/interface_auto_free.c.must_have b/vlib/v/gen/c/testdata/interface_auto_free.c.must_have new file mode 100644 index 000000000..41d8ea445 --- /dev/null +++ b/vlib/v/gen/c/testdata/interface_auto_free.c.must_have @@ -0,0 +1,7 @@ +void main__IFoo_free(main__IFoo* it) { + if (it->_typ == _main__IFoo_main__Foo_index) { main__Foo_free(it->_main__Foo); return; } + if (it->_typ == _main__IFoo_array_index) { array_free(it->_array); return; } + if (it->_typ == _main__IFoo_map_index) { map_free(it->_map); return; } + if (it->_typ == _main__IFoo_VAssertMetaInfo_index) { VAssertMetaInfo_free(it->_VAssertMetaInfo); return; } + if (it->_typ == _main__IFoo_MessageError_index) { MessageError_free(it->_MessageError); return; } +} \ No newline at end of file diff --git a/vlib/v/gen/c/testdata/interface_auto_free.vv b/vlib/v/gen/c/testdata/interface_auto_free.vv new file mode 100644 index 000000000..c33f61558 --- /dev/null +++ b/vlib/v/gen/c/testdata/interface_auto_free.vv @@ -0,0 +1,20 @@ +// vtest vflags: -autofree +module main + +interface IFoo { + free() +} + +struct Bar { + a int +} + +struct Foo implements IFoo { + Bar +} + +fn (f &Foo) free() {} + +fn main() { + a := IFoo(Foo{}) +} -- 2.39.5