Skip to content

Commit

Permalink
cgen: add codegen for auto free methods for interface type (#22555)
Browse files Browse the repository at this point in the history
  • Loading branch information
felipensp authored Oct 21, 2024
1 parent 988f8b4 commit de46d9d
Show file tree
Hide file tree
Showing 6 changed files with 86 additions and 19 deletions.
11 changes: 7 additions & 4 deletions vlib/v/gen/c/assign.v
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
59 changes: 50 additions & 9 deletions vlib/v/gen/c/auto_free_methods.v
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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
}

Expand All @@ -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
}

Expand All @@ -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()
Expand All @@ -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)
Expand All @@ -81,21 +119,16 @@ 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 {
g.gen_free_method(field.typ)
}
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)
Expand Down Expand Up @@ -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)
Expand Down
2 changes: 1 addition & 1 deletion vlib/v/gen/c/cgen.v
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
6 changes: 1 addition & 5 deletions vlib/v/gen/c/fn.v
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
7 changes: 7 additions & 0 deletions vlib/v/gen/c/testdata/interface_auto_free.c.must_have
Original file line number Diff line number Diff line change
@@ -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; }
}
20 changes: 20 additions & 0 deletions vlib/v/gen/c/testdata/interface_auto_free.vv
Original file line number Diff line number Diff line change
@@ -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{})
}

0 comments on commit de46d9d

Please sign in to comment.