From 46a5c487c17ad87d1937e998b79a8666d5963956 Mon Sep 17 00:00:00 2001 From: Delyan Angelov Date: Fri, 8 Jan 2021 17:24:42 +0200 Subject: [PATCH] parser,fmt: implement `[manualfree] module abc` for opting out *all* fns in a given .v from autofree --- CHANGELOG.md | 2 +- vlib/v/ast/ast.v | 1 + vlib/v/fmt/fmt.v | 1 + vlib/v/fmt/tests/manualfree_keep.v | 18 ++++++++++++++++++ vlib/v/parser/fn.v | 2 +- vlib/v/parser/parser.v | 24 ++++++++++++++++++++++++ vlib/v/table/attr.v | 3 +++ 7 files changed, 49 insertions(+), 2 deletions(-) create mode 100644 vlib/v/fmt/tests/manualfree_keep.v diff --git a/CHANGELOG.md b/CHANGELOG.md index 39540f10ee..d0681ce19d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,7 +7,7 @@ - `byte.str()` has been fixed and works like with all other numbers. `byte.ascii_str()` has been added. - Smart cast in for loops: `for mut x is string {}`. - `[noinit]` struct attribute to disallow direct struct initialization with `Foo{}`. -- `[manualfree]` attribute for functions, that want to do their own memory management. +- support `[manualfree] fn f1(){}` and `[manualfree] module m1`, for functions doing their own memory management. ## V 0.2.1 *30 Dec 2020* diff --git a/vlib/v/ast/ast.v b/vlib/v/ast/ast.v index 0309d64615..f1e073b59d 100644 --- a/vlib/v/ast/ast.v +++ b/vlib/v/ast/ast.v @@ -131,6 +131,7 @@ pub fn (e &SelectorExpr) root_ident() Ident { pub struct Module { pub: name string + attrs []table.Attr pos token.Position is_skipped bool // module main can be skipped in single file programs } diff --git a/vlib/v/fmt/fmt.v b/vlib/v/fmt/fmt.v index 85364bdae8..617f843db4 100644 --- a/vlib/v/fmt/fmt.v +++ b/vlib/v/fmt/fmt.v @@ -193,6 +193,7 @@ pub fn (mut f Fmt) mod(mod ast.Module) { if mod.is_skipped { return } + f.attrs(mod.attrs) f.writeln('module $mod.name\n') } diff --git a/vlib/v/fmt/tests/manualfree_keep.v b/vlib/v/fmt/tests/manualfree_keep.v new file mode 100644 index 0000000000..98de3f1d0d --- /dev/null +++ b/vlib/v/fmt/tests/manualfree_keep.v @@ -0,0 +1,18 @@ +[manualfree] +module main + +fn abc() { + x := 'abc should be autofreed' + println(x) +} + +[manualfree] +fn xyz() { + x := 'xyz should do its own memory management' + println(x) +} + +fn main() { + abc() + xyz() +} diff --git a/vlib/v/parser/fn.v b/vlib/v/parser/fn.v index 738aa9a65e..20e9bbe543 100644 --- a/vlib/v/parser/fn.v +++ b/vlib/v/parser/fn.v @@ -156,7 +156,7 @@ pub fn (mut p Parser) call_args() []ast.CallArg { fn (mut p Parser) fn_decl() ast.FnDecl { p.top_level_statement_start() start_pos := p.tok.position() - is_manualfree := p.attrs.contains('manualfree') + is_manualfree := p.is_manualfree || p.attrs.contains('manualfree') is_deprecated := p.attrs.contains('deprecated') is_direct_arr := p.attrs.contains('direct_array_access') mut is_unsafe := p.attrs.contains('unsafe') diff --git a/vlib/v/parser/parser.v b/vlib/v/parser/parser.v index d1cfd5e352..575a0c52a9 100644 --- a/vlib/v/parser/parser.v +++ b/vlib/v/parser/parser.v @@ -45,6 +45,7 @@ mut: or_is_handled bool // ignore `or` in this expression builtin_mod bool // are we in the `builtin` module? mod string // current module name + is_manualfree bool // true when `[manualfree] module abc`, makes *all* fns in the current .v file, opt out of autofree attrs []table.Attr // attributes before next decl stmt expr_mod string // for constructing full type names in parse_type() scope &ast.Scope @@ -817,10 +818,12 @@ fn (mut p Parser) attributes() { } fn (mut p Parser) parse_attr() table.Attr { + apos := p.prev_tok.position() if p.tok.kind == .key_unsafe { p.next() return table.Attr{ name: 'unsafe' + pos: apos.extend(p.tok.position()) } } mut is_ctdefine := false @@ -862,6 +865,7 @@ fn (mut p Parser) parse_attr() table.Attr { is_ctdefine: is_ctdefine arg: arg is_string_arg: is_string_arg + pos: apos.extend(p.tok.position()) } } @@ -1670,10 +1674,16 @@ fn (mut p Parser) parse_number_literal() ast.Expr { } fn (mut p Parser) module_decl() ast.Module { + mut module_attrs := []table.Attr{} + if p.tok.kind == .lsbr { + p.attributes() + module_attrs = p.attrs + } mut name := 'main' is_skipped := p.tok.kind != .key_module mut module_pos := token.Position{} if !is_skipped { + p.attrs = [] module_pos = p.tok.position() p.next() mut pos := p.tok.position() @@ -1710,8 +1720,22 @@ fn (mut p Parser) module_decl() ast.Module { } p.mod = full_mod p.builtin_mod = p.mod == 'builtin' + if !is_skipped { + for ma in module_attrs { + match ma.name { + 'manualfree' { + p.is_manualfree = true + } + else { + p.error_with_pos('unknown module attribute `[$ma.name]`', ma.pos) + return ast.Module{} + } + } + } + } return ast.Module{ name: full_mod + attrs: module_attrs is_skipped: is_skipped pos: module_pos } diff --git a/vlib/v/table/attr.v b/vlib/v/table/attr.v index 91109e7007..8eb93a608e 100644 --- a/vlib/v/table/attr.v +++ b/vlib/v/table/attr.v @@ -3,6 +3,8 @@ // that can be found in the LICENSE file. module table +import v.token + // e.g. `[unsafe]` pub struct Attr { pub: @@ -11,6 +13,7 @@ pub: is_ctdefine bool // [if name] arg string // [name: arg] is_string_arg bool // [name: 'arg'] + pos token.Position } // no square brackets