all: change the way import symbols work & support consts (#7182)

pull/7187/head
joe-conigliaro 2020-12-08 04:13:03 +11:00 committed by GitHub
parent f30faf2627
commit 78a6795319
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
20 changed files with 142 additions and 126 deletions

View File

@ -233,16 +233,10 @@ pub mut:
syms []ImportSymbol syms []ImportSymbol
} }
pub enum ImportSymbolKind {
fn_
type_
}
pub struct ImportSymbol { pub struct ImportSymbol {
pub: pub:
pos token.Position pos token.Position
name string name string
kind ImportSymbolKind
} }
pub struct AnonFn { pub struct AnonFn {
@ -406,16 +400,17 @@ pub mut:
pub struct File { pub struct File {
pub: pub:
path string path string
mod Module mod Module
global_scope &Scope global_scope &Scope
pub mut: pub mut:
scope &Scope scope &Scope
stmts []Stmt stmts []Stmt
imports []Import imports []Import
errors []errors.Error imported_symbols map[string]string // 'Type' => 'module.Type'
warnings []errors.Warning errors []errors.Error
generic_fns []&FnDecl warnings []errors.Warning
generic_fns []&FnDecl
} }
pub struct IdentFn { pub struct IdentFn {

View File

@ -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 { if got_idx == table.array_type_idx || exp_idx == table.array_type_idx {
return true return true
} }
if got_type_sym.kind == .array && exp_type_sym.kind == .array { // TODO
// TODO // accept [] when an expected type is an array
// accept [] when an expected type is an array if got_type_sym.kind == .array &&
if got_type_sym.name == 'array_void' { exp_type_sym.kind == .array && got_type_sym.name == 'array_void' {
return true 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
}
} }
// type alias // type alias
if (got_type_sym.kind == .alias && if (got_type_sym.kind == .alias &&

View File

@ -2753,20 +2753,28 @@ fn (mut c Checker) hash_stmt(mut node ast.HashStmt) {
fn (mut c Checker) import_stmt(imp ast.Import) { fn (mut c Checker) import_stmt(imp ast.Import) {
for sym in imp.syms { for sym in imp.syms {
name := '${imp.mod}.$sym.name' name := '${imp.mod}.$sym.name'
if sym.kind == .fn_ { if sym.name[0].is_capital() {
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 type_sym := c.table.find_type(name) { if type_sym := c.table.find_type(name) {
if type_sym.kind == .placeholder || !type_sym.is_public { if type_sym.kind != .placeholder {
c.error('module `$imp.mod` has no public type `$sym.name{}`', sym.pos) 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 {} else {}
} }
} }
// prepend mod to look for fn call or const
mut name := ident.name 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' name = '${ident.mod}.$ident.name'
} }
if obj := c.file.global_scope.find(name) { if obj := c.file.global_scope.find(name) {

View File

@ -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 } 1 | import crypto { userper }
| ~~~~~~~ | ~~~~~~~
2 | fn main() { 2 | fn main() {

View File

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

View File

@ -0,0 +1,4 @@
import time { now, since }
fn main() {
since(now())
}

View File

@ -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 } 1 | import crypto { Coin }
| ~~~~ | ~~~~
2 | fn main() { 2 | fn main() {
3 | println(Coin{}) 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 } 1 | import crypto { Coin }
2 | fn main() { 2 | fn main() {
3 | println(Coin{}) 3 | println(Coin{})

View File

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

View File

@ -0,0 +1,4 @@
import io { ReaderWriterImpl }
fn main() {
_ := ReaderWriterImpl{}
}

View File

@ -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 12 | y: 1
13 | } 13 | }
14 | two = one + two 14 | two = one + two

View File

@ -1273,6 +1273,9 @@ pub fn (mut f Fmt) short_module(name string) string {
if !name.contains('.') { if !name.contains('.') {
return name return name
} }
if name in f.mod2alias {
return f.mod2alias[name]
}
if name.ends_with('>') { if name.ends_with('>') {
x := name.trim_suffix('>').split('<') x := name.trim_suffix('>').split('<')
if x.len == 2 { if x.len == 2 {
@ -1692,7 +1695,7 @@ pub fn (mut f Fmt) array_init(it ast.ArrayInit) {
f.write('}') f.write('}')
return 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('{') f.write('{')
// TODO copypasta // TODO copypasta
if it.has_len { if it.has_len {

View File

@ -82,17 +82,13 @@ pub fn (mut p Parser) call_expr(language table.Language, mod string) ast.CallExp
p.next() p.next()
or_kind = .propagate or_kind = .propagate
} }
mut fn_mod := p.mod if fn_name in p.imported_symbols {
if registered := p.table.find_fn(fn_name) { fn_name = p.imported_symbols[fn_name]
if registered.is_placeholder {
fn_mod = registered.mod
fn_name = registered.name
}
} }
return ast.CallExpr{ return ast.CallExpr{
name: fn_name name: fn_name
args: args args: args
mod: fn_mod mod: p.mod
pos: pos pos: pos
language: language language: language
generic_type: generic_type generic_type: generic_type

View File

@ -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 } 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 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 { } else if p.mod != 'builtin' && name.len > 1 && name !in p.table.type_idxs {
// `Foo` in module `mod` means `mod.Foo` // `Foo` in module `mod` means `mod.Foo`
name = p.mod + '.' + name name = p.mod + '.' + name

View File

@ -46,6 +46,7 @@ mut:
imports map[string]string // alias => mod_name imports map[string]string // alias => mod_name
ast_imports []ast.Import // mod_names ast_imports []ast.Import // mod_names
used_imports []string // alias used_imports []string // alias
imported_symbols map[string]string
is_amp bool // for generating the right code for `&Foo{}` is_amp bool // for generating the right code for `&Foo{}`
returns bool returns bool
inside_match bool // to separate `match A { }` from `Struct{}` 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 path: p.file_name
mod: module_decl mod: module_decl
imports: p.ast_imports imports: p.ast_imports
imported_symbols: p.imported_symbols
stmts: stmts stmts: stmts
scope: p.scope scope: p.scope
global_scope: p.global_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 { for p.tok.kind == .name {
pos := p.tok.position() pos := p.tok.position()
alias := p.check_name() alias := p.check_name()
name := '${parent.mod}.$alias' p.imported_symbols[alias] = parent.mod + '.' + alias
if alias[0].is_capital() { // so we can work with this in fmt+checker
idx := p.table.add_placeholder_type(name, .v) parent.syms << ast.ImportSymbol{
typ := table.new_type(idx) pos: pos
prepend_mod_name := p.prepend_mod(alias) name: 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_
}
} }
if p.tok.kind == .comma { // go again if more than one if p.tok.kind == .comma { // go again if more than one
p.next() p.next()

View File

@ -387,6 +387,7 @@ fn (mut p Parser) interface_decl() ast.InterfaceDecl {
pre_comments := p.eat_comments() pre_comments := p.eat_comments()
// Declare the type // Declare the type
reg_idx := p.table.register_type_symbol( reg_idx := p.table.register_type_symbol(
is_public: is_pub
kind: .interface_ kind: .interface_
name: interface_name name: interface_name
cname: util.no_dots(interface_name) cname: util.no_dots(interface_name)

View File

@ -842,6 +842,11 @@ pub:
} }
pub fn (table &Table) type_to_str(t Type) string { 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) sym := table.get_type_symbol(t)
mut res := sym.name mut res := sym.name
match sym.kind { match sym.kind {
@ -854,12 +859,12 @@ pub fn (table &Table) type_to_str(t Type) string {
return 'array' return 'array'
} }
info := sym.info as 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' res = '[]$elem_str'
} }
.array_fixed { .array_fixed {
info := sym.info as ArrayFixed 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' res = '[$info.size]$elem_str'
} }
.chan { .chan {
@ -872,7 +877,7 @@ pub fn (table &Table) type_to_str(t Type) string {
mut_str = 'mut ' mut_str = 'mut '
elem_type = elem_type.set_nr_muls(elem_type.nr_muls() - 1) 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' res = 'chan $mut_str$elem_str'
} }
} }
@ -887,8 +892,8 @@ pub fn (table &Table) type_to_str(t Type) string {
return 'map' return 'map'
} }
info := sym.info as Map info := sym.info as Map
key_str := table.type_to_str(info.key_type) key_str := table.type_to_str_using_aliases(info.key_type, import_aliases)
val_str := table.type_to_str(info.value_type) val_str := table.type_to_str_using_aliases(info.value_type, import_aliases)
res = 'map[$key_str]$val_str' res = 'map[$key_str]$val_str'
} }
.multi_return { .multi_return {
@ -898,7 +903,7 @@ pub fn (table &Table) type_to_str(t Type) string {
if i > 0 { if i > 0 {
res += ', ' res += ', '
} }
res += table.type_to_str(typ) res += table.type_to_str_using_aliases(typ, import_aliases)
} }
res += ')' res += ')'
} }
@ -917,6 +922,9 @@ pub fn (table &Table) type_to_str(t Type) string {
if res.starts_with(table.cmod_prefix) { if res.starts_with(table.cmod_prefix) {
res = res.replace_once(table.cmod_prefix, '') res = res.replace_once(table.cmod_prefix, '')
} }
if res in import_aliases {
res = import_aliases[res]
}
} }
} }
nr_muls := t.nr_muls() nr_muls := t.nr_muls()

View File

@ -1,15 +1,26 @@
module main module main
import shapes { Point, Line } import geometry { Point, Line, point_str, module_name }
// test that Point & Line work correctly fn test_imported_symbols_types() {
// with struct init & array's // struct init
fn test_imported_symbols() { p0 := Point{x: 10 y: 20}
p0 := Point {x: 10 y: 10} p1 := Point{x: 40 y: 60}
p1 := Point {x: 50 y: 10} // array init
l0 := Line {
_ := Line {
ps: [p0, p1] 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'
} }

View File

@ -1,9 +1,18 @@
module point module geometry
const(
module_name = 'geometry'
)
pub struct Point { pub struct Point {
pub mut: pub mut:
x int x int
y int y int
}
pub struct Line {
pub mut:
ps []Point
} }
pub fn (a Point) +(b Point) Point { pub fn (a Point) +(b Point) Point {
@ -16,3 +25,7 @@ pub fn (a Point) +(b Point) Point {
pub fn (a Point) str() string { pub fn (a Point) str() string {
return '${a.x} ${a.y}' return '${a.x} ${a.y}'
} }
pub fn point_str(a Point) string {
return a.str()
}

View File

@ -1,6 +1,6 @@
module main module main
import point { Point } import geometry { Point }
fn test_operator_overloading() { fn test_operator_overloading() {
one := Point {x:1, y:2} one := Point {x:1, y:2}

View File

@ -1,12 +0,0 @@
module shapes
pub struct Point {
pub mut:
x int
y int
}
pub struct Line {
pub mut:
ps []Point
}