diff --git a/compiler/fn.v b/compiler/fn.v index 654677fd99..219e02020f 100644 --- a/compiler/fn.v +++ b/compiler/fn.v @@ -146,7 +146,18 @@ fn (p mut Parser) fn_decl() { p.clear_vars() // clear local vars every time a new fn is started p.fgen('fn ') //defer { p.fgenln('\n') } - mut f := Fn { + // If we are in the first pass, create a new function. + // In the second pass fetch the one we created. + /* + mut f := if p.first_pass { + Fn{ + mod: p.mod + is_public: p.tok == .key_pub + } + else { + } + */ + mut f := Fn{ mod: p.mod is_public: p.tok == .key_pub } @@ -185,7 +196,7 @@ fn (p mut Parser) fn_decl() { } // `(f *Foo)` instead of `(f mut Foo)` is a common mistake //if !p.builtin_mod && receiver_typ.contains('*') { - if receiver_typ.contains('*') { + if receiver_typ.ends_with('*') { t := receiver_typ.replace('*', '') p.error('use `($receiver_name mut $t)` instead of `($receiver_name *$t)`') } @@ -208,6 +219,7 @@ fn (p mut Parser) fn_decl() { f.args << receiver p.register_var(receiver) } + // +-/* methods if p.tok == .plus || p.tok == .minus || p.tok == .mul { f.name = p.tok.str() p.next() @@ -398,28 +410,7 @@ fn (p mut Parser) fn_decl() { // First pass? Skip the body for now // Look for generic calls. if !is_sig && !is_fn_header { - mut opened_scopes := 0 - mut closed_scopes := 0 - for { - if p.tok == .lcbr { - opened_scopes++ - } - if p.tok == .rcbr { - closed_scopes++ - } - // find `foo()` in function bodies and register generic types - // TODO - if p.tok.is_decl() { - break - } - // fn body ended, and a new fn attribute declaration like [live] is starting? - if closed_scopes > opened_scopes && p.prev_tok == .rcbr { - if p.tok == .lsbr { - break - } - } - p.next() - } + p.skip_fn_body() } // Live code reloading? Load all fns from .so if is_live && p.first_pass() && p.mod == 'main' { @@ -510,6 +501,35 @@ fn (p mut Parser) fn_decl() { } } +[inline] +// Skips the entire function's body in the first pass. +fn (p mut Parser) skip_fn_body() { + mut opened_scopes := 0 + mut closed_scopes := 0 + for { + if p.tok == .lcbr { + opened_scopes++ + } + if p.tok == .rcbr { + closed_scopes++ + } + // find `foo()` in function bodies and register generic types + // TODO + // ... + // Reached a declaration token? (fn, struct, const etc) Stop. + if p.tok.is_decl() { + break + } + // fn body ended, and a new fn attribute declaration like [live] is starting? + if closed_scopes > opened_scopes && p.prev_tok == .rcbr { + if p.tok == .lsbr { + break + } + } + p.next() + } +} + fn (p mut Parser) check_unused_variables() { for var in p.local_vars { if var.name == '' { @@ -894,6 +914,8 @@ fn (p mut Parser) fn_call_args(f mut Fn) &Fn { } got := typ expected := arg.typ + got_ptr := got.ends_with('*') + exp_ptr := expected.ends_with('*') // println('fn arg got="$got" exp="$expected"') if !p.check_types_no_throw(got, expected) { mut j := i @@ -912,18 +934,23 @@ fn (p mut Parser) fn_call_args(f mut Fn) &Fn { 'argument to `$f.name()`') } is_interface := p.table.is_interface(arg.typ) - // Add `&` or `*` before an argument? + // Automatically add `&` or `*` before an argument. + // V, unlike C and Go, simplifies this aspect: + // `foo(bar)` is allowed where `foo(&bar)` is expected. + // The argument is not mutable, so it won't be changed by the function. + // It doesn't matter whether it's passed by referencee or by value + // to the end user. if !is_interface { // Dereference - if got.ends_with('*') && !expected.ends_with('*') { + if got_ptr && !exp_ptr { p.cgen.set_placeholder(ph, '*') } // Reference // TODO ptr hacks. DOOM hacks, fix please. - if !got.ends_with('*') && expected.ends_with('*') && got != 'voidptr' { + if !got_ptr && exp_ptr && got != 'voidptr' { // Special case for mutable arrays. We can't `&` function results, // have to use `(array[]){ expr }` hack. - if expected.starts_with('array_') && expected.ends_with('*') { + if expected.starts_with('array_') && exp_ptr { p.cgen.set_placeholder(ph, '& /*111*/ (array[]){') p.gen('}[0] ') } @@ -937,9 +964,8 @@ fn (p mut Parser) fn_call_args(f mut Fn) &Fn { } } } - // interface? - if is_interface { - if !got.contains('*') { + else if is_interface { + if !got_ptr { p.cgen.set_placeholder(ph, '&') } // Pass all interface methods @@ -985,7 +1011,7 @@ fn (p mut Parser) fn_call_args(f mut Fn) &Fn { } // "fn (int, string) int" -fn (f Fn) typ_str() string { +fn (f &Fn) typ_str() string { mut sb := strings.new_builder(50) sb.write('fn (') for i, arg in f.args { @@ -1001,6 +1027,10 @@ fn (f Fn) typ_str() string { return sb.str() } +fn (f &Fn) v_definition() string { + return 'todo' +} + // f.args => "int a, string b" fn (f &Fn) str_args(table &Table) string { mut s := '' @@ -1056,3 +1086,12 @@ fn (p &Parser) find_misspelled_local_var(name string, min_match f32) string { } return if closest >= min_match { closest_var } else { '' } } + +fn (fns []Fn) contains(f Fn) bool { + for ff in fns { + if ff.name == f.name { + return true + } + } + return false +} diff --git a/compiler/table.v b/compiler/table.v index f7cda982c2..4bf4b4ca82 100644 --- a/compiler/table.v +++ b/compiler/table.v @@ -126,6 +126,7 @@ struct TypeNode { typ Type } +/* // For debugging types fn (t Type) str() string { mut s := 'type "$t.name" {' @@ -146,6 +147,7 @@ fn (t Type) str() string { s += '}\n' return s } +*/ const ( CReserved = [ @@ -321,6 +323,8 @@ fn (p mut Parser) register_global(name, typ string) { } } +// Only for module functions, not methods. +// That's why searching by fn name works. fn (t mut Table) register_fn(new_fn Fn) { t.fns[new_fn.name] = new_fn } @@ -469,14 +473,10 @@ fn (p mut Parser) add_method(type_name string, f Fn) { } // TODO table.typesmap[type_name].methods << f mut t := p.table.typesmap[type_name] - if type_name == 'str' { - println(t.methods.len) - } - + if f.name != 'str' && f in t.methods { + p.error('redefinition of method `${type_name}.$f.name`') + } t.methods << f - if type_name == 'str' { - println(t.methods.len) - } p.table.typesmap[type_name] = t } diff --git a/september.plan b/september.plan index 69d5efeed3..4b2e3dd816 100644 --- a/september.plan +++ b/september.plan @@ -12,7 +12,7 @@ - bring back vdoc and regenerate all module docs - optimize the parser (reduce map lookups) - cache vlib (right now it's re-compiled every time) -- fix openssl on older linux distros ++ fix openssl on older linux distros - chat.vlang.io - rewrite objective c code in v (ui_mac.m) - v ui for macos diff --git a/vlib/os/os_win.v b/vlib/os/os_win.v index 1ec3ee839b..ad9d88ef25 100644 --- a/vlib/os/os_win.v +++ b/vlib/os/os_win.v @@ -13,14 +13,14 @@ type HANDLE voidptr // win: FILETIME // https://docs.microsoft.com/en-us/windows/win32/api/minwinbase/ns-minwinbase-filetime -struct filetime { +struct Filetime { dwLowDateTime u32 dwHighDateTime u32 } // win: WIN32_FIND_DATA // https://docs.microsoft.com/en-us/windows/win32/api/minwinbase/ns-minwinbase-_win32_find_dataw -struct win32finddata { +struct Win32finddata { mut: dwFileAttributes u32 ftCreationTime filetime @@ -52,7 +52,7 @@ fn init_os_args(argc int, argv &byteptr) []string { pub fn ls(path string) []string { - mut find_file_data := win32finddata{} + mut find_file_data := Win32finddata{} mut dir_files := []string // We can also check if the handle is valid. but using dir_exists instead // h_find_dir := C.FindFirstFile(path.str, &find_file_data)