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

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 {
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 &&

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) {
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) {

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 }
| ~~~~~~~
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 }
| ~~~~
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{})

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
13 | }
14 | two = one + two

View File

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

View File

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

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

View File

@ -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()

View File

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

View File

@ -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()

View File

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

View File

@ -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 {
@ -16,3 +25,7 @@ pub fn (a Point) +(b Point) Point {
pub fn (a Point) str() string {
return '${a.x} ${a.y}'
}
pub fn point_str(a Point) string {
return a.str()
}

View File

@ -1,6 +1,6 @@
module main
import point { Point }
import geometry { Point }
fn test_operator_overloading() {
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
}