2020-01-23 21:04:46 +01:00
|
|
|
// Copyright (c) 2019-2020 Alexander Medvednikov. All rights reserved.
|
2020-01-18 23:26:14 +01:00
|
|
|
// Use of this source code is governed by an MIT license
|
|
|
|
// that can be found in the LICENSE file.
|
|
|
|
module checker
|
|
|
|
|
2020-04-25 17:49:16 +02:00
|
|
|
import v.ast
|
|
|
|
import v.table
|
|
|
|
import v.token
|
|
|
|
import v.pref
|
|
|
|
import v.util
|
2020-04-27 15:08:04 +02:00
|
|
|
import v.errors
|
2020-01-18 23:26:14 +01:00
|
|
|
|
2020-02-19 19:54:36 +01:00
|
|
|
const (
|
2020-04-02 01:45:22 +02:00
|
|
|
max_nr_errors = 300
|
2020-02-19 19:54:36 +01:00
|
|
|
)
|
|
|
|
|
2020-01-18 23:26:14 +01:00
|
|
|
pub struct Checker {
|
2020-05-21 21:51:49 +02:00
|
|
|
table &table.Table
|
2020-05-09 15:16:48 +02:00
|
|
|
pub mut:
|
2020-05-21 21:51:49 +02:00
|
|
|
file ast.File
|
|
|
|
nr_errors int
|
|
|
|
nr_warnings int
|
|
|
|
errors []errors.Error
|
|
|
|
warnings []errors.Warning
|
|
|
|
error_lines []int // to avoid printing multiple errors for the same line
|
|
|
|
expected_type table.Type
|
2020-05-23 08:51:15 +02:00
|
|
|
cur_fn &ast.FnDecl // current function
|
2020-05-21 21:51:49 +02:00
|
|
|
const_decl string
|
|
|
|
const_deps []string
|
|
|
|
const_names []string
|
|
|
|
pref &pref.Preferences // Preferences shared from V struct
|
|
|
|
in_for_count int // if checker is currently in an for loop
|
2020-04-08 19:15:16 +02:00
|
|
|
// checked_ident string // to avoid infinit checker loops
|
2020-05-21 21:51:49 +02:00
|
|
|
returns bool
|
|
|
|
scope_returns bool
|
|
|
|
mod string // current module name
|
|
|
|
is_builtin_mod bool // are we in `builtin`?
|
|
|
|
inside_unsafe bool
|
|
|
|
cur_generic_type table.Type
|
2020-01-18 23:26:14 +01:00
|
|
|
}
|
|
|
|
|
2020-04-03 11:01:09 +02:00
|
|
|
pub fn new_checker(table &table.Table, pref &pref.Preferences) Checker {
|
2020-04-09 15:33:46 +02:00
|
|
|
return Checker{
|
2020-01-18 23:26:14 +01:00
|
|
|
table: table
|
2020-04-03 11:01:09 +02:00
|
|
|
pref: pref
|
2020-05-23 08:51:15 +02:00
|
|
|
cur_fn: 0
|
2020-01-18 23:26:14 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-04-25 17:49:16 +02:00
|
|
|
pub fn (mut c Checker) check(ast_file ast.File) {
|
2020-02-20 11:13:18 +01:00
|
|
|
c.file = ast_file
|
2020-04-30 09:33:12 +02:00
|
|
|
for i, ast_import in ast_file.imports {
|
2020-04-30 18:06:14 +02:00
|
|
|
for j in 0 .. i {
|
2020-04-30 09:33:12 +02:00
|
|
|
if ast_import.mod == ast_file.imports[j].mod {
|
|
|
|
c.error('module name `$ast_import.mod` duplicate', ast_import.pos)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2020-01-18 23:26:14 +01:00
|
|
|
for stmt in ast_file.stmts {
|
|
|
|
c.stmt(stmt)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-04-27 15:08:04 +02:00
|
|
|
pub fn (mut c Checker) check2(ast_file ast.File) []errors.Error {
|
2020-02-20 17:05:16 +01:00
|
|
|
c.file = ast_file
|
|
|
|
for stmt in ast_file.stmts {
|
|
|
|
c.stmt(stmt)
|
|
|
|
}
|
|
|
|
return c.errors
|
|
|
|
}
|
|
|
|
|
2020-04-25 17:49:16 +02:00
|
|
|
pub fn (mut c Checker) check_files(ast_files []ast.File) {
|
2020-04-26 08:04:28 +02:00
|
|
|
mut has_main_mod_file := false
|
2020-04-18 00:20:38 +02:00
|
|
|
mut has_main_fn := false
|
2020-02-03 07:31:54 +01:00
|
|
|
for file in ast_files {
|
2020-01-18 23:26:14 +01:00
|
|
|
c.check(file)
|
2020-04-18 00:20:38 +02:00
|
|
|
if file.mod.name == 'main' {
|
2020-04-26 08:04:28 +02:00
|
|
|
has_main_mod_file = true
|
2020-04-19 00:07:57 +02:00
|
|
|
if c.check_file_in_main(file) {
|
2020-04-18 00:20:38 +02:00
|
|
|
has_main_fn = true
|
|
|
|
}
|
|
|
|
}
|
2020-01-18 23:26:14 +01:00
|
|
|
}
|
2020-04-10 22:27:51 +02:00
|
|
|
// Make sure fn main is defined in non lib builds
|
2020-04-10 22:32:52 +02:00
|
|
|
if c.pref.build_mode == .build_module || c.pref.is_test {
|
2020-04-10 22:27:51 +02:00
|
|
|
return
|
|
|
|
}
|
2020-04-18 17:46:23 +02:00
|
|
|
if c.pref.is_shared {
|
2020-04-16 15:30:19 +02:00
|
|
|
// shared libs do not need to have a main
|
2020-04-11 02:24:00 +02:00
|
|
|
return
|
|
|
|
}
|
2020-05-06 00:09:46 +02:00
|
|
|
if !has_main_mod_file {
|
2020-05-06 12:57:40 +02:00
|
|
|
c.error('project must include a `main` module or be a shared library (compile with `v -shared`)',
|
2020-05-06 12:26:00 +02:00
|
|
|
token.Position{})
|
2020-05-06 00:09:46 +02:00
|
|
|
} else if !has_main_fn {
|
2020-04-18 00:20:38 +02:00
|
|
|
c.error('function `main` must be declared in the main module', token.Position{})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-04-19 00:07:57 +02:00
|
|
|
const (
|
|
|
|
no_pub_in_main_warning = 'in module main cannot be declared public'
|
|
|
|
)
|
|
|
|
|
|
|
|
// do checks specific to files in main module
|
|
|
|
// returns `true` if a main function is in the file
|
2020-04-25 17:49:16 +02:00
|
|
|
fn (mut c Checker) check_file_in_main(file ast.File) bool {
|
2020-04-19 00:07:57 +02:00
|
|
|
mut has_main_fn := false
|
2020-04-18 00:20:38 +02:00
|
|
|
for stmt in file.stmts {
|
2020-04-19 00:07:57 +02:00
|
|
|
match stmt {
|
|
|
|
ast.ConstDecl {
|
|
|
|
if it.is_pub {
|
|
|
|
c.warn('const $no_pub_in_main_warning', it.pos)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
ast.ConstField {
|
|
|
|
if it.is_pub {
|
|
|
|
c.warn('const field `$it.name` $no_pub_in_main_warning', it.pos)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
ast.EnumDecl {
|
|
|
|
if it.is_pub {
|
|
|
|
c.warn('enum `$it.name` $no_pub_in_main_warning', it.pos)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
ast.FnDecl {
|
|
|
|
if it.name == 'main' {
|
|
|
|
has_main_fn = true
|
|
|
|
if it.is_pub {
|
|
|
|
c.error('function `main` cannot be declared public', it.pos)
|
|
|
|
}
|
2020-04-29 09:19:46 +02:00
|
|
|
if it.args.len > 0 {
|
|
|
|
c.error('function `main` cannot have arguments', it.pos)
|
|
|
|
}
|
|
|
|
if it.return_type != table.void_type {
|
|
|
|
c.error('function `main` cannot return values', it.pos)
|
|
|
|
}
|
2020-04-19 00:07:57 +02:00
|
|
|
} else {
|
|
|
|
if it.is_pub {
|
|
|
|
c.warn('function `$it.name` $no_pub_in_main_warning', it.pos)
|
|
|
|
}
|
|
|
|
}
|
2020-04-25 15:57:11 +02:00
|
|
|
if it.ctdefine.len > 0 {
|
|
|
|
if it.return_type != table.void_type {
|
2020-04-25 17:49:16 +02:00
|
|
|
c.error('only functions that do NOT return values can have `[if ${it.ctdefine}]` tags',
|
|
|
|
it.pos)
|
2020-04-25 15:57:11 +02:00
|
|
|
}
|
|
|
|
}
|
2020-04-19 00:07:57 +02:00
|
|
|
}
|
|
|
|
ast.StructDecl {
|
|
|
|
if it.is_pub {
|
|
|
|
c.warn('struct `$it.name` $no_pub_in_main_warning', it.pos)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
ast.TypeDecl {
|
|
|
|
type_decl := stmt as ast.TypeDecl
|
|
|
|
if type_decl is ast.AliasTypeDecl {
|
|
|
|
alias_decl := type_decl as ast.AliasTypeDecl
|
|
|
|
if alias_decl.is_pub {
|
|
|
|
c.warn('type alias `$alias_decl.name` $no_pub_in_main_warning', alias_decl.pos)
|
|
|
|
}
|
|
|
|
} else if type_decl is ast.SumTypeDecl {
|
|
|
|
sum_decl := type_decl as ast.SumTypeDecl
|
|
|
|
if sum_decl.is_pub {
|
|
|
|
c.warn('sum type `$sum_decl.name` $no_pub_in_main_warning', sum_decl.pos)
|
|
|
|
}
|
|
|
|
} else if type_decl is ast.FnTypeDecl {
|
|
|
|
fn_decl := type_decl as ast.FnTypeDecl
|
|
|
|
if fn_decl.is_pub {
|
|
|
|
c.warn('type alias `$fn_decl.name` $no_pub_in_main_warning', fn_decl.pos)
|
|
|
|
}
|
|
|
|
}
|
2020-04-16 15:30:19 +02:00
|
|
|
}
|
2020-04-19 00:07:57 +02:00
|
|
|
else {}
|
2020-04-10 22:27:51 +02:00
|
|
|
}
|
|
|
|
}
|
2020-04-19 00:07:57 +02:00
|
|
|
return has_main_fn
|
2020-01-18 23:26:14 +01:00
|
|
|
}
|
|
|
|
|
2020-05-16 16:12:23 +02:00
|
|
|
fn (mut c Checker) check_valid_snake_case(name, identifier string, pos token.Position) {
|
|
|
|
if name[0] == `_` {
|
|
|
|
c.error('$identifier `$name` cannot start with `_`', pos)
|
|
|
|
}
|
|
|
|
if util.contains_capital(name) {
|
|
|
|
c.error('$identifier `$name` cannot contain uppercase letters, use snake_case instead',
|
|
|
|
pos)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn stripped_name(name string) string {
|
|
|
|
idx := name.last_index('.') or {
|
|
|
|
-1
|
|
|
|
}
|
|
|
|
return name[(idx + 1)..]
|
|
|
|
}
|
|
|
|
|
|
|
|
fn (mut c Checker) check_valid_pascal_case(name, identifier string, pos token.Position) {
|
|
|
|
stripped_name := stripped_name(name)
|
|
|
|
if !stripped_name[0].is_capital() {
|
|
|
|
c.error('$identifier `$name` must begin with capital letter', pos)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-04-26 06:40:54 +02:00
|
|
|
pub fn (mut c Checker) type_decl(node ast.TypeDecl) {
|
|
|
|
match node {
|
|
|
|
ast.AliasTypeDecl {
|
2020-05-19 17:12:47 +02:00
|
|
|
// TODO Replace `c.file.mod.name != 'time'` by `it.language != .v` once available
|
2020-05-16 16:12:23 +02:00
|
|
|
if c.file.mod.name != 'time' {
|
|
|
|
c.check_valid_pascal_case(it.name, 'type alias', it.pos)
|
|
|
|
}
|
2020-04-26 06:40:54 +02:00
|
|
|
typ_sym := c.table.get_type_symbol(it.parent_type)
|
|
|
|
if typ_sym.kind == .placeholder {
|
|
|
|
c.error("type `$typ_sym.name` doesn't exist", it.pos)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
ast.FnTypeDecl {
|
2020-05-16 16:12:23 +02:00
|
|
|
c.check_valid_pascal_case(it.name, 'fn type', it.pos)
|
2020-04-26 06:40:54 +02:00
|
|
|
typ_sym := c.table.get_type_symbol(it.typ)
|
|
|
|
fn_typ_info := typ_sym.info as table.FnType
|
|
|
|
fn_info := fn_typ_info.func
|
|
|
|
ret_sym := c.table.get_type_symbol(fn_info.return_type)
|
|
|
|
if ret_sym.kind == .placeholder {
|
|
|
|
c.error("type `$ret_sym.name` doesn't exist", it.pos)
|
|
|
|
}
|
|
|
|
for arg in fn_info.args {
|
|
|
|
arg_sym := c.table.get_type_symbol(arg.typ)
|
|
|
|
if arg_sym.kind == .placeholder {
|
|
|
|
c.error("type `$arg_sym.name` doesn't exist", it.pos)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
ast.SumTypeDecl {
|
2020-05-16 16:12:23 +02:00
|
|
|
c.check_valid_pascal_case(it.name, 'sum type', it.pos)
|
2020-04-26 06:40:54 +02:00
|
|
|
for typ in it.sub_types {
|
|
|
|
typ_sym := c.table.get_type_symbol(typ)
|
|
|
|
if typ_sym.kind == .placeholder {
|
|
|
|
c.error("type `$typ_sym.name` doesn't exist", it.pos)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-05-16 16:12:23 +02:00
|
|
|
pub fn (mut c Checker) interface_decl(decl ast.InterfaceDecl) {
|
|
|
|
c.check_valid_pascal_case(decl.name, 'interface name', decl.pos)
|
|
|
|
for method in decl.methods {
|
|
|
|
c.check_valid_snake_case(method.name, 'method name', method.pos)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-04-25 17:49:16 +02:00
|
|
|
pub fn (mut c Checker) struct_decl(decl ast.StructDecl) {
|
2020-05-19 17:12:47 +02:00
|
|
|
if decl.language == .v && !c.is_builtin_mod {
|
2020-05-16 16:12:23 +02:00
|
|
|
c.check_valid_pascal_case(decl.name, 'struct name', decl.pos)
|
|
|
|
}
|
2020-04-29 09:11:36 +02:00
|
|
|
for i, field in decl.fields {
|
2020-05-19 17:12:47 +02:00
|
|
|
if decl.language == .v {
|
2020-05-16 16:12:23 +02:00
|
|
|
c.check_valid_snake_case(field.name, 'field name', field.pos)
|
|
|
|
}
|
2020-04-29 12:20:22 +02:00
|
|
|
for j in 0 .. i {
|
2020-04-29 09:11:36 +02:00
|
|
|
if field.name == decl.fields[j].name {
|
|
|
|
c.error('field name `$field.name` duplicate', field.pos)
|
|
|
|
}
|
|
|
|
}
|
2020-04-26 18:38:29 +02:00
|
|
|
sym := c.table.get_type_symbol(field.typ)
|
2020-05-19 17:12:47 +02:00
|
|
|
if sym.kind == .placeholder && decl.language != .c && !sym.name.starts_with('C.') {
|
2020-04-26 18:38:29 +02:00
|
|
|
c.error('unknown type `$sym.name`', field.pos)
|
|
|
|
}
|
2020-05-09 23:45:41 +02:00
|
|
|
if sym.kind == .struct_ {
|
2020-05-11 14:38:25 +02:00
|
|
|
info := sym.info as table.Struct
|
2020-05-09 23:45:41 +02:00
|
|
|
if info.is_ref_only && !field.typ.is_ptr() {
|
|
|
|
c.error('`$sym.name` type can only be used as a reference: `&$sym.name`', field.pos)
|
|
|
|
}
|
|
|
|
}
|
2020-04-26 18:38:29 +02:00
|
|
|
if field.has_default_expr {
|
|
|
|
c.expected_type = field.typ
|
|
|
|
field_expr_type := c.expr(field.default_expr)
|
2020-05-24 04:43:00 +02:00
|
|
|
if !c.check_types(field_expr_type, field.typ) {
|
2020-04-25 17:49:16 +02:00
|
|
|
field_expr_type_sym := c.table.get_type_symbol(field_expr_type)
|
2020-04-26 18:38:29 +02:00
|
|
|
field_type_sym := c.table.get_type_symbol(field.typ)
|
2020-05-13 00:50:46 +02:00
|
|
|
c.error('default expression for field `${field.name}` ' + 'has type `${field_expr_type_sym.name}`, but should be `${field_type_sym.name}`',
|
2020-04-26 18:38:29 +02:00
|
|
|
field.default_expr.position())
|
2020-04-24 20:27:18 +02:00
|
|
|
}
|
|
|
|
}
|
2020-04-24 15:46:55 +02:00
|
|
|
}
|
2020-04-16 11:01:18 +02:00
|
|
|
}
|
|
|
|
|
2020-05-11 14:38:25 +02:00
|
|
|
pub fn (mut c Checker) struct_init(mut struct_init ast.StructInit) table.Type {
|
2020-02-10 08:32:08 +01:00
|
|
|
// typ := c.table.find_type(struct_init.typ.typ.name) or {
|
2020-02-10 13:58:24 +01:00
|
|
|
// c.error('unknown struct: $struct_init.typ.typ.name', struct_init.pos)
|
|
|
|
// panic('')
|
2020-02-10 08:32:08 +01:00
|
|
|
// }
|
2020-04-01 23:23:20 +02:00
|
|
|
if struct_init.typ == table.void_type {
|
|
|
|
// Short syntax `({foo: bar})`
|
|
|
|
if c.expected_type == table.void_type {
|
|
|
|
c.error('unexpected short struct syntax', struct_init.pos)
|
|
|
|
return table.void_type
|
|
|
|
}
|
|
|
|
struct_init.typ = c.expected_type
|
|
|
|
}
|
2020-04-16 11:01:18 +02:00
|
|
|
type_sym := c.table.get_type_symbol(struct_init.typ)
|
2020-05-13 19:20:15 +02:00
|
|
|
if type_sym.kind == .interface_ {
|
|
|
|
c.error('cannot instantiate interface `$type_sym.name`', struct_init.pos)
|
|
|
|
}
|
2020-05-13 00:50:46 +02:00
|
|
|
if !type_sym.is_public && type_sym.kind != .placeholder && type_sym.mod != c.mod {
|
2020-05-13 14:21:50 +02:00
|
|
|
c.error('type `$type_sym.name` is private', struct_init.pos)
|
2020-05-04 21:56:41 +02:00
|
|
|
}
|
2020-02-20 15:42:56 +01:00
|
|
|
// println('check struct $typ_sym.name')
|
2020-04-16 11:01:18 +02:00
|
|
|
match type_sym.kind {
|
2020-01-18 23:26:14 +01:00
|
|
|
.placeholder {
|
2020-04-16 11:01:18 +02:00
|
|
|
c.error('unknown struct: $type_sym.name', struct_init.pos)
|
2020-01-18 23:26:14 +01:00
|
|
|
}
|
2020-03-18 09:56:19 +01:00
|
|
|
// string & array are also structs but .kind of string/array
|
2020-05-19 14:04:51 +02:00
|
|
|
.struct_, .string, .array, .alias {
|
|
|
|
mut info := table.Struct{}
|
|
|
|
if type_sym.kind == .alias {
|
|
|
|
info_t := type_sym.info as table.Alias
|
|
|
|
sym := c.table.get_type_symbol(info_t.parent_typ)
|
|
|
|
if sym.kind != .struct_ {
|
|
|
|
c.error('alias type name: $sym.name is not struct type', struct_init.pos)
|
|
|
|
}
|
|
|
|
info = sym.info as table.Struct
|
|
|
|
} else {
|
|
|
|
info = type_sym.info as table.Struct
|
|
|
|
}
|
2020-04-17 02:38:39 +02:00
|
|
|
if struct_init.is_short && struct_init.fields.len > info.fields.len {
|
2020-02-15 13:37:48 +01:00
|
|
|
c.error('too many fields', struct_init.pos)
|
|
|
|
}
|
2020-04-26 09:17:13 +02:00
|
|
|
mut inited_fields := []string{}
|
2020-04-17 02:38:39 +02:00
|
|
|
for i, field in struct_init.fields {
|
|
|
|
mut info_field := table.Field{}
|
|
|
|
mut field_name := ''
|
|
|
|
if struct_init.is_short {
|
|
|
|
if i >= info.fields.len {
|
|
|
|
// It doesn't make sense to check for fields that don't exist.
|
|
|
|
// We should just stop here.
|
|
|
|
break
|
|
|
|
}
|
|
|
|
info_field = info.fields[i]
|
|
|
|
field_name = info_field.name
|
|
|
|
struct_init.fields[i].name = field_name
|
2020-04-06 17:39:52 +02:00
|
|
|
} else {
|
2020-04-17 02:38:39 +02:00
|
|
|
field_name = field.name
|
|
|
|
mut exists := false
|
2020-04-06 17:39:52 +02:00
|
|
|
for f in info.fields {
|
|
|
|
if f.name == field_name {
|
2020-04-17 02:38:39 +02:00
|
|
|
info_field = f
|
|
|
|
exists = true
|
2020-04-06 17:39:52 +02:00
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
2020-04-17 02:38:39 +02:00
|
|
|
if !exists {
|
2020-04-25 17:49:16 +02:00
|
|
|
c.error('unknown field `$field.name` in struct literal of type `$type_sym.name`',
|
|
|
|
field.pos)
|
2020-04-17 02:38:39 +02:00
|
|
|
continue
|
|
|
|
}
|
|
|
|
if field_name in inited_fields {
|
|
|
|
c.error('duplicate field name in struct literal: `$field_name`', field.pos)
|
2020-04-06 17:39:52 +02:00
|
|
|
continue
|
|
|
|
}
|
2020-02-15 13:37:48 +01:00
|
|
|
}
|
2020-04-17 02:38:39 +02:00
|
|
|
inited_fields << field_name
|
|
|
|
c.expected_type = info_field.typ
|
|
|
|
expr_type := c.expr(field.expr)
|
2020-02-10 08:32:08 +01:00
|
|
|
expr_type_sym := c.table.get_type_symbol(expr_type)
|
2020-04-17 02:38:39 +02:00
|
|
|
field_type_sym := c.table.get_type_symbol(info_field.typ)
|
2020-05-24 04:43:00 +02:00
|
|
|
if !c.check_types(expr_type, info_field.typ) {
|
2020-04-25 17:49:16 +02:00
|
|
|
c.error('cannot assign `$expr_type_sym.name` as `$field_type_sym.name` for field `$info_field.name`',
|
|
|
|
field.pos)
|
2020-01-18 23:26:14 +01:00
|
|
|
}
|
2020-05-18 15:43:46 +02:00
|
|
|
if info_field.typ.is_ptr() && !expr_type.is_ptr() && !expr_type.is_number() {
|
2020-05-18 05:10:56 +02:00
|
|
|
c.error('ref', field.pos)
|
|
|
|
}
|
2020-04-17 02:38:39 +02:00
|
|
|
struct_init.fields[i].typ = expr_type
|
|
|
|
struct_init.fields[i].expected_type = info_field.typ
|
2020-01-18 23:26:14 +01:00
|
|
|
}
|
2020-04-03 20:17:53 +02:00
|
|
|
// Check uninitialized refs
|
|
|
|
for field in info.fields {
|
2020-05-10 22:26:47 +02:00
|
|
|
if field.has_default_expr || field.name in inited_fields {
|
2020-04-03 20:17:53 +02:00
|
|
|
continue
|
|
|
|
}
|
2020-04-25 09:08:53 +02:00
|
|
|
if field.typ.is_ptr() {
|
2020-04-16 11:01:18 +02:00
|
|
|
c.warn('reference field `${type_sym.name}.${field.name}` must be initialized',
|
2020-04-07 18:51:39 +02:00
|
|
|
struct_init.pos)
|
2020-04-03 20:17:53 +02:00
|
|
|
}
|
|
|
|
}
|
2020-01-18 23:26:14 +01:00
|
|
|
}
|
|
|
|
else {}
|
|
|
|
}
|
2020-02-06 13:57:35 +01:00
|
|
|
return struct_init.typ
|
2020-01-18 23:26:14 +01:00
|
|
|
}
|
|
|
|
|
2020-05-11 14:38:25 +02:00
|
|
|
pub fn (mut c Checker) infix_expr(mut infix_expr ast.InfixExpr) table.Type {
|
2020-02-20 12:16:18 +01:00
|
|
|
// println('checker: infix expr(op $infix_expr.op.str())')
|
2020-05-14 17:15:25 +02:00
|
|
|
former_expected_type := c.expected_type
|
|
|
|
defer {
|
|
|
|
c.expected_type = former_expected_type
|
|
|
|
}
|
2020-04-07 03:27:06 +02:00
|
|
|
c.expected_type = table.void_type
|
2020-05-25 05:32:33 +02:00
|
|
|
mut left_type := c.expr(infix_expr.left)
|
|
|
|
if false && left_type == table.t_type {
|
|
|
|
left_type = c.cur_generic_type
|
|
|
|
}
|
2020-03-07 00:34:14 +01:00
|
|
|
infix_expr.left_type = left_type
|
2020-02-29 18:25:38 +01:00
|
|
|
c.expected_type = left_type
|
2020-05-25 05:32:33 +02:00
|
|
|
mut right_type := c.expr(infix_expr.right)
|
|
|
|
if false && right_type == table.t_type {
|
|
|
|
right_type = c.cur_generic_type
|
|
|
|
}
|
2020-03-15 00:46:08 +01:00
|
|
|
infix_expr.right_type = right_type
|
2020-03-19 08:32:42 +01:00
|
|
|
right := c.table.get_type_symbol(right_type)
|
2020-04-06 02:05:08 +02:00
|
|
|
left := c.table.get_type_symbol(left_type)
|
2020-04-25 20:28:49 +02:00
|
|
|
// Single side check
|
2020-04-26 09:17:13 +02:00
|
|
|
// Place these branches according to ops' usage frequency to accelerate.
|
|
|
|
// TODO: First branch includes ops where single side check is not needed, or needed but hasn't been implemented.
|
|
|
|
// TODO: Some of the checks are not single side. Should find a better way to organize them.
|
2020-04-25 20:28:49 +02:00
|
|
|
match infix_expr.op {
|
2020-05-12 19:38:43 +02:00
|
|
|
// .eq, .ne, .gt, .lt, .ge, .le, .and, .logical_or, .dot, .key_as, .right_shift {}
|
2020-04-25 20:28:49 +02:00
|
|
|
.key_in, .not_in {
|
|
|
|
match right.kind {
|
|
|
|
.array {
|
|
|
|
right_sym := c.table.get_type_symbol(right.array_info().elem_type)
|
|
|
|
if left.kind != right_sym.kind {
|
2020-05-17 17:03:32 +02:00
|
|
|
c.error('the data type on the left of `$infix_expr.op.str()` does not match the array item type',
|
2020-05-18 15:43:46 +02:00
|
|
|
infix_expr.pos)
|
2020-04-25 20:28:49 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
.map {
|
|
|
|
key_sym := c.table.get_type_symbol(right.map_info().key_type)
|
|
|
|
if left.kind != key_sym.kind {
|
2020-05-18 15:43:46 +02:00
|
|
|
c.error('the data type on the left of `$infix_expr.op.str()` does not match the map key type',
|
|
|
|
infix_expr.pos)
|
2020-04-25 21:51:44 +02:00
|
|
|
}
|
|
|
|
}
|
2020-04-25 20:28:49 +02:00
|
|
|
.string {
|
|
|
|
if left.kind != .string {
|
2020-05-18 15:43:46 +02:00
|
|
|
c.error('the data type on the left of `$infix_expr.op.str()` must be a string',
|
|
|
|
infix_expr.pos)
|
2020-04-25 20:28:49 +02:00
|
|
|
}
|
2020-04-25 21:51:44 +02:00
|
|
|
}
|
2020-04-25 20:28:49 +02:00
|
|
|
else {
|
2020-05-18 15:43:46 +02:00
|
|
|
c.error('`$infix_expr.op.str()` can only be used with an array/map/string',
|
|
|
|
infix_expr.pos)
|
2020-04-25 20:28:49 +02:00
|
|
|
}
|
2020-04-07 03:27:06 +02:00
|
|
|
}
|
2020-04-25 20:28:49 +02:00
|
|
|
return table.bool_type
|
2020-02-19 19:54:36 +01:00
|
|
|
}
|
2020-04-25 20:28:49 +02:00
|
|
|
.plus, .minus, .mul, .div {
|
2020-04-29 12:20:22 +02:00
|
|
|
if infix_expr.op == .div && (infix_expr.right is ast.IntegerLiteral && infix_expr.right.str() ==
|
|
|
|
'0' || infix_expr.right is ast.FloatLiteral && infix_expr.right.str().f64() == 0.0) {
|
2020-04-28 09:52:01 +02:00
|
|
|
c.error('division by zero', infix_expr.right.position())
|
|
|
|
}
|
2020-04-25 20:28:49 +02:00
|
|
|
if left.kind in [.array, .array_fixed, .map, .struct_] && !left.has_method(infix_expr.op.str()) {
|
|
|
|
c.error('mismatched types `$left.name` and `$right.name`', infix_expr.left.position())
|
|
|
|
} else if right.kind in [.array, .array_fixed, .map, .struct_] && !right.has_method(infix_expr.op.str()) {
|
|
|
|
c.error('mismatched types `$left.name` and `$right.name`', infix_expr.right.position())
|
2020-04-19 22:26:58 +02:00
|
|
|
}
|
2020-04-25 20:28:49 +02:00
|
|
|
}
|
|
|
|
.left_shift {
|
|
|
|
if left.kind == .array {
|
|
|
|
// `array << elm`
|
2020-04-27 22:53:26 +02:00
|
|
|
c.fail_if_immutable(infix_expr.left)
|
2020-05-06 11:29:37 +02:00
|
|
|
left_value_type := c.table.value_type(left_type)
|
|
|
|
left_value_sym := c.table.get_type_symbol(left_value_type)
|
|
|
|
if left_value_sym.kind == .interface_ {
|
|
|
|
if right.kind != .array {
|
|
|
|
// []Animal << Cat
|
|
|
|
c.type_implements(right_type, left_value_type, infix_expr.right.position())
|
|
|
|
} else {
|
|
|
|
// []Animal << Cat
|
|
|
|
c.type_implements(c.table.value_type(right_type), left_value_type,
|
|
|
|
infix_expr.right.position())
|
|
|
|
}
|
|
|
|
return table.void_type
|
|
|
|
}
|
2020-04-25 20:28:49 +02:00
|
|
|
// the expressions have different types (array_x and x)
|
2020-05-24 04:43:00 +02:00
|
|
|
if c.check_types(right_type, left_value_type) { // , right_type) {
|
2020-04-25 20:28:49 +02:00
|
|
|
// []T << T
|
|
|
|
return table.void_type
|
|
|
|
}
|
2020-05-24 04:43:00 +02:00
|
|
|
if right.kind == .array && c.check_types(left_value_type, c.table.value_type(right_type)) {
|
2020-04-25 20:28:49 +02:00
|
|
|
// []T << []T
|
|
|
|
return table.void_type
|
|
|
|
}
|
2020-05-03 16:49:05 +02:00
|
|
|
s := left.name.replace('array_', '[]')
|
2020-05-03 18:08:45 +02:00
|
|
|
c.error('cannot append `$right.name` to `$s`', infix_expr.right.position())
|
2020-04-25 20:28:49 +02:00
|
|
|
return table.void_type
|
|
|
|
} else if !left.is_int() {
|
|
|
|
c.error('cannot shift type $right.name into non-integer type $left.name', infix_expr.left.position())
|
|
|
|
return table.void_type
|
|
|
|
} else if !right.is_int() {
|
|
|
|
c.error('cannot shift non-integer type $right.name into type $left.name', infix_expr.right.position())
|
|
|
|
return table.void_type
|
2020-04-25 06:14:17 +02:00
|
|
|
}
|
2020-04-19 22:26:58 +02:00
|
|
|
}
|
2020-04-25 20:28:49 +02:00
|
|
|
.key_is {
|
|
|
|
type_expr := infix_expr.right as ast.Type
|
|
|
|
typ_sym := c.table.get_type_symbol(type_expr.typ)
|
|
|
|
if typ_sym.kind == .placeholder {
|
|
|
|
c.error('is: type `${typ_sym.name}` does not exist', type_expr.pos)
|
|
|
|
}
|
|
|
|
return table.bool_type
|
2020-04-20 14:49:26 +02:00
|
|
|
}
|
2020-04-25 20:28:49 +02:00
|
|
|
.amp, .pipe, .xor {
|
|
|
|
if !left.is_int() {
|
|
|
|
c.error('left type of `${infix_expr.op.str()}` cannot be non-integer type $left.name',
|
|
|
|
infix_expr.left.position())
|
|
|
|
} else if !right.is_int() {
|
|
|
|
c.error('right type of `${infix_expr.op.str()}` cannot be non-integer type $right.name',
|
|
|
|
infix_expr.right.position())
|
|
|
|
}
|
2020-04-21 13:26:02 +02:00
|
|
|
}
|
2020-04-25 20:28:49 +02:00
|
|
|
.mod {
|
|
|
|
if left.is_int() && !right.is_int() {
|
|
|
|
c.error('mismatched types `$left.name` and `$right.name`', infix_expr.right.position())
|
|
|
|
} else if !left.is_int() && right.is_int() {
|
|
|
|
c.error('mismatched types `$left.name` and `$right.name`', infix_expr.left.position())
|
2020-05-18 15:43:46 +02:00
|
|
|
} else if left.kind == .f32 && right.kind == .f32 || left.kind == .f64 && right.kind ==
|
|
|
|
.f64 {
|
2020-05-16 15:32:46 +02:00
|
|
|
c.error('float modulo not allowed, use math.fmod() instead', infix_expr.left.position())
|
2020-04-25 21:51:44 +02:00
|
|
|
} else if left.kind in [.f32, .f64, .string, .array, .array_fixed, .map, .struct_] &&
|
2020-04-25 20:28:49 +02:00
|
|
|
!left.has_method(infix_expr.op.str()) {
|
|
|
|
c.error('mismatched types `$left.name` and `$right.name`', infix_expr.left.position())
|
2020-04-25 21:51:44 +02:00
|
|
|
} else if right.kind in [.f32, .f64, .string, .array, .array_fixed, .map, .struct_] &&
|
2020-04-25 20:28:49 +02:00
|
|
|
!right.has_method(infix_expr.op.str()) {
|
|
|
|
c.error('mismatched types `$left.name` and `$right.name`', infix_expr.right.position())
|
2020-05-16 15:32:46 +02:00
|
|
|
} else if infix_expr.right is ast.IntegerLiteral && infix_expr.right.str() == '0' {
|
|
|
|
c.error('modulo by zero', infix_expr.right.position())
|
2020-04-25 20:28:49 +02:00
|
|
|
}
|
2020-04-19 22:29:45 +02:00
|
|
|
}
|
2020-04-26 09:17:13 +02:00
|
|
|
else {}
|
2020-04-19 22:29:45 +02:00
|
|
|
}
|
2020-04-25 20:28:49 +02:00
|
|
|
// TODO: Absorb this block into the above single side check block to accelerate.
|
2020-04-26 06:39:23 +02:00
|
|
|
if left_type == table.bool_type && infix_expr.op !in [.eq, .ne, .logical_or, .and] {
|
2020-04-25 21:51:44 +02:00
|
|
|
c.error('bool types only have the following operators defined: `==`, `!=`, `||`, and `&&`',
|
2020-04-14 04:12:24 +02:00
|
|
|
infix_expr.pos)
|
2020-04-26 09:17:13 +02:00
|
|
|
} else if left_type == table.string_type && infix_expr.op !in [.plus, .eq, .ne, .lt, .gt,
|
|
|
|
.le, .ge] {
|
2020-04-14 04:12:24 +02:00
|
|
|
// TODO broken !in
|
|
|
|
c.error('string types only have the following operators defined: `==`, `!=`, `<`, `>`, `<=`, `>=`, and `&&`',
|
|
|
|
infix_expr.pos)
|
|
|
|
}
|
2020-04-25 20:28:49 +02:00
|
|
|
// Dual sides check (compatibility check)
|
2020-05-24 04:43:00 +02:00
|
|
|
if !c.check_types(right_type, left_type) {
|
2020-04-20 14:49:26 +02:00
|
|
|
// for type-unresolved consts
|
|
|
|
if left_type == table.void_type || right_type == table.void_type {
|
|
|
|
return table.void_type
|
|
|
|
}
|
2020-04-25 17:49:16 +02:00
|
|
|
c.error('infix expr: cannot use `$right.name` (right expression) as `$left.name`',
|
|
|
|
infix_expr.pos)
|
2020-04-20 14:49:26 +02:00
|
|
|
}
|
2020-04-26 09:17:13 +02:00
|
|
|
return if infix_expr.op.is_relational() {
|
|
|
|
table.bool_type
|
|
|
|
} else {
|
|
|
|
left_type
|
|
|
|
}
|
2020-01-18 23:26:14 +01:00
|
|
|
}
|
|
|
|
|
2020-04-27 22:53:26 +02:00
|
|
|
fn (mut c Checker) fail_if_immutable(expr ast.Expr) {
|
|
|
|
match expr {
|
|
|
|
ast.Ident {
|
2020-05-03 06:22:41 +02:00
|
|
|
scope := c.file.scope.innermost(it.pos.pos)
|
2020-04-27 22:53:26 +02:00
|
|
|
if v := scope.find_var(it.name) {
|
|
|
|
if !v.is_mut && !v.typ.is_ptr() {
|
|
|
|
c.error('`$it.name` is immutable, declare it with `mut` to make it mutable',
|
|
|
|
it.pos)
|
|
|
|
}
|
2020-05-11 10:09:58 +02:00
|
|
|
} else if it.name in c.const_names {
|
|
|
|
c.error('cannot assign to constant `$it.name`', it.pos)
|
2020-04-27 22:53:26 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
ast.IndexExpr {
|
|
|
|
c.fail_if_immutable(it.left)
|
|
|
|
}
|
|
|
|
ast.ParExpr {
|
|
|
|
c.fail_if_immutable(it.expr)
|
|
|
|
}
|
|
|
|
ast.PrefixExpr {
|
|
|
|
c.fail_if_immutable(it.right)
|
|
|
|
}
|
|
|
|
ast.SelectorExpr {
|
|
|
|
// retrieve table.Field
|
2020-04-29 12:46:06 +02:00
|
|
|
if it.expr_type == 0 {
|
2020-05-03 06:22:41 +02:00
|
|
|
c.error('0 type in SelectorExpr', it.pos)
|
2020-04-29 12:46:06 +02:00
|
|
|
return
|
|
|
|
}
|
2020-04-27 22:53:26 +02:00
|
|
|
typ_sym := c.table.get_type_symbol(it.expr_type)
|
|
|
|
match typ_sym.kind {
|
|
|
|
.struct_ {
|
|
|
|
struct_info := typ_sym.info as table.Struct
|
2020-05-10 02:07:15 +02:00
|
|
|
field_info := struct_info.find_field(it.field_name) or {
|
|
|
|
type_str := c.table.type_to_str(it.expr_type)
|
|
|
|
c.error('unknown field `${type_str}.$it.field_name`', it.pos)
|
|
|
|
return
|
|
|
|
}
|
2020-04-27 22:53:26 +02:00
|
|
|
if !field_info.is_mut {
|
|
|
|
type_str := c.table.type_to_str(it.expr_type)
|
2020-05-11 14:38:25 +02:00
|
|
|
c.error('field `$it.field_name` of struct `${type_str}` is immutable',
|
|
|
|
it.pos)
|
2020-04-27 22:53:26 +02:00
|
|
|
}
|
|
|
|
c.fail_if_immutable(it.expr)
|
|
|
|
}
|
|
|
|
.array, .string {
|
|
|
|
// This should only happen in `builtin`
|
|
|
|
// TODO Remove `crypto.rand` when possible (see vlib/crypto/rand/rand.v,
|
|
|
|
// if `c_array_to_bytes_tmp` doesn't exist, then it's safe to remove it)
|
|
|
|
if c.file.mod.name !in ['builtin', 'crypto.rand'] {
|
2020-04-28 11:20:19 +02:00
|
|
|
c.error('`$typ_sym.kind` can not be modified', it.pos)
|
2020-04-27 22:53:26 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
2020-04-28 11:20:19 +02:00
|
|
|
c.error('unexpected symbol `${typ_sym.kind}`', it.pos)
|
2020-04-27 22:53:26 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
c.error('unexpected expression `${typeof(expr)}`', expr.position())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-05-11 14:38:25 +02:00
|
|
|
fn (mut c Checker) assign_expr(mut assign_expr ast.AssignExpr) {
|
2020-04-07 03:27:06 +02:00
|
|
|
c.expected_type = table.void_type
|
2020-05-25 05:32:33 +02:00
|
|
|
left_type := c.unwrap_generic(c.expr(assign_expr.left))
|
2020-02-27 00:12:37 +01:00
|
|
|
c.expected_type = left_type
|
2020-03-13 01:43:30 +01:00
|
|
|
assign_expr.left_type = left_type
|
2020-02-27 00:12:37 +01:00
|
|
|
// println('setting exp type to $c.expected_type $t.name')
|
2020-05-25 11:32:14 +02:00
|
|
|
right_type := c.check_expr_opt_call(assign_expr.val, c.unwrap_generic(c.expr(assign_expr.val)))
|
2020-03-16 07:42:45 +01:00
|
|
|
assign_expr.right_type = right_type
|
2020-04-19 22:24:12 +02:00
|
|
|
right := c.table.get_type_symbol(right_type)
|
|
|
|
left := c.table.get_type_symbol(left_type)
|
2020-03-18 09:56:19 +01:00
|
|
|
if ast.expr_is_blank_ident(assign_expr.left) {
|
|
|
|
return
|
|
|
|
}
|
2020-04-26 10:25:54 +02:00
|
|
|
// Make sure the variable is mutable
|
2020-04-27 22:53:26 +02:00
|
|
|
c.fail_if_immutable(assign_expr.left)
|
2020-05-11 16:05:59 +02:00
|
|
|
// Do now allow `*x = y` outside `unsafe`
|
|
|
|
if assign_expr.left is ast.PrefixExpr {
|
|
|
|
p := assign_expr.left as ast.PrefixExpr
|
|
|
|
if p.op == .mul && !c.inside_unsafe {
|
|
|
|
c.error('modifying variables via deferencing can only be done in `unsafe` blocks',
|
|
|
|
assign_expr.pos)
|
|
|
|
}
|
|
|
|
}
|
2020-04-20 20:56:05 +02:00
|
|
|
// Single side check
|
|
|
|
match assign_expr.op {
|
2020-04-25 17:49:16 +02:00
|
|
|
.assign {} // No need to do single side check for =. But here put it first for speed.
|
2020-04-20 20:56:05 +02:00
|
|
|
.plus_assign {
|
|
|
|
if !left.is_number() && left_type != table.string_type && !left.is_pointer() {
|
|
|
|
c.error('operator += not defined on left operand type `$left.name`', assign_expr.left.position())
|
2020-04-25 17:49:16 +02:00
|
|
|
} else if !right.is_number() && right_type != table.string_type && !right.is_pointer() {
|
2020-04-20 20:56:05 +02:00
|
|
|
c.error('operator += not defined on right operand type `$right.name`', assign_expr.val.position())
|
|
|
|
}
|
2020-04-19 22:24:12 +02:00
|
|
|
}
|
2020-04-20 20:56:05 +02:00
|
|
|
.minus_assign {
|
|
|
|
if !left.is_number() && !left.is_pointer() {
|
|
|
|
c.error('operator -= not defined on left operand type `$left.name`', assign_expr.left.position())
|
2020-04-25 17:49:16 +02:00
|
|
|
} else if !right.is_number() && !right.is_pointer() {
|
2020-04-20 20:56:05 +02:00
|
|
|
c.error('operator -= not defined on right operand type `$right.name`', assign_expr.val.position())
|
|
|
|
}
|
2020-04-19 22:24:12 +02:00
|
|
|
}
|
2020-04-20 20:56:05 +02:00
|
|
|
.mult_assign, .div_assign {
|
|
|
|
if !left.is_number() {
|
2020-04-25 17:49:16 +02:00
|
|
|
c.error('operator ${assign_expr.op.str()} not defined on left operand type `$left.name`',
|
|
|
|
assign_expr.left.position())
|
|
|
|
} else if !right.is_number() {
|
|
|
|
c.error('operator ${assign_expr.op.str()} not defined on right operand type `$right.name`',
|
|
|
|
assign_expr.val.position())
|
2020-04-20 20:56:05 +02:00
|
|
|
}
|
2020-04-19 22:24:12 +02:00
|
|
|
}
|
2020-04-20 20:56:05 +02:00
|
|
|
.and_assign, .or_assign, .xor_assign, .mod_assign, .left_shift_assign, .right_shift_assign {
|
|
|
|
if !left.is_int() {
|
2020-04-25 17:49:16 +02:00
|
|
|
c.error('operator ${assign_expr.op.str()} not defined on left operand type `$left.name`',
|
|
|
|
assign_expr.left.position())
|
|
|
|
} else if !right.is_int() {
|
|
|
|
c.error('operator ${assign_expr.op.str()} not defined on right operand type `$right.name`',
|
|
|
|
assign_expr.val.position())
|
2020-04-20 20:56:05 +02:00
|
|
|
}
|
2020-04-19 22:24:12 +02:00
|
|
|
}
|
2020-04-25 17:49:16 +02:00
|
|
|
else {}
|
2020-04-19 22:24:12 +02:00
|
|
|
}
|
2020-04-20 20:56:05 +02:00
|
|
|
// Dual sides check (compatibility check)
|
2020-05-24 04:43:00 +02:00
|
|
|
if !c.check_types(right_type, left_type) {
|
2020-04-20 20:56:05 +02:00
|
|
|
left_type_sym := c.table.get_type_symbol(left_type)
|
|
|
|
right_type_sym := c.table.get_type_symbol(right_type)
|
|
|
|
c.error('cannot assign `$right_type_sym.name` to variable `${assign_expr.left.str()}` of type `$left_type_sym.name`',
|
|
|
|
assign_expr.val.position())
|
2020-04-19 22:24:12 +02:00
|
|
|
}
|
2020-01-18 23:26:14 +01:00
|
|
|
}
|
|
|
|
|
2020-05-11 14:38:25 +02:00
|
|
|
pub fn (mut c Checker) call_expr(mut call_expr ast.CallExpr) table.Type {
|
2020-03-18 09:56:19 +01:00
|
|
|
c.stmts(call_expr.or_block.stmts)
|
2020-03-30 12:39:20 +02:00
|
|
|
if call_expr.is_method {
|
2020-04-12 17:45:04 +02:00
|
|
|
return c.call_method(call_expr)
|
|
|
|
}
|
|
|
|
return c.call_fn(call_expr)
|
|
|
|
}
|
|
|
|
|
2020-05-11 14:38:25 +02:00
|
|
|
pub fn (mut c Checker) call_method(mut call_expr ast.CallExpr) table.Type {
|
2020-04-12 17:45:04 +02:00
|
|
|
left_type := c.expr(call_expr.left)
|
|
|
|
call_expr.left_type = left_type
|
|
|
|
left_type_sym := c.table.get_type_symbol(left_type)
|
|
|
|
method_name := call_expr.name
|
|
|
|
// TODO: remove this for actual methods, use only for compiler magic
|
2020-05-01 00:29:54 +02:00
|
|
|
// FIXME: Argument count != 1 will break these
|
2020-04-12 17:45:04 +02:00
|
|
|
if left_type_sym.kind == .array && method_name in ['filter', 'clone', 'repeat', 'reverse',
|
|
|
|
'map', 'slice'] {
|
|
|
|
if method_name in ['filter', 'map'] {
|
|
|
|
array_info := left_type_sym.info as table.Array
|
|
|
|
mut scope := c.file.scope.innermost(call_expr.pos.pos)
|
|
|
|
scope.update_var_type('it', array_info.elem_type)
|
|
|
|
}
|
2020-05-02 15:26:58 +02:00
|
|
|
// map/filter are supposed to have 1 arg only
|
2020-05-01 00:29:54 +02:00
|
|
|
mut arg_type := left_type
|
2020-04-27 15:16:31 +02:00
|
|
|
for arg in call_expr.args {
|
2020-05-01 00:29:54 +02:00
|
|
|
arg_type = c.expr(arg.expr)
|
2020-04-12 17:45:04 +02:00
|
|
|
}
|
|
|
|
call_expr.return_type = left_type
|
2020-05-01 00:29:54 +02:00
|
|
|
call_expr.receiver_type = left_type
|
2020-05-02 15:26:58 +02:00
|
|
|
if method_name == 'map' {
|
2020-05-20 14:28:33 +02:00
|
|
|
arg_sym := c.table.get_type_symbol(arg_type)
|
|
|
|
// FIXME: match expr failed for now
|
|
|
|
mut ret_type := 0
|
|
|
|
match arg_sym.info {
|
|
|
|
table.FnType { ret_type = it.func.return_type }
|
|
|
|
else { ret_type = arg_type }
|
|
|
|
}
|
|
|
|
call_expr.return_type = c.table.find_or_register_array(ret_type, 1)
|
2020-05-01 00:29:54 +02:00
|
|
|
} else if method_name == 'clone' {
|
|
|
|
// need to return `array_xxx` instead of `array`
|
2020-04-12 17:45:04 +02:00
|
|
|
// in ['clone', 'str'] {
|
2020-04-25 09:08:53 +02:00
|
|
|
call_expr.receiver_type = left_type.to_ptr()
|
2020-04-12 17:45:04 +02:00
|
|
|
// call_expr.return_type = call_expr.receiver_type
|
2020-02-20 11:13:18 +01:00
|
|
|
}
|
2020-05-01 00:29:54 +02:00
|
|
|
return call_expr.return_type
|
2020-04-12 17:45:04 +02:00
|
|
|
} else if left_type_sym.kind == .array && method_name in ['first', 'last'] {
|
|
|
|
info := left_type_sym.info as table.Array
|
|
|
|
call_expr.return_type = info.elem_type
|
|
|
|
call_expr.receiver_type = left_type
|
2020-05-01 00:29:54 +02:00
|
|
|
return call_expr.return_type
|
2020-04-12 17:45:04 +02:00
|
|
|
}
|
|
|
|
if method := c.table.type_find_method(left_type_sym, method_name) {
|
2020-04-25 17:49:16 +02:00
|
|
|
if !method.is_pub && !c.is_builtin_mod && !c.pref.is_test && left_type_sym.mod != c.mod &&
|
|
|
|
left_type_sym.mod != '' { // method.mod != c.mod {
|
2020-04-17 17:16:14 +02:00
|
|
|
// If a private method is called outside of the module
|
|
|
|
// its receiver type is defined in, show an error.
|
2020-04-25 17:49:16 +02:00
|
|
|
// println('warn $method_name lef.mod=$left_type_sym.mod c.mod=$c.mod')
|
2020-04-17 17:16:14 +02:00
|
|
|
c.error('method `${left_type_sym.name}.$method_name` is private', call_expr.pos)
|
|
|
|
}
|
2020-04-25 17:49:16 +02:00
|
|
|
if method.return_type == table.void_type && method.ctdefine.len > 0 && method.ctdefine !in
|
|
|
|
c.pref.compile_defines {
|
2020-04-25 15:57:11 +02:00
|
|
|
call_expr.should_be_skipped = true
|
|
|
|
}
|
2020-04-25 17:49:16 +02:00
|
|
|
nr_args := if method.args.len == 0 { 0 } else { method.args.len - 1 }
|
2020-04-12 17:45:04 +02:00
|
|
|
min_required_args := method.args.len - if method.is_variadic && method.args.len > 1 { 2 } else { 1 }
|
2020-03-30 12:39:20 +02:00
|
|
|
if call_expr.args.len < min_required_args {
|
2020-04-12 17:45:04 +02:00
|
|
|
c.error('too few arguments in call to `${left_type_sym.name}.$method_name` ($call_expr.args.len instead of $min_required_args)',
|
2020-04-07 15:15:45 +02:00
|
|
|
call_expr.pos)
|
2020-04-22 20:20:49 +02:00
|
|
|
} else if !method.is_variadic && call_expr.args.len > nr_args {
|
|
|
|
c.error('!too many arguments in call to `${left_type_sym.name}.$method_name` ($call_expr.args.len instead of $nr_args)',
|
2020-04-07 15:15:45 +02:00
|
|
|
call_expr.pos)
|
2020-04-12 17:45:04 +02:00
|
|
|
return method.return_type
|
2020-03-30 12:39:20 +02:00
|
|
|
}
|
2020-04-12 17:45:04 +02:00
|
|
|
// if method_name == 'clone' {
|
|
|
|
// println('CLONE nr args=$method.args.len')
|
|
|
|
// }
|
|
|
|
// call_expr.args << method.args[0].typ
|
|
|
|
// call_expr.exp_arg_types << method.args[0].typ
|
|
|
|
for i, arg in call_expr.args {
|
2020-05-04 21:03:18 +02:00
|
|
|
exp_arg_typ := if method.is_variadic && i >= method.args.len - 1 { method.args[method.args.len -
|
|
|
|
1].typ } else { method.args[i + 1].typ }
|
2020-05-06 11:29:37 +02:00
|
|
|
exp_arg_sym := c.table.get_type_symbol(exp_arg_typ)
|
2020-05-04 17:32:40 +02:00
|
|
|
c.expected_type = exp_arg_typ
|
|
|
|
got_arg_typ := c.expr(arg.expr)
|
|
|
|
call_expr.args[i].typ = got_arg_typ
|
2020-05-04 21:03:18 +02:00
|
|
|
if method.is_variadic && got_arg_typ.flag_is(.variadic) && call_expr.args.len -
|
|
|
|
1 > i {
|
2020-04-22 05:28:47 +02:00
|
|
|
c.error('when forwarding a varg variable, it must be the final argument', call_expr.pos)
|
|
|
|
}
|
2020-05-06 11:29:37 +02:00
|
|
|
if exp_arg_sym.kind == .interface_ {
|
|
|
|
c.type_implements(got_arg_typ, exp_arg_typ, arg.expr.position())
|
|
|
|
continue
|
|
|
|
}
|
2020-05-24 04:43:00 +02:00
|
|
|
if !c.check_types(got_arg_typ, exp_arg_typ) {
|
2020-05-04 17:32:40 +02:00
|
|
|
got_arg_sym := c.table.get_type_symbol(got_arg_typ)
|
|
|
|
// str method, allow type with str method if fn arg is string
|
|
|
|
if exp_arg_sym.kind == .string && got_arg_sym.has_method('str') {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
c.error('cannot use type `$got_arg_sym.str()` as type `$exp_arg_sym.str()` in argument ${i+1} to `${left_type_sym.name}.$method_name`',
|
|
|
|
call_expr.pos)
|
|
|
|
}
|
2020-03-06 10:52:03 +01:00
|
|
|
}
|
2020-03-26 11:09:59 +01:00
|
|
|
// TODO: typ optimize.. this node can get processed more than once
|
2020-04-03 15:18:17 +02:00
|
|
|
if call_expr.expected_arg_types.len == 0 {
|
2020-04-12 17:45:04 +02:00
|
|
|
for i in 1 .. method.args.len {
|
|
|
|
call_expr.expected_arg_types << method.args[i].typ
|
2020-03-26 11:09:59 +01:00
|
|
|
}
|
|
|
|
}
|
2020-04-12 17:45:04 +02:00
|
|
|
call_expr.receiver_type = method.args[0].typ
|
|
|
|
call_expr.return_type = method.return_type
|
|
|
|
return method.return_type
|
|
|
|
}
|
|
|
|
// TODO: str methods
|
2020-04-28 07:22:46 +02:00
|
|
|
if method_name == 'str' {
|
2020-04-12 17:45:04 +02:00
|
|
|
call_expr.receiver_type = left_type
|
|
|
|
call_expr.return_type = table.string_type
|
|
|
|
return table.string_type
|
|
|
|
}
|
2020-04-15 04:28:18 +02:00
|
|
|
// call struct field fn type
|
2020-05-01 12:12:21 +02:00
|
|
|
// TODO: can we use SelectorExpr for all? this dosent really belong here
|
2020-04-15 04:28:18 +02:00
|
|
|
if field := c.table.struct_find_field(left_type_sym, method_name) {
|
|
|
|
field_type_sym := c.table.get_type_symbol(field.typ)
|
|
|
|
if field_type_sym.kind == .function {
|
|
|
|
call_expr.is_method = false
|
|
|
|
info := field_type_sym.info as table.FnType
|
|
|
|
call_expr.return_type = info.func.return_type
|
2020-05-01 12:12:21 +02:00
|
|
|
// TODO: check args (do it once for all of the above)
|
|
|
|
for arg in call_expr.args {
|
|
|
|
c.expr(arg.expr)
|
|
|
|
}
|
2020-04-15 04:28:18 +02:00
|
|
|
return info.func.return_type
|
|
|
|
}
|
|
|
|
}
|
2020-04-22 20:20:49 +02:00
|
|
|
c.error('unknown method: `${left_type_sym.name}.$method_name`', call_expr.pos)
|
2020-04-12 17:45:04 +02:00
|
|
|
return table.void_type
|
|
|
|
}
|
|
|
|
|
2020-05-11 14:38:25 +02:00
|
|
|
pub fn (mut c Checker) call_fn(mut call_expr ast.CallExpr) table.Type {
|
2020-04-12 17:45:04 +02:00
|
|
|
if call_expr.name == 'panic' {
|
|
|
|
c.returns = true
|
|
|
|
}
|
|
|
|
fn_name := call_expr.name
|
2020-04-28 07:21:50 +02:00
|
|
|
if fn_name == 'main' {
|
|
|
|
c.error('the `main` function cannot be called in the program', call_expr.pos)
|
|
|
|
}
|
2020-04-12 17:45:04 +02:00
|
|
|
if fn_name == 'typeof' {
|
|
|
|
// TODO: impl typeof properly (probably not going to be a fn call)
|
|
|
|
return table.string_type
|
|
|
|
}
|
2020-04-12 17:49:31 +02:00
|
|
|
// if c.fileis('json_test.v') {
|
|
|
|
// println(fn_name)
|
|
|
|
// }
|
2020-04-12 17:45:04 +02:00
|
|
|
if fn_name == 'json.encode' {
|
2020-05-04 16:46:36 +02:00
|
|
|
} else if fn_name == 'json.decode' {
|
|
|
|
expr := call_expr.args[0].expr
|
|
|
|
if !(expr is ast.Type) {
|
2020-05-05 14:19:31 +02:00
|
|
|
typ := typeof(expr)
|
|
|
|
c.error('json.decode: first argument needs to be a type, got `$typ`', call_expr.pos)
|
2020-05-04 16:46:36 +02:00
|
|
|
return table.void_type
|
|
|
|
}
|
2020-05-06 20:44:46 +02:00
|
|
|
c.expected_type = table.string_type
|
|
|
|
call_expr.args[1].typ = c.expr(call_expr.args[1].expr)
|
2020-05-13 21:59:05 +02:00
|
|
|
if call_expr.args[1].typ != table.string_type {
|
|
|
|
c.error('json.decode: second argument needs to be a string', call_expr.pos)
|
|
|
|
}
|
2020-05-04 16:46:36 +02:00
|
|
|
typ := expr as ast.Type
|
2020-05-21 22:35:43 +02:00
|
|
|
ret_type := typ.typ.set_flag(.optional)
|
|
|
|
call_expr.return_type = ret_type
|
|
|
|
return ret_type
|
2020-05-01 12:02:31 +02:00
|
|
|
}
|
2020-04-12 17:45:04 +02:00
|
|
|
// look for function in format `mod.fn` or `fn` (main/builtin)
|
|
|
|
mut f := table.Fn{}
|
|
|
|
mut found := false
|
2020-04-28 15:27:49 +02:00
|
|
|
mut found_in_args := false
|
2020-04-12 17:45:04 +02:00
|
|
|
// try prefix with current module as it would have never gotten prefixed
|
2020-04-26 06:39:23 +02:00
|
|
|
if !fn_name.contains('.') && call_expr.mod !in ['builtin', 'main'] {
|
2020-04-12 17:45:04 +02:00
|
|
|
name_prefixed := '${call_expr.mod}.$fn_name'
|
|
|
|
if f1 := c.table.find_fn(name_prefixed) {
|
|
|
|
call_expr.name = name_prefixed
|
|
|
|
found = true
|
|
|
|
f = f1
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// already prefixed (mod.fn) or C/builtin/main
|
|
|
|
if !found {
|
|
|
|
if f1 := c.table.find_fn(fn_name) {
|
|
|
|
found = true
|
|
|
|
f = f1
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// check for arg (var) of fn type
|
|
|
|
if !found {
|
|
|
|
scope := c.file.scope.innermost(call_expr.pos.pos)
|
2020-04-15 01:45:27 +02:00
|
|
|
if v := scope.find_var(fn_name) {
|
|
|
|
if v.typ != 0 {
|
|
|
|
vts := c.table.get_type_symbol(v.typ)
|
2020-04-12 17:45:04 +02:00
|
|
|
if vts.kind == .function {
|
|
|
|
info := vts.info as table.FnType
|
|
|
|
f = info.func
|
|
|
|
found = true
|
2020-04-28 15:27:49 +02:00
|
|
|
found_in_args = true
|
2020-04-07 15:15:45 +02:00
|
|
|
}
|
2020-03-30 12:39:20 +02:00
|
|
|
}
|
|
|
|
}
|
2020-04-12 17:45:04 +02:00
|
|
|
}
|
|
|
|
if !found {
|
2020-04-20 07:04:31 +02:00
|
|
|
c.error('unknown function: $fn_name', call_expr.pos)
|
2020-04-12 17:45:04 +02:00
|
|
|
return table.void_type
|
|
|
|
}
|
2020-04-28 15:27:49 +02:00
|
|
|
if !found_in_args && call_expr.mod in ['builtin', 'main'] {
|
|
|
|
scope := c.file.scope.innermost(call_expr.pos.pos)
|
|
|
|
if _ := scope.find_var(fn_name) {
|
2020-04-29 12:20:22 +02:00
|
|
|
c.error('ambiguous call to: `$fn_name`, may refer to fn `$fn_name` or variable `$fn_name`',
|
|
|
|
call_expr.pos)
|
2020-04-28 15:27:49 +02:00
|
|
|
}
|
|
|
|
}
|
2020-05-21 03:58:50 +02:00
|
|
|
if !f.is_pub && f.language == .v && !c.is_builtin_mod && !c.pref.is_test && f.mod != c.mod &&
|
|
|
|
f.name != '' && f.mod != '' {
|
2020-05-18 22:54:08 +02:00
|
|
|
c.warn('function `$f.name` is private. curmod=$c.mod fmod=$f.mod', call_expr.pos)
|
|
|
|
}
|
2020-04-12 17:45:04 +02:00
|
|
|
call_expr.return_type = f.return_type
|
2020-04-25 15:57:11 +02:00
|
|
|
if f.return_type == table.void_type && f.ctdefine.len > 0 && f.ctdefine !in c.pref.compile_defines {
|
|
|
|
call_expr.should_be_skipped = true
|
|
|
|
}
|
2020-05-19 17:12:47 +02:00
|
|
|
if f.language != .v || call_expr.language != .v {
|
2020-04-12 17:45:04 +02:00
|
|
|
for arg in call_expr.args {
|
|
|
|
c.expr(arg.expr)
|
|
|
|
}
|
|
|
|
return f.return_type
|
|
|
|
}
|
|
|
|
min_required_args := if f.is_variadic { f.args.len - 1 } else { f.args.len }
|
|
|
|
if call_expr.args.len < min_required_args {
|
|
|
|
c.error('too few arguments in call to `$fn_name` ($call_expr.args.len instead of $min_required_args)',
|
|
|
|
call_expr.pos)
|
|
|
|
} else if !f.is_variadic && call_expr.args.len > f.args.len {
|
|
|
|
c.error('too many arguments in call to `$fn_name` ($call_expr.args.len instead of $f.args.len)',
|
|
|
|
call_expr.pos)
|
|
|
|
return f.return_type
|
|
|
|
}
|
|
|
|
// println can print anything
|
2020-05-12 19:38:43 +02:00
|
|
|
if (fn_name == 'println' || fn_name == 'print') && call_expr.args.len > 0 {
|
2020-04-12 17:45:04 +02:00
|
|
|
c.expected_type = table.string_type
|
|
|
|
call_expr.args[0].typ = c.expr(call_expr.args[0].expr)
|
2020-05-08 11:48:01 +02:00
|
|
|
/*
|
|
|
|
// TODO: optimize `struct T{} fn (t &T) str() string {return 'abc'} mut a := []&T{} a << &T{} println(a[0])`
|
|
|
|
// It currently generates:
|
|
|
|
// `println(T_str_no_ptr(*(*(T**)array_get(a, 0))));`
|
|
|
|
// ... which works, but could be just:
|
|
|
|
// `println(T_str(*(T**)array_get(a, 0)));`
|
|
|
|
prexpr := call_expr.args[0].expr
|
|
|
|
prtyp := call_expr.args[0].typ
|
|
|
|
prtyp_sym := c.table.get_type_symbol(prtyp)
|
|
|
|
prtyp_is_ptr := prtyp.is_ptr()
|
|
|
|
prhas_str, prexpects_ptr, prnr_args := prtyp_sym.str_method_info()
|
|
|
|
eprintln('>>> println hack typ: ${prtyp} | sym.name: ${prtyp_sym.name} | is_ptr: $prtyp_is_ptr | has_str: $prhas_str | expects_ptr: $prexpects_ptr | nr_args: $prnr_args | expr: ${prexpr.str()} ')
|
|
|
|
*/
|
2020-03-30 12:39:20 +02:00
|
|
|
return f.return_type
|
2020-03-26 11:32:29 +01:00
|
|
|
}
|
2020-04-12 17:45:04 +02:00
|
|
|
// TODO: typ optimize.. this node can get processed more than once
|
|
|
|
if call_expr.expected_arg_types.len == 0 {
|
|
|
|
for arg in f.args {
|
|
|
|
call_expr.expected_arg_types << arg.typ
|
|
|
|
}
|
|
|
|
}
|
|
|
|
for i, call_arg in call_expr.args {
|
|
|
|
arg := if f.is_variadic && i >= f.args.len - 1 { f.args[f.args.len - 1] } else { f.args[i] }
|
|
|
|
c.expected_type = arg.typ
|
|
|
|
typ := c.expr(call_arg.expr)
|
|
|
|
call_expr.args[i].typ = typ
|
|
|
|
typ_sym := c.table.get_type_symbol(typ)
|
|
|
|
arg_typ_sym := c.table.get_type_symbol(arg.typ)
|
2020-04-25 17:49:16 +02:00
|
|
|
if f.is_variadic && typ.flag_is(.variadic) && call_expr.args.len - 1 > i {
|
2020-04-22 05:28:47 +02:00
|
|
|
c.error('when forwarding a varg variable, it must be the final argument', call_expr.pos)
|
|
|
|
}
|
2020-05-06 11:29:37 +02:00
|
|
|
// Handle expected interface
|
|
|
|
if arg_typ_sym.kind == .interface_ {
|
|
|
|
c.type_implements(typ, arg.typ, call_arg.expr.position())
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
// Handle expected interface array
|
|
|
|
/*
|
|
|
|
if exp_type_sym.kind == .array && t.get_type_symbol(t.value_type(exp_idx)).kind == .interface_ {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
*/
|
2020-05-24 04:43:00 +02:00
|
|
|
if !c.check_types(typ, arg.typ) {
|
2020-04-12 17:45:04 +02:00
|
|
|
// str method, allow type with str method if fn arg is string
|
|
|
|
if arg_typ_sym.kind == .string && typ_sym.has_method('str') {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
if typ_sym.kind == .void && arg_typ_sym.kind == .string {
|
|
|
|
continue
|
|
|
|
}
|
2020-04-14 18:09:59 +02:00
|
|
|
if f.is_generic {
|
|
|
|
continue
|
|
|
|
}
|
2020-04-12 17:45:04 +02:00
|
|
|
if typ_sym.kind == .array_fixed {
|
|
|
|
}
|
|
|
|
c.error('cannot use type `$typ_sym.str()` as type `$arg_typ_sym.str()` in argument ${i+1} to `$fn_name`',
|
|
|
|
call_expr.pos)
|
|
|
|
}
|
|
|
|
}
|
2020-05-21 18:15:04 +02:00
|
|
|
if call_expr.generic_type != table.void_type && f.return_type != 0 { // table.t_type {
|
|
|
|
// Handle `foo<T>() T` => `foo<int>() int` => return int
|
|
|
|
sym := c.table.get_type_symbol(f.return_type)
|
|
|
|
if sym.name == 'T' {
|
|
|
|
return call_expr.generic_type
|
|
|
|
}
|
|
|
|
}
|
2020-04-12 17:45:04 +02:00
|
|
|
return f.return_type
|
2020-01-18 23:26:14 +01:00
|
|
|
}
|
|
|
|
|
2020-05-06 11:29:37 +02:00
|
|
|
fn (mut c Checker) type_implements(typ, inter_typ table.Type, pos token.Position) {
|
|
|
|
typ_sym := c.table.get_type_symbol(typ)
|
|
|
|
inter_sym := c.table.get_type_symbol(inter_typ)
|
|
|
|
styp := c.table.type_to_str(typ)
|
|
|
|
for imethod in inter_sym.methods {
|
|
|
|
if method := typ_sym.find_method(imethod.name) {
|
|
|
|
if !imethod.is_same_method_as(method) {
|
2020-05-06 12:26:00 +02:00
|
|
|
c.error('`$styp` incorrectly implements method `$imethod.name` of interface `$inter_sym.name`, expected `${c.table.fn_to_str(imethod)}`',
|
|
|
|
pos)
|
2020-05-06 11:29:37 +02:00
|
|
|
}
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
c.error("`$styp` doesn't implement method `$imethod.name`", pos)
|
|
|
|
}
|
|
|
|
mut inter_info := inter_sym.info as table.Interface
|
|
|
|
if typ !in inter_info.types && typ_sym.kind != .interface_ {
|
|
|
|
inter_info.types << typ
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-05-25 11:32:14 +02:00
|
|
|
// return the actual type of the expression, once the optional is handled
|
|
|
|
pub fn (mut c Checker) check_expr_opt_call(expr ast.Expr, ret_type table.Type) table.Type {
|
2020-05-21 22:35:43 +02:00
|
|
|
if expr is ast.CallExpr {
|
|
|
|
call_expr := expr as ast.CallExpr
|
|
|
|
if call_expr.return_type.flag_is(.optional) {
|
2020-05-25 11:32:14 +02:00
|
|
|
if call_expr.or_block.kind == .absent {
|
|
|
|
if ret_type != table.void_type {
|
|
|
|
c.error('${call_expr.name}() returns an option, but you missed to add an `or {}` block to it',
|
|
|
|
call_expr.pos)
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
c.check_or_expr(call_expr.or_block, ret_type)
|
|
|
|
}
|
|
|
|
// remove optional flag
|
|
|
|
return ret_type.set_flag(.unset)
|
2020-05-23 08:51:15 +02:00
|
|
|
} else if call_expr.or_block.kind == .block {
|
2020-05-21 22:35:43 +02:00
|
|
|
c.error('unexpected `or` block, the function `$call_expr.name` does not return an optional',
|
|
|
|
call_expr.pos)
|
2020-05-23 08:51:15 +02:00
|
|
|
} else if call_expr.or_block.kind == .propagate {
|
|
|
|
c.error('unexpected `?`, the function `$call_expr.name`, does not return an optional',
|
|
|
|
call_expr.pos)
|
2020-04-08 01:19:24 +02:00
|
|
|
}
|
|
|
|
}
|
2020-05-25 11:32:14 +02:00
|
|
|
return ret_type
|
2020-04-08 01:19:24 +02:00
|
|
|
}
|
2020-04-07 16:36:00 +02:00
|
|
|
|
2020-05-25 11:32:14 +02:00
|
|
|
pub fn (mut c Checker) check_or_expr(mut or_expr ast.OrExpr, ret_type table.Type) {
|
|
|
|
if or_expr.kind == .propagate {
|
2020-05-23 08:51:15 +02:00
|
|
|
if !c.cur_fn.return_type.flag_is(.optional) && c.cur_fn.name != 'main' {
|
|
|
|
c.error('to propagate the optional call, `${c.cur_fn.name}` must itself return an optional',
|
2020-05-25 11:32:14 +02:00
|
|
|
or_expr.pos)
|
2020-05-23 08:51:15 +02:00
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|
2020-05-25 11:32:14 +02:00
|
|
|
stmts_len := or_expr.stmts.len
|
2020-04-08 01:19:24 +02:00
|
|
|
if stmts_len == 0 {
|
2020-05-25 11:32:14 +02:00
|
|
|
if ret_type != table.void_type {
|
2020-04-08 01:19:24 +02:00
|
|
|
// x := f() or {}
|
2020-05-25 11:32:14 +02:00
|
|
|
c.error('assignment requires a non empty `or {}` block', or_expr.pos)
|
2020-04-08 01:19:24 +02:00
|
|
|
return
|
|
|
|
}
|
|
|
|
// allow `f() or {}`
|
|
|
|
return
|
|
|
|
}
|
2020-05-25 11:32:14 +02:00
|
|
|
last_stmt := or_expr.stmts[stmts_len - 1]
|
|
|
|
if ret_type != table.void_type {
|
2020-05-23 08:51:15 +02:00
|
|
|
if !(last_stmt is ast.Return || last_stmt is ast.BranchStmt || last_stmt is ast.ExprStmt) {
|
2020-04-08 01:19:24 +02:00
|
|
|
expected_type_name := c.table.get_type_symbol(ret_type).name
|
2020-05-06 08:10:40 +02:00
|
|
|
c.error('last statement in the `or {}` block should return `$expected_type_name`',
|
2020-05-25 11:32:14 +02:00
|
|
|
or_expr.pos)
|
2020-04-08 01:19:24 +02:00
|
|
|
return
|
|
|
|
}
|
|
|
|
match last_stmt {
|
|
|
|
ast.ExprStmt {
|
2020-05-21 22:35:43 +02:00
|
|
|
it.typ = c.expr(it.expr)
|
2020-05-24 04:43:00 +02:00
|
|
|
type_fits := c.check_types(it.typ, ret_type)
|
2020-04-08 01:19:24 +02:00
|
|
|
is_panic_or_exit := is_expr_panic_or_exit(it.expr)
|
|
|
|
if type_fits || is_panic_or_exit {
|
|
|
|
return
|
2020-04-07 16:36:00 +02:00
|
|
|
}
|
2020-05-21 22:35:43 +02:00
|
|
|
type_name := c.table.get_type_symbol(it.typ).name
|
2020-04-07 16:36:00 +02:00
|
|
|
expected_type_name := c.table.get_type_symbol(ret_type).name
|
2020-04-08 14:19:13 +02:00
|
|
|
c.error('wrong return type `$type_name` in the `or {}` block, expected `$expected_type_name`',
|
|
|
|
it.pos)
|
2020-04-08 01:19:24 +02:00
|
|
|
return
|
2020-04-07 16:36:00 +02:00
|
|
|
}
|
2020-04-08 01:19:24 +02:00
|
|
|
ast.BranchStmt {
|
2020-04-26 06:39:23 +02:00
|
|
|
if it.tok.kind !in [.key_continue, .key_break] {
|
2020-04-08 14:19:13 +02:00
|
|
|
c.error('only break/continue is allowed as a branch statement in the end of an `or {}` block',
|
|
|
|
it.tok.position())
|
2020-04-08 01:19:24 +02:00
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {}
|
2020-04-07 16:36:00 +02:00
|
|
|
}
|
2020-04-08 01:19:24 +02:00
|
|
|
return
|
2020-04-07 16:36:00 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn is_expr_panic_or_exit(expr ast.Expr) bool {
|
|
|
|
match expr {
|
2020-04-25 17:49:16 +02:00
|
|
|
ast.CallExpr { return it.name in ['panic', 'exit'] }
|
|
|
|
else { return false }
|
2020-04-07 16:36:00 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-05-11 14:38:25 +02:00
|
|
|
pub fn (mut c Checker) selector_expr(mut selector_expr ast.SelectorExpr) table.Type {
|
2020-02-06 13:57:35 +01:00
|
|
|
typ := c.expr(selector_expr.expr)
|
2020-03-11 05:56:15 +01:00
|
|
|
if typ == table.void_type_idx {
|
|
|
|
c.error('unknown selector expression', selector_expr.pos)
|
|
|
|
return table.void_type
|
|
|
|
}
|
2020-03-07 04:45:35 +01:00
|
|
|
selector_expr.expr_type = typ
|
2020-03-11 06:30:20 +01:00
|
|
|
// println('sel expr line_nr=$selector_expr.pos.line_nr typ=$selector_expr.expr_type')
|
2020-02-10 08:32:08 +01:00
|
|
|
typ_sym := c.table.get_type_symbol(typ)
|
2020-05-09 15:16:48 +02:00
|
|
|
field_name := selector_expr.field_name
|
2020-02-29 09:04:47 +01:00
|
|
|
// variadic
|
2020-04-25 09:08:53 +02:00
|
|
|
if typ.flag_is(.variadic) {
|
2020-02-29 09:04:47 +01:00
|
|
|
if field_name == 'len' {
|
|
|
|
return table.int_type
|
|
|
|
}
|
|
|
|
}
|
2020-03-11 10:48:45 +01:00
|
|
|
if field := c.table.struct_find_field(typ_sym, field_name) {
|
2020-05-11 14:38:25 +02:00
|
|
|
if typ_sym.mod != c.mod && !field.is_pub {
|
|
|
|
c.error('field `${typ_sym.name}.$field_name` is not public', selector_expr.pos)
|
|
|
|
}
|
2020-03-11 10:48:45 +01:00
|
|
|
return field.typ
|
2020-01-18 23:26:14 +01:00
|
|
|
}
|
2020-02-10 08:32:08 +01:00
|
|
|
if typ_sym.kind != .struct_ {
|
|
|
|
c.error('`$typ_sym.name` is not a struct', selector_expr.pos)
|
2020-04-07 15:15:45 +02:00
|
|
|
} else {
|
2020-02-10 08:32:08 +01:00
|
|
|
c.error('unknown field `${typ_sym.name}.$field_name`', selector_expr.pos)
|
2020-02-08 16:59:57 +01:00
|
|
|
}
|
2020-02-22 12:58:16 +01:00
|
|
|
return table.void_type
|
2020-01-18 23:26:14 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// TODO: non deferred
|
2020-05-11 14:38:25 +02:00
|
|
|
pub fn (mut c Checker) return_stmt(mut return_stmt ast.Return) {
|
2020-05-23 08:51:15 +02:00
|
|
|
c.expected_type = c.cur_fn.return_type
|
|
|
|
if return_stmt.exprs.len > 0 && c.expected_type == table.void_type {
|
2020-04-11 02:24:00 +02:00
|
|
|
c.error('too many arguments to return, current function does not return anything',
|
2020-04-10 18:11:43 +02:00
|
|
|
return_stmt.pos)
|
2020-01-22 21:34:38 +01:00
|
|
|
return
|
2020-05-23 08:51:15 +02:00
|
|
|
} else if return_stmt.exprs.len == 0 && c.expected_type != table.void_type {
|
2020-04-11 04:09:41 +02:00
|
|
|
c.error('too few arguments to return', return_stmt.pos)
|
2020-03-22 13:19:45 +01:00
|
|
|
return
|
|
|
|
}
|
2020-04-10 18:11:43 +02:00
|
|
|
if return_stmt.exprs.len == 0 {
|
|
|
|
return
|
|
|
|
}
|
2020-05-23 08:51:15 +02:00
|
|
|
expected_type := c.expected_type
|
2020-02-10 08:32:08 +01:00
|
|
|
expected_type_sym := c.table.get_type_symbol(expected_type)
|
2020-04-25 09:08:53 +02:00
|
|
|
exp_is_optional := expected_type.flag_is(.optional)
|
2020-02-06 13:57:35 +01:00
|
|
|
mut expected_types := [expected_type]
|
2020-02-10 08:32:08 +01:00
|
|
|
if expected_type_sym.kind == .multi_return {
|
|
|
|
mr_info := expected_type_sym.info as table.MultiReturn
|
2020-02-06 13:57:35 +01:00
|
|
|
expected_types = mr_info.types
|
2020-01-18 23:26:14 +01:00
|
|
|
}
|
2020-04-26 09:17:13 +02:00
|
|
|
mut got_types := []table.Type{}
|
2020-03-05 13:57:05 +01:00
|
|
|
for expr in return_stmt.exprs {
|
|
|
|
typ := c.expr(expr)
|
|
|
|
got_types << typ
|
|
|
|
}
|
2020-03-16 07:42:45 +01:00
|
|
|
return_stmt.types = got_types
|
2020-03-02 03:58:36 +01:00
|
|
|
// allow `none` & `error (Option)` return types for function that returns optional
|
2020-04-25 09:08:53 +02:00
|
|
|
if exp_is_optional && got_types[0].idx() in [table.none_type_idx, c.table.type_idxs['Option']] {
|
2020-03-02 03:58:36 +01:00
|
|
|
return
|
|
|
|
}
|
2020-02-06 13:57:35 +01:00
|
|
|
if expected_types.len > 0 && expected_types.len != got_types.len {
|
2020-04-04 05:14:40 +02:00
|
|
|
// c.error('wrong number of return arguments:\n\texpected: $expected_table.str()\n\tgot: $got_types.str()', return_stmt.pos)
|
2020-03-21 07:01:06 +01:00
|
|
|
c.error('wrong number of return arguments', return_stmt.pos)
|
2020-01-18 23:26:14 +01:00
|
|
|
}
|
2020-05-21 21:51:49 +02:00
|
|
|
for i, exp_type in expected_types {
|
2020-02-06 13:57:35 +01:00
|
|
|
got_typ := got_types[i]
|
2020-05-25 05:32:33 +02:00
|
|
|
is_generic := exp_type == table.t_type
|
|
|
|
ok := if is_generic { c.check_types(got_typ, c.cur_generic_type) || got_typ == exp_type } else { c.check_types(got_typ,
|
2020-05-21 21:51:49 +02:00
|
|
|
exp_type) }
|
2020-05-25 05:32:33 +02:00
|
|
|
// ok := c.check_types(got_typ, exp_type)
|
2020-05-21 21:51:49 +02:00
|
|
|
if !ok { // !c.table.check(got_typ, exp_typ) {
|
2020-02-10 08:32:08 +01:00
|
|
|
got_typ_sym := c.table.get_type_symbol(got_typ)
|
2020-05-25 05:32:33 +02:00
|
|
|
mut exp_typ_sym := c.table.get_type_symbol(exp_type)
|
2020-04-17 16:16:56 +02:00
|
|
|
pos := return_stmt.exprs[i].position()
|
2020-05-25 05:32:33 +02:00
|
|
|
if is_generic {
|
|
|
|
exp_typ_sym = c.table.get_type_symbol(c.cur_generic_type)
|
|
|
|
}
|
2020-04-25 17:49:16 +02:00
|
|
|
c.error('cannot use `$got_typ_sym.name` as type `$exp_typ_sym.name` in return argument',
|
|
|
|
pos)
|
2020-01-18 23:26:14 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-04-25 17:49:16 +02:00
|
|
|
pub fn (mut c Checker) enum_decl(decl ast.EnumDecl) {
|
2020-05-16 16:12:23 +02:00
|
|
|
c.check_valid_pascal_case(decl.name, 'enum name', decl.pos)
|
2020-04-30 09:34:18 +02:00
|
|
|
for i, field in decl.fields {
|
2020-05-16 16:12:23 +02:00
|
|
|
if util.contains_capital(field.name) {
|
|
|
|
c.error('field name `$field.name` cannot contain uppercase letters, use snake_case instead',
|
|
|
|
field.pos)
|
|
|
|
}
|
2020-04-30 18:06:14 +02:00
|
|
|
for j in 0 .. i {
|
2020-04-30 09:34:18 +02:00
|
|
|
if field.name == decl.fields[j].name {
|
|
|
|
c.error('field name `$field.name` duplicate', field.pos)
|
|
|
|
}
|
|
|
|
}
|
2020-04-10 14:44:01 +02:00
|
|
|
if field.has_expr {
|
|
|
|
match field.expr {
|
2020-04-09 19:23:49 +02:00
|
|
|
ast.IntegerLiteral {}
|
|
|
|
ast.PrefixExpr {}
|
|
|
|
else {
|
2020-04-19 22:26:04 +02:00
|
|
|
if field.expr is ast.Ident {
|
|
|
|
expr := field.expr as ast.Ident
|
2020-05-19 17:12:47 +02:00
|
|
|
if expr.language == .c {
|
2020-04-19 22:26:04 +02:00
|
|
|
continue
|
|
|
|
}
|
|
|
|
}
|
2020-04-17 02:38:39 +02:00
|
|
|
mut pos := field.expr.position()
|
2020-04-10 00:09:34 +02:00
|
|
|
if pos.pos == 0 {
|
|
|
|
pos = field.pos
|
|
|
|
}
|
2020-04-11 04:09:41 +02:00
|
|
|
c.error('default value for enum has to be an integer', pos)
|
2020-04-09 19:23:49 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-05-11 14:38:25 +02:00
|
|
|
pub fn (mut c Checker) assign_stmt(mut assign_stmt ast.AssignStmt) {
|
2020-04-25 17:49:16 +02:00
|
|
|
c.expected_type = table.none_type // TODO a hack to make `x := if ... work`
|
2020-05-08 22:49:45 +02:00
|
|
|
right_first := assign_stmt.right[0]
|
|
|
|
mut right_len := assign_stmt.right.len
|
|
|
|
if right_first is ast.CallExpr || right_first is ast.IfExpr || right_first is ast.MatchExpr {
|
2020-05-02 11:45:08 +02:00
|
|
|
right_type0 := c.expr(assign_stmt.right[0])
|
|
|
|
assign_stmt.right_types = [right_type0]
|
|
|
|
right_type_sym0 := c.table.get_type_symbol(right_type0)
|
2020-05-25 11:32:14 +02:00
|
|
|
if right_type0 == table.void_type {
|
|
|
|
right_len = 0
|
|
|
|
} else if right_type_sym0.kind == .multi_return {
|
2020-05-02 11:45:08 +02:00
|
|
|
assign_stmt.right_types = right_type_sym0.mr_info().types
|
|
|
|
right_len = assign_stmt.right_types.len
|
|
|
|
}
|
|
|
|
if assign_stmt.left.len != right_len {
|
2020-05-08 22:49:45 +02:00
|
|
|
if right_first is ast.CallExpr {
|
|
|
|
call_expr := assign_stmt.right[0] as ast.CallExpr
|
|
|
|
c.error('assignment mismatch: $assign_stmt.left.len variable(s) but `${call_expr.name}()` returns $right_len value(s)',
|
|
|
|
assign_stmt.pos)
|
|
|
|
return
|
|
|
|
} else {
|
|
|
|
c.error('assignment mismatch: $assign_stmt.left.len variable(s) $right_len value(s)',
|
|
|
|
assign_stmt.pos)
|
|
|
|
return
|
|
|
|
}
|
2020-05-02 11:45:08 +02:00
|
|
|
}
|
2020-05-08 22:49:45 +02:00
|
|
|
} else if assign_stmt.left.len != right_len {
|
|
|
|
c.error('assignment mismatch: $assign_stmt.left.len variable(s) $assign_stmt.right.len value(s)',
|
|
|
|
assign_stmt.pos)
|
|
|
|
return
|
2020-05-02 11:45:08 +02:00
|
|
|
}
|
|
|
|
mut scope := c.file.scope.innermost(assign_stmt.pos.pos)
|
|
|
|
for i, _ in assign_stmt.left {
|
|
|
|
mut ident := assign_stmt.left[i]
|
2020-05-08 22:49:45 +02:00
|
|
|
if assign_stmt.right_types.len < right_len {
|
2020-05-25 11:32:14 +02:00
|
|
|
right_type := c.expr(assign_stmt.right[i])
|
|
|
|
assign_stmt.right_types << c.check_expr_opt_call(assign_stmt.right[i], right_type)
|
|
|
|
} else if i < assign_stmt.right.len { // only once for multi return
|
|
|
|
c.check_expr_opt_call(assign_stmt.right[i], assign_stmt.right_types[i])
|
2020-05-02 11:45:08 +02:00
|
|
|
}
|
|
|
|
val_type := assign_stmt.right_types[i]
|
|
|
|
// check variable name for beginning with capital letter 'Abc'
|
2020-04-16 12:17:15 +02:00
|
|
|
is_decl := assign_stmt.op == .decl_assign
|
2020-05-16 16:12:23 +02:00
|
|
|
if is_decl && ident.name != '_' {
|
|
|
|
c.check_valid_snake_case(ident.name, 'variable name', ident.pos)
|
2020-04-15 19:09:51 +02:00
|
|
|
}
|
2020-05-02 11:45:08 +02:00
|
|
|
mut ident_var_info := ident.var_info()
|
|
|
|
if assign_stmt.op == .assign {
|
2020-05-23 17:30:28 +02:00
|
|
|
c.fail_if_immutable(ident)
|
2020-05-02 11:45:08 +02:00
|
|
|
var_type := c.expr(ident)
|
|
|
|
assign_stmt.left_types << var_type
|
2020-05-24 04:43:00 +02:00
|
|
|
if !c.check_types(val_type, var_type) {
|
2020-05-02 11:45:08 +02:00
|
|
|
val_type_sym := c.table.get_type_symbol(val_type)
|
|
|
|
var_type_sym := c.table.get_type_symbol(var_type)
|
|
|
|
c.error('assign stmt: cannot use `$val_type_sym.name` as `$var_type_sym.name`',
|
|
|
|
assign_stmt.pos)
|
2020-03-10 12:01:37 +01:00
|
|
|
}
|
|
|
|
}
|
2020-05-02 11:45:08 +02:00
|
|
|
ident_var_info.typ = val_type
|
|
|
|
ident.info = ident_var_info
|
|
|
|
assign_stmt.left[i] = ident
|
|
|
|
scope.update_var_type(ident.name, val_type)
|
2020-02-27 16:51:39 +01:00
|
|
|
}
|
2020-03-18 16:07:52 +01:00
|
|
|
c.expected_type = table.void_type
|
2020-02-27 00:12:37 +01:00
|
|
|
}
|
2020-02-06 17:38:02 +01:00
|
|
|
|
2020-05-11 14:38:25 +02:00
|
|
|
pub fn (mut c Checker) array_init(mut array_init ast.ArrayInit) table.Type {
|
2020-03-24 16:18:37 +01:00
|
|
|
// println('checker: array init $array_init.pos.line_nr $c.file.path')
|
2020-02-10 08:32:08 +01:00
|
|
|
mut elem_type := table.void_type
|
2020-03-04 02:50:32 +01:00
|
|
|
// []string - was set in parser
|
|
|
|
if array_init.typ != table.void_type {
|
2020-05-04 13:22:09 +02:00
|
|
|
if array_init.exprs.len == 0 {
|
|
|
|
if array_init.has_cap {
|
|
|
|
if c.expr(array_init.cap_expr) != table.int_type {
|
|
|
|
c.error('array cap needs to be an int', array_init.pos)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if array_init.has_len {
|
|
|
|
if c.expr(array_init.len_expr) != table.int_type {
|
|
|
|
c.error('array len needs to be an int', array_init.pos)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2020-03-04 02:50:32 +01:00
|
|
|
return array_init.typ
|
|
|
|
}
|
2020-02-15 13:37:48 +01:00
|
|
|
// a = []
|
2020-03-04 02:50:32 +01:00
|
|
|
if array_init.exprs.len == 0 {
|
2020-04-26 17:52:27 +02:00
|
|
|
if array_init.has_cap {
|
|
|
|
if c.expr(array_init.cap_expr) != table.int_type {
|
|
|
|
c.error('array cap needs to be an int', array_init.pos)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if array_init.has_len {
|
|
|
|
if c.expr(array_init.len_expr) != table.int_type {
|
|
|
|
c.error('array len needs to be an int', array_init.pos)
|
|
|
|
}
|
|
|
|
}
|
2020-03-06 14:06:52 +01:00
|
|
|
type_sym := c.table.get_type_symbol(c.expected_type)
|
2020-04-04 13:32:16 +02:00
|
|
|
if type_sym.kind != .array {
|
2020-05-05 00:20:32 +02:00
|
|
|
c.error('array_init: no type specified (maybe: `[]Type{}` instead of `[]`)', array_init.pos)
|
2020-04-04 13:32:16 +02:00
|
|
|
return table.void_type
|
|
|
|
}
|
|
|
|
// TODO: seperate errors once bug is fixed with `x := if expr { ... } else { ... }`
|
|
|
|
// if c.expected_type == table.void_type {
|
2020-05-05 00:20:32 +02:00
|
|
|
// c.error('array_init: use `[]Type{}` instead of `[]`', array_init.pos)
|
2020-04-07 15:15:45 +02:00
|
|
|
// return table.void_type
|
2020-04-04 13:32:16 +02:00
|
|
|
// }
|
2020-03-06 14:06:52 +01:00
|
|
|
array_info := type_sym.array_info()
|
|
|
|
array_init.elem_type = array_info.elem_type
|
2020-03-04 02:50:32 +01:00
|
|
|
return c.expected_type
|
|
|
|
}
|
|
|
|
// [1,2,3]
|
|
|
|
if array_init.exprs.len > 0 && array_init.elem_type == table.void_type {
|
2020-04-29 12:20:22 +02:00
|
|
|
mut expected_value_type := table.void_type
|
|
|
|
mut expecting_interface_array := false
|
|
|
|
cap := array_init.exprs.len
|
|
|
|
mut interface_types := []table.Type{cap: cap}
|
|
|
|
if c.expected_type != 0 {
|
|
|
|
expected_value_type = c.table.value_type(c.expected_type)
|
|
|
|
if c.table.get_type_symbol(expected_value_type).kind == .interface_ {
|
|
|
|
// Array of interfaces? (`[dog, cat]`) Save the interface type (`Animal`)
|
|
|
|
expecting_interface_array = true
|
|
|
|
array_init.interface_type = expected_value_type
|
|
|
|
array_init.is_interface = true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// expecting_interface_array := c.expected_type != 0 &&
|
|
|
|
// c.table.get_type_symbol(c.table.value_type(c.expected_type)).kind == .interface_
|
|
|
|
//
|
2020-04-26 09:17:13 +02:00
|
|
|
// if expecting_interface_array {
|
|
|
|
// println('ex $c.expected_type')
|
|
|
|
// }
|
2020-03-04 02:50:32 +01:00
|
|
|
for i, expr in array_init.exprs {
|
|
|
|
typ := c.expr(expr)
|
2020-04-29 12:20:22 +02:00
|
|
|
if expecting_interface_array {
|
|
|
|
if i == 0 {
|
|
|
|
elem_type = expected_value_type
|
|
|
|
c.expected_type = elem_type
|
|
|
|
}
|
|
|
|
interface_types << typ
|
|
|
|
continue
|
|
|
|
}
|
2020-03-04 02:50:32 +01:00
|
|
|
// The first element's type
|
|
|
|
if i == 0 {
|
|
|
|
elem_type = typ
|
|
|
|
c.expected_type = typ
|
|
|
|
continue
|
|
|
|
}
|
2020-05-24 04:43:00 +02:00
|
|
|
if !c.check_types(elem_type, typ) {
|
2020-03-04 02:50:32 +01:00
|
|
|
elem_type_sym := c.table.get_type_symbol(elem_type)
|
|
|
|
c.error('expected array element with type `$elem_type_sym.name`', array_init.pos)
|
|
|
|
}
|
2020-01-19 13:52:34 +01:00
|
|
|
}
|
2020-04-29 12:20:22 +02:00
|
|
|
if expecting_interface_array {
|
|
|
|
array_init.interface_types = interface_types
|
|
|
|
}
|
2020-04-25 18:26:38 +02:00
|
|
|
if array_init.is_fixed {
|
|
|
|
idx := c.table.find_or_register_array_fixed(elem_type, array_init.exprs.len, 1)
|
|
|
|
array_init.typ = table.new_type(idx)
|
|
|
|
} else {
|
|
|
|
idx := c.table.find_or_register_array(elem_type, 1)
|
|
|
|
array_init.typ = table.new_type(idx)
|
|
|
|
}
|
2020-03-06 14:06:52 +01:00
|
|
|
array_init.elem_type = elem_type
|
2020-04-10 21:00:54 +02:00
|
|
|
} else if array_init.is_fixed && array_init.exprs.len == 1 && array_init.elem_type != table.void_type {
|
2020-04-07 15:15:45 +02:00
|
|
|
// [50]byte
|
2020-03-04 02:50:32 +01:00
|
|
|
mut fixed_size := 1
|
|
|
|
match array_init.exprs[0] {
|
|
|
|
ast.IntegerLiteral {
|
2020-03-17 02:49:15 +01:00
|
|
|
fixed_size = it.val.int()
|
2020-03-04 02:50:32 +01:00
|
|
|
}
|
2020-04-08 19:15:16 +02:00
|
|
|
ast.Ident {
|
2020-04-11 04:09:41 +02:00
|
|
|
// if obj := c.file.global_scope.find_const(it.name) {
|
|
|
|
// if obj := scope.find(it.name) {
|
|
|
|
// scope := c.file.scope.innermost(array_init.pos.pos)
|
|
|
|
// eprintln('scope: ${scope.str()}')
|
|
|
|
// scope.find(it.name) or {
|
|
|
|
// c.error('undefined: `$it.name`', array_init.pos)
|
|
|
|
// }
|
|
|
|
mut full_const_name := if it.mod == 'main' { it.name } else { it.mod + '.' +
|
|
|
|
it.name }
|
|
|
|
if obj := c.file.global_scope.find_const(full_const_name) {
|
2020-04-11 21:31:54 +02:00
|
|
|
if cint := const_int_value(obj) {
|
|
|
|
fixed_size = cint
|
|
|
|
}
|
2020-04-08 19:15:16 +02:00
|
|
|
} else {
|
2020-04-11 04:09:41 +02:00
|
|
|
c.error('non existant integer const $full_const_name while initializing the size of a static array',
|
|
|
|
array_init.pos)
|
2020-04-08 19:15:16 +02:00
|
|
|
}
|
2020-04-11 04:09:41 +02:00
|
|
|
}
|
|
|
|
else {
|
2020-03-04 02:50:32 +01:00
|
|
|
c.error('expecting `int` for fixed size', array_init.pos)
|
|
|
|
}
|
2020-04-07 15:15:45 +02:00
|
|
|
}
|
2020-03-04 02:50:32 +01:00
|
|
|
idx := c.table.find_or_register_array_fixed(array_init.elem_type, fixed_size, 1)
|
2020-02-19 14:34:44 +01:00
|
|
|
array_type := table.new_type(idx)
|
|
|
|
array_init.typ = array_type
|
|
|
|
}
|
2020-02-06 13:57:35 +01:00
|
|
|
return array_init.typ
|
2020-01-19 13:52:34 +01:00
|
|
|
}
|
|
|
|
|
2020-04-11 12:35:41 +02:00
|
|
|
fn const_int_value(cfield ast.ConstField) ?int {
|
|
|
|
if cint := is_const_integer(cfield) {
|
|
|
|
return cint.val.int()
|
|
|
|
}
|
2020-04-11 21:31:54 +02:00
|
|
|
return none
|
2020-04-11 12:35:41 +02:00
|
|
|
}
|
|
|
|
|
2020-04-10 21:00:54 +02:00
|
|
|
fn is_const_integer(cfield ast.ConstField) ?ast.IntegerLiteral {
|
|
|
|
match cfield.expr {
|
2020-05-11 12:02:28 +02:00
|
|
|
ast.IntegerLiteral { return it }
|
2020-04-10 21:00:54 +02:00
|
|
|
else {}
|
|
|
|
}
|
|
|
|
return none
|
|
|
|
}
|
|
|
|
|
2020-04-25 17:49:16 +02:00
|
|
|
fn (mut c Checker) stmt(node ast.Stmt) {
|
2020-02-27 00:12:37 +01:00
|
|
|
// c.expected_type = table.void_type
|
2020-02-03 10:27:06 +01:00
|
|
|
match mut node {
|
2020-03-26 10:27:46 +01:00
|
|
|
ast.AssertStmt {
|
2020-04-08 04:47:29 +02:00
|
|
|
assert_type := c.expr(it.expr)
|
|
|
|
if assert_type != table.bool_type_idx {
|
|
|
|
atype_name := c.table.get_type_symbol(assert_type).name
|
2020-04-08 14:19:13 +02:00
|
|
|
c.error('assert can be used only with `bool` expressions, but found `${atype_name}` instead',
|
|
|
|
it.pos)
|
2020-04-08 04:47:29 +02:00
|
|
|
}
|
2020-03-26 10:27:46 +01:00
|
|
|
}
|
2020-04-26 06:40:54 +02:00
|
|
|
// ast.Attr {}
|
2020-02-06 17:38:02 +01:00
|
|
|
ast.AssignStmt {
|
2020-03-10 12:01:37 +01:00
|
|
|
c.assign_stmt(mut it)
|
2020-02-06 17:38:02 +01:00
|
|
|
}
|
2020-03-25 14:24:48 +01:00
|
|
|
ast.Block {
|
|
|
|
c.stmts(it.stmts)
|
|
|
|
}
|
2020-04-07 12:29:11 +02:00
|
|
|
ast.BranchStmt {
|
|
|
|
if c.in_for_count == 0 {
|
|
|
|
c.error('$it.tok.lit statement not within a loop', it.tok.position())
|
|
|
|
}
|
|
|
|
}
|
2020-03-22 13:36:27 +01:00
|
|
|
ast.CompIf {
|
2020-03-22 13:55:39 +01:00
|
|
|
// c.expr(it.cond)
|
2020-03-22 13:36:27 +01:00
|
|
|
c.stmts(it.stmts)
|
2020-03-22 14:56:13 +01:00
|
|
|
if it.has_else {
|
|
|
|
c.stmts(it.else_stmts)
|
|
|
|
}
|
2020-03-22 13:36:27 +01:00
|
|
|
}
|
2020-02-15 13:37:48 +01:00
|
|
|
ast.ConstDecl {
|
2020-04-26 09:17:13 +02:00
|
|
|
mut field_names := []string{}
|
|
|
|
mut field_order := []int{}
|
2020-04-04 05:14:40 +02:00
|
|
|
for i, field in it.fields {
|
2020-05-16 16:12:23 +02:00
|
|
|
// TODO Check const name once the syntax is decided
|
2020-04-30 12:17:31 +02:00
|
|
|
if field.name in c.const_names {
|
|
|
|
c.error('field name `$field.name` duplicate', field.pos)
|
|
|
|
}
|
|
|
|
c.const_names << field.name
|
2020-04-04 05:14:40 +02:00
|
|
|
field_names << field.name
|
|
|
|
field_order << i
|
|
|
|
}
|
|
|
|
mut needs_order := false
|
2020-04-26 09:17:13 +02:00
|
|
|
mut done_fields := []int{}
|
2020-04-04 05:14:40 +02:00
|
|
|
for i, field in it.fields {
|
2020-04-04 08:05:26 +02:00
|
|
|
c.const_decl = field.name
|
2020-04-04 05:14:40 +02:00
|
|
|
c.const_deps << field.name
|
|
|
|
typ := c.expr(field.expr)
|
|
|
|
it.fields[i].typ = typ
|
2020-04-04 08:05:26 +02:00
|
|
|
for cd in c.const_deps {
|
|
|
|
for j, f in it.fields {
|
2020-04-26 06:39:23 +02:00
|
|
|
if j != i && cd in field_names && cd == f.name && j !in done_fields {
|
2020-04-04 08:05:26 +02:00
|
|
|
needs_order = true
|
|
|
|
x := field_order[j]
|
|
|
|
field_order[j] = field_order[i]
|
|
|
|
field_order[i] = x
|
|
|
|
break
|
2020-04-02 19:39:17 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2020-04-04 08:05:26 +02:00
|
|
|
done_fields << i
|
|
|
|
c.const_deps = []
|
2020-04-02 19:39:17 +02:00
|
|
|
}
|
2020-04-04 05:14:40 +02:00
|
|
|
if needs_order {
|
2020-04-26 09:17:13 +02:00
|
|
|
mut ordered_fields := []ast.ConstField{}
|
2020-04-04 05:14:40 +02:00
|
|
|
for order in field_order {
|
|
|
|
ordered_fields << it.fields[order]
|
2020-04-02 21:31:36 +02:00
|
|
|
}
|
2020-04-04 05:14:40 +02:00
|
|
|
it.fields = ordered_fields
|
2020-04-02 19:39:17 +02:00
|
|
|
}
|
2020-02-15 13:37:48 +01:00
|
|
|
}
|
2020-04-09 19:23:49 +02:00
|
|
|
ast.DeferStmt {
|
|
|
|
c.stmts(it.stmts)
|
|
|
|
}
|
|
|
|
ast.EnumDecl {
|
|
|
|
c.enum_decl(it)
|
|
|
|
}
|
2020-03-01 05:14:36 +01:00
|
|
|
ast.ExprStmt {
|
2020-05-21 22:35:43 +02:00
|
|
|
it.typ = c.expr(it.expr)
|
2020-03-19 15:18:29 +01:00
|
|
|
c.expected_type = table.void_type
|
2020-05-25 11:32:14 +02:00
|
|
|
c.check_expr_opt_call(it.expr, table.void_type)
|
|
|
|
// TODO This should work, even if it's prolly useless .-.
|
|
|
|
// it.typ = c.check_expr_opt_call(it.expr, table.void_type)
|
2020-03-01 05:14:36 +01:00
|
|
|
}
|
|
|
|
ast.FnDecl {
|
2020-05-18 22:54:08 +02:00
|
|
|
c.fn_decl(it)
|
2020-02-03 07:02:54 +01:00
|
|
|
}
|
|
|
|
ast.ForCStmt {
|
2020-04-07 12:29:11 +02:00
|
|
|
c.in_for_count++
|
2020-02-03 07:02:54 +01:00
|
|
|
c.stmt(it.init)
|
|
|
|
c.expr(it.cond)
|
2020-03-05 00:43:02 +01:00
|
|
|
// c.stmt(it.inc)
|
|
|
|
c.expr(it.inc)
|
2020-03-18 13:18:18 +01:00
|
|
|
c.stmts(it.stmts)
|
2020-04-07 12:29:11 +02:00
|
|
|
c.in_for_count--
|
2020-02-03 07:02:54 +01:00
|
|
|
}
|
2020-03-11 19:00:51 +01:00
|
|
|
ast.ForInStmt {
|
2020-04-07 12:29:11 +02:00
|
|
|
c.in_for_count++
|
2020-03-18 13:18:18 +01:00
|
|
|
typ := c.expr(it.cond)
|
2020-04-25 09:08:53 +02:00
|
|
|
typ_idx := typ.idx()
|
2020-03-18 13:18:18 +01:00
|
|
|
if it.is_range {
|
2020-04-25 09:08:53 +02:00
|
|
|
high_type_idx := c.expr(it.high).idx()
|
2020-04-25 06:14:17 +02:00
|
|
|
if typ_idx in table.integer_type_idxs && high_type_idx !in table.integer_type_idxs {
|
2020-04-20 00:21:16 +02:00
|
|
|
c.error('range types do not match', it.cond.position())
|
2020-04-25 06:14:17 +02:00
|
|
|
} else if typ_idx in table.float_type_idxs || high_type_idx in table.float_type_idxs {
|
2020-04-20 00:21:16 +02:00
|
|
|
c.error('range type can not be float', it.cond.position())
|
2020-04-25 06:14:17 +02:00
|
|
|
} else if typ_idx == table.bool_type_idx || high_type_idx == table.bool_type_idx {
|
2020-04-20 00:21:16 +02:00
|
|
|
c.error('range type can not be bool', it.cond.position())
|
2020-04-25 06:14:17 +02:00
|
|
|
} else if typ_idx == table.string_type_idx || high_type_idx == table.string_type_idx {
|
2020-04-20 00:21:16 +02:00
|
|
|
c.error('range type can not be string', it.cond.position())
|
|
|
|
}
|
2020-03-18 13:18:18 +01:00
|
|
|
c.expr(it.high)
|
2020-04-07 15:15:45 +02:00
|
|
|
} else {
|
2020-03-18 13:18:18 +01:00
|
|
|
mut scope := c.file.scope.innermost(it.pos.pos)
|
2020-03-21 10:22:16 +01:00
|
|
|
sym := c.table.get_type_symbol(typ)
|
2020-05-10 02:28:43 +02:00
|
|
|
if sym.kind == .map && !(it.key_var.len > 0 && it.val_var.len > 0) {
|
2020-05-21 03:58:50 +02:00
|
|
|
c.error('declare a key and a value variable when ranging a map: `for key, val in map {`\n' +
|
|
|
|
'use `_` if you do not need the variable', it.pos)
|
2020-05-10 02:28:43 +02:00
|
|
|
}
|
2020-03-18 13:18:18 +01:00
|
|
|
if it.key_var.len > 0 {
|
|
|
|
key_type := match sym.kind {
|
2020-04-25 17:49:16 +02:00
|
|
|
.map { sym.map_info().key_type }
|
|
|
|
else { table.int_type }
|
2020-04-07 12:29:11 +02:00
|
|
|
}
|
2020-03-24 07:25:10 +01:00
|
|
|
it.key_type = key_type
|
2020-03-28 14:00:28 +01:00
|
|
|
scope.update_var_type(it.key_var, key_type)
|
2020-03-18 13:18:18 +01:00
|
|
|
}
|
|
|
|
value_type := c.table.value_type(typ)
|
|
|
|
if value_type == table.void_type {
|
|
|
|
typ_sym := c.table.get_type_symbol(typ)
|
2020-04-17 02:38:39 +02:00
|
|
|
c.error('for in: cannot index `$typ_sym.name`', it.cond.position())
|
2020-03-18 13:18:18 +01:00
|
|
|
}
|
2020-03-24 07:25:10 +01:00
|
|
|
it.cond_type = typ
|
2020-03-21 10:22:16 +01:00
|
|
|
it.kind = sym.kind
|
2020-03-24 07:25:10 +01:00
|
|
|
it.val_type = value_type
|
2020-03-28 14:00:28 +01:00
|
|
|
scope.update_var_type(it.val_var, value_type)
|
2020-03-18 13:18:18 +01:00
|
|
|
}
|
|
|
|
c.stmts(it.stmts)
|
2020-04-07 12:29:11 +02:00
|
|
|
c.in_for_count--
|
2020-03-11 19:00:51 +01:00
|
|
|
}
|
2020-04-26 06:40:54 +02:00
|
|
|
ast.ForStmt {
|
|
|
|
c.in_for_count++
|
|
|
|
typ := c.expr(it.cond)
|
|
|
|
if !it.is_inf && typ.idx() != table.bool_type_idx {
|
|
|
|
c.error('non-bool used as for condition', it.pos)
|
|
|
|
}
|
|
|
|
// TODO: update loop var type
|
|
|
|
// how does this work currenly?
|
|
|
|
c.stmts(it.stmts)
|
|
|
|
c.in_for_count--
|
|
|
|
}
|
2020-05-16 16:12:23 +02:00
|
|
|
ast.GlobalDecl {
|
|
|
|
c.check_valid_snake_case(it.name, 'global name', it.pos)
|
|
|
|
}
|
2020-04-07 15:15:45 +02:00
|
|
|
ast.GoStmt {
|
2020-04-26 06:40:54 +02:00
|
|
|
if !(it.call_expr is ast.CallExpr) {
|
2020-04-17 02:38:39 +02:00
|
|
|
c.error('expression in `go` must be a function call', it.call_expr.position())
|
2020-04-16 12:17:15 +02:00
|
|
|
}
|
2020-04-03 15:18:17 +02:00
|
|
|
c.expr(it.call_expr)
|
|
|
|
}
|
2020-03-01 05:14:36 +01:00
|
|
|
// ast.HashStmt {}
|
|
|
|
ast.Import {}
|
2020-05-06 12:26:00 +02:00
|
|
|
ast.InterfaceDecl {
|
2020-05-16 16:12:23 +02:00
|
|
|
c.interface_decl(it)
|
2020-05-06 12:26:00 +02:00
|
|
|
}
|
2020-04-17 17:16:14 +02:00
|
|
|
ast.Module {
|
|
|
|
c.mod = it.name
|
|
|
|
c.is_builtin_mod = it.name == 'builtin'
|
2020-05-16 16:12:23 +02:00
|
|
|
c.check_valid_snake_case(it.name, 'module name', it.pos)
|
2020-04-17 17:16:14 +02:00
|
|
|
}
|
2020-03-01 05:14:36 +01:00
|
|
|
ast.Return {
|
2020-04-11 21:50:14 +02:00
|
|
|
c.returns = true
|
2020-03-16 07:42:45 +01:00
|
|
|
c.return_stmt(mut it)
|
2020-04-29 12:31:18 +02:00
|
|
|
c.scope_returns = true
|
2020-03-01 05:14:36 +01:00
|
|
|
}
|
2020-04-16 11:01:18 +02:00
|
|
|
ast.StructDecl {
|
|
|
|
c.struct_decl(it)
|
|
|
|
}
|
2020-04-26 06:40:54 +02:00
|
|
|
ast.TypeDecl {
|
|
|
|
c.type_decl(it)
|
|
|
|
}
|
2020-03-04 14:20:37 +01:00
|
|
|
ast.UnsafeStmt {
|
2020-05-11 16:05:59 +02:00
|
|
|
c.inside_unsafe = true
|
2020-03-18 13:18:18 +01:00
|
|
|
c.stmts(it.stmts)
|
2020-05-11 16:05:59 +02:00
|
|
|
c.inside_unsafe = false
|
2020-03-04 14:20:37 +01:00
|
|
|
}
|
2020-04-07 15:15:45 +02:00
|
|
|
else {
|
|
|
|
// println('checker.stmt(): unhandled node')
|
|
|
|
// println('checker.stmt(): unhandled node (${typeof(node)})')
|
|
|
|
}
|
2020-01-18 23:26:14 +01:00
|
|
|
}
|
2020-02-03 07:02:54 +01:00
|
|
|
}
|
2020-01-18 23:26:14 +01:00
|
|
|
|
2020-04-25 17:49:16 +02:00
|
|
|
fn (mut c Checker) stmts(stmts []ast.Stmt) {
|
2020-04-30 18:06:14 +02:00
|
|
|
mut unreachable := token.Position{
|
|
|
|
line_nr: -1
|
|
|
|
}
|
2020-03-18 12:18:48 +01:00
|
|
|
c.expected_type = table.void_type
|
2020-02-29 18:34:25 +01:00
|
|
|
for stmt in stmts {
|
2020-04-29 12:31:18 +02:00
|
|
|
if c.scope_returns {
|
|
|
|
if unreachable.line_nr == -1 {
|
|
|
|
unreachable = stmt.position()
|
|
|
|
}
|
|
|
|
}
|
2020-02-29 18:34:25 +01:00
|
|
|
c.stmt(stmt)
|
|
|
|
}
|
2020-04-29 12:31:18 +02:00
|
|
|
if unreachable.line_nr >= 0 {
|
|
|
|
c.warn('unreachable code', unreachable)
|
|
|
|
}
|
|
|
|
c.scope_returns = false
|
2020-03-18 12:34:27 +01:00
|
|
|
c.expected_type = table.void_type
|
2020-02-29 18:34:25 +01:00
|
|
|
}
|
|
|
|
|
2020-05-25 05:32:33 +02:00
|
|
|
pub fn (mut c Checker) unwrap_generic(typ table.Type) table.Type {
|
|
|
|
if typ == table.t_type {
|
|
|
|
return c.cur_generic_type
|
|
|
|
}
|
|
|
|
return typ
|
|
|
|
}
|
|
|
|
|
|
|
|
// TODO node must be mut
|
2020-04-25 17:49:16 +02:00
|
|
|
pub fn (mut c Checker) expr(node ast.Expr) table.Type {
|
2020-02-06 13:57:35 +01:00
|
|
|
match mut node {
|
2020-05-15 23:14:53 +02:00
|
|
|
ast.AnonFn {
|
2020-05-23 08:51:15 +02:00
|
|
|
keep_fn := c.cur_fn
|
|
|
|
c.cur_fn = &it.decl
|
2020-05-15 23:14:53 +02:00
|
|
|
c.stmts(it.decl.stmts)
|
2020-05-23 08:51:15 +02:00
|
|
|
c.cur_fn = keep_fn
|
2020-05-15 23:14:53 +02:00
|
|
|
return it.typ
|
|
|
|
}
|
2020-03-01 05:14:36 +01:00
|
|
|
ast.ArrayInit {
|
|
|
|
return c.array_init(mut it)
|
|
|
|
}
|
2020-03-02 19:00:33 +01:00
|
|
|
ast.AsCast {
|
2020-03-18 13:55:46 +01:00
|
|
|
it.expr_type = c.expr(it.expr)
|
2020-03-18 14:50:21 +01:00
|
|
|
expr_type_sym := c.table.get_type_symbol(it.expr_type)
|
|
|
|
type_sym := c.table.get_type_symbol(it.typ)
|
|
|
|
if expr_type_sym.kind == .sum_type {
|
|
|
|
info := expr_type_sym.info as table.SumType
|
2020-04-26 06:39:23 +02:00
|
|
|
if it.typ !in info.variants {
|
2020-03-18 14:50:21 +01:00
|
|
|
c.error('cannot cast `$expr_type_sym.name` to `$type_sym.name`', it.pos)
|
|
|
|
// c.error('only $info.variants can be casted to `$typ`', it.pos)
|
|
|
|
}
|
2020-04-26 09:17:13 +02:00
|
|
|
} else {
|
|
|
|
//
|
2020-03-18 14:50:21 +01:00
|
|
|
c.error('cannot cast non sum type `$type_sym.name` using `as`', it.pos)
|
|
|
|
}
|
2020-04-25 18:40:05 +02:00
|
|
|
return it.typ.to_ptr()
|
2020-04-26 09:17:13 +02:00
|
|
|
// return it.typ
|
2020-03-02 19:00:33 +01:00
|
|
|
}
|
2020-02-03 07:02:54 +01:00
|
|
|
ast.AssignExpr {
|
2020-03-13 01:43:30 +01:00
|
|
|
c.assign_expr(mut it)
|
2020-02-03 07:02:54 +01:00
|
|
|
}
|
2020-02-29 18:07:29 +01:00
|
|
|
ast.Assoc {
|
2020-03-10 13:35:25 +01:00
|
|
|
scope := c.file.scope.innermost(it.pos.pos)
|
2020-04-15 01:45:27 +02:00
|
|
|
v := scope.find_var(it.var_name) or {
|
2020-02-29 18:07:29 +01:00
|
|
|
panic(err)
|
|
|
|
}
|
2020-03-19 08:57:33 +01:00
|
|
|
for i, _ in it.fields {
|
|
|
|
c.expr(it.exprs[i])
|
|
|
|
}
|
2020-04-15 01:45:27 +02:00
|
|
|
it.typ = v.typ
|
|
|
|
return v.typ
|
2020-02-29 18:07:29 +01:00
|
|
|
}
|
2020-03-01 05:14:36 +01:00
|
|
|
ast.BoolLiteral {
|
|
|
|
return table.bool_type
|
|
|
|
}
|
|
|
|
ast.CastExpr {
|
2020-03-11 19:00:51 +01:00
|
|
|
it.expr_type = c.expr(it.expr)
|
2020-05-06 12:05:24 +02:00
|
|
|
sym := c.table.get_type_symbol(it.expr_type)
|
2020-05-06 12:26:00 +02:00
|
|
|
if it.typ == table.string_type && !(sym.kind in [.byte, .byteptr] || sym.kind ==
|
|
|
|
.array && sym.name == 'array_byte') {
|
2020-05-06 12:05:24 +02:00
|
|
|
type_name := c.table.type_to_str(it.expr_type)
|
2020-05-07 12:34:06 +02:00
|
|
|
c.error('cannot cast type `$type_name` to string, use `x.str()` instead', it.pos)
|
2020-05-06 12:05:24 +02:00
|
|
|
}
|
2020-05-16 22:48:41 +02:00
|
|
|
if it.expr_type == table.string_type {
|
2020-05-16 22:52:41 +02:00
|
|
|
mut error_msg := 'cannot cast a string'
|
|
|
|
if it.expr is ast.StringLiteral {
|
|
|
|
str_lit := it.expr as ast.StringLiteral
|
|
|
|
if str_lit.val.len == 1 {
|
|
|
|
error_msg += ", for denoting characters use `$str_lit.val` instead of '$str_lit.val'"
|
|
|
|
}
|
|
|
|
}
|
|
|
|
c.error(error_msg, it.pos)
|
2020-05-16 22:48:41 +02:00
|
|
|
}
|
2020-03-11 06:46:14 +01:00
|
|
|
if it.has_arg {
|
|
|
|
c.expr(it.arg)
|
|
|
|
}
|
2020-04-08 04:47:29 +02:00
|
|
|
it.typname = c.table.get_type_symbol(it.typ).name
|
2020-03-01 05:14:36 +01:00
|
|
|
return it.typ
|
|
|
|
}
|
|
|
|
ast.CallExpr {
|
2020-04-08 01:19:24 +02:00
|
|
|
return c.call_expr(mut it)
|
2020-03-01 05:14:36 +01:00
|
|
|
}
|
|
|
|
ast.CharLiteral {
|
|
|
|
return table.byte_type
|
|
|
|
}
|
2020-05-15 23:14:53 +02:00
|
|
|
ast.ConcatExpr {
|
|
|
|
return c.concat_expr(mut it)
|
|
|
|
}
|
2020-02-25 15:02:34 +01:00
|
|
|
ast.EnumVal {
|
2020-03-15 02:51:31 +01:00
|
|
|
return c.enum_val(mut it)
|
2020-02-25 15:02:34 +01:00
|
|
|
}
|
2020-02-17 14:15:42 +01:00
|
|
|
ast.FloatLiteral {
|
2020-02-15 13:37:48 +01:00
|
|
|
return table.f64_type
|
|
|
|
}
|
2020-03-01 05:14:36 +01:00
|
|
|
ast.Ident {
|
2020-04-08 19:15:16 +02:00
|
|
|
// c.checked_ident = it.name
|
|
|
|
res := c.ident(mut it)
|
|
|
|
// c.checked_ident = ''
|
|
|
|
return res
|
2020-02-18 18:13:34 +01:00
|
|
|
}
|
2020-03-01 05:14:36 +01:00
|
|
|
ast.IfExpr {
|
|
|
|
return c.if_expr(mut it)
|
2020-02-03 07:02:54 +01:00
|
|
|
}
|
2020-03-01 05:14:36 +01:00
|
|
|
ast.IfGuardExpr {
|
2020-03-17 16:40:41 +01:00
|
|
|
it.expr_type = c.expr(it.expr)
|
2020-03-01 05:14:36 +01:00
|
|
|
return table.bool_type
|
2020-02-18 18:13:34 +01:00
|
|
|
}
|
2020-03-01 05:14:36 +01:00
|
|
|
ast.IndexExpr {
|
2020-03-06 17:05:58 +01:00
|
|
|
return c.index_expr(mut it)
|
2020-02-03 07:02:54 +01:00
|
|
|
}
|
|
|
|
ast.InfixExpr {
|
2020-03-07 00:34:14 +01:00
|
|
|
return c.infix_expr(mut it)
|
2020-02-03 07:02:54 +01:00
|
|
|
}
|
2020-03-01 05:14:36 +01:00
|
|
|
ast.IntegerLiteral {
|
|
|
|
return table.int_type
|
2020-02-03 07:02:54 +01:00
|
|
|
}
|
2020-03-01 05:14:36 +01:00
|
|
|
ast.MapInit {
|
2020-03-02 04:24:45 +01:00
|
|
|
return c.map_init(mut it)
|
2020-03-01 05:14:36 +01:00
|
|
|
}
|
|
|
|
ast.MatchExpr {
|
|
|
|
return c.match_expr(mut it)
|
2020-02-03 07:02:54 +01:00
|
|
|
}
|
2020-03-01 05:14:36 +01:00
|
|
|
ast.PostfixExpr {
|
|
|
|
return c.postfix_expr(it)
|
2020-02-03 07:02:54 +01:00
|
|
|
}
|
2020-03-01 05:14:36 +01:00
|
|
|
ast.PrefixExpr {
|
2020-03-21 16:41:01 +01:00
|
|
|
right_type := c.expr(it.right)
|
|
|
|
// TODO: testing ref/deref strategy
|
2020-04-25 09:08:53 +02:00
|
|
|
if it.op == .amp && !right_type.is_ptr() {
|
|
|
|
return right_type.to_ptr()
|
2020-03-12 09:11:41 +01:00
|
|
|
}
|
2020-04-25 09:08:53 +02:00
|
|
|
if it.op == .mul && right_type.is_ptr() {
|
|
|
|
return right_type.deref()
|
2020-03-21 15:27:10 +01:00
|
|
|
}
|
2020-03-28 14:38:16 +01:00
|
|
|
if it.op == .not && right_type != table.bool_type_idx {
|
|
|
|
c.error('! operator can only be used with bool types', it.pos)
|
|
|
|
}
|
2020-03-21 16:41:01 +01:00
|
|
|
return right_type
|
2020-02-03 07:02:54 +01:00
|
|
|
}
|
2020-03-01 05:14:36 +01:00
|
|
|
ast.None {
|
|
|
|
return table.none_type
|
|
|
|
}
|
|
|
|
ast.ParExpr {
|
|
|
|
return c.expr(it.expr)
|
2020-02-03 07:02:54 +01:00
|
|
|
}
|
|
|
|
ast.SelectorExpr {
|
2020-03-07 04:45:35 +01:00
|
|
|
return c.selector_expr(mut it)
|
2020-02-03 07:02:54 +01:00
|
|
|
}
|
2020-03-01 05:14:36 +01:00
|
|
|
ast.SizeOf {
|
|
|
|
return table.int_type
|
2020-02-03 07:02:54 +01:00
|
|
|
}
|
2020-03-01 05:14:36 +01:00
|
|
|
ast.StringLiteral {
|
2020-05-19 17:12:47 +02:00
|
|
|
if it.language == .c {
|
2020-04-01 17:14:17 +02:00
|
|
|
return table.byteptr_type
|
|
|
|
}
|
2020-03-01 05:14:36 +01:00
|
|
|
return table.string_type
|
2020-02-10 14:43:17 +01:00
|
|
|
}
|
2020-03-21 07:01:06 +01:00
|
|
|
ast.StringInterLiteral {
|
|
|
|
for expr in it.exprs {
|
|
|
|
it.expr_types << c.expr(expr)
|
|
|
|
}
|
|
|
|
return table.string_type
|
|
|
|
}
|
2020-03-01 05:14:36 +01:00
|
|
|
ast.StructInit {
|
2020-03-19 09:40:21 +01:00
|
|
|
return c.struct_init(mut it)
|
2020-02-29 05:36:39 +01:00
|
|
|
}
|
2020-03-03 15:06:21 +01:00
|
|
|
ast.Type {
|
2020-03-02 10:53:38 +01:00
|
|
|
return it.typ
|
|
|
|
}
|
2020-03-19 12:15:39 +01:00
|
|
|
ast.TypeOf {
|
2020-03-28 17:37:22 +01:00
|
|
|
it.expr_type = c.expr(it.expr)
|
2020-03-19 12:15:39 +01:00
|
|
|
return table.string_type
|
|
|
|
}
|
2020-04-04 20:47:57 +02:00
|
|
|
else {
|
|
|
|
tnode := typeof(node)
|
|
|
|
if tnode != 'unknown v.ast.Expr' {
|
|
|
|
println('checker.expr(): unhandled node with typeof(`${tnode}`)')
|
|
|
|
}
|
|
|
|
}
|
2020-01-18 23:26:14 +01:00
|
|
|
}
|
2020-02-10 08:32:08 +01:00
|
|
|
return table.void_type
|
2020-02-03 07:02:54 +01:00
|
|
|
}
|
2020-01-18 23:26:14 +01:00
|
|
|
|
2020-05-11 14:38:25 +02:00
|
|
|
pub fn (mut c Checker) ident(mut ident ast.Ident) table.Type {
|
2020-04-04 05:14:40 +02:00
|
|
|
// TODO: move this
|
|
|
|
if c.const_deps.len > 0 {
|
|
|
|
mut name := ident.name
|
2020-04-26 06:39:23 +02:00
|
|
|
if !name.contains('.') && ident.mod !in ['builtin', 'main'] {
|
2020-04-04 05:14:40 +02:00
|
|
|
name = '${ident.mod}.$ident.name'
|
|
|
|
}
|
2020-04-04 08:05:26 +02:00
|
|
|
if name == c.const_decl {
|
|
|
|
c.error('cycle in constant `$c.const_decl`', ident.pos)
|
2020-04-04 05:14:40 +02:00
|
|
|
return table.void_type
|
|
|
|
}
|
|
|
|
c.const_deps << name
|
|
|
|
}
|
|
|
|
if ident.kind == .blank_ident {
|
|
|
|
return table.void_type
|
|
|
|
}
|
|
|
|
// second use
|
2020-02-17 12:25:18 +01:00
|
|
|
if ident.kind == .variable {
|
|
|
|
info := ident.info as ast.IdentVar
|
2020-05-21 21:51:49 +02:00
|
|
|
if info.typ == table.t_type {
|
|
|
|
// Got a var with type T, return current generic type
|
2020-05-24 04:43:00 +02:00
|
|
|
// return c.cur_generic_type
|
2020-05-21 21:51:49 +02:00
|
|
|
}
|
2020-04-04 05:14:40 +02:00
|
|
|
return info.typ
|
2020-04-07 15:15:45 +02:00
|
|
|
} else if ident.kind == .constant {
|
2020-04-04 05:14:40 +02:00
|
|
|
info := ident.info as ast.IdentVar
|
|
|
|
return info.typ
|
2020-04-07 15:15:45 +02:00
|
|
|
} else if ident.kind == .function {
|
2020-04-04 05:14:40 +02:00
|
|
|
info := ident.info as ast.IdentFn
|
|
|
|
return info.typ
|
2020-04-07 15:15:45 +02:00
|
|
|
} else if ident.kind == .unresolved {
|
|
|
|
// first use
|
2020-03-10 13:35:25 +01:00
|
|
|
start_scope := c.file.scope.innermost(ident.pos.pos)
|
2020-04-04 13:32:16 +02:00
|
|
|
if obj := start_scope.find(ident.name) {
|
|
|
|
match obj {
|
|
|
|
ast.Var {
|
|
|
|
mut typ := it.typ
|
|
|
|
if typ == 0 {
|
2020-05-24 19:55:04 +02:00
|
|
|
if it.expr is ast.Ident {
|
|
|
|
inner_ident := it.expr as ast.Ident
|
|
|
|
if inner_ident.kind == .unresolved {
|
|
|
|
c.error('unresolved variable: `$ident.name`', ident.pos)
|
|
|
|
return table.void_type
|
|
|
|
}
|
|
|
|
}
|
2020-04-04 13:32:16 +02:00
|
|
|
typ = c.expr(it.expr)
|
|
|
|
}
|
2020-05-10 16:13:30 +02:00
|
|
|
is_optional := typ.flag_is(.optional)
|
|
|
|
ident.kind = .variable
|
|
|
|
ident.info = ast.IdentVar{
|
|
|
|
typ: typ
|
|
|
|
is_optional: is_optional
|
|
|
|
}
|
2020-05-25 05:32:33 +02:00
|
|
|
// if typ == table.t_type {
|
|
|
|
// sym := c.table.get_type_symbol(c.cur_generic_type)
|
|
|
|
// println('IDENT T unresolved $ident.name typ=$sym.name')
|
|
|
|
// Got a var with type T, return current generic type
|
|
|
|
// typ = c.cur_generic_type
|
|
|
|
// }
|
2020-05-24 04:43:00 +02:00
|
|
|
// } else {
|
2020-05-10 16:13:30 +02:00
|
|
|
it.typ = typ
|
|
|
|
// unwrap optional (`println(x)`)
|
|
|
|
if is_optional {
|
|
|
|
return typ.set_flag(.unset)
|
2020-04-04 13:32:16 +02:00
|
|
|
}
|
2020-05-10 16:13:30 +02:00
|
|
|
return typ
|
2020-04-04 13:32:16 +02:00
|
|
|
}
|
|
|
|
else {}
|
|
|
|
}
|
2020-02-17 12:25:18 +01:00
|
|
|
}
|
2020-05-10 16:13:30 +02:00
|
|
|
// prepend mod to look for fn call or const
|
|
|
|
mut name := ident.name
|
|
|
|
if !name.contains('.') && ident.mod !in ['builtin', 'main'] {
|
|
|
|
name = '${ident.mod}.$ident.name'
|
|
|
|
}
|
2020-04-04 05:14:40 +02:00
|
|
|
if obj := c.file.global_scope.find(name) {
|
|
|
|
match obj {
|
|
|
|
ast.GlobalDecl {
|
|
|
|
ident.kind = .global
|
|
|
|
ident.info = ast.IdentVar{
|
|
|
|
typ: it.typ
|
|
|
|
}
|
|
|
|
return it.typ
|
|
|
|
}
|
|
|
|
ast.ConstField {
|
|
|
|
mut typ := it.typ
|
|
|
|
if typ == 0 {
|
|
|
|
typ = c.expr(it.expr)
|
|
|
|
}
|
|
|
|
ident.name = name
|
|
|
|
ident.kind = .constant
|
|
|
|
ident.info = ast.IdentVar{
|
|
|
|
typ: typ
|
|
|
|
}
|
|
|
|
it.typ = typ
|
|
|
|
return typ
|
|
|
|
}
|
|
|
|
else {}
|
2020-02-17 12:25:18 +01:00
|
|
|
}
|
|
|
|
}
|
2020-05-06 20:09:29 +02:00
|
|
|
// Non-anon-function object (not a call), e.g. `onclick(my_click)`
|
2020-02-28 13:29:04 +01:00
|
|
|
if func := c.table.find_fn(name) {
|
2020-05-11 14:38:25 +02:00
|
|
|
fn_type := table.new_type(c.table.find_or_register_fn_type(ident.mod, func, false,
|
|
|
|
true))
|
2020-03-11 19:00:51 +01:00
|
|
|
ident.name = name
|
2020-02-17 12:25:18 +01:00
|
|
|
ident.kind = .function
|
2020-03-11 16:10:46 +01:00
|
|
|
ident.info = ast.IdentFn{
|
|
|
|
typ: fn_type
|
2020-02-17 12:25:18 +01:00
|
|
|
}
|
2020-03-11 16:10:46 +01:00
|
|
|
return fn_type
|
2020-02-17 12:25:18 +01:00
|
|
|
}
|
|
|
|
}
|
2020-05-19 17:12:47 +02:00
|
|
|
if ident.language == .c {
|
2020-02-18 18:13:34 +01:00
|
|
|
return table.int_type
|
|
|
|
}
|
2020-05-01 12:22:39 +02:00
|
|
|
if ident.name != '_' {
|
|
|
|
c.error('undefined: `$ident.name`', ident.pos)
|
|
|
|
}
|
2020-05-01 12:02:31 +02:00
|
|
|
if c.table.known_type(ident.name) {
|
|
|
|
// e.g. `User` in `json.decode(User, '...')`
|
|
|
|
return table.void_type
|
|
|
|
}
|
2020-02-17 12:25:18 +01:00
|
|
|
return table.void_type
|
|
|
|
}
|
|
|
|
|
2020-05-16 16:12:23 +02:00
|
|
|
pub fn (mut c Checker) concat_expr(mut concat_expr ast.ConcatExpr) table.Type {
|
2020-05-15 23:14:53 +02:00
|
|
|
mut mr_types := []table.Type{}
|
|
|
|
for expr in concat_expr.vals {
|
|
|
|
mr_types << c.expr(expr)
|
|
|
|
}
|
|
|
|
if concat_expr.vals.len == 1 {
|
|
|
|
typ := mr_types[0]
|
|
|
|
concat_expr.return_type = typ
|
|
|
|
return typ
|
|
|
|
} else {
|
|
|
|
typ := c.table.find_or_register_multi_return(mr_types)
|
|
|
|
table.new_type(typ)
|
|
|
|
concat_expr.return_type = typ
|
|
|
|
return typ
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-05-11 14:38:25 +02:00
|
|
|
pub fn (mut c Checker) match_expr(mut node ast.MatchExpr) table.Type {
|
2020-03-18 12:18:48 +01:00
|
|
|
node.is_expr = c.expected_type != table.void_type
|
2020-03-18 16:07:52 +01:00
|
|
|
node.expected_type = c.expected_type
|
2020-03-18 12:23:32 +01:00
|
|
|
cond_type := c.expr(node.cond)
|
|
|
|
if cond_type == 0 {
|
|
|
|
c.error('match 0 cond type', node.pos)
|
2020-03-04 15:48:43 +01:00
|
|
|
}
|
2020-04-14 15:46:58 +02:00
|
|
|
type_sym := c.table.get_type_symbol(cond_type)
|
2020-04-15 23:32:56 +02:00
|
|
|
if type_sym.kind != .sum_type {
|
|
|
|
node.is_sum_type = false
|
|
|
|
}
|
2020-04-24 16:04:39 +02:00
|
|
|
c.match_exprs(mut node, type_sym)
|
2020-03-18 12:23:32 +01:00
|
|
|
c.expected_type = cond_type
|
2020-03-03 17:29:16 +01:00
|
|
|
mut ret_type := table.void_type
|
2020-03-04 11:59:45 +01:00
|
|
|
for branch in node.branches {
|
|
|
|
for expr in branch.exprs {
|
2020-03-18 12:23:32 +01:00
|
|
|
c.expected_type = cond_type
|
2020-03-04 11:59:45 +01:00
|
|
|
typ := c.expr(expr)
|
2020-03-02 10:53:38 +01:00
|
|
|
typ_sym := c.table.get_type_symbol(typ)
|
2020-05-24 04:43:00 +02:00
|
|
|
if !node.is_sum_type && !c.check_types(typ, cond_type) {
|
2020-05-04 21:03:18 +02:00
|
|
|
exp_sym := c.table.get_type_symbol(cond_type)
|
|
|
|
c.error('cannot use `$typ_sym.name` as `$exp_sym.name` in `match`', node.pos)
|
|
|
|
}
|
2020-03-02 10:53:38 +01:00
|
|
|
// TODO:
|
2020-04-07 15:15:45 +02:00
|
|
|
if typ_sym.kind == .sum_type {
|
|
|
|
}
|
2020-02-28 13:29:04 +01:00
|
|
|
}
|
2020-03-04 11:59:45 +01:00
|
|
|
c.stmts(branch.stmts)
|
2020-02-15 13:37:48 +01:00
|
|
|
// If the last statement is an expression, return its type
|
2020-03-04 11:59:45 +01:00
|
|
|
if branch.stmts.len > 0 {
|
|
|
|
match branch.stmts[branch.stmts.len - 1] {
|
2020-03-03 17:29:16 +01:00
|
|
|
ast.ExprStmt {
|
|
|
|
ret_type = c.expr(it.expr)
|
2020-05-21 22:35:43 +02:00
|
|
|
it.typ = ret_type
|
2020-03-03 17:29:16 +01:00
|
|
|
}
|
2020-04-07 15:15:45 +02:00
|
|
|
else {
|
|
|
|
// TODO: ask alex about this
|
|
|
|
// typ := c.expr(it.expr)
|
|
|
|
// type_sym := c.table.get_type_symbol(typ)
|
|
|
|
// p.warn('match expr ret $type_sym.name')
|
|
|
|
// node.typ = typ
|
|
|
|
// return typ
|
|
|
|
}
|
|
|
|
}
|
2020-02-15 13:37:48 +01:00
|
|
|
}
|
|
|
|
}
|
2020-03-18 08:41:49 +01:00
|
|
|
// if ret_type != table.void_type {
|
|
|
|
// node.is_expr = c.expected_type != table.void_type
|
|
|
|
// node.expected_type = c.expected_type
|
|
|
|
// }
|
|
|
|
node.return_type = ret_type
|
2020-03-18 12:23:32 +01:00
|
|
|
node.cond_type = cond_type
|
2020-03-04 15:48:43 +01:00
|
|
|
// println('!m $expr_type')
|
2020-03-03 17:29:16 +01:00
|
|
|
return ret_type
|
2020-02-15 13:37:48 +01:00
|
|
|
}
|
|
|
|
|
2020-05-11 14:38:25 +02:00
|
|
|
fn (mut c Checker) match_exprs(mut node ast.MatchExpr, type_sym table.TypeSymbol) {
|
2020-04-24 16:04:39 +02:00
|
|
|
// branch_exprs is a histogram of how many times
|
|
|
|
// an expr was used in the match
|
2020-05-04 21:03:18 +02:00
|
|
|
mut branch_exprs := map[string]int{}
|
2020-04-24 16:04:39 +02:00
|
|
|
for branch in node.branches {
|
|
|
|
for expr in branch.exprs {
|
|
|
|
mut key := ''
|
|
|
|
match expr {
|
2020-04-25 17:49:16 +02:00
|
|
|
ast.Type { key = c.table.type_to_str(it.typ) }
|
|
|
|
ast.EnumVal { key = it.val }
|
|
|
|
else { key = expr.str() }
|
2020-04-24 16:04:39 +02:00
|
|
|
}
|
|
|
|
val := if key in branch_exprs { branch_exprs[key] } else { 0 }
|
|
|
|
if val == 1 {
|
|
|
|
c.error('match case `$key` is handled more than once', branch.pos)
|
|
|
|
}
|
|
|
|
branch_exprs[key] = val + 1
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// check that expressions are exhaustive
|
|
|
|
// this is achieved either by putting an else
|
|
|
|
// or, when the match is on a sum type or an enum
|
|
|
|
// by listing all variants or values
|
2020-04-25 21:51:44 +02:00
|
|
|
mut is_exhaustive := true
|
2020-04-26 09:17:13 +02:00
|
|
|
mut unhandled := []string{}
|
2020-04-25 21:51:44 +02:00
|
|
|
match type_sym.info {
|
2020-04-26 09:17:13 +02:00
|
|
|
table.SumType { for v in it.variants {
|
2020-04-25 21:51:44 +02:00
|
|
|
v_str := c.table.type_to_str(v)
|
|
|
|
if v_str !in branch_exprs {
|
|
|
|
is_exhaustive = false
|
|
|
|
unhandled << '`$v_str`'
|
2020-04-24 16:04:39 +02:00
|
|
|
}
|
2020-04-26 09:17:13 +02:00
|
|
|
} }
|
|
|
|
table.Enum { for v in it.vals {
|
2020-04-25 21:51:44 +02:00
|
|
|
if v !in branch_exprs {
|
|
|
|
is_exhaustive = false
|
|
|
|
unhandled << '`.$v`'
|
2020-04-24 16:04:39 +02:00
|
|
|
}
|
2020-04-26 09:17:13 +02:00
|
|
|
} }
|
|
|
|
else { is_exhaustive = false }
|
2020-04-25 21:51:44 +02:00
|
|
|
}
|
|
|
|
mut else_branch := node.branches[node.branches.len - 1]
|
|
|
|
mut has_else := else_branch.is_else
|
|
|
|
if !has_else {
|
|
|
|
for i, branch in node.branches {
|
|
|
|
if branch.is_else && i != node.branches.len - 1 {
|
|
|
|
c.error('`else` must be the last branch of `match`', branch.pos)
|
|
|
|
else_branch = branch
|
|
|
|
has_else = true
|
2020-04-24 16:04:39 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2020-04-25 21:51:44 +02:00
|
|
|
if is_exhaustive {
|
|
|
|
if has_else {
|
|
|
|
c.error('match expression is exhaustive, `else` is unnecessary', else_branch.pos)
|
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|
|
|
|
if has_else {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
mut err_details := 'match must be exhaustive'
|
|
|
|
if unhandled.len > 0 {
|
|
|
|
err_details += ' (add match branches for: ' + unhandled.join(', ') + ' or `else {}` at the end)'
|
|
|
|
} else {
|
|
|
|
err_details += ' (add `else {}` at the end)'
|
|
|
|
}
|
|
|
|
c.error(err_details, node.pos)
|
2020-04-24 16:04:39 +02:00
|
|
|
}
|
|
|
|
|
2020-05-11 14:38:25 +02:00
|
|
|
pub fn (mut c Checker) if_expr(mut node ast.IfExpr) table.Type {
|
2020-05-14 17:15:25 +02:00
|
|
|
mut expr_required := false
|
2020-04-07 15:15:45 +02:00
|
|
|
if c.expected_type != table.void_type {
|
|
|
|
// sym := c.table.get_type_symbol(c.expected_type)
|
|
|
|
// println('$c.file.path $node.pos.line_nr IF is expr: checker exp type = ' + sym.name)
|
2020-05-14 17:15:25 +02:00
|
|
|
expr_required = true
|
2020-03-18 08:41:49 +01:00
|
|
|
}
|
2020-05-14 17:15:25 +02:00
|
|
|
former_expected_type := c.expected_type
|
2020-03-18 08:41:49 +01:00
|
|
|
node.typ = table.void_type
|
2020-03-20 14:39:56 +01:00
|
|
|
for i, branch in node.branches {
|
2020-04-24 20:27:18 +02:00
|
|
|
if branch.cond is ast.ParExpr {
|
2020-04-25 17:49:16 +02:00
|
|
|
c.error('unnecessary `()` in an if condition. use `if expr {` instead of `if (expr) {`.',
|
|
|
|
branch.pos)
|
2020-04-07 15:15:45 +02:00
|
|
|
}
|
2020-05-14 17:15:25 +02:00
|
|
|
if !node.has_else || i < node.branches.len - 1 {
|
|
|
|
// check condition type is boolean
|
|
|
|
cond_typ := c.expr(branch.cond)
|
|
|
|
if cond_typ.idx() != table.bool_type_idx {
|
|
|
|
typ_sym := c.table.get_type_symbol(cond_typ)
|
|
|
|
c.error('non-bool type `$typ_sym.name` used as if condition', branch.pos)
|
2020-04-30 09:32:29 +02:00
|
|
|
}
|
|
|
|
}
|
2020-03-20 14:39:56 +01:00
|
|
|
c.stmts(branch.stmts)
|
2020-05-14 17:15:25 +02:00
|
|
|
if expr_required {
|
|
|
|
if branch.stmts.len > 0 && branch.stmts[branch.stmts.len - 1] is ast.ExprStmt {
|
|
|
|
last_expr := branch.stmts[branch.stmts.len - 1] as ast.ExprStmt
|
|
|
|
c.expected_type = former_expected_type
|
2020-05-21 22:35:43 +02:00
|
|
|
last_expr.typ = c.expr(last_expr.expr)
|
|
|
|
if last_expr.typ != node.typ {
|
2020-05-14 17:15:25 +02:00
|
|
|
if node.typ == table.void_type {
|
2020-05-21 22:35:43 +02:00
|
|
|
// first branch of if expression
|
2020-05-14 17:15:25 +02:00
|
|
|
node.is_expr = true
|
2020-05-21 22:35:43 +02:00
|
|
|
node.typ = last_expr.typ
|
2020-05-14 17:15:25 +02:00
|
|
|
} else {
|
2020-05-21 22:35:43 +02:00
|
|
|
c.error('mismatched types `${c.table.type_to_str(node.typ)}` and `${c.table.type_to_str(last_expr.typ)}`',
|
2020-04-30 18:06:14 +02:00
|
|
|
node.pos)
|
2020-04-30 12:17:31 +02:00
|
|
|
}
|
2020-03-20 14:39:56 +01:00
|
|
|
}
|
2020-05-14 17:15:25 +02:00
|
|
|
} else {
|
|
|
|
c.error('`if` expression requires an expression as the last statement of every branch',
|
|
|
|
branch.pos)
|
2020-04-07 15:15:45 +02:00
|
|
|
}
|
2020-03-20 14:39:56 +01:00
|
|
|
}
|
2020-02-15 13:37:48 +01:00
|
|
|
}
|
2020-05-14 17:15:25 +02:00
|
|
|
if expr_required {
|
|
|
|
if !node.has_else {
|
|
|
|
c.error('`if` expression needs `else` clause', node.pos)
|
|
|
|
}
|
|
|
|
return node.typ
|
2020-05-11 14:38:25 +02:00
|
|
|
}
|
2020-03-20 14:39:56 +01:00
|
|
|
return table.bool_type
|
2020-02-15 13:37:48 +01:00
|
|
|
}
|
|
|
|
|
2020-04-25 17:49:16 +02:00
|
|
|
pub fn (mut c Checker) postfix_expr(node ast.PostfixExpr) table.Type {
|
2020-02-04 07:37:38 +01:00
|
|
|
typ := c.expr(node.expr)
|
2020-02-27 17:31:10 +01:00
|
|
|
typ_sym := c.table.get_type_symbol(typ)
|
2020-04-25 09:08:53 +02:00
|
|
|
// if !typ.is_number() {
|
2020-02-27 17:31:10 +01:00
|
|
|
if !typ_sym.is_number() {
|
|
|
|
println(typ_sym.kind.str())
|
2020-02-10 08:32:08 +01:00
|
|
|
c.error('invalid operation: $node.op.str() (non-numeric type `$typ_sym.name`)', node.pos)
|
2020-04-28 11:20:19 +02:00
|
|
|
} else {
|
|
|
|
c.fail_if_immutable(node.expr)
|
2020-02-04 07:37:38 +01:00
|
|
|
}
|
|
|
|
return typ
|
|
|
|
}
|
|
|
|
|
2020-05-11 14:38:25 +02:00
|
|
|
pub fn (mut c Checker) index_expr(mut node ast.IndexExpr) table.Type {
|
2020-02-19 14:34:44 +01:00
|
|
|
typ := c.expr(node.left)
|
2020-04-03 10:41:01 +02:00
|
|
|
node.left_type = typ
|
2020-04-25 17:49:16 +02:00
|
|
|
mut is_range := false // TODO is_range := node.index is ast.RangeExpr
|
2020-02-03 11:29:50 +01:00
|
|
|
match node.index {
|
|
|
|
ast.RangeExpr {
|
|
|
|
is_range = true
|
2020-03-13 13:08:03 +01:00
|
|
|
if it.has_low {
|
|
|
|
c.expr(it.low)
|
|
|
|
}
|
|
|
|
if it.has_high {
|
|
|
|
c.expr(it.high)
|
|
|
|
}
|
2020-02-03 11:29:50 +01:00
|
|
|
}
|
|
|
|
else {}
|
|
|
|
}
|
2020-03-31 16:47:55 +02:00
|
|
|
typ_sym := c.table.get_type_symbol(typ)
|
2020-02-19 11:45:06 +01:00
|
|
|
if !is_range {
|
|
|
|
index_type := c.expr(node.index)
|
2020-03-02 11:32:28 +01:00
|
|
|
index_type_sym := c.table.get_type_symbol(index_type)
|
2020-02-29 20:11:04 +01:00
|
|
|
// println('index expr left=$typ_sym.name $node.pos.line_nr')
|
2020-03-31 17:51:16 +02:00
|
|
|
// if typ_sym.kind == .array && (!(table.type_idx(index_type) in table.number_type_idxs) &&
|
|
|
|
// index_type_sym.kind != .enum_) {
|
2020-04-25 09:08:53 +02:00
|
|
|
if typ_sym.kind in [.array, .array_fixed] && !(index_type.is_number() || index_type_sym.kind ==
|
2020-04-07 15:15:45 +02:00
|
|
|
.enum_) {
|
2020-04-08 13:53:11 +02:00
|
|
|
c.error('non-integer index `$index_type_sym.name` (array type `$typ_sym.name`)',
|
2020-04-07 18:51:39 +02:00
|
|
|
node.pos)
|
2020-04-25 09:08:53 +02:00
|
|
|
} else if typ_sym.kind == .map && index_type.idx() != table.string_type_idx {
|
2020-03-31 16:47:55 +02:00
|
|
|
c.error('non-string map index (map type `$typ_sym.name`)', node.pos)
|
2020-02-19 11:45:06 +01:00
|
|
|
}
|
2020-03-18 13:18:18 +01:00
|
|
|
value_type := c.table.value_type(typ)
|
|
|
|
if value_type != table.void_type {
|
|
|
|
return value_type
|
2020-02-03 09:11:10 +01:00
|
|
|
}
|
2020-04-07 15:15:45 +02:00
|
|
|
} else if is_range {
|
2020-03-31 16:47:55 +02:00
|
|
|
// array[1..2] => array
|
|
|
|
// fixed_array[1..2] => array
|
|
|
|
if typ_sym.kind == .array_fixed {
|
|
|
|
elem_type := c.table.value_type(typ)
|
|
|
|
idx := c.table.find_or_register_array(elem_type, 1)
|
|
|
|
return table.new_type(idx)
|
|
|
|
}
|
|
|
|
}
|
2020-02-03 09:11:10 +01:00
|
|
|
return typ
|
|
|
|
}
|
|
|
|
|
2020-02-26 15:51:05 +01:00
|
|
|
// `.green` or `Color.green`
|
|
|
|
// If a short form is used, `expected_type` needs to be an enum
|
|
|
|
// with this value.
|
2020-05-11 14:38:25 +02:00
|
|
|
pub fn (mut c Checker) enum_val(mut node ast.EnumVal) table.Type {
|
2020-04-07 15:15:45 +02:00
|
|
|
typ_idx := if node.enum_name == '' {
|
2020-04-25 09:08:53 +02:00
|
|
|
c.expected_type.idx()
|
2020-04-25 17:49:16 +02:00
|
|
|
} else { //
|
2020-04-07 15:15:45 +02:00
|
|
|
c.table.find_type_idx(node.enum_name)
|
|
|
|
}
|
2020-02-29 18:34:25 +01:00
|
|
|
// println('checker: enum_val: $node.enum_name typeidx=$typ_idx')
|
|
|
|
if typ_idx == 0 {
|
2020-03-01 15:19:10 +01:00
|
|
|
c.error('not an enum (name=$node.enum_name) (type_idx=0)', node.pos)
|
2020-05-08 15:00:04 +02:00
|
|
|
return table.void_type
|
2020-02-29 18:34:25 +01:00
|
|
|
}
|
2020-03-19 11:52:47 +01:00
|
|
|
typ := table.new_type(typ_idx)
|
2020-04-26 19:59:03 +02:00
|
|
|
if typ == table.void_type {
|
|
|
|
c.error('not an enum', node.pos)
|
2020-05-08 15:00:04 +02:00
|
|
|
return table.void_type
|
2020-04-26 19:59:03 +02:00
|
|
|
}
|
2020-03-15 02:51:31 +01:00
|
|
|
typ_sym := c.table.get_type_symbol(typ)
|
2020-04-26 19:59:03 +02:00
|
|
|
// println('tname=$typ_sym.name $node.pos.line_nr $c.file.path')
|
2020-03-15 02:51:31 +01:00
|
|
|
if typ_sym.kind != .enum_ {
|
2020-02-29 18:25:38 +01:00
|
|
|
c.error('not an enum', node.pos)
|
2020-05-08 15:00:04 +02:00
|
|
|
return table.void_type
|
|
|
|
}
|
|
|
|
if !(typ_sym.info is table.Enum) {
|
|
|
|
c.error('not an enum', node.pos)
|
|
|
|
return table.void_type
|
2020-02-29 18:25:38 +01:00
|
|
|
}
|
2020-03-15 02:51:31 +01:00
|
|
|
// info := typ_sym.info as table.Enum
|
|
|
|
info := typ_sym.enum_info()
|
|
|
|
// rintln('checker: x = $info.x enum val $c.expected_type $typ_sym.name')
|
2020-02-27 00:12:37 +01:00
|
|
|
// println(info.vals)
|
2020-04-26 06:39:23 +02:00
|
|
|
if node.val !in info.vals {
|
2020-03-15 02:51:31 +01:00
|
|
|
c.error('enum `$typ_sym.name` does not have a value `$node.val`', node.pos)
|
2020-02-26 15:51:05 +01:00
|
|
|
}
|
2020-03-15 02:51:31 +01:00
|
|
|
node.typ = typ
|
|
|
|
return typ
|
2020-02-26 15:51:05 +01:00
|
|
|
}
|
|
|
|
|
2020-05-11 14:38:25 +02:00
|
|
|
pub fn (mut c Checker) map_init(mut node ast.MapInit) table.Type {
|
2020-03-07 08:13:00 +01:00
|
|
|
// `x ;= map[string]string` - set in parser
|
|
|
|
if node.typ != 0 {
|
|
|
|
info := c.table.get_type_symbol(node.typ).map_info()
|
|
|
|
node.key_type = info.key_type
|
|
|
|
node.value_type = info.value_type
|
|
|
|
return node.typ
|
|
|
|
}
|
|
|
|
// `{'age': 20}`
|
2020-03-01 05:14:36 +01:00
|
|
|
key0_type := c.expr(node.keys[0])
|
|
|
|
val0_type := c.expr(node.vals[0])
|
|
|
|
for i, key in node.keys {
|
2020-05-09 16:55:38 +02:00
|
|
|
key_i := key as ast.StringLiteral
|
2020-05-11 14:38:25 +02:00
|
|
|
for j in 0 .. i {
|
2020-05-09 16:55:38 +02:00
|
|
|
key_j := node.keys[j] as ast.StringLiteral
|
|
|
|
if key_i.val == key_j.val {
|
|
|
|
c.error('duplicate key "$key_i.val" in map literal', key.position())
|
|
|
|
}
|
|
|
|
}
|
2020-03-01 05:14:36 +01:00
|
|
|
if i == 0 {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
val := node.vals[i]
|
|
|
|
key_type := c.expr(key)
|
|
|
|
val_type := c.expr(val)
|
2020-05-24 04:43:00 +02:00
|
|
|
if !c.check_types(key_type, key0_type) {
|
2020-03-01 05:14:36 +01:00
|
|
|
key0_type_sym := c.table.get_type_symbol(key0_type)
|
|
|
|
key_type_sym := c.table.get_type_symbol(key_type)
|
2020-05-02 00:43:59 +02:00
|
|
|
c.error('map init: cannot use `$key_type_sym.name` as `$key0_type_sym.name` for map key',
|
2020-04-07 18:51:39 +02:00
|
|
|
node.pos)
|
2020-03-01 05:14:36 +01:00
|
|
|
}
|
2020-05-24 04:43:00 +02:00
|
|
|
if !c.check_types(val_type, val0_type) {
|
2020-03-01 05:14:36 +01:00
|
|
|
val0_type_sym := c.table.get_type_symbol(val0_type)
|
|
|
|
val_type_sym := c.table.get_type_symbol(val_type)
|
2020-05-02 00:43:59 +02:00
|
|
|
c.error('map init: cannot use `$val_type_sym.name` as `$val0_type_sym.name` for map value',
|
2020-04-07 18:51:39 +02:00
|
|
|
node.pos)
|
2020-03-01 05:14:36 +01:00
|
|
|
}
|
|
|
|
}
|
2020-03-02 04:24:45 +01:00
|
|
|
map_type := table.new_type(c.table.find_or_register_map(key0_type, val0_type))
|
|
|
|
node.typ = map_type
|
2020-03-07 08:13:00 +01:00
|
|
|
node.key_type = key0_type
|
|
|
|
node.value_type = val0_type
|
2020-03-02 04:24:45 +01:00
|
|
|
return map_type
|
2020-03-01 05:14:36 +01:00
|
|
|
}
|
|
|
|
|
2020-04-25 17:49:16 +02:00
|
|
|
pub fn (mut c Checker) warn(s string, pos token.Position) {
|
|
|
|
allow_warnings := !c.pref.is_prod // allow warnings only in dev builds
|
|
|
|
c.warn_or_error(s, pos, allow_warnings) // allow warnings only in dev builds
|
2020-04-03 20:17:53 +02:00
|
|
|
}
|
|
|
|
|
2020-04-25 17:49:16 +02:00
|
|
|
pub fn (mut c Checker) error(message string, pos token.Position) {
|
2020-04-16 15:32:11 +02:00
|
|
|
if c.pref.is_verbose {
|
|
|
|
print_backtrace()
|
|
|
|
}
|
2020-04-13 01:56:01 +02:00
|
|
|
c.warn_or_error(message, pos, false)
|
2020-04-03 20:17:53 +02:00
|
|
|
}
|
|
|
|
|
2020-04-25 17:49:16 +02:00
|
|
|
fn (mut c Checker) warn_or_error(message string, pos token.Position, warn bool) {
|
2020-04-13 01:56:01 +02:00
|
|
|
// add backtrace to issue struct, how?
|
|
|
|
// if c.pref.is_verbose {
|
2020-04-13 19:59:57 +02:00
|
|
|
// print_backtrace()
|
2020-04-13 01:56:01 +02:00
|
|
|
// }
|
2020-05-16 22:45:38 +02:00
|
|
|
if warn && !c.pref.skip_warnings {
|
2020-04-29 11:38:36 +02:00
|
|
|
c.nr_warnings++
|
2020-05-10 11:26:57 +02:00
|
|
|
wrn := errors.Warning{
|
2020-04-27 15:08:04 +02:00
|
|
|
reporter: errors.Reporter.checker
|
2020-04-17 17:16:14 +02:00
|
|
|
pos: pos
|
|
|
|
file_path: c.file.path
|
|
|
|
message: message
|
|
|
|
}
|
2020-05-10 11:26:57 +02:00
|
|
|
c.file.warnings << wrn
|
|
|
|
c.warnings << wrn
|
2020-05-16 22:45:38 +02:00
|
|
|
return
|
|
|
|
}
|
|
|
|
if !warn {
|
2020-04-03 20:17:53 +02:00
|
|
|
c.nr_errors++
|
2020-04-26 06:39:23 +02:00
|
|
|
if pos.line_nr !in c.error_lines {
|
2020-05-10 11:26:57 +02:00
|
|
|
err := errors.Error{
|
2020-04-27 15:08:04 +02:00
|
|
|
reporter: errors.Reporter.checker
|
2020-04-13 01:56:01 +02:00
|
|
|
pos: pos
|
|
|
|
file_path: c.file.path
|
|
|
|
message: message
|
|
|
|
}
|
2020-05-10 11:26:57 +02:00
|
|
|
c.file.errors << err
|
|
|
|
c.errors << err
|
2020-04-13 01:56:01 +02:00
|
|
|
c.error_lines << pos.line_nr
|
2020-04-03 20:17:53 +02:00
|
|
|
}
|
2020-02-19 19:54:36 +01:00
|
|
|
}
|
2020-02-03 07:02:54 +01:00
|
|
|
}
|
2020-04-12 17:45:04 +02:00
|
|
|
|
2020-04-17 17:16:14 +02:00
|
|
|
// for debugging only
|
2020-04-27 15:08:04 +02:00
|
|
|
fn (c &Checker) fileis(s string) bool {
|
|
|
|
return c.file.path.contains(s)
|
2020-04-12 17:45:04 +02:00
|
|
|
}
|
2020-05-18 22:54:08 +02:00
|
|
|
|
|
|
|
fn (mut c Checker) fn_decl(it ast.FnDecl) {
|
2020-05-21 21:51:49 +02:00
|
|
|
if it.is_generic && c.cur_generic_type == 0 { // need the cur_generic_type check to avoid inf. recursion
|
|
|
|
// loop thru each generic type and generate a function
|
|
|
|
for gen_type in c.table.fn_gen_types[it.name] {
|
|
|
|
c.cur_generic_type = gen_type
|
|
|
|
// println('\ncalling check for $it.name for type ' + gen_type.str())
|
|
|
|
c.fn_decl(it)
|
|
|
|
}
|
|
|
|
c.cur_generic_type = 0
|
|
|
|
return
|
|
|
|
}
|
2020-05-19 17:12:47 +02:00
|
|
|
if it.language == .v && !c.is_builtin_mod {
|
2020-05-18 22:54:08 +02:00
|
|
|
c.check_valid_snake_case(it.name, 'function name', it.pos)
|
|
|
|
}
|
|
|
|
if it.is_method {
|
|
|
|
sym := c.table.get_type_symbol(it.receiver.typ)
|
|
|
|
if sym.kind == .interface_ {
|
|
|
|
c.error('interfaces cannot be used as method receiver', it.receiver_pos)
|
|
|
|
}
|
|
|
|
// if sym.has_method(it.name) {
|
|
|
|
// c.warn('duplicate method `$it.name`', it.pos)
|
|
|
|
// }
|
|
|
|
}
|
2020-05-19 17:12:47 +02:00
|
|
|
if it.language == .v {
|
2020-05-18 22:54:08 +02:00
|
|
|
// Make sure all types are valid
|
|
|
|
for arg in it.args {
|
|
|
|
sym := c.table.get_type_symbol(arg.typ)
|
|
|
|
if sym.kind == .placeholder {
|
|
|
|
c.error('unknown type `$sym.name`', it.pos)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
c.expected_type = table.void_type
|
2020-05-23 08:51:15 +02:00
|
|
|
c.cur_fn = &it
|
2020-05-18 22:54:08 +02:00
|
|
|
c.stmts(it.stmts)
|
2020-05-19 17:12:47 +02:00
|
|
|
if it.language == .v && !it.no_body && it.return_type != table.void_type && !c.returns &&
|
2020-05-18 22:54:08 +02:00
|
|
|
it.name !in ['panic', 'exit'] {
|
|
|
|
c.error('missing return at end of function `$it.name`', it.pos)
|
|
|
|
}
|
|
|
|
c.returns = false
|
|
|
|
}
|