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
|
|
|
|
|
|
|
|
import (
|
|
|
|
v.ast
|
|
|
|
v.table
|
|
|
|
v.token
|
2020-02-07 07:34:18 +01:00
|
|
|
os
|
2020-01-18 23:26:14 +01:00
|
|
|
)
|
|
|
|
|
2020-02-19 19:54:36 +01:00
|
|
|
const (
|
2020-03-03 18:37:38 +01:00
|
|
|
max_nr_errors = 100
|
2020-02-19 19:54:36 +01:00
|
|
|
)
|
|
|
|
|
2020-01-18 23:26:14 +01:00
|
|
|
pub struct Checker {
|
2020-03-04 15:48:43 +01:00
|
|
|
table &table.Table
|
2020-01-18 23:26:14 +01:00
|
|
|
mut:
|
2020-03-04 15:48:43 +01:00
|
|
|
file ast.File
|
|
|
|
nr_errors int
|
|
|
|
errors []string
|
|
|
|
expected_type table.Type
|
|
|
|
fn_return_type table.Type // current function's return type
|
2020-03-22 13:19:45 +01:00
|
|
|
// fn_decl ast.FnDecl
|
2020-01-18 23:26:14 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
pub fn new_checker(table &table.Table) Checker {
|
|
|
|
return Checker{
|
|
|
|
table: table
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-02-04 12:03:12 +01:00
|
|
|
pub fn (c mut Checker) check(ast_file ast.File) {
|
2020-02-20 11:13:18 +01:00
|
|
|
c.file = ast_file
|
2020-01-18 23:26:14 +01:00
|
|
|
for stmt in ast_file.stmts {
|
|
|
|
c.stmt(stmt)
|
|
|
|
}
|
2020-02-27 00:12:37 +01:00
|
|
|
/*
|
|
|
|
println('all types:')
|
|
|
|
for t in c.table.types {
|
|
|
|
println(t.name + ' - ' + t.kind.str())
|
|
|
|
}
|
|
|
|
*/
|
2020-03-01 13:07:51 +01:00
|
|
|
|
2020-01-18 23:26:14 +01:00
|
|
|
}
|
|
|
|
|
2020-02-20 17:05:16 +01:00
|
|
|
pub fn (c mut Checker) check2(ast_file ast.File) []string {
|
|
|
|
c.file = ast_file
|
|
|
|
for stmt in ast_file.stmts {
|
|
|
|
c.stmt(stmt)
|
|
|
|
}
|
|
|
|
return c.errors
|
|
|
|
}
|
|
|
|
|
2020-02-03 07:31:54 +01:00
|
|
|
pub fn (c mut Checker) check_files(ast_files []ast.File) {
|
2020-02-22 13:08:14 +01:00
|
|
|
// TODO: temp fix, impl proper solution
|
2020-02-22 13:04:56 +01:00
|
|
|
for file in ast_files {
|
|
|
|
c.file = file
|
|
|
|
for stmt in file.stmts {
|
|
|
|
match mut stmt {
|
|
|
|
ast.ConstDecl {
|
|
|
|
c.stmt(*it)
|
|
|
|
}
|
|
|
|
else {}
|
2020-02-25 15:02:34 +01:00
|
|
|
}
|
2020-02-22 13:04:56 +01:00
|
|
|
}
|
|
|
|
}
|
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-03-19 09:40:21 +01:00
|
|
|
pub fn (c mut Checker) struct_init(struct_init mut 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-02-15 13:37:48 +01:00
|
|
|
typ_sym := c.table.get_type_symbol(struct_init.typ)
|
2020-02-20 15:42:56 +01:00
|
|
|
// println('check struct $typ_sym.name')
|
2020-02-15 13:37:48 +01:00
|
|
|
match typ_sym.kind {
|
2020-01-18 23:26:14 +01:00
|
|
|
.placeholder {
|
2020-02-15 13:37:48 +01:00
|
|
|
c.error('unknown struct: $typ_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
|
|
|
|
.struct_, .string, .array {
|
2020-02-15 13:37:48 +01:00
|
|
|
info := typ_sym.info as table.Struct
|
2020-03-26 17:03:14 +01:00
|
|
|
is_short_syntax := struct_init.fields.len == 0
|
2020-02-15 13:37:48 +01:00
|
|
|
if struct_init.exprs.len > info.fields.len {
|
|
|
|
c.error('too many fields', struct_init.pos)
|
|
|
|
}
|
2020-01-18 23:26:14 +01:00
|
|
|
for i, expr in struct_init.exprs {
|
2020-02-17 14:15:42 +01:00
|
|
|
// struct_field info.
|
2020-03-26 17:03:14 +01:00
|
|
|
field_name := if is_short_syntax { info.fields[i].name } else { struct_init.fields[i] }
|
2020-02-15 13:37:48 +01:00
|
|
|
mut field := info.fields[i]
|
|
|
|
mut found_field := false
|
|
|
|
for f in info.fields {
|
|
|
|
if f.name == field_name {
|
|
|
|
field = f
|
|
|
|
found_field = true
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if !found_field {
|
|
|
|
c.error('struct init: no such field `$field_name` for struct `$typ_sym.name`', struct_init.pos)
|
|
|
|
}
|
2020-02-29 18:25:38 +01:00
|
|
|
c.expected_type = field.typ
|
2020-02-10 08:32:08 +01:00
|
|
|
expr_type := c.expr(expr)
|
|
|
|
expr_type_sym := c.table.get_type_symbol(expr_type)
|
|
|
|
field_type_sym := c.table.get_type_symbol(field.typ)
|
|
|
|
if !c.table.check(expr_type, field.typ) {
|
2020-02-27 00:12:37 +01:00
|
|
|
c.error('cannot assign `$expr_type_sym.name` as `$field_type_sym.name` for field `$field.name`', struct_init.pos)
|
2020-01-18 23:26:14 +01:00
|
|
|
}
|
2020-03-19 09:40:21 +01:00
|
|
|
struct_init.expr_types << expr_type
|
|
|
|
struct_init.expected_types << field.typ
|
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-03-07 00:34:14 +01:00
|
|
|
pub fn (c mut Checker) infix_expr(infix_expr mut ast.InfixExpr) table.Type {
|
2020-02-20 12:16:18 +01:00
|
|
|
// println('checker: infix expr(op $infix_expr.op.str())')
|
2020-02-06 13:57:35 +01:00
|
|
|
left_type := c.expr(infix_expr.left)
|
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-02-06 13:57:35 +01:00
|
|
|
right_type := c.expr(infix_expr.right)
|
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)
|
|
|
|
if infix_expr.op == .key_in && !(right.kind in [.array, .map, .string]) {
|
|
|
|
c.error('infix expr: `in` can only be used with array/map/string.', infix_expr.pos)
|
|
|
|
}
|
2020-02-10 08:32:08 +01:00
|
|
|
if !c.table.check(right_type, left_type) {
|
2020-02-19 19:54:36 +01:00
|
|
|
left := c.table.get_type_symbol(left_type)
|
2020-02-19 21:13:42 +01:00
|
|
|
// `array << elm`
|
2020-02-19 19:54:36 +01:00
|
|
|
// the expressions have different types (array_x and x)
|
|
|
|
if left.kind == .array && infix_expr.op == .left_shift {
|
|
|
|
return table.void_type
|
|
|
|
}
|
2020-02-19 21:13:42 +01:00
|
|
|
// `elm in array`
|
2020-02-29 20:11:04 +01:00
|
|
|
if right.kind in [.array, .map] && infix_expr.op == .key_in {
|
2020-02-19 21:13:42 +01:00
|
|
|
return table.bool_type
|
|
|
|
}
|
2020-02-19 19:54:36 +01:00
|
|
|
c.error('infix expr: cannot use `$right.name` (right) as `$left.name`', infix_expr.pos)
|
2020-01-18 23:26:14 +01:00
|
|
|
}
|
2020-02-03 07:02:54 +01:00
|
|
|
if infix_expr.op.is_relational() {
|
2020-02-10 08:32:08 +01:00
|
|
|
return table.bool_type
|
2020-02-03 07:02:54 +01:00
|
|
|
}
|
2020-02-06 13:57:35 +01:00
|
|
|
return left_type
|
2020-01-18 23:26:14 +01:00
|
|
|
}
|
|
|
|
|
2020-03-13 01:43:30 +01:00
|
|
|
fn (c mut Checker) assign_expr(assign_expr mut ast.AssignExpr) {
|
2020-02-06 13:57:35 +01:00
|
|
|
left_type := 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-02-06 13:57:35 +01:00
|
|
|
right_type := c.expr(assign_expr.val)
|
2020-03-16 07:42:45 +01:00
|
|
|
assign_expr.right_type = right_type
|
2020-03-18 09:56:19 +01:00
|
|
|
if ast.expr_is_blank_ident(assign_expr.left) {
|
|
|
|
return
|
|
|
|
}
|
2020-02-06 13:57:35 +01:00
|
|
|
if !c.table.check(right_type, left_type) {
|
2020-02-10 08:32:08 +01: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 $left_type_sym.name', assign_expr.pos)
|
2020-01-18 23:26:14 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-03-06 15:02:40 +01:00
|
|
|
pub fn (c mut Checker) call_expr(call_expr mut 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 {
|
|
|
|
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
|
|
|
|
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)
|
|
|
|
}
|
|
|
|
for i, arg in call_expr.args {
|
|
|
|
c.expr(arg.expr)
|
|
|
|
}
|
|
|
|
// need to return `array_xxx` instead of `array`
|
|
|
|
call_expr.return_type = left_type
|
|
|
|
if method_name == 'clone' {
|
|
|
|
// in ['clone', 'str'] {
|
|
|
|
call_expr.receiver_type = table.type_to_ptr(left_type)
|
|
|
|
// call_expr.return_type = call_expr.receiver_type
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
call_expr.receiver_type = left_type
|
|
|
|
}
|
|
|
|
return left_type
|
|
|
|
}
|
|
|
|
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
|
|
|
|
return info.elem_type
|
|
|
|
}
|
|
|
|
if method := c.table.type_find_method(left_type_sym, method_name) {
|
|
|
|
no_args := method.args.len - 1
|
|
|
|
min_required_args := method.args.len - if method.is_variadic && method.args.len > 1 { 2 } else { 1 }
|
|
|
|
if call_expr.args.len < min_required_args {
|
|
|
|
c.error('too few arguments in call to `${left_type_sym.name}.$method_name` ($call_expr.args.len instead of $min_required_args)', call_expr.pos)
|
|
|
|
}
|
|
|
|
else if !method.is_variadic && call_expr.args.len > no_args {
|
|
|
|
c.error('too many arguments in call to `${left_type_sym.name}.$method_name` ($call_expr.args.len instead of $no_args)', call_expr.pos)
|
|
|
|
}
|
|
|
|
// 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 {
|
|
|
|
c.expected_type = if method.is_variadic && i >= method.args.len - 1 { method.args[method.args.len - 1].typ } else { method.args[i + 1].typ }
|
|
|
|
call_expr.args[i].typ = c.expr(arg.expr)
|
|
|
|
}
|
|
|
|
// TODO: typ optimize.. this node can get processed more than once
|
|
|
|
if call_expr.exp_arg_types.len == 0 {
|
|
|
|
for i in 1 .. method.args.len {
|
|
|
|
call_expr.exp_arg_types << method.args[i].typ
|
|
|
|
}
|
|
|
|
}
|
|
|
|
call_expr.receiver_type = method.args[0].typ
|
|
|
|
call_expr.return_type = method.return_type
|
|
|
|
return method.return_type
|
|
|
|
}
|
|
|
|
// TODO: str methods
|
|
|
|
if left_type_sym.kind == .map && method_name == 'str' {
|
|
|
|
call_expr.receiver_type = table.new_type(c.table.type_idxs['map_string'])
|
|
|
|
call_expr.return_type = table.string_type
|
|
|
|
return table.string_type
|
|
|
|
}
|
|
|
|
if left_type_sym.kind == .array && method_name == 'str' {
|
|
|
|
call_expr.receiver_type = left_type
|
|
|
|
call_expr.return_type = table.string_type
|
|
|
|
return table.string_type
|
|
|
|
}
|
|
|
|
c.error('unknown method: ${left_type_sym.name}.$method_name', call_expr.pos)
|
|
|
|
return table.void_type
|
2020-03-02 10:53:38 +01:00
|
|
|
}
|
2020-03-30 12:39:20 +02:00
|
|
|
else {
|
|
|
|
fn_name := call_expr.name
|
|
|
|
// TODO: impl typeof properly (probably not going to be a fn call)
|
|
|
|
if fn_name == 'typeof' {
|
|
|
|
return table.string_type
|
|
|
|
}
|
|
|
|
// start hack: until v1 is fixed and c definitions are added for these
|
|
|
|
if fn_name in ['C.calloc', 'C.malloc', 'C.exit', 'C.free'] {
|
|
|
|
for arg in call_expr.args {
|
|
|
|
c.expr(arg.expr)
|
|
|
|
}
|
|
|
|
if fn_name in ['C.calloc', 'C.malloc'] {
|
|
|
|
return table.byteptr_type
|
|
|
|
}
|
|
|
|
return table.void_type
|
2020-03-18 09:56:19 +01:00
|
|
|
}
|
2020-03-30 12:39:20 +02:00
|
|
|
// end hack
|
|
|
|
// look for function in format `mod.fn` or `fn` (main/builtin)
|
|
|
|
mut f := table.Fn{}
|
|
|
|
mut found := false
|
|
|
|
// try prefix with current module as it would have never gotten prefixed
|
|
|
|
if !fn_name.contains('.') && !(c.file.mod.name in ['builtin', 'main']) {
|
|
|
|
name_prefixed := '${c.file.mod.name}.$fn_name'
|
|
|
|
if f1 := c.table.find_fn(name_prefixed) {
|
|
|
|
call_expr.name = name_prefixed
|
|
|
|
found = true
|
|
|
|
f = f1
|
|
|
|
}
|
2020-03-18 09:56:19 +01:00
|
|
|
}
|
2020-03-30 12:39:20 +02:00
|
|
|
// 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)
|
|
|
|
if var := scope.find_var(fn_name) {
|
|
|
|
if var.typ != 0 {
|
|
|
|
vts := c.table.get_type_symbol(var.typ)
|
|
|
|
if vts.kind == .function {
|
|
|
|
info := vts.info as table.FnType
|
|
|
|
f = info.func
|
|
|
|
found = true
|
|
|
|
}
|
2020-03-11 16:10:46 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2020-03-30 12:39:20 +02:00
|
|
|
if !found {
|
|
|
|
c.error('unknown fn: $fn_name', call_expr.pos)
|
|
|
|
return table.void_type
|
2020-03-08 22:11:56 +01:00
|
|
|
}
|
2020-03-30 12:39:20 +02:00
|
|
|
call_expr.return_type = f.return_type
|
|
|
|
if f.is_c || call_expr.is_c {
|
|
|
|
for arg in call_expr.args {
|
|
|
|
c.expr(arg.expr)
|
2020-02-20 13:18:55 +01:00
|
|
|
}
|
2020-03-30 12:39:20 +02:00
|
|
|
return f.return_type
|
2020-02-20 11:13:18 +01:00
|
|
|
}
|
2020-03-30 12:39:20 +02:00
|
|
|
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)
|
|
|
|
}
|
|
|
|
// println can print anything
|
|
|
|
if fn_name == 'println' {
|
|
|
|
c.expected_type = table.string_type
|
|
|
|
call_expr.args[0].typ = c.expr(call_expr.args[0].expr)
|
|
|
|
return f.return_type
|
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-03-30 12:39:20 +02:00
|
|
|
if call_expr.exp_arg_types.len == 0 {
|
|
|
|
for arg in f.args {
|
|
|
|
call_expr.exp_arg_types << arg.typ
|
2020-03-26 11:09:59 +01:00
|
|
|
}
|
|
|
|
}
|
2020-03-30 12:39:20 +02:00
|
|
|
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)
|
|
|
|
if !c.table.check(typ, arg.typ) {
|
|
|
|
// str method, allow type with str method if fn arg is string
|
|
|
|
if arg_typ_sym.kind == .string && typ_sym.has_method('str') {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
// TODO const bug
|
|
|
|
if typ_sym.kind == .void && arg_typ_sym.kind == .string {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
if typ_sym.kind == .array_fixed {}
|
|
|
|
// println('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)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return f.return_type
|
2020-03-26 11:32:29 +01:00
|
|
|
}
|
2020-01-18 23:26:14 +01:00
|
|
|
}
|
|
|
|
|
2020-03-07 04:45:35 +01:00
|
|
|
pub fn (c mut Checker) selector_expr(selector_expr mut 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-01-18 23:26:14 +01:00
|
|
|
field_name := selector_expr.field
|
2020-02-29 09:04:47 +01:00
|
|
|
// variadic
|
|
|
|
if table.type_is_variadic(typ) {
|
|
|
|
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) {
|
|
|
|
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-02-08 16:59:57 +01: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-03-16 07:42:45 +01:00
|
|
|
pub fn (c mut Checker) return_stmt(return_stmt mut ast.Return) {
|
2020-02-29 18:25:38 +01:00
|
|
|
c.expected_type = c.fn_return_type
|
2020-01-22 21:34:38 +01:00
|
|
|
if return_stmt.exprs.len == 0 {
|
|
|
|
return
|
|
|
|
}
|
2020-03-22 13:19:45 +01:00
|
|
|
if return_stmt.exprs.len > 0 && c.fn_return_type == table.void_type {
|
|
|
|
c.error('too many arguments to return, current function does not return anything', return_stmt.pos)
|
|
|
|
return
|
|
|
|
}
|
2020-03-05 13:57:05 +01:00
|
|
|
expected_type := c.fn_return_type
|
2020-02-10 08:32:08 +01:00
|
|
|
expected_type_sym := c.table.get_type_symbol(expected_type)
|
2020-02-29 06:32:00 +01:00
|
|
|
exp_is_optional := table.type_is_optional(expected_type)
|
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-03-05 13:57:05 +01:00
|
|
|
mut got_types := []table.Type
|
|
|
|
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
|
|
|
|
if exp_is_optional && table.type_idx(got_types[0]) in [table.none_type_idx, c.table.type_idxs['Option']] {
|
|
|
|
return
|
|
|
|
}
|
2020-02-06 13:57:35 +01:00
|
|
|
if expected_types.len > 0 && expected_types.len != got_types.len {
|
2020-03-21 07:01:06 +01:00
|
|
|
// c.error('wrong number of return arguments:\n\texpected: $expected_types.str()\n\tgot: $got_types.str()', return_stmt.pos)
|
|
|
|
c.error('wrong number of return arguments', return_stmt.pos)
|
2020-01-18 23:26:14 +01:00
|
|
|
}
|
2020-02-06 13:57:35 +01:00
|
|
|
for i, exp_typ in expected_types {
|
|
|
|
got_typ := got_types[i]
|
|
|
|
if !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)
|
|
|
|
exp_typ_sym := c.table.get_type_symbol(exp_typ)
|
|
|
|
c.error('cannot use `$got_typ_sym.name` as type `$exp_typ_sym.name` in return argument', return_stmt.pos)
|
2020-01-18 23:26:14 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-03-10 12:01:37 +01:00
|
|
|
pub fn (c mut Checker) assign_stmt(assign_stmt mut ast.AssignStmt) {
|
2020-03-18 12:18:48 +01:00
|
|
|
c.expected_type = table.none_type // TODO a hack to make `x := if ... work`
|
2020-02-27 16:51:39 +01:00
|
|
|
// multi return
|
|
|
|
if assign_stmt.left.len > assign_stmt.right.len {
|
2020-03-31 06:34:59 +02:00
|
|
|
match assign_stmt.right[0] {
|
|
|
|
ast.CallExpr {}
|
|
|
|
else {
|
|
|
|
c.error('assign_stmt: expected call', assign_stmt.pos)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
right_type := c.expr(assign_stmt.right[0])
|
|
|
|
right_type_sym := c.table.get_type_symbol(right_type)
|
|
|
|
mr_info := right_type_sym.mr_info()
|
|
|
|
if right_type_sym.kind != .multi_return {
|
2020-02-27 16:51:39 +01:00
|
|
|
c.error('wrong number of vars', assign_stmt.pos)
|
|
|
|
}
|
2020-03-10 13:35:25 +01:00
|
|
|
mut scope := c.file.scope.innermost(assign_stmt.pos.pos)
|
2020-03-10 12:01:37 +01:00
|
|
|
for i, _ in assign_stmt.left {
|
|
|
|
mut ident := assign_stmt.left[i]
|
2020-03-31 06:34:59 +02:00
|
|
|
mut ident_var_info := ident.var_info()
|
2020-03-10 23:21:26 +01:00
|
|
|
val_type := mr_info.types[i]
|
2020-03-10 12:01:37 +01:00
|
|
|
if assign_stmt.op == .assign {
|
2020-03-10 14:45:21 +01:00
|
|
|
var_type := c.expr(ident)
|
2020-03-16 07:42:45 +01:00
|
|
|
assign_stmt.left_types << var_type
|
2020-03-10 14:45:21 +01:00
|
|
|
if !c.table.check(val_type, var_type) {
|
2020-03-10 12:01:37 +01:00
|
|
|
val_type_sym := c.table.get_type_symbol(val_type)
|
2020-03-10 14:45:21 +01:00
|
|
|
var_type_sym := c.table.get_type_symbol(var_type)
|
2020-03-10 12:01:37 +01:00
|
|
|
c.error('assign stmt: cannot use `$val_type_sym.name` as `$var_type_sym.name`', assign_stmt.pos)
|
|
|
|
}
|
|
|
|
}
|
2020-03-31 06:34:59 +02:00
|
|
|
ident_var_info.typ = val_type
|
|
|
|
ident.info = ident_var_info
|
|
|
|
assign_stmt.left[i] = ident
|
2020-03-16 07:42:45 +01:00
|
|
|
assign_stmt.right_types << val_type
|
2020-03-31 06:34:59 +02:00
|
|
|
scope.update_var_type(ident.name, val_type)
|
2020-03-10 12:01:37 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
// `a := 1` | `a,b := 1,2`
|
|
|
|
else {
|
2020-03-10 23:21:26 +01:00
|
|
|
if assign_stmt.left.len != assign_stmt.right.len {
|
2020-03-10 12:01:37 +01:00
|
|
|
c.error('wrong number of vars', assign_stmt.pos)
|
|
|
|
}
|
2020-03-10 13:35:25 +01:00
|
|
|
mut scope := c.file.scope.innermost(assign_stmt.pos.pos)
|
2020-03-10 12:01:37 +01:00
|
|
|
for i, _ in assign_stmt.left {
|
|
|
|
mut ident := assign_stmt.left[i]
|
2020-03-16 07:42:45 +01:00
|
|
|
mut ident_var_info := ident.var_info()
|
2020-03-10 14:45:21 +01:00
|
|
|
val_type := c.expr(assign_stmt.right[i])
|
2020-03-10 12:01:37 +01:00
|
|
|
if assign_stmt.op == .assign {
|
2020-03-10 14:45:21 +01:00
|
|
|
var_type := c.expr(ident)
|
2020-03-16 07:42:45 +01:00
|
|
|
assign_stmt.left_types << var_type
|
2020-03-10 14:45:21 +01:00
|
|
|
if !c.table.check(val_type, var_type) {
|
2020-03-10 12:01:37 +01:00
|
|
|
val_type_sym := c.table.get_type_symbol(val_type)
|
2020-03-10 14:45:21 +01:00
|
|
|
var_type_sym := c.table.get_type_symbol(var_type)
|
2020-03-10 12:01:37 +01:00
|
|
|
c.error('assign stmt: cannot use `$val_type_sym.name` as `$var_type_sym.name`', assign_stmt.pos)
|
|
|
|
}
|
|
|
|
}
|
2020-03-10 15:16:30 +01:00
|
|
|
ident_var_info.typ = val_type
|
|
|
|
ident.info = ident_var_info
|
|
|
|
assign_stmt.left[i] = ident
|
2020-03-31 06:34:59 +02:00
|
|
|
assign_stmt.right_types << val_type
|
2020-03-28 14:00:28 +01:00
|
|
|
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-02-15 13:37:48 +01:00
|
|
|
pub fn (c mut Checker) array_init(array_init mut 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 {
|
|
|
|
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-03-06 14:06:52 +01:00
|
|
|
type_sym := c.table.get_type_symbol(c.expected_type)
|
|
|
|
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 {
|
|
|
|
for i, expr in array_init.exprs {
|
|
|
|
typ := c.expr(expr)
|
|
|
|
// The first element's type
|
|
|
|
if i == 0 {
|
|
|
|
elem_type = typ
|
|
|
|
c.expected_type = typ
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
if !c.table.check(elem_type, typ) {
|
|
|
|
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-03-04 02:50:32 +01:00
|
|
|
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-03-04 02:50:32 +01:00
|
|
|
}
|
|
|
|
// [50]byte
|
|
|
|
else if array_init.exprs.len == 1 && array_init.elem_type != table.void_type {
|
|
|
|
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
|
|
|
}
|
|
|
|
else {
|
|
|
|
c.error('expecting `int` for fixed size', array_init.pos)
|
|
|
|
}
|
2020-03-18 08:41:49 +01: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-02-15 13:37:48 +01:00
|
|
|
fn (c mut 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 {
|
|
|
|
c.expr(it.expr)
|
|
|
|
}
|
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-03-01 05:14:36 +01:00
|
|
|
// ast.Attr {}
|
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-03-27 07:21:22 +01:00
|
|
|
ast.DeferStmt {
|
|
|
|
c.stmts(it.stmts)
|
|
|
|
}
|
2020-02-15 13:37:48 +01:00
|
|
|
ast.ConstDecl {
|
|
|
|
for i, expr in it.exprs {
|
|
|
|
mut field := it.fields[i]
|
|
|
|
typ := c.expr(expr)
|
2020-03-05 12:44:33 +01:00
|
|
|
// TODO: once consts are fixed update here
|
2020-03-05 12:30:16 +01:00
|
|
|
c.table.register_const(table.Var{
|
|
|
|
name: field.name
|
|
|
|
typ: typ
|
|
|
|
})
|
2020-02-15 13:37:48 +01:00
|
|
|
field.typ = typ
|
|
|
|
it.fields[i] = field
|
|
|
|
}
|
|
|
|
}
|
2020-03-01 05:14:36 +01:00
|
|
|
ast.ExprStmt {
|
|
|
|
c.expr(it.expr)
|
2020-03-19 15:18:29 +01:00
|
|
|
c.expected_type = table.void_type
|
2020-03-01 05:14:36 +01:00
|
|
|
}
|
|
|
|
ast.FnDecl {
|
2020-03-18 08:41:49 +01:00
|
|
|
c.expected_type = table.void_type
|
2020-03-11 16:10:46 +01:00
|
|
|
c.fn_return_type = it.return_type
|
2020-03-18 13:18:18 +01:00
|
|
|
c.stmts(it.stmts)
|
2020-02-03 07:02:54 +01:00
|
|
|
}
|
|
|
|
ast.ForStmt {
|
|
|
|
typ := c.expr(it.cond)
|
2020-02-19 11:06:36 +01:00
|
|
|
if !it.is_inf && table.type_idx(typ) != table.bool_type_idx {
|
2020-02-03 07:02:54 +01:00
|
|
|
c.error('non-bool used as for condition', it.pos)
|
2020-01-18 23:26:14 +01:00
|
|
|
}
|
2020-03-05 12:44:33 +01:00
|
|
|
// TODO: update loop var type
|
|
|
|
// how does this work currenly?
|
2020-03-18 13:18:18 +01:00
|
|
|
c.stmts(it.stmts)
|
2020-02-03 07:02:54 +01:00
|
|
|
}
|
|
|
|
ast.ForCStmt {
|
|
|
|
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-02-03 07:02:54 +01:00
|
|
|
}
|
2020-03-11 19:00:51 +01:00
|
|
|
ast.ForInStmt {
|
2020-03-18 13:18:18 +01:00
|
|
|
typ := c.expr(it.cond)
|
|
|
|
if it.is_range {
|
|
|
|
c.expr(it.high)
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
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-03-18 13:18:18 +01:00
|
|
|
if it.key_var.len > 0 {
|
|
|
|
key_type := match sym.kind {
|
|
|
|
.map{
|
|
|
|
sym.map_info().key_type
|
|
|
|
}
|
|
|
|
else {
|
2020-03-18 13:55:46 +01:00
|
|
|
table.int_type}
|
2020-03-18 13:18:18 +01: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-03-24 17:07:27 +01:00
|
|
|
c.error('for in: cannot index `$typ_sym.name`', it.pos)
|
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-03-11 19:00:51 +01:00
|
|
|
}
|
2020-03-01 05:14:36 +01:00
|
|
|
// ast.GlobalDecl {}
|
|
|
|
// ast.HashStmt {}
|
|
|
|
ast.Import {}
|
|
|
|
ast.Return {
|
2020-03-16 07:42:45 +01:00
|
|
|
c.return_stmt(mut it)
|
2020-03-01 05:14:36 +01:00
|
|
|
}
|
2020-02-03 07:02:54 +01:00
|
|
|
// ast.StructDecl {}
|
2020-03-04 14:20:37 +01:00
|
|
|
ast.UnsafeStmt {
|
2020-03-18 13:18:18 +01:00
|
|
|
c.stmts(it.stmts)
|
2020-03-04 14:20:37 +01:00
|
|
|
}
|
2020-03-04 15:48:43 +01: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-02-29 18:34:25 +01:00
|
|
|
fn (c mut Checker) stmts(stmts []ast.Stmt) {
|
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 {
|
|
|
|
c.stmt(stmt)
|
|
|
|
}
|
2020-03-18 12:34:27 +01:00
|
|
|
c.expected_type = table.void_type
|
2020-02-29 18:34:25 +01:00
|
|
|
}
|
|
|
|
|
2020-02-15 13:37:48 +01:00
|
|
|
pub fn (c mut Checker) expr(node ast.Expr) table.Type {
|
2020-02-06 13:57:35 +01:00
|
|
|
match mut node {
|
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
|
|
|
|
if !it.typ in info.variants {
|
|
|
|
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)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
c.error('cannot cast non sum type `$type_sym.name` using `as`', it.pos)
|
|
|
|
}
|
2020-03-02 19:00:33 +01:00
|
|
|
return it.typ
|
|
|
|
}
|
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-02-29 18:07:29 +01:00
|
|
|
var := scope.find_var(it.var_name) or {
|
|
|
|
panic(err)
|
|
|
|
}
|
2020-03-19 08:57:33 +01:00
|
|
|
for i, _ in it.fields {
|
|
|
|
c.expr(it.exprs[i])
|
|
|
|
}
|
2020-03-19 09:52:33 +01:00
|
|
|
it.typ = var.typ
|
2020-02-29 18:07:29 +01:00
|
|
|
return var.typ
|
|
|
|
}
|
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-03-11 06:46:14 +01:00
|
|
|
if it.has_arg {
|
|
|
|
c.expr(it.arg)
|
|
|
|
}
|
2020-03-01 05:14:36 +01:00
|
|
|
return it.typ
|
|
|
|
}
|
|
|
|
ast.CallExpr {
|
2020-03-06 15:02:40 +01:00
|
|
|
return c.call_expr(mut it)
|
2020-03-01 05:14:36 +01:00
|
|
|
}
|
|
|
|
ast.CharLiteral {
|
|
|
|
return table.byte_type
|
|
|
|
}
|
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 {
|
|
|
|
return c.ident(mut it)
|
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
|
|
|
|
if it.op == .amp && !table.type_is_ptr(right_type) {
|
|
|
|
return table.type_to_ptr(right_type)
|
2020-03-12 09:11:41 +01:00
|
|
|
}
|
2020-03-21 16:41:01 +01:00
|
|
|
if it.op == .mul && table.type_is_ptr(right_type) {
|
|
|
|
return table.type_deref(right_type)
|
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 {
|
|
|
|
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-03-01 05:14:36 +01:00
|
|
|
/*
|
|
|
|
ast.UnaryExpr {
|
|
|
|
c.expr(it.left)
|
2020-02-19 19:54:36 +01:00
|
|
|
}
|
2020-03-01 05:14:36 +01:00
|
|
|
*/
|
2020-03-01 13:07:51 +01:00
|
|
|
|
2020-03-04 15:48:43 +01:00
|
|
|
else {}
|
|
|
|
// println('checker.expr(): unhandled node')
|
|
|
|
// TODO: find nil string bug triggered with typeof
|
|
|
|
// println('checker.expr(): unhandled node (${typeof(node)})')
|
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-02-17 12:25:18 +01:00
|
|
|
pub fn (c mut Checker) ident(ident mut ast.Ident) table.Type {
|
2020-03-08 22:11:56 +01:00
|
|
|
// println('IDENT: $ident.name - $ident.pos.pos')
|
2020-02-17 12:25:18 +01:00
|
|
|
if ident.kind == .variable {
|
2020-02-17 14:15:42 +01:00
|
|
|
// println('===========================')
|
|
|
|
// c.scope.print_vars(0)
|
|
|
|
// println('===========================')
|
2020-02-17 12:25:18 +01:00
|
|
|
info := ident.info as ast.IdentVar
|
|
|
|
if info.typ != 0 {
|
|
|
|
return info.typ
|
|
|
|
}
|
2020-03-10 13:35:25 +01:00
|
|
|
start_scope := c.file.scope.innermost(ident.pos.pos)
|
2020-02-17 12:25:18 +01:00
|
|
|
mut found := true
|
2020-03-31 06:39:14 +02:00
|
|
|
var_scope,var := start_scope.find_scope_and_var(ident.name) or {
|
2020-02-17 12:25:18 +01:00
|
|
|
found = false
|
|
|
|
c.error('not found: $ident.name - POS: $ident.pos.pos', ident.pos)
|
|
|
|
panic('')
|
|
|
|
}
|
|
|
|
if found {
|
|
|
|
// update the variable
|
|
|
|
// we need to do this here instead of var_decl since some
|
|
|
|
// vars are registered manually for things like for loops etc
|
2020-03-08 22:11:56 +01:00
|
|
|
// NOTE: or consider making those declarations part of those ast nodes
|
2020-02-17 12:25:18 +01:00
|
|
|
mut typ := var.typ
|
|
|
|
// set var type on first use
|
|
|
|
if typ == 0 {
|
|
|
|
typ = c.expr(var.expr)
|
2020-03-28 14:00:28 +01:00
|
|
|
var_scope.update_var_type(var.name, typ)
|
2020-02-17 12:25:18 +01:00
|
|
|
}
|
|
|
|
// update ident
|
|
|
|
ident.kind = .variable
|
|
|
|
ident.info = ast.IdentVar{
|
|
|
|
typ: typ
|
2020-03-13 05:57:51 +01:00
|
|
|
is_optional: table.type_is_optional(typ)
|
2020-02-17 12:25:18 +01:00
|
|
|
}
|
2020-03-15 08:18:42 +01:00
|
|
|
// unwrap optional (`println(x)`)
|
|
|
|
if table.type_is_optional(typ) {
|
|
|
|
return table.type_clear_extra(typ)
|
|
|
|
}
|
2020-02-17 12:25:18 +01:00
|
|
|
return typ
|
|
|
|
}
|
|
|
|
}
|
2020-03-15 08:18:42 +01:00
|
|
|
// second use, already resovled in unresolved branch
|
2020-02-17 12:25:18 +01:00
|
|
|
else if ident.kind == .constant {
|
|
|
|
info := ident.info as ast.IdentVar
|
|
|
|
return info.typ
|
|
|
|
}
|
|
|
|
// second use, already resovled in unresovled branch
|
|
|
|
else if ident.kind == .function {
|
2020-03-11 16:10:46 +01:00
|
|
|
info := ident.info as ast.IdentFn
|
|
|
|
return info.typ
|
2020-02-17 12:25:18 +01:00
|
|
|
}
|
|
|
|
// Handle indents with unresolved types during the parsing step
|
|
|
|
// (declared after first usage)
|
|
|
|
else if ident.kind == .unresolved {
|
2020-02-28 13:29:04 +01:00
|
|
|
// prepend mod to look for fn call or const
|
|
|
|
mut name := ident.name
|
|
|
|
if !name.contains('.') && !(c.file.mod.name in ['builtin', 'main']) {
|
|
|
|
name = '${c.file.mod.name}.$ident.name'
|
|
|
|
}
|
2020-03-18 13:18:18 +01:00
|
|
|
// hack - const until consts are fixed properly
|
|
|
|
if ident.name == 'v_modules_path' {
|
|
|
|
ident.name = name
|
|
|
|
ident.kind = .constant
|
|
|
|
ident.info = ast.IdentVar{
|
|
|
|
typ: table.string_type
|
|
|
|
}
|
|
|
|
return table.string_type
|
|
|
|
}
|
2020-02-17 12:25:18 +01:00
|
|
|
// constant
|
2020-02-28 13:29:04 +01:00
|
|
|
if constant := c.table.find_const(name) {
|
2020-03-06 00:10:01 +01:00
|
|
|
ident.name = name
|
2020-02-17 12:25:18 +01:00
|
|
|
ident.kind = .constant
|
|
|
|
ident.info = ast.IdentVar{
|
|
|
|
typ: constant.typ
|
|
|
|
}
|
|
|
|
return constant.typ
|
|
|
|
}
|
|
|
|
// 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-03-16 10:12:03 +01:00
|
|
|
fn_type := table.new_type(c.table.find_or_register_fn_type(func, 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-03-06 00:10:01 +01:00
|
|
|
// TODO
|
|
|
|
// c.error('unknown ident: `$ident.name`', ident.pos)
|
2020-02-18 18:13:34 +01:00
|
|
|
if ident.is_c {
|
|
|
|
return table.int_type
|
|
|
|
}
|
2020-02-17 12:25:18 +01:00
|
|
|
return table.void_type
|
|
|
|
}
|
|
|
|
|
2020-02-15 13:37:48 +01:00
|
|
|
pub fn (c mut Checker) match_expr(node mut 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-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)
|
|
|
|
// TODO:
|
2020-03-02 18:26:55 +01: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-02-17 14:30:01 +01:00
|
|
|
// 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
|
|
|
else {}
|
2020-03-04 15:48:43 +01:00
|
|
|
}
|
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
|
|
|
}
|
|
|
|
|
|
|
|
pub fn (c mut Checker) if_expr(node mut ast.IfExpr) table.Type {
|
2020-03-18 16:47:37 +01:00
|
|
|
if c.expected_type != table.void_type {
|
2020-03-18 08:41:49 +01:00
|
|
|
// sym := c.table.get_type_symbol(c.expected_type)
|
|
|
|
// println('$c.file.path $node.pos.line_nr IF: checker exp type = ' + sym.name)
|
|
|
|
node.is_expr = true
|
|
|
|
}
|
|
|
|
node.typ = table.void_type
|
2020-03-20 14:39:56 +01:00
|
|
|
for i, branch in node.branches {
|
2020-03-27 14:57:19 +01:00
|
|
|
match branch.cond {
|
2020-03-31 06:34:59 +02:00
|
|
|
ast.ParExpr {
|
|
|
|
c.error('unnecessary `()` in an if condition. use `if expr {` instead of `if (expr) {`.', node.pos)
|
2020-03-27 14:57:19 +01:00
|
|
|
}
|
|
|
|
else {}
|
2020-03-31 06:34:59 +02:00
|
|
|
}
|
2020-03-20 14:39:56 +01:00
|
|
|
typ := c.expr(branch.cond)
|
2020-03-20 14:46:00 +01:00
|
|
|
if i < node.branches.len - 1 || !node.has_else {
|
2020-03-20 14:39:56 +01:00
|
|
|
typ_sym := c.table.get_type_symbol(typ)
|
|
|
|
// if typ_sym.kind != .bool {
|
|
|
|
if table.type_idx(typ) != table.bool_type_idx {
|
|
|
|
c.error('non-bool (`$typ_sym.name`) used as if condition', node.pos)
|
2020-02-15 13:37:48 +01:00
|
|
|
}
|
2020-03-20 14:39:56 +01:00
|
|
|
}
|
|
|
|
c.stmts(branch.stmts)
|
2020-02-17 14:15:42 +01:00
|
|
|
}
|
2020-03-20 14:39:56 +01:00
|
|
|
if node.has_else && node.is_expr {
|
2020-03-20 14:46:00 +01:00
|
|
|
last_branch := node.branches[node.branches.len - 1]
|
2020-03-20 14:39:56 +01:00
|
|
|
if last_branch.stmts.len > 0 {
|
|
|
|
match last_branch.stmts[last_branch.stmts.len - 1] {
|
|
|
|
ast.ExprStmt {
|
|
|
|
// type_sym := p.table.get_type_symbol(it.typ)
|
|
|
|
// p.warn('if expr ret $type_sym.name')
|
|
|
|
t := c.expr(it.expr)
|
|
|
|
node.typ = t
|
|
|
|
return t
|
|
|
|
}
|
|
|
|
else {}
|
2020-03-20 14:46:00 +01:00
|
|
|
}
|
2020-03-20 14:39:56 +01:00
|
|
|
}
|
2020-02-15 13:37:48 +01:00
|
|
|
}
|
2020-03-20 14:39:56 +01:00
|
|
|
return table.bool_type
|
2020-02-15 13:37:48 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
pub fn (c mut Checker) postfix_expr(node ast.PostfixExpr) table.Type {
|
2020-02-04 07:37:38 +01:00
|
|
|
/*
|
|
|
|
match node.expr {
|
|
|
|
ast.IdentVar {
|
|
|
|
println('postfix identvar')
|
|
|
|
}
|
|
|
|
else {}
|
|
|
|
}
|
|
|
|
*/
|
|
|
|
typ := c.expr(node.expr)
|
2020-02-27 17:31:10 +01:00
|
|
|
typ_sym := c.table.get_type_symbol(typ)
|
|
|
|
// if !table.is_number(typ) {
|
|
|
|
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-02-04 07:37:38 +01:00
|
|
|
}
|
|
|
|
return typ
|
|
|
|
}
|
|
|
|
|
2020-03-06 17:05:58 +01:00
|
|
|
pub fn (c mut Checker) index_expr(node mut ast.IndexExpr) table.Type {
|
2020-02-19 14:34:44 +01:00
|
|
|
typ := c.expr(node.left)
|
2020-02-03 11:29:50 +01:00
|
|
|
mut is_range := false // TODO is_range := node.index is ast.RangeExpr
|
|
|
|
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-07 05:19:15 +01:00
|
|
|
node.container_type = typ
|
2020-02-19 11:45:06 +01:00
|
|
|
if !is_range {
|
2020-02-29 20:11:04 +01:00
|
|
|
typ_sym := c.table.get_type_symbol(typ)
|
2020-02-19 11:45:06 +01:00
|
|
|
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-22 13:36:27 +01:00
|
|
|
if typ_sym.kind == .array && (!(table.type_idx(index_type) in table.number_type_idxs) && index_type_sym.kind != .enum_) {
|
2020-03-02 19:00:33 +01:00
|
|
|
c.error('non-integer index `$index_type_sym.name` (array type `$typ_sym.name`)', node.pos)
|
2020-02-29 20:11:04 +01:00
|
|
|
}
|
|
|
|
else if typ_sym.kind == .map && table.type_idx(index_type) != table.string_type_idx {
|
|
|
|
c.error('non-string map index (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
|
|
|
}
|
|
|
|
}
|
|
|
|
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-03-15 02:51:31 +01:00
|
|
|
pub fn (c mut Checker) enum_val(node mut ast.EnumVal) table.Type {
|
2020-03-19 11:52:47 +01:00
|
|
|
typ_idx := if node.enum_name == '' { table.type_idx(c.expected_type) } else { //
|
2020-02-27 00:12:37 +01: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-02-29 18:34:25 +01:00
|
|
|
}
|
2020-03-19 11:52:47 +01:00
|
|
|
typ := table.new_type(typ_idx)
|
2020-03-15 02:51:31 +01:00
|
|
|
typ_sym := c.table.get_type_symbol(typ)
|
2020-02-29 18:34:25 +01:00
|
|
|
// println('tname=$typ.name')
|
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-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-02-26 15:51:05 +01: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-03-02 04:24:45 +01:00
|
|
|
pub fn (c mut Checker) map_init(node mut 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 {
|
|
|
|
if i == 0 {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
val := node.vals[i]
|
|
|
|
key_type := c.expr(key)
|
|
|
|
val_type := c.expr(val)
|
|
|
|
if !c.table.check(key_type, key0_type) {
|
|
|
|
key0_type_sym := c.table.get_type_symbol(key0_type)
|
|
|
|
key_type_sym := c.table.get_type_symbol(key_type)
|
|
|
|
c.error('map init: cannot use `$key_type_sym.name` as `$key0_type_sym` for map key', node.pos)
|
|
|
|
}
|
|
|
|
if !c.table.check(val_type, val0_type) {
|
|
|
|
val0_type_sym := c.table.get_type_symbol(val0_type)
|
|
|
|
val_type_sym := c.table.get_type_symbol(val_type)
|
|
|
|
c.error('map init: cannot use `$val_type_sym.name` as `$val0_type_sym` for map value', node.pos)
|
|
|
|
}
|
|
|
|
}
|
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-02-19 19:54:36 +01:00
|
|
|
pub fn (c mut Checker) error(s string, pos token.Position) {
|
|
|
|
c.nr_errors++
|
2020-02-03 07:02:54 +01:00
|
|
|
print_backtrace()
|
2020-02-20 11:13:18 +01:00
|
|
|
mut path := c.file.path
|
2020-02-07 07:34:18 +01:00
|
|
|
// Get relative path
|
2020-03-07 22:26:26 +01:00
|
|
|
workdir := os.getwd() + os.path_separator
|
2020-02-07 07:34:18 +01:00
|
|
|
if path.starts_with(workdir) {
|
|
|
|
path = path.replace(workdir, '')
|
|
|
|
}
|
2020-02-19 19:54:36 +01:00
|
|
|
final_msg_line := '$path:$pos.line_nr: checker error #$c.nr_errors: $s'
|
2020-02-20 17:05:16 +01:00
|
|
|
c.errors << final_msg_line
|
2020-02-03 07:02:54 +01:00
|
|
|
eprintln(final_msg_line)
|
|
|
|
/*
|
2020-01-18 23:26:14 +01:00
|
|
|
if colored_output {
|
|
|
|
eprintln(term.bold(term.red(final_msg_line)))
|
|
|
|
}else{
|
|
|
|
eprintln(final_msg_line)
|
|
|
|
}
|
|
|
|
*/
|
2020-01-22 21:34:38 +01:00
|
|
|
|
2020-02-19 19:54:36 +01:00
|
|
|
println('\n\n')
|
|
|
|
if c.nr_errors >= max_nr_errors {
|
|
|
|
exit(1)
|
|
|
|
}
|
2020-02-03 07:02:54 +01:00
|
|
|
}
|