all: change the way import symbols work & support consts (#7182)
parent
f30faf2627
commit
78a6795319
|
@ -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 {
|
||||
|
|
|
@ -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 &&
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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 | }
|
|
@ -0,0 +1,4 @@
|
|||
import time { now, since }
|
||||
fn main() {
|
||||
since(now())
|
||||
}
|
|
@ -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{})
|
||||
|
|
|
@ -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 | }
|
|
@ -0,0 +1,4 @@
|
|||
import io { ReaderWriterImpl }
|
||||
fn main() {
|
||||
_ := ReaderWriterImpl{}
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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'
|
||||
}
|
|
@ -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}'
|
||||
}
|
||||
}
|
||||
|
||||
pub fn point_str(a Point) string {
|
||||
return a.str()
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
module main
|
||||
|
||||
import point { Point }
|
||||
import geometry { Point }
|
||||
|
||||
fn test_operator_overloading() {
|
||||
one := Point {x:1, y:2}
|
||||
|
|
|
@ -1,12 +0,0 @@
|
|||
module shapes
|
||||
|
||||
pub struct Point {
|
||||
pub mut:
|
||||
x int
|
||||
y int
|
||||
}
|
||||
|
||||
pub struct Line {
|
||||
pub mut:
|
||||
ps []Point
|
||||
}
|
Loading…
Reference in New Issue