diff --git a/vlib/v/ast/ast.v b/vlib/v/ast/ast.v index ca59f289a8..c864c4f290 100644 --- a/vlib/v/ast/ast.v +++ b/vlib/v/ast/ast.v @@ -233,16 +233,10 @@ pub mut: syms []ImportSymbol } -pub enum ImportSymbolKind { - fn_ - type_ -} - pub struct ImportSymbol { pub: pos token.Position name string - kind ImportSymbolKind } pub struct AnonFn { @@ -406,16 +400,17 @@ pub mut: pub struct File { pub: - path string - mod Module - global_scope &Scope + path string + mod Module + global_scope &Scope pub mut: - scope &Scope - stmts []Stmt - imports []Import - errors []errors.Error - warnings []errors.Warning - generic_fns []&FnDecl + scope &Scope + stmts []Stmt + imports []Import + imported_symbols map[string]string // 'Type' => 'module.Type' + errors []errors.Error + warnings []errors.Warning + generic_fns []&FnDecl } pub struct IdentFn { diff --git a/vlib/v/checker/check_types.v b/vlib/v/checker/check_types.v index 17ced92db0..75a9c39dcf 100644 --- a/vlib/v/checker/check_types.v +++ b/vlib/v/checker/check_types.v @@ -89,23 +89,11 @@ pub fn (mut c Checker) check_basic(got table.Type, expected table.Type) bool { if got_idx == table.array_type_idx || exp_idx == table.array_type_idx { return true } - if got_type_sym.kind == .array && exp_type_sym.kind == .array { - // TODO - // accept [] when an expected type is an array - if got_type_sym.name == 'array_void' { - return true - } - // if elem_type is an alias, check it - // TODO: think about recursion, how many levels of alias can there be? - got_info := got_type_sym.info as table.Array - exp_info := exp_type_sym.info as table.Array - got_elem_sym := c.table.get_type_symbol(got_info.elem_type) - exp_elem_sym := c.table.get_type_symbol(exp_info.elem_type) - if (got_elem_sym.kind == .alias || - exp_elem_sym.kind == .alias) && - c.check_basic(got_info.elem_type, exp_info.elem_type) { - return true - } + // TODO + // accept [] when an expected type is an array + if got_type_sym.kind == .array && + exp_type_sym.kind == .array && got_type_sym.name == 'array_void' { + return true } // type alias if (got_type_sym.kind == .alias && diff --git a/vlib/v/checker/checker.v b/vlib/v/checker/checker.v index fdbdc08095..a4919c7824 100644 --- a/vlib/v/checker/checker.v +++ b/vlib/v/checker/checker.v @@ -2753,20 +2753,28 @@ fn (mut c Checker) hash_stmt(mut node ast.HashStmt) { fn (mut c Checker) import_stmt(imp ast.Import) { for sym in imp.syms { name := '${imp.mod}.$sym.name' - if sym.kind == .fn_ { - c.table.find_fn(name) or { - c.error('module `$imp.mod` has no public fn named `${sym.name}()`', sym.pos) - } - } - if sym.kind == .type_ { + if sym.name[0].is_capital() { if type_sym := c.table.find_type(name) { - if type_sym.kind == .placeholder || !type_sym.is_public { - c.error('module `$imp.mod` has no public type `$sym.name{}`', sym.pos) + if type_sym.kind != .placeholder { + if !type_sym.is_public { + c.error('module `$imp.mod` type `$sym.name` is private', sym.pos) + } + continue } - } else { - c.error('module `$imp.mod` has no public type `$sym.name{}`', sym.pos) } + c.error('module `$imp.mod` has no type `$sym.name`', sym.pos) + continue } + if func := c.table.find_fn(name) { + if !func.is_pub { + c.error('module `$imp.mod` function `${sym.name}()` is private', sym.pos) + } + continue + } + if _ := c.file.global_scope.find_const(name) { + continue + } + c.error('module `$imp.mod` has no constant or function `$sym.name`', sym.pos) } } @@ -3308,9 +3316,13 @@ pub fn (mut c Checker) ident(mut ident ast.Ident) table.Type { else {} } } - // prepend mod to look for fn call or const mut name := ident.name - if !name.contains('.') && ident.mod != 'builtin' { + // check for imported symbol + if name in c.file.imported_symbols { + name = c.file.imported_symbols[name] + } + // prepend mod to look for fn call or const + else if !name.contains('.') && ident.mod != 'builtin' { name = '${ident.mod}.$ident.name' } if obj := c.file.global_scope.find(name) { diff --git a/vlib/v/checker/tests/import_symbol_fn_err.out b/vlib/v/checker/tests/import_symbol_fn_err.out index 96c9e79b20..5d74a05ec0 100644 --- a/vlib/v/checker/tests/import_symbol_fn_err.out +++ b/vlib/v/checker/tests/import_symbol_fn_err.out @@ -1,4 +1,4 @@ -vlib/v/checker/tests/import_symbol_fn_err.vv:1:17: error: module `crypto` has no public fn named `userper()` +vlib/v/checker/tests/import_symbol_fn_err.vv:1:17: error: module `crypto` has no constant or function `userper` 1 | import crypto { userper } | ~~~~~~~ 2 | fn main() { diff --git a/vlib/v/checker/tests/import_symbol_fn_private_err.out b/vlib/v/checker/tests/import_symbol_fn_private_err.out new file mode 100644 index 0000000000..1ddc4f460c --- /dev/null +++ b/vlib/v/checker/tests/import_symbol_fn_private_err.out @@ -0,0 +1,11 @@ +vlib/v/checker/tests/import_symbol_fn_private_err.vv:1:20: error: module `time` function `since()` is private + 1 | import time { now, since } + | ~~~~~ + 2 | fn main() { + 3 | since(now()) +vlib/v/checker/tests/import_symbol_fn_private_err.vv:3:3: error: function `time.since` is private. curmod=main fmod=time + 1 | import time { now, since } + 2 | fn main() { + 3 | since(now()) + | ~~~~~~~~~~~~ + 4 | } diff --git a/vlib/v/checker/tests/import_symbol_fn_private_err.vv b/vlib/v/checker/tests/import_symbol_fn_private_err.vv new file mode 100644 index 0000000000..9c959e89b2 --- /dev/null +++ b/vlib/v/checker/tests/import_symbol_fn_private_err.vv @@ -0,0 +1,4 @@ +import time { now, since } +fn main() { + since(now()) +} diff --git a/vlib/v/checker/tests/import_symbol_type_err.out b/vlib/v/checker/tests/import_symbol_type_err.out index 2a700be8e8..c9f238abf0 100644 --- a/vlib/v/checker/tests/import_symbol_type_err.out +++ b/vlib/v/checker/tests/import_symbol_type_err.out @@ -1,9 +1,9 @@ -vlib/v/checker/tests/import_symbol_type_err.vv:1:17: error: module `crypto` has no public type `Coin{}` +vlib/v/checker/tests/import_symbol_type_err.vv:1:17: error: module `crypto` has no type `Coin` 1 | import crypto { Coin } | ~~~~ 2 | fn main() { 3 | println(Coin{}) -vlib/v/checker/tests/import_symbol_type_err.vv:3:11: error: unknown struct: Coin +vlib/v/checker/tests/import_symbol_type_err.vv:3:11: error: unknown struct: crypto.Coin 1 | import crypto { Coin } 2 | fn main() { 3 | println(Coin{}) diff --git a/vlib/v/checker/tests/import_symbol_type_private_err.out b/vlib/v/checker/tests/import_symbol_type_private_err.out new file mode 100644 index 0000000000..8efe2f6031 --- /dev/null +++ b/vlib/v/checker/tests/import_symbol_type_private_err.out @@ -0,0 +1,11 @@ +vlib/v/checker/tests/import_symbol_type_private_err.vv:1:13: error: module `io` type `ReaderWriterImpl` is private + 1 | import io { ReaderWriterImpl } + | ~~~~~~~~~~~~~~~~ + 2 | fn main() { + 3 | _ := ReaderWriterImpl{} +vlib/v/checker/tests/import_symbol_type_private_err.vv:3:8: error: type `io.ReaderWriterImpl` is private + 1 | import io { ReaderWriterImpl } + 2 | fn main() { + 3 | _ := ReaderWriterImpl{} + | ~~~~~~~~~~~~~~~~~~ + 4 | } diff --git a/vlib/v/checker/tests/import_symbol_type_private_err.vv b/vlib/v/checker/tests/import_symbol_type_private_err.vv new file mode 100644 index 0000000000..8c9d9b39e3 --- /dev/null +++ b/vlib/v/checker/tests/import_symbol_type_private_err.vv @@ -0,0 +1,4 @@ +import io { ReaderWriterImpl } +fn main() { + _ := ReaderWriterImpl{} +} diff --git a/vlib/v/checker/tests/modules/overload_return_type.out b/vlib/v/checker/tests/modules/overload_return_type.out index a6d1346992..dbdf6a5779 100644 --- a/vlib/v/checker/tests/modules/overload_return_type.out +++ b/vlib/v/checker/tests/modules/overload_return_type.out @@ -1,4 +1,4 @@ -vlib/v/checker/tests/modules/overload_return_type/main.v:14:8: error: cannot assign to `two`: expected `Point`, not `int` +vlib/v/checker/tests/modules/overload_return_type/main.v:14:8: error: cannot assign to `two`: expected `point.Point`, not `int` 12 | y: 1 13 | } 14 | two = one + two diff --git a/vlib/v/fmt/fmt.v b/vlib/v/fmt/fmt.v index d7eb5e5d22..340db6644e 100644 --- a/vlib/v/fmt/fmt.v +++ b/vlib/v/fmt/fmt.v @@ -1273,6 +1273,9 @@ pub fn (mut f Fmt) short_module(name string) string { if !name.contains('.') { return name } + if name in f.mod2alias { + return f.mod2alias[name] + } if name.ends_with('>') { x := name.trim_suffix('>').split('<') if x.len == 2 { @@ -1692,7 +1695,7 @@ pub fn (mut f Fmt) array_init(it ast.ArrayInit) { f.write('}') return } - f.write(f.table.type_to_str(it.typ)) + f.write(f.table.type_to_str_using_aliases(it.typ, f.mod2alias)) f.write('{') // TODO copypasta if it.has_len { diff --git a/vlib/v/parser/fn.v b/vlib/v/parser/fn.v index 1f2b308a3b..85e9fa79bf 100644 --- a/vlib/v/parser/fn.v +++ b/vlib/v/parser/fn.v @@ -82,17 +82,13 @@ pub fn (mut p Parser) call_expr(language table.Language, mod string) ast.CallExp p.next() or_kind = .propagate } - mut fn_mod := p.mod - if registered := p.table.find_fn(fn_name) { - if registered.is_placeholder { - fn_mod = registered.mod - fn_name = registered.name - } + if fn_name in p.imported_symbols { + fn_name = p.imported_symbols[fn_name] } return ast.CallExpr{ name: fn_name args: args - mod: fn_mod + mod: p.mod pos: pos language: language generic_type: generic_type diff --git a/vlib/v/parser/parse_type.v b/vlib/v/parser/parse_type.v index 599cd2d221..a4d85e62e2 100644 --- a/vlib/v/parser/parse_type.v +++ b/vlib/v/parser/parse_type.v @@ -217,6 +217,8 @@ pub fn (mut p Parser) parse_any_type(language table.Language, is_ptr bool, check } } else if p.expr_mod != '' && !p.in_generic_params { // p.expr_mod is from the struct and not from the generic parameter name = p.expr_mod + '.' + name + } else if name in p.imported_symbols { + name = p.imported_symbols[name] } else if p.mod != 'builtin' && name.len > 1 && name !in p.table.type_idxs { // `Foo` in module `mod` means `mod.Foo` name = p.mod + '.' + name diff --git a/vlib/v/parser/parser.v b/vlib/v/parser/parser.v index 504f7150a8..4e5227a777 100644 --- a/vlib/v/parser/parser.v +++ b/vlib/v/parser/parser.v @@ -46,6 +46,7 @@ mut: imports map[string]string // alias => mod_name ast_imports []ast.Import // mod_names used_imports []string // alias + imported_symbols map[string]string is_amp bool // for generating the right code for `&Foo{}` returns bool inside_match bool // to separate `match A { }` from `Struct{}` @@ -218,6 +219,7 @@ pub fn (mut p Parser) parse() ast.File { path: p.file_name mod: module_decl imports: p.ast_imports + imported_symbols: p.imported_symbols stmts: stmts scope: p.scope global_scope: p.global_scope @@ -1681,44 +1683,11 @@ fn (mut p Parser) import_syms(mut parent ast.Import) { for p.tok.kind == .name { pos := p.tok.position() alias := p.check_name() - name := '${parent.mod}.$alias' - if alias[0].is_capital() { - idx := p.table.add_placeholder_type(name, .v) - typ := table.new_type(idx) - prepend_mod_name := p.prepend_mod(alias) - p.table.register_type_symbol(table.TypeSymbol{ - kind: .alias - name: prepend_mod_name - cname: util.no_dots(prepend_mod_name) - mod: p.mod - parent_idx: idx - info: table.Alias{ - parent_type: typ - language: table.Language.v - is_import: true - } - is_public: false - }) - // so we can work with the fully declared type in fmt+checker - parent.syms << ast.ImportSymbol{ - pos: pos - name: alias - kind: .type_ - } - } else { - if !p.table.known_fn(name) { - p.table.fns[alias] = table.Fn{ - is_placeholder: true - mod: parent.mod - name: name - } - } - // so we can work with this in fmt+checker - parent.syms << ast.ImportSymbol{ - pos: pos - name: alias - kind: .fn_ - } + p.imported_symbols[alias] = parent.mod + '.' + alias + // so we can work with this in fmt+checker + parent.syms << ast.ImportSymbol{ + pos: pos + name: alias } if p.tok.kind == .comma { // go again if more than one p.next() diff --git a/vlib/v/parser/struct.v b/vlib/v/parser/struct.v index 8b7afcacc9..ee9c2a94db 100644 --- a/vlib/v/parser/struct.v +++ b/vlib/v/parser/struct.v @@ -387,6 +387,7 @@ fn (mut p Parser) interface_decl() ast.InterfaceDecl { pre_comments := p.eat_comments() // Declare the type reg_idx := p.table.register_type_symbol( + is_public: is_pub kind: .interface_ name: interface_name cname: util.no_dots(interface_name) diff --git a/vlib/v/table/types.v b/vlib/v/table/types.v index 19381c96b8..c6e0263872 100644 --- a/vlib/v/table/types.v +++ b/vlib/v/table/types.v @@ -842,6 +842,11 @@ pub: } pub fn (table &Table) type_to_str(t Type) string { + return table.type_to_str_using_aliases(t, map[string]string{}) +} + +// import_aliases is a map of imported symbol aliases 'module.Type' => 'Type' +pub fn (table &Table) type_to_str_using_aliases(t Type, import_aliases map[string]string) string { sym := table.get_type_symbol(t) mut res := sym.name match sym.kind { @@ -854,12 +859,12 @@ pub fn (table &Table) type_to_str(t Type) string { return 'array' } info := sym.info as Array - elem_str := table.type_to_str(info.elem_type) + elem_str := table.type_to_str_using_aliases(info.elem_type, import_aliases) res = '[]$elem_str' } .array_fixed { info := sym.info as ArrayFixed - elem_str := table.type_to_str(info.elem_type) + elem_str := table.type_to_str_using_aliases(info.elem_type, import_aliases) res = '[$info.size]$elem_str' } .chan { @@ -872,7 +877,7 @@ pub fn (table &Table) type_to_str(t Type) string { mut_str = 'mut ' elem_type = elem_type.set_nr_muls(elem_type.nr_muls() - 1) } - elem_str := table.type_to_str(elem_type) + elem_str := table.type_to_str_using_aliases(elem_type, import_aliases) res = 'chan $mut_str$elem_str' } } @@ -887,8 +892,8 @@ pub fn (table &Table) type_to_str(t Type) string { return 'map' } info := sym.info as Map - key_str := table.type_to_str(info.key_type) - val_str := table.type_to_str(info.value_type) + key_str := table.type_to_str_using_aliases(info.key_type, import_aliases) + val_str := table.type_to_str_using_aliases(info.value_type, import_aliases) res = 'map[$key_str]$val_str' } .multi_return { @@ -898,7 +903,7 @@ pub fn (table &Table) type_to_str(t Type) string { if i > 0 { res += ', ' } - res += table.type_to_str(typ) + res += table.type_to_str_using_aliases(typ, import_aliases) } res += ')' } @@ -917,6 +922,9 @@ pub fn (table &Table) type_to_str(t Type) string { if res.starts_with(table.cmod_prefix) { res = res.replace_once(table.cmod_prefix, '') } + if res in import_aliases { + res = import_aliases[res] + } } } nr_muls := t.nr_muls() diff --git a/vlib/v/tests/imported_symbols_test.v b/vlib/v/tests/imported_symbols_test.v index 85bc43fb6a..db57888da4 100644 --- a/vlib/v/tests/imported_symbols_test.v +++ b/vlib/v/tests/imported_symbols_test.v @@ -1,15 +1,26 @@ module main -import shapes { Point, Line } +import geometry { Point, Line, point_str, module_name } -// test that Point & Line work correctly -// with struct init & array's -fn test_imported_symbols() { - p0 := Point {x: 10 y: 10} - p1 := Point {x: 50 y: 10} - - _ := Line { +fn test_imported_symbols_types() { + // struct init + p0 := Point{x: 10 y: 20} + p1 := Point{x: 40 y: 60} + // array init + l0 := Line { ps: [p0, p1] } + assert l0.ps[0].y == 20 +} +fn test_imported_symbols_functions() { + p0 := Point{x: 20 y: 40} + // method + assert p0.str() == '20 40' + // function + assert point_str(p0) == '20 40' +} + +fn test_imported_symbols_constants() { + assert module_name == 'geometry' } \ No newline at end of file diff --git a/vlib/v/tests/modules/methods_struct_another_module/point.v b/vlib/v/tests/modules/geometry/geometry.v similarity index 50% rename from vlib/v/tests/modules/methods_struct_another_module/point.v rename to vlib/v/tests/modules/geometry/geometry.v index 5ba3697ba9..375c49c2dd 100644 --- a/vlib/v/tests/modules/methods_struct_another_module/point.v +++ b/vlib/v/tests/modules/geometry/geometry.v @@ -1,9 +1,18 @@ -module point +module geometry + +const( + module_name = 'geometry' +) pub struct Point { - pub mut: - x int - y int +pub mut: + x int + y int +} + +pub struct Line { +pub mut: + ps []Point } pub fn (a Point) +(b Point) Point { @@ -15,4 +24,8 @@ pub fn (a Point) +(b Point) Point { pub fn (a Point) str() string { return '${a.x} ${a.y}' -} \ No newline at end of file +} + +pub fn point_str(a Point) string { + return a.str() +} diff --git a/vlib/v/tests/modules/methods_struct_another_module/methods_struct_test.v b/vlib/v/tests/modules/methods_struct_another_module/methods_struct_test.v index 0c17cabdc8..ad80ded468 100644 --- a/vlib/v/tests/modules/methods_struct_another_module/methods_struct_test.v +++ b/vlib/v/tests/modules/methods_struct_another_module/methods_struct_test.v @@ -1,6 +1,6 @@ module main -import point { Point } +import geometry { Point } fn test_operator_overloading() { one := Point {x:1, y:2} diff --git a/vlib/v/tests/modules/shapes/shapes.v b/vlib/v/tests/modules/shapes/shapes.v deleted file mode 100644 index 6d260edf0f..0000000000 --- a/vlib/v/tests/modules/shapes/shapes.v +++ /dev/null @@ -1,12 +0,0 @@ -module shapes - -pub struct Point { -pub mut: - x int - y int -} - -pub struct Line { -pub mut: - ps []Point -} \ No newline at end of file