From 04e4018228653d73b5b3921585e8802f93ce96e5 Mon Sep 17 00:00:00 2001 From: Alexander Medvednikov Date: Tue, 24 Sep 2019 22:30:30 +0300 Subject: [PATCH] compiler: small fixes + some logic for freeing strings --- compiler/fn.v | 44 ++++++++++++++++++++++++++++++++++++-------- compiler/gen_c.v | 13 +++++++------ compiler/main.v | 10 +++++----- compiler/parser.v | 36 +++++++++++++++++++++++------------- compiler/scanner.v | 9 +++------ compiler/table.v | 5 +++-- september.plan | 1 - vlib/darwin/darwin.v | 5 +---- 8 files changed, 78 insertions(+), 45 deletions(-) diff --git a/compiler/fn.v b/compiler/fn.v index a1d67a75e1..8b0bb41b86 100644 --- a/compiler/fn.v +++ b/compiler/fn.v @@ -89,6 +89,17 @@ fn (p mut Parser) mark_var_changed(v Var) { p.local_vars[v.idx].is_changed = true } +fn (p mut Parser) mark_arg_moved(v Var) { + for i, arg in p.cur_fn.args { + if arg.name == v.name { + //println('setting f $p.cur_fn.name arg $arg.name to is_mut') + p.cur_fn.args[i].is_moved = true + break + } + } + p.table.fns[p.cur_fn.name] = p.cur_fn +} + fn (p mut Parser) known_var(name string) bool { _ = p.find_var(name) or { return false @@ -759,7 +770,7 @@ fn (p mut Parser) fn_args(f mut Fn) { if is_mut { typ += '*' } - v := Var { + v := Var{ name: name typ: typ is_arg: true @@ -775,7 +786,7 @@ fn (p mut Parser) fn_args(f mut Fn) { p.next() } if p.tok == .dotdot { - f.args << Var { + f.args << Var{ name: '..' } p.next() @@ -818,10 +829,9 @@ fn (p mut Parser) fn_call_args(f mut Fn) &Fn { '_panic_debug ($p.scanner.line_nr, tos2((byte *)"$file_path"), tos2((byte *)"$mod_name"), tos2((byte *)"$fn_name"), ' )) } - // Receiver - first arg for i, arg in f.args { - // println('$i) arg=$arg.name') - // Skip receiver, because it was already generated in the expression + // Receiver is the first arg + // Skip the receiver, because it was already generated in the expression if i == 0 && f.is_method { if f.args.len > 1 && !p.is_js { p.gen(',') @@ -865,7 +875,14 @@ fn (p mut Parser) fn_call_args(f mut Fn) &Fn { } } p.expected_type = arg.typ + clone := p.pref.autofree && arg.typ == 'string' && arg.is_moved && p.mod != 'builtin' + if clone { + p.gen('/*YY f=$f.name arg=$arg.name is_moved=$arg.is_moved*/string_clone(') + } mut typ := p.bool_expression() + if clone { + p.gen(')') + } // Optimize `println`: replace it with `printf` to avoid extra allocations and // function calls. // `println(777)` => `printf("%d\n", 777)` @@ -927,9 +944,20 @@ fn (p mut Parser) fn_call_args(f mut Fn) &Fn { expected := arg.typ // println('fn arg got="$got" exp="$expected"') if !p.check_types_no_throw(got, expected) { - mut err := 'Fn "$f.name" wrong arg #${i+1}. ' - err += 'Expected "$arg.typ" ($arg.name) but got "$typ"' - p.error(err) + mut j := i + if f.is_method { + j-- + } + mut nr := '${i+1}th' + if j == 0 { + nr = 'first' + } else if j == 1 { + nr = 'second' + } else if j == 2 { + nr = 'third' + } + p.error('cannot use type `$typ` as type `$arg.typ` in $nr ' + + 'argument to `$f.name()`') } is_interface := p.table.is_interface(arg.typ) // Add `&` or `*` before an argument? diff --git a/compiler/gen_c.v b/compiler/gen_c.v index 7b054e0809..a9595c153d 100644 --- a/compiler/gen_c.v +++ b/compiler/gen_c.v @@ -479,22 +479,23 @@ fn type_default(typ string) string { return '{0}' } -fn (p mut Parser) gen_array_push(ph int, typ, expr_type, tmp, tmp_typ string) { +fn (p mut Parser) gen_array_push(ph int, typ, expr_type, tmp, elm_type string) { + // Two arrays of the same type? push_array := typ == expr_type if push_array { p.cgen.set_placeholder(ph, '_PUSH_MANY(&' ) p.gen('), $tmp, $typ)') - } else { - p.check_types(expr_type, tmp_typ) + } else { + p.check_types(expr_type, elm_type) // Pass tmp var info to the _PUSH macro // Prepend tmp initialisation and push call // Don't dereference if it's already a mutable array argument (`fn foo(mut []int)`) push_call := if typ.contains('*'){'_PUSH('} else { '_PUSH(&'} p.cgen.set_placeholder(ph, push_call) - if tmp_typ.ends_with('*') { - p.gen('), $tmp, ${tmp_typ.left(tmp_typ.len - 1)})') + if elm_type.ends_with('*') { + p.gen('), $tmp, ${elm_type.left(elm_type.len - 1)})') } else { - p.gen('), $tmp, $tmp_typ)') + p.gen('), $tmp, $elm_type)') } } } diff --git a/compiler/main.v b/compiler/main.v index 32a7e07116..4b0171efba 100644 --- a/compiler/main.v +++ b/compiler/main.v @@ -241,7 +241,7 @@ fn (v mut V) compile() { for file in v.files { mut p := v.new_parser(file) p.parse(.decl) - if p.pref.autofree { p.scanner.text.free() free(p.scanner) } + //if p.pref.autofree { p.scanner.text.free() free(p.scanner) } } // Main pass cgen.pass = Pass.main @@ -307,7 +307,7 @@ fn (v mut V) compile() { for file in v.files { mut p := v.new_parser(file) p.parse(.main) - if p.pref.autofree { p.scanner.text.free() free(p.scanner) } + //if p.pref.autofree { p.scanner.text.free() free(p.scanner) } // p.g.gen_x64() // Format all files (don't format automatically generated vlib headers) if !v.pref.nofmt && !file.contains('/vlib/') { @@ -569,13 +569,13 @@ fn (v mut V) add_v_files_to_compile() { for file in v.files { mut p := v.new_parser(file) p.parse(.imports) - if p.pref.autofree { p.scanner.text.free() free(p.scanner) } + //if p.pref.autofree { p.scanner.text.free() free(p.scanner) } } // Parse user imports for file in user_files { mut p := v.new_parser(file) p.parse(.imports) - if p.pref.autofree { p.scanner.text.free() free(p.scanner) } + //if p.pref.autofree { p.scanner.text.free() free(p.scanner) } } // Parse lib imports /* @@ -614,7 +614,7 @@ fn (v mut V) add_v_files_to_compile() { for file in vfiles { mut p := v.new_parser(file) p.parse(.imports) - if p.pref.autofree { p.scanner.text.free() free(p.scanner) } + //if p.pref.autofree { p.scanner.text.free() free(p.scanner) } } } if v.pref.is_verbose { diff --git a/compiler/parser.v b/compiler/parser.v index fc282a7690..009b454698 100644 --- a/compiler/parser.v +++ b/compiler/parser.v @@ -13,9 +13,10 @@ struct Parser { file_path string // "/home/user/hello.v" file_name string // "hello.v" file_platform string // ".v", "_win.v", "_nix.v", "_mac.v", "_lin.v" ... - file_pcguard string // When p.file_pcguard != '', it contains a - // C ifdef guard clause that must be put before - // the #include directives in the parsed .v file + // When p.file_pcguard != '', it contains a + // C ifdef guard clause that must be put before + // the #include directives in the parsed .v file + file_pcguard string v &V pref &Preferences // Preferences shared from V struct mut: @@ -36,7 +37,7 @@ mut: expr_var Var has_immutable_field bool first_immutable_field Var - assigned_type string + assigned_type string // non-empty if we are in an assignment expression expected_type string tmp_cnt int is_script bool @@ -74,7 +75,7 @@ mut: } const ( - EmptyFn = Fn { } + EmptyFn = Fn{} MainFn= Fn{name:'main'} ) @@ -712,6 +713,7 @@ fn (p mut Parser) enum_decl(_enum_name string) { if p.tok == .comma { p.next() } + // !!!! NAME free p.table.register_const(name, enum_name, p.mod) val++ } @@ -968,7 +970,7 @@ fn (p mut Parser) get_type() string { typ = p.lit } else { - if warn { + if warn && p.mod != 'ui' { p.warn('use `&Foo` instead of `*Foo`') } // Module specified? (e.g. gx.Image) @@ -1119,7 +1121,7 @@ fn (p mut Parser) close_scope() { break } // Clean up memory, only do this if -autofree was passed for now - if p.pref.autofree && v.is_alloc && !p.pref.is_test { + if p.pref.autofree && v.is_alloc { // && !p.pref.is_test { mut free_fn := 'free' if v.typ.starts_with('array_') { free_fn = 'v_array_free' @@ -1136,6 +1138,7 @@ fn (p mut Parser) close_scope() { continue } if p.returns { + // Don't free a variable that's being returned if !v.is_returned && v.typ != 'FILE*' { //!v.is_c { prev_line := p.cgen.lines[p.cgen.lines.len-2] p.cgen.lines[p.cgen.lines.len-2] = @@ -1608,6 +1611,11 @@ fn (p mut Parser) name_expr() string { else if deref { p.gen('*') } + if p.pref.autofree && v.typ == 'string' && v.is_arg && + p.assigned_type == 'string' { + p.warn('setting moved ' + v.typ) + p.mark_arg_moved(v) + } mut typ := p.var_expr(v) // *var if deref { @@ -2108,11 +2116,7 @@ fn (p mut Parser) index_expr(typ_ string, fn_ph int) string { } // TODO move this from index_expr() // TODO if p.tok in ... - // if p.tok in [.assign, .plus_assign, .minus_assign] - if (p.tok == .assign && !p.is_sql) || p.tok == .plus_assign || p.tok == .minus_assign || - p.tok == .mult_assign || p.tok == .div_assign || p.tok == .xor_assign || p.tok == .mod_assign || - p.tok == .or_assign || p.tok == .and_assign || p.tok == .righ_shift_assign || - p.tok == .left_shift_assign { + if (p.tok == .assign && !p.is_sql) || p.tok.is_assign() { if is_indexer && is_str && !p.builtin_mod { p.error('strings are immutable') } @@ -2219,8 +2223,14 @@ fn (p mut Parser) expression() string { if !p.expr_var.is_changed { p.mark_var_changed(p.expr_var) } + p.gen('/*typ = $typ tmp_typ=$tmp_typ*/') + ph_clone := p.cgen.add_placeholder() expr_type := p.expression() - // Two arrays of the same type? + // Need to clone the string when appending it to an array? + if p.pref.autofree && typ == 'array_string' && expr_type == 'string' { + p.cgen.set_placeholder(ph_clone, 'string_clone(') + p.gen(')') + } p.gen_array_push(ph, typ, expr_type, tmp, tmp_typ) return 'void' } diff --git a/compiler/scanner.v b/compiler/scanner.v index fe9991b0a6..bbc4c0e41b 100644 --- a/compiler/scanner.v +++ b/compiler/scanner.v @@ -57,16 +57,12 @@ fn new_scanner(file_path string) &Scanner { } } - text := raw_text - - scanner := &Scanner { + return &Scanner { file_path: file_path - text: text + text: raw_text fmt_out: strings.new_builder(1000) should_print_line_on_error: true } - - return scanner } @@ -670,6 +666,7 @@ fn (s &Scanner) error(msg string) { // and jump to their source with a keyboard shortcut. // Using only the filename leads to inability of IDE/editors // to find the source file, when it is in another folder. + //println('${s.file_path}:${s.line_nr + 1}:${column+1}: $msg') println('${fullpath}:${s.line_nr + 1}:${column+1}: $msg') exit(1) } diff --git a/compiler/table.v b/compiler/table.v index 4e3c44ad8f..172ec57329 100644 --- a/compiler/table.v +++ b/compiler/table.v @@ -80,7 +80,7 @@ mut: is_changed bool scope_level int is_c bool // todo remove once `typ` is `Type`, not string - moved bool + is_moved bool scanner_pos ScannerPos // TODO: use only scanner_pos, remove line_nr line_nr int } @@ -321,7 +321,8 @@ fn (table &Table) known_type(typ_ string) bool { } fn (table &Table) known_type_fast(t &Type) bool { - return t.name.len > 0 && !t.is_placeholder + return t.name != '' && !t.is_placeholder + } fn (t &Table) find_fn(name string) ?Fn { diff --git a/september.plan b/september.plan index 5989b7cd6f..c7cb654d22 100644 --- a/september.plan +++ b/september.plan @@ -26,7 +26,6 @@ - ui demo: calculator - declarative ui with hot reload (similar to swiftui) - "building a simple blog with vweb" tutorial + youtube video -- webassembly backend (via emscripten) + javascript backend - new playground with a v compiler running in the browser + o(log n) type lookup diff --git a/vlib/darwin/darwin.v b/vlib/darwin/darwin.v index 5b79ca7dc6..849023e7f1 100644 --- a/vlib/darwin/darwin.v +++ b/vlib/darwin/darwin.v @@ -6,8 +6,7 @@ module darwin struct C.NSString { } // macOS and iOS helpers -pub fn nsstring(s string) *NSString { - // #return @"" ; +pub fn nsstring(s string) *C.NSString { // println('ns $s len=$s.len') # return [ [ NSString alloc ] initWithBytesNoCopy:s.str length:s.len # encoding:NSUTF8StringEncoding freeWhenDone: false]; @@ -16,7 +15,5 @@ pub fn nsstring(s string) *NSString { //ns := C.alloc_NSString() //return ns.initWithBytesNoCopy(s.str, length: s.len, //encoding: NSUTF8StringEncoding, freeWhenDone: false) - - }